From 2be4cf2d35c8d117e98f701593e3fbf79207db52 Mon Sep 17 00:00:00 2001 From: pbca26 Date: Tue, 6 Feb 2018 19:01:58 +0300 Subject: [PATCH] notary nodes elections --- main.js | 7 + private/kmdcli.js | 126 ----------------- private/mainmenu.js | 34 +---- routes/shepherd.js | 3 + routes/shepherd/elections.js | 200 +++++++++++++++++++++++++++ routes/shepherd/electrum/createtx.js | 17 ++- 6 files changed, 230 insertions(+), 157 deletions(-) delete mode 100644 private/kmdcli.js create mode 100644 routes/shepherd/elections.js diff --git a/main.js b/main.js index f40b809..cbfbd55 100644 --- a/main.js +++ b/main.js @@ -284,6 +284,13 @@ function createWindow(status, hideLoadingWindow) { firstLoginPH: null, secondaryLoginPH: null, }; + + for (let i = 0; i < process.argv.length; i++) { + if (process.argv[i].indexOf('nvote') > -1) { + console.log(`notary node elections chain ${process.argv[i].replace('nvote=', '')}`); + mainWindow.nnVoteChain = process.argv[i].replace('nvote=', ''); + } + } } else { mainWindow = new BrowserWindow({ width: 500, diff --git a/private/kmdcli.js b/private/kmdcli.js deleted file mode 100644 index cff8afd..0000000 --- a/private/kmdcli.js +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (c) 2015 Satinderjit Singh - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - - /* - * Agama komodo-cli paths - * - */ - -var child_process = require('child_process'), - path = require('path'), - os = require('os'); - -if (os.platform() === 'darwin') { - var komodocliBin = path.join(__dirname, '../assets/bin/osx/komodo-cli'), - zcashcliBin = '/Applications/ZCashSwingWalletUI.app/Contents/MacOS/zcash-cli'; -} - -if (os.platform() === 'linux') { - var komodocliBin = path.join(__dirname, '../assets/bin/linux64/komodo-cli'); -} - -if (os.platform() === 'win32') { - var komodocliBin = path.join(__dirname, '../assets/bin/win64/komodo-cli.exe'), - komodocliBin = path.normalize(komodocliBin); -} - -console.log(komodocliBin) - -/** - * The **komodo-cli** command is used to get komodo api calls answer. - * - * @private - * @category kmdcli - * - */ -var kmdcli = module.exports = { - exec: child_process.exec, - command: command -}; - -/** - * Parses komodo-cli commands. - * - * @private - * @static - * @category kmdcli - * @param {function} callback The callback function. - * - */ -function parse_kmdcli_commands(callback) { - return function(error, stdout, stderr) { - if (error) callback(error, stderr); - else callback(error, stdout); - //console.log(stdout) - }; -} - -/** - * Parses komodo-cli commands. - * - * @private - * @static - * @category kmdcli - * @param {function} callback The callback function. - * @example - * - * var kmdcli = require('./kmdcli'); - * - * kmdcli.command('getinfo', function(err, command) { - * console.log(command); - * }); - * - * // => - * { - * "version" : 1000550, - * "protocolversion" : 170002, - * "notarized" : 254740, - * "notarizedhash" : "01f4f1c46662ccca2e7fa9e7e38d4d2e4ced4402fa0f4fc116b8f004bb8cf272", - * "notarizedtxid" : "2b16e47a176f8c1886ca0268243f9b96f8b2db466ea26ae99873d5224bbf80b6", - * "walletversion" : 60000, - * "balance" : 32632.46167742, - * "interest" : 0.00478671, - * "blocks" : 254791, - * "longestchain" : 254791, - * "timeoffset" : 0, - * "tiptime" : 1490815616, - * "connections" : 8, - * "proxy" : "", - * "difficulty" : 707836.56791394, - * "testnet" : false, - * "keypoololdest" : 1482746526, - * "keypoolsize" : 101, - * "paytxfee" : 0.00000000, - * "relayfee" : 0.00001000, - * "errors" : "WARNING: check your network connection, 157 blocks received in the last 4 hours (240 expected)", - * "notaryid" : -1, - * "pubkey" : "000000000000000000000000000000000000000000000000000000000000000000" - * } - * - */ -function command(kmd_command, callback) { - if (callback) { - return this.exec(komodocliBin + " " + kmd_command, - parse_kmdcli_commands(callback)); - } -} diff --git a/private/mainmenu.js b/private/mainmenu.js index dd8780d..5d0981d 100644 --- a/private/mainmenu.js +++ b/private/mainmenu.js @@ -86,42 +86,18 @@ const template = [ } ] }, - /*{ + { role: 'help', - label: 'Support', + label: 'Debug', submenu: [ { - label: 'Supernet.org', - click () { - if (process.platform === 'linux') { - require('child_process').exec('xdg-open http://support.supernet.org'); - } else { - shell.openExternal('http://support.supernet.org'); - } - } - }, - { - label: 'Slack', + label: 'Reset settings', click () { - if (process.platform === 'linux') { - require('child_process').exec('xdg-open https://sprnt.slack.com/messages/support'); - } else { - shell.openExternal('https://sprnt.slack.com/messages/support'); - } + console.log(window.require('electron').remote.getCurrentWindow().appConfig); } }, - { - label: 'Github', - click () { - if (process.platform === 'linux') { - require('child_process').exec('xdg-open https://github.com/SuperNETorg/iguana/issues'); - } else { - shell.openExternal('https://github.com/SuperNETorg/iguana/issues'); - } - } - } ] - }*/ + } ] if (process.platform === 'darwin') { diff --git a/routes/shepherd.js b/routes/shepherd.js index 8c3c0d8..7e693ac 100644 --- a/routes/shepherd.js +++ b/routes/shepherd.js @@ -126,6 +126,9 @@ shepherd = require('./shepherd/auth.js')(shepherd); shepherd = require('./shepherd/coins.js')(shepherd); shepherd = require('./shepherd/coindWalletKeys.js')(shepherd); +// elections +shepherd = require('./shepherd/elections.js')(shepherd); + // explorer // shepherd = require('./shepherd/explorer/overview.js')(shepherd); diff --git a/routes/shepherd/elections.js b/routes/shepherd/elections.js new file mode 100644 index 0000000..6076918 --- /dev/null +++ b/routes/shepherd/elections.js @@ -0,0 +1,200 @@ +module.exports = (shepherd) => { + shepherd.elections = {}; + + shepherd.hex2str = (hexx) => { + const hex = hexx.toString(); //force conversion + let str = ''; + + for (let i = 0; i < hex.length; i += 2) { + str += String.fromCharCode(parseInt(hex.substr(i, 2), 16)); + } + + return str; + }; + + shepherd.post('/elections/status', (req, res, next) => { + if (shepherd.checkToken(req.body.token)) { + const successObj = { + msg: 'success', + result: shepherd.elections.pub ? shepherd.elections.pub : 'unauth', + }; + + res.end(JSON.stringify(successObj)); + } else { + const errorObj = { + msg: 'error', + result: 'unauthorized access', + }; + + res.end(JSON.stringify(errorObj)); + } + }); + + shepherd.post('/elections/login', (req, res, next) => { + if (shepherd.checkToken(req.body.token)) { + let keys = shepherd.seedToWif(req.body.seed, req.body.network, req.body.iguana); + + shepherd.elections = { + priv: keys.priv, + pub: keys.pub, + }; + + const successObj = { + msg: 'success', + result: keys.pub, + }; + + res.end(JSON.stringify(successObj)); + } else { + const errorObj = { + msg: 'error', + result: 'unauthorized access', + }; + + res.end(JSON.stringify(errorObj)); + } + }); + + shepherd.post('/elections/logout', (req, res, next) => { + if (shepherd.checkToken(req.body.token)) { + shepherd.elections = {}; + + const successObj = { + msg: 'success', + result: true, + }; + + res.end(JSON.stringify(successObj)); + } else { + const errorObj = { + msg: 'error', + result: 'unauthorized access', + }; + + res.end(JSON.stringify(errorObj)); + } + }); + + shepherd.get('/elections/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 type = req.query.type; + const _address = req.query.address; + + shepherd.log('electrum elections listtransactions ==>', true); + + const MAX_TX = req.query.maxlength || 10; + ecl.connect(); + + ecl.blockchainAddressGetHistory(_address) + .then((json) => { + if (json && + json.length) { + json = shepherd.sortTransactions(json); + json = json.length > MAX_TX ? json.slice(0, MAX_TX) : json; + let _rawtx = []; + + shepherd.log(json.length, true); + + shepherd.Promise.all(json.map((transaction, index) => { + return new shepherd.Promise((resolve, reject) => { + ecl.blockchainBlockGetHeader(transaction.height) + .then((blockInfo) => { + if (blockInfo && + blockInfo.timestamp) { + ecl.blockchainTransactionGet(transaction['tx_hash']) + .then((_rawtxJSON) => { + shepherd.log('electrum gettransaction ==>', true); + shepherd.log((index + ' | ' + (_rawtxJSON.length - 1)), true); + shepherd.log(_rawtxJSON, true); + + // decode tx + const _network = shepherd.getNetworkData(network); + const decodedTx = shepherd.electrumJSTxDecoder(_rawtxJSON, network, _network); + let _res = {}; + let _opreturnFound = false; + let _region; + + for (let i = 0; i < decodedTx.outputs.length; i++) { + if (decodedTx.outputs[i].scriptPubKey.asm.indexOf('OP_RETURN') > -1) { + _opreturnFound = true; + _region = shepherd.hex2str(decodedTx.outputs[i].scriptPubKey.hex.substr(4, decodedTx.outputs[i].scriptPubKey.hex.length)); + shepherd.log(`found opreturn tag ${_region}`); + break; + } + } + + if (_opreturnFound) { + let _candidate = {}; + + for (let i = 0; i < decodedTx.outputs.length; i++) { + if (type === 'voter' && + decodedTx.outputs[i].scriptPubKey.addresses && + decodedTx.outputs[i].scriptPubKey.addresses[0] && + decodedTx.outputs[i].scriptPubKey.addresses[0] !== _address) { + shepherd.log(`i voted ${decodedTx.outputs[i].value} for ${decodedTx.outputs[i].scriptPubKey.addresses[0]}`); + _rawtx.push({ + address: decodedTx.outputs[i].scriptPubKey.addresses[0], + amount: decodedTx.outputs[i].value, + region: _region, + timestamp: blockInfo.timestamp, + }); + } + + if (type === 'candidate') { + if (decodedTx.outputs[i].scriptPubKey.addresses[0] === _address) { + _candidate.amount = decodedTx.outputs[i].value; + } else if (decodedTx.outputs[i].scriptPubKey.addresses[0] !== _address && decodedTx.outputs[i].scriptPubKey.asm.indexOf('OP_RETURN') === -1) { + _candidate.address = decodedTx.outputs[i].scriptPubKey.addresses[0]; + _candidate.region = _region; + _candidate.timestamp = blockInfo.timestamp; + } + + if (i === decodedTx.outputs.length - 1) { + shepherd.log(`i received ${_candidate.amount} from ${_candidate.address}`); + _rawtx.push(_candidate); + } + } + } + } + + resolve(true); + }); + } else { + resolve(false); + } + }); + }); + })) + .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 errorObj = { + msg: 'error', + result: 'unauthorized access', + }; + + res.end(JSON.stringify(errorObj)); + } + }); + + return shepherd; +} \ No newline at end of file diff --git a/routes/shepherd/electrum/createtx.js b/routes/shepherd/electrum/createtx.js index 70886c0..64d1b6b 100644 --- a/routes/shepherd/electrum/createtx.js +++ b/routes/shepherd/electrum/createtx.js @@ -54,7 +54,7 @@ module.exports = (shepherd) => { } // single sig - shepherd.buildSignedTx = (sendTo, changeAddress, wif, network, utxo, changeValue, spendValue) => { + shepherd.buildSignedTx = (sendTo, changeAddress, wif, network, utxo, changeValue, spendValue, opreturn) => { let key = shepherd.isZcash(network) ? bitcoinZcash.ECPair.fromWIF(wif, shepherd.getNetworkData(network)) : shepherd.bitcoinJS.ECPair.fromWIF(wif, shepherd.getNetworkData(network)); let tx; @@ -89,6 +89,13 @@ module.exports = (shepherd) => { } } + if (opreturn) { + console.log(`opreturn ${opreturn}`); + const data = Buffer.from(opreturn, 'utf8'); + const dataScript = shepherd.bitcoinJS.script.nullData.output.encode(data); + tx.addOutput(dataScript, 1000); + } + if (network === 'komodo' || network === 'KMD') { const _locktime = Math.floor(Date.now() / 1000) - 777; @@ -195,6 +202,7 @@ module.exports = (shepherd) => { const outputAddress = req.query.address; const changeAddress = req.query.change; const push = req.query.push; + const opreturn = req.query.opreturn; let fee = shepherd.electrumServers[network].txfee; let value = Number(req.query.value); let wif = req.query.wif; @@ -203,6 +211,10 @@ module.exports = (shepherd) => { wif = shepherd.electrumKeys[req.query.coin].priv; } + if (req.query.vote) { + wif = shepherd.elections.priv; + } + shepherd.log('electrum createrawtx =>', true); ecl.connect(); @@ -415,7 +427,8 @@ module.exports = (shepherd) => { network, inputs, _change, - value + value, + opreturn ); } }