miner: better sorting of txs.

This commit is contained in:
Christopher Jeffrey 2016-10-06 00:08:23 -07:00
parent 749a912f8c
commit 12a5bcc4e2
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
4 changed files with 183 additions and 36 deletions

View File

@ -728,7 +728,7 @@ Mempool.prototype._addUnchecked = co(function* addUnchecked(entry) {
this.trackEntry(entry);
this.emit('tx', entry.tx);
this.emit('add tx', entry.tx);
this.emit('add entry', entry);
if (this.fees)
this.fees.processTX(entry, this.chain.isFull());
@ -809,7 +809,7 @@ Mempool.prototype.removeUnchecked = function removeUnchecked(entry, limit) {
this.logger.spam('Removed block tx %s from mempool.', entry.tx.rhash);
}
this.emit('remove tx', entry.tx);
this.emit('remove entry', entry);
};
/**

View File

@ -121,25 +121,30 @@ Miner.prototype.start = co(function* start() {
this.running = true;
try {
attempt = yield this.createBlock();
} catch (e) {
this.running = false;
this.emit('error', e);
return;
}
while (!block) {
if (!this.running)
return;
if (!this.running)
return;
try {
attempt = yield this.createBlock();
} catch (e) {
this.running = false;
this.emit('error', e);
return;
}
this.attempt = attempt;
if (!this.running)
return;
try {
block = yield attempt.mineAsync();
} catch (e) {
this.emit('error', e);
this.restart();
return;
this.attempt = attempt;
try {
block = yield attempt.mineAsync();
} catch (e) {
this.emit('error', e);
this.restart();
return;
}
}
try {
@ -193,7 +198,7 @@ Miner.prototype.restart = function restart() {
Miner.prototype.createBlock = co(function* createBlock(tip) {
var version = this.version;
var i, ts, attempt, txs, tx, target;
var ts, attempt, target, entries;
if (!tip)
tip = this.chain.tip;
@ -211,21 +216,16 @@ Miner.prototype.createBlock = co(function* createBlock(tip) {
tip: tip,
version: version,
bits: target,
flags: this.chain.state.flags,
address: this.address,
coinbaseFlags: this.coinbaseFlags,
witness: this.chain.state.hasWitness(),
network: this.network
});
if (!this.mempool)
return attempt;
entries = this.getSorted();
txs = this.mempool.getHistory();
for (i = 0; i < txs.length; i++) {
tx = txs[i];
attempt.addTX(tx);
}
attempt.build(entries);
return attempt;
});
@ -250,8 +250,99 @@ Miner.prototype.addTX = function addTX(tx) {
if (!this.running)
return;
if (this.attempt)
this.attempt.addTX(tx.clone());
if (!this.attempt)
return;
this.attempt.addTX(tx);
};
/**
* Notify the miner that a new tx has entered the mempool.
* @param {MempoolEntry} entry
*/
Miner.prototype.notifyEntry = function notifyEntry() {
if (!this.running)
return;
if (!this.attempt)
return;
this.attempt.since++;
};
/**
* Create a block "attempt".
* @param {ChainEntry} tip
* @returns {Promise} - Returns {@link MinerBlock}.
*/
Miner.prototype.getSorted = function getSorted() {
var depMap = {};
var count = {};
var result = [];
var top = [];
var i, j, entry, tx, hash, input;
var prev, hasDeps, deps, hashes;
if (!this.mempool)
return [];
hashes = this.mempool.getSnapshot();
for (i = 0; i < hashes.length; i++) {
hash = hashes[i];
entry = this.mempool.getEntry(hash);
tx = entry.tx;
count[hash] = 0;
hasDeps = false;
for (j = 0; j < tx.inputs.length; j++) {
input = tx.inputs[j];
prev = input.prevout.hash;
if (!this.mempool.hasTX(prev))
continue;
hasDeps = true;
if (!depMap[prev])
depMap[prev] = [];
depMap[prev].push(entry);
count[hash]++;
}
if (hasDeps)
continue;
top.push(entry);
}
for (i = 0; i < top.length; i++) {
entry = top[i];
tx = entry.tx;
hash = tx.hash('hex');
result.push(entry);
deps = depMap[hash];
if (!deps)
continue;
for (j = 0; j < deps.length; j++) {
entry = deps[j];
tx = entry.tx;
hash = tx.hash('hex');
if (--count[hash] === 0)
result.push(entry);
}
}
return result;
};
/*

View File

@ -53,6 +53,7 @@ function MinerBlock(options) {
this.height = options.tip.height + 1;
this.bits = options.bits;
this.target = utils.fromCompact(this.bits).toArrayLike(Buffer, 'le', 32);
this.flags = options.flags;
this.extraNonce = new bn(0);
this.iterations = 0;
this.coinbaseFlags = options.coinbaseFlags;
@ -60,6 +61,10 @@ function MinerBlock(options) {
this.address = options.address;
this.network = Network.get(options.network);
this.destroyed = false;
this.since = 0;
this.sigops = 0;
this.weight = 0;
this.coinbase = new TX();
this.coinbase.mutable = true;
@ -241,23 +246,27 @@ MinerBlock.prototype.updateMerkle = function updateMerkle() {
*/
MinerBlock.prototype.addTX = function addTX(tx) {
var weight;
var weight, sigops;
assert(!tx.mutable, 'Cannot add mutable TX to block.');
weight = this.block.getWeight() + tx.getWeight();
if (weight > constants.block.MAX_WEIGHT)
if (this.block.hasTX(tx))
return false;
if (this.block.hasTX(tx))
weight = tx.getWeight();
sigops = tx.getSigopsWeight(this.flags);
if (this.weight + weight > constants.block.MAX_WEIGHT)
return false;
if (this.sigops + sigops > constants.block.MAX_SIGOPS_WEIGHT)
return false;
if (!this.witness && tx.hasWitness())
return false;
// Add the tx to our block
this.block.addTX(tx);
this.block.addTX(tx.clone());
// Update coinbase value
this.updateCoinbase();
@ -268,6 +277,47 @@ MinerBlock.prototype.addTX = function addTX(tx) {
return true;
};
/**
* Fill the block with sorted mempool entries.
* @param {MempoolEntry[]} entries
*/
MinerBlock.prototype.build = function build(entries) {
var len = Math.min(entries.length, constants.block.MAX_SIZE);
var i, entry, tx, weight, sigops;
this.weight = this.block.getWeight() + 12;
this.sigops = this.coinbase.getSigopsWeight(this.flags);
for (i = 0; i < len; i++) {
entry = entries[i];
tx = entry.tx;
weight = tx.getWeight();
sigops = tx.getSigopsWeight(this.flags);
if (this.weight + weight > constants.block.MAX_WEIGHT)
break;
if (this.sigops + sigops > constants.block.MAX_SIGOPS_WEIGHT)
break;
this.weight += weight;
this.sigops += sigops;
// Add the tx to our block
this.block.addTX(tx.clone());
}
// Update coinbase value
this.updateCoinbase();
// Update merkle root for new coinbase and new tx
this.updateMerkle();
assert(this.block.getWeight() <= this.weight);
};
/**
* Hash until the nonce overflows.
* @returns {Boolean} Whether the nonce was found.
@ -322,6 +372,9 @@ MinerBlock.prototype.findNonceAsync = co(function* findNonceAsync() {
if (nonce !== -1)
break;
if (this.since > 20)
return nonce;
block.nonce = max;
min += interval;
@ -381,6 +434,9 @@ MinerBlock.prototype.mineAsync = co(function* mineAsync() {
if (nonce !== -1)
break;
if (this.since > 20)
return;
this.iterate();
}

View File

@ -199,7 +199,7 @@ Fullnode.prototype._init = function _init() {
this.mempool.on('tx', function(tx) {
self.emit('tx', tx);
self.walletdb.addTX(tx).catch(onError);
self.miner.addTX(tx);
self.miner.notifyEntry();
});
this.chain.on('block', function(block) {