This commit is contained in:
Christopher Jeffrey 2016-05-12 15:59:05 -07:00
parent 4ce1ed3046
commit 72c2d25efc
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD

View File

@ -141,7 +141,7 @@ Mempool.prototype._init = function _init() {
self.db = bcoin.ldb(options);
// Use the txdb object for its get methods.
self.tx = new bcoin.txdb(self.db);
self.tx = new TXDB(self.db);
self.db.open(function(err) {
if (err) {
@ -167,6 +167,59 @@ Mempool.prototype._init = function _init() {
});
};
function TXDB(db, options) {
bcoin.txdb.call(this, db, options);
}
utils.inherits(TXDB, bcoin.txdb);
TXDB.prototype.getTX = function getTX(hash, callback) {
return this.db.get('t/' + hash, function(err, entry) {
if (err && err.type !== 'NotFoundError')
return callback(err);
if (!entry)
return callback();
try {
entry = MempoolEntry.fromRaw(entry);
} catch (e) {
return callback(e);
}
return callback(null, entry.tx, entry);
});
};
TXDB.prototype.getRangeEntries = function getRangeEntries(options, callback) {
var self = this;
var entries = [];
return this.getRangeHashes(options, function(err, hashes) {
if (err)
return callback(err);
utils.forEachSerial(hashes, function(hash, next) {
self.getTX(hash, function(err, tx, entry) {
if (err)
return callback(err);
if (!entry)
return next();
entries.push(entry);
next();
});
}, function(err) {
if (err)
return callback(err);
return callback(null, entries);
});
});
};
/**
* Tally up total memory usage from database.
* @param {Function} callback - Returns [Error, Number].
@ -226,6 +279,8 @@ Mempool.prototype.addBlock = function addBlock(block, callback, force) {
var self = this;
var txs = [];
var unlock = this._lock(addBlock, [block, callback], force);
var entry;
if (!unlock)
return;
@ -234,7 +289,7 @@ Mempool.prototype.addBlock = function addBlock(block, callback, force) {
// We add the txs we haven't seen to
// the mempool first to potentially
// resolve orphans.
utils.forEachSerial(block.txs, function(tx, next) {
utils.forEachSerial(block.txs, function(entry, next) {
var hash, copy;
if (!self.chain.isFull())
@ -256,10 +311,9 @@ Mempool.prototype.addBlock = function addBlock(block, callback, force) {
if (err)
return next(err);
copy = tx.clone();
copy.unsetBlock();
entry = MempoolEntry.fromTX(tx, block.height);
self.addUnchecked(tx, next, true);
self.addUnchecked(entry, next, true);
});
});
}, function(err) {
@ -273,20 +327,14 @@ Mempool.prototype.addBlock = function addBlock(block, callback, force) {
if (tx.isCoinbase())
return next();
self.getTX(hash, function(err, existing) {
self.getEntry(hash, function(err, entry) {
if (err)
return next(err);
if (!existing)
if (!entry)
return self.removeOrphan(hash, next);
copy = tx.clone();
copy.ts = existing.ts;
copy.block = existing.block;
copy.height = existing.height;
copy.ps = existing.ps;
self.removeUnchecked(copy, false, function(err) {
self.removeUnchecked(entry, false, function(err) {
if (err)
return next(err);
@ -317,6 +365,7 @@ Mempool.prototype.addBlock = function addBlock(block, callback, force) {
Mempool.prototype.removeBlock = function removeBlock(block, callback, force) {
var self = this;
var unlock = this._lock(removeBlock, [block, callback], force);
var entry;
if (!unlock)
return;
@ -324,8 +373,6 @@ Mempool.prototype.removeBlock = function removeBlock(block, callback, force) {
callback = utils.wrap(callback, unlock);
utils.forEachSerial(block.txs, function(tx, next) {
var copy;
if (tx.isCoinbase())
return next();
@ -336,13 +383,9 @@ Mempool.prototype.removeBlock = function removeBlock(block, callback, force) {
if (result)
return next();
copy = tx.clone();
copy.ts = 0;
copy.block = null;
copy.height = -1;
copy.ps = utils.now();
entry = MempoolEntry.fromTX(tx, block.height);
self.addUnchecked(copy, function(err) {
self.addUnchecked(entry, function(err) {
if (err)
return next(err);
@ -366,22 +409,22 @@ Mempool.prototype.limitMempoolSize = function limitMempoolSize(callback) {
if (this.getSize() <= this.maxSize)
return callback(null, true);
this.getRange({
this.tx.getRangeEntries({
start: 0,
end: utils.now() - constants.mempool.MEMPOOL_EXPIRY
}, function(err, txs) {
}, function(err, entries) {
if (err)
return callback(err);
utils.forEachSerial(function(tx, next) {
utils.forEachSerial(function(entry, next) {
if (self.getSize() <= self.maxSize)
return next();
self.removeUnchecked(tx, true, function(err) {
self.removeUnchecked(entry, true, function(err) {
if (err)
return next(err);
rate = tx.getFee().muln(1000).divn(tx.getVirtualSize()).toNumber();
rate = entry.fees.muln(1000).divn(entry.size).toNumber();
rate += self.minReasonableFee;
if (rate > self.minFeeRate) {
@ -435,6 +478,25 @@ Mempool.prototype.getTX = function getTX(hash, callback) {
return this.tx.getTX(hash, callback);
};
/**
* Retrieve a transaction from the mempool.
* Note that this will not be filled with coins.
* @param {TX|Hash} hash
* @param {Function} callback - Returns [Error, {@link TX}].
*/
Mempool.prototype.getEntry = function getEntry(hash, callback) {
return this.tx.getTX(hash, function(err, tx, entry) {
if (err)
return callback(err);
if (!entry)
return callback();
return callback(null, entry);
});
};
/**
* Retrieve a coin from the mempool (unspents only).
* @param {Hash} hash
@ -561,7 +623,7 @@ Mempool.prototype.addTX = function addTX(tx, callback, force) {
var lockFlags = constants.flags.STANDARD_LOCKTIME_FLAGS;
var hash = tx.hash('hex');
var ret = {};
var now;
var now, entry;
var unlock = this._lock(addTX, [tx, callback], force);
if (!unlock)
@ -650,7 +712,9 @@ Mempool.prototype.addTX = function addTX(tx, callback, force) {
if (!tx.hasCoins())
return self.storeOrphan(tx, callback);
self.verify(tx, function(err) {
entry = MempoolEntry.fromTX(tx, self.chain.height);
self.verify(entry, function(err) {
if (err)
return callback(err);
@ -665,7 +729,7 @@ Mempool.prototype.addTX = function addTX(tx, callback, force) {
0));
}
self.addUnchecked(tx, callback, true);
self.addUnchecked(entry, callback, true);
});
});
});
@ -685,33 +749,34 @@ Mempool.prototype.addTX = function addTX(tx, callback, force) {
* @param {Function} callback - Returns [{@link VerifyError}].
*/
Mempool.prototype.addUnchecked = function addUnchecked(tx, callback, force) {
Mempool.prototype.addUnchecked = function addUnchecked(entry, callback, force) {
var self = this;
var unlock = this._lock(addUnchecked, [tx, callback], force);
var unlock = this._lock(addUnchecked, [entry, callback], force);
if (!unlock)
return;
callback = utils.wrap(callback, unlock);
this._addUnchecked(tx, function(err) {
this._addUnchecked(entry, function(err) {
if (err)
return callback(err);
self.spent += tx.inputs.length;
self.size += self.memUsage(tx);
self.spent += entry.tx.inputs.length;
self.size += self.memUsage(entry.tx);
self.total++;
self.emit('tx', tx);
self.emit('add tx', tx);
self.emit('tx', entry.tx);
self.emit('add tx', entry.tx);
bcoin.debug('Added tx %s to the mempool.', tx.rhash);
bcoin.debug('Added tx %s to the mempool.', entry.tx.rhash);
self.resolveOrphans(tx, function(err, resolved) {
self.resolveOrphans(entry.tx, function(err, resolved) {
if (err)
return callback(err);
utils.forEachSerial(resolved, function(tx, next) {
self.verify(tx, function(err) {
entry = MempoolEntry.fromTX(tx, self.chain.height);
self.verify(entry, function(err) {
if (err) {
if (err.type === 'VerifyError') {
bcoin.debug('Could not resolve orphan %s: %s.',
@ -722,12 +787,12 @@ Mempool.prototype.addUnchecked = function addUnchecked(tx, callback, force) {
self.emit('error', err);
return next();
}
self.addUnchecked(tx, function(err) {
self.addUnchecked(entry, function(err) {
if (err) {
self.emit('error', err);
return next();
}
bcoin.debug('Resolved orphan %s in mempool.', tx.rhash);
bcoin.debug('Resolved orphan %s in mempool.', entry.tx.rhash);
next();
}, true);
});
@ -743,33 +808,33 @@ Mempool.prototype.addUnchecked = function addUnchecked(tx, callback, force) {
* @param {Function} callback
*/
Mempool.prototype.removeUnchecked = function removeUnchecked(tx, limit, callback, force) {
Mempool.prototype.removeUnchecked = function removeUnchecked(entry, limit, callback, force) {
var self = this;
var rate;
var unlock = this._lock(removeUnchecked, [tx, limit, callback], force);
var unlock = this._lock(removeUnchecked, [entry, limit, callback], force);
if (!unlock)
return;
callback = utils.wrap(callback, unlock);
this.fillAllHistory(tx, function(err, tx) {
this.fillAllHistory(entry.tx, function(err, tx) {
if (err)
return callback(err);
self.removeOrphan(tx, function(err) {
self.removeOrphan(entry.tx, function(err) {
if (err)
return callback(err);
self._removeUnchecked(tx, limit, function(err) {
self._removeUnchecked(entry, limit, function(err) {
if (err)
return callback(err);
self.spent -= tx.inputs.length;
self.size -= self.memUsage(tx);
self.spent -= entry.tx.inputs.length;
self.size -= self.memUsage(entry.tx);
self.total--;
self.emit('remove tx', tx);
self.emit('remove tx', entry.tx);
return callback();
});
@ -820,12 +885,13 @@ Mempool.prototype.getMinRate = function getMinRate() {
* @param {Function} callback - Returns [{@link VerifyError}].
*/
Mempool.prototype.verify = function verify(tx, callback) {
Mempool.prototype.verify = function verify(entry, callback) {
var self = this;
var height = this.chain.height + 1;
var lockFlags = constants.flags.STANDARD_LOCKTIME_FLAGS;
var flags = constants.flags.STANDARD_VERIFY_FLAGS;
var mandatory = constants.flags.MANDATORY_VERIFY_FLAGS;
var tx = entry.tx;
var ret = {};
var fee, modFee, now, size, rejectFee, minRelayFee;
@ -857,11 +923,9 @@ Mempool.prototype.verify = function verify(tx, callback) {
0));
}
// In reality we would apply deltas
// to the modified fee (only for mining).
fee = tx.getFee();
modFee = fee;
size = tx.getVirtualSize();
modFee = entry.fees;
size = entry.size;
rejectFee = tx.getMinFee(size, self.getMinRate());
minRelayFee = tx.getMinFee(size, self.minRelayFee);
@ -873,7 +937,7 @@ Mempool.prototype.verify = function verify(tx, callback) {
}
if (self.relayPriority && modFee.cmp(minRelayFee) < 0) {
if (!tx.isFree(height, size)) {
if (!entry.isFree(height)) {
return callback(new VerifyError(tx,
'insufficientfee',
'insufficient priority',
@ -1403,15 +1467,16 @@ Mempool.prototype.getConfidence = function getConfidence(hash, callback) {
* @param {Function} callback
*/
Mempool.prototype._addUnchecked = function _addUnchecked(tx, callback) {
Mempool.prototype._addUnchecked = function _addUnchecked(entry, callback) {
var self = this;
var tx = entry.tx;
var hash = tx.hash('hex');
var i, addresses, address, input, output, key, coin, batch;
batch = this.db.batch();
batch.put('t/' + hash, tx.toExtended());
batch.put('m/' + pad32(tx.ps) + '/' + hash, DUMMY);
batch.put('m/' + pad32(entry.ts) + '/' + hash, DUMMY);
if (this.options.indexAddress) {
addresses = tx.getAddresses();
@ -1469,22 +1534,23 @@ Mempool.prototype._addUnchecked = function _addUnchecked(tx, callback) {
Mempool.prototype._removeUnchecked = function _removeUnchecked(hash, limit, callback) {
var self = this;
var batch, i, addresses, output;
var batch, i, addresses, output, tx;
if (hash.hash)
hash = hash.hash('hex');
if (hash.tx)
hash = hash.tx.hash('hex');
this.getTX(hash, function(err, tx) {
this.getEntry(hash, function(err, entry) {
if (err)
return callback(err);
if (!tx)
if (!entry)
return callback();
tx = entry.tx;
batch = self.db.batch();
batch.del('t/' + hash);
batch.del('m/' + pad32(tx.ps) + '/' + hash);
batch.del('m/' + pad32(entry.ts) + '/' + hash);
if (self.options.indexAddress) {
addresses = tx.getAddresses();
@ -1617,7 +1683,7 @@ Mempool.prototype.getSize = function getSize() {
+ this.size;
};
function MempoolTX(options) {
function MempoolEntry(options) {
this.tx = options.tx;
this.height = options.height;
this.priority = options.priority;
@ -1629,10 +1695,10 @@ function MempoolTX(options) {
this.fees = options.fees;
}
MempoolTX.fromTX = function fromTX(tx, height) {
MempoolEntry.fromTX = function fromTX(tx, height) {
var data = tx.getPriority(height);
return new MempoolTX({
return new MempoolEntry({
tx: tx,
height: height,
priority: data.priority,
@ -1644,7 +1710,7 @@ MempoolTX.fromTX = function fromTX(tx, height) {
});
};
MempoolTX.prototype.toRaw = function toRaw() {
MempoolEntry.prototype.toRaw = function toRaw() {
var p = new BufferWriter();
bcoin.protocol.framer.tx(this.tx, p);
p.writeU32(this.height);
@ -1657,10 +1723,10 @@ MempoolTX.prototype.toRaw = function toRaw() {
return p.render();
};
MempoolTX.fromRaw = function fromRaw(data, saveCoins) {
MempoolEntry.fromRaw = function fromRaw(data, saveCoins) {
var p = new BufferReader(data);
return new MempoolTX({
tx: bcoin.protocol.parser.parseTX(p),
return new MempoolEntry({
tx: bcoin.tx.fromRaw(p),
height: p.readU32(),
priority: p.readVarint(true),
chainValue: p.readVarint(true),
@ -1671,7 +1737,7 @@ MempoolTX.fromRaw = function fromRaw(data, saveCoins) {
});
};
MempoolTX.prototype.getPriority = function getPriority(height) {
MempoolEntry.prototype.getPriority = function getPriority(height) {
var heightDelta = Math.max(0, height - this.height);
var modSize = this.tx.getModifiedSize(this.size);
var deltaPriority = new bn(heightDelta).mul(this.chainValue).divn(modSize);
@ -1681,7 +1747,7 @@ MempoolTX.prototype.getPriority = function getPriority(height) {
return result;
};
MempoolTX.prototype.isFree = function isFree(height) {
MempoolEntry.prototype.isFree = function isFree(height) {
var priority = this.getPriority(height);
return priority.cmp(constants.tx.FREE_THRESHOLD) > 0;
};