diff --git a/lib/http/rpc.js b/lib/http/rpc.js index 84be8bb6..49fffe8e 100644 --- a/lib/http/rpc.js +++ b/lib/http/rpc.js @@ -42,9 +42,10 @@ function RPC(node) { this.mining = false; this.proclimit = 0; - this.currentBlock = null; - this.blockStart = 0; + this.attempt = null; + this.start = 0; this._boundChain = false; + this.coinbase = {}; } utils.inherits(RPC, EventEmitter); @@ -102,6 +103,8 @@ RPC.prototype.execute = function execute(json, callback) { return this.prioritisetransaction(json.params, callback); case 'getwork': return this.getwork(json.params, callback); + case 'getworklp': + return this.getworklp(json.params, callback); case 'getblocktemplate': return this.getblocktemplate(json.params, callback); case 'submitblock': @@ -1390,20 +1393,28 @@ RPC.prototype.verifychain = function verifychain(args, callback) { */ RPC.prototype._submitwork = function getwork(data, callback) { - var data, block, header; + var attempt = this.attempt; + var data, block, header, cb; if (data.length !== 128) return callback(new RPCError('Invalid parameter.')); - if (!this.currentBlock) - return this._getwork(callback); + if (!attempt) + return callback(null, false); data = data.slice(0, 80); reverseEndian(data); header = bcoin.headers.fromAbbr(data); - block = this.currentBlock.block; + cb = this.coinbase[header.merkleRoot]; + + if (!cb) + return callback(null, false); + + block = attempt.block; + block.txs[0] = cb; + attempt.updateMerkle(); if (header.prevBlock !== block.prevBlock || header.merkleRoot !== block.merkleRoot @@ -1416,7 +1427,7 @@ RPC.prototype._submitwork = function getwork(data, callback) { block.mutable = false; block.txs[0].mutable = false; - return this.chain.add(block, function(err) { + this.chain.add(block, function(err) { if (err) { if (err.type === 'VerifyError') return callback(null, false); @@ -1426,10 +1437,10 @@ RPC.prototype._submitwork = function getwork(data, callback) { }); }; -RPC.prototype._getwork = function getwork(callback) { +RPC.prototype._getwork = function _getwork(callback) { var i, data, abbr; - this._createBlock(function(err, attempt) { + this._getAttempt(function(err, attempt) { if (err) return callback(err); @@ -1452,6 +1463,13 @@ RPC.prototype._getwork = function getwork(callback) { }); }; +RPC.prototype.getworklp = function getworklp(args, callback) { + var self = this; + this.once('clear block', function() { + self._getwork(callback); + }); +}; + RPC.prototype.getwork = function getwork(args, callback) { var self = this; var data; @@ -1560,12 +1578,12 @@ RPC.prototype.getblocktemplate = function getblocktemplate(args, callback) { } } - if (coinbasetxn && !coinbasevalue) + if (!coinbasetxn) coinbase = false; } } - if (this.network !== bcoin.network.regtest) { + if (!this.network.selfConnect) { if (this.pool.peers.all.length === 0) return callback(new RPCError('Bitcoin is not connected!')); @@ -1592,7 +1610,7 @@ RPC.prototype._tmpl = function _tmpl(version, coinbase, rules, callback) { if (!callback) return; - this._createBlock(function(err, attempt) { + this._getAttempt(function(err, attempt) { if (err) return callback(err); @@ -1681,7 +1699,7 @@ RPC.prototype._tmpl = function _tmpl(version, coinbase, rules, callback) { target: utils.revHex(attempt.target.toString('hex')), submitold: false, mintime: block.ts, - maxtime: bcoin.now(), + maxtime: bcoin.now() + 2 * 60 * 60, mutable: mutable, noncerange: '00000000ffffffff', sigoplimit: attempt.witness @@ -1689,7 +1707,7 @@ RPC.prototype._tmpl = function _tmpl(version, coinbase, rules, callback) { : constants.block.MAX_SIGOPS, sizelimit: constants.block.MAX_SIZE, weightlimit: constants.block.MAX_WEIGHT, - curtime: bcoin.now(), + curtime: block.ts, bits: utils.hex32(block.bits), height: attempt.height, default_witness_commitment: attempt.witness @@ -1697,7 +1715,7 @@ RPC.prototype._tmpl = function _tmpl(version, coinbase, rules, callback) { : undefined }; - if (!coinbase) { + if (coinbase) { template.coinbaseaux = undefined; template.coinbasevalue = undefined; tx = block.txs[0]; @@ -1742,8 +1760,9 @@ RPC.prototype._poll = function _poll(lpid, callback) { }; RPC.prototype._clearBlock = function _clearBlock() { - this.currentBlock = null; - this.blockStart = 0; + this.attempt = null; + this.start = 0; + this.coinbase = {}; }; RPC.prototype._bindChain = function _bindChain() { @@ -1755,7 +1774,7 @@ RPC.prototype._bindChain = function _bindChain() { this._boundChain = true; this.chain.on('connect', function() { - if (!self.currentBlock) + if (!self.attempt) return; self._clearBlock(); @@ -1765,10 +1784,10 @@ RPC.prototype._bindChain = function _bindChain() { this.mempool.on('tx', function() { var diff; - if (!self.currentBlock) + if (!self.attempt) return; - diff = utils.now() - self.blockStart; + diff = utils.now() - self.start; if (diff > 5) { self._clearBlock(); @@ -1777,19 +1796,26 @@ RPC.prototype._bindChain = function _bindChain() { }); }; -RPC.prototype._createBlock = function _createBlock(callback) { +RPC.prototype._getAttempt = function _getAttempt(callback) { var self = this; + var attempt = this.attempt; this._bindChain(); - if (this.currentBlock) - return callback(null, this.currentBlock); + if (attempt) { + attempt.updateNonce(); + this.coinbase[attempt.block.merkleRoot] = attempt.coinbase.clone(); + return callback(null, attempt); + } this.miner.createBlock(function(err, attempt) { if (err) return callback(err); - self.currentBlock = attempt; - self.blockStart = utils.now(); + + self.attempt = attempt; + self.start = utils.now(); + self.coinbase[attempt.block.merkleRoot] = attempt.coinbase.clone(); + callback(null, attempt); }); }; @@ -2208,6 +2234,9 @@ RPC.prototype.signrawtransaction = function signrawtransaction(args, callback) { + ' sighashtype )')); } + if (!utils.isHex(args[0])) + return callback(new RPCError('Invalid parameter')); + raw = new Buffer(args[0], 'hex'); p = new bcoin.reader(raw); txs = []; diff --git a/lib/http/server.js b/lib/http/server.js index 2d9ba840..c0e35633 100644 --- a/lib/http/server.js +++ b/lib/http/server.js @@ -88,6 +88,8 @@ HTTPServer.prototype._init = function _init() { var self = this; this.server.on('request', function(req, res) { + if (req.pathname === '/') + return; self.logger.debug('Request for path=%s (%s).', req.pathname, req.socket.remoteAddress); }); @@ -441,6 +443,12 @@ HTTPServer.prototype._init = function _init() { }); } + if (req.body.method === 'getwork') { + res.setHeader('X-Long-Polling', '/?longpoll=1'); + if (req.query.longpoll) + req.body.method = 'getworklp'; + } + try { self.rpc.execute(req.body, handle); } catch (e) { diff --git a/lib/miner/minerblock.js b/lib/miner/minerblock.js index 51fb1911..7850c21e 100644 --- a/lib/miner/minerblock.js +++ b/lib/miner/minerblock.js @@ -185,6 +185,24 @@ MinerBlock.prototype.updateCoinbase = function updateCoinbase() { output.value = this.block.getReward(this.network); }; +/** + * Increment the extraNonce. + */ + +MinerBlock.prototype.updateNonce = function updateNonce() { + this.block.ts = Math.max(bcoin.now(), this.tip.ts + 1); + + // Overflow the nonce and increment the extraNonce. + this.block.nonce = 0; + this.extraNonce.iaddn(1); + + // We incremented the extraNonce, need to update coinbase. + this.updateCoinbase(); + + // We changed the coinbase, need to update merkleRoot. + this.updateMerkle(); +}; + /** * Rebuild the merkle tree and update merkle root as well as the * timestamp (also calls {@link MinerBlock#updateCommitment} @@ -297,14 +315,7 @@ MinerBlock.prototype.findNonce = function findNonce() { } // Overflow the nonce and increment the extraNonce. - block.nonce = 0; - this.extraNonce.iaddn(1); - - // We incremented the extraNonce, need to update coinbase. - this.updateCoinbase(); - - // We changed the coinbase, need to update merkleRoot. - this.updateMerkle(); + this.updateNonce(); return false; };