miner needs some love.
This commit is contained in:
parent
af6ac736bf
commit
46a47b5bf2
@ -169,6 +169,7 @@ Chain.prototype._init = function _init() {
|
||||
|
||||
self.loaded = true;
|
||||
self.emit('open');
|
||||
self.emit('tip', tip);
|
||||
|
||||
if (self.isFull())
|
||||
self.emit('full');
|
||||
@ -888,6 +889,7 @@ Chain.prototype._setBestChain = function _setBestChain(entry, block, callback) {
|
||||
|
||||
self.tip = entry;
|
||||
self.height = entry.height;
|
||||
self.emit('tip', entry);
|
||||
|
||||
return callback();
|
||||
});
|
||||
|
||||
@ -28,6 +28,7 @@ function Miner(node, options) {
|
||||
this.options = options;
|
||||
this.address = this.options.address;
|
||||
this.coinbaseFlags = this.options.coinbaseFlags || 'mined by bcoin';
|
||||
this.dsha256 = this.options.dsha256 || utils.dsha256;
|
||||
|
||||
this.node = node;
|
||||
this.pool = node.pool;
|
||||
@ -36,7 +37,6 @@ function Miner(node, options) {
|
||||
|
||||
this.running = false;
|
||||
this.timeout = null;
|
||||
this.interval = null;
|
||||
|
||||
this.fee = new bn(0);
|
||||
this.last = this.node.chain.tip;
|
||||
@ -66,10 +66,11 @@ Miner.prototype._init = function _init() {
|
||||
});
|
||||
}
|
||||
|
||||
this.chain.on('add block', function(block) {
|
||||
this.chain.on('tip', function(tip) {
|
||||
if (!self.running)
|
||||
return;
|
||||
self.addBlock(block);
|
||||
self.last = tip;
|
||||
self.start();
|
||||
});
|
||||
|
||||
this.on('block', function(block) {
|
||||
@ -78,7 +79,7 @@ Miner.prototype._init = function _init() {
|
||||
block.height,
|
||||
block.hash('hex'));
|
||||
// Emit the block hex as a failsafe (in case we can't send it)
|
||||
utils.debug('Block: %s', utils.toHex(block.render()));
|
||||
utils.debug('Raw: %s', utils.toHex(block.render()));
|
||||
self.pool.sendBlock(block);
|
||||
});
|
||||
|
||||
@ -94,21 +95,54 @@ Miner.prototype._init = function _init() {
|
||||
};
|
||||
|
||||
Miner.prototype.start = function start() {
|
||||
var mempool = this.pool.loadMempool.bind(this.pool);
|
||||
var self = this;
|
||||
|
||||
// Wait for `tip`.
|
||||
if (!this.last)
|
||||
return;
|
||||
|
||||
this.stop();
|
||||
|
||||
this.running = true;
|
||||
|
||||
// Ask our peers for mempool txs every so often.
|
||||
this.interval = setInterval(mempool, 60 * 1000);
|
||||
|
||||
// Reset iterations
|
||||
this.iterations = 0;
|
||||
|
||||
// Create a new block and start hashing
|
||||
this.block = this.createBlock();
|
||||
this.iterate();
|
||||
this.createBlock(function(err, block) {
|
||||
if (err)
|
||||
return self.emit('error', err);
|
||||
|
||||
self.block = block;
|
||||
|
||||
if (!self.mempool)
|
||||
return self.iterate();
|
||||
|
||||
self.mempool.getSnapshot(function(err, hashes) {
|
||||
if (err)
|
||||
return self.emit('error', err);
|
||||
|
||||
utils.forEachSerial(hashes, function(hash, next) {
|
||||
self.mempool.getTX(hash, function(err, tx) {
|
||||
if (err)
|
||||
return next(err);
|
||||
|
||||
self.mempool.fillCoins(tx, function(err) {
|
||||
if (err)
|
||||
return next(err);
|
||||
|
||||
self.addTX(tx);
|
||||
next();
|
||||
});
|
||||
});
|
||||
}, function(err) {
|
||||
if (err)
|
||||
return self.emit('error', err);
|
||||
|
||||
self.iterate();
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Miner.prototype.stop = function stop() {
|
||||
@ -119,152 +153,110 @@ Miner.prototype.stop = function stop() {
|
||||
|
||||
clearTimeout(this.timeout);
|
||||
this.timeout = null;
|
||||
|
||||
clearInterval(this.interval);
|
||||
this.interval = null;
|
||||
};
|
||||
|
||||
Miner.prototype.add = function add(msg) {
|
||||
if (msg.type === 'tx')
|
||||
return this.addTX(msg);
|
||||
return this.addBlock(msg);
|
||||
};
|
||||
|
||||
Miner.prototype.addBlock = function addBlock(block) {
|
||||
if (!block)
|
||||
block = this.chain.tip;
|
||||
|
||||
if (!this.last)
|
||||
this.last = this.chain.tip;
|
||||
|
||||
// Somebody found the next block before
|
||||
// us, start over with the new target.
|
||||
if (block.height > this.last.height) {
|
||||
this.last = block.type === 'block'
|
||||
? this.chain.getEntry(block)
|
||||
: block;
|
||||
assert(this.last);
|
||||
this.start();
|
||||
}
|
||||
};
|
||||
|
||||
Miner.prototype.addTX = function addTX(tx) {
|
||||
var flags, height, ts;
|
||||
|
||||
flags = constants.flags.MANDATORY_VERIFY_FLAGS
|
||||
| constants.flags.VERIFY_DERSIG
|
||||
| constants.flags.VERIFY_CHECKLOCKTIMEVERIFY;
|
||||
|
||||
// Cannot calculate fee if we don't have the prev_out.
|
||||
// Could possibly just burn some coins.
|
||||
if (this.options.burn === false) {
|
||||
if (!tx.hasCoins())
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ignore if it's already in a block
|
||||
if (tx.height !== -1)
|
||||
return false;
|
||||
|
||||
if (!tx.verify(null, true, flags))
|
||||
return false;
|
||||
|
||||
if (tx.isCoinbase())
|
||||
return false;
|
||||
|
||||
// Get timestamp for tx.isFinal() - bip113
|
||||
height = this.last.height + 1;
|
||||
ts = this.block.version >= 8
|
||||
? this.last.getMedianTime()
|
||||
: this.block.ts;
|
||||
|
||||
if (!tx.isFinal(height, ts))
|
||||
return false;
|
||||
var size = this.block.getVirtualSize() + tx.getVirtualSize();
|
||||
|
||||
// Deliver me from the block size debate, please
|
||||
if (this.block.getSize() + tx.getSize() > constants.blocks.maxSize)
|
||||
if (size > constants.blocks.maxSize)
|
||||
return false;
|
||||
|
||||
if (this.block._txMap[tx.hash('hex')])
|
||||
return false;
|
||||
|
||||
// Add the tx to our block
|
||||
this.block.txs.push(tx);
|
||||
this.block._txMap[tx.hash('hex')] = true;
|
||||
|
||||
// Calculate our new reward fee
|
||||
if (tx.hasCoins())
|
||||
this.fee.iadd(tx.getFee());
|
||||
this.fee.iadd(tx.getFee());
|
||||
|
||||
// Update coinbase value
|
||||
this.updateCoinbase();
|
||||
|
||||
// Update merkle root for new coinbase and new tx
|
||||
this.updateMerkle();
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
Miner.prototype.createBlock = function createBlock() {
|
||||
Miner.prototype.createBlock = function createBlock(callback) {
|
||||
var self = this;
|
||||
var ts, target, coinbase, headers, block;
|
||||
|
||||
ts = Math.max(utils.now(), this.last.ts + 1);
|
||||
|
||||
// Find target
|
||||
target = this.chain.getTarget(this.last, ts);
|
||||
this.last.ensureAncestors(function(err) {
|
||||
if (err) {
|
||||
self.last.free();
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
// Create a coinbase
|
||||
coinbase = bcoin.tx();
|
||||
target = self.chain.getTarget(self.last, ts);
|
||||
|
||||
coinbase.input({
|
||||
prevout: {
|
||||
hash: utils.toHex(constants.zeroHash),
|
||||
index: 0xffffffff
|
||||
},
|
||||
script: [
|
||||
// Height (required in v2+ blocks)
|
||||
bcoin.script.array(this.last.height + 1),
|
||||
// extraNonce - incremented when
|
||||
// the nonce overflows.
|
||||
0,
|
||||
// Add a nonce to ensure we don't
|
||||
// collide with a previous coinbase
|
||||
// of ours. This isn't really
|
||||
// necessary nowdays due to bip34
|
||||
// (used above).
|
||||
utils.nonce().toBuffer(),
|
||||
// Let the world know this little
|
||||
// miner succeeded.
|
||||
new Buffer(this.coinbaseFlags || 'mined by bcoin', 'ascii')
|
||||
],
|
||||
sequence: 0xffffffff
|
||||
// Create a coinbase
|
||||
coinbase = bcoin.mtx();
|
||||
|
||||
coinbase.addInput({
|
||||
prevout: {
|
||||
hash: utils.toHex(constants.zeroHash),
|
||||
index: 0xffffffff
|
||||
},
|
||||
script: new bcoin.script([
|
||||
// Height (required in v2+ blocks)
|
||||
bcoin.script.array(self.last.height + 1),
|
||||
// extraNonce - incremented when
|
||||
// the nonce overflows.
|
||||
new Buffer([0]),
|
||||
// Add a nonce to ensure we don't
|
||||
// collide with a previous coinbase
|
||||
// of ours. This isn't really
|
||||
// necessary nowdays due to bip34
|
||||
// (used above).
|
||||
utils.nonce().toBuffer(),
|
||||
// Let the world know this little
|
||||
// miner succeeded.
|
||||
new Buffer(self.coinbaseFlags || 'mined by bcoin', 'ascii')
|
||||
]),
|
||||
witness: new bcoin.script.witness([]),
|
||||
sequence: 0xffffffff
|
||||
});
|
||||
|
||||
coinbase.addOutput({
|
||||
address: self.address,
|
||||
value: new bn(0)
|
||||
});
|
||||
|
||||
// Create our block
|
||||
headers = {
|
||||
version: 4,
|
||||
prevBlock: self.last.hash,
|
||||
merkleRoot: utils.toHex(constants.zeroHash),
|
||||
ts: ts,
|
||||
bits: target,
|
||||
nonce: 0
|
||||
};
|
||||
|
||||
block = bcoin.block(headers, 'block');
|
||||
block._txMap = {};
|
||||
|
||||
block.txs.push(coinbase);
|
||||
|
||||
block.target = utils.fromCompact(target);
|
||||
block.extraNonce = new bn(0, 'le');
|
||||
|
||||
// Update coinbase since our coinbase was added.
|
||||
self.updateCoinbase(block);
|
||||
|
||||
// Create our merkle root.
|
||||
self.updateMerkle(block);
|
||||
|
||||
self.last.free();
|
||||
|
||||
return callback(null, block);
|
||||
});
|
||||
|
||||
coinbase.addOutput({
|
||||
address: this.address,
|
||||
value: new bn(0)
|
||||
});
|
||||
|
||||
// Create our block
|
||||
headers = {
|
||||
version: 4,
|
||||
prevBlock: this.last.hash,
|
||||
merkleRoot: utils.toHex(constants.zeroHash),
|
||||
ts: ts,
|
||||
bits: target,
|
||||
nonce: 0
|
||||
};
|
||||
|
||||
block = bcoin.block(headers, 'block');
|
||||
|
||||
delete block.valid;
|
||||
|
||||
block.txs.push(coinbase);
|
||||
|
||||
block.target = utils.fromCompact(target);
|
||||
block.extraNonce = new bn(0, 'le');
|
||||
|
||||
// Update coinbase since our coinbase was added.
|
||||
this.updateCoinbase(block);
|
||||
|
||||
// Create our merkle root.
|
||||
this.updateMerkle(block);
|
||||
|
||||
return block;
|
||||
};
|
||||
|
||||
Miner.prototype.updateCoinbase = function updateCoinbase(block) {
|
||||
@ -276,8 +268,12 @@ Miner.prototype.updateCoinbase = function updateCoinbase(block) {
|
||||
if (!block)
|
||||
block = this.block;
|
||||
|
||||
delete block._txMap[coinbase.hash('hex')];
|
||||
|
||||
coinbase.inputs[0].script[1] = block.extraNonce.toBuffer();
|
||||
coinbase.outputs[0].value = reward.add(this.fee);
|
||||
|
||||
block._txMap[coinbase.hash('hex')] = true;
|
||||
};
|
||||
|
||||
Miner.prototype.updateMerkle = function updateMerkle(block) {
|
||||
@ -301,24 +297,29 @@ Miner.prototype.iterate = function iterate() {
|
||||
hash = self.block.hash('hex');
|
||||
|
||||
// Make sure our block is valid
|
||||
if (!self.block.verify())
|
||||
return utils.debug('%s did not verify.', hash);
|
||||
if (!self.block.verify()) {
|
||||
utils.debug('%s did not verify.', hash);
|
||||
return self.emit('error', new Error(hash + ' did not verify.'));
|
||||
}
|
||||
|
||||
// Add our block to the chain
|
||||
res = self.chain.add(self.block);
|
||||
if (res > 0)
|
||||
return utils.debug('%s could not be added to chain.', hash);
|
||||
self.chain.add(self.block, self.pool.peers.load, function(err, total) {
|
||||
if (err)
|
||||
return self.emit('error', err);
|
||||
|
||||
// Emit our newly found block
|
||||
self.emit('block', self.block);
|
||||
if (total === 0) {
|
||||
utils.debug('%s could not be added to chain.', hash);
|
||||
return self.emit('error',
|
||||
new Error(hash + ' could not be added to chain.'));
|
||||
}
|
||||
|
||||
// Try to find a new block
|
||||
self.last = self.chain.getEntry(self.block);
|
||||
assert(self.last);
|
||||
self.block = self.createBlock();
|
||||
// Emit our newly found block
|
||||
self.emit('block', self.block);
|
||||
|
||||
return self.iterate();
|
||||
}, 10);
|
||||
// `tip` will now be emitted by chain
|
||||
// and the whole process starts over.
|
||||
});
|
||||
}, 100);
|
||||
};
|
||||
|
||||
Miner.prototype.__defineGetter__('hashes', function() {
|
||||
@ -344,7 +345,7 @@ Miner.prototype.sendStatus = function sendStatus() {
|
||||
};
|
||||
|
||||
Miner.prototype.findNonce = function findNonce() {
|
||||
var data = new Buffer(this.block.render());
|
||||
var data = this.block.abbr();
|
||||
var now;
|
||||
|
||||
// Track how long we've been at it.
|
||||
@ -353,7 +354,7 @@ Miner.prototype.findNonce = function findNonce() {
|
||||
// The heart and soul of the miner: match the target.
|
||||
while (this.block.nonce <= 0xffffffff) {
|
||||
// Hash and test against the next target
|
||||
if (utils.testTarget(this.block.target, utils.dsha256(data)))
|
||||
if (utils.testTarget(this.block.target, this.dsha256(data)))
|
||||
return true;
|
||||
|
||||
// Increment the nonce to get a different hash
|
||||
|
||||
Loading…
Reference in New Issue
Block a user