diff --git a/lib/http/rpc.js b/lib/http/rpc.js index 49fffe8e..4b265e94 100644 --- a/lib/http/rpc.js +++ b/lib/http/rpc.js @@ -36,6 +36,8 @@ function RPC(node) { this.miner = node.miner; this.wallet = node.wallet; this.walletdb = node.walletdb; + this.logger = node.logger; + this.locker = new bcoin.locker(this); this.feeRate = null; @@ -775,8 +777,12 @@ RPC.prototype.getblock = function getblock(args, callback) { return callback(err); if (!block) { + if (self.chain.db.options.spv) + return callback(new RPCError('Block not available (spv mode)')); + if (self.chain.db.prune) return callback(new RPCError('Block not available (pruned data)')); + return callback(new RPCError('Can\'t read block from disk')); } @@ -1070,6 +1076,12 @@ RPC.prototype.getdifficulty = function getdifficulty(args, callback) { }; RPC.prototype.getmempoolinfo = function getmempoolinfo(args, callback) { + if (args.help || args.length !== 0) + return callback(new RPCError('getmempoolinfo')); + + if (!this.mempool) + return callback(new RPCError('No mempool available.')); + callback(null, { size: this.mempool.totalTX, bytes: this.mempool.getSize(), @@ -1085,6 +1097,9 @@ RPC.prototype.getmempoolancestors = function getmempoolancestors(args, callback) if (args.help || args.length < 1 || args.length > 2) return callback(new RPCError('getmempoolancestors txid (verbose)')); + if (!this.mempool) + return callback(new RPCError('No mempool available.')); + hash = toHash(args[0]); if (!hash) @@ -1117,6 +1132,9 @@ RPC.prototype.getmempooldescendants = function getmempooldescendants(args, callb if (args.help || args.length < 1 || args.length > 2) return callback(new RPCError('getmempooldescendants txid (verbose)')); + if (!this.mempool) + return callback(new RPCError('No mempool available.')); + hash = toHash(args[0]); if (!hash) @@ -1149,6 +1167,9 @@ RPC.prototype.getmempoolentry = function getmempoolentry(args, callback) { if (args.help || args.length !== 1) return callback(new RPCError('getmempoolentry txid')); + if (!this.mempool) + return callback(new RPCError('No mempool available.')); + hash = toHash(args[0]); if (!hash) @@ -1228,6 +1249,12 @@ RPC.prototype.gettxout = function gettxout(args, callback) { if (args.help || args.length < 2 || args.length > 3) return callback(new RPCError('gettxout "txid" n ( includemempool )')); + if (this.chain.db.options.spv) + return callback(new RPCError('Cannot get coins in SPV mode.')); + + if (this.chain.db.options.prune) + return callback(new RPCError('Cannot get coins when pruned.')); + hash = toHash(args[0]); index = toNumber(args[1]); mempool = true; @@ -1272,6 +1299,12 @@ RPC.prototype.gettxoutproof = function gettxoutproof(args, callback) { + ' ["txid",...] ( blockhash )')); } + if (this.chain.db.options.spv) + return callback(new RPCError('Cannot get coins in SPV mode.')); + + if (this.chain.db.options.prune) + return callback(new RPCError('Cannot get coins when pruned.')); + txids = toArray(args[0]); block = args[1]; @@ -1370,6 +1403,9 @@ RPC.prototype.gettxoutsetinfo = function gettxoutsetinfo(args, callback) { if (args.help || args.length !== 0) return callback(new RPCError('gettxoutsetinfo')); + if (this.chain.db.options.spv) + return callback(new RPCError('Chain state not available in SPV mode.')); + callback(null, { height: this.chain.height, bestblock: this.chain.tip.rhash, @@ -1385,6 +1421,12 @@ RPC.prototype.verifychain = function verifychain(args, callback) { if (args.help || args.length > 2) return callback(new RPCError('verifychain ( checklevel numblocks )')); + if (this.chain.db.options.spv) + return callback(new RPCError('Cannot verify chain in SPV mode.')); + + if (this.chain.db.options.prune) + return callback(new RPCError('Cannot verify chain when pruned.')); + callback(); }; @@ -1440,7 +1482,7 @@ RPC.prototype._submitwork = function getwork(data, callback) { RPC.prototype._getwork = function _getwork(callback) { var i, data, abbr; - this._getAttempt(function(err, attempt) { + this._getAttempt(true, function(err, attempt) { if (err) return callback(err); @@ -1610,7 +1652,7 @@ RPC.prototype._tmpl = function _tmpl(version, coinbase, rules, callback) { if (!callback) return; - this._getAttempt(function(err, attempt) { + this._getAttempt(false, function(err, attempt) { if (err) return callback(err); @@ -1695,7 +1737,7 @@ RPC.prototype._tmpl = function _tmpl(version, coinbase, rules, callback) { flags: attempt.coinbaseFlags.toString('hex') }, coinbasevalue: attempt.coinbase.outputs[0].value, - longpollid: self.chain.tip.rhash + utils.pad32(self.mempool.totalTX), + longpollid: self.chain.tip.rhash + utils.pad32(self._totalTX()), target: utils.revHex(attempt.target.toString('hex')), submitold: false, mintime: block.ts, @@ -1736,7 +1778,6 @@ RPC.prototype._tmpl = function _tmpl(version, coinbase, rules, callback) { }; RPC.prototype._poll = function _poll(lpid, callback) { - var self = this; var watched, lastTX; if (typeof lpid !== 'string') @@ -1753,7 +1794,7 @@ RPC.prototype._poll = function _poll(lpid, callback) { watched = utils.revHex(watched); - if (self.chain.tip.hash !== watched) + if (this.chain.tip.hash !== watched) return callback(); this.once('clear block', callback); @@ -1763,6 +1804,7 @@ RPC.prototype._clearBlock = function _clearBlock() { this.attempt = null; this.start = 0; this.coinbase = {}; + this.emit('clear block'); }; RPC.prototype._bindChain = function _bindChain() { @@ -1778,33 +1820,31 @@ RPC.prototype._bindChain = function _bindChain() { return; self._clearBlock(); - self.emit('clear block'); }); - this.mempool.on('tx', function() { - var diff; + if (!this.mempool) + return; + this.mempool.on('tx', function() { if (!self.attempt) return; - diff = utils.now() - self.start; - - if (diff > 5) { + if (utils.now() - self.start > 5) self._clearBlock(); - self.emit('clear block'); - } }); }; -RPC.prototype._getAttempt = function _getAttempt(callback) { +RPC.prototype._getAttempt = function _getAttempt(update, callback) { var self = this; var attempt = this.attempt; this._bindChain(); if (attempt) { - attempt.updateNonce(); - this.coinbase[attempt.block.merkleRoot] = attempt.coinbase.clone(); + if (update) { + attempt.updateNonce(); + this.coinbase[attempt.block.merkleRoot] = attempt.coinbase.clone(); + } return callback(null, attempt); } @@ -1820,6 +1860,10 @@ RPC.prototype._getAttempt = function _getAttempt(callback) { }); }; +RPC.prototype._totalTX = function _totalTX() { + return this.mempool ? this.mempool.totalTX : 0; +}; + RPC.prototype.getmininginfo = function getmininginfo(args, callback) { var self = this; @@ -1845,7 +1889,7 @@ RPC.prototype.getmininginfo = function getmininginfo(args, callback) { errors: '', genproclimit: self.proclimit, networkhashps: hashps, - pooledtx: self.mempool.totalTX, + pooledtx: self._totalTX(), testnet: self.network !== bcoin.network.main, chain: 'main', generate: self.mining @@ -1878,6 +1922,9 @@ RPC.prototype.prioritisetransaction = function prioritisetransaction(args, callb + ' ')); } + if (!this.mempool) + return callback(new RPCError('No mempool available.')); + hash = toHash(args[0]); pri = args[1]; fee = args[2]; @@ -1990,13 +2037,23 @@ RPC.prototype.setgenerate = function setgenerate(args, callback) { RPC.prototype.generate = function generate(args, callback) { var self = this; - var numblocks, hashes; + var numblocks; + + callback = this.locker.lock(generate, [args, callback]); + + if (!callback) + return; if (args.help || args.length < 1 || args.length > 2) return callback(new RPCError('generate numblocks ( maxtries )')); numblocks = toNumber(args[0], 1); - hashes = []; + + this._generate(numblocks, callback); +}; + +RPC.prototype._generate = function _generate(numblock, callback, force) { + var hashes = []; utils.forRangeSerial(0, numblocks, function(i, next) { self.miner.mineBlock(function(err, block) { @@ -2014,23 +2071,29 @@ RPC.prototype.generate = function generate(args, callback) { RPC.prototype.generatetoaddress = function generatetoaddress(args, callback) { var self = this; - var address; + var numblocks, address; + + callback = this.locker.lock(generatetoaddress, [args, callback]); + + if (!callback) + return; if (args.help || args.length < 2 || args.length > 3) { return callback(new RPCError('generatetoaddress' + ' numblocks address ( maxtries )')); } + numblocks = toNumber(args[0], 1); address = this.miner.address; + this.miner.address = bcoin.address.fromBase58(toString(args[1])); - args = args.slice(); - args.splice(1, 1); - - this.generate(args, function(err, hashes) { + this._generate(numblocks, function(err, hashes) { if (err) return callback(err); + self.miner.address = address; + callback(null, hashes); }); }; @@ -2246,7 +2309,7 @@ RPC.prototype.signrawtransaction = function signrawtransaction(args, callback) { merged = txs[0]; - this.node.fillCoins(merged, function(err) { + this._fillCoins(merged, function(err) { if (err) return callback(err); @@ -2263,6 +2326,13 @@ RPC.prototype.signrawtransaction = function signrawtransaction(args, callback) { }); }; +RPC.prototype._fillCoins = function _fillCoins(tx, callback) { + if (this.chain.db.options.spv) + return callback(); + + this.node.fillCoins(tx, callback); +}; + RPC.prototype._signrawtransaction = function signrawtransaction(merged, txs, args, callback) { var keys = []; var keyMap = {}; @@ -2472,7 +2542,10 @@ RPC.prototype._createRedeem = function _createRedeem(args, callback) { }); }; -/* Utility functions */ +/* + * Utility functions + */ + RPC.prototype.createmultisig = function createmultisig(args, callback) { var self = this; @@ -2619,6 +2692,9 @@ RPC.prototype.estimatefee = function estimatefee(args, callback) { if (args.help || args.length !== 1) return callback(new RPCError('estimatefee nblocks')); + if (!this.fees) + return callback(new RPCError('Fee estimation not available.')); + blocks = toNumber(args[0], 1); if (blocks < 1) @@ -2640,6 +2716,9 @@ RPC.prototype.estimatepriority = function estimatepriority(args, callback) { if (args.help || args.length !== 1) return callback(new RPCError('estimatepriority nblocks')); + if (!this.fees) + return callback(new RPCError('Priority estimation not available.')); + blocks = toNumber(args[0], 1); if (blocks < 1) @@ -2656,6 +2735,9 @@ RPC.prototype.estimatesmartfee = function estimatesmartfee(args, callback) { if (args.help || args.length !== 1) return callback(new RPCError('estimatesmartfee nblocks')); + if (!this.fees) + return callback(new RPCError('Fee estimation not available.')); + blocks = toNumber(args[0], 1); if (blocks < 1) @@ -2680,6 +2762,9 @@ RPC.prototype.estimatesmartpriority = function estimatesmartpriority(args, callb if (args.help || args.length !== 1) return callback(new RPCError('estimatesmartpriority nblocks')); + if (!this.fees) + return callback(new RPCError('Priority estimation not available.')); + blocks = toNumber(args[0], 1); if (blocks < 1)