From 59611f4d23f861d3dc065f802f32ca568050d123 Mon Sep 17 00:00:00 2001 From: pbca26 Date: Tue, 10 Oct 2017 16:25:56 +0300 Subject: [PATCH] promises; listtransactions, getbalance error handling --- routes/shepherd.js | 658 +++++++++++++++++++++++---------------------- 1 file changed, 337 insertions(+), 321 deletions(-) diff --git a/routes/shepherd.js b/routes/shepherd.js index e6a14a4..cc7af03 100644 --- a/routes/shepherd.js +++ b/routes/shepherd.js @@ -542,33 +542,44 @@ shepherd.verifyMerkle = function(txid, height, serverList, mainServer) { ecl.connect(); ecl.blockchainTransactionGetMerkle(txid, height) .then((merkleData) => { - console.log('electrum getmerkle =>'); - console.log(merkleData); - ecl.close(); - - const _res = shepherd.getMerkleRoot(txid, merkleData.merkle, merkleData.pos); - console.log(_res); - - ecl = new electrumJSCore(_randomServer[1], _randomServer[0], 'tcp'); - ecl.connect(); - ecl.blockchainBlockGetHeader(height) - .then((blockInfo) => { + if (merkleData && + merkleData.merkle && + merkleData.pos) { + console.log('electrum getmerkle =>'); + console.log(merkleData); ecl.close(); - console.log('blockinfo =>'); - console.log(blockInfo); - console.log(blockInfo['merkle_root']); - - if (blockInfo && - blockInfo['merkle_root']) { - if (_res === blockInfo['merkle_root']) { - resolve(true); + + const _res = shepherd.getMerkleRoot(txid, merkleData.merkle, merkleData.pos); + console.log(_res); + + ecl = new electrumJSCore(_randomServer[1], _randomServer[0], 'tcp'); + ecl.connect(); + ecl.blockchainBlockGetHeader(height) + .then((blockInfo) => { + if (blockInfo && + blockInfo['merkle_root']) { + ecl.close(); + console.log('blockinfo =>'); + console.log(blockInfo); + console.log(blockInfo['merkle_root']); + + if (blockInfo && + blockInfo['merkle_root']) { + if (_res === blockInfo['merkle_root']) { + resolve(true); + } else { + resolve(false); + } + } else { + resolve(CONNECTION_ERROR_OR_INCOMPLETE_DATA); + } } else { - resolve(false); + resolve(f); } - } else { - resolve(CONNECTION_ERROR_OR_INCOMPLETE_DATA); - } - }); + }); + } else { + resolve(CONNECTION_ERROR_OR_INCOMPLETE_DATA); + } }); }); } @@ -736,17 +747,8 @@ shepherd.kdmCalcInterest = function(locktime, value) { // value in sats console.log(`minutes if statement ${timestampDiffMinutes}`); - let denominator = (365 * 24 * 60) / timestampDiffMinutes; - - if (denominator === 0) { - denominator = 1; // max KOMODO_INTEREST per transfer, do it at least annually! - } - - console.log(`denominator ${denominator}`); - // TODO: check if interest is > 5% yr // calc ytd and 5% for 1 yr - const numerator = Number(value) * 0.00000001 / 20; // assumes 5%! // const hoursInOneYear = 365 * 24; // const hoursDiff = hoursInOneYear - hoursPassed; @@ -764,66 +766,93 @@ shepherd.get('/electrum/getbalance', function(req, res, next) { ecl.connect(); ecl.blockchainAddressGetBalance(req.query.address) .then((json) => { - if (network === 'komodo') { - ecl.connect(); - ecl.blockchainAddressListunspent(req.query.address) - .then((utxoList) => { - if (utxoList && - utxoList.length) { - // filter out < 10 KMD amounts - let _utxo = []; + if (json && + json.hasOwnProperty('confirmed') && + json.hasOwnProperty('unconfirmed')) { + if (network === 'komodo') { + ecl.connect(); + ecl.blockchainAddressListunspent(req.query.address) + .then((utxoList) => { + if (utxoList && + utxoList.length) { + // filter out < 10 KMD amounts + let _utxo = []; + + for (let i = 0; i < utxoList.length; i++) { + console.log(`utxo ${utxoList[i]['tx_hash']} sats ${utxoList[i].value} value ${Number(utxoList[i].value) * 0.00000001}`); + + if (Number(utxoList[i].value) * 0.00000001 >= 10) { + _utxo.push(utxoList[i]); + } + } - for (let i = 0; i < utxoList.length; i++) { - console.log(`utxo ${utxoList[i]['tx_hash']} sats ${utxoList[i].value} value ${Number(utxoList[i].value) * 0.00000001}`); + console.log('filtered utxo list =>'); + console.log(_utxo); - if (Number(utxoList[i].value) * 0.00000001 >= 10) { - _utxo.push(utxoList[i]); - } - } + if (_utxo && + _utxo.length) { + let interestTotal = 0; - console.log('filtered utxo list =>'); - console.log(_utxo); + Promise.all(_utxo.map((_utxoItem, index) => { + return new Promise((resolve, reject) => { + ecl.blockchainTransactionGet(_utxoItem['tx_hash']) + .then((_rawtxJSON) => { + console.log('electrum gettransaction ==>'); + console.log(index + ' | ' + (_rawtxJSON.length - 1)); + console.log(_rawtxJSON); - if (_utxo && - _utxo.length) { - let interestTotal = 0; + // decode tx + const _network = shepherd.getNetworkData(network); + const decodedTx = electrumJSTxDecoder(_rawtxJSON, _network); - for (let i = 0; i < _utxo.length; i++) { - ecl.blockchainTransactionGet(_utxo[i]['tx_hash']) - .then((_rawtxJSON) => { - console.log('electrum gettransaction ==>'); - console.log(i + ' | ' + (_rawtxJSON.length - 1)); - console.log(_rawtxJSON); + if (decodedTx && + decodedTx.format && + decodedTx.format.locktime > 0) { + interestTotal += shepherd.kdmCalcInterest(decodedTx.format.locktime, _utxoItem.value); + } - // decode tx - const _network = shepherd.getNetworkData(network); - const decodedTx = electrumJSTxDecoder(_rawtxJSON, _network); + console.log('decoded tx =>'); + console.log(decodedTx); - if (decodedTx.format.locktime > 0) { - interestTotal += shepherd.kdmCalcInterest(decodedTx.format.locktime, _utxo[i].value); - } - console.log('decoded tx =>'); - console.log(decodedTx); - console.log(decodedTx.format.locktime); - - if (i === _utxo.length -1) { - const successObj = { - msg: 'success', - result: { - balance: Number((0.00000001 * json.confirmed).toFixed(8)), - unconfirmed: Number((0.00000001 * json.unconfirmed).toFixed(8)), - unconfirmedSats: json.unconfirmed, - balanceSats: json.confirmed, - interest: Number(interestTotal.toFixed(8)), - interestSats: Math.floor(interestTotal * 100000000), - total: interestTotal > 0 ? Number((0.00000001 * json.confirmed + interestTotal).toFixed(8)) : 0, - totalSats: interestTotal > 0 ?json.confirmed + Math.floor(interestTotal * 100000000) : 0, - }, - }; + resolve(true); + }); + }); + })) + .then(promiseResult => { + ecl.close(); - res.end(JSON.stringify(successObj)); - } + const successObj = { + msg: 'success', + result: { + balance: Number((0.00000001 * json.confirmed).toFixed(8)), + unconfirmed: Number((0.00000001 * json.unconfirmed).toFixed(8)), + unconfirmedSats: json.unconfirmed, + balanceSats: json.confirmed, + interest: Number(interestTotal.toFixed(8)), + interestSats: Math.floor(interestTotal * 100000000), + total: interestTotal > 0 ? Number((0.00000001 * json.confirmed + interestTotal).toFixed(8)) : 0, + totalSats: interestTotal > 0 ?json.confirmed + Math.floor(interestTotal * 100000000) : 0, + }, + }; + + res.end(JSON.stringify(successObj)); }); + } else { + const successObj = { + msg: 'success', + result: { + balance: Number((0.00000001 * json.confirmed).toFixed(8)), + unconfirmed: Number((0.00000001 * json.unconfirmed).toFixed(8)), + unconfirmedSats: json.unconfirmed, + balanceSats: json.confirmed, + interest: 0, + interestSats: 0, + total: 0, + totalSats: 0, + }, + }; + + res.end(JSON.stringify(successObj)); } } else { const successObj = { @@ -842,33 +871,28 @@ shepherd.get('/electrum/getbalance', function(req, res, next) { res.end(JSON.stringify(successObj)); } - } else { - const successObj = { - msg: 'success', - result: { - balance: Number((0.00000001 * json.confirmed).toFixed(8)), - unconfirmed: Number((0.00000001 * json.unconfirmed).toFixed(8)), - unconfirmedSats: json.unconfirmed, - balanceSats: json.confirmed, - interest: 0, - }, - }; + }); + } else { + ecl.close(); + console.log('electrum getbalance ==>'); + console.log(json); - res.end(JSON.stringify(successObj)); - } - }); - } else { - ecl.close(); - console.log('electrum getbalance ==>'); - console.log(json); + const successObj = { + msg: 'success', + result: { + balance: Number((0.00000001 * json.confirmed).toFixed(8)), + unconfirmed: Number((0.00000001 * json.unconfirmed).toFixed(8)), + unconfirmedSats: json.unconfirmed, + balanceSats: json.confirmed, + }, + }; + res.end(JSON.stringify(successObj)); + } + } else { const successObj = { - msg: 'success', - result: { - balance: 0.00000001 * json.confirmed, - unconfirmed: json.unconfirmed, - sats: json.confirmed, - }, + msg: 'error', + result: CONNECTION_ERROR_OR_INCOMPLETE_DATA, }; res.end(JSON.stringify(successObj)); @@ -920,54 +944,69 @@ shepherd.get('/electrum/listtransactions', function(req, res, next) { ecl.connect(); ecl.blockchainNumblocksSubscribe() - .then(function(currentHeight) { - ecl.blockchainAddressGetHistory(req.query.address) - .then((json) => { - if (json && - json.length) { - json = shepherd.sortTransactions(json); - json = json.slice(0, MAX_TX); - console.log(json.length); - let _rawtx = []; - - // get raw tx - for (let i = 0; i < json.length; i++) { - ecl.blockchainBlockGetHeader(json[i].height) - .then((blockInfo) => { - ecl.blockchainTransactionGet(json[i]['tx_hash']) - .then((_rawtxJSON) => { - console.log('electrum gettransaction ==>'); - console.log(i + ' | ' + (_rawtxJSON.length - 1)); - console.log(_rawtxJSON); - - // decode tx - const _network = shepherd.getNetworkData(network); - const decodedTx = electrumJSTxDecoder(_rawtxJSON, _network); - - let txInputs = []; - - console.log('decodedtx =>'); - console.log(decodedTx.outputs); - - if (decodedTx.inputs) { - for (let j = 0; j < decodedTx.inputs.length; j++) { - if (decodedTx.inputs[j].txid !== '0000000000000000000000000000000000000000000000000000000000000000') { - ecl.blockchainTransactionGet(decodedTx.inputs[j].txid) - .then((rawInput) => { - console.log('electrum raw input tx ==>'); - const decodedVinVout = electrumJSTxDecoder(rawInput, _network); - console.log(decodedVinVout.outputs[decodedTx.inputs[j].n]); - txInputs.push(decodedVinVout.outputs[decodedTx.inputs[j].n]); - - if (j === decodedTx.inputs.length - 1) { + .then((currentHeight) => { + if (currentHeight && + Number(currentHeight) > 0) { + ecl.blockchainAddressGetHistory(req.query.address) + .then((json) => { + if (json && + json.length) { + json = shepherd.sortTransactions(json); + json = json.slice(0, MAX_TX); + console.log(json.length); + let _rawtx = []; + + Promise.all(json.map((transaction, index) => { + return new Promise((resolve, reject) => { + ecl.blockchainBlockGetHeader(transaction.height) + .then((blockInfo) => { + if (blockInfo && + blockInfo.timestamp) { + ecl.blockchainTransactionGet(transaction['tx_hash']) + .then((_rawtxJSON) => { + console.log('electrum gettransaction ==>'); + console.log(index + ' | ' + (_rawtxJSON.length - 1)); + console.log(_rawtxJSON); + + // decode tx + const _network = shepherd.getNetworkData(network); + const decodedTx = electrumJSTxDecoder(_rawtxJSON, _network); + + let txInputs = []; + + console.log('decodedtx =>'); + console.log(decodedTx.outputs); + + if (decodedTx && + decodedTx.inputs) { + Promise.all(decodedTx.inputs.map((_decodedInput, index) => { + return new Promise((_resolve, _reject) => { + if (_decodedInput.txid !== '0000000000000000000000000000000000000000000000000000000000000000') { + ecl.blockchainTransactionGet(_decodedInput.txid) + .then((rawInput) => { + console.log('electrum raw input tx ==>'); + const decodedVinVout = electrumJSTxDecoder(rawInput, _network); + + if (decodedVinVout) { + console.log(decodedVinVout.outputs[_decodedInput.n]); + txInputs.push(decodedVinVout.outputs[_decodedInput.n]); + _resolve(true); + } + }); + } else { + _resolve(true); + } + }); + })) + .then(promiseResult => { const _parsedTx = { network: decodedTx.network, format: decodedTx.format, inputs: txInputs, outputs: decodedTx.outputs, - height: json[i].height, + height: transaction.height, timestamp: blockInfo.timestamp, - confirmations: currentHeight - json[i].height, + confirmations: currentHeight - transaction.height, }; const formattedTx = shepherd.parseTransactionAddresses(_parsedTx, req.query.address, network); @@ -978,104 +1017,68 @@ shepherd.get('/electrum/listtransactions', function(req, res, next) { _rawtx.push(formattedTx[0]); _rawtx.push(formattedTx[1]); } - - if (i === json.length - 1) { - ecl.close(); - console.log('electrum gettransaction array ==>'); - console.log(_rawtx); - - const successObj = { - msg: 'success', - result: { - listtransactions: _rawtx, - }, - }; - - res.end(JSON.stringify(successObj)); - } - } - }); - } else { - if (j === decodedTx.inputs.length - 1) { + resolve(true); + }); + } else { const _parsedTx = { network: decodedTx.network, - format: decodedTx.format, - inputs: txInputs, - outputs: decodedTx.outputs, - height: json[i].height, + format: 'cant parse', + inputs: 'cant parse', + outputs: 'cant parse', + height: transaction.height, timestamp: blockInfo.timestamp, - confirmations: currentHeight - json[i].height, + confirmations: currentHeight - transaction.height, }; const formattedTx = shepherd.parseTransactionAddresses(_parsedTx, req.query.address, network); - - if (formattedTx.type) { - _rawtx.push(formattedTx); - } else { - _rawtx.push(formattedTx[0]); - _rawtx.push(formattedTx[1]); - } - - if (i === json.length - 1) { - ecl.close(); - console.log('electrum gettransaction array ==>'); - console.log(_rawtx); - - const successObj = { - msg: 'success', - result: { - listtransactions: _rawtx, - }, - }; - - res.end(JSON.stringify(successObj)); - } + _rawtx.push(formattedTx); + resolve(true); } - } - } - } else { - const _parsedTx = { - network: decodedTx.network, - format: 'cant parse', - inputs: 'cant parse', - outputs: 'cant parse', - height: json[i].height, - timestamp: blockInfo.timestamp, - confirmations: currentHeight - json[i].height, - }; - - const formattedTx = shepherd.parseTransactionAddresses(_parsedTx, req.query.address, network); - _rawtx.push(formattedTx); - - if (i === json.length - 1) { - ecl.close(); - console.log('electrum gettransaction array ==>'); - console.log(_rawtx); - - const successObj = { - msg: 'success', - result: { - listtransactions: _rawtx, - }, + }); + } else { + const _parsedTx = { + network: 'cant parse', + format: 'cant parse', + inputs: 'cant parse', + outputs: 'cant parse', + height: transaction.height, + timestamp: 'cant get block info', + confirmations: currentHeight - transaction.height, }; - - res.end(JSON.stringify(successObj)); + const formattedTx = shepherd.parseTransactionAddresses(_parsedTx, req.query.address, network); + _rawtx.push(formattedTx); + resolve(true); } - } + }); }); + })) + .then(promiseResult => { + ecl.close(); + + const successObj = { + msg: 'success', + result: _rawtx, + }; + + res.end(JSON.stringify(successObj)); }); + } else { + const successObj = { + msg: 'success', + result: [], + }; + + res.end(JSON.stringify(successObj)); } - } else { - const successObj = { - msg: 'success', - result: { - listtransactions: [], - }, - }; + }); + } else { + const successObj = { + msg: 'error', + result: 'cant get current height', + }; - res.end(JSON.stringify(successObj)); - } - }); + res.end(JSON.stringify(successObj)); + } }); } }); @@ -1388,7 +1391,7 @@ shepherd.buildSignedTx = function(sendTo, changeAddress, wif, network, utxo, cha let key = bitcoinJS.ECPair.fromWIF(wif, shepherd.getNetworkData(network)); let tx = new bitcoinJS.TransactionBuilder(shepherd.getNetworkData(network)); - console.log(`buildSignedTx priv key ${wif}`); + // console.log(`buildSignedTx priv key ${wif}`); console.log(`buildSignedTx pub key ${key.getAddress().toString()}`); // console.log('buildSignedTx std tx fee ' + electrumServers[network].txfee); @@ -1701,104 +1704,117 @@ shepherd.listunspent = function(ecl, address, network, full, verify) { ecl.blockchainNumblocksSubscribe() .then((currentHeight) => { - // filter out unconfirmed utxos - for (let i = 0; i < _utxoJSON.length; i++) { - if (Number(currentHeight) - Number(_utxoJSON[i].height) !== 0) { - _utxo.push(_utxoJSON[i]); + if (currentHeight && + Number(currentHeight) > 0) { + // filter out unconfirmed utxos + for (let i = 0; i < _utxoJSON.length; i++) { + if (Number(currentHeight) - Number(_utxoJSON[i].height) !== 0) { + _utxo.push(_utxoJSON[i]); + } } - } - - if (!_utxo.length) { // no confirmed utxo - resolve('no valid utxo'); - } else { - Promise.all(_utxo.map((_utxoItem, index) => { - return new Promise((resolve, reject) => { - ecl.blockchainTransactionGet(_utxoItem['tx_hash']) - .then((_rawtxJSON) => { - console.log('electrum gettransaction ==>'); - console.log(index + ' | ' + (_rawtxJSON.length - 1)); - console.log(_rawtxJSON); - // decode tx - const _network = shepherd.getNetworkData(network); - const decodedTx = electrumJSTxDecoder(_rawtxJSON, _network); - - console.log('decoded tx =>'); - console.log(decodedTx); - - if (!decodedTx) { - _atLeastOneDecodeTxFailed = true; - resolve('cant decode tx'); - } else { - if (network === 'komodo') { - let interest = 0; + if (!_utxo.length) { // no confirmed utxo + resolve('no valid utxo'); + } else { + Promise.all(_utxo.map((_utxoItem, index) => { + return new Promise((resolve, reject) => { + ecl.blockchainTransactionGet(_utxoItem['tx_hash']) + .then((_rawtxJSON) => { + console.log('electrum gettransaction ==>'); + console.log(index + ' | ' + (_rawtxJSON.length - 1)); + console.log(_rawtxJSON); + + // decode tx + const _network = shepherd.getNetworkData(network); + const decodedTx = electrumJSTxDecoder(_rawtxJSON, _network); + + console.log('decoded tx =>'); + console.log(decodedTx); + + if (!decodedTx) { + _atLeastOneDecodeTxFailed = true; + resolve('cant decode tx'); + } else { + if (network === 'komodo') { + let interest = 0; - if (Number(_utxoItem.value) * 0.00000001 >= 10 && - decodedTx.format.locktime > 0) { - interest = shepherd.kdmCalcInterest(decodedTx.format.locktime, _utxoItem.value); - } + if (Number(_utxoItem.value) * 0.00000001 >= 10 && + decodedTx.format.locktime > 0) { + interest = shepherd.kdmCalcInterest(decodedTx.format.locktime, _utxoItem.value); + } - let _resolveObj = { - txid: _utxoItem['tx_hash'], - vout: _utxoItem['tx_pos'], - address, - amount: Number(_utxoItem.value) * 0.00000001, - amountSats: _utxoItem.value, - interest: interest, - interestSats: Math.floor(interest * 100000000), - confirmations: currentHeight - _utxoItem.height, - spendable: true, - verified: false, - }; + let _resolveObj = { + txid: _utxoItem['tx_hash'], + vout: _utxoItem['tx_pos'], + address, + amount: Number(_utxoItem.value) * 0.00000001, + amountSats: _utxoItem.value, + interest: interest, + interestSats: Math.floor(interest * 100000000), + confirmations: currentHeight - _utxoItem.height, + spendable: true, + verified: false, + }; - // merkle root verification agains another electrum server - if (verify) { - shepherd.verifyMerkleByCoin(shepherd.findCoinName(network), _utxoItem['tx_hash'], _utxoItem.height) - .then((verifyMerkleRes) => { - _resolveObj.verified = verifyMerkleRes; + // merkle root verification agains another electrum server + if (verify) { + shepherd.verifyMerkleByCoin(shepherd.findCoinName(network), _utxoItem['tx_hash'], _utxoItem.height) + .then((verifyMerkleRes) => { + if (verifyMerkleRes && verifyMerkleRes === CONNECTION_ERROR_OR_INCOMPLETE_DATA) { + verifyMerkleRes = false; + } + + _resolveObj.verified = verifyMerkleRes; + resolve(_resolveObj); + }); + } else { resolve(_resolveObj); - }); + } } else { - resolve(_resolveObj); - } - } else { - let _resolveObj = { - txid: _utxoItem['tx_hash'], - vout: _utxoItem['tx_pos'], - address, - amount: Number(_utxoItem.value) * 0.00000001, - amountSats: _utxoItem.value, - confirmations: currentHeight - _utxoItem.height, - spendable: true, - verified: false, - }; + let _resolveObj = { + txid: _utxoItem['tx_hash'], + vout: _utxoItem['tx_pos'], + address, + amount: Number(_utxoItem.value) * 0.00000001, + amountSats: _utxoItem.value, + confirmations: currentHeight - _utxoItem.height, + spendable: true, + verified: false, + }; - // merkle root verification agains another electrum server - if (verify) { - shepherd.verifyMerkleByCoin(shepherd.findCoinName(network), _utxoItem['tx_hash'], _utxoItem.height) - .then((verifyMerkleRes) => { - _resolveObj.verified = verifyMerkleRes; + // merkle root verification agains another electrum server + if (verify) { + shepherd.verifyMerkleByCoin(shepherd.findCoinName(network), _utxoItem['tx_hash'], _utxoItem.height) + .then((verifyMerkleRes) => { + if (verifyMerkleRes && verifyMerkleRes === CONNECTION_ERROR_OR_INCOMPLETE_DATA) { + verifyMerkleRes = false; + } + + _resolveObj.verified = verifyMerkleRes; + resolve(_resolveObj); + }); + } else { resolve(_resolveObj); - }); - } else { - resolve(_resolveObj); + } } } - } + }); }); - }); - })) - .then(promiseResult => { - ecl.close(); + })) + .then(promiseResult => { + ecl.close(); - if (!_atLeastOneDecodeTxFailed) { - console.log(promiseResult); - resolve(promiseResult); - } else { - console.log('listunspent error, cant decode tx(s)'); - resolve('decode error'); - } - }); + if (!_atLeastOneDecodeTxFailed) { + console.log(promiseResult); + resolve(promiseResult); + } else { + console.log('listunspent error, cant decode tx(s)'); + resolve('decode error'); + } + }); + } + } else { + resolve('cant get current height'); } }); } else {