diff --git a/package.json b/package.json index 1805a31..4e54ee5 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "author": "SuperNET Team", "license": "MIT", "devDependencies": { - "electron": "1.8.3" + "electron": "1.8.4" }, "dependencies": { "adm-zip": "^0.4.7", @@ -40,7 +40,7 @@ "body-parser": "^1.15.2", "buffer-reverse": "^1.0.1", "coinselect": "github:bitcoinjs/coinselect", - "electron": "1.8.3", + "electron": "1.8.4", "express": "^4.14.0", "fix-path": "^2.1.0", "fs-extra": "^4.0.2", diff --git a/routes/electrumjs/electrumServers.js b/routes/electrumjs/electrumServers.js index 18c14c9..736a143 100644 --- a/routes/electrumjs/electrumServers.js +++ b/routes/electrumjs/electrumServers.js @@ -548,6 +548,15 @@ let electrumServers = { 's2.qtum.info:50001' ], }, + aby: { + address: 'http://explorer.artbyte.me', + port: 'none', + proto: 'insight', + insightRawApi: false, + txfee: 100000, + abbr: 'ABY', + serverList: 'none', + }, }; electrumServers.crw = electrumServers.crown; diff --git a/routes/electrumjs/electrumjs.networks.js b/routes/electrumjs/electrumjs.networks.js index 217eae1..5c57158 100644 --- a/routes/electrumjs/electrumjs.networks.js +++ b/routes/electrumjs/electrumjs.networks.js @@ -374,6 +374,18 @@ networks.grs = { // fails to gen a proper addr dustThreshold: 1000, }; +networks.aby = { + messagePrefix: '\x19Crown Signed Message:\n', + bip32: { + public: 0x0488b21e, + private: 0x0488ade4, + }, + pubKeyHash: 0x17, + scriptHash: 0x5, + wif: 0x97, + dustThreshold: 1000, +}; + networks.btc = networks.bitcoin; networks.crw = networks.crown; networks.dgb = networks.digibyte; diff --git a/routes/shepherd.js b/routes/shepherd.js index 421c1ec..f6ec72d 100644 --- a/routes/shepherd.js +++ b/routes/shepherd.js @@ -104,6 +104,7 @@ shepherd = require('./shepherd/electrum/interest.js')(shepherd); shepherd = require('./shepherd/electrum/listunspent.js')(shepherd); shepherd = require('./shepherd/electrum/estimate.js')(shepherd); shepherd = require('./shepherd/electrum/btcFees.js')(shepherd); +shepherd = require('./shepherd/electrum/insight.js')(shepherd); // dex shepherd = require('./shepherd/dex/coind.js')(shepherd); diff --git a/routes/shepherd/electrum/balance.js b/routes/shepherd/electrum/balance.js index 9c4aa25..3d596b2 100644 --- a/routes/shepherd/electrum/balance.js +++ b/routes/shepherd/electrum/balance.js @@ -2,7 +2,7 @@ module.exports = (shepherd) => { shepherd.get('/electrum/getbalance', (req, res, next) => { if (shepherd.checkToken(req.query.token)) { const network = req.query.network || shepherd.findNetworkObj(req.query.coin); - const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls + const ecl = shepherd.electrumServers[network].proto === 'insight' ? shepherd.insightJSCore(shepherd.electrumServers[network]) : new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls shepherd.log('electrum getbalance =>', true); @@ -14,7 +14,7 @@ module.exports = (shepherd) => { json.hasOwnProperty('unconfirmed')) { if (network === 'komodo') { ecl.blockchainAddressListunspent(req.query.address) - .then((utxoList) => { + .then((utxoList) => { if (utxoList && utxoList.length) { // filter out < 10 KMD amounts @@ -45,7 +45,7 @@ module.exports = (shepherd) => { // decode tx const _network = shepherd.getNetworkData(network); - const decodedTx = shepherd.electrumJSTxDecoder(_rawtxJSON, network, _network); + const decodedTx = shepherd.electrumJSTxDecoder(_rawtxJSON, network, _network, shepherd.electrumServers[network].proto === 'insight'); if (decodedTx && decodedTx.format && @@ -81,7 +81,7 @@ module.exports = (shepherd) => { }); } else { ecl.close(); - + const successObj = { msg: 'success', result: { @@ -100,7 +100,7 @@ module.exports = (shepherd) => { } } else { ecl.close(); - + const successObj = { msg: 'success', result: { @@ -137,7 +137,7 @@ module.exports = (shepherd) => { } } else { ecl.close(); - + const successObj = { msg: 'error', result: shepherd.CONNECTION_ERROR_OR_INCOMPLETE_DATA, diff --git a/routes/shepherd/electrum/btcFees.js b/routes/shepherd/electrum/btcFees.js index 525ea04..25f25be 100644 --- a/routes/shepherd/electrum/btcFees.js +++ b/routes/shepherd/electrum/btcFees.js @@ -92,7 +92,7 @@ module.exports = (shepherd) => { }); }); } else { - console.log(`btcfees, use cache`); + shepherd.log('btcfees, use cache', true); const successObj = { msg: 'success', diff --git a/routes/shepherd/electrum/insight.js b/routes/shepherd/electrum/insight.js new file mode 100644 index 0000000..c5b4122 --- /dev/null +++ b/routes/shepherd/electrum/insight.js @@ -0,0 +1,201 @@ +const request = require('request'); + +// abstraction layer to communicate with insight explorers + +module.exports = (shepherd) => { + /*shepherd.httpReq = (url, type) => { + + };*/ + shepherd.insightJSCoreActiveCoin = {}; + + shepherd.insightJSCore = (electrumServer) => { + shepherd.log('insight =>'); + shepherd.log(electrumServer, true); + + if (electrumServer) { + shepherd.insightJSCoreActiveCoin = electrumServer; + } + + return { + insight: true, + connect: () => { + shepherd.log('insight fake connect', true); + }, + close: () => { + shepherd.log('insight fake close', true); + }, + blockchainAddressGetBalance: (address) => { + shepherd.log('insight blockchainAddressGetBalance', true); + + return new shepherd.Promise((resolve, reject) => { + let options = { + url: `${shepherd.insightJSCoreActiveCoin.address}/api/addr/${address}/utxo`, + method: 'GET', + }; + + console.log(`${shepherd.insightJSCoreActiveCoin.address}/api/addr/${address}/utxo`); + + // send back body on both success and error + // this bit replicates iguana core's behaviour + request(options, (error, response, body) => { + if (response && + response.statusCode && + response.statusCode === 200) { + try { + const _parsedBody = JSON.parse(body); + console.log(_parsedBody); + if (_parsedBody) { + let _balance = 0; + + for (let i = 0; i < _parsedBody.length; i++) { + _balance += Number(_parsedBody[i].amount); + } + + resolve({ + confirmed: _balance * 100000000, + unconfirmed: 0, + }); + } + shepherd.log(`insight blockchainAddressGetBalance ${address}`); + } catch (e) { + shepherd.log(`parse error insight blockchainAddressGetBalance ${address}`, true); + } + } else { + shepherd.log(`req error insight blockchainAddressGetBalance ${address}`, true); + } + }); + }); + }, + blockchainAddressListunspent: (address) => { + shepherd.log('insight blockchainAddressListunspent', true); + + return new shepherd.Promise((resolve, reject) => { + let options = { + url: `${shepherd.insightJSCoreActiveCoin.address}/api/addr/${address}/utxo`, + method: 'GET', + }; + + console.log(`${shepherd.insightJSCoreActiveCoin.address}/api/addr/${address}/utxo`); + + // send back body on both success and error + // this bit replicates iguana core's behaviour + request(options, (error, response, body) => { + if (response && + response.statusCode && + response.statusCode === 200) { + try { + const _parsedBody = JSON.parse(body); + console.log(_parsedBody); + + if (_parsedBody) { + let _utxos = []; + + for (let i = 0; i < _parsedBody.length; i++) { + _utxos.push({ + txid: _parsedBody[i].txid, + vout: _parsedBody[i].vout, + address: _parsedBody[i].address, + amount: Number(_parsedBody[i].amount), + amountSats: Number(_parsedBody[i].amount) * 100000000, + confirmations: _parsedBody[i].confirmations, + spendable: true, + verified: false, + }); + } + + resolve(_utxos); + } + shepherd.log(`insight blockchainAddressListunspent ${address}`); + } catch (e) { + shepherd.log(`parse error insight blockchainAddressListunspent ${address}`, true); + } + } else { + shepherd.log(`req error insight blockchainAddressListunspent ${address}`, true); + } + }); + }); + }, + blockchainAddressGetHistory: (address) => { + shepherd.log('insight blockchainAddressGetHistory', true); + + return new shepherd.Promise((resolve, reject) => { + let options = { + url: `${shepherd.insightJSCoreActiveCoin.address}/api/txs/?address=${address}`, + method: 'GET', + }; + + console.log(`${shepherd.insightJSCoreActiveCoin.address}/api/txs/?address=${address}`); + + // send back body on both success and error + // this bit replicates iguana core's behaviour + request(options, (error, response, body) => { + if (response && + response.statusCode && + response.statusCode === 200) { + try { + const _parsedBody = JSON.parse(body); + console.log(_parsedBody.txs); + + if (_parsedBody && + _parsedBody.txs) { + const _txs = _parsedBody.txs; + let txs = []; + + for (let i = 0; i < _txs.length; i++) { + const _parsedTx = { + format: { + txid: _txs[i].txid, + version: _txs[i].version, + locktime: _txs[i].locktime, + }, + inputs: _txs[i].vin, + outputs: _txs[i].vout, + timestamp: _txs[i].time, + confirmations: _txs[i].confirmations, + }; + + const formattedTx = shepherd.parseTransactionAddresses(_parsedTx, address, shepherd.insightJSCoreActiveCoin.abbr.toLowerCase()); + + if (formattedTx.type) { + formattedTx.blocktime = _parsedTx.timestamp; + formattedTx.timereceived = _parsedTx.timestamp; + formattedTx.hex = 'N/A'; + formattedTx.inputs = _parsedTx.inputs; + formattedTx.outputs = _parsedTx.outputs; + formattedTx.locktime = _parsedTx.format.locktime; + txs.push(formattedTx); + } else { + formattedTx[0].blocktime = _parsedTx.timestamp; + formattedTx[0].timereceived = _parsedTx.timestamp; + formattedTx[0].hex = 'N/A'; + formattedTx[0].inputs = _parsedTx.inputs; + formattedTx[0].outputs = _parsedTx.outputs; + formattedTx[0].locktime = _parsedTx.format.locktime; + formattedTx[1].blocktime = _parsedTx.timestamp; + formattedTx[1].timereceived = _parsedTx.timestamp; + formattedTx[1].hex = 'N/A'; + formattedTx[1].inputs = _parsedTx.inputs; + formattedTx[1].outputs = _parsedTx.outputs; + formattedTx[1].locktime = _parsedTx.format.locktime; + txs.push(formattedTx[0]); + txs.push(formattedTx[1]); + } + } + + resolve(txs); + } + shepherd.log(`insight blockchainAddressGetHistory ${address}`); + } catch (e) { + shepherd.log(`parse error insight blockchainAddressGetHistory ${address}`, true); + } + } else { + shepherd.log(`req error insight blockchainAddressGetHistory ${address}`, true); + } + }); + }); + }, + }; + }; + + return shepherd; +} \ No newline at end of file diff --git a/routes/shepherd/electrum/listunspent.js b/routes/shepherd/electrum/listunspent.js index 0e662a7..ace8293 100644 --- a/routes/shepherd/electrum/listunspent.js +++ b/routes/shepherd/electrum/listunspent.js @@ -4,7 +4,7 @@ module.exports = (shepherd) => { shepherd.listunspent = (ecl, address, network, full, verify) => { let _atLeastOneDecodeTxFailed = false; - if (full) { + if (full && !ecl.insight) { return new shepherd.Promise((resolve, reject) => { ecl.connect(); ecl.blockchainAddressListunspent(address) @@ -161,7 +161,7 @@ module.exports = (shepherd) => { shepherd.get('/electrum/listunspent', (req, res, next) => { if (shepherd.checkToken(req.query.token)) { const network = req.query.network || shepherd.findNetworkObj(req.query.coin); - const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls + const ecl = shepherd.electrumServers[network].proto === 'insight' ? shepherd.insightJSCore(shepherd.electrumServers[network]) : new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls if (req.query.full && req.query.full === 'true') { diff --git a/routes/shepherd/electrum/network.js b/routes/shepherd/electrum/network.js index adceeff..6774184 100644 --- a/routes/shepherd/electrum/network.js +++ b/routes/shepherd/electrum/network.js @@ -31,11 +31,13 @@ module.exports = (shepherd) => { } }; - shepherd.electrumJSTxDecoder = (rawtx, networkName, network) => { + shepherd.electrumJSTxDecoder = (rawtx, networkName, network, insight) => { if (shepherd.isZcash(networkName)) { return txDecoder.zcash(rawtx, network); } else if (shepherd.isPos(networkName)) { return txDecoder.pos(rawtx, network); + } else if (insight) { + console.log('insight decoder'); } else { return txDecoder.default(rawtx, network); } diff --git a/routes/shepherd/electrum/transactions.js b/routes/shepherd/electrum/transactions.js index cd15b05..da76425 100644 --- a/routes/shepherd/electrum/transactions.js +++ b/routes/shepherd/electrum/transactions.js @@ -1,39 +1,39 @@ const async = require('async'); module.exports = (shepherd) => { - shepherd.sortTransactions = (transactions) => { + shepherd.sortTransactions = (transactions, sortBy) => { return transactions.sort((b, a) => { - if (a.height < b.height) { + if (a[sortBy ? sortBy : 'height'] < b[sortBy ? sortBy : 'height']) { return -1; } - if (a.height > b.height) { + if (a[sortBy ? sortBy : 'height'] > b[sortBy ? sortBy : 'height']) { return 1; } return 0; }); } - + shepherd.getTransaction = (txid, network, ecl) => { return new shepherd.Promise((resolve, reject) => { if (!shepherd.electrumCache[network]) { shepherd.electrumCache[network] = {}; } if (!shepherd.electrumCache[network].tx) { - shepherd.electrumCache[network]['tx'] = {}; + shepherd.electrumCache[network]['tx'] = {}; } if (!shepherd.electrumCache[network].tx[txid]) { shepherd.log(`electrum raw input tx ${txid}`, true); - + ecl.blockchainTransactionGet(txid) .then((_rawtxJSON) => { shepherd.electrumCache[network].tx[txid] = _rawtxJSON; resolve(_rawtxJSON); }); } else { - shepherd.log(`electrum cached raw input tx ${txid}`, true); + shepherd.log(`electrum cached raw input tx ${txid}`, true); resolve(shepherd.electrumCache[network].tx[txid]); } }); @@ -45,19 +45,19 @@ module.exports = (shepherd) => { shepherd.electrumCache[network] = {}; } if (!shepherd.electrumCache[network].blockHeader) { - shepherd.electrumCache[network]['blockHeader'] = {}; + shepherd.electrumCache[network]['blockHeader'] = {}; } if (!shepherd.electrumCache[network].blockHeader[height]) { shepherd.log(`electrum raw block ${height}`, true); - + ecl.blockchainBlockGetHeader(height) .then((_rawtxJSON) => { shepherd.electrumCache[network].blockHeader[height] = _rawtxJSON; resolve(_rawtxJSON); }); } else { - shepherd.log(`electrum cached raw block ${height}`, true); + shepherd.log(`electrum cached raw block ${height}`, true); resolve(shepherd.electrumCache[network].blockHeader[height]); } }); @@ -66,18 +66,18 @@ module.exports = (shepherd) => { shepherd.get('/electrum/listtransactions', (req, res, next) => { if (shepherd.checkToken(req.query.token)) { const network = req.query.network || shepherd.findNetworkObj(req.query.coin); - const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls + const ecl = shepherd.electrumServers[network].proto === 'insight' ? shepherd.insightJSCore(shepherd.electrumServers[network]) : new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls shepherd.log('electrum listtransactions ==>', true); - if (!req.query.full) { + if (!req.query.full || ecl.insight) { ecl.connect(); ecl.blockchainAddressGetHistory(req.query.address) .then((json) => { ecl.close(); shepherd.log(json, true); - json = shepherd.sortTransactions(json); + json = shepherd.sortTransactions(json, 'timestamp'); const successObj = { msg: 'success', @@ -136,7 +136,7 @@ module.exports = (shepherd) => { async.eachOfSeries(decodedTx.inputs, (_decodedInput, ind2, callback2) => { function checkLoop() { index2++; - + if (index2 === decodedTx.inputs.length) { shepherd.log(`tx history decode inputs ${decodedTx.inputs.length} | ${index2} => main callback`, true); const _parsedTx = { @@ -182,7 +182,7 @@ module.exports = (shepherd) => { if (index === json.length) { ecl.close(); - + const successObj = { msg: 'success', result: _rawtx, @@ -194,7 +194,7 @@ module.exports = (shepherd) => { callback(); shepherd.log(`tx history main loop ${json.length} | ${index}`, true); } - callback2(); + callback2(); } if (_decodedInput.txid !== '0000000000000000000000000000000000000000000000000000000000000000') { @@ -229,15 +229,15 @@ module.exports = (shepherd) => { if (index === json.length) { ecl.close(); - + const successObj = { msg: 'success', result: _rawtx, }; - + res.end(JSON.stringify(successObj)); } else { - callback(); + callback(); } } }); @@ -257,12 +257,12 @@ module.exports = (shepherd) => { if (index === json.length) { ecl.close(); - + const successObj = { msg: 'success', result: _rawtx, }; - + res.end(JSON.stringify(successObj)); } else { callback(); diff --git a/version b/version index 8554d79..35c7958 100644 --- a/version +++ b/version @@ -1,3 +1,3 @@ -version=0.2.30c -type=c-beta -minversion=0.2.30 \ No newline at end of file +version=0.2.31 +type=beta +minversion=0.2.31 \ No newline at end of file diff --git a/version_build b/version_build index c81c2d4..6676242 100644 --- a/version_build +++ b/version_build @@ -1 +1 @@ -0.2.30c-beta \ No newline at end of file +0.2.31-beta \ No newline at end of file