Browse Source

zec, hush spv

pkg_automation_electrum
pbca26 7 years ago
parent
commit
b4e7effa57
  1. 4
      package.json
  2. 35
      routes/electrumjs/electrumServers.js
  3. 27
      routes/electrumjs/electrumjs.networks.js
  4. 120
      routes/electrumjs/electrumjs.txdecoder-2bytes.js
  5. 2
      routes/shepherd.js
  6. 85
      routes/shepherd/daemonControl.js
  7. 2
      routes/shepherd/electrum/balance.js
  8. 5
      routes/shepherd/electrum/createtx.js
  9. 34
      routes/shepherd/electrum/keys.js
  10. 5
      routes/shepherd/electrum/listunspent.js
  11. 4
      routes/shepherd/electrum/merkle.js
  12. 13
      routes/shepherd/electrum/network.js
  13. 6
      routes/shepherd/electrum/transactions.js

4
package.json

@ -28,14 +28,14 @@
"dependencies": {
"adm-zip": "^0.4.7",
"arch": "^2.1.0",
"bigi": "^1.4.2",
"bip39": "^2.4.0",
"bitcoinjs-lib": "^3.2.0",
"bitcoinforksjs-lib": "git://github.com/bitcoinjs/bitcoinjs-lib#opt-in-bitcoincash-sighash",
"bitcoinjs-lib": "^3.2.0",
"bitcoinjs-lib-zcash": "git://github.com/karel-3d/bitcoinjs-lib#zcash",
"bluebird": "^3.4.7",
"body-parser": "^1.15.2",
"buffer-reverse": "^1.0.1",
"coinkey": "^2.0.0",
"coinselect": "github:bitcoinjs/coinselect",
"electron": "1.6.5",
"express": "^4.14.0",

35
routes/electrumjs/electrumServers.js

@ -1,13 +1,4 @@
let electrumServers = {
/*zcash: { 2 bytes address fix is required
address: '173.212.225.176',
port: 50032,
proto: 'tcp',
serverList: [
'173.212.225.176:50032',
'136.243.45.140:50032'
],
},*/
coqui: { // !estimatefee
address: 'electrum1.cipig.net',
port: 10011,
@ -298,7 +289,7 @@ let electrumServers = {
'94.130.224.11:10052'
],
},
/*blk: { can't decode tx!
blk: { // can't decode tx!
address: 'electrum1.cipig.net',
port: 10054,
proto: 'tcp',
@ -308,7 +299,7 @@ let electrumServers = {
'electrum1.cipig.net:10054',
'electrum2.cipig.net:10054'
],
},*/
},
sib: {
address: 'electrum1.cipig.net',
port: 10050,
@ -353,6 +344,28 @@ let electrumServers = {
'electrum2.cipig.net:10053'
],
},
zec: {
address: '173.212.225.176',
port: 50032,
proto: 'tcp',
txfee: 10000,
abbr: 'ZEC',
serverList: [
'173.212.225.176:50032',
'136.243.45.140:50032'
],
},
hush: {
address: '173.212.225.176',
port: 50013,
proto: 'tcp',
txfee: 10000,
abbr: 'HUSH',
serverList: [
'173.212.225.176:50013',
'136.243.45.140:50013'
],
},
};
electrumServers.crw = electrumServers.crown;

27
routes/electrumjs/electrumjs.networks.js

@ -240,13 +240,20 @@ networks.sib = {
dustThreshold: 1000,
};
networks.btc = networks.bitcoin;
networks.crw = networks.crown;
networks.dgb = networks.digibyte;
networks.arg = networks.argentum;
/*networks.zcash = {
networks.zcash = {
messagePrefix: '\x19Zcash Signed Message:\n',
bip32: {
public: 0x0488b21e,
private: 0x05358394,
},
pubKeyHash: 0x1cb8,
scriptHash: 0x1cbd,
wif: 0x80,
dustThreshold: 1000,
};
networks.hush = {
messagePrefix: '\Hush Signed Message:\n',
bip32: {
public: 0x0488b21e,
private: 0x0488ade4,
@ -255,4 +262,10 @@ networks.arg = networks.argentum;
scriptHash: 0x1cbd,
wif: 0x80,
dustThreshold: 1000,
};*/
};
networks.btc = networks.bitcoin;
networks.crw = networks.crown;
networks.dgb = networks.digibyte;
networks.arg = networks.argentum;
networks.zec = networks.zcash;

120
routes/electrumjs/electrumjs.txdecoder-2bytes.js

@ -0,0 +1,120 @@
/*
MIT License
Copyright (c) 2017 Yuki Akiyama, SuperNET
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.
*/
var bitcoin = require('bitcoinjs-lib-zcash');
var decodeFormat = function(tx) {
var result = {
txid: tx.getId(),
version: tx.version,
locktime: tx.locktime,
};
return result;
}
var decodeInput = function(tx) {
var result = [];
tx.ins.forEach(function(input, n) {
var vin = {
txid: input.hash.reverse().toString('hex'),
n: input.index,
script: bitcoin.script.toASM(input.script),
sequence: input.sequence,
};
result.push(vin);
});
return result;
}
var decodeOutput = function(tx, network) {
var format = function(out, n, network) {
var vout = {
satoshi: out.value,
value: (1e-8 * out.value).toFixed(8),
n: n,
scriptPubKey: {
asm: bitcoin.script.toASM(out.script),
hex: out.script.toString('hex'),
type: bitcoin.script.classifyOutput(out.script),
addresses: [],
},
};
switch(vout.scriptPubKey.type) {
case 'pubkeyhash':
vout.scriptPubKey.addresses.push(bitcoin.address.fromOutputScript(out.script, network));
break;
case 'pubkey':
const pubKeyBuffer = new Buffer(vout.scriptPubKey.asm.split(' ')[0], 'hex');
vout.scriptPubKey.addresses.push(bitcoin.ECPair.fromPublicKeyBuffer(pubKeyBuffer, network).getAddress());
break;
case 'scripthash':
vout.scriptPubKey.addresses.push(bitcoin.address.fromOutputScript(out.script, network));
break;
}
return vout;
}
var result = [];
tx.outs.forEach(function(out, n) {
result.push(format(out, n, network));
});
return result;
}
var TxDecoder = module.exports = function(rawtx, network) {
try {
const _tx = bitcoin.Transaction.fromHex(rawtx);
return {
tx: _tx,
network: network,
format: decodeFormat(_tx),
inputs: decodeInput(_tx),
outputs: decodeOutput(_tx, network),
};
} catch (e) {
return false;
}
}
TxDecoder.prototype.decode = function() {
var result = {};
var self = this;
Object.keys(self.format).forEach(function(key) {
result[key] = self.format[key];
});
result.outputs = self.outputs;
return result;
}

2
routes/shepherd.js

@ -18,7 +18,6 @@ shepherd.Promise = require('bluebird');
shepherd.exec = require('child_process').exec;
shepherd.execFile = require('child_process').execFile;
shepherd.sha256 = require('sha256');
shepherd.CoinKey = require('coinkey');
shepherd.bitcoinJS = require('bitcoinjs-lib');
shepherd.coinSelect = require('coinselect');
shepherd.fixPath = require('fix-path');
@ -63,6 +62,7 @@ shepherd.electrumKeys = {};
shepherd.electrumJSCore = require('./electrumjs/electrumjs.core.js');
shepherd.electrumJSNetworks = require('./electrumjs/electrumjs.networks.js');
shepherd.electrumJSTxDecoder = require('./electrumjs/electrumjs.txdecoder.js');
shepherd.electrumJSTxDecoderZ = require('./electrumjs/electrumjs.txdecoder-2bytes.js');
shepherd.electrumServers = require('./electrumjs/electrumServers.js');
shepherd.CONNECTION_ERROR_OR_INCOMPLETE_DATA = 'connection error or incomplete data';

85
routes/shepherd/daemonControl.js

@ -11,6 +11,7 @@ const md5 = require('../md5.js');
module.exports = (shepherd) => {
const getConf = (flock, coind) => {
const _platform = os.platform();
let DaemonConfPath = '';
let nativeCoindDir;
@ -23,7 +24,7 @@ module.exports = (shepherd) => {
shepherd.writeLog(`getconf flock: ${flock}`);
if (coind) {
switch (os.platform()) {
switch (_platform) {
case 'darwin':
nativeCoindDir = `${process.env.HOME}/Library/Application Support/${shepherd.nativeCoindList[coind.toLowerCase()].bin}`;
break;
@ -39,29 +40,29 @@ module.exports = (shepherd) => {
switch (flock) {
case 'komodod':
DaemonConfPath = shepherd.komodoDir;
if (os.platform() === 'win32') {
if (_platform === 'win32') {
DaemonConfPath = path.normalize(DaemonConfPath);
shepherd.log('===>>> SHEPHERD API OUTPUT ===>>>');
}
break;
case 'zcashd':
DaemonConfPath = shepherd.ZcashDir;
if (os.platform() === 'win32') {
if (_platform === 'win32') {
DaemonConfPath = path.normalize(DaemonConfPath);
}
break;
case 'chipsd':
DaemonConfPath = shepherd.chipsDir;
if (os.platform() === 'win32') {
if (_platform === 'win32') {
DaemonConfPath = path.normalize(DaemonConfPath);
}
break;
case 'coind':
DaemonConfPath = os.platform() === 'win32' ? shepherd.path.normalize(`${shepherd.coindRootDir}/${coind.toLowerCase()}`) : `${shepherd.coindRootDir}/${coind.toLowerCase()}`;
DaemonConfPath = _platform === 'win32' ? shepherd.path.normalize(`${shepherd.coindRootDir}/${coind.toLowerCase()}`) : `${shepherd.coindRootDir}/${coind.toLowerCase()}`;
break;
default:
DaemonConfPath = `${shepherd.komodoDir}/${flock}`;
if (os.platform() === 'win32') {
if (_platform === 'win32') {
DaemonConfPath = shepherd.path.normalize(DaemonConfPath);
}
}
@ -482,57 +483,58 @@ module.exports = (shepherd) => {
}
const setConf = (flock, coind) => {
const _platform = os.platform();
let nativeCoindDir;
let DaemonConfPath;
shepherd.log(flock);
shepherd.writeLog(`setconf ${flock}`);
if (os.platform() === 'darwin') {
switch (_platform) {
case 'darwin':
nativeCoindDir = coind ? `${process.env.HOME}/Library/Application Support/${shepherd.nativeCoindList[coind.toLowerCase()].bin}` : null;
}
if (os.platform() === 'linux') {
break;
case 'linux':
nativeCoindDir = coind ? `${process.env.HOME}/.${shepherd.nativeCoindList[coind.toLowerCase()].bin.toLowerCase()}` : null;
}
if (os.platform() === 'win32') {
break;
case 'win32':
nativeCoindDir = coind ? `${process.env.APPDATA}/${shepherd.nativeCoindList[coind.toLowerCase()].bin}` : null;
break;
}
switch (flock) {
case 'komodod':
DaemonConfPath = `${shepherd.komodoDir}/komodo.conf`;
if (os.platform() === 'win32') {
if (_platform === 'win32') {
DaemonConfPath = path.normalize(DaemonConfPath);
}
break;
case 'zcashd':
DaemonConfPath = `${shepherd.ZcashDir}/zcash.conf`;
if (os.platform() === 'win32') {
if (_platform === 'win32') {
DaemonConfPath = path.normalize(DaemonConfPath);
}
break;
case 'chipsd':
DaemonConfPath = `${shepherd.chipsDir}/chips.conf`;
if (os.platform() === 'win32') {
if (_platform === 'win32') {
DaemonConfPath = path.normalize(DaemonConfPath);
}
break;
case 'coind':
DaemonConfPath = `${nativeCoindDir}/${shepherd.nativeCoindList[coind.toLowerCase()].bin.toLowerCase()}.conf`;
if (os.platform() === 'win32') {
if (_platform === 'win32') {
DaemonConfPath = path.normalize(DaemonConfPath);
}
break;
default:
DaemonConfPath = `${shepherd.komodoDir}/${flock}/${flock}.conf`;
if (os.platform() === 'win32') {
if (_platform === 'win32') {
DaemonConfPath = path.normalize(DaemonConfPath);
}
}
@ -790,14 +792,17 @@ module.exports = (shepherd) => {
* params: herd
*/
shepherd.post('/herd', (req, res) => {
shepherd.log('======= req.body =======');
shepherd.log(req.body);
const _body = req.body;
shepherd.log('herd req.body =>');
shepherd.log(_body);
if (req.body.options &&
if (_body.options &&
!shepherd.kmdMainPassiveMode) {
const testCoindPort = (skipError) => {
const _acName = req.body.options.ac_name;
if (!shepherd.lockDownAddCoin) {
const _port = shepherd.assetChainPorts[req.body.options.ac_name];
const _port = shepherd.assetChainPorts[_acName];
portscanner.checkPortStatus(_port, '127.0.0.1', (error, status) => {
// Status is 'open' if currently in use or 'closed' if available
@ -808,13 +813,13 @@ module.exports = (shepherd) => {
shepherd.writeLog(`komodod service start error at port ${_port}, reason: port is closed`);
shepherd.io.emit('service', {
komodod: {
error: `error starting ${req.body.herd} ${req.body.options.ac_name} daemon. Port ${_port} is already taken!`,
error: `error starting ${_body.herd} ${_acName} daemon. Port ${_port} is already taken!`,
},
});
const obj = {
msg: 'error',
result: `error starting ${req.body.herd} ${req.body.options.ac_name} daemon. Port ${_port} is already taken!`,
result: `error starting ${_body.herd} ${_acName} daemon. Port ${_port} is already taken!`,
};
res.status(500);
@ -825,7 +830,7 @@ module.exports = (shepherd) => {
}
} else {
if (!skipError) {
herder(req.body.herd, req.body.options);
herder(_body.herd, _body.options);
const obj = {
msg: 'success',
@ -842,14 +847,14 @@ module.exports = (shepherd) => {
}
}
if (req.body.herd === 'komodod') {
if (_body.herd === 'komodod') {
// check if komodod instance is already running
testCoindPort();
setTimeout(() => {
testCoindPort(true);
}, 10000);
} else {
herder(req.body.herd, req.body.options, req.body.coind);
herder(_body.herd, _body.options, _body.coind);
const obj = {
msg: 'success',
@ -860,7 +865,7 @@ module.exports = (shepherd) => {
}
} else {
// (?)
herder(req.body.herd, req.body.options);
herder(_body.herd, _body.options);
const obj = {
msg: 'success',
@ -875,14 +880,16 @@ module.exports = (shepherd) => {
* type: POST
*/
shepherd.post('/setconf', (req, res) => {
shepherd.log('======= req.body =======');
shepherd.log(req.body);
const _body = req.body;
shepherd.log('setconf req.body =>');
shepherd.log(_body);
if (os.platform() === 'win32' &&
req.body.chain == 'komodod') {
_body.chain == 'komodod') {
setkomodoconf = spawn(path.join(__dirname, '../assets/bin/win64/genkmdconf.bat'));
} else {
shepherd.setConf(req.body.chain);
shepherd.setConf(_body.chain);
}
const obj = {
@ -897,15 +904,15 @@ module.exports = (shepherd) => {
* type: POST
*/
shepherd.post('/getconf', (req, res) => {
shepherd.log('======= req.body =======');
shepherd.log(req.body);
const _body = req.body;
shepherd.log('getconf req.body =>');
shepherd.log(_body);
const confpath = getConf(req.body.chain, req.body.coind);
const confpath = getConf(_body.chain, _body.coind);
shepherd.log('got conf path is:');
shepherd.log(confpath);
shepherd.writeLog('got conf path is:');
shepherd.writeLog(confpath);
shepherd.log(`getconf path is: ${confpath}`);
shepherd.writeLog(`getconf path is: ${confpath}`);
const obj = {
msg: 'success',

2
routes/shepherd/electrum/balance.js

@ -45,7 +45,7 @@ module.exports = (shepherd) => {
// decode tx
const _network = shepherd.getNetworkData(network);
const decodedTx = shepherd.electrumJSTxDecoder(_rawtxJSON, _network);
const decodedTx = shepherd.isZcash(network) ? shepherd.electrumJSTxDecoderZ(_rawtxJSON, _network) : shepherd.electrumJSTxDecoder(_rawtxJSON, _network);
if (decodedTx &&
decodedTx.format &&

5
routes/shepherd/electrum/createtx.js

@ -1,4 +1,5 @@
const bitcoinJSForks = require('bitcoinforksjs-lib');
const bitcoinZcash = require('bitcoinjs-lib-zcash');
module.exports = (shepherd) => {
// unsigned tx
@ -52,8 +53,8 @@ module.exports = (shepherd) => {
// single sig
shepherd.buildSignedTx = (sendTo, changeAddress, wif, network, utxo, changeValue, spendValue) => {
let key = shepherd.bitcoinJS.ECPair.fromWIF(wif, shepherd.getNetworkData(network));
let tx = new shepherd.bitcoinJS.TransactionBuilder(shepherd.getNetworkData(network));
let key = shepherd.isZcash(network) ? bitcoinZcash.ECPair.fromWIF(wif, shepherd.getNetworkData(network)) : shepherd.bitcoinJS.ECPair.fromWIF(wif, shepherd.getNetworkData(network));
let tx = shepherd.isZcash(network) ? new bitcoinZcash.TransactionBuilder(shepherd.getNetworkData(network)) : new shepherd.bitcoinJS.TransactionBuilder(shepherd.getNetworkData(network));
shepherd.log('buildSignedTx');
// console.log(`buildSignedTx priv key ${wif}`);

34
routes/shepherd/electrum/keys.js

@ -1,6 +1,9 @@
const sha256 = require('js-sha256');
const bip39 = require('bip39');
const crypto = require('crypto');
const bigi = require('bigi');
const bitcoinZcash = require('bitcoinjs-lib-zcash');
const bitcoin = require('bitcoinjs-lib');
module.exports = (shepherd) => {
shepherd.seedToWif = (seed, network, iguana) => {
@ -19,30 +22,19 @@ module.exports = (shepherd) => {
bytes[31] |= 64;
}
const toHexString = (byteArray) => {
return Array.from(byteArray, (byte) => {
return ('0' + (byte & 0xFF).toString(16)).slice(-2);
}).join('');
}
const hex = toHexString(bytes);
const key = new shepherd.CoinKey(new Buffer(hex, 'hex'), {
private: shepherd.getNetworkData(network).wif,
public: shepherd.getNetworkData(network).pubKeyHash,
});
key.compressed = true;
const d = bigi.fromBuffer(bytes);
const keyPair = shepherd.isZcash(network) ? new bitcoinZcash.ECPair(d, null, { network: shepherd.getNetworkData(network) }) : new bitcoinZcash.ECPair(d, null, { network: shepherd.getNetworkData(network) });
const keys = {
pub: keyPair.getAddress(),
priv: keyPair.toWIF(),
};
/*shepherd.log(`seed: ${seed}`, true);
shepherd.log(`network ${network}`, true);
shepherd.log(`seedtowif priv key ${key.privateWif}`, true);
shepherd.log(`seedtowif pub key ${key.publicAddress}`, true);*/
shepherd.log(`seedtowif priv key ${keys.priv}`, true);
shepherd.log(`seedtowif pub key ${keys.pub}`, true);*/
return {
priv: key.privateWif,
pub: key.publicAddress,
};
return keys;
}
shepherd.get('/electrum/wiftopub', (req, res, next) => {
@ -63,7 +55,7 @@ module.exports = (shepherd) => {
});
shepherd.get('/electrum/seedtowif', (req, res, next) => {
const keys = shepherd.seedToWif(req.query.seed, req.query.network, req.query.iguana);
let keys = shepherd.seedToWif(req.query.seed, req.query.network, req.query.iguana);
const successObj = {
msg: 'success',

5
routes/shepherd/electrum/listunspent.js

@ -36,7 +36,7 @@ module.exports = (shepherd) => {
// decode tx
const _network = shepherd.getNetworkData(network);
const decodedTx = shepherd.electrumJSTxDecoder(_rawtxJSON, _network);
const decodedTx = shepherd.isZcash(network) ? shepherd.electrumJSTxDecoderZ(_rawtxJSON, _network) : shepherd.electrumJSTxDecoder(_rawtxJSON, _network);
shepherd.log('decoded tx =>', true);
shepherd.log(decodedTx, true);
@ -71,7 +71,8 @@ module.exports = (shepherd) => {
if (verify) {
shepherd.verifyMerkleByCoin(shepherd.findCoinName(network), _utxoItem['tx_hash'], _utxoItem.height)
.then((verifyMerkleRes) => {
if (verifyMerkleRes && verifyMerkleRes === shepherd.CONNECTION_ERROR_OR_INCOMPLETE_DATA) {
if (verifyMerkleRes &&
verifyMerkleRes === shepherd.CONNECTION_ERROR_OR_INCOMPLETE_DATA) {
verifyMerkleRes = false;
}

4
routes/shepherd/electrum/merkle.js

@ -2,11 +2,11 @@ module.exports = (shepherd) => {
// get merkle root
shepherd.getMerkleRoot = (txid, proof, pos) => {
const reverse = require('buffer-reverse');
let hash = txid;
let serialized;
const _sha256 = (data) => {
return shepherd.crypto.createHash('sha256').update(data).digest();
}
let hash = txid;
let serialized;
shepherd.log(`getMerkleRoot txid ${txid}`, true);
shepherd.log(`getMerkleRoot pos ${pos}`, true);

13
routes/shepherd/electrum/network.js

@ -1,4 +1,17 @@
module.exports = (shepherd) => {
shepherd.isZcash = (network) => {
if (network === 'ZEC' ||
network === 'zec' ||
network === 'zcash' ||
network === 'ZCASH' ||
network === 'HUSH' ||
network === 'hush' ||
network === 'blk' ||
network === 'BLK') {
return true;
}
};
shepherd.getNetworkData = (network) => {
const coin = shepherd.findNetworkObj(network) || shepherd.findNetworkObj(network.toUpperCase()) || shepherd.findNetworkObj(network.toLowerCase());
const coinUC = coin ? coin.toUpperCase() : null;

6
routes/shepherd/electrum/transactions.js

@ -69,9 +69,10 @@ module.exports = (shepherd) => {
// decode tx
const _network = shepherd.getNetworkData(network);
const decodedTx = shepherd.electrumJSTxDecoder(_rawtxJSON, _network);
const decodedTx = shepherd.isZcash(network) ? shepherd.electrumJSTxDecoderZ(_rawtxJSON, _network) : shepherd.electrumJSTxDecoder(_rawtxJSON, _network);
let txInputs = [];
shepherd.log(`decodedtx network ${network}`, true);
shepherd.log('decodedtx =>', true);
shepherd.log(decodedTx.outputs, true);
@ -83,7 +84,7 @@ module.exports = (shepherd) => {
if (_decodedInput.txid !== '0000000000000000000000000000000000000000000000000000000000000000') {
ecl.blockchainTransactionGet(_decodedInput.txid)
.then((rawInput) => {
const decodedVinVout = shepherd.electrumJSTxDecoder(rawInput, _network);
const decodedVinVout = shepherd.isZcash(network) ? shepherd.electrumJSTxDecoderZ(rawInput, _network) : shepherd.electrumJSTxDecoder(rawInput, _network);
shepherd.log('electrum raw input tx ==>', true);
@ -352,6 +353,7 @@ module.exports = (shepherd) => {
shepherd.log('electrum decoderawtx input tx ==>', true);
if (req.query.parseonly ||
decodedTx.inputs[0].txid === '0000000000000000000000000000000000000000000000000000000000000000') {
const successObj = {

Loading…
Cancel
Save