From 10bb8d1e18be1bd7396178cce6fd38209a2c19b1 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Tue, 23 Aug 2016 23:56:50 -0700 Subject: [PATCH] refactor: even more restructuring. --- lib/bcoin/env.js | 4 +- lib/bcoin/mempool/fees.js | 6 +- lib/bcoin/mempool/mempool.js | 10 +- lib/bcoin/miner/miner.js | 336 ++++++++++++++++++++ lib/bcoin/{miner.js => miner/minerblock.js} | 329 +------------------ lib/bcoin/{ => net}/timedata.js | 2 +- lib/bcoin/workers/jobs.js | 80 +++++ lib/bcoin/{ => workers}/workers.js | 75 +---- lib/{bcoin => }/worker.js | 2 +- 9 files changed, 437 insertions(+), 407 deletions(-) create mode 100644 lib/bcoin/miner/miner.js rename lib/bcoin/{miner.js => miner/minerblock.js} (62%) rename lib/bcoin/{ => net}/timedata.js (98%) create mode 100644 lib/bcoin/workers/jobs.js rename lib/bcoin/{ => workers}/workers.js (94%) rename lib/{bcoin => }/worker.js (95%) diff --git a/lib/bcoin/env.js b/lib/bcoin/env.js index d2f5721e..183cac6f 100644 --- a/lib/bcoin/env.js +++ b/lib/bcoin/env.js @@ -132,7 +132,7 @@ function Environment() { this.network = require('./protocol/network'); this.errors = require('./utils/errors'); this.ldb = require('./db/ldb'); - this.timedata = require('./timedata'); + this.timedata = require('./net/timedata'); this.script = require('./primitives/script'); this.opcode = this.script.Opcode; this.stack = this.script.Stack; @@ -172,7 +172,7 @@ function Environment() { this.path = this.walletdb.Path; this.peer = require('./net/peer'); this.pool = require('./net/pool'); - this.miner = require('./miner'); + this.miner = require('./miner/miner'); this.minerblock = this.miner.MinerBlock; this.http = require('./http'); this.workers = require('./workers'); diff --git a/lib/bcoin/mempool/fees.js b/lib/bcoin/mempool/fees.js index d96f6cd8..7e48b315 100644 --- a/lib/bcoin/mempool/fees.js +++ b/lib/bcoin/mempool/fees.js @@ -8,12 +8,12 @@ 'use strict'; -var bcoin = require('./env'); +var bcoin = require('../env'); var utils = bcoin.utils; var assert = require('assert'); var constants = bcoin.protocol.constants; -var BufferReader = require('./utils/reader'); -var BufferWriter = require('./utils/writer'); +var BufferReader = require('../utils/reader'); +var BufferWriter = require('../utils/writer'); var global = bcoin.utils.global; var Float64Array = global.Float64Array || Array; var Int32Array = global.Int32Array || Array; diff --git a/lib/bcoin/mempool/mempool.js b/lib/bcoin/mempool/mempool.js index 2ac3909a..13d627bf 100644 --- a/lib/bcoin/mempool/mempool.js +++ b/lib/bcoin/mempool/mempool.js @@ -12,13 +12,13 @@ * (inherits all from txdb) */ -var bcoin = require('./env'); -var AsyncObject = require('./utils/async'); +var bcoin = require('../env'); +var AsyncObject = require('../utils/async'); var constants = bcoin.protocol.constants; -var utils = require('./utils/utils'); +var utils = require('../utils/utils'); var assert = utils.assert; -var BufferWriter = require('./utils/writer'); -var BufferReader = require('./utils/reader'); +var BufferWriter = require('../utils/writer'); +var BufferReader = require('../utils/reader'); var VerifyError = bcoin.errors.VerifyError; /** diff --git a/lib/bcoin/miner/miner.js b/lib/bcoin/miner/miner.js new file mode 100644 index 00000000..b8e62640 --- /dev/null +++ b/lib/bcoin/miner/miner.js @@ -0,0 +1,336 @@ +/*! + * miner.js - inefficient miner for bcoin (because we can) + * Copyright (c) 2014-2015, Fedor Indutny (MIT License) + * Copyright (c) 2014-2016, Christopher Jeffrey (MIT License). + * https://github.com/bcoin-org/bcoin + */ + +'use strict'; + +var bcoin = require('../env'); +var utils = require('../utils/utils'); +var assert = utils.assert; +var AsyncObject = require('../utils/async'); +var MinerBlock = require('./minerblock'); + +/** + * A bitcoin miner (supports mining witness blocks). + * @exports Miner + * @constructor + * @param {Object} options + * @param {Base58Address} options.address - Payout address. + * @param {String?} [options.coinbaseFlags="mined by bcoin"] + * @property {Boolean} running + * @property {Boolean} loaded + * @emits Miner#block + * @emits Miner#status + */ + +function Miner(options) { + if (!(this instanceof Miner)) + return new Miner(options); + + AsyncObject.call(this); + + if (!options) + options = {}; + + this.options = options; + this.address = bcoin.address(this.options.address); + this.coinbaseFlags = this.options.coinbaseFlags || 'mined by bcoin'; + this.version = null; + + 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.running = false; + this.timeout = null; + this.attempt = null; + this.workerPool = null; + + this._init(); +} + +utils.inherits(Miner, AsyncObject); + +/** + * Initialize the miner. + * @private + */ + +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); + }); + + this.on('block', function(block) { + // Emit the block hex as a failsafe (in case we can't send it) + self.logger.info('Found block: %d (%s).', block.height, block.rhash); + self.logger.debug('Raw: %s', block.toRaw().toString('hex')); + }); + + this.on('status', function(stat) { + self.logger.info( + 'Miner: hashrate=%dkhs hashes=%d target=%d height=%d best=%s', + stat.hashrate / 1000 | 0, + stat.hashes, + stat.target, + stat.height, + stat.best); + }); + + if (bcoin.useWorkers) { + this.workerPool = new bcoin.workers({ + size: this.options.parallel ? 2 : 1, + timeout: -1 + }); + + this.workerPool.on('error', function(err) { + self.emit('error', err); + }); + + this.workerPool.on('status', function(stat) { + self.emit('status', stat); + }); + } +}; + +/** + * Open the miner, wait for the chain and mempool to load. + * @alias Miner#open + * @param {Function} callback + */ + +Miner.prototype._open = function open(callback) { + var self = this; + + function open(callback) { + if (self.mempool) + self.mempool.open(callback); + else + self.chain.open(callback); + } + + open(function(err) { + if (err) + return callback(err); + + self.logger.info('Miner loaded (flags=%s).', self.coinbaseFlags); + + callback(); + }); +}; + +/** + * Close the miner. + * @alias Miner#close + * @param {Function} callback + */ + +Miner.prototype._close = function close(callback) { + callback(); +}; + +/** + * Start mining. + * @param {Number?} version - Custom block version. + */ + +Miner.prototype.start = function start() { + var self = this; + + this.stop(); + + this.running = true; + + // Create a new block and start hashing + this.createBlock(function(err, attempt) { + if (err) + return self.emit('error', err); + + if (!self.running) + return; + + self.attempt = attempt; + + attempt.on('status', function(status) { + self.emit('status', status); + }); + + attempt.mineAsync(function(err, block) { + if (err) { + if (!self.running) + return; + self.emit('error', err); + return self.start(); + } + + // Add our block to the chain + self.chain.add(block, function(err) { + if (err) { + if (err.type === 'VerifyError') + self.logger.warning('%s could not be added to chain.', block.rhash); + self.emit('error', err); + return self.start(); + } + + // Emit our newly found block + self.emit('block', block); + + // `tip` will now be emitted by chain + // and the whole process starts over. + }); + }); + }); +}; + +/** + * Stop mining. + */ + +Miner.prototype.stop = function stop() { + if (!this.running) + return; + + this.running = false; + + if (this.attempt) { + this.attempt.destroy(); + this.attempt = null; + } + + if (this.workerPool) + this.workerPool.destroy(); +}; + +/** + * Create a block "attempt". + * @param {Number?} version - Custom block version. + * @param {Function} callback - Returns [Error, {@link MinerBlock}]. + */ + +Miner.prototype.createBlock = function createBlock(tip, callback) { + var self = this; + var i, ts, attempt, txs, tx; + + if (typeof tip === 'function') { + callback = tip; + tip = null; + } + + if (!tip) + tip = this.chain.tip; + + ts = Math.max(bcoin.now(), tip.ts + 1); + + function computeVersion(callback) { + if (self.version != null) + return callback(null, self.version); + self.chain.computeBlockVersion(tip, callback); + } + + if (!this.loaded) { + this.open(function(err) { + if (err) + return callback(err); + self.createBlock(tip, callback); + }); + return; + } + + assert(tip); + + // Find target + this.chain.getTargetAsync(ts, tip, function(err, target) { + if (err) + return callback(err); + + // Calculate version with versionbits + computeVersion(function(err, version) { + if (err) + return callback(err); + + attempt = new MinerBlock({ + workerPool: self.workerPool, + tip: tip, + version: version, + target: target, + address: self.address, + coinbaseFlags: self.coinbaseFlags, + witness: self.chain.segwitActive, + parallel: self.options.parallel, + network: self.network + }); + + if (!self.mempool) + return callback(null, attempt); + + txs = self.mempool.getHistory(); + + for (i = 0; i < txs.length; i++) { + tx = txs[i]; + attempt.addTX(tx); + } + + callback(null, attempt); + }); + }); +}; + +/** + * Mine a single block. + * @param {Number?} version - Custom block version. + * @param {Function} callback - Returns [Error, [{@link Block}]]. + */ + +Miner.prototype.mineBlock = function mineBlock(tip, callback) { + if (typeof tip === 'function') { + callback = tip; + tip = null; + } + + // Create a new block and start hashing + this.createBlock(tip, function(err, attempt) { + if (err) + return callback(err); + + attempt.mineAsync(callback); + }); +}; + +Miner.MinerBlock = MinerBlock; + +/* + * Expose + */ + +module.exports = Miner; diff --git a/lib/bcoin/miner.js b/lib/bcoin/miner/minerblock.js similarity index 62% rename from lib/bcoin/miner.js rename to lib/bcoin/miner/minerblock.js index 79b6fe67..45a72a61 100644 --- a/lib/bcoin/miner.js +++ b/lib/bcoin/miner/minerblock.js @@ -7,329 +7,14 @@ 'use strict'; -var bcoin = require('./env'); -var utils = require('./utils/utils'); +var bcoin = require('../env'); +var utils = require('../utils/utils'); var assert = utils.assert; var constants = bcoin.protocol.constants; var bn = require('bn.js'); var EventEmitter = require('events').EventEmitter; -var AsyncObject = require('./utils/async'); -var BufferReader = require('./utils/reader'); -var BufferWriter = require('./utils/writer'); - -/** - * A bitcoin miner (supports mining witness blocks). - * @exports Miner - * @constructor - * @param {Object} options - * @param {Base58Address} options.address - Payout address. - * @param {String?} [options.coinbaseFlags="mined by bcoin"] - * @property {Boolean} running - * @property {Boolean} loaded - * @emits Miner#block - * @emits Miner#status - */ - -function Miner(options) { - if (!(this instanceof Miner)) - return new Miner(options); - - AsyncObject.call(this); - - if (!options) - options = {}; - - this.options = options; - this.address = bcoin.address(this.options.address); - this.coinbaseFlags = this.options.coinbaseFlags || 'mined by bcoin'; - this.version = null; - - 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.running = false; - this.timeout = null; - this.attempt = null; - this.workerPool = null; - - this._init(); -} - -utils.inherits(Miner, AsyncObject); - -/** - * Initialize the miner. - * @private - */ - -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); - }); - - this.on('block', function(block) { - // Emit the block hex as a failsafe (in case we can't send it) - self.logger.info('Found block: %d (%s).', block.height, block.rhash); - self.logger.debug('Raw: %s', block.toRaw().toString('hex')); - }); - - this.on('status', function(stat) { - self.logger.info( - 'Miner: hashrate=%dkhs hashes=%d target=%d height=%d best=%s', - stat.hashrate / 1000 | 0, - stat.hashes, - stat.target, - stat.height, - stat.best); - }); - - if (bcoin.useWorkers) { - this.workerPool = new bcoin.workers({ - size: this.options.parallel ? 2 : 1, - timeout: -1 - }); - - this.workerPool.on('error', function(err) { - self.emit('error', err); - }); - - this.workerPool.on('status', function(stat) { - self.emit('status', stat); - }); - } -}; - -/** - * Open the miner, wait for the chain and mempool to load. - * @alias Miner#open - * @param {Function} callback - */ - -Miner.prototype._open = function open(callback) { - var self = this; - - function open(callback) { - if (self.mempool) - self.mempool.open(callback); - else - self.chain.open(callback); - } - - open(function(err) { - if (err) - return callback(err); - - self.logger.info('Miner loaded (flags=%s).', self.coinbaseFlags); - - callback(); - }); -}; - -/** - * Close the miner. - * @alias Miner#close - * @param {Function} callback - */ - -Miner.prototype._close = function close(callback) { - callback(); -}; - -/** - * Start mining. - * @param {Number?} version - Custom block version. - */ - -Miner.prototype.start = function start() { - var self = this; - - this.stop(); - - this.running = true; - - // Create a new block and start hashing - this.createBlock(function(err, attempt) { - if (err) - return self.emit('error', err); - - if (!self.running) - return; - - self.attempt = attempt; - - attempt.on('status', function(status) { - self.emit('status', status); - }); - - attempt.mineAsync(function(err, block) { - if (err) { - if (!self.running) - return; - self.emit('error', err); - return self.start(); - } - - // Add our block to the chain - self.chain.add(block, function(err) { - if (err) { - if (err.type === 'VerifyError') - self.logger.warning('%s could not be added to chain.', block.rhash); - self.emit('error', err); - return self.start(); - } - - // Emit our newly found block - self.emit('block', block); - - // `tip` will now be emitted by chain - // and the whole process starts over. - }); - }); - }); -}; - -/** - * Stop mining. - */ - -Miner.prototype.stop = function stop() { - if (!this.running) - return; - - this.running = false; - - if (this.attempt) { - this.attempt.destroy(); - this.attempt = null; - } - - if (this.workerPool) - this.workerPool.destroy(); -}; - -/** - * Create a block "attempt". - * @param {Number?} version - Custom block version. - * @param {Function} callback - Returns [Error, {@link MinerBlock}]. - */ - -Miner.prototype.createBlock = function createBlock(tip, callback) { - var self = this; - var i, ts, attempt, txs, tx; - - if (typeof tip === 'function') { - callback = tip; - tip = null; - } - - if (!tip) - tip = this.chain.tip; - - ts = Math.max(bcoin.now(), tip.ts + 1); - - function computeVersion(callback) { - if (self.version != null) - return callback(null, self.version); - self.chain.computeBlockVersion(tip, callback); - } - - if (!this.loaded) { - this.open(function(err) { - if (err) - return callback(err); - self.createBlock(tip, callback); - }); - return; - } - - assert(tip); - - // Find target - this.chain.getTargetAsync(ts, tip, function(err, target) { - if (err) - return callback(err); - - // Calculate version with versionbits - computeVersion(function(err, version) { - if (err) - return callback(err); - - attempt = new MinerBlock({ - workerPool: self.workerPool, - tip: tip, - version: version, - target: target, - address: self.address, - coinbaseFlags: self.coinbaseFlags, - witness: self.chain.segwitActive, - parallel: self.options.parallel, - network: self.network - }); - - if (!self.mempool) - return callback(null, attempt); - - txs = self.mempool.getHistory(); - - for (i = 0; i < txs.length; i++) { - tx = txs[i]; - attempt.addTX(tx); - } - - callback(null, attempt); - }); - }); -}; - -/** - * Mine a single block. - * @param {Number?} version - Custom block version. - * @param {Function} callback - Returns [Error, [{@link Block}]]. - */ - -Miner.prototype.mineBlock = function mineBlock(tip, callback) { - if (typeof tip === 'function') { - callback = tip; - tip = null; - } - - // Create a new block and start hashing - this.createBlock(tip, function(err, attempt) { - if (err) - return callback(err); - - attempt.mineAsync(callback); - }); -}; +var BufferReader = require('../utils/reader'); +var BufferWriter = require('../utils/writer'); /** * MinerBlock @@ -354,6 +39,8 @@ function MinerBlock(options) { if (!(this instanceof MinerBlock)) return new MinerBlock(options); + EventEmitter.call(this); + this.options = options; this.workerPool = options.workerPool; this.tip = options.tip; @@ -802,10 +489,8 @@ function rcmp(a, b) { return 0; } -Miner.MinerBlock = MinerBlock; - /* * Expose */ -module.exports = Miner; +module.exports = MinerBlock; diff --git a/lib/bcoin/timedata.js b/lib/bcoin/net/timedata.js similarity index 98% rename from lib/bcoin/timedata.js rename to lib/bcoin/net/timedata.js index 83240486..81b1b009 100644 --- a/lib/bcoin/timedata.js +++ b/lib/bcoin/net/timedata.js @@ -7,7 +7,7 @@ 'use strict'; -var utils = require('./utils/utils'); +var utils = require('../utils/utils'); var EventEmitter = require('events').EventEmitter; /** diff --git a/lib/bcoin/workers/jobs.js b/lib/bcoin/workers/jobs.js new file mode 100644 index 00000000..424825c6 --- /dev/null +++ b/lib/bcoin/workers/jobs.js @@ -0,0 +1,80 @@ +/*! + * jobs.js - worker jobs for bcoin + * Copyright (c) 2014-2016, Christopher Jeffrey (MIT License). + * https://github.com/bcoin-org/bcoin + */ + +'use strict'; + +var bcoin = require('./env'); + +/** + * Jobs to execute within the worker. + * @memberof Workers + * @const {Object} + */ + +var jobs = exports; + +/** + * Execute tx.verify() on worker. + * @see TX#verify + * @param {TX} tx + * @param {VerifyFlags} flags + * @returns {Boolean} + */ + +jobs.verify = function verify(tx, flags) { + return tx.verify(flags); +}; + +/** + * Execute Wallet.sign() on worker. + * @see Wallet.sign + * @param {KeyRing[]} rings + * @param {HDPrivateKey} master + * @param {MTX} tx + */ + +jobs.sign = function sign(tx, ring, type) { + var total = tx.sign(ring, type); + var sigs = []; + var i, input; + + for (i = 0; i < tx.inputs.length; i++) { + input = tx.inputs[i]; + sigs.push([input.script, input.witness]); + } + + return [sigs, total]; +}; + +/** + * Mine a block on worker. + * @param {Object} attempt - Naked {@link MinerBlock}. + * @returns {Block} + */ + +jobs.mine = function mine(attempt) { + attempt.on('status', function(stat) { + bcoin.master.sendEvent('status', stat); + }); + return attempt.mineSync(); +}; + +/** + * Execute scrypt() on worker. + * @see scrypt + * @param {Buffer} passwd + * @param {Buffer} salt + * @param {Number} N + * @param {Number} r + * @param {Number} p + * @param {Number} len + * @returns {Buffer} + */ + +jobs.scrypt = function scrypt(passwd, salt, N, r, p, len) { + var scrypt = require('./scrypt'); + return scrypt(passwd, salt, N >>> 0, r >>> 0, p >>> 0, len); +}; diff --git a/lib/bcoin/workers.js b/lib/bcoin/workers/workers.js similarity index 94% rename from lib/bcoin/workers.js rename to lib/bcoin/workers/workers.js index 8fb7fcf3..b8e4d7c6 100644 --- a/lib/bcoin/workers.js +++ b/lib/bcoin/workers/workers.js @@ -15,7 +15,7 @@ var global = utils.global; var assert = utils.assert; var BufferWriter = require('./utils/writer'); var BufferReader = require('./utils/reader'); -var jobs; +var jobs = require('./jobs'); /** * A worker pool. @@ -354,7 +354,7 @@ Worker.prototype._init = function _init() { } else { cp = require('child_process'); - this.child = cp.spawn(process.argv[0], [__dirname + '/worker.js'], { + this.child = cp.spawn(process.argv[0], [__dirname + '/../../worker.js'], { stdio: 'pipe', env: utils.merge({}, process.env, penv) }); @@ -746,77 +746,6 @@ Master.listen = function listen(options) { return master; }; -/** - * Jobs to execute within the worker. - * @memberof Workers - * @const {Object} - */ - -jobs = {}; - -/** - * Execute tx.verify() on worker. - * @see TX#verify - * @param {TX} tx - * @param {VerifyFlags} flags - * @returns {Boolean} - */ - -jobs.verify = function verify(tx, flags) { - return tx.verify(flags); -}; - -/** - * Execute Wallet.sign() on worker. - * @see Wallet.sign - * @param {KeyRing[]} rings - * @param {HDPrivateKey} master - * @param {MTX} tx - */ - -jobs.sign = function sign(tx, ring, type) { - var total = tx.sign(ring, type); - var sigs = []; - var i, input; - - for (i = 0; i < tx.inputs.length; i++) { - input = tx.inputs[i]; - sigs.push([input.script, input.witness]); - } - - return [sigs, total]; -}; - -/** - * Mine a block on worker. - * @param {Object} attempt - Naked {@link MinerBlock}. - * @returns {Block} - */ - -jobs.mine = function mine(attempt) { - attempt.on('status', function(stat) { - bcoin.master.sendEvent('status', stat); - }); - return attempt.mineSync(); -}; - -/** - * Execute scrypt() on worker. - * @see scrypt - * @param {Buffer} passwd - * @param {Buffer} salt - * @param {Number} N - * @param {Number} r - * @param {Number} p - * @param {Number} len - * @returns {Buffer} - */ - -jobs.scrypt = function scrypt(passwd, salt, N, r, p, len) { - var scrypt = require('./scrypt'); - return scrypt(passwd, salt, N >>> 0, r >>> 0, p >>> 0, len); -}; - /** * Framer * @constructor diff --git a/lib/bcoin/worker.js b/lib/worker.js similarity index 95% rename from lib/bcoin/worker.js rename to lib/worker.js index ada8fb10..ab873b16 100644 --- a/lib/bcoin/worker.js +++ b/lib/worker.js @@ -24,7 +24,7 @@ if (typeof importScripts !== 'undefined') { }; } else { env = process.env; - bcoin = require('./env'); + bcoin = require('./bcoin/env'); bcoin.set(env.BCOIN_WORKER_NETWORK); bcoin.workers.listen(); }