You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

372 lines
14 KiB

const bs58check = require('bs58check');
const bitcoin = require('bitcoinjs-lib');
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)) {
const _seed = req.body.seed;
const _network = req.body.network;
let keys;
let isWif = false;
if (_seed.match('^[a-zA-Z0-9]{34}$')) {
shepherd.log('watchonly elections pub addr');
shepherd.elections = {
priv: _seed,
pub: _seed,
};
} else {
try {
bs58check.decode(_seed);
isWif = true;
} catch (e) {}
if (isWif) {
try {
let key = bitcoin.ECPair.fromWIF(_seed, shepherd.getNetworkData(_network.toLowerCase()), true);
keys = {
priv: key.toWIF(),
pub: key.getAddress(),
};
} catch (e) {
_wifError = true;
}
} else {
keys = shepherd.seedToWif(_seed, _network, req.body.iguana);
}
shepherd.elections = {
priv: keys.priv,
pub: keys.pub,
};
}
const successObj = {
msg: 'success',
result: shepherd.elections.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.electionsDecodeTx = (decodedTx, ecl, network, _network, transaction, blockInfo, address) => {
let txInputs = [];
return new shepherd.Promise((resolve, reject) => {
if (decodedTx &&
decodedTx.inputs) {
shepherd.Promise.all(decodedTx.inputs.map((_decodedInput, index) => {
return new shepherd.Promise((_resolve, _reject) => {
if (_decodedInput.txid !== '0000000000000000000000000000000000000000000000000000000000000000') {
ecl.blockchainTransactionGet(_decodedInput.txid)
.then((rawInput) => {
const decodedVinVout = shepherd.electrumJSTxDecoder(rawInput, network, _network);
shepherd.log('electrum raw input tx ==>', true);
if (decodedVinVout) {
shepherd.log(decodedVinVout.outputs[_decodedInput.n], true);
txInputs.push(decodedVinVout.outputs[_decodedInput.n]);
_resolve(true);
} else {
_resolve(true);
}
});
} else {
_resolve(true);
}
});
}))
.then(promiseResult => {
const _parsedTx = {
network: decodedTx.network,
format: decodedTx.format,
inputs: txInputs,
outputs: decodedTx.outputs,
height: transaction.height,
timestamp: Number(transaction.height) === 0 ? Math.floor(Date.now() / 1000) : blockInfo.timestamp,
};
resolve(shepherd.parseTransactionAddresses(_parsedTx, address, network, true));
});
} else {
const _parsedTx = {
network: decodedTx.network,
format: 'cant parse',
inputs: 'cant parse',
outputs: 'cant parse',
height: transaction.height,
timestamp: Number(transaction.height) === 0 ? Math.floor(Date.now() / 1000) : blockInfo.timestamp,
};
resolve(shepherd.parseTransactionAddresses(_parsedTx, address, network));
}
});
};
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) {
let _rawtx = [];
json = shepherd.sortTransactions(json);
// json = json.length > MAX_TX ? json.slice(0, MAX_TX) : json;
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;
if (decodedTx &&
decodedTx.outputs &&
decodedTx.outputs.length) {
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) {
if (_region === 'ne2k18-na-1-eu-2-ae-3-sh-4') {
const _regionsLookup = [
'ne2k18-na',
'ne2k18-eu',
'ne2k18-ae',
'ne2k18-sh'
];
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: _regionsLookup[i],
timestamp: blockInfo.timestamp,
});
resolve(true);
} else {
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,
});
resolve(true);
}
}
if (type === 'candidate') {
if (_region === 'ne2k18-na-1-eu-2-ae-3-sh-4') {
if (decodedTx.outputs[i].scriptPubKey.addresses[0] === _address && decodedTx.outputs[i].scriptPubKey.asm.indexOf('OP_RETURN') === -1) {
const _regionsLookup = [
'ne2k18-na',
'ne2k18-eu',
'ne2k18-ae',
'ne2k18-sh'
];
shepherd.electionsDecodeTx(decodedTx, ecl, network, _network, transaction, blockInfo, _address)
.then((res) => {
shepherd.log(`i received ${decodedTx.outputs[i].value} from ${res.outputAddresses[0]} out ${i} region ${_regionsLookup[i]}`);
_rawtx.push({
address: res.outputAddresses[0],
timestamp: blockInfo.timestamp,
amount: decodedTx.outputs[i].value,
region: _regionsLookup[i],
});
resolve(true);
});
}
} else {
shepherd.electionsDecodeTx(decodedTx, ecl, network, _network, transaction, blockInfo, _address)
.then((res) => {
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} region ${_region}`);
_rawtx.push(_candidate);
resolve(true);
}
});
}
}
}
} else {
shepherd.log('elections regular tx', true);
shepherd.electionsDecodeTx(decodedTx, ecl, network, _network, transaction, blockInfo, _address)
.then((_regularTx) => {
if (_regularTx[0] &&
_regularTx[1]) {
_rawtx.push({
address: _regularTx[type === 'voter' ? 0 : 1].address || 'self',
timestamp: _regularTx[type === 'voter' ? 0 : 1].timestamp,
amount: _regularTx[type === 'voter' ? 0 : 1].amount,
region: 'unknown',
regularTx: true,
hash: transaction['tx_hash'],
});
} else {
if ((type === 'voter' && _regularTx.type !== 'received') && (type === 'candidate' && _regularTx.type !== 'sent')) {
_rawtx.push({
address: _regularTx.address || 'self',
timestamp: _regularTx.timestamp,
amount: _regularTx.amount,
region: 'unknown',
regularTx: true,
hash: transaction['tx_hash'],
});
}
}
resolve(true);
});
}
});
} else {
_rawtx.push({
address: 'unknown',
timestamp: 'cant get block info',
amount: 'unknown',
region: 'unknown',
regularTx: true,
});
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;
}