UNPKG

biot-core

Version:

```sh $ npm install $ cd examples $ node balance.js ```

627 lines (565 loc) 20.2 kB
const util = require('util'); const conf = require('ocore/conf'); const Wallet = require('ocore/wallet'); const db = require('ocore/db'); const eventBus = require('ocore/event_bus'); const walletDefinedByKeys = require('ocore/wallet_defined_by_keys'); const ecdsaSig = require('ocore/signature.js'); const ecdsa = require('secp256k1'); const Mnemonic = require('bitcore-mnemonic'); const toEs6 = require('./lib/toEs6'); const libKeys = require('./lib/keys'); const libWallet = require('./lib/wallet'); const libAddress = require('./lib/address'); const libTransactions = require('./lib/transactions'); const libCorrespondents = require('./lib/correspondents'); const libSqliteMigrations = require('./lib/sqlite_migrations'); const libWalletTransactions = require('./lib/walletTransactions'); const protocolVersion = '0.1'; let xPrivKey; process.on('unhandledRejection', up => { throw up; }); function replaceConsoleLog() { if (global.window && window.cordova) return; const fs = require('fs' + ''); if (!fs) return; const desktopApp = require('ocore/desktop_app' + ''); const appDataDir = desktopApp.getAppDataDir(); let log_filename = conf.LOG_FILENAME || (appDataDir + '/log.txt'); let writeStream = fs.createWriteStream(log_filename); console.log('---------------'); console.log('From this point, output will be redirected to ' + log_filename); console.log("To release the terminal, type Ctrl-Z, then 'bg'"); console.log = function () { writeStream.write(Date().toString() + ': '); writeStream.write(util.format.apply(null, arguments) + '\n'); }; console.warn = console.log; console.info = console.log; } /** @async @description Core initializing @param {string} passphrase Passphrase to unlock your account @return {string} Status @example await core.init('test') */ exports.init = async (passphrase) => { let deviceName; if (global.window && window.cordova) { deviceName = window.localStorage.getItem("device_name"); if (!deviceName) { return 'Please set device name'; } } else { if (!conf.deviceName) { return 'Please set device name'; } else { deviceName = conf.deviceName; } } let keys = await libKeys.readKeys(passphrase); let saveTempKeys = (new_temp_key, new_prev_temp_key, onDone) => { libKeys.writeKeys(keys.mnemonic_phrase, new_temp_key, new_prev_temp_key, onDone).catch(Promise.reject); }; let mnemonic = new Mnemonic(keys.mnemonic_phrase); xPrivKey = mnemonic.toHDPrivateKey(passphrase); libKeys.setXPrivKey(xPrivKey); let devicePrivKey = xPrivKey.derive("m/1'").privateKey.bn.toBuffer({size: 32}); let device = require('ocore/device'); device.setDevicePrivateKey(devicePrivKey); let my_device_address = device.getMyDeviceAddress(); let rows = await toEs6.dbQuery("SELECT 1 FROM extended_pubkeys WHERE device_address=?", [my_device_address]); if (rows.length === 0) { console.log('passphrase is incorrect'); return false; } if (conf.permanent_pairing_secret) db.query( "INSERT " + db.getIgnore() + " INTO pairing_secrets (pairing_secret, is_permanent, expiry_date) VALUES (?, 1, '2038-01-01')", [conf.permanent_pairing_secret] ); device.setTempKeys(keys.deviceTempPrivKey, keys.devicePrevTempPrivKey, saveTempKeys); device.setDeviceName(deviceName); device.setDeviceHub(conf.hub); let my_device_pubkey = device.getMyDevicePubKey(); console.log("====== my device address: " + my_device_address); console.log("====== my device pubkey: " + my_device_pubkey); if (conf.permanent_pairing_secret) console.log("====== my pairing code: " + my_device_pubkey + "@" + conf.hub + "#" + conf.permanent_pairing_secret); if (conf.bLight) { let light_wallet = require('ocore/light_wallet'); light_wallet.setLightVendorHost(conf.hub); } await libSqliteMigrations.migrateDb(); eventBus.emit('headless_wallet_ready'); if (!process.env.DEBUG) { replaceConsoleLog(); } return keys.mnemonic_phrase; }; async function updateConfFile(obj) { const desktopApp = require('ocore/desktop_app' + ''); const appDataDir = desktopApp.getAppDataDir(); const userConfFile = appDataDir + '/conf.json'; let json = require(userConfFile); for (let key in obj) { if (obj.hasOwnProperty(key)) { json[key] = obj[key]; } } await new Promise(resolve => { fs.writeFile(userConfFile, JSON.stringify(json, null, '\t'), 'utf8', (err) => { if (err) throw Error('failed to write conf.json: ' + err); resolve(); }); }); } async function setDeviceName(name) { if (global.window && window.cordova) { window.localStorage.setItem('device_name', name); } else { await updateConfFile({deviceName: name}); } } /** @async @description Getting list of wallets @return {string[]} Wallets list @example await core.getWallets() */ async function getWallets() { let rows = await toEs6.dbQuery("SELECT wallet FROM wallets"); return rows.map(row => row.wallet); } async function getMyDeviceWallets() { let device = require('ocore/device') let rows = await toEs6.dbQuery("SELECT wallet FROM wallets JOIN wallet_signing_paths USING(wallet) WHERE device_address = ?", [device.getMyDeviceAddress()]); return rows.map(row => row.wallet); } /** @async @description Getting list of addresses @param {string} walletId Wallet id @return {string[]} Addresses list @example await core.getAddressesInWallet('yXSWvqast2rrmwcR/f5QfUAXZLwaaiRvwE+N9whoZLc=') */ async function getAddressesInWallet(walletId) { let rows = await toEs6.dbQuery("SELECT address FROM my_addresses WHERE wallet = ?", [walletId]); return rows.map(row => row.address); } /** @async @description Creating new wallet @return {string} walletId @example await core.createNewWallet() */ async function createNewWallet() { let rows = await toEs6.dbQuery("SELECT wallet FROM wallets"); let walletId = await libWallet.createWallet(xPrivKey, rows.length); return walletId; } /** @description Creating new address @param {string} walletId Wallet id @return {Promise.<string>} Address @example await core.createNewAddress('yXSWvqast2rrmwcR/f5QfUAXZLwaaiRvwE+N9whoZLc=') */ function createNewAddress(walletId) { return new Promise(resolve => { walletDefinedByKeys.issueNextAddress(walletId, 0, function (addressInfo) { return resolve(addressInfo.address); }); }); } /** @description Getting balance of wallet @param {string} walletId Wallet id @return {Object} Balance @example await core.getWalletBalance('yXSWvqast2rrmwcR/f5QfUAXZLwaaiRvwE+N9whoZLc=') */ function getWalletBalance(walletId) { return new Promise(resolve => { Wallet.readBalance(walletId, (balance) => { return resolve(balance); }) }); } /** @async @description Getting balance of address @param {string} address Byteball address @return {Object} Balance @example await core.getAddressBalance('VY52VTHNX27WKRGFUGJY7KNTGXP3Z6YU') */ async function getAddressBalance(address) { return libAddress.getAddressBalance(address); } /** @description Sending text message to device address @param {string} device_address Device address @param {string} text Message text @example core.sendTextMessageToDevice('0PZT5VOY5AINZKW2SJ3Z7O4IDQNKPV364', 'Hello!') */ function sendTextMessageToDevice(device_address, text) { let device = require('ocore/device'); device.sendMessageToDevice(device_address, 'text', text); } /** @description Sending tech message to device address @param {string} device_address Device address @param {object} object Object @param {function} callback callback @example core.sendTechMessageToDevice('0PZT5VOY5AINZKW2SJ3Z7O4IDQNKPV364', {version: '0.1'}) */ function sendTechMessageToDevice(device_address, object, callback) { let device = require('ocore/device'); object.version = protocolVersion; object.app = 'BIoT'; device.sendMessageToDevice(device_address, 'object', object, callback); } /** @description Sending payment from wallet @param {Object} options Payment options @param {string} options.asset Payment asset @param {string} options.wallet Wallet from which goes payment @param {string} options.toAddress Address on which you send the payment @param {number} options.amount Payment amount @param {string} options.changeAddress Your new address after the payment @param {string|null} options.deviceAddress Device address on what will be sent notification @return {Promise.<string>} unit @example core.sendTransaction({ asset: 'base', wallet: 'yXSWvqast2rrmwcR/f5QfUAXZLwaaiRvwE+N9whoZLc=', toAddress: 'VY52VTHNX27WKRGFUGJY7KNTGXP3Z6YU', amount: 10, changeAddress: 'IZHG7LKW2FJ2KAUHL4RRPY3JG2HNVNPD', deviceAddress: null }); */ function sendPaymentFromWallet(options) { return libTransactions.sendPaymentFromWallet(options); } function sendMultiPayment(options) { return libTransactions.sendMultiPayment(options); } /** @description Sending payment from wallet use unstable units @param {Object} options Payment options @param {string} options.asset Payment asset @param {string} options.wallet Wallet from which goes payment @param {string} options.toAddress Address on which you send the payment @param {number} options.amount Payment amount @param {string} options.changeAddress Your new address after the payment @param {string|null} options.deviceAddress Device address on what will be sent notification @return {Promise.<string>} unit @example core.sendPaymentFromWalletUseUnstableUnits({ asset: 'base', wallet: 'yXSWvqast2rrmwcR/f5QfUAXZLwaaiRvwE+N9whoZLc=', toAddress: 'VY52VTHNX27WKRGFUGJY7KNTGXP3Z6YU', amount: 10, changeAddress: 'IZHG7LKW2FJ2KAUHL4RRPY3JG2HNVNPD', deviceAddress: null }); */ function sendPaymentUseUnstableUnits(to_address, amount, my_addresses, asset) { return libTransactions.sendPaymentUseUnstableUnits(to_address, amount, my_addresses, asset); } /** @description Getting list of transactions by wallet. @param {string} walletId Wallet id @return {Promise.<Object>} history @example core.getListTransactionsForWallet('yXSWvqast2rrmwcR/f5QfUAXZLwaaiRvwE+N9whoZLc='); */ function getListTransactionsForWallet(walletId) { return new Promise(resolve => { Wallet.readTransactionHistory({wallet: walletId}, resolve); }) } /** @description Getting list of transactions by address. @param {string} address Byteball address @return {Promise.<Object>} history @example core.getListTransactionsForAddress('IZHG7LKW2FJ2KAUHL4RRPY3JG2HNVNPD'); */ function getListTransactionsForAddress(address) { return new Promise(resolve => { Wallet.readTransactionHistory({address}, resolve); }) } /** * @typedef {Object} AddressInfo * @property {number} account * @property {number} is_change * @property {number} address_index */ /** @async @description Getting address info @param {string} address Byteball address @return {AddressInfo} address info @example await core.myAddressInfo('IZHG7LKW2FJ2KAUHL4RRPY3JG2HNVNPD'); */ function myAddressInfo(address) { return libAddress.myAddressInfo(address); } /** * @typedef {Object} Sign * @property {string} sign * @property {string} pub_b64 */ /** @description Signing device private key @param {string} hash Hash string @return {Sign} sign object @example core.signDevicePrivateKey('IZHG7LKW2FJ2KAUHL4RRPY3JG2HNVNPD'); */ function signDevicePrivateKey(hash) { let buffer = Buffer.from(hash); let device = require('ocore/device'); let devicePrivKey = xPrivKey.derive("m/1'").privateKey.bn.toBuffer({size: 32}); return {sign: ecdsaSig.sign(buffer, devicePrivKey), pub_b64: device.getMyDevicePubKey()}; } /** @description Signing with address (watch: myAddressInfo) @param {number} account @param {number} is_change @param {number} address_index @param {string} hash Hash string @return {Sign} sign object @example core.signWithAddress(0, 0, 0, 'IZHG7LKW2FJ2KAUHL4RRPY3JG2HNVNPD'); */ function signWithAddress(account, is_change, address_index, hash) { let buffer = Buffer.from(hash); let path = "m/44'/0'/" + account + "'/" + is_change + "/" + address_index; let privateKey = xPrivKey.derive(path).privateKey; let privKeyBuf = privateKey.bn.toBuffer({size: 32}); // https://github.com/bitpay/bitcore-lib/issues/47 let pubKeyb64 = ecdsa.publicKeyCreate(privKeyBuf, true).toString('base64') return {sign: ecdsaSig.sign(buffer, privKeyBuf), pub_b64: pubKeyb64}; } /** @description Sign verification @param {string} hash Hash string @param {string} b64_sig Sign.sign @param {string} b64_pub_key Sign.pub_b64 @return {boolean} Verification result @example core.verifySign('IZHG7LKW2FJ2KAUHL4RRPY3JG2HNVNPD', '/J6Gv9aT8KSgEP2TwmNoQ2W/JmZXYaXBLt4zBE8Po5Vm8TOX+fu53Y7DSYtuH/61EgR7WP5Spk76J8gFTCmPpg==', 'A4QdpqFIqVbyCXgbuzlHEMl+1osh2hGC3oVRzHU1V5V0'); */ function verifySign(hash, b64_sig, b64_pub_key) { let buffer = Buffer.from(hash); return ecdsaSig.verify(buffer, b64_sig, b64_pub_key); } /** @description Add a correspondent @param {string} code Pairing code @example await core.addCorrespondent('ApM6ZzpMhnK87Qqz4LhkIHTxGA79VVTVqb1PmtrAzOzo@byteball.org/bb-test#O3IZDFeH4SR0'); */ function addCorrespondent(code) { return libCorrespondents.add(code); } /** @description Remove a correspondent @param {string} device_address Device address @example await core.removeCorrespondent('0WI73XY6WPR46D4ZKEQEFFQSSPBZMUOVD'); */ function removeCorrespondent(device_address) { return libCorrespondents.remove(device_address); } /** @description List of correspondents @return {Array.<Object>} list @example await core.listCorrespondents() */ function listCorrespondents() { return libCorrespondents.list(); } function getMyParingCode() { const device = require('ocore/device'); return device.getMyDevicePubKey() + "@" + conf.hub + "#"; } function postDataFeed(objDataFeed) { return new Promise(resolve => { (async () => { const network = require('ocore/network.js'); const composer = require('ocore/composer.js'); const objectHash = require('ocore/object_hash.js'); const wallet = require('ocore/wallet.js'); const wallets = await getWallets(); const addresses = await getAddressesInWallet(wallets[0]); const my_address = addresses[0]; let params = { paying_addresses: [my_address], outputs: [{address: my_address, amount: 0}], signer: wallet.getSigner({}, addresses, libKeys.signWithLocalPrivateKey), spend_unconfirmed: 'all', callbacks: composer.getSavingCallbacks({ ifNotEnoughFunds: console.error, ifError: console.error, ifOk: function (objJoint) { network.broadcastJoint(objJoint); return resolve(objJoint); } }) }; let objMessage = { app: "data_feed", payload_location: "inline", payload_hash: objectHash.getBase64Hash(objDataFeed), payload: objDataFeed }; params.messages = [objMessage]; composer.composeJoint(params); })(); }); } function postPrivateProfile(user_address, profile) { return new Promise(resolve => { (async () => { const network = require('ocore/network.js'); const composer = require('ocore/composer.js'); const objectHash = require('ocore/object_hash.js'); const wallet = require('ocore/wallet.js'); const wallets = await getWallets(); const addresses = await getAddressesInWallet(wallets[0]); const my_address = addresses[0]; let src_profile = {}; let hidden_profile = {}; for (let field in profile) { let value = profile[field]; let blinding = composer.generateBlinding(); hidden_profile[field] = objectHash.getBase64Hash([value, blinding]); src_profile[field] = [value, blinding]; } let profile_hash = objectHash.getBase64Hash(hidden_profile); let privProfile = { profile_hash: profile_hash, user_id: objectHash.getBase64Hash([profile, conf.salt || '']) }; let payload = { address: user_address, profile: privProfile }; let params = { paying_addresses: [my_address], outputs: [{address: my_address, amount: 0}], signer: wallet.getSigner({}, addresses, libKeys.signWithLocalPrivateKey), spend_unconfirmed: 'all', callbacks: composer.getSavingCallbacks({ ifNotEnoughFunds: console.error, ifError: console.error, ifOk: function (objJoint) { network.broadcastJoint(objJoint); return resolve({objJoint, src_profile, address: my_address}); } }) }; console.error('payload', payload); let objMessage = { app: "attestation", payload_location: "inline", payload_hash: objectHash.getBase64Hash(payload), payload: payload }; params.messages = [objMessage]; composer.composeJoint(params); })(); }); } function postPublicProfile(user_address, profile) { return new Promise(resolve => { (async () => { const network = require('ocore/network.js'); const composer = require('ocore/composer.js'); const objectHash = require('ocore/object_hash.js'); const wallet = require('ocore/wallet.js'); const wallets = await getWallets(); const addresses = await getAddressesInWallet(wallets[0]); const my_address = addresses[0]; let payload = { address: user_address, profile: profile }; let params = { paying_addresses: [my_address], outputs: [{address: my_address, amount: 0}], signer: wallet.getSigner({}, addresses, libKeys.signWithLocalPrivateKey), spend_unconfirmed: 'all', callbacks: composer.getSavingCallbacks({ ifNotEnoughFunds: console.error, ifError: console.error, ifOk: function (objJoint) { network.broadcastJoint(objJoint); return resolve({objJoint, address: my_address}); } }) }; console.error('payload', payload); let objMessage = { app: "attestation", payload_location: "inline", payload_hash: objectHash.getBase64Hash(payload), payload: payload }; params.messages = [objMessage]; composer.composeJoint(params); })(); }); } function saveProfile(attester, my_address, unit, src_profile) { return toEs6.dbQuery("INSERT INTO profiles (attester, address, unit, object) VALUES(?,?,?,?)", [attester, my_address, unit, src_profile]); } function getProfiles() { return toEs6.dbQuery("SELECT * FROM profiles"); } exports.createNewWallet = createNewWallet; exports.getWallets = getWallets; exports.getMyDeviceWallets = getMyDeviceWallets; exports.getAddressesInWallet = getAddressesInWallet; exports.createNewAddress = createNewAddress; exports.getWalletBalance = getWalletBalance; exports.getAddressBalance = getAddressBalance; exports.sendTextMessageToDevice = sendTextMessageToDevice; exports.sendTechMessageToDevice = sendTechMessageToDevice; exports.sendPaymentFromWallet = sendPaymentFromWallet; exports.sendPaymentUseUnstableUnits = sendPaymentUseUnstableUnits; exports.getListTransactionsForAddress = getListTransactionsForAddress; exports.getListTransactionsForWallet = getListTransactionsForWallet; exports.myAddressInfo = myAddressInfo; exports.signDevicePrivateKey = signDevicePrivateKey; exports.signWithAddress = signWithAddress; exports.verifySign = verifySign; exports.addCorrespondent = addCorrespondent; exports.removeCorrespondent = removeCorrespondent; exports.listCorrespondents = listCorrespondents; exports.getMyParingCode = getMyParingCode; exports.postDataFeed = postDataFeed; exports.postPrivateProfile = postPrivateProfile; exports.postPublicProfile = postPublicProfile; exports.saveProfile = saveProfile; exports.getProfiles = getProfiles; exports.setDeviceName = setDeviceName; exports.sendMultiPayment = sendMultiPayment; exports.getWalletTransactions = libWalletTransactions.getWalletTransactions;