From c56640a4333064f1043da2b04dc30d47d6eae693 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Wed, 6 Jan 2016 19:57:35 -0800 Subject: [PATCH] various fixes for our miner. --- lib/bcoin.js | 1 + lib/bcoin/chain.js | 13 ++++-- lib/bcoin/miner.js | 106 +++++++++++++++++++++++++++++++++++---------- lib/bcoin/pool.js | 14 +++--- 4 files changed, 100 insertions(+), 34 deletions(-) diff --git a/lib/bcoin.js b/lib/bcoin.js index 419f9fd8..8dbf56c9 100644 --- a/lib/bcoin.js +++ b/lib/bcoin.js @@ -25,6 +25,7 @@ bcoin.wallet = require('./bcoin/wallet'); bcoin.peer = require('./bcoin/peer'); bcoin.pool = require('./bcoin/pool'); bcoin.hd = require('./bcoin/hd'); +bcoin.miner = require('./bcoin/miner'); bcoin.protocol.network.set(process.env.BCOIN_NETWORK || 'main'); diff --git a/lib/bcoin/chain.js b/lib/bcoin/chain.js index 5f8e3319..82b1b990 100644 --- a/lib/bcoin/chain.js +++ b/lib/bcoin/chain.js @@ -110,15 +110,20 @@ Chain.prototype._init = function _init() { var self = this; var s; - if (!this.storage) + this.loading = true; + + if (!this.storage) { + utils.nextTick(function() { + self.loading = false; + self.emit('load'); + }); return; + } utils.nextTick(function() { self.emit('debug', 'Chain is loading.'); }); - this.loading = true; - s = this.storage.createReadStream({ start: this.prefix, end: this.prefix + 'z' @@ -681,7 +686,7 @@ Chain.prototype._compact = function _compact(keep) { var entries = this._filter(this.index.entries); if (!keep) - keep = 1000; + keep = network.powDiffInterval + 10; // Keep only last 1000 consequent blocks, dilate others at: // 7 day range for blocks before 2013 diff --git a/lib/bcoin/miner.js b/lib/bcoin/miner.js index 8bea3ba7..8fa76929 100644 --- a/lib/bcoin/miner.js +++ b/lib/bcoin/miner.js @@ -13,6 +13,8 @@ var bn = require('bn.js'); var inherits = require('inherits'); var EventEmitter = require('events').EventEmitter; +var crypto = require('crypto'); + /** * Miner */ @@ -30,8 +32,8 @@ function Miner(options) { this.address = this.options.address; this.msg = this.options.msg || 'mined by bcoin'; - this.chain = options.chain || bcoin.chain.global; this.pool = options.pool || bcoin.pool.global; + this.chain = options.chain || this.pool.chain || bcoin.chain.global; this.running = false; this.timeout = null; @@ -40,7 +42,9 @@ function Miner(options) { this.fee = new bn(0); this.last = this.chain.getTip(); this.block = null; - this.rate = 0; + this._begin = utils.now(); + + this._init(); } inherits(Miner, EventEmitter); @@ -61,15 +65,23 @@ Miner.prototype._init = function _init() { // }); this.on('block', function(block) { - self.emit('debug', + self.chain.emit('debug', 'Found block: %d (%s)', - self.last.height + 1, + block.height, block.hash('hex')); + // Emit the block hex as a failsafe (in case we can't send it) + self.chain.emit('debug', 'Block: %s', utils.toHex(block.render())); self.pool.sendBlock(block); }); this.on('status', function(stat) { - self.emit('debug', 'Hashes per second: %s', stat.hashrate); + self.chain.emit('debug', + 'hashrate=%dkhs hashes=%d target=%d height=%d best=%s', + stat.hashrate / 1000 | 0, + stat.hashes, + stat.target, + stat.height, + stat.best); }); }; @@ -154,7 +166,7 @@ Miner.prototype.addTX = function addTX(tx) { // Add the tx to our block this.block.txs.push(tx); - // Calulcate our new reward fee + // Calculate our new reward fee if (full) this.fee.iadd(tx.getFee()); @@ -202,7 +214,7 @@ Miner.prototype.createBlock = function createBlock(tx) { : this.last.hash, merkleRoot: utils.toHex(constants.zeroHash.slice()), ts: ts, - bits: utils.toCompact(target), + bits: target, nonce: 0 }; @@ -212,20 +224,21 @@ Miner.prototype.createBlock = function createBlock(tx) { block.txs.push(coinbase); + block.target = utils.fromCompact(target); + block.extraNonce = new bn(0); + // Update coinbase since our coinbase was added. this.updateCoinbase(block); // Create our merkle root. this.updateMerkle(block); - block.target = target; - block.extraNonce = new bn(0); - return block; }; Miner.prototype.updateCoinbase = function updateCoinbase(block) { var coinbase = block.coinbase; + var reward = bcoin.block.reward(this.last.height + 1); assert(coinbase); @@ -233,7 +246,7 @@ Miner.prototype.updateCoinbase = function updateCoinbase(block) { block = this.block; coinbase.inputs[0].script[1] = block.extraNonce.toArray(); - coinbase.outputs[0].value = bcoin.block.reward(this.last.height + 1).add(fee); + coinbase.outputs[0].value = reward.add(this.fee); }; Miner.prototype.updateMerkle = function updateMerkle(block) { @@ -277,19 +290,62 @@ Miner.prototype.iterate = function iterate() { }, 10); }; +Miner.prototype.__defineGetter__('hashes', function() { + return this.block.extraNonce.muln(0xffffffff).addn(this.block.nonce); +}); + +Miner.prototype.__defineGetter__('rate', function() { + if (!this.block.nonce) + return 0; + // Calculate our terrible hashrate + return (this.block.nonce / (utils.now() - this._begin)) * 2 | 0; +}); + +Miner.prototype.sendStatus = function sendStatus() { + this.emit('status', { + block: this.block, + target: this.block.bits, + hashes: this.hashes.toString(10), + hashrate: this.rate, + height: this.last.height + 1, + best: utils.revHex(this.last.hash) + }); +}; + Miner.prototype.findNonce = function findNonce() { - var begin = utils.now(); + var data = new Buffer(this.block.render()); + var now; + + this._begin = utils.now(); // The heart and soul of the miner: match the target. while (this.block.nonce <= 0xffffffff) { - if (utils.testTarget(this.block.target, this.block.hash())) + // Hash and test against the next target + if (utils.testTarget(this.block.target, dsha256(data))) return true; + // Increment the nonce to get a different hash this.block.nonce++; + utils.writeU32(data, this.block.nonce, 76); + + // Send progress report every so often + if (this.block.nonce % 100000 === 0) + this.sendStatus(); } - // Calculate our terrible hashrate - this.rate = (0xffffffff / (utils.now() - begin)) * 2; + // Send progress report + this.sendStatus(); + + // If we took more a second or more (likely), + // skip incrementing the extra nonce and just + // update the timestamp. This improves + // performance because we do not have to + // recalculate the merkle root. + now = utils.now(); + if (now > this.block.ts) { + this.block.ts = now; + return false; + } // Overflow the nonce and increment the extraNonce. this.block.nonce = 0; @@ -301,17 +357,21 @@ Miner.prototype.findNonce = function findNonce() { // We changed the coinbase, need to update merkleRoot. this.updateMerkle(); - // Send progress report - this.emit('status', { - block: this.block, - target: this.block.target, - hashes: this.block.extraNonce.mul(0xffffffff).toString(10), - hashrate: this.rate - }); - return false; }; +/** + * Utils + */ + +function sha256(data, enc) { + return crypto.createHash('sha256').update(data).digest(); +} + +function dsha256(data, enc) { + return Array.prototype.slice.call(sha256(sha256(data, enc))); +} + /** * Expose */ diff --git a/lib/bcoin/pool.js b/lib/bcoin/pool.js index 841e0b03..84aa8837 100644 --- a/lib/bcoin/pool.js +++ b/lib/bcoin/pool.js @@ -149,13 +149,13 @@ function Pool(options) { Pool.global = this; - if (!this.chain.loading) { - this._init(); - } else { - this.chain.once('load', function() { - self._init(); - }); - } + this.loading = true; + + this.chain.once('load', function() { + self.loading = false; + self.emit('load'); + self._init(); + }); } inherits(Pool, EventEmitter);