Added blockHandler.

This commit is contained in:
Chris Kleeschulte 2017-01-18 18:23:17 -05:00
parent aeeaba0754
commit 43dfeffd5e
2 changed files with 70 additions and 73 deletions

View File

@ -9,19 +9,24 @@ var $ = bitcore.util.preconditions;
var exports = {}; var exports = {};
exports.encodeAddressIndexKey = function(hashTypeBuffer, hashBuffer, height, txidBuffer, index, spending) { exports.encodeAddressIndexKey = function(address, isSpent, height, txidBuffer, index, spending) {
var addressSizeBuffer = new Buffer(1);
addressSizeBuffer.writeUInt8(address.length);
var addressBuffer = new Buffer(address, 'utf8');
var heightBuffer = new Buffer(4); var heightBuffer = new Buffer(4);
heightBuffer.writeUInt32BE(height); heightBuffer.writeUInt32BE(height);
var indexBuffer = new Buffer(4); var indexBuffer = new Buffer(4);
indexBuffer.writeUInt32BE(index); indexBuffer.writeUInt32BE(index);
var spendingBuffer = new Buffer(1); var spendingBuffer = new Buffer(1);
spendingBuffer.writeUInt8(spending); spendingBuffer.writeUInt8(spending);
var isSpentBuffer = new Buffer(1);
isSpentBuffer.writeUInt8(isSpent);
var key = Buffer.concat({ var key = Buffer.concat({
constants.PREFIXES.ADDRESS, constants.PREFIXES.ADDRESS,
hashTypeBuffer, addressSizeBuffer,
hashBuffer, addressBuffer,
constants.SPACER_MIN, isSpentBuffer,
heightBuffer, heightBuffer,
txidBuffer, txidBuffer,
indexBuffer, indexBuffer,
@ -32,34 +37,38 @@ exports.encodeAddressIndexKey = function(hashTypeBuffer, hashBuffer, height, txi
exports.decodeAddressIndexKey = function(buffer) { exports.decodeAddressIndexKey = function(buffer) {
var reader = new BufferReader(buffer); var reader = new BufferReader(buffer);
var prefix = reader.read(1); var prefix = reader.read(1);
var hashTypeBuffer = reader.read(1);
var hashBuffer = reader.read(20);
var spacer = reader.read(1); var addressSize = reader.readUInt8();
var address = reader.read(addressSize).toString('utf8');
var isSpent = reader.readUInt8();
var height = reader.readUInt32BE(); var height = reader.readUInt32BE();
var txid = reader.read(32); var txid = reader.read(32);
var index = reader.readUInt32BE(); var index = reader.readUInt32BE();
var spending = reader.readUInt8(); var spending = reader.readUInt8();
return { return {
prefix: prefix, prefix: prefix,
hashTypeBuffer: hashTypeBuffer, address: address,
hashBuffer: hashBuffer, isSpent: isSpent ? true : false,
height: height, height: height,
txid: txid, txid: txid,
index: outputIndex, index: outputIndex,
spending: spending spending: spending ? true : false
}; };
}; };
exports.encodeAddressIndexValue = function(satoshis) { exports.encodeAddressIndexValue = function(satoshis, scriptBuffer) {
var satoshisBuffer = new Buffer(8); var satoshisBuffer = new Buffer(8);
satoshisBuffer.writeDoubleBE(satoshis); satoshisBuffer.writeDoubleBE(satoshis);
return satoshisBuffer; return Buffer.concat([satoshisBuffer, scriptBuffer]);
}; };
exports.decodeAddressIndexValue = function(buffer) { exports.decodeAddressIndexValue = function(buffer) {
var satoshis = buffer.readDoubleBE(0); var satoshis = buffer.readDoubleBE(0);
return satoshis; var scriptBuffer = buffer.slice(8, buffer.length);
return {
satoshis: satoshis,
script: scriptBuffer
};
}; };
exports.encodeUnspentIndexKey = function(hashTypeBuffer, hashBuffer, txidBuffer, index) { exports.encodeUnspentIndexKey = function(hashTypeBuffer, hashBuffer, txidBuffer, index) {
@ -367,7 +376,7 @@ exports.decodeSummaryCacheValue = function(buffer) {
exports.getAddressInfo = function(addressStr) { exports.getAddressInfo = function(addressStr) {
var addrObj = bitcore.Address(addressStr); var addrObj = bitcore.Address(addressStr);
var hashTypeBuffer = constants.HASH_TYPES_MAP[addrObj.type]; var hashTypeBuffer = constants.HASH_TYPES_MAP[addrObj.type];
return { return {
hashBuffer: addrObj.hashBuffer, hashBuffer: addrObj.hashBuffer,
hashTypeBuffer: hashTypeBuffer, hashTypeBuffer: hashTypeBuffer,

View File

@ -49,6 +49,8 @@ var AddressService = function(options) {
this.maxInputsQueryLength = options.maxInputsQueryLength || constants.MAX_INPUTS_QUERY_LENGTH; this.maxInputsQueryLength = options.maxInputsQueryLength || constants.MAX_INPUTS_QUERY_LENGTH;
this.maxOutputsQueryLength = options.maxOutputsQueryLength || constants.MAX_OUTPUTS_QUERY_LENGTH; this.maxOutputsQueryLength = options.maxOutputsQueryLength || constants.MAX_OUTPUTS_QUERY_LENGTH;
this.concurrency = options.concurrency || 20;
this._setMempoolIndexPath(); this._setMempoolIndexPath();
if (options.mempoolMemoryIndex) { if (options.mempoolMemoryIndex) {
this.levelupStore = memdown; this.levelupStore = memdown;
@ -450,23 +452,23 @@ AddressService.prototype.updateMempoolIndex = function(tx, add, callback) {
* @param {Boolean} addOutput - If the block is being removed or added to the chain * @param {Boolean} addOutput - If the block is being removed or added to the chain
* @param {Function} callback * @param {Function} callback
*/ */
AddressService.prototype.blockHandler = function(block, addOutput, callback) { AddressService.prototype.blockHandler = function(block, connectBlock, callback) {
var txs = block.transactions; var txs = block.transactions;
var height = block.__height; var height = block.__height;
var action = 'put'; var action = 'put';
if (!addOutput) { var reverseAction = 'del';
if (!connectBlock) {
action = 'del'; action = 'del';
reverseAction = 'put';
} }
var operations = []; var operations = [];
var transactionLength = txs.length; var transactionLength = txs.length;
for (var i = 0; i < transactionLength; i++) { async.eachLimit(txs, self.concurrency, function(tx, next) {
var tx = txs[i];
var txid = tx.id; var txid = tx.id;
var txidBuffer = new Buffer(txid, 'hex');
var inputs = tx.inputs; var inputs = tx.inputs;
var outputs = tx.outputs; var outputs = tx.outputs;
@ -478,24 +480,15 @@ AddressService.prototype.blockHandler = function(block, addOutput, callback) {
var output = outputs[outputIndex]; var output = outputs[outputIndex];
var script = output.script; var script = output.script;
var address = script.toAddress();
if(!script) { if(!script) {
log.debug('Invalid script'); log.debug('Invalid script');
continue; continue;
} }
var addressInfo = encoding.extractAddressInfoFromScript(script, this.node.network);
if (!addressInfo) {
continue;
}
// We need to use the height for indexes (and not the timestamp) because the var key = encoding.encodeAddressIndexKey(address, false, height, txid, outputIndex);
// the timestamp has unreliable sequential ordering. The next block
// can have a time that is previous to the previous block (however not
// less than the mean of the 11 previous blocks) and not greater than 2
// hours in the future.
var key = encoding.encodeOutputKey(addressInfo.hashBuffer, addressInfo.hashTypeBuffer,
height, txidBuffer, outputIndex);
var value = encoding.encodeOutputValue(output.satoshis, output._scriptBuffer); var value = encoding.encodeOutputValue(output.satoshis, output._scriptBuffer);
operations.push({ operations.push({
type: action, type: action,
@ -503,22 +496,20 @@ AddressService.prototype.blockHandler = function(block, addOutput, callback) {
value: value value: value
}); });
addressInfo.hashHex = addressInfo.hashBuffer.toString('hex');
// Collect data for subscribers // Collect data for subscribers
if (txmessages[addressInfo.hashHex]) { if (txmessages[address]) {
txmessages[addressInfo.hashHex].outputIndexes.push(outputIndex); txmessages[address].outputIndexes.push(outputIndex);
} else { } else {
txmessages[addressInfo.hashHex] = { txmessages[address] = {
tx: tx, tx: tx,
height: height, height: height,
outputIndexes: [outputIndex], outputIndexes: [outputIndex],
addressInfo: addressInfo, address: address,
timestamp: block.header.timestamp timestamp: block.header.timestamp
}; };
} }
this.balanceEventHandler(block, addressInfo); this.balanceEventHandler(block, address);
} }
@ -528,53 +519,50 @@ AddressService.prototype.blockHandler = function(block, addOutput, callback) {
} }
if(tx.isCoinbase()) { if(tx.isCoinbase()) {
continue; return next();
} }
for(var inputIndex = 0; inputIndex < inputs.length; inputIndex++) { async.eachLimit(inputs, self.concurrency, function(input, next) {
var input = inputs[inputIndex]; var input = inputs[inputIndex];
var inputHash; var inputHash;
var inputHashType; var inputHashType;
if (input.script.isPublicKeyHashIn()) { var address = input.script.toAddress();
inputHash = Hash.sha256ripemd160(input.script.chunks[1].buf);
inputHashType = constants.HASH_TYPES.PUBKEY;
} else if (input.script.isScriptHashIn()) {
inputHash = Hash.sha256ripemd160(input.script.chunks[input.script.chunks.length - 1].buf);
inputHashType = constants.HASH_TYPES.REDEEMSCRIPT;
} else {
continue;
}
var prevTxIdBuffer = new Buffer(input.prevTxId, 'hex');
// To be able to query inputs by address and spent height // To be able to query inputs by address and spent height
var inputKey = encoding.encodeInputKey(inputHash, inputHashType, height, prevTxIdBuffer, input.outputIndex); var inputKey = encoding.encodeAddressIndexKey(address, true, height, txid, inputIndex, true);
var inputValue = encoding.encodeInputValue(txidBuffer, inputIndex); self.node.services.transaction.getTransaction(input.prevTxId, function(err, tx) {
if(err) {
operations.push({ return next(err);
type: action, }
key: inputKey, var output = tx.outputs[input.outputIndex];
value: inputValue var outputKey = encoding.encodeAddressIndexKey(address, true, tx.__height, tx.id, input.outputIndex, false);
var outputKeyToDelete = encoding.encodeAddressIndexKey(address, false, tx.__height, tx.id, input.outputIndex, false);
var outputValue = encoding.encodeAdressIndexValue(output.satoshis, output._scriptBuffer);
var inputValue = encoding.encodeAddressIndexValue(output.satoshis, input._scriptBuffer);
operations = operations.concat([{
type: action,
key: inputKey,
value: inputValue
},
{
type: action,
key: outputKey,
value: outputValue
},
{
type: reverseAction,
key: outputKeyToDelete,
value: outputValue
}]);
next();
}); });
// To be able to search for an input spending an output }, function(err) {
var inputKeyMap = encoding.encodeInputKeyMap(prevTxIdBuffer, input.outputIndex); callback(err, operations);
var inputValueMap = encoding.encodeInputValueMap(txidBuffer, inputIndex); });
operations.push({
type: action,
key: inputKeyMap,
value: inputValueMap
});
}
}
setImmediate(function() {
callback(null, operations);
}); });
}; };
/** /**