mirror of https://github.com/lukechilds/Agama.git
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
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;
|
|
}
|