diff --git a/lib/mining/common.js b/lib/mining/common.js index bc677df6..e2bff308 100644 --- a/lib/mining/common.js +++ b/lib/mining/common.js @@ -7,6 +7,8 @@ 'use strict'; var assert = require('assert'); +var consensus = require('../protocol/consensus'); +var BN = require('bn.js'); /** * @exports mining/common @@ -18,7 +20,7 @@ var common = exports; * Constants */ -var DIFF_TARGET = 0x00000000ffff0000000000000000000000000000000000000000000000000000; +var DIFF = 0x00000000ffff0000000000000000000000000000000000000000000000000000; var B192 = 0x1000000000000000000000000000000000000000000000000; var B128 = 0x100000000000000000000000000000000; var B64 = 0x10000000000000000; @@ -30,7 +32,7 @@ var B0 = 0x1; * @returns {Buffer} */ -exports.swap32 = function swap32(data) { +common.swap32 = function swap32(data) { var i, field; for (i = 0; i < data.length; i += 4) { @@ -43,14 +45,13 @@ exports.swap32 = function swap32(data) { /** * Swap 32 bit endianness of uint256 (hex). - * @param {String} data + * @param {String} str * @returns {String} */ -exports.hswap32 = function hswap32(hex) { - var data = new Buffer(hex, 'hex'); - exports.swap32(data) - return data.toString('hex'); +common.swap32hex = function swap32hex(str) { + var data = new Buffer(str, 'hex'); + return common.swap32(data).toString('hex'); }; /** @@ -60,7 +61,7 @@ exports.hswap32 = function hswap32(hex) { * @returns {Number} */ -exports.rcmp = function rcmp(a, b) { +common.rcmp = function rcmp(a, b) { var i; assert(a.length === b.length); @@ -81,7 +82,7 @@ exports.rcmp = function rcmp(a, b) { * @returns {Number} */ -exports.double256 = function double256(target) { +common.double256 = function double256(target) { var n = 0; var hi, lo; @@ -113,12 +114,48 @@ exports.double256 = function double256(target) { * @returns {Number} */ -exports.getDifficulty = function getDifficulty(target) { - var d = DIFF_TARGET; - var n = exports.double256(target); +common.getDifficulty = function getDifficulty(target) { + var d = DIFF; + var n = common.double256(target); + if (n === 0) return d; + if (n > d) return d; + return Math.floor(d / n); }; + +/** + * Get target from bits as a uint256le. + * @param {Number} bits + * @returns {Buffer} + */ + +common.getTarget = function getTarget(bits) { + var target = consensus.fromCompact(bits); + + if (target.isNeg()) + throw new Error('Target is negative.'); + + if (target.cmpn(0) === 0) + throw new Error('Target is zero.'); + + return target.toArrayLike(Buffer, 'le', 32); +}; + +/** + * Get bits from target. + * @param {Buffer} data + * @returns {Buffer} + */ + +common.getBits = function getBits(data) { + var target = new BN(data, 'le'); + + if (target.cmpn(0) === 0) + throw new Error('Target is zero.'); + + return consensus.toCompact(target); +}; diff --git a/lib/mining/miner.js b/lib/mining/miner.js index 9a666701..a4c9e8a6 100644 --- a/lib/mining/miner.js +++ b/lib/mining/miner.js @@ -138,7 +138,7 @@ Miner.prototype._createBlock = co(function* createBlock(tip, address) { address: address, coinbaseFlags: this.options.coinbaseFlags, witness: this.chain.state.hasWitness(), - halvingInterval: this.network.halvingInterval, + interval: this.network.halvingInterval, weight: this.options.reservedWeight, sigops: this.options.reservedSigops }); @@ -146,11 +146,12 @@ Miner.prototype._createBlock = co(function* createBlock(tip, address) { this.assemble(attempt); this.logger.debug( - 'Created miner block (height=%d, weight=%d, fees=%d, txs=%s).', + 'Created block template (height=%d, weight=%d, fees=%d, txs=%s, diff=%d).', attempt.height, attempt.weight, Amount.btc(attempt.fees), - attempt.items.length + 1); + attempt.items.length + 1, + attempt.getDifficulty()); if (this.options.preverify) { block = attempt.toBlock(); diff --git a/lib/mining/template.js b/lib/mining/template.js index 2b5e410d..611f2a2b 100644 --- a/lib/mining/template.js +++ b/lib/mining/template.js @@ -35,27 +35,127 @@ function BlockTemplate(options) { if (!(this instanceof BlockTemplate)) return new BlockTemplate(options); - this.prevBlock = options.prevBlock; - this.version = options.version; - this.height = options.height; - this.ts = options.ts; - this.bits = options.bits; - this.target = consensus.fromCompact(this.bits).toArrayLike(Buffer, 'le', 32); - this.locktime = options.locktime; - this.flags = options.flags; - this.coinbaseFlags = options.coinbaseFlags; - this.witness = options.witness; - this.address = options.address; - this.sigops = options.sigops; - this.weight = options.weight; - this.reward = consensus.getReward(this.height, options.halvingInterval); + this.prevBlock = encoding.NULL_HASH; + this.version = 1; + this.height = 0; + this.ts = 0; + this.bits = 0; + this.target = encoding.ZERO_HASH; + this.locktime = 0; + this.flags = 0; + this.coinbaseFlags = DUMMY; + this.witness = false; + this.address = new Address(); + this.sigops = 400; + this.weight = 4000; + this.interval = 210000; + this.fees = 0; this.tree = new MerkleTree(); this.left = DUMMY; this.right = DUMMY; - this.fees = 0; this.items = []; + + if (options) + this.fromOptions(options); } +/** + * Inject properties from options. + * @private + * @param {Object} options + * @returns {BlockTemplate} + */ + +BlockTemplate.prototype.fromOptions = function fromOptions(options) { + assert(options); + + if (options.prevBlock != null) { + assert(typeof options.prevBlock === 'string'); + this.prevBlock = options.prevBlock; + } + + if (options.version != null) { + assert(typeof options.version === 'number'); + this.version = options.version; + } + + if (options.height != null) { + assert(typeof options.height === 'number'); + this.height = options.height; + } + + if (options.ts != null) { + assert(typeof options.ts === 'number'); + this.ts = options.ts; + } + + if (options.bits != null) + this.setBits(options.bits); + + if (options.target != null) + this.setTarget(options.target); + + if (options.locktime != null) { + assert(typeof options.locktime === 'number'); + this.locktime = options.locktime; + } + + if (options.flags != null) { + assert(typeof options.flags === 'number'); + this.flags = options.flags; + } + + if (options.coinbaseFlags != null) { + assert(Buffer.isBuffer(options.coinbaseFlags)); + this.coinbaseFlags = options.coinbaseFlags; + } + + if (options.witness != null) { + assert(typeof options.witness === 'boolean'); + this.witness = options.witness; + } + + if (options.address != null) + this.address.fromOptions(options.address); + + if (options.sigops != null) { + assert(typeof options.sigops === 'number'); + this.sigops = options.sigops; + } + + if (options.weight != null) { + assert(typeof options.weight === 'number'); + this.weight = options.weight; + } + + if (options.interval != null) { + assert(typeof options.interval === 'number'); + this.interval = options.interval; + } + + if (options.fees != null) { + assert(typeof options.fees === 'number'); + this.fees = options.fees; + } + + if (options.items != null) { + assert(Array.isArray(options.items)); + this.items = options.items; + } + + return this; +}; + +/** + * Instantiate block template from options. + * @param {Object} options + * @returns {BlockTemplate} + */ + +BlockTemplate.fromOptions = function fromOptions(options) { + return new BlockTemplate().fromOptions(options); +}; + /** * Create witness commitment hash. * @returns {Buffer} @@ -82,13 +182,36 @@ BlockTemplate.prototype.getWitnessHash = function getWitnessHash() { return crypto.hash256(data); }; +/** + * Set the target (bits). + * @param {Number} bits + */ + +BlockTemplate.prototype.setBits = function setBits(bits) { + assert(typeof bits === 'number'); + this.bits = bits; + this.target = common.getTarget(bits); +}; + +/** + * Set the target (uint256le). + * @param {Buffer} target + */ + +BlockTemplate.prototype.setTarget = function setTarget(target) { + assert(Buffer.isBuffer(target)); + this.bits = common.getBits(target); + this.target = target; +}; + /** * Calculate the block reward. * @returns {Amount} */ BlockTemplate.prototype.getReward = function getReward() { - return this.reward + this.fees; + var reward = consensus.getReward(this.height, this.interval); + return reward + this.fees; }; /** diff --git a/test/chain-test.js b/test/chain-test.js index 4e2afe08..4b70dc87 100644 --- a/test/chain-test.js +++ b/test/chain-test.js @@ -707,7 +707,7 @@ describe('Chain', function() { it('should fail to connect bad amount', co(function* () { var job = yield cpu.createJob(); - job.attempt.reward += 1; + job.attempt.fees += 1; job.refresh(); assert.equal(yield mineBlock(job), 'bad-cb-amount'); }));