get address index to work
This commit is contained in:
parent
df8cf1bbb3
commit
efa8480651
@ -264,6 +264,7 @@ Node.prototype.start = function(callback) {
|
|||||||
if (err) {
|
if (err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.emit('ready');
|
self.emit('ready');
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -139,6 +139,7 @@ function loadModule(req, service) {
|
|||||||
var serviceFile = path.resolve(__dirname, '../services/' + service.name);
|
var serviceFile = path.resolve(__dirname, '../services/' + service.name);
|
||||||
service.module = req(serviceFile);
|
service.module = req(serviceFile);
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
|
console.log('Could not load ' + service.name + ' service');
|
||||||
log.error(e.stack);
|
log.error(e.stack);
|
||||||
var servicePackage = req(service.name + '/package.json');
|
var servicePackage = req(service.name + '/package.json');
|
||||||
var serviceModule = service.name;
|
var serviceModule = service.name;
|
||||||
|
|||||||
@ -9,12 +9,17 @@ var $ = bitcore.util.preconditions;
|
|||||||
|
|
||||||
var exports = {};
|
var exports = {};
|
||||||
|
|
||||||
exports.encodeAddressIndexKey = function(address, isSpent, height, txidBuffer, index, spending) {
|
function Encoding(prefix) {
|
||||||
|
this.prefix = prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
Encoding.prototype.encodeAddressIndexKey = function(address, isSpent, height, txid, index, spending) {
|
||||||
var addressSizeBuffer = new Buffer(1);
|
var addressSizeBuffer = new Buffer(1);
|
||||||
addressSizeBuffer.writeUInt8(address.length);
|
addressSizeBuffer.writeUInt8(address.length);
|
||||||
var addressBuffer = new Buffer(address, 'utf8');
|
var addressBuffer = new Buffer(address, 'utf8');
|
||||||
var heightBuffer = new Buffer(4);
|
var heightBuffer = new Buffer(4);
|
||||||
heightBuffer.writeUInt32BE(height);
|
heightBuffer.writeUInt32BE(height);
|
||||||
|
var txidBuffer = new Buffer(txid, 'hex');
|
||||||
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);
|
||||||
@ -22,8 +27,8 @@ exports.encodeAddressIndexKey = function(address, isSpent, height, txidBuffer, i
|
|||||||
var isSpentBuffer = new Buffer(1);
|
var isSpentBuffer = new Buffer(1);
|
||||||
isSpentBuffer.writeUInt8(isSpent);
|
isSpentBuffer.writeUInt8(isSpent);
|
||||||
|
|
||||||
var key = Buffer.concat({
|
return Buffer.concat([
|
||||||
constants.PREFIXES.ADDRESS,
|
this.prefix,
|
||||||
addressSizeBuffer,
|
addressSizeBuffer,
|
||||||
addressBuffer,
|
addressBuffer,
|
||||||
isSpentBuffer,
|
isSpentBuffer,
|
||||||
@ -31,38 +36,37 @@ exports.encodeAddressIndexKey = function(address, isSpent, height, txidBuffer, i
|
|||||||
txidBuffer,
|
txidBuffer,
|
||||||
indexBuffer,
|
indexBuffer,
|
||||||
spendingBuffer
|
spendingBuffer
|
||||||
});
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.decodeAddressIndexKey = function(buffer) {
|
Encoding.prototype.decodeAddressIndexKey = function(buffer) {
|
||||||
var reader = new BufferReader(buffer);
|
var reader = new BufferReader(buffer);
|
||||||
var prefix = reader.read(1);
|
var prefix = reader.read(2);
|
||||||
|
|
||||||
var addressSize = reader.readUInt8();
|
var addressSize = reader.readUInt8();
|
||||||
var address = reader.read(addressSize).toString('utf8');
|
var address = reader.read(addressSize).toString('utf8');
|
||||||
var isSpent = reader.readUInt8();
|
var isSpent = reader.readUInt8();
|
||||||
var height = reader.readUInt32BE();
|
var height = reader.readUInt32BE();
|
||||||
var txid = reader.read(32);
|
var txid = reader.read(32).toString('hex');
|
||||||
var index = reader.readUInt32BE();
|
var index = reader.readUInt32BE();
|
||||||
var spending = reader.readUInt8();
|
var spending = reader.readUInt8();
|
||||||
return {
|
return {
|
||||||
prefix: prefix,
|
|
||||||
address: address,
|
address: address,
|
||||||
isSpent: isSpent ? true : false,
|
isSpent: isSpent ? true : false,
|
||||||
height: height,
|
height: height,
|
||||||
txid: txid,
|
txid: txid,
|
||||||
index: outputIndex,
|
index: index,
|
||||||
spending: spending ? true : false
|
spending: spending ? true : false
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.encodeAddressIndexValue = function(satoshis, scriptBuffer) {
|
Encoding.prototype.encodeAddressIndexValue = function(satoshis, scriptBuffer) {
|
||||||
var satoshisBuffer = new Buffer(8);
|
var satoshisBuffer = new Buffer(8);
|
||||||
satoshisBuffer.writeDoubleBE(satoshis);
|
satoshisBuffer.writeDoubleBE(satoshis);
|
||||||
return Buffer.concat([satoshisBuffer, scriptBuffer]);
|
return Buffer.concat([satoshisBuffer, scriptBuffer]);
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.decodeAddressIndexValue = function(buffer) {
|
Encoding.prototype.decodeAddressIndexValue = function(buffer) {
|
||||||
var satoshis = buffer.readDoubleBE(0);
|
var satoshis = buffer.readDoubleBE(0);
|
||||||
var scriptBuffer = buffer.slice(8, buffer.length);
|
var scriptBuffer = buffer.slice(8, buffer.length);
|
||||||
return {
|
return {
|
||||||
@ -71,354 +75,355 @@ exports.decodeAddressIndexValue = function(buffer) {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.encodeUnspentIndexKey = function(hashTypeBuffer, hashBuffer, txidBuffer, index) {
|
// exports.encodeUnspentIndexKey = function(hashTypeBuffer, hashBuffer, txid, index) {
|
||||||
var indexBuffer = new Buffer(4);
|
// var indexBuffer = new Buffer(4);
|
||||||
indexBuffer.writeUInt32BE(index);
|
// indexBuffer.writeUInt32BE(index);
|
||||||
|
// var txidBuffer = new Buffer(txid, 'hex');
|
||||||
|
|
||||||
var key = Buffer.concat({
|
// var key = Buffer.concat([
|
||||||
constants.PREFIXES.UNSPENT,
|
// constants.PREFIXES.UNSPENT,
|
||||||
hashTypeBuffer,
|
// hashTypeBuffer,
|
||||||
hashBuffer,
|
// hashBuffer,
|
||||||
constants.SPACER_MIN,
|
// constants.SPACER_MIN,
|
||||||
txidBuffer,
|
// txidBuffer,
|
||||||
indexBuffer
|
// indexBuffer
|
||||||
});
|
// ]);
|
||||||
};
|
// };
|
||||||
|
|
||||||
exports.decodeUnspentIndexKey = function(buffer) {
|
// exports.decodeUnspentIndexKey = 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 hashTypeBuffer = reader.read(1);
|
||||||
var hashBuffer = reader.read(20);
|
// var hashBuffer = reader.read(20);
|
||||||
|
|
||||||
var spacer = reader.read(1);
|
// var spacer = reader.read(1);
|
||||||
var txid = reader.read(32);
|
// var txid = reader.read(32);
|
||||||
var index = reader.readUInt32BE();
|
// var index = reader.readUInt32BE();
|
||||||
return {
|
// return {
|
||||||
prefix: prefix,
|
// prefix: prefix,
|
||||||
hashTypeBuffer: hashTypeBuffer,
|
// hashTypeBuffer: hashTypeBuffer,
|
||||||
hashBuffer: hashBuffer,
|
// hashBuffer: hashBuffer,
|
||||||
txid: txid,
|
// txid: txid,
|
||||||
index: outputIndex
|
// index: outputIndex
|
||||||
};
|
// };
|
||||||
};
|
// };
|
||||||
|
|
||||||
exports.encodeUnspentIndexValue = function(satoshis, height, scriptBuffer) {
|
// exports.encodeUnspentIndexValue = function(satoshis, height, scriptBuffer) {
|
||||||
var satoshisBuffer = new Buffer(8);
|
// var satoshisBuffer = new Buffer(8);
|
||||||
satoshisBuffer.writeDoubleBE(satoshis);
|
// satoshisBuffer.writeDoubleBE(satoshis);
|
||||||
|
|
||||||
var heightBuffer = new Buffer(4);
|
// var heightBuffer = new Buffer(4);
|
||||||
heightBuffer.writeUInt32BE(height);
|
// heightBuffer.writeUInt32BE(height);
|
||||||
|
|
||||||
return Buffer.concat([satoshisBuffer, heightBuffer, scriptBuffer]);
|
// return Buffer.concat([satoshisBuffer, heightBuffer, scriptBuffer]);
|
||||||
};
|
// };
|
||||||
|
|
||||||
exports.decodeUnspentIndexValue = function(buffer) {
|
// exports.decodeUnspentIndexValue = function(buffer) {
|
||||||
var satoshis = buffer.readDoubleBE(0);
|
// var satoshis = buffer.readDoubleBE(0);
|
||||||
var height = buffer.readUInt32BE(8);
|
// var height = buffer.readUInt32BE(8);
|
||||||
var scriptBuffer = buffer.slice(12, buffer.length);
|
// var scriptBuffer = buffer.slice(12, buffer.length);
|
||||||
|
|
||||||
return {
|
// return {
|
||||||
satoshis: satoshis,
|
// satoshis: satoshis,
|
||||||
height: height,
|
// height: height,
|
||||||
scriptBuffer: scriptBuffer
|
// scriptBuffer: scriptBuffer
|
||||||
};
|
// };
|
||||||
};
|
// };
|
||||||
|
|
||||||
|
|
||||||
exports.encodeSpentIndexSyncKey = function(txidBuffer, outputIndex) {
|
// exports.encodeSpentIndexSyncKey = function(txidBuffer, outputIndex) {
|
||||||
var outputIndexBuffer = new Buffer(4);
|
// var outputIndexBuffer = new Buffer(4);
|
||||||
outputIndexBuffer.writeUInt32BE(outputIndex);
|
// outputIndexBuffer.writeUInt32BE(outputIndex);
|
||||||
var key = Buffer.concat([
|
// var key = Buffer.concat([
|
||||||
txidBuffer,
|
// txidBuffer,
|
||||||
outputIndexBuffer
|
// outputIndexBuffer
|
||||||
]);
|
// ]);
|
||||||
return key.toString('binary');
|
// return key.toString('binary');
|
||||||
};
|
// };
|
||||||
|
|
||||||
exports.encodeMempoolAddressIndexKey = function(hashBuffer, hashTypeBuffer) {
|
// exports.encodeMempoolAddressIndexKey = function(hashBuffer, hashTypeBuffer) {
|
||||||
var key = Buffer.concat([
|
// var key = Buffer.concat([
|
||||||
hashBuffer,
|
// hashBuffer,
|
||||||
hashTypeBuffer,
|
// hashTypeBuffer,
|
||||||
]);
|
// ]);
|
||||||
return key.toString('binary');
|
// return key.toString('binary');
|
||||||
};
|
// };
|
||||||
|
|
||||||
|
|
||||||
exports.encodeOutputKey = function(hashBuffer, hashTypeBuffer, height, txidBuffer, outputIndex) {
|
// exports.encodeOutputKey = function(hashBuffer, hashTypeBuffer, height, txidBuffer, outputIndex) {
|
||||||
var heightBuffer = new Buffer(4);
|
// var heightBuffer = new Buffer(4);
|
||||||
heightBuffer.writeUInt32BE(height);
|
// heightBuffer.writeUInt32BE(height);
|
||||||
var outputIndexBuffer = new Buffer(4);
|
// var outputIndexBuffer = new Buffer(4);
|
||||||
outputIndexBuffer.writeUInt32BE(outputIndex);
|
// outputIndexBuffer.writeUInt32BE(outputIndex);
|
||||||
var key = Buffer.concat([
|
// var key = Buffer.concat([
|
||||||
constants.PREFIXES.OUTPUTS,
|
// constants.PREFIXES.OUTPUTS,
|
||||||
hashBuffer,
|
// hashBuffer,
|
||||||
hashTypeBuffer,
|
// hashTypeBuffer,
|
||||||
constants.SPACER_MIN,
|
// constants.SPACER_MIN,
|
||||||
heightBuffer,
|
// heightBuffer,
|
||||||
txidBuffer,
|
// txidBuffer,
|
||||||
outputIndexBuffer
|
// outputIndexBuffer
|
||||||
]);
|
// ]);
|
||||||
return key;
|
// return key;
|
||||||
};
|
// };
|
||||||
|
|
||||||
exports.decodeOutputKey = function(buffer) {
|
// exports.decodeOutputKey = function(buffer) {
|
||||||
var reader = new BufferReader(buffer);
|
// var reader = new BufferReader(buffer);
|
||||||
var prefix = reader.read(1);
|
// var prefix = reader.read(1);
|
||||||
var hashBuffer = reader.read(20);
|
// var hashBuffer = reader.read(20);
|
||||||
var hashTypeBuffer = reader.read(1);
|
// var hashTypeBuffer = reader.read(1);
|
||||||
var spacer = reader.read(1);
|
// var spacer = reader.read(1);
|
||||||
var height = reader.readUInt32BE();
|
// var height = reader.readUInt32BE();
|
||||||
var txid = reader.read(32);
|
// var txid = reader.read(32);
|
||||||
var outputIndex = reader.readUInt32BE();
|
// var outputIndex = reader.readUInt32BE();
|
||||||
return {
|
// return {
|
||||||
prefix: prefix,
|
// prefix: prefix,
|
||||||
hashBuffer: hashBuffer,
|
// hashBuffer: hashBuffer,
|
||||||
hashTypeBuffer: hashTypeBuffer,
|
// hashTypeBuffer: hashTypeBuffer,
|
||||||
height: height,
|
// height: height,
|
||||||
txid: txid,
|
// txid: txid,
|
||||||
outputIndex: outputIndex
|
// outputIndex: outputIndex
|
||||||
};
|
// };
|
||||||
};
|
// };
|
||||||
|
|
||||||
exports.encodeOutputValue = function(satoshis, scriptBuffer) {
|
// exports.encodeOutputValue = function(satoshis, scriptBuffer) {
|
||||||
var satoshisBuffer = new Buffer(8);
|
// var satoshisBuffer = new Buffer(8);
|
||||||
satoshisBuffer.writeDoubleBE(satoshis);
|
// satoshisBuffer.writeDoubleBE(satoshis);
|
||||||
return Buffer.concat([satoshisBuffer, scriptBuffer]);
|
// return Buffer.concat([satoshisBuffer, scriptBuffer]);
|
||||||
};
|
// };
|
||||||
|
|
||||||
exports.encodeOutputMempoolValue = function(satoshis, timestampBuffer, scriptBuffer) {
|
// exports.encodeOutputMempoolValue = function(satoshis, timestampBuffer, scriptBuffer) {
|
||||||
var satoshisBuffer = new Buffer(8);
|
// var satoshisBuffer = new Buffer(8);
|
||||||
satoshisBuffer.writeDoubleBE(satoshis);
|
// satoshisBuffer.writeDoubleBE(satoshis);
|
||||||
return Buffer.concat([satoshisBuffer, timestampBuffer, scriptBuffer]);
|
// return Buffer.concat([satoshisBuffer, timestampBuffer, scriptBuffer]);
|
||||||
};
|
// };
|
||||||
|
|
||||||
exports.decodeOutputValue = function(buffer) {
|
// exports.decodeOutputValue = function(buffer) {
|
||||||
var satoshis = buffer.readDoubleBE(0);
|
// var satoshis = buffer.readDoubleBE(0);
|
||||||
var scriptBuffer = buffer.slice(8, buffer.length);
|
// var scriptBuffer = buffer.slice(8, buffer.length);
|
||||||
return {
|
// return {
|
||||||
satoshis: satoshis,
|
// satoshis: satoshis,
|
||||||
scriptBuffer: scriptBuffer
|
// scriptBuffer: scriptBuffer
|
||||||
};
|
// };
|
||||||
};
|
// };
|
||||||
|
|
||||||
exports.decodeOutputMempoolValue = function(buffer) {
|
// exports.decodeOutputMempoolValue = function(buffer) {
|
||||||
var satoshis = buffer.readDoubleBE(0);
|
// var satoshis = buffer.readDoubleBE(0);
|
||||||
var timestamp = buffer.readDoubleBE(8);
|
// var timestamp = buffer.readDoubleBE(8);
|
||||||
var scriptBuffer = buffer.slice(16, buffer.length);
|
// var scriptBuffer = buffer.slice(16, buffer.length);
|
||||||
return {
|
// return {
|
||||||
satoshis: satoshis,
|
// satoshis: satoshis,
|
||||||
timestamp: timestamp,
|
// timestamp: timestamp,
|
||||||
scriptBuffer: scriptBuffer
|
// scriptBuffer: scriptBuffer
|
||||||
};
|
// };
|
||||||
};
|
// };
|
||||||
|
|
||||||
exports.encodeInputKey = function(hashBuffer, hashTypeBuffer, height, prevTxIdBuffer, outputIndex) {
|
// exports.encodeInputKey = function(hashBuffer, hashTypeBuffer, height, prevTxIdBuffer, outputIndex) {
|
||||||
var heightBuffer = new Buffer(4);
|
// var heightBuffer = new Buffer(4);
|
||||||
heightBuffer.writeUInt32BE(height);
|
// heightBuffer.writeUInt32BE(height);
|
||||||
var outputIndexBuffer = new Buffer(4);
|
// var outputIndexBuffer = new Buffer(4);
|
||||||
outputIndexBuffer.writeUInt32BE(outputIndex);
|
// outputIndexBuffer.writeUInt32BE(outputIndex);
|
||||||
return Buffer.concat([
|
// return Buffer.concat([
|
||||||
constants.PREFIXES.SPENTS,
|
// constants.PREFIXES.SPENTS,
|
||||||
hashBuffer,
|
// hashBuffer,
|
||||||
hashTypeBuffer,
|
// hashTypeBuffer,
|
||||||
constants.SPACER_MIN,
|
// constants.SPACER_MIN,
|
||||||
heightBuffer,
|
// heightBuffer,
|
||||||
prevTxIdBuffer,
|
// prevTxIdBuffer,
|
||||||
outputIndexBuffer
|
// outputIndexBuffer
|
||||||
]);
|
// ]);
|
||||||
};
|
// };
|
||||||
|
|
||||||
exports.decodeInputKey = function(buffer) {
|
// exports.decodeInputKey = function(buffer) {
|
||||||
var reader = new BufferReader(buffer);
|
// var reader = new BufferReader(buffer);
|
||||||
var prefix = reader.read(1);
|
// var prefix = reader.read(1);
|
||||||
var hashBuffer = reader.read(20);
|
// var hashBuffer = reader.read(20);
|
||||||
var hashTypeBuffer = reader.read(1);
|
// var hashTypeBuffer = reader.read(1);
|
||||||
var spacer = reader.read(1);
|
// var spacer = reader.read(1);
|
||||||
var height = reader.readUInt32BE();
|
// var height = reader.readUInt32BE();
|
||||||
var prevTxId = reader.read(32);
|
// var prevTxId = reader.read(32);
|
||||||
var outputIndex = reader.readUInt32BE();
|
// var outputIndex = reader.readUInt32BE();
|
||||||
return {
|
// return {
|
||||||
prefix: prefix,
|
// prefix: prefix,
|
||||||
hashBuffer: hashBuffer,
|
// hashBuffer: hashBuffer,
|
||||||
hashTypeBuffer: hashTypeBuffer,
|
// hashTypeBuffer: hashTypeBuffer,
|
||||||
height: height,
|
// height: height,
|
||||||
prevTxId: prevTxId,
|
// prevTxId: prevTxId,
|
||||||
outputIndex: outputIndex
|
// outputIndex: outputIndex
|
||||||
};
|
// };
|
||||||
};
|
// };
|
||||||
|
|
||||||
exports.encodeInputValue = function(txidBuffer, inputIndex) {
|
// exports.encodeInputValue = function(txidBuffer, inputIndex) {
|
||||||
var inputIndexBuffer = new Buffer(4);
|
// var inputIndexBuffer = new Buffer(4);
|
||||||
inputIndexBuffer.writeUInt32BE(inputIndex);
|
// inputIndexBuffer.writeUInt32BE(inputIndex);
|
||||||
return Buffer.concat([
|
// return Buffer.concat([
|
||||||
txidBuffer,
|
// txidBuffer,
|
||||||
inputIndexBuffer
|
// inputIndexBuffer
|
||||||
]);
|
// ]);
|
||||||
};
|
// };
|
||||||
|
|
||||||
exports.decodeInputValue = function(buffer) {
|
// exports.decodeInputValue = function(buffer) {
|
||||||
var txid = buffer.slice(0, 32);
|
// var txid = buffer.slice(0, 32);
|
||||||
var inputIndex = buffer.readUInt32BE(32);
|
// var inputIndex = buffer.readUInt32BE(32);
|
||||||
return {
|
// return {
|
||||||
txid: txid,
|
// txid: txid,
|
||||||
inputIndex: inputIndex
|
// inputIndex: inputIndex
|
||||||
};
|
// };
|
||||||
};
|
// };
|
||||||
|
|
||||||
exports.encodeInputKeyMap = function(outputTxIdBuffer, outputIndex) {
|
// exports.encodeInputKeyMap = function(outputTxIdBuffer, outputIndex) {
|
||||||
var outputIndexBuffer = new Buffer(4);
|
// var outputIndexBuffer = new Buffer(4);
|
||||||
outputIndexBuffer.writeUInt32BE(outputIndex);
|
// outputIndexBuffer.writeUInt32BE(outputIndex);
|
||||||
return Buffer.concat([
|
// return Buffer.concat([
|
||||||
constants.PREFIXES.SPENTSMAP,
|
// constants.PREFIXES.SPENTSMAP,
|
||||||
outputTxIdBuffer,
|
// outputTxIdBuffer,
|
||||||
outputIndexBuffer
|
// outputIndexBuffer
|
||||||
]);
|
// ]);
|
||||||
};
|
// };
|
||||||
|
|
||||||
exports.decodeInputKeyMap = function(buffer) {
|
// exports.decodeInputKeyMap = function(buffer) {
|
||||||
var txid = buffer.slice(1, 33);
|
// var txid = buffer.slice(1, 33);
|
||||||
var outputIndex = buffer.readUInt32BE(33);
|
// var outputIndex = buffer.readUInt32BE(33);
|
||||||
return {
|
// return {
|
||||||
outputTxId: txid,
|
// outputTxId: txid,
|
||||||
outputIndex: outputIndex
|
// outputIndex: outputIndex
|
||||||
};
|
// };
|
||||||
};
|
// };
|
||||||
|
|
||||||
exports.encodeInputValueMap = function(inputTxIdBuffer, inputIndex) {
|
// exports.encodeInputValueMap = function(inputTxIdBuffer, inputIndex) {
|
||||||
var inputIndexBuffer = new Buffer(4);
|
// var inputIndexBuffer = new Buffer(4);
|
||||||
inputIndexBuffer.writeUInt32BE(inputIndex);
|
// inputIndexBuffer.writeUInt32BE(inputIndex);
|
||||||
return Buffer.concat([
|
// return Buffer.concat([
|
||||||
inputTxIdBuffer,
|
// inputTxIdBuffer,
|
||||||
inputIndexBuffer
|
// inputIndexBuffer
|
||||||
]);
|
// ]);
|
||||||
};
|
// };
|
||||||
|
|
||||||
exports.decodeInputValueMap = function(buffer) {
|
// exports.decodeInputValueMap = function(buffer) {
|
||||||
var txid = buffer.slice(0, 32);
|
// var txid = buffer.slice(0, 32);
|
||||||
var inputIndex = buffer.readUInt32BE(32);
|
// var inputIndex = buffer.readUInt32BE(32);
|
||||||
return {
|
// return {
|
||||||
inputTxId: txid,
|
// inputTxId: txid,
|
||||||
inputIndex: inputIndex
|
// inputIndex: inputIndex
|
||||||
};
|
// };
|
||||||
};
|
// };
|
||||||
|
|
||||||
exports.encodeSummaryCacheKey = function(address) {
|
// exports.encodeSummaryCacheKey = function(address) {
|
||||||
return Buffer.concat([address.hashBuffer, constants.HASH_TYPES_MAP[address.type]]);
|
// return Buffer.concat([address.hashBuffer, constants.HASH_TYPES_MAP[address.type]]);
|
||||||
};
|
// };
|
||||||
|
|
||||||
exports.decodeSummaryCacheKey = function(buffer, network) {
|
// exports.decodeSummaryCacheKey = function(buffer, network) {
|
||||||
var hashBuffer = buffer.read(20);
|
// var hashBuffer = buffer.read(20);
|
||||||
var type = constants.HASH_TYPES_READABLE[buffer.read(20, 2).toString('hex')];
|
// var type = constants.HASH_TYPES_READABLE[buffer.read(20, 2).toString('hex')];
|
||||||
var address = new Address({
|
// var address = new Address({
|
||||||
hashBuffer: hashBuffer,
|
// hashBuffer: hashBuffer,
|
||||||
type: type,
|
// type: type,
|
||||||
network: network
|
// network: network
|
||||||
});
|
// });
|
||||||
return address;
|
// return address;
|
||||||
};
|
// };
|
||||||
|
|
||||||
exports.encodeSummaryCacheValue = function(cache, tipHeight, tipHash) {
|
// exports.encodeSummaryCacheValue = function(cache, tipHeight, tipHash) {
|
||||||
var tipHashBuffer = new Buffer(tipHash, 'hex');
|
// var tipHashBuffer = new Buffer(tipHash, 'hex');
|
||||||
var buffer = new Buffer(new Array(20));
|
// var buffer = new Buffer(new Array(20));
|
||||||
buffer.writeUInt32BE(tipHeight);
|
// buffer.writeUInt32BE(tipHeight);
|
||||||
buffer.writeDoubleBE(cache.result.totalReceived, 4);
|
// buffer.writeDoubleBE(cache.result.totalReceived, 4);
|
||||||
buffer.writeDoubleBE(cache.result.balance, 12);
|
// buffer.writeDoubleBE(cache.result.balance, 12);
|
||||||
var txidBuffers = [];
|
// var txidBuffers = [];
|
||||||
for (var i = 0; i < cache.result.txids.length; i++) {
|
// for (var i = 0; i < cache.result.txids.length; i++) {
|
||||||
var buf = new Buffer(new Array(36));
|
// var buf = new Buffer(new Array(36));
|
||||||
var txid = cache.result.txids[i];
|
// var txid = cache.result.txids[i];
|
||||||
buf.write(txid, 'hex');
|
// buf.write(txid, 'hex');
|
||||||
buf.writeUInt32BE(cache.result.appearanceIds[txid], 32);
|
// buf.writeUInt32BE(cache.result.appearanceIds[txid], 32);
|
||||||
txidBuffers.push(buf);
|
// txidBuffers.push(buf);
|
||||||
}
|
// }
|
||||||
var txidsBuffer = Buffer.concat(txidBuffers);
|
// var txidsBuffer = Buffer.concat(txidBuffers);
|
||||||
var value = Buffer.concat([tipHashBuffer, buffer, txidsBuffer]);
|
// var value = Buffer.concat([tipHashBuffer, buffer, txidsBuffer]);
|
||||||
|
|
||||||
return value;
|
// return value;
|
||||||
};
|
// };
|
||||||
|
|
||||||
exports.decodeSummaryCacheValue = function(buffer) {
|
// exports.decodeSummaryCacheValue = function(buffer) {
|
||||||
|
|
||||||
var hash = buffer.slice(0, 32).toString('hex');
|
// var hash = buffer.slice(0, 32).toString('hex');
|
||||||
var height = buffer.readUInt32BE(32);
|
// var height = buffer.readUInt32BE(32);
|
||||||
var totalReceived = buffer.readDoubleBE(36);
|
// var totalReceived = buffer.readDoubleBE(36);
|
||||||
var balance = buffer.readDoubleBE(44);
|
// var balance = buffer.readDoubleBE(44);
|
||||||
|
|
||||||
// read 32 byte chunks until exhausted
|
// // read 32 byte chunks until exhausted
|
||||||
var appearanceIds = {};
|
// var appearanceIds = {};
|
||||||
var txids = [];
|
// var txids = [];
|
||||||
var pos = 52;
|
// var pos = 52;
|
||||||
while(pos < buffer.length) {
|
// while(pos < buffer.length) {
|
||||||
var txid = buffer.slice(pos, pos + 32).toString('hex');
|
// var txid = buffer.slice(pos, pos + 32).toString('hex');
|
||||||
var txidHeight = buffer.readUInt32BE(pos + 32);
|
// var txidHeight = buffer.readUInt32BE(pos + 32);
|
||||||
txids.push(txid);
|
// txids.push(txid);
|
||||||
appearanceIds[txid] = txidHeight;
|
// appearanceIds[txid] = txidHeight;
|
||||||
pos += 36;
|
// pos += 36;
|
||||||
}
|
// }
|
||||||
|
|
||||||
var cache = {
|
// var cache = {
|
||||||
height: height,
|
// height: height,
|
||||||
hash: hash,
|
// hash: hash,
|
||||||
result: {
|
// result: {
|
||||||
appearanceIds: appearanceIds,
|
// appearanceIds: appearanceIds,
|
||||||
txids: txids,
|
// txids: txids,
|
||||||
totalReceived: totalReceived,
|
// totalReceived: totalReceived,
|
||||||
balance: balance,
|
// balance: balance,
|
||||||
unconfirmedAppearanceIds: {}, // unconfirmed values are never stored in cache
|
// unconfirmedAppearanceIds: {}, // unconfirmed values are never stored in cache
|
||||||
unconfirmedBalance: 0
|
// unconfirmedBalance: 0
|
||||||
}
|
// }
|
||||||
};
|
// };
|
||||||
|
|
||||||
return cache;
|
// return cache;
|
||||||
};
|
// };
|
||||||
|
|
||||||
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,
|
||||||
hashTypeReadable: addrObj.type
|
// hashTypeReadable: addrObj.type
|
||||||
};
|
// };
|
||||||
};
|
// };
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* This function is optimized to return address information about an output script
|
// * This function is optimized to return address information about an output script
|
||||||
* without constructing a Bitcore Address instance.
|
// * without constructing a Bitcore Address instance.
|
||||||
* @param {Script} - An instance of a Bitcore Script
|
// * @param {Script} - An instance of a Bitcore Script
|
||||||
* @param {Network|String} - The network for the address
|
// * @param {Network|String} - The network for the address
|
||||||
*/
|
// */
|
||||||
exports.extractAddressInfoFromScript = function(script, network) {
|
// exports.extractAddressInfoFromScript = function(script, network) {
|
||||||
$.checkArgument(network, 'Second argument is expected to be a network');
|
// $.checkArgument(network, 'Second argument is expected to be a network');
|
||||||
var hashBuffer;
|
// var hashBuffer;
|
||||||
var addressType;
|
// var addressType;
|
||||||
var hashTypeBuffer;
|
// var hashTypeBuffer;
|
||||||
if (script.isPublicKeyHashOut()) {
|
// if (script.isPublicKeyHashOut()) {
|
||||||
hashBuffer = script.chunks[2].buf;
|
// hashBuffer = script.chunks[2].buf;
|
||||||
hashTypeBuffer = constants.HASH_TYPES.PUBKEY;
|
// hashTypeBuffer = constants.HASH_TYPES.PUBKEY;
|
||||||
addressType = Address.PayToPublicKeyHash;
|
// addressType = Address.PayToPublicKeyHash;
|
||||||
} else if (script.isScriptHashOut()) {
|
// } else if (script.isScriptHashOut()) {
|
||||||
hashBuffer = script.chunks[1].buf;
|
// hashBuffer = script.chunks[1].buf;
|
||||||
hashTypeBuffer = constants.HASH_TYPES.REDEEMSCRIPT;
|
// hashTypeBuffer = constants.HASH_TYPES.REDEEMSCRIPT;
|
||||||
addressType = Address.PayToScriptHash;
|
// addressType = Address.PayToScriptHash;
|
||||||
} else if (script.isPublicKeyOut()) {
|
// } else if (script.isPublicKeyOut()) {
|
||||||
var pubkey = script.chunks[0].buf;
|
// var pubkey = script.chunks[0].buf;
|
||||||
var address = Address.fromPublicKey(new PublicKey(pubkey), network);
|
// var address = Address.fromPublicKey(new PublicKey(pubkey), network);
|
||||||
hashBuffer = address.hashBuffer;
|
// hashBuffer = address.hashBuffer;
|
||||||
hashTypeBuffer = constants.HASH_TYPES.PUBKEY;
|
// hashTypeBuffer = constants.HASH_TYPES.PUBKEY;
|
||||||
// pay-to-publickey doesn't have an address, however for compatibility
|
// // pay-to-publickey doesn't have an address, however for compatibility
|
||||||
// purposes, we can create an address
|
// // purposes, we can create an address
|
||||||
addressType = Address.PayToPublicKeyHash;
|
// addressType = Address.PayToPublicKeyHash;
|
||||||
} else {
|
// } else {
|
||||||
return false;
|
// return false;
|
||||||
}
|
// }
|
||||||
return {
|
// return {
|
||||||
hashBuffer: hashBuffer,
|
// hashBuffer: hashBuffer,
|
||||||
hashTypeBuffer: hashTypeBuffer,
|
// hashTypeBuffer: hashTypeBuffer,
|
||||||
addressType: addressType
|
// addressType: addressType
|
||||||
};
|
// };
|
||||||
};
|
// };
|
||||||
|
|
||||||
module.exports = exports;
|
module.exports = Encoding;
|
||||||
|
|||||||
@ -20,7 +20,7 @@ var EventEmitter = require('events').EventEmitter;
|
|||||||
var Address = bitcore.Address;
|
var Address = bitcore.Address;
|
||||||
var AddressHistory = require('./history');
|
var AddressHistory = require('./history');
|
||||||
var constants = require('./constants');
|
var constants = require('./constants');
|
||||||
var encoding = require('./encoding');
|
var Encoding = require('./encoding');
|
||||||
var InputsTransformStream = require('./streams/inputs-transform');
|
var InputsTransformStream = require('./streams/inputs-transform');
|
||||||
var OutputsTransformStream = require('./streams/outputs-transform');
|
var OutputsTransformStream = require('./streams/outputs-transform');
|
||||||
|
|
||||||
@ -37,29 +37,23 @@ var OutputsTransformStream = require('./streams/outputs-transform');
|
|||||||
var AddressService = function(options) {
|
var AddressService = function(options) {
|
||||||
BaseService.call(this, options);
|
BaseService.call(this, options);
|
||||||
|
|
||||||
this.subscriptions = {};
|
// this.subscriptions = {};
|
||||||
this.subscriptions['address/transaction'] = {};
|
// this.subscriptions['address/transaction'] = {};
|
||||||
this.subscriptions['address/balance'] = {};
|
// this.subscriptions['address/balance'] = {};
|
||||||
|
|
||||||
this._bitcoindTransactionListener = this.transactionHandler.bind(this);
|
// this._bitcoindTransactionListener = this.transactionHandler.bind(this);
|
||||||
this._bitcoindTransactionLeaveListener = this.transactionLeaveHandler.bind(this);
|
// this._bitcoindTransactionLeaveListener = this.transactionLeaveHandler.bind(this);
|
||||||
this.node.services.bitcoind.on('tx', this._bitcoindTransactionListener);
|
// this.node.services.bitcoind.on('tx', this._bitcoindTransactionListener);
|
||||||
this.node.services.bitcoind.on('txleave', this._bitcoindTransactionLeaveListener);
|
// this.node.services.bitcoind.on('txleave', this._bitcoindTransactionLeaveListener);
|
||||||
|
|
||||||
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.concurrency = options.concurrency || 20;
|
||||||
|
|
||||||
this._setMempoolIndexPath();
|
// this.mempoolIndex = null; // Used for larger mempool indexes
|
||||||
if (options.mempoolMemoryIndex) {
|
// this.mempoolSpentIndex = {}; // Used for small quick synchronous lookups
|
||||||
this.levelupStore = memdown;
|
// this.mempoolAddressIndex = {}; // Used to check if an address is on the spend pool
|
||||||
} else {
|
|
||||||
this.levelupStore = leveldown;
|
|
||||||
}
|
|
||||||
this.mempoolIndex = null; // Used for larger mempool indexes
|
|
||||||
this.mempoolSpentIndex = {}; // Used for small quick synchronous lookups
|
|
||||||
this.mempoolAddressIndex = {}; // Used to check if an address is on the spend pool
|
|
||||||
};
|
};
|
||||||
|
|
||||||
inherits(AddressService, BaseService);
|
inherits(AddressService, BaseService);
|
||||||
@ -73,71 +67,27 @@ AddressService.dependencies = [
|
|||||||
AddressService.prototype.start = function(callback) {
|
AddressService.prototype.start = function(callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
async.series([
|
this.store = this.node.services.db.store;
|
||||||
|
|
||||||
function(next) {
|
this.node.services.db.getPrefix(this.name, function(err, prefix) {
|
||||||
// Flush any existing mempool index
|
if(err) {
|
||||||
if (fs.existsSync(self.mempoolIndexPath)) {
|
return callback(err);
|
||||||
leveldown.destroy(self.mempoolIndexPath, next);
|
|
||||||
} else {
|
|
||||||
setImmediate(next);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
function(next) {
|
|
||||||
// Setup new mempool index
|
|
||||||
if (!fs.existsSync(self.mempoolIndexPath)) {
|
|
||||||
mkdirp(self.mempoolIndexPath, next);
|
|
||||||
} else {
|
|
||||||
setImmediate(next);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
function(next) {
|
|
||||||
self.mempoolIndex = levelup(
|
|
||||||
self.mempoolIndexPath,
|
|
||||||
{
|
|
||||||
db: self.levelupStore,
|
|
||||||
keyEncoding: 'binary',
|
|
||||||
valueEncoding: 'binary',
|
|
||||||
fillCache: false,
|
|
||||||
maxOpenFiles: 200
|
|
||||||
},
|
|
||||||
next
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
], callback);
|
|
||||||
|
|
||||||
|
self.prefix = prefix;
|
||||||
|
|
||||||
|
self.encoding = new Encoding(self.prefix);
|
||||||
|
|
||||||
|
callback();
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
AddressService.prototype.stop = function(callback) {
|
AddressService.prototype.stop = function(callback) {
|
||||||
// TODO Keep track of ongoing db requests before shutting down
|
// TODO Keep track of ongoing db requests before shutting down
|
||||||
this.node.services.bitcoind.removeListener('tx', this._bitcoindTransactionListener);
|
// this.node.services.bitcoind.removeListener('tx', this._bitcoindTransactionListener);
|
||||||
this.node.services.bitcoind.removeListener('txleave', this._bitcoindTransactionLeaveListener);
|
// this.node.services.bitcoind.removeListener('txleave', this._bitcoindTransactionLeaveListener);
|
||||||
this.mempoolIndex.close(callback);
|
// this.mempoolIndex.close(callback);
|
||||||
};
|
setImmediate(callback);
|
||||||
|
|
||||||
/**
|
|
||||||
* This function will set `this.mempoolIndexPath` based on `this.node.network`.
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
AddressService.prototype._setMempoolIndexPath = function() {
|
|
||||||
this.mempoolIndexPath = this._getDBPathFor('bitcore-addressmempool.db');
|
|
||||||
};
|
|
||||||
|
|
||||||
AddressService.prototype._getDBPathFor = function(dbname) {
|
|
||||||
$.checkState(this.node.datadir, 'Node is expected to have a "datadir" property');
|
|
||||||
var path;
|
|
||||||
if (this.node.network === Networks.livenet) {
|
|
||||||
path = this.node.datadir + '/' + dbname;
|
|
||||||
} else if (this.node.network === Networks.testnet) {
|
|
||||||
if (this.node.network.regtestEnabled) {
|
|
||||||
path = this.node.datadir + '/regtest/' + dbname;
|
|
||||||
} else {
|
|
||||||
path = this.node.datadir + '/testnet3/' + dbname;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new Error('Unknown network: ' + this.network);
|
|
||||||
}
|
|
||||||
return path;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -160,289 +110,21 @@ AddressService.prototype.getAPIMethods = function() {
|
|||||||
* Called by the Bus to get the available events for this service.
|
* Called by the Bus to get the available events for this service.
|
||||||
*/
|
*/
|
||||||
AddressService.prototype.getPublishEvents = function() {
|
AddressService.prototype.getPublishEvents = function() {
|
||||||
return [
|
return [];
|
||||||
{
|
// return [
|
||||||
name: 'address/transaction',
|
// {
|
||||||
scope: this,
|
// name: 'address/transaction',
|
||||||
subscribe: this.subscribe.bind(this, 'address/transaction'),
|
// scope: this,
|
||||||
unsubscribe: this.unsubscribe.bind(this, 'address/transaction')
|
// subscribe: this.subscribe.bind(this, 'address/transaction'),
|
||||||
},
|
// unsubscribe: this.unsubscribe.bind(this, 'address/transaction')
|
||||||
{
|
// },
|
||||||
name: 'address/balance',
|
// {
|
||||||
scope: this,
|
// name: 'address/balance',
|
||||||
subscribe: this.subscribe.bind(this, 'address/balance'),
|
// scope: this,
|
||||||
unsubscribe: this.unsubscribe.bind(this, 'address/balance')
|
// subscribe: this.subscribe.bind(this, 'address/balance'),
|
||||||
}
|
// unsubscribe: this.unsubscribe.bind(this, 'address/balance')
|
||||||
];
|
// }
|
||||||
};
|
// ];
|
||||||
|
|
||||||
/**
|
|
||||||
* Will process each output of a transaction from the daemon "tx" event, and construct
|
|
||||||
* an object with the data for the message to be relayed to any subscribers for an address.
|
|
||||||
*
|
|
||||||
* @param {Object} messages - An object to collect messages
|
|
||||||
* @param {Transaction} tx - Instance of the transaction
|
|
||||||
* @param {Number} outputIndex - The index of the output in the transaction
|
|
||||||
* @param {Boolean} rejected - If the transaction was rejected by the mempool
|
|
||||||
*/
|
|
||||||
AddressService.prototype.transactionOutputHandler = function(messages, tx, outputIndex, rejected) {
|
|
||||||
var script = tx.outputs[outputIndex].script;
|
|
||||||
|
|
||||||
// If the script is invalid skip
|
|
||||||
if (!script) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var addressInfo = encoding.extractAddressInfoFromScript(script, this.node.network);
|
|
||||||
if (!addressInfo) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
addressInfo.hashHex = addressInfo.hashBuffer.toString('hex');
|
|
||||||
|
|
||||||
// Collect data to publish to address subscribers
|
|
||||||
if (messages[addressInfo.hashHex]) {
|
|
||||||
messages[addressInfo.hashHex].outputIndexes.push(outputIndex);
|
|
||||||
} else {
|
|
||||||
messages[addressInfo.hashHex] = {
|
|
||||||
tx: tx,
|
|
||||||
outputIndexes: [outputIndex],
|
|
||||||
addressInfo: addressInfo,
|
|
||||||
rejected: rejected
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This will handle data from the daemon "txleave" that a transaction has left the mempool.
|
|
||||||
* @param {Object} txInfo - The data from the daemon.on('txleave') event
|
|
||||||
* @param {Buffer} txInfo.buffer - The transaction buffer
|
|
||||||
* @param {String} txInfo.hash - The hash of the transaction
|
|
||||||
*/
|
|
||||||
AddressService.prototype.transactionLeaveHandler = function(txInfo) {
|
|
||||||
var tx = bitcore.Transaction().fromBuffer(txInfo.buffer);
|
|
||||||
this.updateMempoolIndex(tx, false);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This will handle data from the daemon "tx" event, go through each of the outputs
|
|
||||||
* and send messages by calling `transactionEventHandler` to any subscribers for a
|
|
||||||
* particular address.
|
|
||||||
* @param {Object} txInfo - The data from the daemon.on('tx') event
|
|
||||||
* @param {Buffer} txInfo.buffer - The transaction buffer
|
|
||||||
* @param {Boolean} txInfo.mempool - If the transaction was accepted in the mempool
|
|
||||||
* @param {String} txInfo.hash - The hash of the transaction
|
|
||||||
* @param {Function} [callback] - Optional callback
|
|
||||||
*/
|
|
||||||
AddressService.prototype.transactionHandler = function(buffer, callback) {
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
if (!callback) {
|
|
||||||
callback = function(err) {
|
|
||||||
if (err) {
|
|
||||||
return log.error(err);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.node.stopping) {
|
|
||||||
return callback();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Basic transaction format is handled by the daemon
|
|
||||||
// and we can safely assume the buffer is properly formatted.
|
|
||||||
var tx = bitcore.Transaction().fromBuffer(buffer);
|
|
||||||
|
|
||||||
var messages = {};
|
|
||||||
|
|
||||||
var mempool = false; // txInfo.mempool
|
|
||||||
|
|
||||||
var outputsLength = tx.outputs.length;
|
|
||||||
for (var i = 0; i < outputsLength; i++) {
|
|
||||||
// TODO missing txInfo.mempool
|
|
||||||
this.transactionOutputHandler(messages, tx, i, mempool);
|
|
||||||
}
|
|
||||||
|
|
||||||
function finish(err) {
|
|
||||||
if (err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
for (var key in messages) {
|
|
||||||
self.transactionEventHandler(messages[key]);
|
|
||||||
self.balanceEventHandler(null, messages[key].addressInfo);
|
|
||||||
}
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mempool) {
|
|
||||||
self.updateMempoolIndex(tx, true, finish);
|
|
||||||
} else {
|
|
||||||
setImmediate(finish);
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
AddressService.prototype._updateAddressIndex = function(key, add) {
|
|
||||||
var currentValue = this.mempoolAddressIndex[key] || 0;
|
|
||||||
|
|
||||||
if(add) {
|
|
||||||
if (currentValue > 0) {
|
|
||||||
this.mempoolAddressIndex[key] = currentValue + 1;
|
|
||||||
} else {
|
|
||||||
this.mempoolAddressIndex[key] = 1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (currentValue <= 1) {
|
|
||||||
delete this.mempoolAddressIndex[key];
|
|
||||||
} else {
|
|
||||||
this.mempoolAddressIndex[key]--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function will update the mempool address index with the necessary
|
|
||||||
* information for further lookups.
|
|
||||||
* @param {Transaction} - An instance of a Bitcore Transaction
|
|
||||||
* @param {Boolean} - Add/remove from the index
|
|
||||||
*/
|
|
||||||
AddressService.prototype.updateMempoolIndex = function(tx, add, callback) {
|
|
||||||
/* jshint maxstatements: 100 */
|
|
||||||
|
|
||||||
var operations = [];
|
|
||||||
var timestampBuffer = new Buffer(new Array(8));
|
|
||||||
timestampBuffer.writeDoubleBE(new Date().getTime());
|
|
||||||
|
|
||||||
var action = 'put';
|
|
||||||
if (!add) {
|
|
||||||
action = 'del';
|
|
||||||
}
|
|
||||||
|
|
||||||
var txid = tx.hash;
|
|
||||||
var txidBuffer = new Buffer(txid, 'hex');
|
|
||||||
|
|
||||||
var outputLength = tx.outputs.length;
|
|
||||||
for (var outputIndex = 0; outputIndex < outputLength; outputIndex++) {
|
|
||||||
var output = tx.outputs[outputIndex];
|
|
||||||
if (!output.script) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
var addressInfo = encoding.extractAddressInfoFromScript(output.script, this.node.network);
|
|
||||||
if (!addressInfo) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var addressIndexKey = encoding.encodeMempoolAddressIndexKey(addressInfo.hashBuffer, addressInfo.hashTypeBuffer);
|
|
||||||
|
|
||||||
this._updateAddressIndex(addressIndexKey, add);
|
|
||||||
|
|
||||||
// Update output index
|
|
||||||
var outputIndexBuffer = new Buffer(4);
|
|
||||||
outputIndexBuffer.writeUInt32BE(outputIndex);
|
|
||||||
|
|
||||||
var outKey = Buffer.concat([
|
|
||||||
constants.MEMPREFIXES.OUTPUTS,
|
|
||||||
addressInfo.hashBuffer,
|
|
||||||
addressInfo.hashTypeBuffer,
|
|
||||||
txidBuffer,
|
|
||||||
outputIndexBuffer
|
|
||||||
]);
|
|
||||||
|
|
||||||
var outValue = encoding.encodeOutputMempoolValue(
|
|
||||||
output.satoshis,
|
|
||||||
timestampBuffer,
|
|
||||||
output._scriptBuffer
|
|
||||||
);
|
|
||||||
|
|
||||||
operations.push({
|
|
||||||
type: action,
|
|
||||||
key: outKey,
|
|
||||||
value: outValue
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
var inputLength = tx.inputs.length;
|
|
||||||
for (var inputIndex = 0; inputIndex < inputLength; inputIndex++) {
|
|
||||||
|
|
||||||
var input = tx.inputs[inputIndex];
|
|
||||||
|
|
||||||
var inputOutputIndexBuffer = new Buffer(4);
|
|
||||||
inputOutputIndexBuffer.writeUInt32BE(input.outputIndex);
|
|
||||||
|
|
||||||
// Add an additional small spent index for fast synchronous lookups
|
|
||||||
var spentIndexSyncKey = encoding.encodeSpentIndexSyncKey(
|
|
||||||
input.prevTxId,
|
|
||||||
input.outputIndex
|
|
||||||
);
|
|
||||||
if (add) {
|
|
||||||
this.mempoolSpentIndex[spentIndexSyncKey] = true;
|
|
||||||
} else {
|
|
||||||
delete this.mempoolSpentIndex[spentIndexSyncKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a more detailed spent index with values
|
|
||||||
var spentIndexKey = Buffer.concat([
|
|
||||||
constants.MEMPREFIXES.SPENTSMAP,
|
|
||||||
input.prevTxId,
|
|
||||||
inputOutputIndexBuffer
|
|
||||||
]);
|
|
||||||
var inputIndexBuffer = new Buffer(4);
|
|
||||||
inputIndexBuffer.writeUInt32BE(inputIndex);
|
|
||||||
var inputIndexValue = Buffer.concat([
|
|
||||||
txidBuffer,
|
|
||||||
inputIndexBuffer
|
|
||||||
]);
|
|
||||||
operations.push({
|
|
||||||
type: action,
|
|
||||||
key: spentIndexKey,
|
|
||||||
value: inputIndexValue
|
|
||||||
});
|
|
||||||
|
|
||||||
// Update input index
|
|
||||||
var inputHashBuffer;
|
|
||||||
var inputHashType;
|
|
||||||
if (input.script.isPublicKeyHashIn()) {
|
|
||||||
inputHashBuffer = Hash.sha256ripemd160(input.script.chunks[1].buf);
|
|
||||||
inputHashType = constants.HASH_TYPES.PUBKEY;
|
|
||||||
} else if (input.script.isScriptHashIn()) {
|
|
||||||
inputHashBuffer = Hash.sha256ripemd160(input.script.chunks[input.script.chunks.length - 1].buf);
|
|
||||||
inputHashType = constants.HASH_TYPES.REDEEMSCRIPT;
|
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
var inputKey = Buffer.concat([
|
|
||||||
constants.MEMPREFIXES.SPENTS,
|
|
||||||
inputHashBuffer,
|
|
||||||
inputHashType,
|
|
||||||
input.prevTxId,
|
|
||||||
inputOutputIndexBuffer
|
|
||||||
]);
|
|
||||||
var inputValue = Buffer.concat([
|
|
||||||
txidBuffer,
|
|
||||||
inputIndexBuffer,
|
|
||||||
timestampBuffer
|
|
||||||
]);
|
|
||||||
operations.push({
|
|
||||||
type: action,
|
|
||||||
key: inputKey,
|
|
||||||
value: inputValue
|
|
||||||
});
|
|
||||||
|
|
||||||
var addressIndexKey = encoding.encodeMempoolAddressIndexKey(inputHashBuffer, inputHashType);
|
|
||||||
|
|
||||||
this._updateAddressIndex(addressIndexKey, add);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!callback) {
|
|
||||||
callback = function(err) {
|
|
||||||
if (err) {
|
|
||||||
return log.error(err);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
this.mempoolIndex.batch(operations, callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -453,6 +135,8 @@ AddressService.prototype.updateMempoolIndex = function(tx, add, callback) {
|
|||||||
* @param {Function} callback
|
* @param {Function} callback
|
||||||
*/
|
*/
|
||||||
AddressService.prototype.blockHandler = function(block, connectBlock, callback) {
|
AddressService.prototype.blockHandler = function(block, connectBlock, callback) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
var txs = block.transactions;
|
var txs = block.transactions;
|
||||||
var height = block.__height;
|
var height = block.__height;
|
||||||
|
|
||||||
@ -480,16 +164,21 @@ AddressService.prototype.blockHandler = function(block, connectBlock, 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 address = self._getAddressString(script);
|
||||||
|
|
||||||
var key = encoding.encodeAddressIndexKey(address, false, height, txid, outputIndex);
|
if(!address) {
|
||||||
var value = encoding.encodeOutputValue(output.satoshis, output._scriptBuffer);
|
log.warn('Unsupported script type for output in ' + txid);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var key = self.encoding.encodeAddressIndexKey(address, false, height, txid, outputIndex);
|
||||||
|
var value = self.encoding.encodeAddressIndexValue(output.satoshis, output._scriptBuffer);
|
||||||
operations.push({
|
operations.push({
|
||||||
type: action,
|
type: action,
|
||||||
key: key,
|
key: key,
|
||||||
@ -509,40 +198,49 @@ AddressService.prototype.blockHandler = function(block, connectBlock, callback)
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
this.balanceEventHandler(block, address);
|
//this.balanceEventHandler(block, address);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Publish events to any subscribers for this transaction
|
// Publish events to any subscribers for this transaction
|
||||||
for (var addressKey in txmessages) {
|
for (var addressKey in txmessages) {
|
||||||
this.transactionEventHandler(txmessages[addressKey]);
|
//this.transactionEventHandler(txmessages[addressKey]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(tx.isCoinbase()) {
|
if(tx.isCoinbase()) {
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
|
|
||||||
async.eachLimit(inputs, self.concurrency, function(input, next) {
|
async.eachOfLimit(inputs, self.concurrency, function(input, inputIndex, next) {
|
||||||
var input = inputs[inputIndex];
|
var input = inputs[inputIndex];
|
||||||
var inputHash;
|
var inputHash;
|
||||||
var inputHashType;
|
var inputHashType;
|
||||||
|
|
||||||
var address = input.script.toAddress();
|
if(!input.script) {
|
||||||
|
log.debug('Invalid script');
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
var inputKey = encoding.encodeAddressIndexKey(address, true, height, txid, inputIndex, true);
|
self.node.services.transaction.getTransaction(input.prevTxId.toString('hex'), function(err, tx) {
|
||||||
|
|
||||||
self.node.services.transaction.getTransaction(input.prevTxId, function(err, result) {
|
|
||||||
if(err) {
|
if(err) {
|
||||||
return next(err);
|
return next(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
var tx = result.transaction;
|
|
||||||
var height = result.height;
|
|
||||||
var output = tx.outputs[input.outputIndex];
|
var output = tx.outputs[input.outputIndex];
|
||||||
var outputKey = encoding.encodeAddressIndexKey(address, true, height, tx.id, input.outputIndex, false);
|
|
||||||
var outputKeyToDelete = encoding.encodeAddressIndexKey(address, false, height, tx.id, input.outputIndex, false);
|
var address = self._getAddressString(input.script, output);
|
||||||
var outputValue = encoding.encodeAdressIndexValue(output.satoshis, output._scriptBuffer);
|
|
||||||
var inputValue = encoding.encodeAddressIndexValue(output.satoshis, input._scriptBuffer);
|
if(!address) {
|
||||||
|
log.warn('Unsupported script for input in transaction ' + txid);
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
var inputKey = self.encoding.encodeAddressIndexKey(address, true, height, txid, inputIndex, true);
|
||||||
|
|
||||||
|
var outputKey = self.encoding.encodeAddressIndexKey(address, true, tx.__height, tx.id, input.outputIndex, false);
|
||||||
|
var outputKeyToDelete = self.encoding.encodeAddressIndexKey(address, false, tx.__height, tx.id, input.outputIndex, false);
|
||||||
|
var outputValue = self.encoding.encodeAddressIndexValue(output.satoshis, output._scriptBuffer);
|
||||||
|
var inputValue = self.encoding.encodeAddressIndexValue(output.satoshis, input._scriptBuffer);
|
||||||
|
|
||||||
operations = operations.concat([{
|
operations = operations.concat([{
|
||||||
type: action,
|
type: action,
|
||||||
@ -562,11 +260,33 @@ AddressService.prototype.blockHandler = function(block, connectBlock, callback)
|
|||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
|
||||||
}, function(err) {
|
}, next);
|
||||||
callback(err, operations);
|
}, function(err) {
|
||||||
});
|
callback(err, operations);
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
AddressService.prototype._getAddressString = function(script, output) {
|
||||||
|
var address = script.toAddress();
|
||||||
|
if(address) {
|
||||||
|
return address.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var pubkey = script.getPublicKey();
|
||||||
|
if(pubkey) {
|
||||||
|
return pubkey.toString('hex');
|
||||||
|
}
|
||||||
|
} catch(e) {
|
||||||
|
// if there is an error, it's because a pubkey can not be extracted from the script
|
||||||
|
// continue on and return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if(output.script.isPublicKeyOut()) {
|
||||||
|
return output.script.getPublicKey().toString('hex');
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -42,7 +42,7 @@ function DB(options) {
|
|||||||
// to determine during an upgrade if a reindex is required
|
// to determine during an upgrade if a reindex is required
|
||||||
this.version = 2;
|
this.version = 2;
|
||||||
|
|
||||||
this.dbPrefix = 0;
|
this.dbPrefix = '\u0000\u0000';
|
||||||
this.tip = null;
|
this.tip = null;
|
||||||
this.genesis = null;
|
this.genesis = null;
|
||||||
|
|
||||||
@ -105,10 +105,10 @@ DB.prototype._setDataPath = function() {
|
|||||||
DB.prototype._checkVersion = function(callback) {
|
DB.prototype._checkVersion = function(callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var options = {
|
var options = {
|
||||||
keyEncoding: 'binary',
|
keyEncoding: 'string',
|
||||||
valueEncoding: 'binary'
|
valueEncoding: 'binary'
|
||||||
};
|
};
|
||||||
self.store.get(DB.PREFIXES.TIP, options, function(err) {
|
self.store.get(self.dbPrefix + 'tip', options, function(err) {
|
||||||
if (err instanceof levelup.errors.NotFoundError) {
|
if (err instanceof levelup.errors.NotFoundError) {
|
||||||
// The database is brand new and doesn't have a tip stored
|
// The database is brand new and doesn't have a tip stored
|
||||||
// we can skip version checking
|
// we can skip version checking
|
||||||
@ -116,7 +116,7 @@ DB.prototype._checkVersion = function(callback) {
|
|||||||
} else if (err) {
|
} else if (err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
self.store.get(DB.PREFIXES.VERSION, options, function(err, buffer) {
|
self.store.get(self.dbPrefix + 'version', options, function(err, buffer) {
|
||||||
var version;
|
var version;
|
||||||
if (err instanceof levelup.errors.NotFoundError) {
|
if (err instanceof levelup.errors.NotFoundError) {
|
||||||
// The initial version (1) of the database didn't store the version number
|
// The initial version (1) of the database didn't store the version number
|
||||||
@ -143,7 +143,7 @@ DB.prototype._checkVersion = function(callback) {
|
|||||||
DB.prototype._setVersion = function(callback) {
|
DB.prototype._setVersion = function(callback) {
|
||||||
var versionBuffer = new Buffer(new Array(4));
|
var versionBuffer = new Buffer(new Array(4));
|
||||||
versionBuffer.writeUInt32BE(this.version);
|
versionBuffer.writeUInt32BE(this.version);
|
||||||
this.store.put(DB.PREFIXES.VERSION, versionBuffer, callback);
|
this.store.put(this.dbPrefix + 'version', versionBuffer, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -158,14 +158,14 @@ DB.prototype.start = function(callback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.genesis = Block.fromBuffer(this.node.services.bitcoind.genesisBuffer);
|
this.genesis = Block.fromBuffer(this.node.services.bitcoind.genesisBuffer);
|
||||||
this.store = levelup(this.dataPath, { db: this.levelupStore, maxOpenFiles: this.maxOpenFiles });
|
this.store = levelup(this.dataPath, { db: this.levelupStore, maxOpenFiles: this.maxOpenFiles, keyEncoding: 'binary', valueEncoding: 'binary'});
|
||||||
this.node.services.bitcoind.on('tx', this.transactionHandler.bind(this));
|
this.node.services.bitcoind.on('tx', this.transactionHandler.bind(this));
|
||||||
|
|
||||||
this.once('ready', function() {
|
this.once('ready', function() {
|
||||||
log.info('Bitcoin Database Ready');
|
log.info('Bitcoin Database Ready');
|
||||||
|
|
||||||
// Notify that there is a new tip
|
// Notify that there is a new tip
|
||||||
self.node.services.bitcoind.on('tip', function(height) {
|
self.node.services.bitcoind.on('tip', function() {
|
||||||
if(!self.node.stopping) {
|
if(!self.node.stopping) {
|
||||||
self.sync();
|
self.sync();
|
||||||
}
|
}
|
||||||
@ -240,12 +240,12 @@ DB.prototype.close = function(callback) {
|
|||||||
* @param {String} txInfo.hash - The hash of the transaction
|
* @param {String} txInfo.hash - The hash of the transaction
|
||||||
*/
|
*/
|
||||||
DB.prototype.transactionHandler = function(tx) {
|
DB.prototype.transactionHandler = function(tx) {
|
||||||
for (var i = 0; i < this.subscriptions.transaction.length; i++) {
|
// for (var i = 0; i < this.subscriptions.transaction.length; i++) {
|
||||||
this.subscriptions.transaction[i].emit('db/transaction', {
|
// this.subscriptions.transaction[i].emit('db/transaction', {
|
||||||
rejected: !txInfo.mempool,
|
// rejected: !txInfo.mempool,
|
||||||
tx: tx
|
// tx: tx
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -267,11 +267,11 @@ DB.prototype.loadTip = function(callback) {
|
|||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
var options = {
|
var options = {
|
||||||
keyEncoding: 'binary',
|
keyEncoding: 'string',
|
||||||
valueEncoding: 'binary'
|
valueEncoding: 'binary'
|
||||||
};
|
};
|
||||||
|
|
||||||
self.store.get(DB.PREFIXES.TIP, options, function(err, tipData) {
|
self.store.get(self.dbPrefix + 'tip', options, function(err, tipData) {
|
||||||
if(err && err instanceof levelup.errors.NotFoundError) {
|
if(err && err instanceof levelup.errors.NotFoundError) {
|
||||||
self.tip = self.genesis;
|
self.tip = self.genesis;
|
||||||
self.tip.__height = 0;
|
self.tip.__height = 0;
|
||||||
@ -288,7 +288,8 @@ DB.prototype.loadTip = function(callback) {
|
|||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
var hash = tipData.toString('hex');
|
var hash = tipData.slice(0, 32).toString('hex');
|
||||||
|
var height = tipData.readUInt32BE(32);
|
||||||
|
|
||||||
var times = 0;
|
var times = 0;
|
||||||
async.retry({times: 3, interval: self.retryInterval}, function(done) {
|
async.retry({times: 3, interval: self.retryInterval}, function(done) {
|
||||||
@ -311,12 +312,9 @@ DB.prototype.loadTip = function(callback) {
|
|||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tip.__height = height;
|
||||||
self.tip = tip;
|
self.tip = tip;
|
||||||
var blockIndex = self.node.services.bitcoind.getBlockIndex(self.tip.hash);
|
|
||||||
if(!blockIndex) {
|
|
||||||
return callback(new Error('Could not get height for tip.'));
|
|
||||||
}
|
|
||||||
self.tip.__height = blockIndex.height;
|
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -330,55 +328,6 @@ DB.prototype.getBlock = function(hash, callback) {
|
|||||||
this.node.services.bitcoind.getBlock(hash, callback);
|
this.node.services.bitcoind.getBlock(hash, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Get block hashes between two timestamps
|
|
||||||
* @param {Number} high - high timestamp, in seconds, inclusive
|
|
||||||
* @param {Number} low - low timestamp, in seconds, inclusive
|
|
||||||
* @param {Function} callback
|
|
||||||
*/
|
|
||||||
DB.prototype.getBlockHashesByTimestamp = function(high, low, callback) {
|
|
||||||
var self = this;
|
|
||||||
var hashes = [];
|
|
||||||
var lowKey;
|
|
||||||
var highKey;
|
|
||||||
|
|
||||||
try {
|
|
||||||
lowKey = this._encodeBlockIndexKey(low);
|
|
||||||
highKey = this._encodeBlockIndexKey(high);
|
|
||||||
} catch(e) {
|
|
||||||
return callback(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
var stream = this.store.createReadStream({
|
|
||||||
gte: lowKey,
|
|
||||||
lte: highKey,
|
|
||||||
reverse: true,
|
|
||||||
valueEncoding: 'binary',
|
|
||||||
keyEncoding: 'binary'
|
|
||||||
});
|
|
||||||
|
|
||||||
stream.on('data', function(data) {
|
|
||||||
hashes.push(self._decodeBlockIndexValue(data.value));
|
|
||||||
});
|
|
||||||
|
|
||||||
var error;
|
|
||||||
|
|
||||||
stream.on('error', function(streamError) {
|
|
||||||
if (streamError) {
|
|
||||||
error = streamError;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
stream.on('close', function() {
|
|
||||||
if (error) {
|
|
||||||
return callback(error);
|
|
||||||
}
|
|
||||||
callback(null, hashes);
|
|
||||||
});
|
|
||||||
|
|
||||||
return stream;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Will give a Bitcore Transaction from bitcoind by txid
|
* Will give a Bitcore Transaction from bitcoind by txid
|
||||||
* @param {String} txid - A transaction hash
|
* @param {String} txid - A transaction hash
|
||||||
@ -532,23 +481,26 @@ DB.prototype.runAllBlockHandlers = function(block, add, callback) {
|
|||||||
var operations = [];
|
var operations = [];
|
||||||
|
|
||||||
// Notify block subscribers
|
// Notify block subscribers
|
||||||
for (var i = 0; i < this.subscriptions.block.length; i++) {
|
// for (var i = 0; i < this.subscriptions.block.length; i++) {
|
||||||
this.subscriptions.block[i].emit('db/block', block.hash);
|
// this.subscriptions.block[i].emit('db/block', block.hash);
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Update tip
|
// Update tip
|
||||||
var tipHash = add ? new Buffer(block.hash, 'hex') : BufferUtil.reverse(block.header.prevHash);
|
var tipData = new Buffer(36);
|
||||||
|
var heightBuffer = new Buffer(4);
|
||||||
|
|
||||||
|
if(add) {
|
||||||
|
heightBuffer.writeUInt32BE(block.__height);
|
||||||
|
tipData = Buffer.concat([new Buffer(block.hash, 'hex'), heightBuffer]);
|
||||||
|
} else {
|
||||||
|
heightBuffer.writeUInt32BE(block.__height - 1);
|
||||||
|
tipData = Buffer.concat([BufferUtil.reverse(block.header.prevHash), heightBuffer]);
|
||||||
|
}
|
||||||
|
|
||||||
operations.push({
|
operations.push({
|
||||||
type: 'put',
|
type: 'put',
|
||||||
key: DB.PREFIXES.TIP,
|
key: self.dbPrefix + 'tip',
|
||||||
value: tipHash
|
value: tipData
|
||||||
});
|
|
||||||
|
|
||||||
// Update block index
|
|
||||||
operations.push({
|
|
||||||
type: add ? 'put' : 'del',
|
|
||||||
key: this._encodeBlockIndexKey(block.header.timestamp),
|
|
||||||
value: this._encodeBlockIndexValue(block.hash)
|
|
||||||
});
|
});
|
||||||
|
|
||||||
async.eachSeries(
|
async.eachSeries(
|
||||||
@ -582,22 +534,6 @@ DB.prototype.runAllBlockHandlers = function(block, add, callback) {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
DB.prototype._encodeBlockIndexKey = function(timestamp) {
|
|
||||||
$.checkArgument(timestamp >= 0 && timestamp <= 4294967295, 'timestamp out of bounds');
|
|
||||||
var timestampBuffer = new Buffer(4);
|
|
||||||
timestampBuffer.writeUInt32BE(timestamp);
|
|
||||||
return Buffer.concat([DB.PREFIXES.BLOCKS, timestampBuffer]);
|
|
||||||
};
|
|
||||||
|
|
||||||
DB.prototype._encodeBlockIndexValue = function(hash) {
|
|
||||||
return new Buffer(hash, 'hex');
|
|
||||||
};
|
|
||||||
|
|
||||||
DB.prototype._decodeBlockIndexValue = function(value) {
|
|
||||||
return value.toString('hex');
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function will find the common ancestor between the current chain and a forked block,
|
* This function will find the common ancestor between the current chain and a forked block,
|
||||||
* by moving backwards on both chains until there is a meeting point.
|
* by moving backwards on both chains until there is a meeting point.
|
||||||
@ -739,6 +675,7 @@ DB.prototype.sync = function() {
|
|||||||
height = self.tip.__height;
|
height = self.tip.__height;
|
||||||
return height < self.node.services.bitcoind.height && !self.node.stopping;
|
return height < self.node.services.bitcoind.height && !self.node.stopping;
|
||||||
}, function(done) {
|
}, function(done) {
|
||||||
|
console.log('fetching block ' + (height + 1));
|
||||||
self.node.services.bitcoind.getBlock(height + 1, function(err, block) {
|
self.node.services.bitcoind.getBlock(height + 1, function(err, block) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return done(err);
|
return done(err);
|
||||||
@ -805,6 +742,57 @@ DB.prototype.sync = function() {
|
|||||||
DB.prototype.getPrefix = function(service, callback) {
|
DB.prototype.getPrefix = function(service, callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
|
function getPrefix(next) {
|
||||||
|
self.store.get(self.dbPrefix + 'prefix-' + service, function(err, buffer) {
|
||||||
|
if(err) {
|
||||||
|
if(err.notFound) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
// we already have the prefix, call the callback
|
||||||
|
return callback(null, buffer);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUnused(next) {
|
||||||
|
self.store.get(self.dbPrefix + 'nextUnused', function(err, buffer) {
|
||||||
|
if(err) {
|
||||||
|
if(err.notFound) {
|
||||||
|
return next(null, new Buffer('0001', 'hex'));
|
||||||
|
}
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return next(null, buffer);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function putPrefix(buffer, next) {
|
||||||
|
self.store.put(self.dbPrefix + 'prefix-' + service, buffer, function(err) {
|
||||||
|
if(err) {
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
next(null, buffer);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function putUnused(buffer, next) {
|
||||||
|
var prefix = buffer.readUInt16BE();
|
||||||
|
var nextUnused = new Buffer(2);
|
||||||
|
nextUnused.writeUInt16BE(prefix + 1);
|
||||||
|
|
||||||
|
self.store.put(self.dbPrefix + 'nextUnused', nextUnused, function(err) {
|
||||||
|
if(err) {
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return next(null, buffer);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async.waterfall(
|
async.waterfall(
|
||||||
[
|
[
|
||||||
getPrefix,
|
getPrefix,
|
||||||
@ -814,56 +802,6 @@ DB.prototype.getPrefix = function(service, callback) {
|
|||||||
],
|
],
|
||||||
callback
|
callback
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
function getPrefix(next) {
|
|
||||||
self.store.get(this.dbPrefix + 'prefix-' + service, function(err, buffer) {
|
|
||||||
if(err && !err.notFound) {
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!buffer || err.notFound) {
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
|
|
||||||
// we already have the prefix, call the callback
|
|
||||||
return callback(null, buffer.readUInt16BE());
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getUnused(next) {
|
|
||||||
self.store.get(self.dbPrefix + 'nextUnused', function(err, buffer) {
|
|
||||||
if(err && !err.notFound) {
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!buffer || err.notFound) {
|
|
||||||
return next(null, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return next(null, buffer.readUInt16BE());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function putPrefix(prefix, next) {
|
|
||||||
self.store.put(self.dbPrefix + 'prefix-' + service, prefix, function(err) {
|
|
||||||
if(err) {
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
next(null, prefix);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function putUnused(prefix, next) {
|
|
||||||
self.store.put(self.dbPrefix + 'nextUnused', prefix + 1, function(err) {
|
|
||||||
if(err) {
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
return next(null, prefix);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = DB;
|
module.exports = DB;
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
var BaseService = require('../../service');
|
var BaseService = require('../service');
|
||||||
var inherits = require('util').inherits;
|
var inherits = require('util').inherits;
|
||||||
|
var bitcore = require('bitcore-lib');
|
||||||
|
|
||||||
function TransactionService(options) {
|
function TransactionService(options) {
|
||||||
BaseService.call(this, options);
|
BaseService.call(this, options);
|
||||||
|
|
||||||
|
this.currentTransactions = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
inherits(TransactionService, BaseService);
|
inherits(TransactionService, BaseService);
|
||||||
@ -41,8 +43,13 @@ TransactionService.prototype.blockHandler = function(block, connectBlock, callba
|
|||||||
|
|
||||||
var operations = [];
|
var operations = [];
|
||||||
|
|
||||||
|
this.currentTransactions = {};
|
||||||
|
|
||||||
for(var i = 0; i < block.transactions.length; i++) {
|
for(var i = 0; i < block.transactions.length; i++) {
|
||||||
var tx = block.transactions[i];
|
var tx = block.transactions[i];
|
||||||
|
tx.__height = block.__height;
|
||||||
|
|
||||||
|
this.currentTransactions[tx.id] = tx;
|
||||||
|
|
||||||
operations.push({
|
operations.push({
|
||||||
type: action,
|
type: action,
|
||||||
@ -56,25 +63,46 @@ TransactionService.prototype.blockHandler = function(block, connectBlock, callba
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
TransactionService.prototype.getTransaction = function(txid, callback) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
if(self.currentTransactions[txid]) {
|
||||||
|
return setImmediate(function() {
|
||||||
|
callback(null, self.currentTransactions[txid]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var key = self._encodeTransactionKey(txid);
|
||||||
|
|
||||||
|
self.node.services.db.store.get(key, function(err, buffer) {
|
||||||
|
if(err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
var tx = self._decodeTransactionValue(buffer);
|
||||||
|
callback(null, tx);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
TransactionService.prototype._encodeTransactionKey = function(txid) {
|
TransactionService.prototype._encodeTransactionKey = function(txid) {
|
||||||
return Buffer.concat([this.prefix, new Buffer(txid, 'hex')]);
|
return Buffer.concat([this.prefix, new Buffer(txid, 'hex')]);
|
||||||
};
|
};
|
||||||
|
|
||||||
TransactionService.prototype._decodeTransactionKey = function(buffer) {
|
TransactionService.prototype._decodeTransactionKey = function(buffer) {
|
||||||
return buffer.slice(1).toString('hex');
|
return buffer.slice(2).toString('hex');
|
||||||
};
|
};
|
||||||
|
|
||||||
TransactionService.prototype._encodeTransactionValue = function(transaction, height) {
|
TransactionService.prototype._encodeTransactionValue = function(transaction, height) {
|
||||||
var heightBuffer = new Buffer(4);
|
var heightBuffer = new Buffer(4);
|
||||||
heightBuffer.writeUInt32BE(height);
|
heightBuffer.writeUInt32BE();
|
||||||
return new Buffer.concat([heightBuffer, transaction.uncheckedSerialize()]);
|
return new Buffer.concat([heightBuffer, transaction.toBuffer()]);
|
||||||
};
|
};
|
||||||
|
|
||||||
TransactionService.prototype._decodeTransactionValue = function(buffer) {
|
TransactionService.prototype._decodeTransactionValue = function(buffer) {
|
||||||
return {
|
var height = buffer.readUInt32BE();
|
||||||
height: Buffer.readUInt32BE(height),
|
var transaction = new bitcore.Transaction(buffer.slice(4));
|
||||||
transaction: new bitcore.Transaction(buffer)
|
transaction.__height = height;
|
||||||
};
|
return transaction;
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = TransactionService;
|
module.exports = TransactionService;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user