diff --git a/lib/miner/miner.js b/lib/miner/miner.js index 6bafa6be..355cf71d 100644 --- a/lib/miner/miner.js +++ b/lib/miner/miner.js @@ -23,7 +23,7 @@ var time = require('../net/timedata'); * @param {Base58Address} options.address - Payout address. * @param {String?} [options.coinbaseFlags="mined by bcoin"] * @property {Boolean} running - * @property {Boolean} loaded + * @property {MinerBlock} attempt * @emits Miner#block * @emits Miner#status */ @@ -34,30 +34,21 @@ function Miner(options) { AsyncObject.call(this); - if (!options) - options = {}; + assert(options, 'Miner requires options.'); + assert(options.chain, 'Miner requires a blockchain.'); - this.options = options; - this.address = Address(this.options.address); - this.coinbaseFlags = this.options.coinbaseFlags || 'mined by bcoin'; - this.version = null; - - if (typeof this.coinbaseFlags === 'string') - this.coinbaseFlags = new Buffer(this.coinbaseFlags, 'utf8'); - - this.pool = options.pool; this.chain = options.chain; - this.logger = options.logger || this.chain.logger; this.mempool = options.mempool; - this.fees = this.mempool ? this.mempool.fees : options.fees; - - assert(this.chain, 'Miner requires a blockchain.'); - this.network = this.chain.network; + this.logger = options.logger || this.chain.logger; + this.running = false; - this.timeout = null; this.attempt = null; + this.version = -1; + this.address = Address(options.address); + this.coinbaseFlags = options.coinbaseFlags || 'mined by bcoin'; + this._init(); } @@ -71,29 +62,8 @@ utils.inherits(Miner, AsyncObject); Miner.prototype._init = function _init() { var self = this; - if (this.mempool) { - this.mempool.on('tx', function(tx) { - if (!self.running) - return; - if (self.attempt) - self.attempt.addTX(tx.clone()); - }); - } else if (this.pool) { - this.pool.on('tx', function(tx) { - if (!self.running) - return; - if (self.attempt) - self.attempt.addTX(tx.clone()); - }); - } - this.chain.on('tip', function(tip) { - if (!self.running) - return; - self.stop(); - setTimeout(function() { - self.start(); - }, self.network.type === 'regtest' ? 100 : 5000); + self.restart(); }); this.on('block', function(block) { @@ -120,10 +90,10 @@ Miner.prototype._init = function _init() { */ Miner.prototype._open = co(function* open() { + yield this.chain.open(); + if (this.mempool) yield this.mempool.open(); - else - yield this.chain.open(); this.logger.info('Miner loaded (flags=%s).', this.coinbaseFlags.toString('utf8')); @@ -145,17 +115,16 @@ Miner.prototype._close = function close() { */ Miner.prototype.start = co(function* start() { - var self = this; var attempt, block; - this.stop(); + assert(!this.running, 'Miner is already running.'); this.running = true; - // Create a new block and start hashing try { attempt = yield this.createBlock(); } catch (e) { + this.running = false; this.emit('error', e); return; } @@ -165,35 +134,23 @@ Miner.prototype.start = co(function* start() { this.attempt = attempt; - attempt.on('status', function(status) { - self.emit('status', status); - }); - try { block = yield attempt.mineAsync(); } catch (e) { - if (!this.running) - return; this.emit('error', e); - return this.start(); - } - - // Add our block to the chain - try { - yield this.chain.add(block); - } catch (err) { - if (err.type === 'VerifyError') - this.logger.warning('%s could not be added to chain.', block.rhash); - this.emit('error', err); - this.start(); + this.restart(); return; } - // Emit our newly found block - this.emit('block', block); + try { + yield this.chain.add(block); + } catch (e) { + this.emit('error', e); + this.restart(); + return; + } - // `tip` will now be emitted by chain - // and the whole process starts over. + this.emit('block', block); }); /** @@ -201,8 +158,7 @@ Miner.prototype.start = co(function* start() { */ Miner.prototype.stop = function stop() { - if (!this.running) - return; + assert(this.running, 'Miner is not running.'); this.running = false; @@ -212,17 +168,32 @@ Miner.prototype.stop = function stop() { } }; +/** + * Restart miner. + */ + +Miner.prototype.restart = function restart() { + var self = this; + + if (!this.running) + return; + + this.stop(); + + setTimeout(function() { + self.start(); + }, 500); +}; + /** * Create a block "attempt". - * @param {Number?} version - Custom block version. + * @param {ChainEntry} tip * @returns {Promise} - Returns {@link MinerBlock}. */ Miner.prototype.createBlock = co(function* createBlock(tip) { - var i, ts, attempt, txs, tx, target, version; - - if (!this.loaded) - yield this.open(); + var version = this.version; + var i, ts, attempt, txs, tx, target; if (!tip) tip = this.chain.tip; @@ -231,23 +202,18 @@ Miner.prototype.createBlock = co(function* createBlock(tip) { ts = Math.max(time.now(), tip.ts + 1); - // Find target target = yield this.chain.getTargetAsync(ts, tip); - if (this.version != null) { - version = this.version; - } else { - // Calculate version with versionbits + if (version === -1) version = yield this.chain.computeBlockVersion(tip); - } attempt = new MinerBlock({ tip: tip, version: version, - target: target, + bits: target, address: this.address, coinbaseFlags: this.coinbaseFlags, - witness: this.chain.segwitActive, + witness: this.chain.state.hasWitness(), network: this.network }); @@ -266,16 +232,28 @@ Miner.prototype.createBlock = co(function* createBlock(tip) { /** * Mine a single block. - * @param {Number?} version - Custom block version. + * @param {ChainEntry} tip * @returns {Promise} - Returns [{@link Block}]. */ Miner.prototype.mineBlock = co(function* mineBlock(tip) { - // Create a new block and start hashing var attempt = yield this.createBlock(tip); return yield attempt.mineAsync(); }); +/** + * Add a transaction to the current block. + * @param {TX} tx + */ + +Miner.prototype.addTX = function addTX(tx) { + if (!this.running) + return; + + if (this.attempt) + this.attempt.addTX(tx.clone()); +}; + /* * Expose */ diff --git a/lib/miner/minerblock.js b/lib/miner/minerblock.js index d77c3350..25b1c7af 100644 --- a/lib/miner/minerblock.js +++ b/lib/miner/minerblock.js @@ -48,11 +48,10 @@ function MinerBlock(options) { EventEmitter.call(this); - this.options = options; - this.workerPool = options.workerPool; this.tip = options.tip; + this.version = options.version; this.height = options.tip.height + 1; - this.bits = options.target; + this.bits = options.bits; this.target = utils.fromCompact(this.bits).toArrayLike(Buffer, 'le', 32); this.extraNonce = new bn(0); this.iterations = 0; @@ -95,10 +94,9 @@ MinerBlock.prototype.__defineGetter__('rate', function() { */ MinerBlock.prototype._init = function _init() { - var options = this.options; var block = this.block; var cb = this.coinbase; - var i, input, output, hash, witnessNonce; + var input, output, hash, witnessNonce; // Coinbase input. input = new Input(); @@ -148,7 +146,7 @@ MinerBlock.prototype._init = function _init() { } // Setup our block. - block.version = options.version; + block.version = this.version; block.prevBlock = this.tip.hash; block.merkleRoot = constants.NULL_HASH; block.ts = Math.max(time.now(), this.tip.ts + 1); @@ -158,11 +156,6 @@ MinerBlock.prototype._init = function _init() { block.addTX(cb); - if (options.txs) { - for (i = 0; i < options.txs.length; i++) - block.addTX(options.txs[i]); - } - // Update coinbase since our coinbase was added. this.updateCoinbase(); diff --git a/lib/node/fullnode.js b/lib/node/fullnode.js index 74501ce6..2767940f 100644 --- a/lib/node/fullnode.js +++ b/lib/node/fullnode.js @@ -199,6 +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); }); this.chain.on('block', function(block) {