entries.
This commit is contained in:
parent
4ce1ed3046
commit
72c2d25efc
@ -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;
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user