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) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
self.emit('ready');
|
||||
callback();
|
||||
}
|
||||
|
||||
@ -139,6 +139,7 @@ function loadModule(req, service) {
|
||||
var serviceFile = path.resolve(__dirname, '../services/' + service.name);
|
||||
service.module = req(serviceFile);
|
||||
} catch(e) {
|
||||
console.log('Could not load ' + service.name + ' service');
|
||||
log.error(e.stack);
|
||||
var servicePackage = req(service.name + '/package.json');
|
||||
var serviceModule = service.name;
|
||||
|
||||
@ -9,12 +9,17 @@ var $ = bitcore.util.preconditions;
|
||||
|
||||
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);
|
||||
addressSizeBuffer.writeUInt8(address.length);
|
||||
var addressBuffer = new Buffer(address, 'utf8');
|
||||
var heightBuffer = new Buffer(4);
|
||||
heightBuffer.writeUInt32BE(height);
|
||||
var txidBuffer = new Buffer(txid, 'hex');
|
||||
var indexBuffer = new Buffer(4);
|
||||
indexBuffer.writeUInt32BE(index);
|
||||
var spendingBuffer = new Buffer(1);
|
||||
@ -22,8 +27,8 @@ exports.encodeAddressIndexKey = function(address, isSpent, height, txidBuffer, i
|
||||
var isSpentBuffer = new Buffer(1);
|
||||
isSpentBuffer.writeUInt8(isSpent);
|
||||
|
||||
var key = Buffer.concat({
|
||||
constants.PREFIXES.ADDRESS,
|
||||
return Buffer.concat([
|
||||
this.prefix,
|
||||
addressSizeBuffer,
|
||||
addressBuffer,
|
||||
isSpentBuffer,
|
||||
@ -31,38 +36,37 @@ exports.encodeAddressIndexKey = function(address, isSpent, height, txidBuffer, i
|
||||
txidBuffer,
|
||||
indexBuffer,
|
||||
spendingBuffer
|
||||
});
|
||||
]);
|
||||
};
|
||||
|
||||
exports.decodeAddressIndexKey = function(buffer) {
|
||||
Encoding.prototype.decodeAddressIndexKey = function(buffer) {
|
||||
var reader = new BufferReader(buffer);
|
||||
var prefix = reader.read(1);
|
||||
var prefix = reader.read(2);
|
||||
|
||||
var addressSize = reader.readUInt8();
|
||||
var address = reader.read(addressSize).toString('utf8');
|
||||
var isSpent = reader.readUInt8();
|
||||
var height = reader.readUInt32BE();
|
||||
var txid = reader.read(32);
|
||||
var txid = reader.read(32).toString('hex');
|
||||
var index = reader.readUInt32BE();
|
||||
var spending = reader.readUInt8();
|
||||
return {
|
||||
prefix: prefix,
|
||||
address: address,
|
||||
isSpent: isSpent ? true : false,
|
||||
height: height,
|
||||
txid: txid,
|
||||
index: outputIndex,
|
||||
index: index,
|
||||
spending: spending ? true : false
|
||||
};
|
||||
};
|
||||
|
||||
exports.encodeAddressIndexValue = function(satoshis, scriptBuffer) {
|
||||
Encoding.prototype.encodeAddressIndexValue = function(satoshis, scriptBuffer) {
|
||||
var satoshisBuffer = new Buffer(8);
|
||||
satoshisBuffer.writeDoubleBE(satoshis);
|
||||
return Buffer.concat([satoshisBuffer, scriptBuffer]);
|
||||
};
|
||||
|
||||
exports.decodeAddressIndexValue = function(buffer) {
|
||||
Encoding.prototype.decodeAddressIndexValue = function(buffer) {
|
||||
var satoshis = buffer.readDoubleBE(0);
|
||||
var scriptBuffer = buffer.slice(8, buffer.length);
|
||||
return {
|
||||
@ -71,354 +75,355 @@ exports.decodeAddressIndexValue = function(buffer) {
|
||||
};
|
||||
};
|
||||
|
||||
exports.encodeUnspentIndexKey = function(hashTypeBuffer, hashBuffer, txidBuffer, index) {
|
||||
var indexBuffer = new Buffer(4);
|
||||
indexBuffer.writeUInt32BE(index);
|
||||
// exports.encodeUnspentIndexKey = function(hashTypeBuffer, hashBuffer, txid, index) {
|
||||
// var indexBuffer = new Buffer(4);
|
||||
// indexBuffer.writeUInt32BE(index);
|
||||
// var txidBuffer = new Buffer(txid, 'hex');
|
||||
|
||||
var key = Buffer.concat({
|
||||
constants.PREFIXES.UNSPENT,
|
||||
hashTypeBuffer,
|
||||
hashBuffer,
|
||||
constants.SPACER_MIN,
|
||||
txidBuffer,
|
||||
indexBuffer
|
||||
});
|
||||
};
|
||||
// var key = Buffer.concat([
|
||||
// constants.PREFIXES.UNSPENT,
|
||||
// hashTypeBuffer,
|
||||
// hashBuffer,
|
||||
// constants.SPACER_MIN,
|
||||
// txidBuffer,
|
||||
// indexBuffer
|
||||
// ]);
|
||||
// };
|
||||
|
||||
exports.decodeUnspentIndexKey = function(buffer) {
|
||||
var reader = new BufferReader(buffer);
|
||||
var prefix = reader.read(1);
|
||||
var hashTypeBuffer = reader.read(1);
|
||||
var hashBuffer = reader.read(20);
|
||||
// exports.decodeUnspentIndexKey = function(buffer) {
|
||||
// var reader = new BufferReader(buffer);
|
||||
// var prefix = reader.read(1);
|
||||
// var hashTypeBuffer = reader.read(1);
|
||||
// var hashBuffer = reader.read(20);
|
||||
|
||||
var spacer = reader.read(1);
|
||||
var txid = reader.read(32);
|
||||
var index = reader.readUInt32BE();
|
||||
return {
|
||||
prefix: prefix,
|
||||
hashTypeBuffer: hashTypeBuffer,
|
||||
hashBuffer: hashBuffer,
|
||||
txid: txid,
|
||||
index: outputIndex
|
||||
};
|
||||
};
|
||||
// var spacer = reader.read(1);
|
||||
// var txid = reader.read(32);
|
||||
// var index = reader.readUInt32BE();
|
||||
// return {
|
||||
// prefix: prefix,
|
||||
// hashTypeBuffer: hashTypeBuffer,
|
||||
// hashBuffer: hashBuffer,
|
||||
// txid: txid,
|
||||
// index: outputIndex
|
||||
// };
|
||||
// };
|
||||
|
||||
exports.encodeUnspentIndexValue = function(satoshis, height, scriptBuffer) {
|
||||
var satoshisBuffer = new Buffer(8);
|
||||
satoshisBuffer.writeDoubleBE(satoshis);
|
||||
// exports.encodeUnspentIndexValue = function(satoshis, height, scriptBuffer) {
|
||||
// var satoshisBuffer = new Buffer(8);
|
||||
// satoshisBuffer.writeDoubleBE(satoshis);
|
||||
|
||||
var heightBuffer = new Buffer(4);
|
||||
heightBuffer.writeUInt32BE(height);
|
||||
// var heightBuffer = new Buffer(4);
|
||||
// heightBuffer.writeUInt32BE(height);
|
||||
|
||||
return Buffer.concat([satoshisBuffer, heightBuffer, scriptBuffer]);
|
||||
};
|
||||
// return Buffer.concat([satoshisBuffer, heightBuffer, scriptBuffer]);
|
||||
// };
|
||||
|
||||
exports.decodeUnspentIndexValue = function(buffer) {
|
||||
var satoshis = buffer.readDoubleBE(0);
|
||||
var height = buffer.readUInt32BE(8);
|
||||
var scriptBuffer = buffer.slice(12, buffer.length);
|
||||
// exports.decodeUnspentIndexValue = function(buffer) {
|
||||
// var satoshis = buffer.readDoubleBE(0);
|
||||
// var height = buffer.readUInt32BE(8);
|
||||
// var scriptBuffer = buffer.slice(12, buffer.length);
|
||||
|
||||
return {
|
||||
satoshis: satoshis,
|
||||
height: height,
|
||||
scriptBuffer: scriptBuffer
|
||||
};
|
||||
};
|
||||
// return {
|
||||
// satoshis: satoshis,
|
||||
// height: height,
|
||||
// scriptBuffer: scriptBuffer
|
||||
// };
|
||||
// };
|
||||
|
||||
|
||||
exports.encodeSpentIndexSyncKey = function(txidBuffer, outputIndex) {
|
||||
var outputIndexBuffer = new Buffer(4);
|
||||
outputIndexBuffer.writeUInt32BE(outputIndex);
|
||||
var key = Buffer.concat([
|
||||
txidBuffer,
|
||||
outputIndexBuffer
|
||||
]);
|
||||
return key.toString('binary');
|
||||
};
|
||||
// exports.encodeSpentIndexSyncKey = function(txidBuffer, outputIndex) {
|
||||
// var outputIndexBuffer = new Buffer(4);
|
||||
// outputIndexBuffer.writeUInt32BE(outputIndex);
|
||||
// var key = Buffer.concat([
|
||||
// txidBuffer,
|
||||
// outputIndexBuffer
|
||||
// ]);
|
||||
// return key.toString('binary');
|
||||
// };
|
||||
|
||||
exports.encodeMempoolAddressIndexKey = function(hashBuffer, hashTypeBuffer) {
|
||||
var key = Buffer.concat([
|
||||
hashBuffer,
|
||||
hashTypeBuffer,
|
||||
]);
|
||||
return key.toString('binary');
|
||||
};
|
||||
// exports.encodeMempoolAddressIndexKey = function(hashBuffer, hashTypeBuffer) {
|
||||
// var key = Buffer.concat([
|
||||
// hashBuffer,
|
||||
// hashTypeBuffer,
|
||||
// ]);
|
||||
// return key.toString('binary');
|
||||
// };
|
||||
|
||||
|
||||
exports.encodeOutputKey = function(hashBuffer, hashTypeBuffer, height, txidBuffer, outputIndex) {
|
||||
var heightBuffer = new Buffer(4);
|
||||
heightBuffer.writeUInt32BE(height);
|
||||
var outputIndexBuffer = new Buffer(4);
|
||||
outputIndexBuffer.writeUInt32BE(outputIndex);
|
||||
var key = Buffer.concat([
|
||||
constants.PREFIXES.OUTPUTS,
|
||||
hashBuffer,
|
||||
hashTypeBuffer,
|
||||
constants.SPACER_MIN,
|
||||
heightBuffer,
|
||||
txidBuffer,
|
||||
outputIndexBuffer
|
||||
]);
|
||||
return key;
|
||||
};
|
||||
// exports.encodeOutputKey = function(hashBuffer, hashTypeBuffer, height, txidBuffer, outputIndex) {
|
||||
// var heightBuffer = new Buffer(4);
|
||||
// heightBuffer.writeUInt32BE(height);
|
||||
// var outputIndexBuffer = new Buffer(4);
|
||||
// outputIndexBuffer.writeUInt32BE(outputIndex);
|
||||
// var key = Buffer.concat([
|
||||
// constants.PREFIXES.OUTPUTS,
|
||||
// hashBuffer,
|
||||
// hashTypeBuffer,
|
||||
// constants.SPACER_MIN,
|
||||
// heightBuffer,
|
||||
// txidBuffer,
|
||||
// outputIndexBuffer
|
||||
// ]);
|
||||
// return key;
|
||||
// };
|
||||
|
||||
exports.decodeOutputKey = function(buffer) {
|
||||
var reader = new BufferReader(buffer);
|
||||
var prefix = reader.read(1);
|
||||
var hashBuffer = reader.read(20);
|
||||
var hashTypeBuffer = reader.read(1);
|
||||
var spacer = reader.read(1);
|
||||
var height = reader.readUInt32BE();
|
||||
var txid = reader.read(32);
|
||||
var outputIndex = reader.readUInt32BE();
|
||||
return {
|
||||
prefix: prefix,
|
||||
hashBuffer: hashBuffer,
|
||||
hashTypeBuffer: hashTypeBuffer,
|
||||
height: height,
|
||||
txid: txid,
|
||||
outputIndex: outputIndex
|
||||
};
|
||||
};
|
||||
// exports.decodeOutputKey = function(buffer) {
|
||||
// var reader = new BufferReader(buffer);
|
||||
// var prefix = reader.read(1);
|
||||
// var hashBuffer = reader.read(20);
|
||||
// var hashTypeBuffer = reader.read(1);
|
||||
// var spacer = reader.read(1);
|
||||
// var height = reader.readUInt32BE();
|
||||
// var txid = reader.read(32);
|
||||
// var outputIndex = reader.readUInt32BE();
|
||||
// return {
|
||||
// prefix: prefix,
|
||||
// hashBuffer: hashBuffer,
|
||||
// hashTypeBuffer: hashTypeBuffer,
|
||||
// height: height,
|
||||
// txid: txid,
|
||||
// outputIndex: outputIndex
|
||||
// };
|
||||
// };
|
||||
|
||||
exports.encodeOutputValue = function(satoshis, scriptBuffer) {
|
||||
var satoshisBuffer = new Buffer(8);
|
||||
satoshisBuffer.writeDoubleBE(satoshis);
|
||||
return Buffer.concat([satoshisBuffer, scriptBuffer]);
|
||||
};
|
||||
// exports.encodeOutputValue = function(satoshis, scriptBuffer) {
|
||||
// var satoshisBuffer = new Buffer(8);
|
||||
// satoshisBuffer.writeDoubleBE(satoshis);
|
||||
// return Buffer.concat([satoshisBuffer, scriptBuffer]);
|
||||
// };
|
||||
|
||||
exports.encodeOutputMempoolValue = function(satoshis, timestampBuffer, scriptBuffer) {
|
||||
var satoshisBuffer = new Buffer(8);
|
||||
satoshisBuffer.writeDoubleBE(satoshis);
|
||||
return Buffer.concat([satoshisBuffer, timestampBuffer, scriptBuffer]);
|
||||
};
|
||||
// exports.encodeOutputMempoolValue = function(satoshis, timestampBuffer, scriptBuffer) {
|
||||
// var satoshisBuffer = new Buffer(8);
|
||||
// satoshisBuffer.writeDoubleBE(satoshis);
|
||||
// return Buffer.concat([satoshisBuffer, timestampBuffer, scriptBuffer]);
|
||||
// };
|
||||
|
||||
exports.decodeOutputValue = function(buffer) {
|
||||
var satoshis = buffer.readDoubleBE(0);
|
||||
var scriptBuffer = buffer.slice(8, buffer.length);
|
||||
return {
|
||||
satoshis: satoshis,
|
||||
scriptBuffer: scriptBuffer
|
||||
};
|
||||
};
|
||||
// exports.decodeOutputValue = function(buffer) {
|
||||
// var satoshis = buffer.readDoubleBE(0);
|
||||
// var scriptBuffer = buffer.slice(8, buffer.length);
|
||||
// return {
|
||||
// satoshis: satoshis,
|
||||
// scriptBuffer: scriptBuffer
|
||||
// };
|
||||
// };
|
||||
|
||||
exports.decodeOutputMempoolValue = function(buffer) {
|
||||
var satoshis = buffer.readDoubleBE(0);
|
||||
var timestamp = buffer.readDoubleBE(8);
|
||||
var scriptBuffer = buffer.slice(16, buffer.length);
|
||||
return {
|
||||
satoshis: satoshis,
|
||||
timestamp: timestamp,
|
||||
scriptBuffer: scriptBuffer
|
||||
};
|
||||
};
|
||||
// exports.decodeOutputMempoolValue = function(buffer) {
|
||||
// var satoshis = buffer.readDoubleBE(0);
|
||||
// var timestamp = buffer.readDoubleBE(8);
|
||||
// var scriptBuffer = buffer.slice(16, buffer.length);
|
||||
// return {
|
||||
// satoshis: satoshis,
|
||||
// timestamp: timestamp,
|
||||
// scriptBuffer: scriptBuffer
|
||||
// };
|
||||
// };
|
||||
|
||||
exports.encodeInputKey = function(hashBuffer, hashTypeBuffer, height, prevTxIdBuffer, outputIndex) {
|
||||
var heightBuffer = new Buffer(4);
|
||||
heightBuffer.writeUInt32BE(height);
|
||||
var outputIndexBuffer = new Buffer(4);
|
||||
outputIndexBuffer.writeUInt32BE(outputIndex);
|
||||
return Buffer.concat([
|
||||
constants.PREFIXES.SPENTS,
|
||||
hashBuffer,
|
||||
hashTypeBuffer,
|
||||
constants.SPACER_MIN,
|
||||
heightBuffer,
|
||||
prevTxIdBuffer,
|
||||
outputIndexBuffer
|
||||
]);
|
||||
};
|
||||
// exports.encodeInputKey = function(hashBuffer, hashTypeBuffer, height, prevTxIdBuffer, outputIndex) {
|
||||
// var heightBuffer = new Buffer(4);
|
||||
// heightBuffer.writeUInt32BE(height);
|
||||
// var outputIndexBuffer = new Buffer(4);
|
||||
// outputIndexBuffer.writeUInt32BE(outputIndex);
|
||||
// return Buffer.concat([
|
||||
// constants.PREFIXES.SPENTS,
|
||||
// hashBuffer,
|
||||
// hashTypeBuffer,
|
||||
// constants.SPACER_MIN,
|
||||
// heightBuffer,
|
||||
// prevTxIdBuffer,
|
||||
// outputIndexBuffer
|
||||
// ]);
|
||||
// };
|
||||
|
||||
exports.decodeInputKey = function(buffer) {
|
||||
var reader = new BufferReader(buffer);
|
||||
var prefix = reader.read(1);
|
||||
var hashBuffer = reader.read(20);
|
||||
var hashTypeBuffer = reader.read(1);
|
||||
var spacer = reader.read(1);
|
||||
var height = reader.readUInt32BE();
|
||||
var prevTxId = reader.read(32);
|
||||
var outputIndex = reader.readUInt32BE();
|
||||
return {
|
||||
prefix: prefix,
|
||||
hashBuffer: hashBuffer,
|
||||
hashTypeBuffer: hashTypeBuffer,
|
||||
height: height,
|
||||
prevTxId: prevTxId,
|
||||
outputIndex: outputIndex
|
||||
};
|
||||
};
|
||||
// exports.decodeInputKey = function(buffer) {
|
||||
// var reader = new BufferReader(buffer);
|
||||
// var prefix = reader.read(1);
|
||||
// var hashBuffer = reader.read(20);
|
||||
// var hashTypeBuffer = reader.read(1);
|
||||
// var spacer = reader.read(1);
|
||||
// var height = reader.readUInt32BE();
|
||||
// var prevTxId = reader.read(32);
|
||||
// var outputIndex = reader.readUInt32BE();
|
||||
// return {
|
||||
// prefix: prefix,
|
||||
// hashBuffer: hashBuffer,
|
||||
// hashTypeBuffer: hashTypeBuffer,
|
||||
// height: height,
|
||||
// prevTxId: prevTxId,
|
||||
// outputIndex: outputIndex
|
||||
// };
|
||||
// };
|
||||
|
||||
exports.encodeInputValue = function(txidBuffer, inputIndex) {
|
||||
var inputIndexBuffer = new Buffer(4);
|
||||
inputIndexBuffer.writeUInt32BE(inputIndex);
|
||||
return Buffer.concat([
|
||||
txidBuffer,
|
||||
inputIndexBuffer
|
||||
]);
|
||||
};
|
||||
// exports.encodeInputValue = function(txidBuffer, inputIndex) {
|
||||
// var inputIndexBuffer = new Buffer(4);
|
||||
// inputIndexBuffer.writeUInt32BE(inputIndex);
|
||||
// return Buffer.concat([
|
||||
// txidBuffer,
|
||||
// inputIndexBuffer
|
||||
// ]);
|
||||
// };
|
||||
|
||||
exports.decodeInputValue = function(buffer) {
|
||||
var txid = buffer.slice(0, 32);
|
||||
var inputIndex = buffer.readUInt32BE(32);
|
||||
return {
|
||||
txid: txid,
|
||||
inputIndex: inputIndex
|
||||
};
|
||||
};
|
||||
// exports.decodeInputValue = function(buffer) {
|
||||
// var txid = buffer.slice(0, 32);
|
||||
// var inputIndex = buffer.readUInt32BE(32);
|
||||
// return {
|
||||
// txid: txid,
|
||||
// inputIndex: inputIndex
|
||||
// };
|
||||
// };
|
||||
|
||||
exports.encodeInputKeyMap = function(outputTxIdBuffer, outputIndex) {
|
||||
var outputIndexBuffer = new Buffer(4);
|
||||
outputIndexBuffer.writeUInt32BE(outputIndex);
|
||||
return Buffer.concat([
|
||||
constants.PREFIXES.SPENTSMAP,
|
||||
outputTxIdBuffer,
|
||||
outputIndexBuffer
|
||||
]);
|
||||
};
|
||||
// exports.encodeInputKeyMap = function(outputTxIdBuffer, outputIndex) {
|
||||
// var outputIndexBuffer = new Buffer(4);
|
||||
// outputIndexBuffer.writeUInt32BE(outputIndex);
|
||||
// return Buffer.concat([
|
||||
// constants.PREFIXES.SPENTSMAP,
|
||||
// outputTxIdBuffer,
|
||||
// outputIndexBuffer
|
||||
// ]);
|
||||
// };
|
||||
|
||||
exports.decodeInputKeyMap = function(buffer) {
|
||||
var txid = buffer.slice(1, 33);
|
||||
var outputIndex = buffer.readUInt32BE(33);
|
||||
return {
|
||||
outputTxId: txid,
|
||||
outputIndex: outputIndex
|
||||
};
|
||||
};
|
||||
// exports.decodeInputKeyMap = function(buffer) {
|
||||
// var txid = buffer.slice(1, 33);
|
||||
// var outputIndex = buffer.readUInt32BE(33);
|
||||
// return {
|
||||
// outputTxId: txid,
|
||||
// outputIndex: outputIndex
|
||||
// };
|
||||
// };
|
||||
|
||||
exports.encodeInputValueMap = function(inputTxIdBuffer, inputIndex) {
|
||||
var inputIndexBuffer = new Buffer(4);
|
||||
inputIndexBuffer.writeUInt32BE(inputIndex);
|
||||
return Buffer.concat([
|
||||
inputTxIdBuffer,
|
||||
inputIndexBuffer
|
||||
]);
|
||||
};
|
||||
// exports.encodeInputValueMap = function(inputTxIdBuffer, inputIndex) {
|
||||
// var inputIndexBuffer = new Buffer(4);
|
||||
// inputIndexBuffer.writeUInt32BE(inputIndex);
|
||||
// return Buffer.concat([
|
||||
// inputTxIdBuffer,
|
||||
// inputIndexBuffer
|
||||
// ]);
|
||||
// };
|
||||
|
||||
exports.decodeInputValueMap = function(buffer) {
|
||||
var txid = buffer.slice(0, 32);
|
||||
var inputIndex = buffer.readUInt32BE(32);
|
||||
return {
|
||||
inputTxId: txid,
|
||||
inputIndex: inputIndex
|
||||
};
|
||||
};
|
||||
// exports.decodeInputValueMap = function(buffer) {
|
||||
// var txid = buffer.slice(0, 32);
|
||||
// var inputIndex = buffer.readUInt32BE(32);
|
||||
// return {
|
||||
// inputTxId: txid,
|
||||
// inputIndex: inputIndex
|
||||
// };
|
||||
// };
|
||||
|
||||
exports.encodeSummaryCacheKey = function(address) {
|
||||
return Buffer.concat([address.hashBuffer, constants.HASH_TYPES_MAP[address.type]]);
|
||||
};
|
||||
// exports.encodeSummaryCacheKey = function(address) {
|
||||
// return Buffer.concat([address.hashBuffer, constants.HASH_TYPES_MAP[address.type]]);
|
||||
// };
|
||||
|
||||
exports.decodeSummaryCacheKey = function(buffer, network) {
|
||||
var hashBuffer = buffer.read(20);
|
||||
var type = constants.HASH_TYPES_READABLE[buffer.read(20, 2).toString('hex')];
|
||||
var address = new Address({
|
||||
hashBuffer: hashBuffer,
|
||||
type: type,
|
||||
network: network
|
||||
});
|
||||
return address;
|
||||
};
|
||||
// exports.decodeSummaryCacheKey = function(buffer, network) {
|
||||
// var hashBuffer = buffer.read(20);
|
||||
// var type = constants.HASH_TYPES_READABLE[buffer.read(20, 2).toString('hex')];
|
||||
// var address = new Address({
|
||||
// hashBuffer: hashBuffer,
|
||||
// type: type,
|
||||
// network: network
|
||||
// });
|
||||
// return address;
|
||||
// };
|
||||
|
||||
exports.encodeSummaryCacheValue = function(cache, tipHeight, tipHash) {
|
||||
var tipHashBuffer = new Buffer(tipHash, 'hex');
|
||||
var buffer = new Buffer(new Array(20));
|
||||
buffer.writeUInt32BE(tipHeight);
|
||||
buffer.writeDoubleBE(cache.result.totalReceived, 4);
|
||||
buffer.writeDoubleBE(cache.result.balance, 12);
|
||||
var txidBuffers = [];
|
||||
for (var i = 0; i < cache.result.txids.length; i++) {
|
||||
var buf = new Buffer(new Array(36));
|
||||
var txid = cache.result.txids[i];
|
||||
buf.write(txid, 'hex');
|
||||
buf.writeUInt32BE(cache.result.appearanceIds[txid], 32);
|
||||
txidBuffers.push(buf);
|
||||
}
|
||||
var txidsBuffer = Buffer.concat(txidBuffers);
|
||||
var value = Buffer.concat([tipHashBuffer, buffer, txidsBuffer]);
|
||||
// exports.encodeSummaryCacheValue = function(cache, tipHeight, tipHash) {
|
||||
// var tipHashBuffer = new Buffer(tipHash, 'hex');
|
||||
// var buffer = new Buffer(new Array(20));
|
||||
// buffer.writeUInt32BE(tipHeight);
|
||||
// buffer.writeDoubleBE(cache.result.totalReceived, 4);
|
||||
// buffer.writeDoubleBE(cache.result.balance, 12);
|
||||
// var txidBuffers = [];
|
||||
// for (var i = 0; i < cache.result.txids.length; i++) {
|
||||
// var buf = new Buffer(new Array(36));
|
||||
// var txid = cache.result.txids[i];
|
||||
// buf.write(txid, 'hex');
|
||||
// buf.writeUInt32BE(cache.result.appearanceIds[txid], 32);
|
||||
// txidBuffers.push(buf);
|
||||
// }
|
||||
// var txidsBuffer = Buffer.concat(txidBuffers);
|
||||
// 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 height = buffer.readUInt32BE(32);
|
||||
var totalReceived = buffer.readDoubleBE(36);
|
||||
var balance = buffer.readDoubleBE(44);
|
||||
// var hash = buffer.slice(0, 32).toString('hex');
|
||||
// var height = buffer.readUInt32BE(32);
|
||||
// var totalReceived = buffer.readDoubleBE(36);
|
||||
// var balance = buffer.readDoubleBE(44);
|
||||
|
||||
// read 32 byte chunks until exhausted
|
||||
var appearanceIds = {};
|
||||
var txids = [];
|
||||
var pos = 52;
|
||||
while(pos < buffer.length) {
|
||||
var txid = buffer.slice(pos, pos + 32).toString('hex');
|
||||
var txidHeight = buffer.readUInt32BE(pos + 32);
|
||||
txids.push(txid);
|
||||
appearanceIds[txid] = txidHeight;
|
||||
pos += 36;
|
||||
}
|
||||
// // read 32 byte chunks until exhausted
|
||||
// var appearanceIds = {};
|
||||
// var txids = [];
|
||||
// var pos = 52;
|
||||
// while(pos < buffer.length) {
|
||||
// var txid = buffer.slice(pos, pos + 32).toString('hex');
|
||||
// var txidHeight = buffer.readUInt32BE(pos + 32);
|
||||
// txids.push(txid);
|
||||
// appearanceIds[txid] = txidHeight;
|
||||
// pos += 36;
|
||||
// }
|
||||
|
||||
var cache = {
|
||||
height: height,
|
||||
hash: hash,
|
||||
result: {
|
||||
appearanceIds: appearanceIds,
|
||||
txids: txids,
|
||||
totalReceived: totalReceived,
|
||||
balance: balance,
|
||||
unconfirmedAppearanceIds: {}, // unconfirmed values are never stored in cache
|
||||
unconfirmedBalance: 0
|
||||
}
|
||||
};
|
||||
// var cache = {
|
||||
// height: height,
|
||||
// hash: hash,
|
||||
// result: {
|
||||
// appearanceIds: appearanceIds,
|
||||
// txids: txids,
|
||||
// totalReceived: totalReceived,
|
||||
// balance: balance,
|
||||
// unconfirmedAppearanceIds: {}, // unconfirmed values are never stored in cache
|
||||
// unconfirmedBalance: 0
|
||||
// }
|
||||
// };
|
||||
|
||||
return cache;
|
||||
};
|
||||
// return cache;
|
||||
// };
|
||||
|
||||
exports.getAddressInfo = function(addressStr) {
|
||||
var addrObj = bitcore.Address(addressStr);
|
||||
var hashTypeBuffer = constants.HASH_TYPES_MAP[addrObj.type];
|
||||
// exports.getAddressInfo = function(addressStr) {
|
||||
// var addrObj = bitcore.Address(addressStr);
|
||||
// var hashTypeBuffer = constants.HASH_TYPES_MAP[addrObj.type];
|
||||
|
||||
return {
|
||||
hashBuffer: addrObj.hashBuffer,
|
||||
hashTypeBuffer: hashTypeBuffer,
|
||||
hashTypeReadable: addrObj.type
|
||||
};
|
||||
};
|
||||
// return {
|
||||
// hashBuffer: addrObj.hashBuffer,
|
||||
// hashTypeBuffer: hashTypeBuffer,
|
||||
// hashTypeReadable: addrObj.type
|
||||
// };
|
||||
// };
|
||||
|
||||
/**
|
||||
* This function is optimized to return address information about an output script
|
||||
* without constructing a Bitcore Address instance.
|
||||
* @param {Script} - An instance of a Bitcore Script
|
||||
* @param {Network|String} - The network for the address
|
||||
*/
|
||||
exports.extractAddressInfoFromScript = function(script, network) {
|
||||
$.checkArgument(network, 'Second argument is expected to be a network');
|
||||
var hashBuffer;
|
||||
var addressType;
|
||||
var hashTypeBuffer;
|
||||
if (script.isPublicKeyHashOut()) {
|
||||
hashBuffer = script.chunks[2].buf;
|
||||
hashTypeBuffer = constants.HASH_TYPES.PUBKEY;
|
||||
addressType = Address.PayToPublicKeyHash;
|
||||
} else if (script.isScriptHashOut()) {
|
||||
hashBuffer = script.chunks[1].buf;
|
||||
hashTypeBuffer = constants.HASH_TYPES.REDEEMSCRIPT;
|
||||
addressType = Address.PayToScriptHash;
|
||||
} else if (script.isPublicKeyOut()) {
|
||||
var pubkey = script.chunks[0].buf;
|
||||
var address = Address.fromPublicKey(new PublicKey(pubkey), network);
|
||||
hashBuffer = address.hashBuffer;
|
||||
hashTypeBuffer = constants.HASH_TYPES.PUBKEY;
|
||||
// pay-to-publickey doesn't have an address, however for compatibility
|
||||
// purposes, we can create an address
|
||||
addressType = Address.PayToPublicKeyHash;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return {
|
||||
hashBuffer: hashBuffer,
|
||||
hashTypeBuffer: hashTypeBuffer,
|
||||
addressType: addressType
|
||||
};
|
||||
};
|
||||
// /**
|
||||
// * This function is optimized to return address information about an output script
|
||||
// * without constructing a Bitcore Address instance.
|
||||
// * @param {Script} - An instance of a Bitcore Script
|
||||
// * @param {Network|String} - The network for the address
|
||||
// */
|
||||
// exports.extractAddressInfoFromScript = function(script, network) {
|
||||
// $.checkArgument(network, 'Second argument is expected to be a network');
|
||||
// var hashBuffer;
|
||||
// var addressType;
|
||||
// var hashTypeBuffer;
|
||||
// if (script.isPublicKeyHashOut()) {
|
||||
// hashBuffer = script.chunks[2].buf;
|
||||
// hashTypeBuffer = constants.HASH_TYPES.PUBKEY;
|
||||
// addressType = Address.PayToPublicKeyHash;
|
||||
// } else if (script.isScriptHashOut()) {
|
||||
// hashBuffer = script.chunks[1].buf;
|
||||
// hashTypeBuffer = constants.HASH_TYPES.REDEEMSCRIPT;
|
||||
// addressType = Address.PayToScriptHash;
|
||||
// } else if (script.isPublicKeyOut()) {
|
||||
// var pubkey = script.chunks[0].buf;
|
||||
// var address = Address.fromPublicKey(new PublicKey(pubkey), network);
|
||||
// hashBuffer = address.hashBuffer;
|
||||
// hashTypeBuffer = constants.HASH_TYPES.PUBKEY;
|
||||
// // pay-to-publickey doesn't have an address, however for compatibility
|
||||
// // purposes, we can create an address
|
||||
// addressType = Address.PayToPublicKeyHash;
|
||||
// } else {
|
||||
// return false;
|
||||
// }
|
||||
// return {
|
||||
// hashBuffer: hashBuffer,
|
||||
// hashTypeBuffer: hashTypeBuffer,
|
||||
// addressType: addressType
|
||||
// };
|
||||
// };
|
||||
|
||||
module.exports = exports;
|
||||
module.exports = Encoding;
|
||||
|
||||
@ -20,7 +20,7 @@ var EventEmitter = require('events').EventEmitter;
|
||||
var Address = bitcore.Address;
|
||||
var AddressHistory = require('./history');
|
||||
var constants = require('./constants');
|
||||
var encoding = require('./encoding');
|
||||
var Encoding = require('./encoding');
|
||||
var InputsTransformStream = require('./streams/inputs-transform');
|
||||
var OutputsTransformStream = require('./streams/outputs-transform');
|
||||
|
||||
@ -37,29 +37,23 @@ var OutputsTransformStream = require('./streams/outputs-transform');
|
||||
var AddressService = function(options) {
|
||||
BaseService.call(this, options);
|
||||
|
||||
this.subscriptions = {};
|
||||
this.subscriptions['address/transaction'] = {};
|
||||
this.subscriptions['address/balance'] = {};
|
||||
// this.subscriptions = {};
|
||||
// this.subscriptions['address/transaction'] = {};
|
||||
// this.subscriptions['address/balance'] = {};
|
||||
|
||||
this._bitcoindTransactionListener = this.transactionHandler.bind(this);
|
||||
this._bitcoindTransactionLeaveListener = this.transactionLeaveHandler.bind(this);
|
||||
this.node.services.bitcoind.on('tx', this._bitcoindTransactionListener);
|
||||
this.node.services.bitcoind.on('txleave', this._bitcoindTransactionLeaveListener);
|
||||
// this._bitcoindTransactionListener = this.transactionHandler.bind(this);
|
||||
// this._bitcoindTransactionLeaveListener = this.transactionLeaveHandler.bind(this);
|
||||
// this.node.services.bitcoind.on('tx', this._bitcoindTransactionListener);
|
||||
// this.node.services.bitcoind.on('txleave', this._bitcoindTransactionLeaveListener);
|
||||
|
||||
this.maxInputsQueryLength = options.maxInputsQueryLength || constants.MAX_INPUTS_QUERY_LENGTH;
|
||||
this.maxOutputsQueryLength = options.maxOutputsQueryLength || constants.MAX_OUTPUTS_QUERY_LENGTH;
|
||||
|
||||
this.concurrency = options.concurrency || 20;
|
||||
|
||||
this._setMempoolIndexPath();
|
||||
if (options.mempoolMemoryIndex) {
|
||||
this.levelupStore = memdown;
|
||||
} 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
|
||||
// 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);
|
||||
@ -73,71 +67,27 @@ AddressService.dependencies = [
|
||||
AddressService.prototype.start = function(callback) {
|
||||
var self = this;
|
||||
|
||||
async.series([
|
||||
this.store = this.node.services.db.store;
|
||||
|
||||
function(next) {
|
||||
// Flush any existing mempool index
|
||||
if (fs.existsSync(self.mempoolIndexPath)) {
|
||||
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
|
||||
);
|
||||
this.node.services.db.getPrefix(this.name, function(err, prefix) {
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
], callback);
|
||||
|
||||
self.prefix = prefix;
|
||||
|
||||
self.encoding = new Encoding(self.prefix);
|
||||
|
||||
callback();
|
||||
});
|
||||
};
|
||||
|
||||
AddressService.prototype.stop = function(callback) {
|
||||
// TODO Keep track of ongoing db requests before shutting down
|
||||
this.node.services.bitcoind.removeListener('tx', this._bitcoindTransactionListener);
|
||||
this.node.services.bitcoind.removeListener('txleave', this._bitcoindTransactionLeaveListener);
|
||||
this.mempoolIndex.close(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;
|
||||
// this.node.services.bitcoind.removeListener('tx', this._bitcoindTransactionListener);
|
||||
// this.node.services.bitcoind.removeListener('txleave', this._bitcoindTransactionLeaveListener);
|
||||
// this.mempoolIndex.close(callback);
|
||||
setImmediate(callback);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -160,289 +110,21 @@ AddressService.prototype.getAPIMethods = function() {
|
||||
* Called by the Bus to get the available events for this service.
|
||||
*/
|
||||
AddressService.prototype.getPublishEvents = function() {
|
||||
return [
|
||||
{
|
||||
name: 'address/transaction',
|
||||
scope: this,
|
||||
subscribe: this.subscribe.bind(this, 'address/transaction'),
|
||||
unsubscribe: this.unsubscribe.bind(this, 'address/transaction')
|
||||
},
|
||||
{
|
||||
name: 'address/balance',
|
||||
scope: this,
|
||||
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);
|
||||
return [];
|
||||
// return [
|
||||
// {
|
||||
// name: 'address/transaction',
|
||||
// scope: this,
|
||||
// subscribe: this.subscribe.bind(this, 'address/transaction'),
|
||||
// unsubscribe: this.unsubscribe.bind(this, 'address/transaction')
|
||||
// },
|
||||
// {
|
||||
// name: 'address/balance',
|
||||
// scope: this,
|
||||
// subscribe: this.subscribe.bind(this, 'address/balance'),
|
||||
// unsubscribe: this.unsubscribe.bind(this, 'address/balance')
|
||||
// }
|
||||
// ];
|
||||
};
|
||||
|
||||
/**
|
||||
@ -453,6 +135,8 @@ AddressService.prototype.updateMempoolIndex = function(tx, add, callback) {
|
||||
* @param {Function} callback
|
||||
*/
|
||||
AddressService.prototype.blockHandler = function(block, connectBlock, callback) {
|
||||
var self = this;
|
||||
|
||||
var txs = block.transactions;
|
||||
var height = block.__height;
|
||||
|
||||
@ -480,16 +164,21 @@ AddressService.prototype.blockHandler = function(block, connectBlock, callback)
|
||||
var output = outputs[outputIndex];
|
||||
|
||||
var script = output.script;
|
||||
var address = script.toAddress();
|
||||
|
||||
if(!script) {
|
||||
log.debug('Invalid script');
|
||||
continue;
|
||||
}
|
||||
|
||||
var address = self._getAddressString(script);
|
||||
|
||||
var key = encoding.encodeAddressIndexKey(address, false, height, txid, outputIndex);
|
||||
var value = encoding.encodeOutputValue(output.satoshis, output._scriptBuffer);
|
||||
if(!address) {
|
||||
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({
|
||||
type: action,
|
||||
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
|
||||
for (var addressKey in txmessages) {
|
||||
this.transactionEventHandler(txmessages[addressKey]);
|
||||
//this.transactionEventHandler(txmessages[addressKey]);
|
||||
}
|
||||
|
||||
if(tx.isCoinbase()) {
|
||||
return next();
|
||||
}
|
||||
|
||||
async.eachLimit(inputs, self.concurrency, function(input, next) {
|
||||
async.eachOfLimit(inputs, self.concurrency, function(input, inputIndex, next) {
|
||||
var input = inputs[inputIndex];
|
||||
var inputHash;
|
||||
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, function(err, result) {
|
||||
self.node.services.transaction.getTransaction(input.prevTxId.toString('hex'), function(err, tx) {
|
||||
if(err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
var tx = result.transaction;
|
||||
var height = result.height;
|
||||
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 outputValue = encoding.encodeAdressIndexValue(output.satoshis, output._scriptBuffer);
|
||||
var inputValue = encoding.encodeAddressIndexValue(output.satoshis, input._scriptBuffer);
|
||||
|
||||
var address = self._getAddressString(input.script, output);
|
||||
|
||||
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([{
|
||||
type: action,
|
||||
@ -562,11 +260,33 @@ AddressService.prototype.blockHandler = function(block, connectBlock, callback)
|
||||
next();
|
||||
});
|
||||
|
||||
}, function(err) {
|
||||
callback(err, operations);
|
||||
});
|
||||
}, next);
|
||||
}, 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
|
||||
this.version = 2;
|
||||
|
||||
this.dbPrefix = 0;
|
||||
this.dbPrefix = '\u0000\u0000';
|
||||
this.tip = null;
|
||||
this.genesis = null;
|
||||
|
||||
@ -105,10 +105,10 @@ DB.prototype._setDataPath = function() {
|
||||
DB.prototype._checkVersion = function(callback) {
|
||||
var self = this;
|
||||
var options = {
|
||||
keyEncoding: 'binary',
|
||||
keyEncoding: 'string',
|
||||
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) {
|
||||
// The database is brand new and doesn't have a tip stored
|
||||
// we can skip version checking
|
||||
@ -116,7 +116,7 @@ DB.prototype._checkVersion = function(callback) {
|
||||
} else if (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;
|
||||
if (err instanceof levelup.errors.NotFoundError) {
|
||||
// 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) {
|
||||
var versionBuffer = new Buffer(new Array(4));
|
||||
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.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.once('ready', function() {
|
||||
log.info('Bitcoin Database Ready');
|
||||
|
||||
// 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) {
|
||||
self.sync();
|
||||
}
|
||||
@ -240,12 +240,12 @@ DB.prototype.close = function(callback) {
|
||||
* @param {String} txInfo.hash - The hash of the transaction
|
||||
*/
|
||||
DB.prototype.transactionHandler = function(tx) {
|
||||
for (var i = 0; i < this.subscriptions.transaction.length; i++) {
|
||||
this.subscriptions.transaction[i].emit('db/transaction', {
|
||||
rejected: !txInfo.mempool,
|
||||
tx: tx
|
||||
});
|
||||
}
|
||||
// for (var i = 0; i < this.subscriptions.transaction.length; i++) {
|
||||
// this.subscriptions.transaction[i].emit('db/transaction', {
|
||||
// rejected: !txInfo.mempool,
|
||||
// tx: tx
|
||||
// });
|
||||
// }
|
||||
};
|
||||
|
||||
/**
|
||||
@ -267,11 +267,11 @@ DB.prototype.loadTip = function(callback) {
|
||||
var self = this;
|
||||
|
||||
var options = {
|
||||
keyEncoding: 'binary',
|
||||
keyEncoding: 'string',
|
||||
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) {
|
||||
self.tip = self.genesis;
|
||||
self.tip.__height = 0;
|
||||
@ -288,7 +288,8 @@ DB.prototype.loadTip = function(callback) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
var hash = tipData.toString('hex');
|
||||
var hash = tipData.slice(0, 32).toString('hex');
|
||||
var height = tipData.readUInt32BE(32);
|
||||
|
||||
var times = 0;
|
||||
async.retry({times: 3, interval: self.retryInterval}, function(done) {
|
||||
@ -311,12 +312,9 @@ DB.prototype.loadTip = function(callback) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
tip.__height = height;
|
||||
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();
|
||||
});
|
||||
});
|
||||
@ -330,55 +328,6 @@ DB.prototype.getBlock = function(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
|
||||
* @param {String} txid - A transaction hash
|
||||
@ -532,23 +481,26 @@ DB.prototype.runAllBlockHandlers = function(block, add, callback) {
|
||||
var operations = [];
|
||||
|
||||
// Notify block subscribers
|
||||
for (var i = 0; i < this.subscriptions.block.length; i++) {
|
||||
this.subscriptions.block[i].emit('db/block', block.hash);
|
||||
}
|
||||
// for (var i = 0; i < this.subscriptions.block.length; i++) {
|
||||
// this.subscriptions.block[i].emit('db/block', block.hash);
|
||||
// }
|
||||
|
||||
// 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({
|
||||
type: 'put',
|
||||
key: DB.PREFIXES.TIP,
|
||||
value: tipHash
|
||||
});
|
||||
|
||||
// Update block index
|
||||
operations.push({
|
||||
type: add ? 'put' : 'del',
|
||||
key: this._encodeBlockIndexKey(block.header.timestamp),
|
||||
value: this._encodeBlockIndexValue(block.hash)
|
||||
key: self.dbPrefix + 'tip',
|
||||
value: tipData
|
||||
});
|
||||
|
||||
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,
|
||||
* by moving backwards on both chains until there is a meeting point.
|
||||
@ -739,6 +675,7 @@ DB.prototype.sync = function() {
|
||||
height = self.tip.__height;
|
||||
return height < self.node.services.bitcoind.height && !self.node.stopping;
|
||||
}, function(done) {
|
||||
console.log('fetching block ' + (height + 1));
|
||||
self.node.services.bitcoind.getBlock(height + 1, function(err, block) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
@ -805,6 +742,57 @@ DB.prototype.sync = function() {
|
||||
DB.prototype.getPrefix = function(service, callback) {
|
||||
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(
|
||||
[
|
||||
getPrefix,
|
||||
@ -814,56 +802,6 @@ DB.prototype.getPrefix = function(service, 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;
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
'use strict';
|
||||
var BaseService = require('../../service');
|
||||
var BaseService = require('../service');
|
||||
var inherits = require('util').inherits;
|
||||
var bitcore = require('bitcore-lib');
|
||||
|
||||
function TransactionService(options) {
|
||||
BaseService.call(this, options);
|
||||
|
||||
this.currentTransactions = {};
|
||||
}
|
||||
|
||||
inherits(TransactionService, BaseService);
|
||||
@ -41,8 +43,13 @@ TransactionService.prototype.blockHandler = function(block, connectBlock, callba
|
||||
|
||||
var operations = [];
|
||||
|
||||
this.currentTransactions = {};
|
||||
|
||||
for(var i = 0; i < block.transactions.length; i++) {
|
||||
var tx = block.transactions[i];
|
||||
tx.__height = block.__height;
|
||||
|
||||
this.currentTransactions[tx.id] = tx;
|
||||
|
||||
operations.push({
|
||||
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) {
|
||||
return Buffer.concat([this.prefix, new Buffer(txid, 'hex')]);
|
||||
};
|
||||
|
||||
TransactionService.prototype._decodeTransactionKey = function(buffer) {
|
||||
return buffer.slice(1).toString('hex');
|
||||
return buffer.slice(2).toString('hex');
|
||||
};
|
||||
|
||||
TransactionService.prototype._encodeTransactionValue = function(transaction, height) {
|
||||
var heightBuffer = new Buffer(4);
|
||||
heightBuffer.writeUInt32BE(height);
|
||||
return new Buffer.concat([heightBuffer, transaction.uncheckedSerialize()]);
|
||||
heightBuffer.writeUInt32BE();
|
||||
return new Buffer.concat([heightBuffer, transaction.toBuffer()]);
|
||||
};
|
||||
|
||||
TransactionService.prototype._decodeTransactionValue = function(buffer) {
|
||||
return {
|
||||
height: Buffer.readUInt32BE(height),
|
||||
transaction: new bitcore.Transaction(buffer)
|
||||
};
|
||||
var height = buffer.readUInt32BE();
|
||||
var transaction = new bitcore.Transaction(buffer.slice(4));
|
||||
transaction.__height = height;
|
||||
return transaction;
|
||||
};
|
||||
|
||||
module.exports = TransactionService;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user