get address index to work

This commit is contained in:
Patrick Nagurny 2017-01-19 16:11:04 -05:00
parent df8cf1bbb3
commit efa8480651
6 changed files with 550 additions and 857 deletions

View File

@ -264,6 +264,7 @@ Node.prototype.start = function(callback) {
if (err) {
return callback(err);
}
self.emit('ready');
callback();
}

View File

@ -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;

View File

@ -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;

View File

@ -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;
};
/**

View File

@ -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;

View File

@ -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;