From 65982454868b4cc3e24ae9369c0930e30fda0058 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Sat, 17 Sep 2016 06:24:58 -0700 Subject: [PATCH] rpc: improve mining. --- lib/http/rpc.js | 125 ++++++++++++++++++++++++++++++++++++++++----- lib/http/server.js | 2 +- 2 files changed, 113 insertions(+), 14 deletions(-) diff --git a/lib/http/rpc.js b/lib/http/rpc.js index d70ca235..4425cff0 100644 --- a/lib/http/rpc.js +++ b/lib/http/rpc.js @@ -33,6 +33,7 @@ function RPC(node) { this.miner = node.miner; this.wallet = node.wallet; this.walletdb = node.walletdb; + this.locker = new bcoin.locker(this); this.feeRate = null; this.mining = false; @@ -95,6 +96,8 @@ RPC.prototype.execute = function execute(json, callback) { return this.getmininginfo(json.params, callback); case 'prioritisetransaction': return this.prioritisetransaction(json.params, callback); + case 'getwork': + return this.getwork(json.params, callback); case 'getblocktemplate': return this.getblocktemplate(json.params, callback); case 'submitblock': @@ -272,7 +275,7 @@ RPC.prototype.execute = function execute(json, callback) { return this.getmemory(json.params, callback); default: - return callback(new Error('Method not found.')); + return callback(new Error('Method not found: ' + json.method + '.')); } }; @@ -1382,6 +1385,74 @@ RPC.prototype.verifychain = function verifychain(args, callback) { * Mining */ +RPC.prototype.getwork = function getwork(args, callback, force) { + var self = this; + var i, data, abbr, field, block, header; + + callback = this.locker.lock(getwork, [args, callback], force); + + if (!callback) + return; + + if (args.length > 1) + return callback(new RPCError('getwork ( "data" )')); + + if (args.length === 1 && this.currentBlock) { + if (!utils.isHex(args[0])) + return callback(new RPCError('Invalid parameter.')); + + if (args[0].length !== 256) + return callback(new RPCError('Invalid parameter.')); + + data = new Buffer(args[0], 'hex').slice(0, 80); + reverseEndian(data); + + header = bcoin.headers.fromAbbr(data); + block = this.currentBlock.block; + + this._clearBlock(); + + if (header.prevBlock !== block.prevBlock) + return self.getwork([], callback, true); + + block.nonce = header.nonce; + block.ts = header.ts; + block.mutable = false; + block.txs[0].mutable = false; + + return this.chain.add(block, function(err) { + if (err) { + if (err.reason) + return callback(new RPCError(err.reason)); + return callback(err); + } + return self.getwork([], callback, true); + }); + } + + this._createBlock(function(err, attempt) { + if (err) + return callback(err); + + data = new Buffer(128); + data.fill(0); + + abbr = attempt.block.abbr(); + abbr.copy(data, 0); + + data[80] = 0x80; + data.writeUInt32BE(80 * 8, data.length - 4, true); + + reverseEndian(data); + + callback(null, { + data: data.toString('hex'), + target: attempt.target.toString('hex'), + height: attempt.height + }); + }); +}; + RPC.prototype.getblocktemplate = function getblocktemplate(args, callback) { var self = this; var txs = []; @@ -1391,6 +1462,11 @@ RPC.prototype.getblocktemplate = function getblocktemplate(args, callback) { var i, j, tx, deps, input, dep, block; var opt, lpid, keys, vbavailable, vbrules, mutable, clientRules; + callback = this.locker.lock(getblocktemplate, [args, callback]); + + if (!callback) + return; + if (args.help || args.length > 1) return callback(new RPCError('getblocktemplate ( "jsonrequestobject" )')); @@ -1433,11 +1509,13 @@ RPC.prototype.getblocktemplate = function getblocktemplate(args, callback) { } } - if (this.pool.peers.all.length === 0) - return callback(new RPCError('Bitcoin is not connected!')); + if (this.network !== bcoin.network.regtest) { + if (this.pool.peers.all.length === 0) + return callback(new RPCError('Bitcoin is not connected!')); - if (!this.chain.isFull()) - return callback(new RPCError('Bitcoin is downloading blocks...')); + if (!this.chain.isFull()) + return callback(new RPCError('Bitcoin is downloading blocks...')); + } this._poll(lpid, function(err) { if (err) @@ -1528,16 +1606,18 @@ RPC.prototype.getblocktemplate = function getblocktemplate(args, callback) { flags: new Buffer(attempt.coinbaseFlags, 'utf8').toString('hex') }, coinbasevalue: attempt.coinbase.outputs[0].value, - longpollid: self.chain.tip.rhash + self.mempool.total, + longpollid: self.chain.tip.rhash + utils.pad32(self.mempool.totalTX), target: utils.revHex(attempt.target.toString('hex')), - mintime: attempt.ts, + mintime: attempt.block.ts, mutable: mutable, noncerange: '00000000ffffffff', - sigoplimit: constants.block.MAX_SIGOPS_WEIGHT, + sigoplimit: attempt.witness + ? constants.block.MAX_SIGOPS_WEIGHT + : constants.block.MAX_SIGOPS, sizelimit: constants.block.MAX_SIZE, weightlimit: constants.block.MAX_WEIGHT, curtime: block.ts, - bits: String(block.bits), + bits: utils.hex32(block.bits), height: attempt.height, default_witness_commitment: attempt.witness ? attempt.coinbase.outputs[1].script.toJSON() @@ -1569,7 +1649,7 @@ RPC.prototype._poll = function _poll(lpid, callback) { } function listener() { - if (self.chain.tip.hash !== watched || self.mempool.total !== lastTX) { + if (self.chain.tip.hash !== watched || self.mempool.totalTX !== lastTX) { self.chain.removeListener('block', listener); self.mempool.removeListener('tx', listener); return callback(); @@ -1580,20 +1660,31 @@ RPC.prototype._poll = function _poll(lpid, callback) { this.mempool.on('tx', listener); }; +RPC.prototype._clearBlock = function _clearBlock() { + this.prevBlock = null; + this.currentBlock = null; + this.lastTX = 0; + this.start = 0; +}; + RPC.prototype._createBlock = function _createBlock(callback) { var self = this; + var diff = utils.now() - this.start; + var total = this.mempool.totalTX; + if (this.prevBlock !== this.chain.tip.hash - || (this.mempool.total !== this.lastTX && utils.now() - this.start > 5)) { + || (total !== this.lastTX && diff > 5)) { return this.miner.createBlock(function(err, attempt) { if (err) return callback(err); self.prevBlock = attempt.block.prevBlock; self.currentBlock = attempt; - self.lastTX = self.mempool.total; + self.lastTX = self.mempool.totalTX; self.start = utils.now(); callback(null, attempt); }); } + callback(null, this.currentBlock); }; @@ -1622,7 +1713,7 @@ RPC.prototype.getmininginfo = function getmininginfo(args, callback) { errors: '', genproclimit: self.proclimit, networkhashps: hashps, - pooledtx: self.mempool.total, + pooledtx: self.mempool.totalTX, testnet: self.network.type !== bcoin.network.main, chain: self.network.type, generate: self.mining @@ -4170,6 +4261,14 @@ function toSatoshi(obj) { return utils.satoshi(obj + ''); } +function reverseEndian(data) { + var i, field; + for (i = 0; i < data.length; i += 4) { + field = data.readUInt32LE(i, true); + data.writeUInt32BE(field, i, true); + } +} + /* * Expose */ diff --git a/lib/http/server.js b/lib/http/server.js index 2e1dac4b..2d9ba840 100644 --- a/lib/http/server.js +++ b/lib/http/server.js @@ -435,7 +435,7 @@ HTTPServer.prototype._init = function _init() { } send(200, { - result: json, + result: json != null ? json : null, error: null, id: req.body.id });