diff --git a/lib/http/rpc.js b/lib/http/rpc.js index f9b2340f..9b1bd7e6 100644 --- a/lib/http/rpc.js +++ b/lib/http/rpc.js @@ -915,7 +915,7 @@ RPC.prototype._submitWork = co(function* _submitWork(data) { var header = Headers.fromAbbr(data); var nonce = header.nonce; var ts = header.ts; - var job, n1, n2, hash, block, entry; + var job, n1, n2, proof, block, entry; if (!attempt) return false; @@ -942,12 +942,12 @@ RPC.prototype._submitWork = co(function* _submitWork(data) { n1 = job.nonce1; n2 = job.nonce2; - hash = attempt.hash(n1, n2, ts, nonce); + proof = attempt.getProof(n1, n2, ts, nonce); - if (!consensus.verifyPOW(hash, attempt.bits)) + if (!proof.verify(attempt.target)) return false; - block = attempt.commit(n1, n2, ts, nonce); + block = attempt.commit(proof); try { entry = yield this.chain.add(block); @@ -983,12 +983,14 @@ RPC.prototype._createWork = co(function* _createWork() { var n1 = this.nonce1; var n2 = this.nonce2; var ts = attempt.ts; - var data, head; + var data, root, head; data = new Buffer(128); data.fill(0); - head = attempt.getHeader(n1, n2, ts, 0); + root = attempt.getRoot(n1, n2); + head = attempt.getHeader(root, ts, 0); + head.copy(data, 0); data[80] = 0x80; diff --git a/lib/mining/common.js b/lib/mining/common.js new file mode 100644 index 00000000..bc677df6 --- /dev/null +++ b/lib/mining/common.js @@ -0,0 +1,124 @@ +/*! + * common.js - mining utils + * Copyright (c) 2014-2017, Christopher Jeffrey (MIT License). + * https://github.com/bcoin-org/bcoin + */ + +'use strict'; + +var assert = require('assert'); + +/** + * @exports mining/common + */ + +var common = exports; + +/* + * Constants + */ + +var DIFF_TARGET = 0x00000000ffff0000000000000000000000000000000000000000000000000000; +var B192 = 0x1000000000000000000000000000000000000000000000000; +var B128 = 0x100000000000000000000000000000000; +var B64 = 0x10000000000000000; +var B0 = 0x1; + +/** + * Swap 32 bit endianness of uint256. + * @param {Buffer} data + * @returns {Buffer} + */ + +exports.swap32 = function swap32(data) { + var i, field; + + for (i = 0; i < data.length; i += 4) { + field = data.readUInt32LE(i, true); + data.writeUInt32BE(field, i, true); + } + + return data; +}; + +/** + * Swap 32 bit endianness of uint256 (hex). + * @param {String} data + * @returns {String} + */ + +exports.hswap32 = function hswap32(hex) { + var data = new Buffer(hex, 'hex'); + exports.swap32(data) + return data.toString('hex'); +}; + +/** + * Compare two uint256le's. + * @param {Buffer} a + * @param {Buffer} b + * @returns {Number} + */ + +exports.rcmp = function rcmp(a, b) { + var i; + + assert(a.length === b.length); + + for (i = a.length - 1; i >= 0; i--) { + if (a[i] < b[i]) + return -1; + if (a[i] > b[i]) + return 1; + } + + return 0; +}; + +/** + * Convert a uint256le to a double. + * @param {Buffer} target + * @returns {Number} + */ + +exports.double256 = function double256(target) { + var n = 0; + var hi, lo; + + assert(target.length === 32); + + hi = target.readUInt32LE(28, true); + lo = target.readUInt32LE(24, true); + n += (hi * 0x100000000 + lo) * B192; + + hi = target.readUInt32LE(20, true); + lo = target.readUInt32LE(16, true); + n += (hi * 0x100000000 + lo) * B128; + + hi = target.readUInt32LE(12, true); + lo = target.readUInt32LE(8, true); + n += (hi * 0x100000000 + lo) * B64; + + hi = target.readUInt32LE(4, true); + lo = target.readUInt32LE(0, true); + n += (hi * 0x100000000 + lo) * B0; + + return n; +}; + +/** + * Calculate mining difficulty + * from little-endian target. + * @param {Buffer} target + * @returns {Number} + */ + +exports.getDifficulty = function getDifficulty(target) { + var d = DIFF_TARGET; + var n = exports.double256(target); + if (n === 0) + return d; + if (n > d) + return d; + return Math.floor(d / n); +}; diff --git a/lib/mining/cpuminer.js b/lib/mining/cpuminer.js index 71c23650..13511f47 100644 --- a/lib/mining/cpuminer.js +++ b/lib/mining/cpuminer.js @@ -279,7 +279,7 @@ CPUMiner.prototype.notifyEntry = function notifyEntry() { */ CPUMiner.prototype.findNonce = function findNonce(job) { - var data = job.getHeader(0); + var data = job.getHeader(); var target = job.attempt.target; var interval = CPUMiner.INTERVAL; var min = 0; @@ -309,7 +309,7 @@ CPUMiner.prototype.findNonce = function findNonce(job) { */ CPUMiner.prototype.findNonceAsync = co(function* findNonceAsync(job) { - var data = job.getHeader(0); + var data = job.getHeader(); var target = job.attempt.target; var interval = CPUMiner.INTERVAL; var min = 0; @@ -439,12 +439,14 @@ function CPUJob(miner, attempt) { * @returns {Buffer} */ -CPUJob.prototype.getHeader = function getHeader(nonce) { +CPUJob.prototype.getHeader = function getHeader() { var attempt = this.attempt; var n1 = this.nonce1; var n2 = this.nonce2; var ts = attempt.ts; - return this.attempt.getHeader(n1, n2, ts, nonce); + var root = attempt.getRoot(n1, n2); + var data = attempt.getHeader(root, ts, 0); + return data; }; /** @@ -458,11 +460,14 @@ CPUJob.prototype.commit = function commit(nonce) { var n1 = this.nonce1; var n2 = this.nonce2; var ts = attempt.ts; + var proof; assert(!this.committed, 'Job already committed.'); this.committed = true; - return this.attempt.commit(n1, n2, ts, nonce); + proof = attempt.getProof(n1, n2, ts, nonce); + + return attempt.commit(proof); }; /** diff --git a/lib/mining/index.js b/lib/mining/index.js index 6e5ddd59..e56c54a4 100644 --- a/lib/mining/index.js +++ b/lib/mining/index.js @@ -5,6 +5,7 @@ */ exports.BlockTemplate = require('./template'); +exports.common = require('./common'); exports.CPUMiner = require('./cpuminer'); exports.mine = require('./mine'); exports.Miner = require('./miner'); diff --git a/lib/mining/template.js b/lib/mining/template.js index 662b299f..2b5e410d 100644 --- a/lib/mining/template.js +++ b/lib/mining/template.js @@ -21,6 +21,7 @@ var consensus = require('../protocol/consensus'); var policy = require('../protocol/policy'); var encoding = require('../utils/encoding'); var CoinView = require('../coins/coinview'); +var common = require('./common'); var DUMMY = new Buffer(0); /** @@ -60,7 +61,7 @@ function BlockTemplate(options) { * @returns {Buffer} */ -BlockTemplate.prototype.commitmentHash = function commitmentHash() { +BlockTemplate.prototype.getWitnessHash = function getWitnessHash() { var nonce = encoding.ZERO_HASH; var leaves = []; var i, item, root, data; @@ -139,7 +140,7 @@ BlockTemplate.prototype.createCoinbase = function createCoinbase() { if (this.witness) { // Commitment output. commit = new Output(); - hash = this.commitmentHash(); + hash = this.getWitnessHash(); commit.script.fromCommitment(hash); cb.outputs.push(commit); } @@ -220,7 +221,7 @@ BlockTemplate.prototype.refresh = function refresh() { * @returns {Buffer} */ -BlockTemplate.prototype.getCoinbase = function getCoinbase(nonce1, nonce2) { +BlockTemplate.prototype.getRawCoinbase = function getRawCoinbase(nonce1, nonce2) { var size = 0; var bw; @@ -245,23 +246,21 @@ BlockTemplate.prototype.getCoinbase = function getCoinbase(nonce1, nonce2) { */ BlockTemplate.prototype.getRoot = function getRoot(nonce1, nonce2) { - var raw = this.getCoinbase(nonce1, nonce2); + var raw = this.getRawCoinbase(nonce1, nonce2); var hash = crypto.hash256(raw); return this.tree.withFirst(hash); }; /** * Create raw block header with given parameters. - * @param {Number} nonce1 - * @param {Number} nonce2 + * @param {Buffer} root * @param {Number} ts * @param {Number} nonce * @returns {Buffer} */ -BlockTemplate.prototype.getHeader = function getHeader(nonce1, nonce2, ts, nonce) { +BlockTemplate.prototype.getHeader = function getHeader(root, ts, nonce) { var bw = new StaticWriter(80); - var root = this.getRoot(nonce1, nonce2); bw.writeU32(this.version); bw.writeHash(this.prevBlock); @@ -274,17 +273,19 @@ BlockTemplate.prototype.getHeader = function getHeader(nonce1, nonce2, ts, nonce }; /** - * Calculate block hash with given parameters. + * Calculate proof with given parameters. * @param {Number} nonce1 * @param {Number} nonce2 * @param {Number} ts * @param {Number} nonce - * @returns {Buffer} + * @returns {BlockProof} */ -BlockTemplate.prototype.hash = function hash(nonce1, nonce2, ts, nonce) { - var data = this.getHeader(nonce1, nonce2, ts, nonce); - return crypto.hash256(data); +BlockTemplate.prototype.getProof = function getProof(nonce1, nonce2, ts, nonce) { + var root = this.getRoot(nonce1, nonce2); + var data = this.getHeader(root, ts, nonce); + var hash = crypto.hash256(data); + return new BlockProof(hash, root, nonce1, nonce2, ts, nonce); }; /** @@ -294,8 +295,8 @@ BlockTemplate.prototype.hash = function hash(nonce1, nonce2, ts, nonce) { * @returns {TX} */ -BlockTemplate.prototype.coinbase = function coinbase(nonce1, nonce2) { - var raw = this.getCoinbase(nonce1, nonce2); +BlockTemplate.prototype.getCoinbase = function getCoinbase(nonce1, nonce2) { + var raw = this.getRawCoinbase(nonce1, nonce2); var tx = TX.fromRaw(raw); var input; @@ -310,19 +311,19 @@ BlockTemplate.prototype.coinbase = function coinbase(nonce1, nonce2) { }; /** - * Create block from given parameters. - * @param {Number} nonce1 - * @param {Number} nonce2 - * @param {Number} ts - * @param {Number} nonce + * Create block from calculated proof. + * @param {BlockProof} proof * @returns {Block} */ -BlockTemplate.prototype.commit = function commit(nonce1, nonce2, ts, nonce) { - var tx = this.coinbase(nonce1, nonce2); - var root = this.tree.withFirst(tx.hash()); +BlockTemplate.prototype.commit = function commit(proof) { + var root = proof.root; + var n1 = proof.nonce1; + var n2 = proof.nonce2; + var ts = proof.ts; + var nonce = proof.nonce; var block = new Block(); - var i, item; + var i, tx, item; block.version = this.version; block.prevBlock = this.prevBlock; @@ -331,6 +332,8 @@ BlockTemplate.prototype.commit = function commit(nonce1, nonce2, ts, nonce) { block.bits = this.bits; block.nonce = nonce; + tx = this.getCoinbase(n1, n2); + block.txs.push(tx); for (i = 0; i < this.items.length; i++) { @@ -348,7 +351,7 @@ BlockTemplate.prototype.commit = function commit(nonce1, nonce2, ts, nonce) { */ BlockTemplate.prototype.toCoinbase = function toCoinbase() { - return this.coinbase(0, 0); + return this.getCoinbase(0, 0); }; /** @@ -358,7 +361,17 @@ BlockTemplate.prototype.toCoinbase = function toCoinbase() { */ BlockTemplate.prototype.toBlock = function toBlock() { - return this.commit(0, 0, this.ts, 0); + var proof = this.getProof(0, 0, this.ts, 0); + return this.commit(proof); +}; + +/** + * Calculate the target difficulty. + * @returns {Number} + */ + +BlockTemplate.prototype.getDifficulty = function getDifficulty() { + return common.getDifficulty(this.target); }; /** @@ -601,6 +614,38 @@ MerkleTree.fromLeaves = function fromLeaves(leaves) { return new MerkleTree().fromLeaves(leaves); }; +/* + * BlockProof + * @constructor + * @param {Hash} hash + * @param {Hash} root + * @param {Number} nonce1 + * @param {Number} nonce2 + * @param {Number} ts + * @param {Number} nonce + */ + +function BlockProof(hash, root, nonce1, nonce2, ts, nonce) { + this.hash = hash; + this.root = root; + this.nonce1 = nonce1; + this.nonce2 = nonce2; + this.ts = ts; + this.nonce = nonce; +} + +BlockProof.prototype.rhash = function rhash() { + return util.revHex(this.hash.toString('hex')); +}; + +BlockProof.prototype.verify = function verify(target) { + return common.rcmp(this.hash, target) <= 0; +}; + +BlockProof.prototype.getDifficulty = function getDifficulty() { + return common.getDifficulty(this.hash); +}; + /* * Helpers */