Improved block sync speed
-A number of functions have been rewritten to be more optimized and faster: calculate_total, is_unique, convert_to_satoshi, get_input_addresses, processVoutAddresses, prepare_vout, prepare_vin -Txes are now written to database via bulk writes which helps improve the sync speed and also controls memory usage with batching to write data once a certain threshold is reached -update_address function changed to update_addresses since it now bulk writes the addresses in batches to improve sync speed and also controls memory usage with batching to write data once a certain threshold is reached -The syncLoop function has been completely removed from the project and replaced with async library loops or even normal "for" loops in some cases which greatly improves sync speeds over large batches of data -Fixed an issue with the flattened count of txes that is saved to the coinstats collection which could save incorrectly when using more than 1 thread -Fixed an issue with the block sync which caused an unwanted delay when syncing less blocks than the amount of threads used to sync the data -Fixed an issue with vout data processing that could sometimes populate data out of order -Added a new sync.batch_size setting used to determine how many records (txes, addresses, addresstxes) should be saved in a single database transaction -Added a new wait_for_bulk_database_save setting used to increase the block sync speed at the cost of not returning any error msgs for data that failed to save -get_input_addresses function no longer returns in the exports section of the explorer.js file since it is only referenced in that file -Updated explorerspec tests to use the newest function changes for any tests that needed to be updated Special thanks to Karzo from Pepecoin for help with the bulkwrite code changes!
This commit is contained in:
+284
-397
@@ -1,10 +1,9 @@
|
||||
var request = require('postman-request'),
|
||||
async = require('async'),
|
||||
settings = require('./settings'),
|
||||
Address = require('../models/address');
|
||||
|
||||
var base_server = 'http://127.0.0.1:' + settings.webserver.port + "/";
|
||||
var base_url = base_server + 'api/';
|
||||
const request = require('postman-request');
|
||||
const async = require('async');
|
||||
const settings = require('./settings');
|
||||
const Address = require('../models/address');
|
||||
const base_server = 'http://127.0.0.1:' + settings.webserver.port + '/';
|
||||
const base_url = base_server + 'api/';
|
||||
const onode = require('./node');
|
||||
const client = new onode.Client(settings.wallet);
|
||||
|
||||
@@ -76,7 +75,7 @@ function convertHashUnits(hashes) {
|
||||
}
|
||||
}
|
||||
|
||||
function processVoutAddresses(address_list, vout_value, arr_vout, cb) {
|
||||
function processVoutAddresses(address_list, vout_value, arr_vout) {
|
||||
// check if there are any addresses to process
|
||||
if (address_list != null && address_list.length > 0) {
|
||||
// check if vout address is inside an array
|
||||
@@ -85,28 +84,21 @@ function processVoutAddresses(address_list, vout_value, arr_vout, cb) {
|
||||
address_list[0] = address_list[0][0];
|
||||
}
|
||||
|
||||
const amount_sat = module.exports.convert_to_satoshi(parseFloat(vout_value));
|
||||
const index = module.exports.is_unique(arr_vout, address_list[0], 'addresses');
|
||||
|
||||
// check if vout address is unique, if so add to array, if not add its amount to existing index
|
||||
module.exports.is_unique(arr_vout, address_list[0], 'addresses', function(unique, index) {
|
||||
if (unique == true) {
|
||||
// unique vout
|
||||
module.exports.convert_to_satoshi(parseFloat(vout_value), function(amount_sat) {
|
||||
arr_vout.push({addresses: address_list[0], amount: amount_sat});
|
||||
|
||||
return cb(arr_vout);
|
||||
});
|
||||
} else {
|
||||
// already exists
|
||||
module.exports.convert_to_satoshi(parseFloat(vout_value), function(amount_sat) {
|
||||
arr_vout[index].amount = arr_vout[index].amount + amount_sat;
|
||||
|
||||
return cb(arr_vout);
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// no address, move to next vout
|
||||
return cb(arr_vout);
|
||||
if (index == -1) {
|
||||
// unique vout
|
||||
arr_vout.push({addresses: address_list[0], amount: amount_sat});
|
||||
} else {
|
||||
// already exists
|
||||
arr_vout[index].amount += amount_sat;
|
||||
}
|
||||
}
|
||||
|
||||
// return the list of addresses
|
||||
return arr_vout;
|
||||
}
|
||||
|
||||
function encodeP2PKaddress(p2pk_descriptor, cb) {
|
||||
@@ -182,12 +174,146 @@ function normalizeMasternodeCount(raw_count) {
|
||||
}
|
||||
}
|
||||
|
||||
function get_input_addresses(input, vout, cb) {
|
||||
let addresses = [];
|
||||
|
||||
if (input.coinbase) {
|
||||
let amount = 0;
|
||||
|
||||
for (let i = 0; i < vout.length; i++)
|
||||
amount += parseFloat(vout[i].value);
|
||||
|
||||
addresses.push({ hash: 'coinbase', amount: amount });
|
||||
return cb(addresses, null);
|
||||
} else {
|
||||
module.exports.get_rawtransaction(input.txid, function(tx) {
|
||||
if (tx) {
|
||||
let tx_type = null;
|
||||
|
||||
async.eachSeries(tx.vout, function(current_vout, loop) {
|
||||
if (current_vout.n == input.vout) {
|
||||
if (current_vout.scriptPubKey.addresses || current_vout.scriptPubKey.address) {
|
||||
let new_address = current_vout.scriptPubKey.address || current_vout.scriptPubKey.addresses[0];
|
||||
|
||||
// check if address is inside an array
|
||||
if (Array.isArray(new_address)) {
|
||||
// extract the address
|
||||
new_address = new_address[0];
|
||||
}
|
||||
|
||||
const index = module.exports.is_unique(addresses, new_address, 'hash');
|
||||
|
||||
if (index == -1)
|
||||
addresses.push({hash: new_address, amount: current_vout.value});
|
||||
else
|
||||
addresses[index].amount += current_vout.value;
|
||||
|
||||
loop();
|
||||
} else {
|
||||
// no addresses defined
|
||||
// check if bitcoin features are enabled
|
||||
if (settings.blockchain_specific.bitcoin.enabled == true) {
|
||||
// assume the asm value is a P2PK (Pay To Pubkey) public key that should be encoded as a P2PKH (Pay To Pubkey Hash) address
|
||||
encodeP2PKaddress(current_vout.scriptPubKey.asm, function(p2pkh_address) {
|
||||
// check if the address was encoded properly
|
||||
if (p2pkh_address != null) {
|
||||
// mark this tx as p2pk
|
||||
tx_type = 'p2pk';
|
||||
|
||||
// check if address is inside an array
|
||||
if (Array.isArray(p2pkh_address)) {
|
||||
// extract the address
|
||||
p2pkh_address = p2pkh_address[0];
|
||||
}
|
||||
|
||||
// save the P2PKH address
|
||||
const index = module.exports.is_unique(addresses, p2pkh_address, 'hash');
|
||||
|
||||
if (index == -1)
|
||||
addresses.push({hash: p2pkh_address, amount: current_vout.value});
|
||||
else
|
||||
addresses[index].amount += current_vout.value;
|
||||
|
||||
loop();
|
||||
} else {
|
||||
// could not decipher the address, save as unknown and move to next vin
|
||||
console.log('Failed to find vin address from tx ' + input.txid);
|
||||
|
||||
const index = module.exports.is_unique(addresses, 'unknown_address', 'hash');
|
||||
|
||||
if (index == -1)
|
||||
addresses.push({hash: 'unknown_address', amount: current_vout.value});
|
||||
else
|
||||
addresses[index].amount += current_vout.value;
|
||||
|
||||
loop();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// could not decipher the address, save as unknown and move to next vin
|
||||
console.log('Failed to find vin address from tx ' + input.txid);
|
||||
|
||||
const index = module.exports.is_unique(addresses, 'unknown_address', 'hash');
|
||||
|
||||
if (index == -1)
|
||||
addresses.push({hash: 'unknown_address', amount: current_vout.value});
|
||||
else
|
||||
addresses[index].amount += current_vout.value;
|
||||
|
||||
loop();
|
||||
}
|
||||
}
|
||||
} else
|
||||
loop();
|
||||
}, function() {
|
||||
return cb(addresses, tx_type);
|
||||
});
|
||||
} else
|
||||
return cb();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function finalize_vout(first_vout, arr_vout, arr_vin, tx_type, txid, cb) {
|
||||
if (typeof first_vout !== 'undefined' && first_vout.scriptPubKey.type == 'nonstandard') {
|
||||
if (arr_vin.length > 0 && arr_vout.length > 0) {
|
||||
if (arr_vin[0].addresses == arr_vout[0].addresses) {
|
||||
// pos
|
||||
arr_vout[0].amount -= arr_vin[0].amount;
|
||||
arr_vin.shift();
|
||||
|
||||
// check if any vin remains
|
||||
if (arr_vin == null || arr_vin.length == 0) {
|
||||
// empty vin should be linked to coinbase
|
||||
arr_vin = [{coinbase: 'coinbase'}];
|
||||
|
||||
let new_vout = [];
|
||||
|
||||
// loop through the arr_vout to create a copy of the data with coin amounts only for use with prepare_vin()
|
||||
for (i = 0; i < arr_vout.length; i++)
|
||||
new_vout.push({ value: arr_vout[i].amount / 100000000 });
|
||||
|
||||
// call the prepare_vin again to populate the vin data correctly
|
||||
module.exports.prepare_vin({ txid: txid, vin: arr_vin, vout: new_vout}, function(return_vin, return_tx_type_vin) {
|
||||
return cb(arr_vout, return_vin, return_tx_type_vin);
|
||||
});
|
||||
} else
|
||||
return cb(arr_vout, arr_vin, tx_type);
|
||||
} else
|
||||
return cb(arr_vout, arr_vin, tx_type);
|
||||
} else
|
||||
return cb(arr_vout, arr_vin, tx_type);
|
||||
} else
|
||||
return cb(arr_vout, arr_vin, tx_type);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
convert_to_satoshi: function(amount, cb) {
|
||||
// fix to 8dp & convert to string
|
||||
var fixed = amount.toFixed(8).toString();
|
||||
convert_to_satoshi: function(amount) {
|
||||
// fix to 8 decimals and convert to string
|
||||
const fixed = amount.toFixed(8).toString();
|
||||
|
||||
// remove decimal (.) and return integer
|
||||
return cb(parseInt(fixed.replace('.', '')));
|
||||
return parseInt(fixed.replace('.', ''));
|
||||
},
|
||||
|
||||
get_hashrate: function(cb) {
|
||||
@@ -810,77 +936,13 @@ module.exports = {
|
||||
}
|
||||
},
|
||||
|
||||
// synchonous loop used to interate through an array,
|
||||
// avoid use unless absolutely neccessary
|
||||
syncLoop: function(iterations, process, exit) {
|
||||
var index = 0,
|
||||
done = false,
|
||||
shouldExit = false;
|
||||
|
||||
var loop = {
|
||||
next: function() {
|
||||
if (done) {
|
||||
if (shouldExit && exit) {
|
||||
// exit if we're done
|
||||
exit();
|
||||
}
|
||||
|
||||
// stop the loop if we're done
|
||||
return;
|
||||
}
|
||||
|
||||
// if we're not finished
|
||||
if (index < iterations) {
|
||||
// increment our index
|
||||
index++;
|
||||
|
||||
if (index % 100 === 0) {
|
||||
// clear stack
|
||||
setTimeout(function() {
|
||||
// run our process, pass in the loop
|
||||
process(loop);
|
||||
}, 1);
|
||||
} else {
|
||||
// run our process, pass in the loop
|
||||
process(loop);
|
||||
}
|
||||
} else {
|
||||
// otherwise we're done
|
||||
// make sure we say we're done
|
||||
done = true;
|
||||
|
||||
if (exit) {
|
||||
// call the callback on exit
|
||||
exit();
|
||||
}
|
||||
}
|
||||
},
|
||||
iteration: function() {
|
||||
// return the loop number we're on
|
||||
return index - 1;
|
||||
},
|
||||
break: function(end) {
|
||||
// end the loop
|
||||
done = true;
|
||||
// passing end as true means we still call the exit callback
|
||||
shouldExit = end;
|
||||
}
|
||||
};
|
||||
|
||||
loop.next();
|
||||
|
||||
return loop;
|
||||
},
|
||||
|
||||
balance_supply: function(cb) {
|
||||
Address.find({}, 'balance').where('balance').gt(0).exec().then((docs) => {
|
||||
var count = 0;
|
||||
let count = 0;
|
||||
|
||||
module.exports.syncLoop(docs.length, function (loop) {
|
||||
var i = loop.iteration();
|
||||
|
||||
count = count + docs[i].balance;
|
||||
loop.next();
|
||||
async.eachSeries(docs, function(current_doc, loop) {
|
||||
count += current_doc.balance;
|
||||
loop();
|
||||
}, function() {
|
||||
return cb(count);
|
||||
});
|
||||
@@ -1080,222 +1142,160 @@ module.exports = {
|
||||
});
|
||||
},
|
||||
|
||||
is_unique: function(array, object, key_name, cb) {
|
||||
var unique = true;
|
||||
var index = null;
|
||||
is_unique: function(array, object, key_name) {
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
if (array[i][key_name] === object)
|
||||
return i;
|
||||
}
|
||||
|
||||
module.exports.syncLoop(array.length, function (loop) {
|
||||
var i = loop.iteration();
|
||||
|
||||
if (array[i][key_name] == object) {
|
||||
unique = false;
|
||||
index = i;
|
||||
loop.break(true);
|
||||
loop.next();
|
||||
} else
|
||||
loop.next();
|
||||
}, function() {
|
||||
return cb(unique, index);
|
||||
});
|
||||
return -1;
|
||||
},
|
||||
|
||||
calculate_total: function(vout, cb) {
|
||||
var total = 0;
|
||||
calculate_total: function(vout) {
|
||||
let total = 0;
|
||||
|
||||
module.exports.syncLoop(vout.length, function (loop) {
|
||||
var i = loop.iteration();
|
||||
for (let i = 0; i < vout.length; i++)
|
||||
total += vout[i].amount;
|
||||
|
||||
total = total + vout[i].amount;
|
||||
loop.next();
|
||||
}, function() {
|
||||
return cb(total);
|
||||
});
|
||||
return total;
|
||||
},
|
||||
|
||||
prepare_vout: function(vout, txid, vin, vhidden, cb) {
|
||||
var arr_vout = [];
|
||||
var arr_vin = vin;
|
||||
var tx_type = null;
|
||||
let arr_vout = [];
|
||||
let arr_vin = vin;
|
||||
let tx_type = null;
|
||||
|
||||
try {
|
||||
module.exports.syncLoop(vout.length, function (loop) {
|
||||
var i = loop.iteration();
|
||||
async.eachSeries(vout, function(current_vout, loop) {
|
||||
// make sure vout has an address
|
||||
if (vout[i].scriptPubKey.type != 'nonstandard' && vout[i].scriptPubKey.type != 'nulldata') {
|
||||
if (current_vout.scriptPubKey.type != 'nonstandard' && current_vout.scriptPubKey.type != 'nulldata') {
|
||||
// check if this is a zerocoin tx
|
||||
if (vout[i].scriptPubKey.type != 'zerocoinmint') {
|
||||
var address_list = vout[i].scriptPubKey.addresses;
|
||||
if (current_vout.scriptPubKey.type != 'zerocoinmint') {
|
||||
const address_list = current_vout.scriptPubKey.addresses;
|
||||
|
||||
// check if there are one or more addresses in the vout
|
||||
if (address_list == null || address_list.length == 0) {
|
||||
// no addresses defined
|
||||
// check if there is a single address defined
|
||||
if (vout[i].scriptPubKey.address == null) {
|
||||
if (current_vout.scriptPubKey.address == null) {
|
||||
// no single address defined
|
||||
// check if bitcoin features are enabled
|
||||
if (settings.blockchain_specific.bitcoin.enabled == true) {
|
||||
// assume the asm value is a P2PK (Pay To Pubkey) public key that should be encoded as a P2PKH (Pay To Pubkey Hash) address
|
||||
encodeP2PKaddress(vout[i].scriptPubKey.asm, function(p2pkh_address) {
|
||||
encodeP2PKaddress(current_vout.scriptPubKey.asm, function(p2pkh_address) {
|
||||
// check if the address was encoded properly
|
||||
if (p2pkh_address != null) {
|
||||
// mark this tx as p2pk
|
||||
tx_type = 'p2pk';
|
||||
// process vout addresses
|
||||
processVoutAddresses(p2pkh_address, vout[i].value, arr_vout, function(vout_array) {
|
||||
// save updated array
|
||||
arr_vout = vout_array;
|
||||
// move to next vout
|
||||
loop.next();
|
||||
});
|
||||
|
||||
// process vout addresses and save the updated array
|
||||
arr_vout = processVoutAddresses(p2pkh_address, current_vout.value, arr_vout);
|
||||
} else {
|
||||
// could not decipher the address, save as unknown and move to next vout
|
||||
// could not decipher the address, save as unknown
|
||||
console.log('Failed to find vout address from tx ' + txid);
|
||||
// process vout addresses
|
||||
processVoutAddresses(['unknown_address'], vout[i].value, arr_vout, function(vout_array) {
|
||||
// save updated array
|
||||
arr_vout = vout_array;
|
||||
// move to next vout
|
||||
loop.next();
|
||||
});
|
||||
|
||||
// process vout addresses and save the updated array
|
||||
arr_vout = processVoutAddresses(['unknown_address'], current_vout.value, arr_vout);
|
||||
}
|
||||
|
||||
// move to next vout
|
||||
loop();
|
||||
});
|
||||
} else {
|
||||
// could not decipher the address, save as unknown and move to next vout
|
||||
console.log('Failed to find vout address from tx ' + txid);
|
||||
// process vout addresses
|
||||
processVoutAddresses(['unknown_address'], vout[i].value, arr_vout, function(vout_array) {
|
||||
// save updated array
|
||||
arr_vout = vout_array;
|
||||
// move to next vout
|
||||
loop.next();
|
||||
});
|
||||
|
||||
// process vout addresses and save the updated array
|
||||
arr_vout = processVoutAddresses(['unknown_address'], current_vout.value, arr_vout);
|
||||
|
||||
// move to next vout
|
||||
loop();
|
||||
}
|
||||
} else {
|
||||
// process vout address
|
||||
processVoutAddresses([vout[i].scriptPubKey.address], vout[i].value, arr_vout, function(vout_array) {
|
||||
// save updated array
|
||||
arr_vout = vout_array;
|
||||
// move to next vout
|
||||
loop.next();
|
||||
});
|
||||
// process vout addresses and save the updated array
|
||||
arr_vout = processVoutAddresses([current_vout.scriptPubKey.address], current_vout.value, arr_vout);
|
||||
|
||||
// move to next vout
|
||||
loop();
|
||||
}
|
||||
} else {
|
||||
// process vout addresses
|
||||
processVoutAddresses(address_list, vout[i].value, arr_vout, function(vout_array) {
|
||||
// save updated array
|
||||
arr_vout = vout_array;
|
||||
// move to next vout
|
||||
loop.next();
|
||||
});
|
||||
// process vout addresses and save the updated array
|
||||
arr_vout = processVoutAddresses(address_list, current_vout.value, arr_vout);
|
||||
|
||||
// move to next vout
|
||||
loop();
|
||||
}
|
||||
} else {
|
||||
// TODO: add support for zerocoin transactions
|
||||
console.log('Zerocoin tx found. skipping for now as it is unsupported');
|
||||
tx_type = "zerocoin";
|
||||
loop.next();
|
||||
loop();
|
||||
}
|
||||
} else {
|
||||
// no address, move to next vout
|
||||
loop.next();
|
||||
loop();
|
||||
}
|
||||
}, function() {
|
||||
// check if zksnarks is enabled
|
||||
if (settings.blockchain_specific.zksnarks.enabled == true) {
|
||||
// check for hidden/anonymous outputs
|
||||
if (vhidden != null && vhidden.length > 0) {
|
||||
tx_type = "zksnarks";
|
||||
// loop through all hidden/anonymous outputs
|
||||
module.exports.syncLoop(vhidden.length, function (loop) {
|
||||
var i = loop.iteration();
|
||||
// check if zksnarks is enabled and there are any hidden/anonymous outputs
|
||||
if (settings.blockchain_specific.zksnarks.enabled == true && vhidden != null && vhidden.length > 0) {
|
||||
const hidden_address = 'hidden_address';
|
||||
tx_type = 'zksnarks';
|
||||
|
||||
if (vhidden[i].vpub_old > 0) {
|
||||
// process vout addresses
|
||||
processVoutAddresses(['hidden_address'], parseFloat(vhidden[i].vpub_old), arr_vout, function(vout_array) {
|
||||
// save updated array
|
||||
arr_vout = vout_array;
|
||||
// move to next vout
|
||||
loop.next();
|
||||
});
|
||||
} else {
|
||||
if ((!vout || vout.length == 0) && (!vin || vin.length == 0)) {
|
||||
// hidden sender is sending to hidden recipient
|
||||
// the sent and received values are not known in this case. only the fee paid is known and subtracted from the sender.
|
||||
// process vout addresses
|
||||
processVoutAddresses(['hidden_address'], 0, arr_vout, function(vout_array) {
|
||||
// save updated array
|
||||
arr_vout = vout_array;
|
||||
// add a private send address with the known amount sent
|
||||
module.exports.is_unique(arr_vin, 'hidden_address', 'addresses', function(unique, index) {
|
||||
if (unique == true) {
|
||||
module.exports.convert_to_satoshi(parseFloat(vhidden[i].vpub_new), function(amount_sat) {
|
||||
arr_vin.push({addresses: 'hidden_address', amount: amount_sat});
|
||||
// move to next vout
|
||||
loop.next();
|
||||
});
|
||||
} else {
|
||||
module.exports.convert_to_satoshi(parseFloat(vhidden[i].vpub_new), function(amount_sat) {
|
||||
arr_vin[index].amount = arr_vin[index].amount + amount_sat;
|
||||
// move to next vout
|
||||
loop.next();
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
// loop through all hidden/anonymous outputs
|
||||
async.eachSeries(vhidden, function(current_vhidden, loop) {
|
||||
if (current_vhidden.vpub_old > 0) {
|
||||
// process vout addresses and save the updated array
|
||||
arr_vout = processVoutAddresses([hidden_address], parseFloat(current_vhidden.vpub_old), arr_vout);
|
||||
|
||||
// move to next vout
|
||||
loop();
|
||||
} else {
|
||||
const amount_sat = module.exports.convert_to_satoshi(parseFloat(current_vhidden.vpub_new));
|
||||
const index = module.exports.is_unique(arr_vin, hidden_address, 'addresses');
|
||||
|
||||
if ((!vout || vout.length == 0) && (!vin || vin.length == 0)) {
|
||||
// hidden sender is sending to hidden recipient
|
||||
// the sent and received values are not known in this case. only the fee paid is known and subtracted from the sender.
|
||||
// process vout addresses and save the updated array
|
||||
arr_vout = processVoutAddresses([hidden_address], 0, arr_vout);
|
||||
|
||||
// add a private send address with the known amount sent
|
||||
if (index == -1) {
|
||||
// add hidden address to array
|
||||
arr_vin.push({addresses: hidden_address, amount: amount_sat});
|
||||
} else {
|
||||
module.exports.is_unique(arr_vin, 'hidden_address', 'addresses', function(unique, index) {
|
||||
if (unique == true) {
|
||||
module.exports.convert_to_satoshi(parseFloat(vhidden[i].vpub_new), function(amount_sat) {
|
||||
arr_vin.push({addresses: 'hidden_address', amount: amount_sat});
|
||||
// move to next vout
|
||||
loop.next();
|
||||
});
|
||||
} else {
|
||||
module.exports.convert_to_satoshi(parseFloat(vhidden[i].vpub_new), function(amount_sat) {
|
||||
arr_vin[index].amount = arr_vin[index].amount + amount_sat;
|
||||
// move to next vout
|
||||
loop.next();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof vout[0] !== 'undefined' && vout[0].scriptPubKey.type == 'nonstandard') {
|
||||
if (arr_vin.length > 0 && arr_vout.length > 0) {
|
||||
if (arr_vin[0].addresses == arr_vout[0].addresses) {
|
||||
//PoS
|
||||
arr_vout[0].amount = arr_vout[0].amount - arr_vin[0].amount;
|
||||
arr_vin.shift();
|
||||
|
||||
// check if any vin remains
|
||||
if (arr_vin == null || arr_vin.length == 0) {
|
||||
// empty vin should be linked to coinbase
|
||||
arr_vin = [{coinbase: "coinbase"}];
|
||||
|
||||
var new_vout = [];
|
||||
|
||||
// loop through the arr_vout to create a copy of the data with coin amounts only for use with prepare_vin()
|
||||
for (i = 0; i < arr_vout.length; i++) {
|
||||
new_vout.push({
|
||||
value: arr_vout[i].amount / 100000000
|
||||
});
|
||||
// update hidden address amount in array
|
||||
arr_vin[index].amount = arr_vin[index].amount + amount_sat;
|
||||
}
|
||||
|
||||
// call the prepare_vin again to populate the vin data correctly
|
||||
module.exports.prepare_vin({txid: txid, vin: arr_vin, vout: new_vout}, function(return_vin, return_tx_type_vin) {
|
||||
return cb(arr_vout, return_vin, return_tx_type_vin);
|
||||
});
|
||||
// move to next vout
|
||||
loop();
|
||||
} else {
|
||||
return cb(arr_vout, arr_vin, tx_type);
|
||||
// add a private send address with the known amount sent
|
||||
if (index == -1) {
|
||||
// add hidden address to array
|
||||
arr_vin.push({addresses: hidden_address, amount: amount_sat});
|
||||
} else {
|
||||
// update hidden address amount in array
|
||||
arr_vin[index].amount += amount_sat;
|
||||
}
|
||||
|
||||
// move to next vout
|
||||
loop();
|
||||
}
|
||||
} else
|
||||
return cb(arr_vout, arr_vin, tx_type);
|
||||
} else
|
||||
return cb(arr_vout, arr_vin, tx_type);
|
||||
} else
|
||||
return cb(arr_vout, arr_vin, tx_type);
|
||||
}
|
||||
}, function() {
|
||||
// finished updating hidden vout data
|
||||
finalize_vout(vout[0], arr_vout, arr_vin, tx_type, txid, function(final_arr_vout, final_arr_vin, final_tx_type) {
|
||||
return cb(final_arr_vout, final_arr_vin, final_tx_type);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// finalize vout data
|
||||
finalize_vout(vout[0], arr_vout, arr_vin, tx_type, txid, function(final_arr_vout, final_arr_vin, final_tx_type) {
|
||||
return cb(final_arr_vout, final_arr_vin, final_tx_type);
|
||||
});
|
||||
}
|
||||
});
|
||||
} catch(err) {
|
||||
// check if a "Maximum call stack size exceeded" error occurred
|
||||
@@ -1309,122 +1309,12 @@ module.exports = {
|
||||
}
|
||||
},
|
||||
|
||||
get_input_addresses: function(input, vout, cb) {
|
||||
var addresses = [];
|
||||
|
||||
if (input.coinbase) {
|
||||
var amount = 0;
|
||||
|
||||
module.exports.syncLoop(vout.length, function (loop) {
|
||||
var i = loop.iteration();
|
||||
|
||||
amount = amount + parseFloat(vout[i].value);
|
||||
loop.next();
|
||||
}, function() {
|
||||
addresses.push({hash: 'coinbase', amount: amount});
|
||||
return cb(addresses, null);
|
||||
});
|
||||
} else {
|
||||
module.exports.get_rawtransaction(input.txid, function(tx) {
|
||||
if (tx) {
|
||||
var tx_type = null;
|
||||
|
||||
module.exports.syncLoop(tx.vout.length, function (loop) {
|
||||
var i = loop.iteration();
|
||||
|
||||
if (tx.vout[i].n == input.vout) {
|
||||
if (tx.vout[i].scriptPubKey.addresses || tx.vout[i].scriptPubKey.address) {
|
||||
var new_address = tx.vout[i].scriptPubKey.address || tx.vout[i].scriptPubKey.addresses[0];
|
||||
|
||||
// check if address is inside an array
|
||||
if (Array.isArray(new_address)) {
|
||||
// extract the address
|
||||
new_address = new_address[0];
|
||||
}
|
||||
|
||||
module.exports.is_unique(addresses, new_address, 'hash', function(unique, index) {
|
||||
if (unique == true)
|
||||
addresses.push({hash: new_address, amount: tx.vout[i].value});
|
||||
else
|
||||
addresses[index].amount = addresses[index].amount + tx.vout[i].value;
|
||||
|
||||
loop.break(true);
|
||||
loop.next();
|
||||
});
|
||||
} else {
|
||||
// no addresses defined
|
||||
// check if bitcoin features are enabled
|
||||
if (settings.blockchain_specific.bitcoin.enabled == true) {
|
||||
// assume the asm value is a P2PK (Pay To Pubkey) public key that should be encoded as a P2PKH (Pay To Pubkey Hash) address
|
||||
encodeP2PKaddress(tx.vout[i].scriptPubKey.asm, function(p2pkh_address) {
|
||||
// check if the address was encoded properly
|
||||
if (p2pkh_address != null) {
|
||||
// mark this tx as p2pk
|
||||
tx_type = 'p2pk';
|
||||
|
||||
// check if address is inside an array
|
||||
if (Array.isArray(p2pkh_address)) {
|
||||
// extract the address
|
||||
p2pkh_address = p2pkh_address[0];
|
||||
}
|
||||
|
||||
// save the P2PKH address
|
||||
module.exports.is_unique(addresses, p2pkh_address, 'hash', function(unique, index) {
|
||||
if (unique == true)
|
||||
addresses.push({hash: p2pkh_address, amount: tx.vout[i].value});
|
||||
else
|
||||
addresses[index].amount = addresses[index].amount + tx.vout[i].value;
|
||||
|
||||
loop.break(true);
|
||||
loop.next();
|
||||
});
|
||||
} else {
|
||||
// could not decipher the address, save as unknown and move to next vin
|
||||
console.log('Failed to find vin address from tx ' + input.txid);
|
||||
module.exports.is_unique(addresses, 'unknown_address', 'hash', function(unique, index) {
|
||||
if (unique == true)
|
||||
addresses.push({hash: 'unknown_address', amount: tx.vout[i].value});
|
||||
else
|
||||
addresses[index].amount = addresses[index].amount + tx.vout[i].value;
|
||||
|
||||
loop.break(true);
|
||||
loop.next();
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// could not decipher the address, save as unknown and move to next vin
|
||||
console.log('Failed to find vin address from tx ' + input.txid);
|
||||
module.exports.is_unique(addresses, 'unknown_address', 'hash', function(unique, index) {
|
||||
if (unique == true)
|
||||
addresses.push({hash: 'unknown_address', amount: tx.vout[i].value});
|
||||
else
|
||||
addresses[index].amount = addresses[index].amount + tx.vout[i].value;
|
||||
|
||||
loop.break(true);
|
||||
loop.next();
|
||||
});
|
||||
}
|
||||
}
|
||||
} else
|
||||
loop.next();
|
||||
}, function() {
|
||||
return cb(addresses, tx_type);
|
||||
});
|
||||
} else
|
||||
return cb();
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
prepare_vin: function(tx, cb) {
|
||||
var arr_vin = [];
|
||||
var tx_type = null;
|
||||
let arr_vin = [];
|
||||
let tx_type = null;
|
||||
|
||||
module.exports.syncLoop(tx.vin.length, function (loop) {
|
||||
var i = loop.iteration();
|
||||
|
||||
module.exports.get_input_addresses(tx.vin[i], tx.vout, function(addresses, tx_type_vin) {
|
||||
async.eachSeries(tx.vin, function(vin, loop) {
|
||||
get_input_addresses(vin, tx.vout, function(addresses, tx_type_vin) {
|
||||
// check if the tx type is set
|
||||
if (tx_type_vin != null) {
|
||||
// set the tx type return value
|
||||
@@ -1432,28 +1322,25 @@ module.exports = {
|
||||
}
|
||||
|
||||
if (addresses && addresses.length) {
|
||||
module.exports.is_unique(arr_vin, addresses[0].hash, 'addresses', function(unique, index) {
|
||||
if (unique == true) {
|
||||
module.exports.convert_to_satoshi(parseFloat(addresses[0].amount), function(amount_sat) {
|
||||
arr_vin.push({addresses: addresses[0].hash, amount: amount_sat});
|
||||
loop.next();
|
||||
});
|
||||
} else {
|
||||
module.exports.convert_to_satoshi(parseFloat(addresses[0].amount), function(amount_sat) {
|
||||
arr_vin[index].amount = arr_vin[index].amount + amount_sat;
|
||||
loop.next();
|
||||
});
|
||||
}
|
||||
});
|
||||
const amount_sat = module.exports.convert_to_satoshi(parseFloat(addresses[0].amount));
|
||||
const index = module.exports.is_unique(arr_vin, addresses[0].hash, 'addresses');
|
||||
|
||||
if (index == -1)
|
||||
arr_vin.push({addresses: addresses[0].hash, amount: amount_sat});
|
||||
else
|
||||
arr_vin[index].amount += amount_sat;
|
||||
|
||||
loop();
|
||||
} else {
|
||||
// could not decipher the address, save as unknown and move to next vin
|
||||
console.log('Failed to find vin address from tx ' + tx.txid);
|
||||
module.exports.is_unique(arr_vin, 'unknown_address', 'addresses', function(unique, index) {
|
||||
if (unique == true)
|
||||
arr_vin.push({addresses: 'unknown_address', amount: 0});
|
||||
|
||||
loop.next();
|
||||
});
|
||||
const index = module.exports.is_unique(arr_vin, 'unknown_address', 'addresses');
|
||||
|
||||
if (index == -1)
|
||||
arr_vin.push({addresses: 'unknown_address', amount: 0});
|
||||
|
||||
loop();
|
||||
}
|
||||
});
|
||||
}, function() {
|
||||
|
||||
Reference in New Issue
Block a user