Improve db storage efficiency by ~2 times
Encodes and decodes keys and values for leveldb storage more efficiently.
This commit is contained in:
parent
d3641f3b0a
commit
f88eee5a1c
@ -10,6 +10,7 @@ var bitcore = require('bitcore');
|
|||||||
var $ = bitcore.util.preconditions;
|
var $ = bitcore.util.preconditions;
|
||||||
var _ = bitcore.deps._;
|
var _ = bitcore.deps._;
|
||||||
var Hash = bitcore.crypto.Hash;
|
var Hash = bitcore.crypto.Hash;
|
||||||
|
var BufferReader = bitcore.encoding.BufferReader;
|
||||||
var EventEmitter = require('events').EventEmitter;
|
var EventEmitter = require('events').EventEmitter;
|
||||||
var PublicKey = bitcore.PublicKey;
|
var PublicKey = bitcore.PublicKey;
|
||||||
var Address = bitcore.Address;
|
var Address = bitcore.Address;
|
||||||
@ -34,10 +35,13 @@ AddressService.dependencies = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
AddressService.PREFIXES = {
|
AddressService.PREFIXES = {
|
||||||
OUTPUTS: 'outs',
|
OUTPUTS: new Buffer('32', 'hex'),
|
||||||
SPENTS: 'sp'
|
SPENTS: new Buffer('33', 'hex')
|
||||||
};
|
};
|
||||||
|
|
||||||
|
AddressService.SPACER_MIN = new Buffer('00', 'hex');
|
||||||
|
AddressService.SPACER_MAX = new Buffer('ff', 'hex');
|
||||||
|
|
||||||
AddressService.prototype.getAPIMethods = function() {
|
AddressService.prototype.getAPIMethods = function() {
|
||||||
return [
|
return [
|
||||||
['getBalance', this, this.getBalance, 2],
|
['getBalance', this, this.getBalance, 2],
|
||||||
@ -153,6 +157,7 @@ AddressService.prototype._extractAddressInfoFromScript = function(script) {
|
|||||||
|
|
||||||
AddressService.prototype.blockHandler = function(block, addOutput, callback) {
|
AddressService.prototype.blockHandler = function(block, addOutput, callback) {
|
||||||
var txs = block.transactions;
|
var txs = block.transactions;
|
||||||
|
var height = block.__height;
|
||||||
|
|
||||||
var action = 'put';
|
var action = 'put';
|
||||||
if (!addOutput) {
|
if (!addOutput) {
|
||||||
@ -188,43 +193,28 @@ AddressService.prototype.blockHandler = function(block, addOutput, callback) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
addressInfo.hashHex = addressInfo.hashBuffer.toString('hex');
|
|
||||||
|
|
||||||
// We need to use the height for indexes (and not the timestamp) because the
|
// We need to use the height for indexes (and not the timestamp) because the
|
||||||
// the timestamp has unreliable sequential ordering. The next block
|
// the timestamp has unreliable sequential ordering. The next block
|
||||||
// can have a time that is previous to the previous block (however not
|
// 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
|
// less than the mean of the 11 previous blocks) and not greater than 2
|
||||||
// hours in the future.
|
// hours in the future.
|
||||||
var height = block.__height;
|
var key = this._encodeOutputKey(addressInfo.hashBuffer, height, txid, outputIndex);
|
||||||
|
var value = this._encodeOutputValue(output.satoshis, output._scriptBuffer);
|
||||||
var scriptHex = output._scriptBuffer.toString('hex');
|
|
||||||
|
|
||||||
// To lookup outputs by address and height
|
|
||||||
var key = [
|
|
||||||
AddressService.PREFIXES.OUTPUTS,
|
|
||||||
addressInfo.hashHex,
|
|
||||||
height,
|
|
||||||
txid,
|
|
||||||
outputIndex
|
|
||||||
].join('-');
|
|
||||||
|
|
||||||
// TODO use buffers directly to save on disk storage
|
|
||||||
|
|
||||||
var value = [output.satoshis, scriptHex].join(':');
|
|
||||||
|
|
||||||
operations.push({
|
operations.push({
|
||||||
type: action,
|
type: action,
|
||||||
key: key,
|
key: key,
|
||||||
value: value
|
value: value
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addressInfo.hashHex = addressInfo.hashBuffer.toString('hex');
|
||||||
|
|
||||||
// Collect data for subscribers
|
// Collect data for subscribers
|
||||||
if (txmessages[addressInfo.hashHex]) {
|
if (txmessages[addressInfo.hashHex]) {
|
||||||
txmessages[addressInfo.hashHex].outputIndexes.push(outputIndex);
|
txmessages[addressInfo.hashHex].outputIndexes.push(outputIndex);
|
||||||
} else {
|
} else {
|
||||||
txmessages[addressInfo.hashHex] = {
|
txmessages[addressInfo.hashHex] = {
|
||||||
tx: tx,
|
tx: tx,
|
||||||
height: block.__height,
|
height: height,
|
||||||
outputIndexes: [outputIndex],
|
outputIndexes: [outputIndex],
|
||||||
addressInfo: addressInfo,
|
addressInfo: addressInfo,
|
||||||
timestamp: block.header.timestamp
|
timestamp: block.header.timestamp
|
||||||
@ -247,30 +237,19 @@ AddressService.prototype.blockHandler = function(block, addOutput, callback) {
|
|||||||
for(var inputIndex = 0; inputIndex < inputs.length; inputIndex++) {
|
for(var inputIndex = 0; inputIndex < inputs.length; inputIndex++) {
|
||||||
|
|
||||||
var input = inputs[inputIndex];
|
var input = inputs[inputIndex];
|
||||||
|
var inputHash;
|
||||||
var inputHashBuffer;
|
|
||||||
|
|
||||||
if (input.script.isPublicKeyHashIn()) {
|
if (input.script.isPublicKeyHashIn()) {
|
||||||
inputHashBuffer = Hash.sha256ripemd160(input.script.chunks[1].buf);
|
inputHash = Hash.sha256ripemd160(input.script.chunks[1].buf);
|
||||||
} else if (input.script.isScriptHashIn()) {
|
} else if (input.script.isScriptHashIn()) {
|
||||||
inputHashBuffer = Hash.sha256ripemd160(input.script.chunks[input.script.chunks.length - 1].buf);
|
inputHash = Hash.sha256ripemd160(input.script.chunks[input.script.chunks.length - 1].buf);
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// To be able to query inputs by address and spent height
|
// To be able to query inputs by address and spent height
|
||||||
var inputKey = [
|
var inputKey = this._encodeInputKey(inputHash, height, input.prevTxId, input.outputIndex);
|
||||||
AddressService.PREFIXES.SPENTS,
|
var inputValue = this._encodeInputValue(txid, inputIndex);
|
||||||
inputHashBuffer.toString('hex'),
|
|
||||||
block.__height,
|
|
||||||
input.prevTxId.toString('hex'),
|
|
||||||
input.outputIndex
|
|
||||||
].join('-');
|
|
||||||
|
|
||||||
var inputValue = [
|
|
||||||
txid,
|
|
||||||
inputIndex
|
|
||||||
].join(':');
|
|
||||||
|
|
||||||
operations.push({
|
operations.push({
|
||||||
type: action,
|
type: action,
|
||||||
@ -285,6 +264,104 @@ AddressService.prototype.blockHandler = function(block, addOutput, callback) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
AddressService.prototype._encodeOutputKey = function(hashBuffer, height, txid, outputIndex) {
|
||||||
|
var heightBuffer = new Buffer(4);
|
||||||
|
heightBuffer.writeUInt32BE(height);
|
||||||
|
var outputIndexBuffer = new Buffer(4);
|
||||||
|
outputIndexBuffer.writeUInt32BE(outputIndex);
|
||||||
|
var key = Buffer.concat([
|
||||||
|
AddressService.PREFIXES.OUTPUTS,
|
||||||
|
hashBuffer,
|
||||||
|
AddressService.SPACER_MIN,
|
||||||
|
heightBuffer,
|
||||||
|
new Buffer(txid, 'hex'), //TODO get buffer directly from tx
|
||||||
|
outputIndexBuffer
|
||||||
|
]);
|
||||||
|
return key;
|
||||||
|
};
|
||||||
|
|
||||||
|
AddressService.prototype._decodeOutputKey = function(buffer) {
|
||||||
|
var reader = new BufferReader(buffer);
|
||||||
|
var prefix = reader.read(1);
|
||||||
|
var hashBuffer = reader.read(20);
|
||||||
|
var spacer = reader.read(1);
|
||||||
|
var height = reader.readUInt32BE();
|
||||||
|
var txid = reader.read(32);
|
||||||
|
var outputIndex = reader.readUInt32BE();
|
||||||
|
return {
|
||||||
|
prefix: prefix,
|
||||||
|
hashBuffer: hashBuffer,
|
||||||
|
height: height,
|
||||||
|
txid: txid,
|
||||||
|
outputIndex: outputIndex
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
AddressService.prototype._encodeOutputValue = function(satoshis, scriptBuffer) {
|
||||||
|
var satoshisBuffer = new Buffer(8);
|
||||||
|
satoshisBuffer.writeDoubleBE(satoshis);
|
||||||
|
return Buffer.concat([satoshisBuffer, scriptBuffer]);
|
||||||
|
};
|
||||||
|
|
||||||
|
AddressService.prototype._decodeOutputValue = function(buffer) {
|
||||||
|
var satoshis = buffer.readDoubleBE(0);
|
||||||
|
var scriptBuffer = buffer.slice(8, buffer.length);
|
||||||
|
return {
|
||||||
|
satoshis: satoshis,
|
||||||
|
scriptBuffer: scriptBuffer
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
AddressService.prototype._encodeInputKey = function(hashBuffer, height, prevTxIdBuffer, outputIndex) {
|
||||||
|
var heightBuffer = new Buffer(4);
|
||||||
|
heightBuffer.writeUInt32BE(height);
|
||||||
|
var outputIndexBuffer = new Buffer(4);
|
||||||
|
outputIndexBuffer.writeUInt32BE(outputIndex);
|
||||||
|
return Buffer.concat([
|
||||||
|
AddressService.PREFIXES.SPENTS,
|
||||||
|
hashBuffer,
|
||||||
|
AddressService.SPACER_MIN,
|
||||||
|
heightBuffer,
|
||||||
|
prevTxIdBuffer,
|
||||||
|
outputIndexBuffer
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
AddressService.prototype._decodeInputKey = function(buffer) {
|
||||||
|
var reader = new BufferReader(buffer);
|
||||||
|
var prefix = reader.read(1);
|
||||||
|
var hashBuffer = reader.read(20);
|
||||||
|
var spacer = reader.read(1);
|
||||||
|
var height = reader.readUInt32BE();
|
||||||
|
var prevTxId = reader.read(32);
|
||||||
|
var outputIndex = reader.readUInt32BE();
|
||||||
|
return {
|
||||||
|
prefix: prefix,
|
||||||
|
hashBuffer: hashBuffer,
|
||||||
|
height: height,
|
||||||
|
prevTxId: prevTxId,
|
||||||
|
outputIndex: outputIndex
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
AddressService.prototype._encodeInputValue = function(txid, inputIndex) {
|
||||||
|
var inputIndexBuffer = new Buffer(4);
|
||||||
|
inputIndexBuffer.writeUInt32BE(inputIndex);
|
||||||
|
return Buffer.concat([
|
||||||
|
new Buffer(txid, 'hex'),
|
||||||
|
inputIndexBuffer
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
AddressService.prototype._decodeInputValue = function(buffer) {
|
||||||
|
var txid = buffer.slice(0, 32);
|
||||||
|
var inputIndex = buffer.readUInt32BE(32);
|
||||||
|
return {
|
||||||
|
txid: txid,
|
||||||
|
inputIndex: inputIndex
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Object} obj
|
* @param {Object} obj
|
||||||
* @param {Transaction} obj.tx - The transaction
|
* @param {Transaction} obj.tx - The transaction
|
||||||
@ -424,45 +501,56 @@ AddressService.prototype.getInputs = function(addressStr, options, callback) {
|
|||||||
var inputs = [];
|
var inputs = [];
|
||||||
var stream;
|
var stream;
|
||||||
|
|
||||||
var hashHex = bitcore.Address(addressStr).hashBuffer.toString('hex');
|
var hashBuffer = bitcore.Address(addressStr).hashBuffer;
|
||||||
|
|
||||||
if (options.start && options.end) {
|
if (options.start && options.end) {
|
||||||
|
|
||||||
// The positions will be flipped because the end position should be greater
|
var endBuffer = new Buffer(4);
|
||||||
// than the starting position for the stream, and we'll add one to the end key
|
endBuffer.writeUInt32BE(options.end);
|
||||||
// so that it's included in the results.
|
|
||||||
|
|
||||||
var endKey = [AddressService.PREFIXES.SPENTS, hashHex, options.start + 1].join('-');
|
var startBuffer = new Buffer(4);
|
||||||
var startKey = [AddressService.PREFIXES.SPENTS, hashHex, options.end].join('-');
|
startBuffer.writeUInt32BE(options.start + 1);
|
||||||
|
|
||||||
stream = this.node.services.db.store.createReadStream({
|
stream = this.node.services.db.store.createReadStream({
|
||||||
start: startKey,
|
gte: Buffer.concat([
|
||||||
end: endKey
|
AddressService.PREFIXES.SPENTS,
|
||||||
|
hashBuffer,
|
||||||
|
AddressService.SPACER_MIN,
|
||||||
|
endBuffer
|
||||||
|
]),
|
||||||
|
lte: Buffer.concat([
|
||||||
|
AddressService.PREFIXES.SPENTS,
|
||||||
|
hashBuffer,
|
||||||
|
AddressService.SPACER_MIN,
|
||||||
|
startBuffer
|
||||||
|
]),
|
||||||
|
valueEncoding: 'binary',
|
||||||
|
keyEncoding: 'binary'
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
var allKey = [AddressService.PREFIXES.SPENTS, hashHex].join('-');
|
var allKey = Buffer.concat([AddressService.PREFIXES.SPENTS, hashBuffer]);
|
||||||
stream = this.node.services.db.store.createReadStream({
|
stream = this.node.services.db.store.createReadStream({
|
||||||
start: allKey,
|
gte: Buffer.concat([allKey, AddressService.SPACER_MIN]),
|
||||||
end: allKey + '~'
|
lte: Buffer.concat([allKey, AddressService.SPACER_MAX]),
|
||||||
|
valueEncoding: 'binary',
|
||||||
|
keyEncoding: 'binary'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
stream.on('data', function(data) {
|
stream.on('data', function(data) {
|
||||||
|
|
||||||
var key = data.key.split('-');
|
var key = self._decodeInputKey(data.key);
|
||||||
var value = data.value.split(':');
|
var value = self._decodeInputValue(data.value);
|
||||||
|
|
||||||
var blockHeight = Number(key[2]);
|
var input = {
|
||||||
|
|
||||||
var output = {
|
|
||||||
address: addressStr,
|
address: addressStr,
|
||||||
txid: value[0],
|
txid: value.txid.toString('hex'),
|
||||||
inputIndex: Number(value[1]),
|
inputIndex: value.inputIndex,
|
||||||
height: blockHeight,
|
height: key.height,
|
||||||
confirmations: self.node.services.db.tip.__height - blockHeight + 1
|
confirmations: self.node.services.db.tip.__height - key.height + 1
|
||||||
};
|
};
|
||||||
|
|
||||||
inputs.push(output);
|
inputs.push(input);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -502,44 +590,57 @@ AddressService.prototype.getOutputs = function(addressStr, options, callback) {
|
|||||||
$.checkArgument(_.isObject(options), 'Second argument is expected to be an options object.');
|
$.checkArgument(_.isObject(options), 'Second argument is expected to be an options object.');
|
||||||
$.checkArgument(_.isFunction(callback), 'Third argument is expected to be a callback function.');
|
$.checkArgument(_.isFunction(callback), 'Third argument is expected to be a callback function.');
|
||||||
|
|
||||||
var hashHex = bitcore.Address(addressStr).hashBuffer.toString('hex');
|
var hashBuffer = bitcore.Address(addressStr).hashBuffer;
|
||||||
|
|
||||||
var outputs = [];
|
var outputs = [];
|
||||||
var stream;
|
var stream;
|
||||||
|
|
||||||
if (options.start && options.end) {
|
if (options.start && options.end) {
|
||||||
|
|
||||||
// The positions will be flipped because the end position should be greater
|
var startBuffer = new Buffer(4);
|
||||||
// than the starting position for the stream, and we'll add one to the end key
|
startBuffer.writeUInt32BE(options.start + 1);
|
||||||
// so that it's included in the results.
|
var endBuffer = new Buffer(4);
|
||||||
var endKey = [AddressService.PREFIXES.OUTPUTS, hashHex, options.start + 1].join('-');
|
endBuffer.writeUInt32BE(options.end);
|
||||||
var startKey = [AddressService.PREFIXES.OUTPUTS, hashHex, options.end].join('-');
|
|
||||||
|
|
||||||
stream = this.node.services.db.store.createReadStream({
|
stream = this.node.services.db.store.createReadStream({
|
||||||
start: startKey,
|
gte: Buffer.concat([
|
||||||
end: endKey
|
AddressService.PREFIXES.OUTPUTS,
|
||||||
|
hashBuffer,
|
||||||
|
AddressService.SPACER_MIN,
|
||||||
|
endBuffer
|
||||||
|
]),
|
||||||
|
lte: Buffer.concat([
|
||||||
|
AddressService.PREFIXES.OUTPUTS,
|
||||||
|
hashBuffer,
|
||||||
|
AddressService.SPACER_MIN,
|
||||||
|
startBuffer
|
||||||
|
]),
|
||||||
|
valueEncoding: 'binary',
|
||||||
|
keyEncoding: 'binary'
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
var allKey = [AddressService.PREFIXES.OUTPUTS, hashHex].join('-');
|
var allKey = Buffer.concat([AddressService.PREFIXES.OUTPUTS, hashBuffer]);
|
||||||
stream = this.node.services.db.store.createReadStream({
|
stream = this.node.services.db.store.createReadStream({
|
||||||
start: allKey,
|
gte: Buffer.concat([allKey, AddressService.SPACER_MIN]),
|
||||||
end: allKey + '~'
|
lte: Buffer.concat([allKey, AddressService.SPACER_MAX]),
|
||||||
|
valueEncoding: 'binary',
|
||||||
|
keyEncoding: 'binary'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
stream.on('data', function(data) {
|
stream.on('data', function(data) {
|
||||||
|
|
||||||
var key = data.key.split('-');
|
var key = self._decodeOutputKey(data.key);
|
||||||
var value = data.value.split(':');
|
var value = self._decodeOutputValue(data.value);
|
||||||
|
|
||||||
var output = {
|
var output = {
|
||||||
address: addressStr,
|
address: addressStr,
|
||||||
txid: key[3],
|
txid: key.txid.toString('hex'),
|
||||||
outputIndex: Number(key[4]),
|
outputIndex: key.outputIndex,
|
||||||
height: Number(key[2]),
|
height: key.height,
|
||||||
satoshis: Number(value[0]),
|
satoshis: value.satoshis,
|
||||||
script: value[1],
|
script: value.scriptBuffer.toString('hex'),
|
||||||
confirmations: self.node.services.db.tip.__height - Number(key[2]) + 1
|
confirmations: self.node.services.db.tip.__height - key.height + 1
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs.push(output);
|
outputs.push(output);
|
||||||
|
|||||||
@ -108,53 +108,6 @@ describe('Address Service', function() {
|
|||||||
var am;
|
var am;
|
||||||
var testBlock = bitcore.Block.fromString(blockData);
|
var testBlock = bitcore.Block.fromString(blockData);
|
||||||
|
|
||||||
var data = [
|
|
||||||
{
|
|
||||||
key: {
|
|
||||||
hashHex: bitcore.Address('1F1MAvhTKg2VG29w8cXsiSN2PJ8gSsrJw').hashBuffer.toString('hex'),
|
|
||||||
height: 345003,
|
|
||||||
txid: 'fdbefe0d064729d85556bd3ab13c3a889b685d042499c02b4aa2064fb1e16923',
|
|
||||||
outputIndex: 0
|
|
||||||
},
|
|
||||||
value: {
|
|
||||||
satoshis: 2502227470,
|
|
||||||
script: '76a91402a61d2066d19e9e2fd348a8320b7ebd4dd3ca2b88ac',
|
|
||||||
blockHeight: 345003
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: {
|
|
||||||
hashHex: bitcore.Address('1Q8ec8kG7c7HqgK7uSzQyWsX9tzepRcKEL').hashBuffer.toString('hex'),
|
|
||||||
height: 345003,
|
|
||||||
prevTxId: '3d7d5d98df753ef2a4f82438513c509e3b11f3e738e94a7234967b03a03123a9',
|
|
||||||
prevOutputIndex: 32
|
|
||||||
},
|
|
||||||
value: {
|
|
||||||
txid: '5780f3ee54889a0717152a01abee9a32cec1b0cdf8d5537a08c7bd9eeb6bfbca',
|
|
||||||
inputIndex: 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: {
|
|
||||||
hashHex: bitcore.Address('1Ep5LA4T6Y7zaBPiwruUJurjGFvCJHzJhm').hashBuffer.toString('hex'),
|
|
||||||
height: 345003,
|
|
||||||
txid: 'e66f3b989c790178de2fc1a5329f94c0d8905d0d3df4e7ecf0115e7f90a6283d',
|
|
||||||
outputIndex: 1
|
|
||||||
},
|
|
||||||
value: {
|
|
||||||
satoshis: 3100000,
|
|
||||||
script: '76a9149780ccd5356e2acc0ee439ee04e0fe69426c752888ac',
|
|
||||||
blockHeight: 345003
|
|
||||||
}
|
|
||||||
}
|
|
||||||
];
|
|
||||||
var key0 = data[0].key;
|
|
||||||
var value0 = data[0].value;
|
|
||||||
var key3 = data[1].key;
|
|
||||||
var value3 = data[1].value;
|
|
||||||
var key64 = data[2].key;
|
|
||||||
var value64 = data[2].value;
|
|
||||||
|
|
||||||
before(function() {
|
before(function() {
|
||||||
am = new AddressService({node: mocknode});
|
am = new AddressService({node: mocknode});
|
||||||
am.node.network = Networks.livenet;
|
am.node.network = Networks.livenet;
|
||||||
@ -173,17 +126,14 @@ describe('Address Service', function() {
|
|||||||
should.not.exist(err);
|
should.not.exist(err);
|
||||||
operations.length.should.equal(81);
|
operations.length.should.equal(81);
|
||||||
operations[0].type.should.equal('put');
|
operations[0].type.should.equal('put');
|
||||||
var expected0 = ['outs', key0.hashHex, key0.height, key0.txid, key0.outputIndex].join('-');
|
operations[0].key.toString('hex').should.equal('3202a61d2066d19e9e2fd348a8320b7ebd4dd3ca2b00000543abfdbefe0d064729d85556bd3ab13c3a889b685d042499c02b4aa2064fb1e1692300000000');
|
||||||
operations[0].key.should.equal(expected0);
|
operations[0].value.toString('hex').should.equal('41e2a49ec1c0000076a91402a61d2066d19e9e2fd348a8320b7ebd4dd3ca2b88ac');
|
||||||
operations[0].value.should.equal([value0.satoshis, value0.script].join(':'));
|
|
||||||
operations[3].type.should.equal('put');
|
operations[3].type.should.equal('put');
|
||||||
var expected3 = ['sp', key3.hashHex, key3.height, key3.prevTxId, key3.prevOutputIndex].join('-');
|
operations[3].key.toString('hex').should.equal('33fdbd324b28ea69e49c998816407dc055fb81d06e00000543ab3d7d5d98df753ef2a4f82438513c509e3b11f3e738e94a7234967b03a03123a900000020');
|
||||||
operations[3].key.should.equal(expected3);
|
operations[3].value.toString('hex').should.equal('5780f3ee54889a0717152a01abee9a32cec1b0cdf8d5537a08c7bd9eeb6bfbca00000000');
|
||||||
operations[3].value.should.equal([value3.txid, value3.inputIndex].join(':'));
|
|
||||||
operations[64].type.should.equal('put');
|
operations[64].type.should.equal('put');
|
||||||
var expected64 = ['outs', key64.hashHex, key64.height, key64.txid, key64.outputIndex].join('-');
|
operations[64].key.toString('hex').should.equal('329780ccd5356e2acc0ee439ee04e0fe69426c752800000543abe66f3b989c790178de2fc1a5329f94c0d8905d0d3df4e7ecf0115e7f90a6283d00000001');
|
||||||
operations[64].key.should.equal(expected64);
|
operations[64].value.toString('hex').should.equal('4147a6b00000000076a9149780ccd5356e2acc0ee439ee04e0fe69426c752888ac');
|
||||||
operations[64].value.should.equal([value64.satoshis, value64.script].join(':'));
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -199,14 +149,14 @@ describe('Address Service', function() {
|
|||||||
should.not.exist(err);
|
should.not.exist(err);
|
||||||
operations.length.should.equal(81);
|
operations.length.should.equal(81);
|
||||||
operations[0].type.should.equal('del');
|
operations[0].type.should.equal('del');
|
||||||
operations[0].key.should.equal(['outs', key0.hashHex, key0.height, key0.txid, key0.outputIndex].join('-'));
|
operations[0].key.toString('hex').should.equal('3202a61d2066d19e9e2fd348a8320b7ebd4dd3ca2b00000543abfdbefe0d064729d85556bd3ab13c3a889b685d042499c02b4aa2064fb1e1692300000000');
|
||||||
operations[0].value.should.equal([value0.satoshis, value0.script].join(':'));
|
operations[0].value.toString('hex').should.equal('41e2a49ec1c0000076a91402a61d2066d19e9e2fd348a8320b7ebd4dd3ca2b88ac');
|
||||||
operations[3].type.should.equal('del');
|
operations[3].type.should.equal('del');
|
||||||
operations[3].key.should.equal(['sp', key3.hashHex, key3.height, key3.prevTxId, key3.prevOutputIndex].join('-'));
|
operations[3].key.toString('hex').should.equal('33fdbd324b28ea69e49c998816407dc055fb81d06e00000543ab3d7d5d98df753ef2a4f82438513c509e3b11f3e738e94a7234967b03a03123a900000020');
|
||||||
operations[3].value.should.equal([value3.txid, value3.inputIndex].join(':'));
|
operations[3].value.toString('hex').should.equal('5780f3ee54889a0717152a01abee9a32cec1b0cdf8d5537a08c7bd9eeb6bfbca00000000');
|
||||||
operations[64].type.should.equal('del');
|
operations[64].type.should.equal('del');
|
||||||
operations[64].key.should.equal(['outs', key64.hashHex, key64.height, key64.txid, key64.outputIndex].join('-'));
|
operations[64].key.toString('hex').should.equal('329780ccd5356e2acc0ee439ee04e0fe69426c752800000543abe66f3b989c790178de2fc1a5329f94c0d8905d0d3df4e7ecf0115e7f90a6283d00000001');
|
||||||
operations[64].value.should.equal([value64.satoshis, value64.script].join(':'));
|
operations[64].value.toString('hex').should.equal('4147a6b00000000076a9149780ccd5356e2acc0ee439ee04e0fe69426c752888ac');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -442,7 +392,7 @@ describe('Address Service', function() {
|
|||||||
describe('#getInputs', function() {
|
describe('#getInputs', function() {
|
||||||
var am;
|
var am;
|
||||||
var address = '1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W';
|
var address = '1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W';
|
||||||
var hashHex = bitcore.Address(address).hashBuffer.toString('hex');
|
var hashBuffer = bitcore.Address(address).hashBuffer;
|
||||||
var db = {
|
var db = {
|
||||||
tip: {
|
tip: {
|
||||||
__height: 1
|
__height: 1
|
||||||
@ -470,8 +420,10 @@ describe('Address Service', function() {
|
|||||||
var createReadStreamCallCount = 0;
|
var createReadStreamCallCount = 0;
|
||||||
am.node.services.db.store = {
|
am.node.services.db.store = {
|
||||||
createReadStream: function(ops) {
|
createReadStream: function(ops) {
|
||||||
ops.start.should.equal([AddressService.PREFIXES.SPENTS, hashHex, 12].join('-'));
|
var gte = Buffer.concat([AddressService.PREFIXES.SPENTS, hashBuffer, new Buffer('000000000c', 'hex')]);
|
||||||
ops.end.should.equal([AddressService.PREFIXES.SPENTS, hashHex, 16].join('-'));
|
ops.gte.toString('hex').should.equal(gte.toString('hex'));
|
||||||
|
var lte = Buffer.concat([AddressService.PREFIXES.SPENTS, hashBuffer, new Buffer('0000000010', 'hex')]);
|
||||||
|
ops.lte.toString('hex').should.equal(lte.toString('hex'));
|
||||||
createReadStreamCallCount++;
|
createReadStreamCallCount++;
|
||||||
return testStream;
|
return testStream;
|
||||||
}
|
}
|
||||||
@ -490,8 +442,8 @@ describe('Address Service', function() {
|
|||||||
});
|
});
|
||||||
createReadStreamCallCount.should.equal(1);
|
createReadStreamCallCount.should.equal(1);
|
||||||
var data = {
|
var data = {
|
||||||
key: ['sp', address, '15', '125dd0e50fc732d67c37b6c56be7f9dc00b6859cebf982ee2cc83ed2d604bf87', '1'].join('-'),
|
key: new Buffer('33038a213afdfc551fc658e9a2a58a86e98d69b687000000000f125dd0e50fc732d67c37b6c56be7f9dc00b6859cebf982ee2cc83ed2d604bf8700000001', 'hex'),
|
||||||
value: ['3b6bc2939d1a70ce04bc4f619ee32608fbff5e565c1f9b02e4eaa97959c59ae7', '0'].join(':')
|
value: new Buffer('3b6bc2939d1a70ce04bc4f619ee32608fbff5e565c1f9b02e4eaa97959c59ae700000000', 'hex')
|
||||||
};
|
};
|
||||||
testStream.emit('data', data);
|
testStream.emit('data', data);
|
||||||
testStream.emit('close');
|
testStream.emit('close');
|
||||||
@ -504,8 +456,10 @@ describe('Address Service', function() {
|
|||||||
var createReadStreamCallCount = 0;
|
var createReadStreamCallCount = 0;
|
||||||
am.node.services.db.store = {
|
am.node.services.db.store = {
|
||||||
createReadStream: function(ops) {
|
createReadStream: function(ops) {
|
||||||
ops.start.should.equal([AddressService.PREFIXES.SPENTS, hashHex].join('-'));
|
var gte = Buffer.concat([AddressService.PREFIXES.SPENTS, hashBuffer, new Buffer('00', 'hex')]);
|
||||||
ops.end.should.equal([AddressService.PREFIXES.SPENTS, hashHex].join('-') + '~');
|
ops.gte.toString('hex').should.equal(gte.toString('hex'));
|
||||||
|
var lte = Buffer.concat([AddressService.PREFIXES.SPENTS, hashBuffer, new Buffer('ff', 'hex')]);
|
||||||
|
ops.lte.toString('hex').should.equal(lte.toString('hex'));
|
||||||
createReadStreamCallCount++;
|
createReadStreamCallCount++;
|
||||||
return testStream;
|
return testStream;
|
||||||
}
|
}
|
||||||
@ -524,8 +478,8 @@ describe('Address Service', function() {
|
|||||||
});
|
});
|
||||||
createReadStreamCallCount.should.equal(1);
|
createReadStreamCallCount.should.equal(1);
|
||||||
var data = {
|
var data = {
|
||||||
key: ['sp', address, '15', '125dd0e50fc732d67c37b6c56be7f9dc00b6859cebf982ee2cc83ed2d604bf87', '1'].join('-'),
|
key: new Buffer('33038a213afdfc551fc658e9a2a58a86e98d69b687000000000f125dd0e50fc732d67c37b6c56be7f9dc00b6859cebf982ee2cc83ed2d604bf8700000001', 'hex'),
|
||||||
value: ['3b6bc2939d1a70ce04bc4f619ee32608fbff5e565c1f9b02e4eaa97959c59ae7', '0'].join(':')
|
value: new Buffer('3b6bc2939d1a70ce04bc4f619ee32608fbff5e565c1f9b02e4eaa97959c59ae700000000', 'hex')
|
||||||
};
|
};
|
||||||
testStream.emit('data', data);
|
testStream.emit('data', data);
|
||||||
testStream.emit('close');
|
testStream.emit('close');
|
||||||
@ -553,7 +507,7 @@ describe('Address Service', function() {
|
|||||||
describe('#getOutputs', function() {
|
describe('#getOutputs', function() {
|
||||||
var am;
|
var am;
|
||||||
var address = '1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W';
|
var address = '1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W';
|
||||||
var hashHex = bitcore.Address('1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W').hashBuffer.toString('hex');
|
var hashBuffer = bitcore.Address('1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W').hashBuffer;
|
||||||
var db = {
|
var db = {
|
||||||
tip: {
|
tip: {
|
||||||
__height: 1
|
__height: 1
|
||||||
@ -585,8 +539,10 @@ describe('Address Service', function() {
|
|||||||
var createReadStreamCallCount = 0;
|
var createReadStreamCallCount = 0;
|
||||||
am.node.services.db.store = {
|
am.node.services.db.store = {
|
||||||
createReadStream: function(ops) {
|
createReadStream: function(ops) {
|
||||||
ops.start.should.equal([AddressService.PREFIXES.OUTPUTS, hashHex, 12].join('-'));
|
var gte = Buffer.concat([AddressService.PREFIXES.OUTPUTS, hashBuffer, new Buffer('000000000c', 'hex')]);
|
||||||
ops.end.should.equal([AddressService.PREFIXES.OUTPUTS, hashHex, 16].join('-'));
|
ops.gte.toString('hex').should.equal(gte.toString('hex'));
|
||||||
|
var lte = Buffer.concat([AddressService.PREFIXES.OUTPUTS, hashBuffer, new Buffer('0000000010', 'hex')]);
|
||||||
|
ops.lte.toString('hex').should.equal(lte.toString('hex'));
|
||||||
createReadStreamCallCount++;
|
createReadStreamCallCount++;
|
||||||
return testStream;
|
return testStream;
|
||||||
}
|
}
|
||||||
@ -607,8 +563,8 @@ describe('Address Service', function() {
|
|||||||
});
|
});
|
||||||
createReadStreamCallCount.should.equal(1);
|
createReadStreamCallCount.should.equal(1);
|
||||||
var data = {
|
var data = {
|
||||||
key: ['outs', address, '15', '125dd0e50fc732d67c37b6c56be7f9dc00b6859cebf982ee2cc83ed2d604bf87', '1'].join('-'),
|
key: new Buffer('32038a213afdfc551fc658e9a2a58a86e98d69b687000000000f125dd0e50fc732d67c37b6c56be7f9dc00b6859cebf982ee2cc83ed2d604bf8700000001', 'hex'),
|
||||||
value: ['4527773864', '76a914038a213afdfc551fc658e9a2a58a86e98d69b68788ac'].join(':')
|
value: new Buffer('41f0de058a80000076a914038a213afdfc551fc658e9a2a58a86e98d69b68788ac', 'hex')
|
||||||
};
|
};
|
||||||
testStream.emit('data', data);
|
testStream.emit('data', data);
|
||||||
testStream.emit('close');
|
testStream.emit('close');
|
||||||
@ -655,13 +611,13 @@ describe('Address Service', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
var data1 = {
|
var data1 = {
|
||||||
key: ['outs', address, 345000, '125dd0e50fc732d67c37b6c56be7f9dc00b6859cebf982ee2cc83ed2d604bf87', '1'].join('-'),
|
key: new Buffer('32038a213afdfc551fc658e9a2a58a86e98d69b68700000543a8125dd0e50fc732d67c37b6c56be7f9dc00b6859cebf982ee2cc83ed2d604bf8700000001', 'hex'),
|
||||||
value: ['4527773864', '76a914038a213afdfc551fc658e9a2a58a86e98d69b68788ac'].join(':')
|
value: new Buffer('41f0de058a80000076a914038a213afdfc551fc658e9a2a58a86e98d69b68788ac', 'hex')
|
||||||
};
|
};
|
||||||
|
|
||||||
var data2 = {
|
var data2 = {
|
||||||
key: ['outs', address, 345004, '3b6bc2939d1a70ce04bc4f619ee32608fbff5e565c1f9b02e4eaa97959c59ae7', '2'].join('-'),
|
key: new Buffer('32038a213afdfc551fc658e9a2a58a86e98d69b68700000543ac3b6bc2939d1a70ce04bc4f619ee32608fbff5e565c1f9b02e4eaa97959c59ae700000002', 'hex'),
|
||||||
value: ['10000', '76a914038a213afdfc551fc658e9a2a58a86e98d69b68788ac'].join(':')
|
value: new Buffer('40c388000000000076a914038a213afdfc551fc658e9a2a58a86e98d69b68788ac', 'hex')
|
||||||
};
|
};
|
||||||
|
|
||||||
readStream1.emit('data', data1);
|
readStream1.emit('data', data1);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user