This commit is contained in:
Chris Kleeschulte 2017-07-15 15:50:52 -04:00
parent 98ea052405
commit b471857bf0
6 changed files with 439 additions and 287 deletions

View File

@ -7,7 +7,7 @@ function Encoding(servicePrefix) {
this.servicePrefix = servicePrefix;
}
Encoding.prototype.encodeAddressIndexKey = function(address, height, txid) {
Encoding.prototype.encodeAddressIndexKey = function(address, height, txid, index, input) {
var prefix = new Buffer('00', 'hex');
var buffers = [this.servicePrefix, prefix];
@ -25,6 +25,15 @@ Encoding.prototype.encodeAddressIndexKey = function(address, height, txid) {
var txidBuffer = new Buffer(txid || Array(65).join('0'), 'hex');
buffers.push(txidBuffer);
var indexBuffer = new Buffer(4);
indexBuffer.writeUInt32BE(index || 0);
buffers.push(indexBuffer);
// this is whether the address appears in an input (1) or output (0)
var inputBuffer = new Buffer(1);
inputBuffer.writeUInt8(input || 0);
buffers.push(inputBuffer);
return Buffer.concat(buffers);
};
@ -36,10 +45,14 @@ Encoding.prototype.decodeAddressIndexKey = function(buffer) {
var address = reader.read(addressSize).toString('utf8');
var height = reader.readUInt32BE();
var txid = reader.read(32).toString('hex');
var index = reader.readUInt32BE();
var input = reader.readUInt8();
return {
address: address,
height: height,
txid: txid,
index: index,
input: input
};
};

View File

@ -7,10 +7,33 @@ var index = require('../../');
var log = index.log;
var errors = index.errors;
var bitcore = require('bitcore-lib');
var Unit = bitcore.Unit;
var _ = bitcore.deps._;
var Address = bitcore.Address;
var Encoding = require('./encoding');
var utils = require('../../utils');
var Transform = require('stream').Transform;
/*
1. getAddressSummary
2. getAddressUnspentOutputs
3. bitcoind.height
4. getBlockHeader
5. getDetailedTransaction
6. getTransaction
7. sendTransaction
8. getInfo
9. bitcoind.tiphash
10. getBestBlockHash
11. isSynced
12. getAddressHistory
13. getBlock
14. getRawBlock
15. getBlockHashesByTimestamp
16. estimateFee
17. getBlockOverview
18. syncPercentage
*/
var AddressService = function(options) {
BaseService.call(this, options);
@ -28,25 +51,6 @@ AddressService.dependencies = [
];
// ---- public function prototypes
AddressService.prototype.getBalance = function(address, queryMempool, callback) {
this.getUtxos(address, queryMempool, function(err, outputs) {
if(err) {
return callback(err);
}
var satoshis = outputs.map(function(output) {
return output.satoshis;
});
var sum = satoshis.reduce(function(a, b) {
return a + b;
}, 0);
return callback(null, sum);
});
};
AddressService.prototype.getUtxos = function(addresses, queryMempool, callback) {
var self = this;
@ -127,266 +131,275 @@ AddressService.prototype.stop = function(callback) {
AddressService.prototype.getAPIMethods = function() {
return [
['getAddressBalance', this, this.getAddressBalance, 2],
['getAddressHistory', this, this.getAddressHistory, 2],
['getAddressSummary', this, this.getAddressSummary, 1],
['getAddressTxids', this, this.getAddressTxids, 2],
['getAddressUnspentOutputs', this, this.getAddressUnspentOutputs, 1],
['syncPercentage', this, this.syncPercentage, 0]
];
};
AddressService.prototype.getAddressBalance = function(addresses, options, callback) {
AddressService.prototype.getAddressHistory = function(addresses, options, callback) {
var self = this;
addresses = utils.normalizeAddressArg(addresses);
var balance = 0;
async.eachLimit(addresses, 4, function(address, next) {
options = options || {};
var from = options.from || 0;
var to = options.to || 0xffffffff;
var start = self._encoding.encodeUtxoIndexKey(address);
var criteria = {
gte: start,
lte: Buffer.concat([ start.slice(-36), new Buffer(new Array(73).join('f'), 'hex') ])
async.mapLimit(addresses, 4, function(address, next) {
self._getAddressHistory(address, next);
}, function(err, res) {
if(err) {
return callback(err);
}
var results = {
totalItems: res.length,
from: from,
to: to,
items: res
};
var stream = this._db.createReadStream(criteria);
stream.on('data', function(data) {
callback(null, results);
});
stream.on('error', function(err) {
});
stream.on('end', function() {
});
});
};
AddressService.prototype.getAddressHistory = function(addresses, options, callback) {
AddressService.prototype._getAddressHistory = function(address, options, callback) {
var self = this;
var txids = [];
async.eachLimit(addresses, 4, function(address, next) {
self.getAddressTxids(address, options, function(err, tmpTxids) {
var results = [];
var start = self._encoding.encodeAddressIndexKey(address);
var criteria = {
gte: start,
lte: utils.getTerminalKey(start)
};
// txid stream
var txidStream = self._db.createKeyStream(criteria);
txidStream.on('close', function() {
txidStream.unpipe();
});
// tx stream
var txStream = new Transform({ objectMode: true, highWaterMark: 1000 });
var streamErr;
txStream.on('end', function() {
if (streamErr) {
return callback(streamErr);
}
callback(null, results);
});
// pipe txids into tx stream for processing
txidStream.pipe(txStream);
txStream._transform = function(chunk, enc, callback) {
var key = self._encoding.decodeWalletTransactionKey(chunk);
self._tx.getDetailedTransaction(key.txid, options, function(err, tx) {
if(err) {
return next(err);
log.error(err);
txStream.emit('error', err);
return callback();
}
txids = _.union(txids, tmpTxids);
return next();
if (!tx) {
log.error('Could not find tx for txid: ' + key.txid + '. This should not be possible, check indexes.');
return callback();
}
results.push(tx);
callback();
});
}, function() {
async.mapLimit(txids, 4, function(txid, next) {
self.node.services.transaction.getTransaction(txid.toString('hex'), options, function(err, tx) {
if(err) {
return next(err);
}
var txObj = tx.toObject();
for(var i = 0; i < txObj.inputs.length; i++) {
txObj.inputs[i].satoshis = tx.__inputValues[i];
}
};
next(null, txObj);
});
}, callback);
txStream.on('error', function(err) {
log.error(err);
txStream.unpipe();
});
txStream._flush = function(callback) {
txStream.emit('end');
callback();
};
};
AddressService.prototype.getAddressSummary = function(addressArg, options, callback) {
AddressService.prototype.getAddressSummary = function(address, options, callback) {
var self = this;
var startTime = new Date();
var address = new Address(addressArg);
if (_.isUndefined(options.queryMempool)) {
options.queryMempool = true;
}
async.waterfall([
function(next) {
self._getAddressConfirmedSummary(address, options, next);
},
function(result, next) {
self._getAddressMempoolSummary(address, options, result, next);
},
function(result, next) {
self._setAndSortTxidsFromAppearanceIds(result, next);
}
], function(err, result) {
if (err) {
return callback(err);
}
var result = {
addrStr: address,
balance: 0,
balanceSat: 0,
totalReceived: 0,
totalReceivedSat: 0,
totalSent: 0,
totalSentSat: 0,
unconfirmedBalance: 0,
unconfirmedBalanceSat: 0,
unconfirmedTxApperances: 0,
txApperances: 0,
transactions: []
};
var summary = self._transformAddressSummaryFromResult(result, options);
var timeDelta = new Date() - startTime;
if (timeDelta > 5000) {
var seconds = Math.round(timeDelta / 1000);
log.warn('Slow (' + seconds + 's) getAddressSummary request for address: ' + address.toString());
}
callback(null, summary);
});
};
AddressService.prototype.getAddressTxids = function(address, options, callback) {
var self = this;
var opts = options || { start: 0, end: 0xffffffff, txid: new Array(65).join('0') };
var txids = {};
var start = self._encoding.encodeAddressIndexKey(address, opts.start, opts.txid);
var end = self._encoding.encodeAddressIndexKey(address, opts.end, opts.txid);
var stream = self.db.createKeyStream({
// txid criteria
var start = self._encoding.encodeAddressIndexKey(address);
var criteria = {
gte: start,
lt: end
lte: utils.getTerminalKey(start)
};
// txid stream
var txidStream = self._db.createKeyStream(criteria);
txidStream.on('close', function() {
txidStream.unpipe();
});
var streamErr = null;
stream.on('close', function() {
// tx stream
var txStream = new Transform({ objectMode: true, highWaterMark: 1000 });
txStream.on('end', function() {
result.balance = Unit.fromSatoshis(result.balanceSat).toBTC();
result.totalReceived = Unit.fromSatoshis(result.totalReceivedSat).toBTC();
result.totalSent = Unit.fromSatoshis(result.totalSentSat).toBTC();
result.unconfirmedBalance = Unit.fromSatoshis(result.unconfirmedBalanceSat).toBTC();
callback(null, result);
});
stream.on('data', function(buffer) {
var key = self._encoding.decodeAddressIndexKey(buffer);
txids[key.txid] = true;
// pipe txids into tx stream for processing
txidStream.pipe(txStream);
txStream._transform = function(chunk, enc, callback) {
var key = self._encoding.decodeWalletTransactionKey(chunk);
self._tx.getTransaction(key.txid, options, function(err, res) {
if(err) {
log.error(err);
txStream.emit('error', err);
return callback();
}
if (!res) {
log.error('Could not find tx for txid: ' + key.txid + '. This should not be possible, check indexes.');
return callback();
}
var tx = res.tx;
result.transactions.push(tx.id);
result.txApperances++;
if (key.input) {
result.balanceSat -= tx.inputValues[key.index];
result.totalSentSat += tx.inputValues[key.index];
if (res.confirmations === 0) {
result.unconfirmedBalanceSat -= tx.inputValues[key.index];
result.unconfirmedTxApperances++;
}
} else {
result.balanceSat += tx.outputs[key.index].satoshis;
result.totalReceivedSat += tx.outputs[key.index].satoshis;
if (res.confirmations === 0) {
result.unconfirmedBalanceSat += tx.inputValues[key.index];
result.unconfirmedTxApperances++;
}
}
callback();
});
};
txStream.on('error', function(err) {
log.error(err);
txStream.unpipe();
});
stream.on('end', function() {
callback(streamErr, Object.keys(txids));
});
stream.on('error', function(err) {
streamErr = err;
});
txStream._flush = function(callback) {
txStream.emit('end');
callback();
};
};
AddressService.prototype.getAddressUnspentOutputs = function(address, options, callback) {
var queryMempool = _.isUndefined(options.queryMempool) ? true : options.queryMempool;
var addresses = utils._normalizeAddressArg(address);
var cacheKey = addresses.join('');
var utxos = this.utxosCache.get(cacheKey);
function transformUnspentOutput(delta) {
var script = bitcore.Script.fromAddress(delta.address);
return {
address: delta.address,
txid: delta.txid,
outputIndex: delta.index,
script: script.toHex(),
satoshis: delta.satoshis,
timestamp: delta.timestamp
};
}
function updateWithMempool(confirmedUtxos, mempoolDeltas) {
if (!mempoolDeltas || !mempoolDeltas.length) {
return confirmedUtxos;
}
var isSpentOutputs = false;
var mempoolUnspentOutputs = [];
var spentOutputs = [];
for (var i = 0; i < mempoolDeltas.length; i++) {
var delta = mempoolDeltas[i];
if (delta.prevtxid && delta.satoshis <= 0) {
if (!spentOutputs[delta.prevtxid]) {
spentOutputs[delta.prevtxid] = [delta.prevout];
} else {
spentOutputs[delta.prevtxid].push(delta.prevout);
}
isSpentOutputs = true;
} else {
mempoolUnspentOutputs.push(transformUnspentOutput(delta));
}
}
var utxos = mempoolUnspentOutputs.reverse().concat(confirmedUtxos);
if (isSpentOutputs) {
return utxos.filter(function(utxo) {
if (!spentOutputs[utxo.txid]) {
return true;
} else {
return (spentOutputs[utxo.txid].indexOf(utxo.outputIndex) === -1);
}
});
}
return utxos;
}
function finish(mempoolDeltas) {
if (utxos) {
return setImmediate(function() {
callback(null, updateWithMempool(utxos, mempoolDeltas));
});
} else {
self.client.getAddressUtxos({addresses: addresses}, function(err, response) {
if (err) {
return callback(self._wrapRPCError(err));
}
var utxos = response.result.reverse();
self.utxosCache.set(cacheKey, utxos);
callback(null, updateWithMempool(utxos, mempoolDeltas));
});
}
}
if (queryMempool) {
self.client.getAddressMempool({addresses: addresses}, function(err, response) {
if (err) {
return callback(self._wrapRPCError(err));
}
finish(response.result);
});
} else {
finish();
}
};
AddressService.prototype.syncPercentage = function(callback) {
return callback(null, ((this._tip.height / this._block.getBestBlockHeight()) * 100).toFixed(2) + '%');
};
AddressService.prototype.getAddressTxidsWithHeights = function(address, options, callback) {
var self = this;
if (_.isUndefined(options.queryMempool)) {
options.queryMempool = true;
}
var opts = options || {};
var txids = {};
var results = [];
var start = self._encoding.encodeAddressIndexKey(address, opts.start || 0);
var end = Buffer.concat([ start.slice(0, -36), new Buffer((opts.end || 'ffffffff'), 'hex') ]);
var stream = self.db.createKeyStream({
var start = self._encoding.encodeUtxoIndexKey(address);
var criteria = {
gte: start,
lt: end
lt: utils.getTerminalKey(start)
};
var utxoStream = self._db.createReadStream(criteria);
var streamErr;
utxoStream.on('end', function() {
if (streamErr) {
return callback(streamErr);
}
callback(null, results);
});
var streamErr = null;
stream.on('data', function(buffer) {
var key = self._encoding.decodeAddressIndexKey(buffer);
txids[key.txid] = key.height;
});
stream.on('end', function() {
callback(streamErr, txids);
});
stream.on('error', function(err) {
utxoStream.on('error', function(err) {
streamErr = err;
});
utxoStream.on('data', function(data) {
var key = self._decodeUtxoIndexKey(data.key);
var value = self._encoding.decodeUtxoIndexValue(data.value);
results.push({
address: address,
txid: key.txid,
vout: key.oudputIndex,
ts: null,
scriptPubKey: value.scriptBuffer.toString('hex'),
amount: Unit.fromSatoshis(value.satoshis).toBTC(),
confirmations: self._p2p.getBestHeight() - value.height,
satoshis: value.satoshis,
confirmationsFromCache: true
});
});
};
// ---- private function prototypes
AddressService.prototype._setListeners = function() {

View File

@ -14,6 +14,28 @@ var BN = require('bn.js');
var consensus = require('bcoin').consensus;
var constants = require('../../constants');
/*
1. getAddressSummary
2. getAddressUnspentOutputs
3. bitcoind.height
4. getBlockHeader
5. getDetailedTransaction
6. getTransaction
7. sendTransaction
8. getInfo
9. bitcoind.tiphash
10. getBestBlockHash
11. isSynced
12. getAddressHistory
13. getBlock
14. getRawBlock
15. getBlockHashesByTimestamp
16. estimateFee
17. getBlockOverview
18. syncPercentage
*/
var BlockService = function(options) {
BaseService.call(this, options);
@ -67,27 +89,23 @@ BlockService.prototype.getAPIMethods = function() {
return methods;
};
BlockService.prototype.getBestBlockHash = function() {
return this._meta[this._meta.length - 1].hash;
BlockService.prototype.getBestBlockHash = function(callback) {
return callback(null, this._meta[this._meta.length - 1].hash);
};
BlockService.prototype.getBlock = function(hash, callback) {
var self = this;
this._db.get(this._encoding.encodeBlockKey(hash), function(err, data) {
if(err) {
return callback(err);
}
callback(null, self._encoding.decodeBlockValue(data));
});
};
BlockService.prototype._getHash = function(blockArg) {
blockArg = this._getHash(blockArg);
return (_.isNumber(blockArg) || (blockArg.length < 40 && /^[0-9]+$/.test(blockArg))) &&
this._meta[blockArg] ? this._meta[blockArg] : null;
if (!blockArg) {
return callback();
}
this._getBlock(blockArg, callback);
};
BlockService.prototype.getBlockHeader = function(blockArg, callback) {
blockArg = this._getHash(blockArg);
@ -112,14 +130,6 @@ BlockService.prototype.getBlockHeader = function(blockArg, callback) {
};
BlockService.prototype._getBlock = function(hash, callback) {
var block = this._blockQueue(hash);
if (block) {
return callback(null, block);
}
this._db.get(this._encoding.encodeBlockKey(hash), callback);
};
BlockService.prototype.getBlockOverview = function(hash, callback) {
this._getBlock(hash, function(err, block) {
@ -169,14 +179,18 @@ BlockService.prototype.getPublishEvents = function() {
};
BlockService.prototype.getRawBlock = function(hash, callback) {
this.getBlock(hash, function(err, data) {
this.getBlock(hash, function(err, block) {
if(err) {
return callback(err);
}
data.toString();
callback(null, block.toString());
});
};
BlockService.prototype.isSynced = function(callback) {
callback(null, this._p2p.getBestHeight <= this._tip.height);
};
BlockService.prototype.start = function(callback) {
var self = this;
@ -217,6 +231,12 @@ BlockService.prototype.subscribe = function(name, emitter) {
};
BlockService.prototype.syncPercentage = function(callback) {
var p2pHeight = this._p2p.getBestHeight();
var percentage = ((p2pHeight / (this._tip.height || p2pHeight)) * 100).toFixed(2);
callback(null, percentage);
};
BlockService.prototype.unsubscribe = function(name, emitter) {
var index = this._subscriptions[name].indexOf(emitter);
@ -327,6 +347,14 @@ BlockService.prototype._findCommonAncestor = function(block) {
}
};
BlockService.prototype._getBlock = function(hash, callback) {
var block = this._blockQueue(hash);
if (block) {
return callback(null, block);
}
this._db.get(this._encoding.encodeBlockKey(hash), callback);
};
BlockService.prototype._getBlockOperations = function(block) {
var self = this;
@ -379,6 +407,13 @@ BlockService.prototype._getDelta = function(tip) {
};
BlockService.prototype._getHash = function(blockArg) {
return (_.isNumber(blockArg) || (blockArg.length < 40 && /^[0-9]+$/.test(blockArg))) &&
this._meta[blockArg] ? this._meta[blockArg] : null;
};
BlockService.prototype._getIncompleteChainIndexes = function(block) {
var ret = [];
for(var i = 0; i < this._incompleteChains.length; i++) {
@ -392,7 +427,9 @@ BlockService.prototype._getIncompleteChainIndexes = function(block) {
};
BlockService.prototype._handleReorg = function(block) {
this._reorging = true;
log.warn('Chain reorganization detected! Our current block tip is: ' +
this._tip.hash + ' the current block: ' + block.hash + '.');
@ -407,8 +444,11 @@ BlockService.prototype._handleReorg = function(block) {
}
log.warn('A common ancestor block was found to at hash: ' + commonAncestor + '.');
this._broadcast(this.subscriptions.reorg, 'block/reorg', [commonAncestor, [block]]);
this._onReorg(commonAncestor, [block]);
this._reorging = false;
};
@ -446,8 +486,11 @@ BlockService.prototype._loadMeta = function(callback) {
gte: self._encoding.encodeMetaKey(0),
lte: self._encoding.encodeMetaKey(0xffffffff)
};
var stream = this._db.createReadStream(criteria);
stream.on('error', self._onDbError.bind(self));
stream.on('end', function() {
if (self._meta.length < 1) {
self._meta.push({
@ -475,11 +518,15 @@ BlockService.prototype._onBlock = function(block) {
}
// 2. log the reception
log.debug('New block received: ' + block.hash);
log.debug2('New block received: ' + block.hash);
// 3. store the block for safe keeping
this._cacheBlock(block);
// don't process any more blocks if we are currently in a reorg
if (this._reorging) {
return;
}
// 4. determine block state, reorg, outoforder, normal
var blockState = this._determineBlockState(block);
@ -492,6 +539,7 @@ BlockService.prototype._onBlock = function(block) {
// nothing to do, but wait until ancestor blocks come in
break;
case 'reorg':
this._handleReorg();
this.emit('reorg', block);
break;
default:
@ -575,7 +623,6 @@ BlockService.prototype._setListeners = function() {
self._p2p.once('bestHeight', self._onBestHeight.bind(self));
self._db.on('error', self._onDbError.bind(self));
self.on('reorg', self._handleReorg.bind(self));
};

View File

@ -9,6 +9,28 @@ var BaseService = require('../../service');
var assert = require('assert');
var Bcoin = require('./bcoin');
/*
1. getAddressSummary
2. getAddressUnspentOutputs
3. bitcoind.height
4. getBlockHeader
5. getDetailedTransaction
6. getTransaction
7. sendTransaction
8. getInfo
9. bitcoind.tiphash
10. getBestBlockHash
11. isSynced
12. getAddressHistory
13. getBlock
14. getRawBlock
15. getBlockHashesByTimestamp
16. estimateFee
17. getBlockOverview
18. syncPercentage
*/
var P2P = function(options) {
if (!(this instanceof P2P)) {
@ -61,6 +83,19 @@ P2P.prototype.getHeaders = function(filter) {
};
P2P.prototype.getInfo = function(callback) {
callback(null, {
version: '4.0',
protocolversion: 'latest',
blocks: this._getBestHeight(),
timeoffset: 0,
connections: this._pool.numberConnected,
difficulty: 0,
testnet: false,
relayfee: 0
});
};
P2P.prototype.getMempool = function(filter) {
var peer = this._getPeer();

View File

@ -6,6 +6,28 @@ var inherits = require('util').inherits;
var LRU = require('lru-cache');
var utils = require('../../../lib/utils');
/*
1. getAddressSummary
2. getAddressUnspentOutputs
3. bitcoind.height
4. getBlockHeader
5. getDetailedTransaction
6. getTransaction
7. sendTransaction
8. getInfo
9. bitcoind.tiphash
10. getBestBlockHash
11. isSynced
12. getAddressHistory
13. getBlock
14. getRawBlock
15. getBlockHashesByTimestamp
16. estimateFee
17. getBlockOverview
18. syncPercentage
*/
function TimestampService(options) {
BaseService.call(this, options);
this._db = this.node.services.db;
@ -24,6 +46,7 @@ TimestampService.prototype.getAPIMethods = function() {
};
TimestampService.prototype.syncPercentage = function(callback) {
return callback(null, ((this._tip.height / this._block.getBestBlockHeight()) * 100).toFixed(2) + '%');
};
TimestampService.prototype.getBlockHashesByTimestamp = function(callback) {

View File

@ -10,77 +10,97 @@ var levelup = require('levelup');
function TransactionService(options) {
BaseService.call(this, options);
this._db = this.node.services.db;
this.currentTransactions = {};
this._mempool = this.node.services._mempool;
this._block = this.node.services.block;
this._p2p = this.node.services.p2p;
}
inherits(TransactionService, BaseService);
TransactionService.dependencies = [
'p2p',
'db',
'block',
'timestamp',
'mempool'
];
/*
1. getAddressSummary
2. getAddressUnspentOutputs
3. bitcoind.height
4. getBlockHeader
5. getDetailedTransaction
6. getTransaction
7. sendTransaction
8. getInfo
9. bitcoind.tiphash
10. getBestBlockHash
11. isSynced
12. getAddressHistory
13. getBlock
14. getRawBlock
15. getBlockHashesByTimestamp
16. estimateFee
17. getBlockOverview
18. syncPercentage
*/
TransactionService.prototype.getAPIMethods = function() {
return [
['getRawTransaction', this, this.getRawTransaction, 1],
['getTransaction', this, this.getTransaction, 1],
['getDetailedTransaction', this, this.getDetailedTransaction, 1],
['sendTransaction', this, this.sendTransaction, 1],
['getSpentInfo', this, this.getSpentInfo, 1],
['syncPercentage', this, this.syncPercentage, 0]
];
};
TransactionService.prototype.getSpentInfo = function(txid, callback) {
};
TransactionService.prototype.getRawTransaction = function(txid, callback) {
this.getTransaction(txid, function(err, tx) {
if (err) {
return callback(err);
}
return tx.serialize();
});
};
TransactionService.prototype.getDetailedTransaction = TransactionService.prototype.getTransaction = function(txid, options, callback) {
TransactionService.prototype.getTransaction = function(txid, options, callback) {
var self = this;
var key = self.encoding.encodeTransactionKey(txid);
var queryMempool = _.isUndefined(options.queryMempool) ? true : options.queryMempool;
async.waterfall([
function(next) {
self.node.services.db.get(key, function(err, buffer) {
if (err instanceof levelup.errors.NotFoundError) {
return next(null, false);
} else if (err) {
var key = self.encoding.encodeTransactionKey(txid);
this._db.get(key, function(err, tx) {
if(err) {
return callback(err);
}
if (queryMempool && !tx) {
this._mempool.getTransaction(tx, function(err, memTx) {
if(err) {
return callback(err);
}
var tx = self.encoding.decodeTransactionValue(buffer);
next(null, tx);
if (memTx) {
return callback(null, { tx: memTx, confirmations: 0});
}
return callback();
});
}, function(tx, next) {
} else {
if (tx) {
return next(null, tx);
return callback(null, { tx: tx, confirmations: this._p2p.getBestHeight - tx.__height });
}
if (!options || !options.queryMempool) {
return next(new Error('Transaction: ' + txid + ' not found in index'));
}
self.node.services.mempool.getTransaction(txid, function(err, tx) {
if (err instanceof levelup.errors.NotFoundError) {
return callback(new Error('Transaction: ' + txid + ' not found in index or mempool'));
} else if (err) {
return callback(err);
}
self._getMissingInputValues(tx, next);
});
}], callback);
return callback();
}
});
};
TransactionService.prototype.onBlock = function(block, connectBlock, callback) {
TransactionService.prototype._onBlock = function(block, connectBlock, callback) {
var self = this;
var action = 'put';
var reverseAction = 'del';
@ -156,6 +176,7 @@ TransactionService.prototype._onReorg = function(commonAncestor, newBlockList) {
};
TransactionService.prototype.start = function(callback) {
var self = this;
self._setListeners();
@ -173,7 +194,7 @@ TransactionService.prototype.start = function(callback) {
self._tip = tip;
self.prefix = prefix;
self.encoding = new Encoding(self.prefix);
self._encoding = new Encoding(self.prefix);
self._startSubscriptions();
callback();