Cleaned up old code that may not make it into the next release.
This commit is contained in:
parent
fe2d4231cb
commit
8d98abd080
237
lib/encoding.js
237
lib/encoding.js
@ -1,237 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
var bitcore = require('bitcore-lib');
|
|
||||||
var BufferReader = bitcore.encoding.BufferReader;
|
|
||||||
|
|
||||||
function Encoding(servicePrefix) {
|
|
||||||
this.servicePrefix = servicePrefix;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Encoding.prototype.encodeWalletTransactionKey = function(walletId, height) {
|
|
||||||
var buffers = [this.servicePrefix];
|
|
||||||
|
|
||||||
var walletIdSizeBuffer = new Buffer(1);
|
|
||||||
walletIdSizeBuffer.writeUInt8(walletId.length);
|
|
||||||
var walletIdBuffer = new Buffer(walletId, 'utf8');
|
|
||||||
|
|
||||||
buffers.push(walletIdSizeBuffer);
|
|
||||||
buffers.push(walletIdBuffer);
|
|
||||||
|
|
||||||
if(height !== undefined) {
|
|
||||||
var heightBuffer = new Buffer(4);
|
|
||||||
heightBuffer.writeUInt32BE(height);
|
|
||||||
buffers.push(heightBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Buffer.concat(buffers);
|
|
||||||
};
|
|
||||||
|
|
||||||
Encoding.prototype.decodeWalletTransactionKey = function(buffer) {
|
|
||||||
var reader = new BufferReader(buffer);
|
|
||||||
reader.read(1);
|
|
||||||
|
|
||||||
var walletSize = reader.readUInt8();
|
|
||||||
var walletId = reader.read(walletSize).toString('utf8');
|
|
||||||
var height = reader.readUInt32BE();
|
|
||||||
var blockIndex = reader.readUInt32BE();
|
|
||||||
|
|
||||||
return {
|
|
||||||
walletId: walletId,
|
|
||||||
height: height,
|
|
||||||
blockIndex: blockIndex
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
Encoding.prototype.encodeWalletTransactionValue = function(txid) {
|
|
||||||
return new Buffer(txid, 'hex');
|
|
||||||
};
|
|
||||||
|
|
||||||
Encoding.prototype.decodeWalletTransactionValue = function(buffer) {
|
|
||||||
return buffer.toString('hex');
|
|
||||||
};
|
|
||||||
|
|
||||||
Encoding.prototype.encodeWalletUtxoKey = function(walletId, txid, outputIndex) {
|
|
||||||
var buffers = [this.servicePrefix];
|
|
||||||
|
|
||||||
var walletIdSizeBuffer = new Buffer(1);
|
|
||||||
walletIdSizeBuffer.writeUInt8(walletId.length);
|
|
||||||
var walletIdBuffer = new Buffer(walletId, 'utf8');
|
|
||||||
|
|
||||||
buffers.push(walletIdSizeBuffer);
|
|
||||||
buffers.push(walletIdBuffer);
|
|
||||||
|
|
||||||
if(txid) {
|
|
||||||
var txidBuffer = new Buffer(txid, 'hex');
|
|
||||||
buffers.push(txidBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(outputIndex !== undefined) {
|
|
||||||
var outputIndexBuffer = new Buffer(4);
|
|
||||||
outputIndexBuffer.writeUInt32BE(outputIndex);
|
|
||||||
buffers.push(outputIndexBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Buffer.concat(buffers);
|
|
||||||
};
|
|
||||||
|
|
||||||
Encoding.prototype.decodeWalletUtxoKey = function(buffer) {
|
|
||||||
var reader = new BufferReader(buffer);
|
|
||||||
reader.read(1);
|
|
||||||
|
|
||||||
var walletIdSize = reader.readUInt8();
|
|
||||||
var walletId = reader.read(walletIdSize).toString('utf8');
|
|
||||||
var txid = reader.read(32).toString('hex');
|
|
||||||
var outputIndex = reader.readUInt32BE();
|
|
||||||
return {
|
|
||||||
walletId: walletId,
|
|
||||||
txid: txid,
|
|
||||||
outputIndex: outputIndex
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
Encoding.prototype.encodeWalletUtxoValue = function(height, satoshis, scriptBuffer) {
|
|
||||||
var heightBuffer = new Buffer(4);
|
|
||||||
heightBuffer.writeUInt32BE(height);
|
|
||||||
var satoshisBuffer = new Buffer(8);
|
|
||||||
satoshisBuffer.writeDoubleBE(satoshis);
|
|
||||||
return Buffer.concat([height, satoshisBuffer, scriptBuffer]);
|
|
||||||
};
|
|
||||||
|
|
||||||
Encoding.prototype.decodeWalletUtxoValue = function(buffer) {
|
|
||||||
var reader = new BufferReader(buffer);
|
|
||||||
var height = reader.readUInt32BE();
|
|
||||||
var satoshis = reader.readDoubleBE();
|
|
||||||
var scriptBuffer = reader.read(buffer.length - 12);
|
|
||||||
return {
|
|
||||||
height: height,
|
|
||||||
satoshis: satoshis,
|
|
||||||
script: scriptBuffer
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
Encoding.prototype.encodeWalletUtxoSatoshisKey = function(walletId, satoshis, txid, outputIndex) {
|
|
||||||
var buffers = [this.servicePrefix];
|
|
||||||
|
|
||||||
var walletIdSizeBuffer = new Buffer(1);
|
|
||||||
walletIdSizeBuffer.writeUInt8(walletId.length);
|
|
||||||
var walletIdBuffer = new Buffer(walletId, 'utf8');
|
|
||||||
|
|
||||||
buffers.push(walletIdSizeBuffer);
|
|
||||||
buffers.push(walletIdBuffer);
|
|
||||||
|
|
||||||
if(satoshis !== undefined) {
|
|
||||||
var satoshisBuffer = new Buffer(8);
|
|
||||||
satoshisBuffer.writeUInt32BE(satoshis);
|
|
||||||
buffers.push(satoshisBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(txid) {
|
|
||||||
var txidBuffer = new Buffer(txid, 'hex');
|
|
||||||
buffers.push(txidBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(outputIndex !== undefined) {
|
|
||||||
var outputIndexBuffer = new Buffer(4);
|
|
||||||
outputIndexBuffer.writeUInt32BE(outputIndex);
|
|
||||||
buffers.push(outputIndexBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Buffer.concat(buffers);
|
|
||||||
};
|
|
||||||
|
|
||||||
Encoding.prototype.decodeWalletUtxoSatoshisKey = function(buffer) {
|
|
||||||
var reader = new BufferReader(buffer);
|
|
||||||
reader.read(1);
|
|
||||||
|
|
||||||
var walletIdSizeBuffer = reader.readUInt8();
|
|
||||||
var walletId = reader.read(walletIdSizeBuffer).toString('utf8');
|
|
||||||
var satoshis = reader.readDoubleBE();
|
|
||||||
var txid = reader.read(32).toString('hex');
|
|
||||||
var outputIndex = reader.readUInt32BE();
|
|
||||||
return {
|
|
||||||
walletId: walletId,
|
|
||||||
satoshis: satoshis,
|
|
||||||
txid: txid,
|
|
||||||
outputIndex: outputIndex
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
Encoding.prototype.encodeWalletUtxoSatoshisValue = function(height, scriptBuffer) {
|
|
||||||
var heightBuffer = new Buffer(4);
|
|
||||||
heightBuffer.writeUInt32BE(height);
|
|
||||||
return Buffer.concat([height, scriptBuffer]);
|
|
||||||
};
|
|
||||||
|
|
||||||
Encoding.prototype.decodeWalletUtxoSatoshisValue = function(buffer) {
|
|
||||||
var reader = new BufferReader(buffer);
|
|
||||||
var height = reader.readUInt32BE();
|
|
||||||
var scriptBuffer = reader.read(buffer.length - 4);
|
|
||||||
return {
|
|
||||||
height: height,
|
|
||||||
script: scriptBuffer
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
Encoding.prototype.encodeWalletAddressesKey = function(walletId) {
|
|
||||||
var prefix = new Buffer('00', 'hex');
|
|
||||||
var walletIdBuffer = new Buffer(walletId, 'hex');
|
|
||||||
return Buffer.concat([this.servicePrefix, prefix, walletIdBuffer]);
|
|
||||||
};
|
|
||||||
|
|
||||||
Encoding.prototype.decodeWalletAddressesKey = function(buffer) {
|
|
||||||
return buffer.slice(3).toString('hex');
|
|
||||||
};
|
|
||||||
|
|
||||||
Encoding.prototype.encodeWalletAddressesValue = function(addresses) {
|
|
||||||
var bufferList = [];
|
|
||||||
var addressesLengthBuffer = new Buffer(4);
|
|
||||||
addressesLengthBuffer.writeUInt32BE(addresses.length);
|
|
||||||
bufferList.push(addressesLengthBuffer);
|
|
||||||
for(var i = 0; i < addresses.length; i++) {
|
|
||||||
var addressSizeBuffer = new Buffer(1);
|
|
||||||
addressSizeBuffer.writeUInt8(addresses[i].length);
|
|
||||||
bufferList.push(addressSizeBuffer);
|
|
||||||
bufferList.push(new Buffer(addresses[i], 'utf8'));
|
|
||||||
}
|
|
||||||
|
|
||||||
return Buffer.concat(bufferList);
|
|
||||||
};
|
|
||||||
|
|
||||||
Encoding.prototype.decodeWalletAddressesValue = function(buffer) {
|
|
||||||
var reader = new BufferReader(buffer);
|
|
||||||
var addressesLength = reader.readUInt32BE();
|
|
||||||
var addresses = [];
|
|
||||||
var addressSize = 0;
|
|
||||||
for(var i = 0; i < addressesLength.length; i++) {
|
|
||||||
addressSize = reader.readUInt8(addressSize);
|
|
||||||
addresses.push(reader.read(addressSize).toString('utf8'));
|
|
||||||
}
|
|
||||||
|
|
||||||
return addresses;
|
|
||||||
};
|
|
||||||
|
|
||||||
Encoding.prototype.encodeWalletBalanceKey = function(walletId) {
|
|
||||||
var prefix = new Buffer('01', 'hex');
|
|
||||||
var walletIdBuffer = new Buffer(walletId, 'hex');
|
|
||||||
return Buffer.concat([this.servicePrefix, prefix, walletIdBuffer]);
|
|
||||||
};
|
|
||||||
|
|
||||||
Encoding.prototype.decodeWalletBalanceKey = function(buffer) {
|
|
||||||
return buffer.slice(3).toString('hex');
|
|
||||||
};
|
|
||||||
|
|
||||||
Encoding.prototype.encodeWalletBalanceValue = function(balance) {
|
|
||||||
var balanceBuffer = new Buffer(8);
|
|
||||||
balanceBuffer.writeUInt32BE(balance);
|
|
||||||
return balanceBuffer;
|
|
||||||
};
|
|
||||||
|
|
||||||
Encoding.prototype.decodeWalletBalanceValue = function(buffer) {
|
|
||||||
var reader = new BufferReader(buffer);
|
|
||||||
var balance = reader.readDoubleBE();
|
|
||||||
|
|
||||||
return balance;
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = Encoding;
|
|
||||||
@ -7,12 +7,6 @@ function Encoding(servicePrefix) {
|
|||||||
this.servicePrefix = servicePrefix;
|
this.servicePrefix = servicePrefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
Encoding.prototype.getTerminalKey = function(startKey) {
|
|
||||||
var endKey = Buffer.from(startKey);
|
|
||||||
endKey.writeUInt8(startKey.readUInt8(startKey.length - 1) + 1, startKey.length - 1);
|
|
||||||
return endKey;
|
|
||||||
};
|
|
||||||
|
|
||||||
Encoding.prototype.encodeAddressIndexKey = function(address, height, txid) {
|
Encoding.prototype.encodeAddressIndexKey = function(address, height, txid) {
|
||||||
var prefix = new Buffer('00', 'hex');
|
var prefix = new Buffer('00', 'hex');
|
||||||
var buffers = [this.servicePrefix, prefix];
|
var buffers = [this.servicePrefix, prefix];
|
||||||
@ -24,16 +18,12 @@ Encoding.prototype.encodeAddressIndexKey = function(address, height, txid) {
|
|||||||
buffers.push(addressSizeBuffer);
|
buffers.push(addressSizeBuffer);
|
||||||
buffers.push(addressBuffer);
|
buffers.push(addressBuffer);
|
||||||
|
|
||||||
if(height !== undefined) {
|
var heightBuffer = new Buffer(4);
|
||||||
var heightBuffer = new Buffer(4);
|
heightBuffer.writeUInt32BE(height || 0);
|
||||||
heightBuffer.writeUInt32BE(height);
|
buffers.push(heightBuffer);
|
||||||
buffers.push(heightBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(txid) {
|
var txidBuffer = new Buffer(txid || Array(65).join('0'), 'hex');
|
||||||
var txidBuffer = new Buffer(txid, 'hex');
|
buffers.push(txidBuffer);
|
||||||
buffers.push(txidBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Buffer.concat(buffers);
|
return Buffer.concat(buffers);
|
||||||
};
|
};
|
||||||
@ -64,16 +54,12 @@ Encoding.prototype.encodeUtxoIndexKey = function(address, txid, outputIndex) {
|
|||||||
buffers.push(addressSizeBuffer);
|
buffers.push(addressSizeBuffer);
|
||||||
buffers.push(addressBuffer);
|
buffers.push(addressBuffer);
|
||||||
|
|
||||||
if(txid) {
|
var txidBuffer = new Buffer(txid || new Array(65).join('0'), 'hex');
|
||||||
var txidBuffer = new Buffer(txid, 'hex');
|
buffers.push(txidBuffer);
|
||||||
buffers.push(txidBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(outputIndex !== undefined) {
|
var outputIndexBuffer = new Buffer(4);
|
||||||
var outputIndexBuffer = new Buffer(4);
|
outputIndexBuffer.writeUInt32BE(outputIndex || 0);
|
||||||
outputIndexBuffer.writeUInt32BE(outputIndex);
|
buffers.push(outputIndexBuffer);
|
||||||
buffers.push(outputIndexBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Buffer.concat(buffers);
|
return Buffer.concat(buffers);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,266 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
var bitcore = require('bitcore-lib');
|
|
||||||
var async = require('async');
|
|
||||||
var _ = bitcore.deps._;
|
|
||||||
|
|
||||||
var constants = require('../../constants');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This represents an instance that keeps track of data over a series of
|
|
||||||
* asynchronous I/O calls to get the transaction history for a group of
|
|
||||||
* addresses. History can be queried by start and end block heights to limit large sets
|
|
||||||
* of results (uses leveldb key streaming).
|
|
||||||
*/
|
|
||||||
function AddressHistory(args) {
|
|
||||||
this.node = args.node;
|
|
||||||
this.options = args.options;
|
|
||||||
|
|
||||||
if(Array.isArray(args.addresses)) {
|
|
||||||
this.addresses = args.addresses;
|
|
||||||
} else {
|
|
||||||
this.addresses = [args.addresses];
|
|
||||||
}
|
|
||||||
|
|
||||||
this.maxHistoryQueryLength = args.options.maxHistoryQueryLength || constants.MAX_HISTORY_QUERY_LENGTH;
|
|
||||||
this.maxAddressesQuery = args.options.maxAddressesQuery || constants.MAX_ADDRESSES_QUERY;
|
|
||||||
this.maxAddressesLimit = args.options.maxAddressesLimit || constants.MAX_ADDRESSES_LIMIT;
|
|
||||||
|
|
||||||
this.addressStrings = [];
|
|
||||||
for (var i = 0; i < this.addresses.length; i++) {
|
|
||||||
var address = this.addresses[i];
|
|
||||||
if (address instanceof bitcore.Address) {
|
|
||||||
this.addressStrings.push(address.toString());
|
|
||||||
} else if (_.isString(address)) {
|
|
||||||
this.addressStrings.push(address);
|
|
||||||
} else {
|
|
||||||
throw new TypeError('Addresses are expected to be strings');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.detailedArray = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
AddressHistory.prototype._mergeAndSortTxids = function(summaries) {
|
|
||||||
var appearanceIds = {};
|
|
||||||
var unconfirmedAppearanceIds = {};
|
|
||||||
|
|
||||||
for (var i = 0; i < summaries.length; i++) {
|
|
||||||
var summary = summaries[i];
|
|
||||||
for (var key in summary.appearanceIds) {
|
|
||||||
appearanceIds[key] = summary.appearanceIds[key];
|
|
||||||
delete summary.appearanceIds[key];
|
|
||||||
}
|
|
||||||
for (var unconfirmedKey in summary.unconfirmedAppearanceIds) {
|
|
||||||
unconfirmedAppearanceIds[unconfirmedKey] = summary.unconfirmedAppearanceIds[unconfirmedKey];
|
|
||||||
delete summary.unconfirmedAppearanceIds[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var confirmedTxids = Object.keys(appearanceIds);
|
|
||||||
confirmedTxids.sort(function(a, b) {
|
|
||||||
// Confirmed are sorted by height
|
|
||||||
return appearanceIds[a] - appearanceIds[b];
|
|
||||||
});
|
|
||||||
var unconfirmedTxids = Object.keys(unconfirmedAppearanceIds);
|
|
||||||
unconfirmedTxids.sort(function(a, b) {
|
|
||||||
// Unconfirmed are sorted by timestamp
|
|
||||||
return unconfirmedAppearanceIds[a] - unconfirmedAppearanceIds[b];
|
|
||||||
});
|
|
||||||
return confirmedTxids.concat(unconfirmedTxids);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function will give detailed history for the configured
|
|
||||||
* addresses. See AddressService.prototype.getAddressHistory
|
|
||||||
* for complete documentation about options and response format.
|
|
||||||
*/
|
|
||||||
AddressHistory.prototype.get = function(callback) {
|
|
||||||
var self = this;
|
|
||||||
if (this.addresses.length > this.maxAddressesQuery) {
|
|
||||||
return callback(new TypeError('Maximum number of addresses (' + this.maxAddressesQuery + ') exceeded'));
|
|
||||||
}
|
|
||||||
|
|
||||||
var opts = _.clone(this.options);
|
|
||||||
opts.noBalance = true;
|
|
||||||
|
|
||||||
if (this.addresses.length === 1) {
|
|
||||||
var address = this.addresses[0];
|
|
||||||
self.node.services.address.getAddressSummary(address, opts, function(err, summary) {
|
|
||||||
if (err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
return self._paginateWithDetails.call(self, summary.txids, callback);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
|
|
||||||
opts.fullTxList = true;
|
|
||||||
async.mapLimit(
|
|
||||||
self.addresses,
|
|
||||||
self.maxAddressesLimit,
|
|
||||||
function(address, next) {
|
|
||||||
self.node.services.address.getAddressSummary(address, opts, next);
|
|
||||||
},
|
|
||||||
function(err, summaries) {
|
|
||||||
if (err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
var txids = self._mergeAndSortTxids(summaries);
|
|
||||||
return self._paginateWithDetails.call(self, txids, callback);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
AddressHistory.prototype._paginateWithDetails = function(allTxids, callback) {
|
|
||||||
var self = this;
|
|
||||||
var totalCount = allTxids.length;
|
|
||||||
|
|
||||||
// Slice the page starting with the most recent
|
|
||||||
var txids;
|
|
||||||
if (self.options.from >= 0 && self.options.to >= 0) {
|
|
||||||
var fromOffset = Math.max(0, totalCount - self.options.from);
|
|
||||||
var toOffset = Math.max(0, totalCount - self.options.to);
|
|
||||||
txids = allTxids.slice(toOffset, fromOffset);
|
|
||||||
} else {
|
|
||||||
txids = allTxids;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify that this query isn't too long
|
|
||||||
if (txids.length > self.maxHistoryQueryLength) {
|
|
||||||
return callback(new Error(
|
|
||||||
'Maximum length query (' + self.maxHistoryQueryLength + ') exceeded for address(es): ' +
|
|
||||||
self.addresses.join(',')
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reverse to include most recent at the top
|
|
||||||
txids.reverse();
|
|
||||||
|
|
||||||
async.eachSeries(
|
|
||||||
txids,
|
|
||||||
function(txid, next) {
|
|
||||||
self.getDetailedInfo(txid, next);
|
|
||||||
},
|
|
||||||
function(err) {
|
|
||||||
if (err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
callback(null, {
|
|
||||||
totalCount: totalCount,
|
|
||||||
items: self.detailedArray
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function will transform items from the combinedArray into
|
|
||||||
* the detailedArray with the full transaction, satoshis and confirmation.
|
|
||||||
* @param {Object} txInfo - An item from the `combinedArray`
|
|
||||||
* @param {Function} next
|
|
||||||
*/
|
|
||||||
AddressHistory.prototype.getDetailedInfo = function(txid, next) {
|
|
||||||
var self = this;
|
|
||||||
var queryMempool = _.isUndefined(self.options.queryMempool) ? true : self.options.queryMempool;
|
|
||||||
|
|
||||||
self.node.services.db.getTransactionWithBlockInfo(
|
|
||||||
txid,
|
|
||||||
queryMempool,
|
|
||||||
function(err, transaction) {
|
|
||||||
if (err) {
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
transaction.populateInputs(self.node.services.db, [], function(err) {
|
|
||||||
if (err) {
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
var addressDetails = self.getAddressDetailsForTransaction(transaction);
|
|
||||||
|
|
||||||
self.detailedArray.push({
|
|
||||||
addresses: addressDetails.addresses,
|
|
||||||
satoshis: addressDetails.satoshis,
|
|
||||||
height: transaction.__height,
|
|
||||||
confirmations: self.getConfirmationsDetail(transaction),
|
|
||||||
timestamp: transaction.__timestamp,
|
|
||||||
// TODO bitcore-lib should return null instead of throwing error on coinbase
|
|
||||||
fees: !transaction.isCoinbase() ? transaction.getFee() : null,
|
|
||||||
tx: transaction
|
|
||||||
});
|
|
||||||
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A helper function for `getDetailedInfo` for getting the confirmations.
|
|
||||||
* @param {Transaction} transaction - A transaction with a populated __height value.
|
|
||||||
*/
|
|
||||||
AddressHistory.prototype.getConfirmationsDetail = function(transaction) {
|
|
||||||
var confirmations = 0;
|
|
||||||
if (transaction.__height >= 0) {
|
|
||||||
confirmations = this.node.services.db.tip.__height - transaction.__height + 1;
|
|
||||||
}
|
|
||||||
return confirmations;
|
|
||||||
};
|
|
||||||
|
|
||||||
AddressHistory.prototype.getAddressDetailsForTransaction = function(transaction) {
|
|
||||||
var result = {
|
|
||||||
addresses: {},
|
|
||||||
satoshis: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
for (var inputIndex = 0; inputIndex < transaction.inputs.length; inputIndex++) {
|
|
||||||
var input = transaction.inputs[inputIndex];
|
|
||||||
if (!input.script) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
var inputAddress = input.script.toAddress(this.node.network);
|
|
||||||
if (inputAddress) {
|
|
||||||
var inputAddressString = inputAddress.toString();
|
|
||||||
if (this.addressStrings.indexOf(inputAddressString) >= 0) {
|
|
||||||
if (!result.addresses[inputAddressString]) {
|
|
||||||
result.addresses[inputAddressString] = {
|
|
||||||
inputIndexes: [inputIndex],
|
|
||||||
outputIndexes: []
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
result.addresses[inputAddressString].inputIndexes.push(inputIndex);
|
|
||||||
}
|
|
||||||
result.satoshis -= input.output.satoshis;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var outputIndex = 0; outputIndex < transaction.outputs.length; outputIndex++) {
|
|
||||||
var output = transaction.outputs[outputIndex];
|
|
||||||
if (!output.script) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
var outputAddress = output.script.toAddress(this.node.network);
|
|
||||||
if (outputAddress) {
|
|
||||||
var outputAddressString = outputAddress.toString();
|
|
||||||
if (this.addressStrings.indexOf(outputAddressString) >= 0) {
|
|
||||||
if (!result.addresses[outputAddressString]) {
|
|
||||||
result.addresses[outputAddressString] = {
|
|
||||||
inputIndexes: [],
|
|
||||||
outputIndexes: [outputIndex]
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
result.addresses[outputAddressString].outputIndexes.push(outputIndex);
|
|
||||||
}
|
|
||||||
result.satoshis += output.satoshis;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = AddressHistory;
|
|
||||||
@ -14,9 +14,7 @@ var EventEmitter = require('events').EventEmitter;
|
|||||||
var Address = bitcore.Address;
|
var Address = bitcore.Address;
|
||||||
var constants = require('../../constants');
|
var constants = require('../../constants');
|
||||||
var Encoding = require('./encoding');
|
var Encoding = require('./encoding');
|
||||||
var InputsTransformStream = require('./streams/inputs-transform');
|
var utils = require('../../utils');
|
||||||
var OutputsTransformStream = require('./streams/outputs-transform');
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Address Service builds upon the Database Service and the Bitcoin Service to add additional
|
* The Address Service builds upon the Database Service and the Bitcoin Service to add additional
|
||||||
@ -61,15 +59,13 @@ AddressService.prototype.start = function(callback) {
|
|||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
this.store = this.node.services.db.store;
|
this.store = this.node.services.db.store;
|
||||||
|
|
||||||
this.node.services.db.getPrefix(this.name, function(err, prefix) {
|
this.node.services.db.getPrefix(this.name, function(err, prefix) {
|
||||||
if(err) {
|
if(err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.prefix = prefix;
|
self.prefix = prefix;
|
||||||
|
|
||||||
self.encoding = new Encoding(self.prefix);
|
self._encoding = new Encoding(self.prefix);
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
@ -91,7 +87,7 @@ AddressService.prototype.getAPIMethods = function() {
|
|||||||
return [
|
return [
|
||||||
['getBalance', this, this.getBalance, 2],
|
['getBalance', this, this.getBalance, 2],
|
||||||
['getOutputs', this, this.getOutputs, 2],
|
['getOutputs', this, this.getOutputs, 2],
|
||||||
['getUnspentOutputs', this, this.getUnspentOutputs, 2],
|
['getUtxos', this, this.getUtxos, 2],
|
||||||
['getInputForOutput', this, this.getInputForOutput, 2],
|
['getInputForOutput', this, this.getInputForOutput, 2],
|
||||||
['isSpent', this, this.isSpent, 2],
|
['isSpent', this, this.isSpent, 2],
|
||||||
['getAddressHistory', this, this.getAddressHistory, 2],
|
['getAddressHistory', this, this.getAddressHistory, 2],
|
||||||
@ -169,7 +165,7 @@ AddressService.prototype.concurrentBlockHandler = function(block, connectBlock,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var key = self.encoding.encodeAddressIndexKey(address, height, txid);
|
var key = self._encoding.encodeAddressIndexKey(address, height, txid);
|
||||||
operations.push({
|
operations.push({
|
||||||
type: action,
|
type: action,
|
||||||
key: key
|
key: key
|
||||||
@ -208,7 +204,7 @@ AddressService.prototype.concurrentBlockHandler = function(block, connectBlock,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var inputKey = self.encoding.encodeAddressIndexKey(inputAddress, height, txid);
|
var inputKey = self._encoding.encodeAddressIndexKey(inputAddress, height, txid);
|
||||||
|
|
||||||
operations.push({
|
operations.push({
|
||||||
type: action,
|
type: action,
|
||||||
@ -258,8 +254,8 @@ AddressService.prototype.blockHandler = function(block, connectBlock, callback)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var key = self.encoding.encodeUtxoIndexKey(address, txid, outputIndex);
|
var key = self._encoding.encodeUtxoIndexKey(address, txid, outputIndex);
|
||||||
var value = self.encoding.encodeUtxoIndexValue(block.__height, output.satoshis, output._scriptBuffer);
|
var value = self._encoding.encodeUtxoIndexValue(block.__height, output.satoshis, output._scriptBuffer);
|
||||||
operations.push({
|
operations.push({
|
||||||
type: action,
|
type: action,
|
||||||
key: key,
|
key: key,
|
||||||
@ -285,7 +281,7 @@ AddressService.prototype.blockHandler = function(block, connectBlock, callback)
|
|||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
|
|
||||||
var inputKey = self.encoding.encodeUtxoIndexKey(inputAddress, input.prevTxId, input.outputIndex);
|
var inputKey = self._encoding.encodeUtxoIndexKey(inputAddress, input.prevTxId, input.outputIndex);
|
||||||
//common case is connecting blocks and deleting outputs spent by these inputs
|
//common case is connecting blocks and deleting outputs spent by these inputs
|
||||||
if (connectBlock) {
|
if (connectBlock) {
|
||||||
operations.push({
|
operations.push({
|
||||||
@ -296,7 +292,7 @@ AddressService.prototype.blockHandler = function(block, connectBlock, callback)
|
|||||||
} else { // uncommon and slower, this happens during a reorg
|
} else { // uncommon and slower, this happens during a reorg
|
||||||
self.node.services.transaction.getTransaction(input.prevTxId, {}, function(err, tx) {
|
self.node.services.transaction.getTransaction(input.prevTxId, {}, function(err, tx) {
|
||||||
var utxo = tx.outputs[input.outputIndex];
|
var utxo = tx.outputs[input.outputIndex];
|
||||||
var inputValue = self.encoding.encodeUtxoIndexValue(tx.__height, utxo.satoshis, utxo._scriptBuffer);
|
var inputValue = self._encoding.encodeUtxoIndexValue(tx.__height, utxo.satoshis, utxo._scriptBuffer);
|
||||||
operations.push({
|
operations.push({
|
||||||
type: 'put',
|
type: 'put',
|
||||||
key: inputKey,
|
key: inputKey,
|
||||||
@ -332,9 +328,6 @@ AddressService.prototype.getAddressString = function(script, output) {
|
|||||||
return pubkey.toString('hex');
|
return pubkey.toString('hex');
|
||||||
}
|
}
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
//log.warn('Error getting public key from: ', script.toASM(), script.toHex());
|
|
||||||
// if there is an error, it's because a pubkey can not be extracted from the script
|
|
||||||
// continue on and return null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO add back in P2PK, but for this we need to look up the utxo for this script
|
//TODO add back in P2PK, but for this we need to look up the utxo for this script
|
||||||
@ -342,7 +335,6 @@ AddressService.prototype.getAddressString = function(script, output) {
|
|||||||
return output.script.getPublicKey().toString('hex');
|
return output.script.getPublicKey().toString('hex');
|
||||||
}
|
}
|
||||||
|
|
||||||
//log.warn('No utxo given for script spending a P2PK: ', script.toASM(), script.toHex());
|
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -484,7 +476,7 @@ AddressService.prototype.unsubscribeAll = function(name, emitter) {
|
|||||||
* @param {Function} callback
|
* @param {Function} callback
|
||||||
*/
|
*/
|
||||||
AddressService.prototype.getBalance = function(address, queryMempool, callback) {
|
AddressService.prototype.getBalance = function(address, queryMempool, callback) {
|
||||||
this.getUnspentOutputs(address, queryMempool, function(err, outputs) {
|
this.getUtxos(address, queryMempool, function(err, outputs) {
|
||||||
if(err) {
|
if(err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
@ -523,12 +515,12 @@ AddressService.prototype.getInputForOutput = function(txid, outputIndex, options
|
|||||||
txidBuffer = new Buffer(txid, 'hex');
|
txidBuffer = new Buffer(txid, 'hex');
|
||||||
}
|
}
|
||||||
if (options.queryMempool) {
|
if (options.queryMempool) {
|
||||||
var spentIndexSyncKey = self.encoding.encodeSpentIndexSyncKey(txidBuffer, outputIndex);
|
var spentIndexSyncKey = self._encoding.encodeSpentIndexSyncKey(txidBuffer, outputIndex);
|
||||||
if (this.mempoolSpentIndex[spentIndexSyncKey]) {
|
if (this.mempoolSpentIndex[spentIndexSyncKey]) {
|
||||||
return this._getSpentMempool(txidBuffer, outputIndex, callback);
|
return this._getSpentMempool(txidBuffer, outputIndex, callback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var key = self.encoding.encodeInputKeyMap(txidBuffer, outputIndex);
|
var key = self._encoding.encodeInputKeyMap(txidBuffer, outputIndex);
|
||||||
var dbOptions = {
|
var dbOptions = {
|
||||||
valueEncoding: 'binary',
|
valueEncoding: 'binary',
|
||||||
keyEncoding: 'binary'
|
keyEncoding: 'binary'
|
||||||
@ -539,7 +531,7 @@ AddressService.prototype.getInputForOutput = function(txid, outputIndex, options
|
|||||||
} else if (err) {
|
} else if (err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
var value = self.encoding.decodeInputValueMap(buffer);
|
var value = self._encoding.decodeInputValueMap(buffer);
|
||||||
callback(null, {
|
callback(null, {
|
||||||
inputTxId: value.inputTxId.toString('hex'),
|
inputTxId: value.inputTxId.toString('hex'),
|
||||||
inputIndex: value.inputIndex
|
inputIndex: value.inputIndex
|
||||||
@ -645,7 +637,7 @@ AddressService.prototype.getInputs = function(addressStr, options, callback) {
|
|||||||
|
|
||||||
var inputs = [];
|
var inputs = [];
|
||||||
|
|
||||||
var addrObj = self.encoding.getAddressInfo(addressStr);
|
var addrObj = self._encoding.getAddressInfo(addressStr);
|
||||||
var hashBuffer = addrObj.hashBuffer;
|
var hashBuffer = addrObj.hashBuffer;
|
||||||
var hashTypeBuffer = addrObj.hashTypeBuffer;
|
var hashTypeBuffer = addrObj.hashTypeBuffer;
|
||||||
|
|
||||||
@ -861,7 +853,7 @@ AddressService.prototype.getOutputs = function(addressStr, options, callback) {
|
|||||||
$.checkArgument(_.isObject(options), 'Second argument is expected to be an options object.');
|
$.checkArgument(_.isObject(options), 'Second argument is expected to be an options object.');
|
||||||
$.checkArgument(_.isFunction(callback), 'Third argument is expected to be a callback function.');
|
$.checkArgument(_.isFunction(callback), 'Third argument is expected to be a callback function.');
|
||||||
|
|
||||||
var addrObj = self.encoding.getAddressInfo(addressStr);
|
var addrObj = self._encoding.getAddressInfo(addressStr);
|
||||||
var hashBuffer = addrObj.hashBuffer;
|
var hashBuffer = addrObj.hashBuffer;
|
||||||
var hashTypeBuffer = addrObj.hashTypeBuffer;
|
var hashTypeBuffer = addrObj.hashTypeBuffer;
|
||||||
if (!hashTypeBuffer) {
|
if (!hashTypeBuffer) {
|
||||||
@ -935,7 +927,7 @@ AddressService.prototype._getOutputsMempool = function(addressStr, hashBuffer, h
|
|||||||
// prefix: 1, hashBuffer: 20, hashTypeBuffer: 1, txid: 32, outputIndex: 4
|
// prefix: 1, hashBuffer: 20, hashTypeBuffer: 1, txid: 32, outputIndex: 4
|
||||||
var txid = data.key.slice(22, 54);
|
var txid = data.key.slice(22, 54);
|
||||||
var outputIndex = data.key.readUInt32BE(54);
|
var outputIndex = data.key.readUInt32BE(54);
|
||||||
var value = self.encoding.decodeOutputMempoolValue(data.value);
|
var value = self._encoding.decodeOutputMempoolValue(data.value);
|
||||||
var output = {
|
var output = {
|
||||||
address: addressStr,
|
address: addressStr,
|
||||||
hashType: constants.HASH_TYPES_READABLE[hashTypeBuffer.toString('hex')],
|
hashType: constants.HASH_TYPES_READABLE[hashTypeBuffer.toString('hex')],
|
||||||
@ -973,7 +965,7 @@ AddressService.prototype._getOutputsMempool = function(addressStr, hashBuffer, h
|
|||||||
* @param {Boolean} queryMempool - Include or exclude the mempool
|
* @param {Boolean} queryMempool - Include or exclude the mempool
|
||||||
* @param {Function} callback
|
* @param {Function} callback
|
||||||
*/
|
*/
|
||||||
AddressService.prototype.getUnspentOutputs = function(addresses, queryMempool, callback) {
|
AddressService.prototype.getUtxos = function(addresses, queryMempool, callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
if(!Array.isArray(addresses)) {
|
if(!Array.isArray(addresses)) {
|
||||||
@ -983,7 +975,7 @@ AddressService.prototype.getUnspentOutputs = function(addresses, queryMempool, c
|
|||||||
var utxos = [];
|
var utxos = [];
|
||||||
|
|
||||||
async.eachSeries(addresses, function(address, next) {
|
async.eachSeries(addresses, function(address, next) {
|
||||||
self.getUnspentOutputsForAddress(address, queryMempool, function(err, unspents) {
|
self.getUtxosForAddress(address, queryMempool, function(err, unspents) {
|
||||||
if(err && err instanceof errors.NoOutputs) {
|
if(err && err instanceof errors.NoOutputs) {
|
||||||
return next();
|
return next();
|
||||||
} else if(err) {
|
} else if(err) {
|
||||||
@ -1004,29 +996,26 @@ AddressService.prototype.getUnspentOutputs = function(addresses, queryMempool, c
|
|||||||
* @param {Boolean} queryMempool - Include or exclude the mempool
|
* @param {Boolean} queryMempool - Include or exclude the mempool
|
||||||
* @param {Function} callback
|
* @param {Function} callback
|
||||||
*/
|
*/
|
||||||
AddressService.prototype.getUnspentOutputsForAddress = function(address, queryMempool, callback) {
|
AddressService.prototype.getUtxosForAddress = function(address, queryMempool, callback) {
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
var addressLengthBuffer = new Buffer(1);
|
|
||||||
addressLengthBuffer.writeUInt8(address.length);
|
|
||||||
var start = Buffer.concat([ self.prefix, addressLengthBuffer, new Buffer(address, 'utf8'), new Buffer('00', 'hex') ]);
|
|
||||||
var end = Buffer.concat([ self.prefix, addressLengthBuffer, new Buffer(address, 'utf8'), new Buffer('01', 'hex') ]);
|
|
||||||
var stream = self.store.createReadStream({
|
var stream = self.store.createReadStream({
|
||||||
gte: start,
|
gte: self._encoding.encodeUtxoIndexKey(address),
|
||||||
lt: end
|
lt: self._encoding.encodeUtxoIndexKey(utils.getTerminalKey(new Buffer(address)))
|
||||||
});
|
});
|
||||||
|
|
||||||
var utxos = [];
|
var utxos = [];
|
||||||
stream.on('data', function(data) {
|
stream.on('data', function(data) {
|
||||||
var key = self.encoding.decodeAddressIndexKey(data.key);
|
var key = self._encoding.decodeUtxoIndexKey(data.key);
|
||||||
var value = self.encoding.decodeAddressIndexValue(data.value);
|
var value = self._encoding.decodeUtxoIndexValue(data.value);
|
||||||
utxos.push({
|
utxos.push({
|
||||||
address: key.address,
|
address: key.address,
|
||||||
txid: key.txid,
|
txid: key.txid,
|
||||||
outputIndex: key.index,
|
outputIndex: key.outputIndex,
|
||||||
satoshis: value.satoshis,
|
satoshis: value.satoshis,
|
||||||
height: key.height
|
height: value.height,
|
||||||
|
script: value.script
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1069,7 +1058,7 @@ AddressService.prototype.isSpent = function(output, options, callback) {
|
|||||||
var spent = self.node.services.bitcoind.isSpent(txid, output.outputIndex);
|
var spent = self.node.services.bitcoind.isSpent(txid, output.outputIndex);
|
||||||
if (!spent && queryMempool) {
|
if (!spent && queryMempool) {
|
||||||
var txidBuffer = new Buffer(txid, 'hex');
|
var txidBuffer = new Buffer(txid, 'hex');
|
||||||
var spentIndexSyncKey = self.encoding.encodeSpentIndexSyncKey(txidBuffer, output.outputIndex);
|
var spentIndexSyncKey = self._encoding.encodeSpentIndexSyncKey(txidBuffer, output.outputIndex);
|
||||||
spent = self.mempoolSpentIndex[spentIndexSyncKey] ? true : false;
|
spent = self.mempoolSpentIndex[spentIndexSyncKey] ? true : false;
|
||||||
}
|
}
|
||||||
setImmediate(function() {
|
setImmediate(function() {
|
||||||
@ -1149,8 +1138,8 @@ AddressService.prototype.getAddressTxids = function(address, options, callback)
|
|||||||
|
|
||||||
var txids = {};
|
var txids = {};
|
||||||
|
|
||||||
var start = self.encoding.encodeAddressIndexKey(address, options.start);
|
var start = self._encoding.encodeAddressIndexKey(address, options.start);
|
||||||
var end = self.encoding.encodeAddressIndexKey(address, options.end);
|
var end = self._encoding.encodeAddressIndexKey(address, options.end);
|
||||||
|
|
||||||
var stream = self.store.createKeyStream({
|
var stream = self.store.createKeyStream({
|
||||||
gte: start,
|
gte: start,
|
||||||
@ -1160,7 +1149,7 @@ AddressService.prototype.getAddressTxids = function(address, options, callback)
|
|||||||
var streamErr = null;
|
var streamErr = null;
|
||||||
|
|
||||||
stream.on('data', function(buffer) {
|
stream.on('data', function(buffer) {
|
||||||
var key = self.encoding.decodeAddressIndexKey(buffer);
|
var key = self._encoding.decodeAddressIndexKey(buffer);
|
||||||
txids[key.txid] = true;
|
txids[key.txid] = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1178,8 +1167,8 @@ AddressService.prototype.getAddressTxidsWithHeights = function(address, options,
|
|||||||
|
|
||||||
var txids = {};
|
var txids = {};
|
||||||
|
|
||||||
var start = self.encoding.encodeAddressIndexKey(address, options.start);
|
var start = self._encoding.encodeAddressIndexKey(address, options.start);
|
||||||
var end = self.encoding.encodeAddressIndexKey(address, options.end);
|
var end = self._encoding.encodeAddressIndexKey(address, options.end);
|
||||||
|
|
||||||
var stream = self.store.createKeyStream({
|
var stream = self.store.createKeyStream({
|
||||||
gte: start,
|
gte: start,
|
||||||
@ -1189,7 +1178,7 @@ AddressService.prototype.getAddressTxidsWithHeights = function(address, options,
|
|||||||
var streamErr = null;
|
var streamErr = null;
|
||||||
|
|
||||||
stream.on('data', function(buffer) {
|
stream.on('data', function(buffer) {
|
||||||
var key = self.encoding.decodeAddressIndexKey(buffer);
|
var key = self._encoding.decodeAddressIndexKey(buffer);
|
||||||
txids[key.txid] = key.height;
|
txids[key.txid] = key.height;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1341,7 +1330,7 @@ AddressService.prototype._getAddressConfirmedOutputsSummary = function(address,
|
|||||||
if(options.queryMempool) {
|
if(options.queryMempool) {
|
||||||
// Check to see if this output is spent in the mempool and if so
|
// Check to see if this output is spent in the mempool and if so
|
||||||
// we will subtract it from the unconfirmedBalance (a.k.a unconfirmedDelta)
|
// we will subtract it from the unconfirmedBalance (a.k.a unconfirmedDelta)
|
||||||
var spentIndexSyncKey = self.encoding.encodeSpentIndexSyncKey(
|
var spentIndexSyncKey = self._encoding.encodeSpentIndexSyncKey(
|
||||||
new Buffer(txid, 'hex'), // TODO: get buffer directly
|
new Buffer(txid, 'hex'), // TODO: get buffer directly
|
||||||
outputIndex
|
outputIndex
|
||||||
);
|
);
|
||||||
@ -1399,7 +1388,7 @@ AddressService.prototype._getAddressMempoolSummary = function(address, options,
|
|||||||
var addressStr = address.toString();
|
var addressStr = address.toString();
|
||||||
var hashBuffer = address.hashBuffer;
|
var hashBuffer = address.hashBuffer;
|
||||||
var hashTypeBuffer = constants.HASH_TYPES_MAP[address.type];
|
var hashTypeBuffer = constants.HASH_TYPES_MAP[address.type];
|
||||||
var addressIndexKey = self.encoding.encodeMempoolAddressIndexKey(hashBuffer, hashTypeBuffer);
|
var addressIndexKey = self._encoding.encodeMempoolAddressIndexKey(hashBuffer, hashTypeBuffer);
|
||||||
|
|
||||||
if(!this.mempoolAddressIndex[addressIndexKey]) {
|
if(!this.mempoolAddressIndex[addressIndexKey]) {
|
||||||
return callback(null, result);
|
return callback(null, result);
|
||||||
@ -1429,7 +1418,7 @@ AddressService.prototype._getAddressMempoolSummary = function(address, options,
|
|||||||
result.unconfirmedAppearanceIds[output.txid] = output.timestamp;
|
result.unconfirmedAppearanceIds[output.txid] = output.timestamp;
|
||||||
|
|
||||||
if(!options.noBalance) {
|
if(!options.noBalance) {
|
||||||
var spentIndexSyncKey = self.encoding.encodeSpentIndexSyncKey(
|
var spentIndexSyncKey = self._encoding.encodeSpentIndexSyncKey(
|
||||||
new Buffer(output.txid, 'hex'), // TODO: get buffer directly
|
new Buffer(output.txid, 'hex'), // TODO: get buffer directly
|
||||||
output.outputIndex
|
output.outputIndex
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,40 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
var Transform = require('stream').Transform;
|
|
||||||
var inherits = require('util').inherits;
|
|
||||||
var bitcore = require('bitcore-lib');
|
|
||||||
var encodingUtil = require('../../../encoding');
|
|
||||||
var $ = bitcore.util.preconditions;
|
|
||||||
|
|
||||||
function InputsTransformStream(options) {
|
|
||||||
$.checkArgument(options.address instanceof bitcore.Address);
|
|
||||||
Transform.call(this, {
|
|
||||||
objectMode: true
|
|
||||||
});
|
|
||||||
this._address = options.address;
|
|
||||||
this._addressStr = this._address.toString();
|
|
||||||
this._tipHeight = options.tipHeight;
|
|
||||||
}
|
|
||||||
inherits(InputsTransformStream, Transform);
|
|
||||||
|
|
||||||
InputsTransformStream.prototype._transform = function(chunk, encoding, callback) {
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
var key = encodingUtil.decodeInputKey(chunk.key);
|
|
||||||
var value = encodingUtil.decodeInputValue(chunk.value);
|
|
||||||
|
|
||||||
var input = {
|
|
||||||
address: this._addressStr,
|
|
||||||
hashType: this._address.type,
|
|
||||||
txid: value.txid.toString('hex'),
|
|
||||||
inputIndex: value.inputIndex,
|
|
||||||
height: key.height,
|
|
||||||
confirmations: this._tipHeight - key.height + 1
|
|
||||||
};
|
|
||||||
|
|
||||||
self.push(input);
|
|
||||||
callback();
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = InputsTransformStream;
|
|
||||||
@ -1,42 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
var Transform = require('stream').Transform;
|
|
||||||
var inherits = require('util').inherits;
|
|
||||||
var bitcore = require('bitcore-lib');
|
|
||||||
var encodingUtil = require('../../../encoding');
|
|
||||||
var $ = bitcore.util.preconditions;
|
|
||||||
|
|
||||||
function OutputsTransformStream(options) {
|
|
||||||
Transform.call(this, {
|
|
||||||
objectMode: true
|
|
||||||
});
|
|
||||||
$.checkArgument(options.address instanceof bitcore.Address);
|
|
||||||
this._address = options.address;
|
|
||||||
this._addressStr = this._address.toString();
|
|
||||||
this._tipHeight = options.tipHeight;
|
|
||||||
}
|
|
||||||
inherits(OutputsTransformStream, Transform);
|
|
||||||
|
|
||||||
OutputsTransformStream.prototype._transform = function(chunk, encoding, callback) {
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
var key = encodingUtil.decodeOutputKey(chunk.key);
|
|
||||||
var value = encodingUtil.decodeOutputValue(chunk.value);
|
|
||||||
|
|
||||||
var output = {
|
|
||||||
address: this._addressStr,
|
|
||||||
hashType: this._address.type,
|
|
||||||
txid: key.txid.toString('hex'), //TODO use a buffer
|
|
||||||
outputIndex: key.outputIndex,
|
|
||||||
height: key.height,
|
|
||||||
satoshis: value.satoshis,
|
|
||||||
script: value.scriptBuffer.toString('hex'), //TODO use a buffer
|
|
||||||
confirmations: this._tipHeight - key.height + 1
|
|
||||||
};
|
|
||||||
|
|
||||||
self.push(output);
|
|
||||||
callback();
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = OutputsTransformStream;
|
|
||||||
File diff suppressed because it is too large
Load Diff
@ -12,18 +12,12 @@ var Networks = bitcore.Networks;
|
|||||||
var Block = bitcore.Block;
|
var Block = bitcore.Block;
|
||||||
var $ = bitcore.util.preconditions;
|
var $ = bitcore.util.preconditions;
|
||||||
var index = require('../../');
|
var index = require('../../');
|
||||||
var errors = index.errors;
|
|
||||||
var log = index.log;
|
var log = index.log;
|
||||||
var Transaction = require('../../transaction');
|
|
||||||
var Service = require('../../service');
|
var Service = require('../../service');
|
||||||
var Sync = require('./sync');
|
var Sync = require('./sync');
|
||||||
var Reorg = require('./reorg');
|
var Reorg = require('./reorg');
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* This service synchronizes a leveldb database with bitcoin block chain by connecting and
|
|
||||||
* disconnecting blocks to build new indexes that can be queried. Other services can extend
|
|
||||||
* the data that is indexed by implementing a `blockHandler` method.
|
|
||||||
*
|
|
||||||
* @param {Object} options
|
* @param {Object} options
|
||||||
* @param {Node} options.node - A reference to the node
|
* @param {Node} options.node - A reference to the node
|
||||||
* @param {Node} options.store - A levelup backend store
|
* @param {Node} options.store - A levelup backend store
|
||||||
@ -40,22 +34,21 @@ function DB(options) {
|
|||||||
|
|
||||||
Service.call(this, options);
|
Service.call(this, options);
|
||||||
|
|
||||||
// Used to keep track of the version of the indexes
|
|
||||||
// to determine during an upgrade if a reindex is required
|
|
||||||
this.version = 2;
|
this.version = 2;
|
||||||
|
|
||||||
this.dbPrefix = '\u0000\u0000';
|
this.dbPrefix = '\u0000\u0000';
|
||||||
this.tip = null;
|
this.tip = null;
|
||||||
this.genesis = null;
|
this.genesis = null;
|
||||||
|
this.dbOptions = {
|
||||||
|
keyEncoding: 'string',
|
||||||
|
valueEncoding: 'binary'
|
||||||
|
};
|
||||||
|
|
||||||
$.checkState(this.node.network, 'Node is expected to have a "network" property');
|
$.checkState(this.node.network, 'Node is expected to have a "network" property');
|
||||||
this.network = this.node.network;
|
this.network = this.node.network;
|
||||||
|
|
||||||
this._setDataPath();
|
this._setDataPath();
|
||||||
|
|
||||||
this.maxOpenFiles = options.maxOpenFiles || DB.DEFAULT_MAX_OPEN_FILES;
|
|
||||||
this.maxTransactionLimit = options.maxTransactionLimit || DB.MAX_TRANSACTION_LIMIT;
|
|
||||||
|
|
||||||
this.levelupStore = leveldown;
|
this.levelupStore = leveldown;
|
||||||
if (options.store) {
|
if (options.store) {
|
||||||
this.levelupStore = options.store;
|
this.levelupStore = options.store;
|
||||||
@ -75,22 +68,6 @@ util.inherits(DB, Service);
|
|||||||
|
|
||||||
DB.dependencies = ['bitcoind'];
|
DB.dependencies = ['bitcoind'];
|
||||||
|
|
||||||
// keys
|
|
||||||
// 0version
|
|
||||||
// 0prefix-service
|
|
||||||
// 0tip
|
|
||||||
|
|
||||||
// The maximum number of transactions to query at once
|
|
||||||
// Used for populating previous inputs
|
|
||||||
DB.MAX_TRANSACTION_LIMIT = 5;
|
|
||||||
|
|
||||||
// The default maxiumum number of files open for leveldb
|
|
||||||
DB.DEFAULT_MAX_OPEN_FILES = 200;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function will set `this.dataPath` based on `this.node.network`.
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
DB.prototype._setDataPath = function() {
|
DB.prototype._setDataPath = function() {
|
||||||
$.checkState(this.node.datadir, 'Node is expected to have a "datadir" property');
|
$.checkState(this.node.datadir, 'Node is expected to have a "datadir" property');
|
||||||
if (this.node.network === Networks.livenet) {
|
if (this.node.network === Networks.livenet) {
|
||||||
@ -108,22 +85,16 @@ DB.prototype._setDataPath = function() {
|
|||||||
|
|
||||||
DB.prototype._checkVersion = function(callback) {
|
DB.prototype._checkVersion = function(callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var options = {
|
|
||||||
keyEncoding: 'string',
|
self.store.get(self.dbPrefix + 'tip', self.dbOptions, function(err) {
|
||||||
valueEncoding: 'binary'
|
|
||||||
};
|
|
||||||
self.store.get(self.dbPrefix + 'tip', options, function(err) {
|
|
||||||
if (err instanceof levelup.errors.NotFoundError) {
|
if (err instanceof levelup.errors.NotFoundError) {
|
||||||
// The database is brand new and doesn't have a tip stored
|
|
||||||
// we can skip version checking
|
|
||||||
return callback();
|
return callback();
|
||||||
} else if (err) {
|
} else if (err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
self.store.get(self.dbPrefix + 'version', options, function(err, buffer) {
|
self.store.get(self.dbPrefix + 'version', self.dbOptions, function(err, buffer) {
|
||||||
var version;
|
var version;
|
||||||
if (err instanceof levelup.errors.NotFoundError) {
|
if (err instanceof levelup.errors.NotFoundError) {
|
||||||
// The initial version (1) of the database didn't store the version number
|
|
||||||
version = 1;
|
version = 1;
|
||||||
} else if (err) {
|
} else if (err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
@ -150,20 +121,14 @@ DB.prototype._setVersion = function(callback) {
|
|||||||
this.store.put(this.dbPrefix + 'version', versionBuffer, callback);
|
this.store.put(this.dbPrefix + 'version', versionBuffer, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Called by Node to start the service.
|
|
||||||
* @param {Function} callback
|
|
||||||
*/
|
|
||||||
DB.prototype.start = function(callback) {
|
DB.prototype.start = function(callback) {
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
if (!fs.existsSync(this.dataPath)) {
|
if (!fs.existsSync(this.dataPath)) {
|
||||||
mkdirp.sync(this.dataPath);
|
mkdirp.sync(this.dataPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.genesis = Block.fromBuffer(this.node.services.bitcoind.genesisBuffer);
|
this.genesis = Block.fromBuffer(this.node.services.bitcoind.genesisBuffer);
|
||||||
this.store = levelup(this.dataPath, { db: this.levelupStore, maxOpenFiles: this.maxOpenFiles, keyEncoding: 'binary', valueEncoding: 'binary'});
|
this.store = levelup(this.dataPath, { db: this.levelupStore, keyEncoding: 'binary', valueEncoding: 'binary'});
|
||||||
this.node.services.bitcoind.on('tx', this.transactionHandler.bind(this));
|
|
||||||
|
|
||||||
this._sync.on('error', function(err) {
|
this._sync.on('error', function(err) {
|
||||||
log.error(err);
|
log.error(err);
|
||||||
@ -234,14 +199,8 @@ DB.prototype.start = function(callback) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Called by Node to stop the service
|
|
||||||
* @param {Function} callback
|
|
||||||
*/
|
|
||||||
DB.prototype.stop = function(callback) {
|
DB.prototype.stop = function(callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
// Wait until syncing stops and all db operations are completed before closing leveldb
|
|
||||||
async.whilst(function() {
|
async.whilst(function() {
|
||||||
return self.bitcoindSyncing;
|
return self.bitcoindSyncing;
|
||||||
}, function(next) {
|
}, function(next) {
|
||||||
@ -251,66 +210,18 @@ DB.prototype.stop = function(callback) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Will give information about the database from bitcoin.
|
|
||||||
* @param {Function} callback
|
|
||||||
*/
|
|
||||||
DB.prototype.getInfo = function(callback) {
|
|
||||||
var self = this;
|
|
||||||
setImmediate(function() {
|
|
||||||
var info = self.node.bitcoind.getInfo();
|
|
||||||
callback(null, info);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes the underlying store database
|
|
||||||
* @param {Function} callback
|
|
||||||
*/
|
|
||||||
DB.prototype.close = function(callback) {
|
DB.prototype.close = function(callback) {
|
||||||
this.store.close(callback);
|
this.store.close(callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* This function is responsible for emitting `db/transaction` events.
|
|
||||||
* @param {Object} txInfo - The data from the bitcoind.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
|
|
||||||
*/
|
|
||||||
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
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called by Node to determine the available API methods.
|
|
||||||
*/
|
|
||||||
DB.prototype.getAPIMethods = function() {
|
DB.prototype.getAPIMethods = function() {
|
||||||
var methods = [
|
return [];
|
||||||
['getBlock', this, this.getBlock, 1],
|
|
||||||
['getBlockHashesByTimestamp', this, this.getBlockHashesByTimestamp, 2],
|
|
||||||
['getTransaction', this, this.getTransaction, 2],
|
|
||||||
['getTransactionWithBlockInfo', this, this.getTransactionWithBlockInfo, 2],
|
|
||||||
['sendTransaction', this, this.sendTransaction, 1],
|
|
||||||
['estimateFee', this, this.estimateFee, 1]
|
|
||||||
];
|
|
||||||
return methods;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
DB.prototype.loadTip = function(callback) {
|
DB.prototype.loadTip = function(callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
var options = {
|
self.store.get(self.dbPrefix + 'tip', self.dbOptions, function(err, tipData) {
|
||||||
keyEncoding: 'string',
|
|
||||||
valueEncoding: 'binary'
|
|
||||||
};
|
|
||||||
|
|
||||||
self.store.get(self.dbPrefix + 'tip', options, function(err, tipData) {
|
|
||||||
if(err && err instanceof levelup.errors.NotFoundError) {
|
if(err && err instanceof levelup.errors.NotFoundError) {
|
||||||
self.tip = self.genesis;
|
self.tip = self.genesis;
|
||||||
self.tip.__height = 0;
|
self.tip.__height = 0;
|
||||||
@ -332,7 +243,7 @@ DB.prototype.loadTip = function(callback) {
|
|||||||
|
|
||||||
var times = 0;
|
var times = 0;
|
||||||
async.retry({times: 3, interval: self.retryInterval}, function(done) {
|
async.retry({times: 3, interval: self.retryInterval}, function(done) {
|
||||||
self.getBlock(hash, function(err, tip) {
|
self.node.services.bitcoind.getBlock(hash, function(err, tip) {
|
||||||
if(err) {
|
if(err) {
|
||||||
times++;
|
times++;
|
||||||
log.warn('Bitcoind does not have our tip (' + hash + '). Bitcoind may have crashed and needs to catch up.');
|
log.warn('Bitcoind does not have our tip (' + hash + '). Bitcoind may have crashed and needs to catch up.');
|
||||||
@ -362,12 +273,7 @@ DB.prototype.loadTip = function(callback) {
|
|||||||
DB.prototype.loadConcurrentTip = function(callback) {
|
DB.prototype.loadConcurrentTip = function(callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
var options = {
|
self.store.get(self.dbPrefix + 'concurrentTip', self.dbOptions, function(err, tipData) {
|
||||||
keyEncoding: 'string',
|
|
||||||
valueEncoding: 'binary'
|
|
||||||
};
|
|
||||||
|
|
||||||
self.store.get(self.dbPrefix + 'concurrentTip', options, function(err, tipData) {
|
|
||||||
if(err && err instanceof levelup.errors.NotFoundError) {
|
if(err && err instanceof levelup.errors.NotFoundError) {
|
||||||
self.concurrentTip = self.genesis;
|
self.concurrentTip = self.genesis;
|
||||||
self.concurrentTip.__height = 0;
|
self.concurrentTip.__height = 0;
|
||||||
@ -381,10 +287,11 @@ DB.prototype.loadConcurrentTip = function(callback) {
|
|||||||
|
|
||||||
var times = 0;
|
var times = 0;
|
||||||
async.retry({times: 3, interval: self.retryInterval}, function(done) {
|
async.retry({times: 3, interval: self.retryInterval}, function(done) {
|
||||||
self.getBlock(hash, function(err, concurrentTip) {
|
self.node.services.bitcoind.getBlock(hash, function(err, concurrentTip) {
|
||||||
if(err) {
|
if(err) {
|
||||||
times++;
|
times++;
|
||||||
log.warn('Bitcoind does not have our concurrentTip (' + hash + '). Bitcoind may have crashed and needs to catch up.');
|
log.warn('Bitcoind does not have our concurrentTip (' + hash + ').' +
|
||||||
|
' Bitcoind may have crashed and needs to catch up.');
|
||||||
if(times < 3) {
|
if(times < 3) {
|
||||||
log.warn('Retrying in ' + (self.retryInterval / 1000) + ' seconds.');
|
log.warn('Retrying in ' + (self.retryInterval / 1000) + ' seconds.');
|
||||||
}
|
}
|
||||||
@ -408,91 +315,6 @@ DB.prototype.loadConcurrentTip = function(callback) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Will get a block from bitcoind and give a Bitcore Block
|
|
||||||
* @param {String|Number} hash - A block hash or block height
|
|
||||||
*/
|
|
||||||
DB.prototype.getBlock = function(hash, callback) {
|
|
||||||
this.node.services.bitcoind.getBlock(hash, callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Will give a Bitcore Transaction from bitcoind by txid
|
|
||||||
* @param {String} txid - A transaction hash
|
|
||||||
* @param {Boolean} queryMempool - Include the mempool
|
|
||||||
* @param {Function} callback
|
|
||||||
*/
|
|
||||||
DB.prototype.getTransaction = function(txid, queryMempool, callback) {
|
|
||||||
this.node.services.bitcoind.getTransaction(txid, queryMempool, function(err, txBuffer) {
|
|
||||||
if (err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
if (!txBuffer) {
|
|
||||||
return callback(new errors.Transaction.NotFound());
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(null, Transaction().fromBuffer(txBuffer));
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Will give a Bitcore Transaction and populated information about the block included.
|
|
||||||
* @param {String} txid - A transaction hash
|
|
||||||
* @param {Boolean} queryMempool - Include the mempool
|
|
||||||
* @param {Function} callback
|
|
||||||
*/
|
|
||||||
DB.prototype.getTransactionWithBlockInfo = function(txid, queryMempool, callback) {
|
|
||||||
this.node.services.bitcoind.getTransactionWithBlockInfo(txid, queryMempool, function(err, obj) {
|
|
||||||
if (err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
var tx = Transaction().fromBuffer(obj.buffer);
|
|
||||||
tx.__blockHash = obj.blockHash;
|
|
||||||
tx.__height = obj.height;
|
|
||||||
tx.__timestamp = obj.timestamp;
|
|
||||||
|
|
||||||
callback(null, tx);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Will send a transaction to the Bitcoin network.
|
|
||||||
* @param {Transaction} tx - An instance of a Bitcore Transaction
|
|
||||||
* @param {Function} callback
|
|
||||||
*/
|
|
||||||
DB.prototype.sendTransaction = function(tx, callback) {
|
|
||||||
var txString;
|
|
||||||
if (tx instanceof Transaction) {
|
|
||||||
txString = tx.serialize();
|
|
||||||
} else {
|
|
||||||
txString = tx;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
var txid = this.node.services.bitcoind.sendTransaction(txString);
|
|
||||||
return callback(null, txid);
|
|
||||||
} catch(err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Will estimate fees for a transaction and give a result in
|
|
||||||
* satoshis per kilobyte. Similar to the bitcoind estimateFee method.
|
|
||||||
* @param {Number} blocks - The number of blocks for the transaction to be included.
|
|
||||||
* @param {Function} callback
|
|
||||||
*/
|
|
||||||
DB.prototype.estimateFee = function(blocks, callback) {
|
|
||||||
var self = this;
|
|
||||||
setImmediate(function() {
|
|
||||||
callback(null, self.node.services.bitcoind.estimateFee(blocks));
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called by the Bus to determine the available events.
|
|
||||||
*/
|
|
||||||
DB.prototype.getPublishEvents = function() {
|
DB.prototype.getPublishEvents = function() {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
@ -521,27 +343,6 @@ DB.prototype.unsubscribe = function(name, emitter) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Will give the previous hash for a block.
|
|
||||||
* @param {String} blockHash
|
|
||||||
* @param {Function} callback
|
|
||||||
*/
|
|
||||||
DB.prototype.getPrevHash = function(blockHash, callback) {
|
|
||||||
var blockIndex = this.node.services.bitcoind.getBlockIndex(blockHash);
|
|
||||||
setImmediate(function() {
|
|
||||||
if (blockIndex) {
|
|
||||||
callback(null, blockIndex.prevHash);
|
|
||||||
} else {
|
|
||||||
callback(new Error('Could not get prevHash, block not found'));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Connects a block to the database and add indexes
|
|
||||||
* @param {Block} block - The bitcore block
|
|
||||||
* @param {Function} callback
|
|
||||||
*/
|
|
||||||
DB.prototype.connectBlock = function(block, callback) {
|
DB.prototype.connectBlock = function(block, callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
@ -569,11 +370,6 @@ DB.prototype.connectBlock = function(block, callback) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Disconnects a block from the database and removes indexes
|
|
||||||
* @param {Block} block - The bitcore block
|
|
||||||
* @param {Function} callback
|
|
||||||
*/
|
|
||||||
DB.prototype.disconnectBlock = function(block, callback) {
|
DB.prototype.disconnectBlock = function(block, callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
@ -602,7 +398,6 @@ DB.prototype.disconnectBlock = function(block, callback) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
DB.prototype.getConcurrentBlockOperations = function(block, add, callback) {
|
DB.prototype.getConcurrentBlockOperations = function(block, add, callback) {
|
||||||
var self = this;
|
|
||||||
var operations = [];
|
var operations = [];
|
||||||
|
|
||||||
async.each(
|
async.each(
|
||||||
@ -637,7 +432,6 @@ DB.prototype.getConcurrentBlockOperations = function(block, add, callback) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
DB.prototype.getSerialBlockOperations = function(block, add, callback) {
|
DB.prototype.getSerialBlockOperations = function(block, add, callback) {
|
||||||
var self = this;
|
|
||||||
var operations = [];
|
var operations = [];
|
||||||
|
|
||||||
async.eachSeries(
|
async.eachSeries(
|
||||||
@ -693,7 +487,6 @@ DB.prototype.getTipOperation = function(block, add) {
|
|||||||
DB.prototype.getConcurrentTipOperation = function(block, add) {
|
DB.prototype.getConcurrentTipOperation = function(block, add) {
|
||||||
var heightBuffer = new Buffer(4);
|
var heightBuffer = new Buffer(4);
|
||||||
var tipData;
|
var tipData;
|
||||||
|
|
||||||
if(add) {
|
if(add) {
|
||||||
heightBuffer.writeUInt32BE(block.__height);
|
heightBuffer.writeUInt32BE(block.__height);
|
||||||
tipData = Buffer.concat([new Buffer(block.hash, 'hex'), heightBuffer]);
|
tipData = Buffer.concat([new Buffer(block.hash, 'hex'), heightBuffer]);
|
||||||
@ -709,8 +502,6 @@ DB.prototype.getConcurrentTipOperation = function(block, add) {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
DB.prototype.getPrefix = function(service, callback) {
|
DB.prototype.getPrefix = function(service, callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
@ -722,8 +513,6 @@ DB.prototype.getPrefix = function(service, callback) {
|
|||||||
}
|
}
|
||||||
return next(err);
|
return next(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
// we already have the prefix, call the callback
|
|
||||||
return callback(null, buffer);
|
return callback(null, buffer);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -63,7 +63,7 @@ function Sync(node, db) {
|
|||||||
this.node = node;
|
this.node = node;
|
||||||
this.db = db;
|
this.db = db;
|
||||||
this.syncing = false;
|
this.syncing = false;
|
||||||
this.highWaterMark = 10;
|
this.highWaterMark = 100;
|
||||||
this.progressBar = null;
|
this.progressBar = null;
|
||||||
this.lastReportedBlock = 0;
|
this.lastReportedBlock = 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -78,7 +78,7 @@ Encoding.prototype.encodeWalletUtxoKey = function(walletId, txid, outputIndex) {
|
|||||||
buffers.push(walletIdSizeBuffer);
|
buffers.push(walletIdSizeBuffer);
|
||||||
buffers.push(walletIdBuffer);
|
buffers.push(walletIdBuffer);
|
||||||
|
|
||||||
var txidBuffer = new Buffer(txid || new Array(33).join('0'), 'hex');
|
var txidBuffer = new Buffer(txid || new Array(65).join('0'), 'hex');
|
||||||
buffers.push(txidBuffer);
|
buffers.push(txidBuffer);
|
||||||
|
|
||||||
var outputIndexBuffer = new Buffer(4);
|
var outputIndexBuffer = new Buffer(4);
|
||||||
@ -137,7 +137,7 @@ Encoding.prototype.encodeWalletUtxoSatoshisKey = function(walletId, satoshis, tx
|
|||||||
satoshisBuffer.writeDoubleBE(satoshis || 0);
|
satoshisBuffer.writeDoubleBE(satoshis || 0);
|
||||||
buffers.push(satoshisBuffer);
|
buffers.push(satoshisBuffer);
|
||||||
|
|
||||||
var txidBuffer = new Buffer(txid || new Array(33).join('0'), 'hex');
|
var txidBuffer = new Buffer(txid || new Array(65).join('0'), 'hex');
|
||||||
buffers.push(txidBuffer);
|
buffers.push(txidBuffer);
|
||||||
|
|
||||||
var outputIndexBuffer = new Buffer(4);
|
var outputIndexBuffer = new Buffer(4);
|
||||||
|
|||||||
@ -35,7 +35,8 @@ inherits(WalletService, BaseService);
|
|||||||
WalletService.dependencies = [
|
WalletService.dependencies = [
|
||||||
'bitcoind',
|
'bitcoind',
|
||||||
'web',
|
'web',
|
||||||
'address'
|
'address',
|
||||||
|
'transaction'
|
||||||
];
|
];
|
||||||
|
|
||||||
WalletService.prototype.getAPIMethods = function() {
|
WalletService.prototype.getAPIMethods = function() {
|
||||||
@ -123,7 +124,7 @@ WalletService.prototype.blockHandler = function(block, connectBlock, callback) {
|
|||||||
operations.push({
|
operations.push({
|
||||||
type: action,
|
type: action,
|
||||||
key: self._encoding.encodeWalletUtxoSatoshisKey(walletId, output.satoshis, tx.id, outputIndex),
|
key: self._encoding.encodeWalletUtxoSatoshisKey(walletId, output.satoshis, tx.id, outputIndex),
|
||||||
value: self._encoding.encodeWalletUtxoValue(block.__height, output._scriptBuffer)
|
value: self._encoding.encodeWalletUtxoSatoshisValue(block.__height, output._scriptBuffer)
|
||||||
});
|
});
|
||||||
|
|
||||||
if(connectBlock) {
|
if(connectBlock) {
|
||||||
@ -265,7 +266,7 @@ WalletService.prototype.concurrentBlockHandler = function(block, connectBlock, c
|
|||||||
var walletIds = self._addressMap[address];
|
var walletIds = self._addressMap[address];
|
||||||
|
|
||||||
for(var j = 0; j < walletIds.length; j++) {
|
for(var j = 0; j < walletIds.length; j++) {
|
||||||
var walletId = walletIds[i];
|
var walletId = walletIds[j];
|
||||||
operations.push({
|
operations.push({
|
||||||
type: action,
|
type: action,
|
||||||
key: self._encoding.encodeWalletTransactionKey(walletId, block.__height),
|
key: self._encoding.encodeWalletTransactionKey(walletId, block.__height),
|
||||||
@ -383,16 +384,12 @@ WalletService.prototype._endpointUTXOs = function() {
|
|||||||
return function(req, res) {
|
return function(req, res) {
|
||||||
req.setTimeout(600000);
|
req.setTimeout(600000);
|
||||||
var walletId = req.params.walletId;
|
var walletId = req.params.walletId;
|
||||||
var queryMempool = req.query.queryMempool === false ? false : true;
|
var queryMempool = req.query.queryMempool !== false;
|
||||||
//var tip = self.node.bitcoind.tip;
|
|
||||||
// TODO: get the height of the tip
|
|
||||||
//var height = tip;
|
|
||||||
var height = null;
|
var height = null;
|
||||||
|
|
||||||
var options = {
|
var options = {
|
||||||
queryMempool: queryMempool
|
queryMempool: queryMempool
|
||||||
};
|
};
|
||||||
self._getUtxos(walletId, function(err, utxos) {
|
self._getUtxos(walletId, options, function(err, utxos) {
|
||||||
if(err) {
|
if(err) {
|
||||||
return utils.sendError(err, res);
|
return utils.sendError(err, res);
|
||||||
}
|
}
|
||||||
@ -409,12 +406,9 @@ WalletService.prototype._endpointGetBalance= function() {
|
|||||||
return function(req, res) {
|
return function(req, res) {
|
||||||
req.setTimeout(600000);
|
req.setTimeout(600000);
|
||||||
var walletId = req.params.walletId;
|
var walletId = req.params.walletId;
|
||||||
var queryMempool = req.query.queryMempool === false ? false : true;
|
var queryMempool = req.query.queryMempool !== false;
|
||||||
var byAddress = req.query.byAddress;
|
var byAddress = req.query.byAddress;
|
||||||
|
|
||||||
//var tip = self.node.bitcoind.tip;
|
|
||||||
// TODO: get the height of the tip
|
|
||||||
//var height = tip;
|
|
||||||
var height = null;
|
var height = null;
|
||||||
|
|
||||||
var options = {
|
var options = {
|
||||||
@ -422,7 +416,7 @@ WalletService.prototype._endpointGetBalance= function() {
|
|||||||
byAddress: byAddress
|
byAddress: byAddress
|
||||||
};
|
};
|
||||||
|
|
||||||
self._getBalance(walletId, function(err, result) {
|
self._getBalance(walletId, options, function(err, result) {
|
||||||
if(err) {
|
if(err) {
|
||||||
return utils.sendError(err, res);
|
return utils.sendError(err, res);
|
||||||
}
|
}
|
||||||
@ -505,7 +499,6 @@ WalletService.prototype._endpointDumpAllWallets = function() {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
WalletService.prototype._endpointGetWalletIds = function() {
|
WalletService.prototype._endpointGetWalletIds = function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
return function(req, res) {
|
return function(req, res) {
|
||||||
@ -617,7 +610,6 @@ WalletService.prototype._endpointPutAddresses = function() {
|
|||||||
var addAddresses = _.without(newAddresses, oldAddresses);
|
var addAddresses = _.without(newAddresses, oldAddresses);
|
||||||
var amountAdded = addAddresses.length;
|
var amountAdded = addAddresses.length;
|
||||||
|
|
||||||
//TODO this may take too long
|
|
||||||
self._importAddresses(walletId, addAddresses, function(err) {
|
self._importAddresses(walletId, addAddresses, function(err) {
|
||||||
if(err) {
|
if(err) {
|
||||||
return utils.sendError(err, res);
|
return utils.sendError(err, res);
|
||||||
@ -632,12 +624,12 @@ WalletService.prototype._endpointPutAddresses = function() {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
WalletService.prototype._getUtxos = function(walletId, callback) {
|
WalletService.prototype._getUtxos = function(walletId, options, callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
var stream = self.store.createReadStream({
|
var stream = self.store.createReadStream({
|
||||||
gte: self._encoding.encodeWalletUtxoKey(walletId),
|
gte: self._encoding.encodeWalletUtxoKey(walletId),
|
||||||
lt: self._encoding.encodeWalletUtxoKey(walletId, Array(33).join('f')) // come up with better terminal key
|
lt: self._encoding.encodeWalletUtxoKey(utils.getTerminalKey(new Buffer(walletId)))
|
||||||
});
|
});
|
||||||
|
|
||||||
var utxos = [];
|
var utxos = [];
|
||||||
@ -665,7 +657,7 @@ WalletService.prototype._getUtxos = function(walletId, callback) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
WalletService.prototype._getBalance = function(walletId, callback) {
|
WalletService.prototype._getBalance = function(walletId, options, callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
var key = self._encoding.encodeWalletBalanceKey(walletId);
|
var key = self._encoding.encodeWalletBalanceKey(walletId);
|
||||||
@ -854,10 +846,6 @@ WalletService.prototype._importAddresses = function(walletId, addresses, callbac
|
|||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO check if height has changed since we first entered the function
|
|
||||||
// if it has, we need to get operations for the new blocks
|
|
||||||
|
|
||||||
// Update addressMap and wallet balances
|
|
||||||
self._loadAllAddresses(function(err) {
|
self._loadAllAddresses(function(err) {
|
||||||
if(err) {
|
if(err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
@ -874,8 +862,6 @@ WalletService.prototype._importAddresses = function(walletId, addresses, callbac
|
|||||||
WalletService.prototype._getUTXOIndexOperations = function(walletId, addresses, callback) {
|
WalletService.prototype._getUTXOIndexOperations = function(walletId, addresses, callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
// TODO what if initialBalance changes while we are getting unspent outputs on new addresses?
|
|
||||||
|
|
||||||
var balance = 0;
|
var balance = 0;
|
||||||
|
|
||||||
self._getBalance(walletId, function(err, initialBalance) {
|
self._getBalance(walletId, function(err, initialBalance) {
|
||||||
@ -887,7 +873,7 @@ WalletService.prototype._getUTXOIndexOperations = function(walletId, addresses,
|
|||||||
balance = initialBalance;
|
balance = initialBalance;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.node.services.address.getUnspentOutputs(addresses, false, function(err, utxos) {
|
self.node.services.address.getUtxos(addresses, false, function(err, utxos) {
|
||||||
if(err) {
|
if(err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
|
|||||||
38
lib/utils.js
38
lib/utils.js
@ -39,38 +39,6 @@ utils.parseParamsWithJSON = function parseParamsWithJSON(paramsArg) {
|
|||||||
return params;
|
return params;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
|
||||||
* input: string representing a number + multiple of bytes, e.g. 500MB, 200KB, 100B
|
|
||||||
* output: integer representing the byte count
|
|
||||||
*/
|
|
||||||
utils.parseByteCount = function(byteCountString) {
|
|
||||||
|
|
||||||
function finish(n, m) {
|
|
||||||
var num = parseInt(n);
|
|
||||||
if (num > 0) {
|
|
||||||
return num * m;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_.isString(byteCountString)) {
|
|
||||||
return byteCountString;
|
|
||||||
}
|
|
||||||
var str = byteCountString.replace(/\s+/g, '');
|
|
||||||
var map = { 'MB': 1E6, 'kB': 1000, 'KB': 1000, 'MiB': (1024 * 1024),
|
|
||||||
'KiB': 1024, 'GiB': Math.pow(1024, 3), 'GB': 1E9 };
|
|
||||||
var keys = Object.keys(map);
|
|
||||||
for(var i = 0; i < keys.length; i++) {
|
|
||||||
var re = new RegExp(keys[i] + '$');
|
|
||||||
var match = str.match(re);
|
|
||||||
if (match) {
|
|
||||||
var num = str.slice(0, match.index);
|
|
||||||
return finish(num, map[keys[i]]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return finish(byteCountString, 1);
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* input: arguments passed into originating function (whoever called us)
|
* input: arguments passed into originating function (whoever called us)
|
||||||
* output: bool args are valid for encoding a key to the database
|
* output: bool args are valid for encoding a key to the database
|
||||||
@ -97,6 +65,12 @@ utils.hasRequiredArgsForEncoding = function(args) {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
utils.getTerminalKey = function(startKey) {
|
||||||
|
var endKey = Buffer.from(startKey);
|
||||||
|
endKey.writeUInt8(startKey.readUInt8(startKey.length - 1) + 1, startKey.length - 1);
|
||||||
|
return endKey;
|
||||||
|
};
|
||||||
|
|
||||||
utils.diffTime = function(time) {
|
utils.diffTime = function(time) {
|
||||||
var diff = process.hrtime(time);
|
var diff = process.hrtime(time);
|
||||||
return (diff[0] * 1E9 + diff[1])/(1E9 * 1.0);
|
return (diff[0] * 1E9 + diff[1])/(1E9 * 1.0);
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
exit 0
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
root_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/.."
|
root_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/.."
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var bitcore = require('bitcore-lib');
|
var bitcore = require('bitcore-lib');
|
||||||
|
var should = require('chai').should();
|
||||||
var Encoding = require('../../../lib/services/address/encoding');
|
var Encoding = require('../../../lib/services/address/encoding');
|
||||||
|
|
||||||
describe('Address service encoding', function() {
|
describe('Address service encoding', function() {
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
var should = require('chai').should();
|
||||||
|
|
||||||
var Encoding = require('../../../lib/services/timestamp/encoding');
|
var Encoding = require('../../../lib/services/timestamp/encoding');
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var should = require('chai').should();
|
var should = require('chai').should();
|
||||||
var sinon = require('sinon');
|
|
||||||
var bitcore = require('bitcore-lib');
|
var bitcore = require('bitcore-lib');
|
||||||
|
|
||||||
var Encoding = require('../../../lib/services/transaction/encoding');
|
var Encoding = require('../../../lib/services/transaction/encoding');
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
var should = require('chai').should();
|
||||||
var bitcore = require('bitcore-lib');
|
var bitcore = require('bitcore-lib');
|
||||||
|
|
||||||
var Encoding = require('../../../lib/services/wallet-api/encoding');
|
var Encoding = require('../../../lib/services/wallet-api/encoding');
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user