mempool: cleanup. more sync methods.

This commit is contained in:
Christopher Jeffrey 2016-08-16 10:54:33 -07:00
parent e517a7a9f7
commit 82ca244669
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
2 changed files with 134 additions and 200 deletions

View File

@ -1597,20 +1597,7 @@ RPC.prototype.prioritisetransaction = function prioritisetransaction(args, callb
if (entry.fees < 0)
entry.fees = 0;
this.mempool.fillAllCoins(entry.tx, function(err) {
if (err)
return callback(err);
if (!entry.tx.hasCoins())
return callback(new RPCError('Transaction not in mempool.'));
self.mempool.addUnchecked(entry, function(err) {
if (err)
return callback(err);
callback(null, true);
});
});
return callback(null, true);
};
RPC.prototype.submitblock = function submitblock(args, callback) {

View File

@ -149,54 +149,37 @@ Mempool.prototype._lock = function _lock(func, args, force) {
* @param {Function} callback
*/
Mempool.prototype.addBlock = function addBlock(block, callback, force) {
var self = this;
var unlock = this._lock(addBlock, [block, callback], force);
var len, entries, entry;
Mempool.prototype.addBlock = function addBlock(block, callback) {
var entries = [];
var i, entry, tx, hash;
if (!unlock)
return;
callback = utils.wrap(callback, unlock);
len = block.txs.length - 1;
entries = [];
utils.forRangeSerial(0, block.txs.length, function(i, next) {
var tx = block.txs[len--];
var hash = tx.hash('hex');
for (i = block.txs.length - 1; i >= 0; i--) {
tx = block.txs[i];
hash = tx.hash('hex');
if (tx.isCoinbase())
return next();
continue;
entry = self.getEntry(hash);
entry = this.getEntry(hash);
if (!entry) {
self.removeOrphan(hash);
return next();
this.removeOrphan(hash);
continue;
}
self.removeUnchecked(entry, false, function(err) {
if (err)
return next(err);
this.removeUnchecked(entry);
this.emit('confirmed', tx, block);
self.emit('confirmed', tx, block);
entries.push(entry);
}
entries.push(entry);
this.blockSinceBump = true;
this.lastFeeUpdate = utils.now();
return next();
}, true);
}, function(err) {
if (err)
return callback(err);
if (this.fees)
this.fees.processBlock(block.height, entries, this.chain.isFull());
self.blockSinceBump = true;
self.lastFeeUpdate = utils.now();
if (self.fees)
self.fees.processBlock(block.height, entries, self.chain.isFull());
return callback();
});
return callback();
};
/**
@ -245,62 +228,54 @@ Mempool.prototype.removeBlock = function removeBlock(block, callback, force) {
* @param {Function} callback
*/
Mempool.prototype.limitMempoolSize = function limitMempoolSize(entryHash, callback) {
var self = this;
Mempool.prototype.limitMempoolSize = function limitMempoolSize(entryHash) {
var trimmed = false;
var hashes, end, entry;
var i, hashes, hash, end, entry;
if (this.getSize() <= this.maxSize)
return callback(null, trimmed);
return trimmed;
hashes = Object.keys(this.tx);
hashes = this.getSnapshot();
end = utils.now() - constants.mempool.MEMPOOL_EXPIRY;
utils.forEachSerial(hashes, function(hash, next) {
var entry = self.getEntry(hash);
for (i = 0; i < hashes.length; i++) {
hash = hashes[i];
entry = this.getEntry(hash);
if (!entry)
return next();
if (self.getSize() <= self.maxSize)
return callback(null, trimmed);
continue;
if (entry.ts >= end)
return next();
continue;
if (!trimmed && hash === entryHash)
trimmed = true;
self.removeUnchecked(entry, true, next, true);
}, function(err) {
if (err)
return callback(err);
this.removeUnchecked(entry, true);
if (self.getSize() <= self.maxSize)
return callback(null, trimmed);
if (this.getSize() <= this.maxSize)
return trimmed;
}
hashes = Object.keys(self.tx);
hashes = this.getSnapshot();
utils.forEachSerial(hashes, function(hash, next) {
if (self.getSize() <= self.maxSize)
return callback(null, trimmed);
for (i = 0; i < hashes.length; i++) {
hash = hashes[i];
entry = this.getEntry(hash);
entry = self.getEntry(hash);
if (!entry)
continue;
if (!entry)
return next();
if (!trimmed && hash === entryHash)
trimmed = true;
if (!trimmed && hash === entryHash)
trimmed = true;
this.removeUnchecked(entry, true);
self.removeUnchecked(entry, true, next, true);
}, function(err) {
if (err)
return callback(err);
if (this.getSize() <= this.maxSize)
return trimmed;
}
return callback(null, trimmed);
});
});
return trimmed;
};
/**
@ -595,11 +570,11 @@ Mempool.prototype.addTX = function addTX(tx, callback, force) {
}
}
this.chain.checkFinal(this.chain.tip, tx, lockFlags, function(err, isFinal) {
this.chain.checkFinal(this.chain.tip, tx, lockFlags, function(err, result) {
if (err)
return callback(err);
if (!isFinal) {
if (!result) {
return callback(new VerifyError(tx,
'nonstandard',
'non-final',
@ -650,19 +625,14 @@ Mempool.prototype.addTX = function addTX(tx, callback, force) {
if (err)
return callback(err);
self.limitMempoolSize(hash, function(err, trimmed) {
if (err)
return callback(err);
if (self.limitMempoolSize(hash)) {
return callback(new VerifyError(tx,
'insufficientfee',
'mempool full',
0));
}
if (trimmed) {
return callback(new VerifyError(tx,
'insufficientfee',
'mempool full',
0));
}
return callback();
});
return callback();
}, true);
});
});
@ -739,48 +709,35 @@ Mempool.prototype.addUnchecked = function addUnchecked(entry, callback, force) {
* @param {Function} callback
*/
Mempool.prototype.removeUnchecked = function removeUnchecked(entry, limit, callback, force) {
var self = this;
var unlock, rate, hash;
unlock = this._lock(removeUnchecked, [entry, limit, callback], force);
if (!unlock)
return;
callback = utils.wrap(callback, unlock);
Mempool.prototype.removeUnchecked = function removeUnchecked(entry, limit) {
var rate, hash;
this.removeOrphan(entry.tx);
this._removeUnchecked(entry, limit, function(err) {
if (err)
return callback(err);
this._removeUnchecked(entry, limit);
self.totalSpent -= entry.tx.inputs.length;
self.size -= self.memUsage(entry.tx);
self.totalTX--;
this.totalSpent -= entry.tx.inputs.length;
this.size -= this.memUsage(entry.tx);
this.totalTX--;
if (self.fees) {
hash = entry.tx.hash('hex');
self.fees.removeTX(hash);
if (this.fees) {
hash = entry.tx.hash('hex');
this.fees.removeTX(hash);
}
if (limit) {
this.logger.spam('Removed tx %s from mempool.', entry.tx.rhash);
rate = bcoin.tx.getRate(entry.sizes, entry.fees);
rate += this.minReasonableFee;
if (rate > this.minFeeRate) {
this.minFeeRate = rate;
this.blockSinceBump = false;
}
} else {
this.logger.spam('Removed block tx %s from mempool.', entry.tx.rhash);
}
if (limit) {
self.logger.spam('Removed tx %s from mempool.', entry.tx.rhash);
rate = bcoin.tx.getRate(entry.sizes, entry.fees);
rate += self.minReasonableFee;
if (rate > self.minFeeRate) {
self.minFeeRate = rate;
self.blockSinceBump = false;
}
} else {
self.logger.spam('Removed block tx %s from mempool.', entry.tx.rhash);
}
self.emit('remove tx', entry.tx);
return callback();
});
this.emit('remove tx', entry.tx);
};
/**
@ -1001,7 +958,7 @@ Mempool.prototype.countDescendants = function countDescendants(tx) {
if (!next)
continue;
count = 1;
count += this.countDescendants(next);
count += this.countDescendants(next.tx);
if (count > max)
max = count;
}
@ -1081,13 +1038,15 @@ Mempool.prototype.storeOrphan = function storeOrphan(tx) {
*/
Mempool.prototype.getBalance = function getBalance() {
var keys = Object.keys(this.tx);
var hashes = this.getSnapshot();
var total = 0;
var i, j, key, tx, hash, coin;
var i, j, tx, hash, coin;
for (i = 0; i < keys.length; i++) {
key = keys[i];
tx = this.tx[key].tx;
for (i = 0; i < hashes.length; i++) {
hash = hashes[i];
tx = this.getTX(hash);
if (!tx)
continue;
hash = tx.hash('hex');
for (j = 0; j < tx.outputs.length; j++) {
coin = this.getCoin(hash, j);
@ -1105,13 +1064,13 @@ Mempool.prototype.getBalance = function getBalance() {
*/
Mempool.prototype.getHistory = function getHistory() {
var keys = Object.keys(this.tx);
var hashes = this.getSnapshot();
var txs = [];
var i, key, tx;
var i, hash, tx;
for (i = 0; i < keys.length; i++) {
key = keys[i];
tx = this.getTX(key);
for (i = 0; i < hashes.length; i++) {
hash = hashes[i];
tx = this.getTX(hash);
if (!tx)
continue;
txs.push(tx);
@ -1304,7 +1263,7 @@ Mempool.prototype.fillAllCoins = function fillAllCoins(tx, callback) {
/**
* Get a snapshot of all transaction hashes in the mempool. Used
* for generating INV packets in response to MEMPOOL packets.
* @param {Function} callback - Returns [Error, {@link Hash}[]].
* @returns {Hash[]}
*/
Mempool.prototype.getSnapshot = function getSnapshot() {
@ -1421,16 +1380,16 @@ Mempool.prototype._addUnchecked = function _addUnchecked(entry) {
if (this.options.indexAddress)
this.coinIndex.removeCoin(input.coin);
this.spents[key] = bcoin.outpoint.fromTX(tx, i);
this.spents[key] = entry;
}
for (i = 0; i < tx.outputs.length; i++) {
output = tx.outputs[i];
if (this.options.indexAddress) {
for (i = 0; i < tx.outputs.length; i++) {
output = tx.outputs[i];
if (output.script.isUnspendable())
continue;
if (output.script.isUnspendable())
continue;
if (this.options.indexAddress) {
coin = bcoin.coin.fromTX(tx, i);
this.coinIndex.addCoin(coin);
}
@ -1460,50 +1419,49 @@ Mempool.prototype._addUnchecked = function _addUnchecked(entry) {
* @param {Function} callback
*/
Mempool.prototype._removeUnchecked = function _removeUnchecked(entry, limit, callback) {
var self = this;
Mempool.prototype._removeUnchecked = function _removeUnchecked(entry, limit) {
var tx = entry.tx;
var hash = tx.hash('hex');
var i, input, output, key, coin;
this._removeSpenders(entry, limit, function(err) {
if (err)
return callback(err);
// We do not remove spenders if this is
// being removed for a block. The spenders
// are still spending valid coins (which
// now exist on the blockchain).
if (limit)
this._removeSpenders(entry);
delete self.tx[hash];
delete this.tx[hash];
if (self.options.indexAddress)
self.txIndex.addTX(tx);
if (this.options.indexAddress)
this.txIndex.removeTX(tx);
for (i = 0; i < tx.inputs.length; i++) {
input = tx.inputs[i];
key = input.prevout.hash + input.prevout.index;
for (i = 0; i < tx.inputs.length; i++) {
input = tx.inputs[i];
key = input.prevout.hash + input.prevout.index;
if (tx.isCoinbase())
break;
if (tx.isCoinbase())
break;
delete self.spents[key];
delete this.spents[key];
if (self.options.indexAddress) {
assert(input.coin);
self.coinIndex.removeCoin(input.coin);
}
}
assert(input.coin);
if (this.options.indexAddress)
this.coinIndex.removeCoin(input.coin);
}
if (this.options.indexAddress) {
for (i = 0; i < tx.outputs.length; i++) {
output = tx.outputs[i];
if (output.script.isUnspendable())
continue;
if (self.options.indexAddress) {
coin = bcoin.coin.fromTX(tx, i);
self.coinIndex.removeCoin(coin);
}
coin = bcoin.coin.fromTX(tx, i);
this.coinIndex.removeCoin(coin);
}
return callback();
});
}
};
/**
@ -1514,33 +1472,19 @@ Mempool.prototype._removeUnchecked = function _removeUnchecked(entry, limit, cal
* @param {Function} callback
*/
Mempool.prototype._removeSpenders = function _removeSpenders(entry, limit, callback) {
var self = this;
Mempool.prototype._removeSpenders = function _removeSpenders(entry) {
var tx = entry.tx;
var hash, spender;
var hash = tx.hash('hex');
var i, spender;
// We do not remove spenders if this is
// being removed for a block. The spenders
// are still spending valid coins (which
// now exist on the blockchain).
if (!limit)
return callback();
hash = tx.hash('hex');
utils.forEachSerial(tx.outputs, function(output, next, i) {
spender = self.isSpent(hash, i);
for (i = 0; i < tx.outputs.length; i++) {
spender = this.isSpent(hash, i);
if (!spender)
return next();
continue;
entry = self.getEntry(spender.hash);
if (!entry)
return next();
self.removeUnchecked(entry, limit, next, true);
}, callback);
this.removeUnchecked(spender, true);
}
};
/**
@ -1575,11 +1519,14 @@ Mempool.prototype.memUsage = function memUsage(tx) {
mem += 8;
// Mempool Entry
mem += 40;
// Map entry
mem += 64;
// Spent entry
mem += tx.outputs.length * 72;
mem += 64;
return mem;
};