From 5ee8a9b306c3c8c08e55911e921cd688870950cd Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Wed, 15 Mar 2017 04:49:54 -0700 Subject: [PATCH] rpc: improve gbt proposal and capabilities handling. --- lib/http/rpc.js | 56 ++++++++++++++++++++++++++++++++++----------- lib/http/rpcbase.js | 54 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 13 deletions(-) diff --git a/lib/http/rpc.js b/lib/http/rpc.js index 8734d77c..01988518 100644 --- a/lib/http/rpc.js +++ b/lib/http/rpc.js @@ -32,6 +32,7 @@ var Validator = require('../utils/validator'); var RPCBase = require('./rpcbase'); var pkg = require('../pkg'); var RPCError = RPCBase.RPCError; +var errs = RPCBase.errors; var MAGIC_STRING = RPCBase.MAGIC_STRING; /** @@ -1061,7 +1062,9 @@ RPC.prototype.getBlockTemplate = co(function* getBlockTemplate(args, help) { var capabilities = valid.array('capabilities'); var version = valid.u32('maxversion', -1); var coinbase = false; - var i, cap, block, value, txn; + var txnCap = false; + var valueCap = false; + var i, capability, block; if (help || args.length > 1) throw new RPCError('getblocktemplate ( "jsonrequestobject" )'); @@ -1075,7 +1078,18 @@ RPC.prototype.getBlockTemplate = co(function* getBlockTemplate(args, help) { block = Block.fromRaw(data); - return yield this.addBlock(block); + if (block.prevBlock !== this.chain.tip.hash) + return 'inconclusive-not-best-prevblk'; + + try { + yield this.chain.verifyBlock(block); + } catch (e) { + if (e.type === 'VerifyError') + return e.reason; + throw e; + } + + return null; } if (rules) @@ -1083,21 +1097,29 @@ RPC.prototype.getBlockTemplate = co(function* getBlockTemplate(args, help) { if (capabilities) { for (i = 0; i < capabilities.length; i++) { - cap = capabilities[i]; - switch (cap) { + capability = capabilities[i]; + + if (typeof capability !== 'string') + throw new RPCError('Capability must be a string.'); + + switch (capability) { case 'coinbasetxn': - txn = true; + txnCap = true; break; case 'coinbasevalue': - value = true; + // Prefer value if they support it. + valueCap = true; break; case 'coinbase/append': break; } } - if (txn) + if (txnCap && !valueCap) { + if (this.miner.addresses.length === 0) + throw new RPCError('No addresses available for coinbase.'); coinbase = true; + } } if (!this.network.selfConnect) { @@ -1224,10 +1246,8 @@ RPC.prototype._createTemplate = co(function* _createTemplate(version, coinbase, weightlimit: undefined, longpollid: this.chain.tip.rhash() + util.pad32(this.totalTX()), submitold: false, - coinbaseaux: { - flags: attempt.coinbaseFlags.toString('hex') - }, - coinbasevalue: attempt.getReward(), + coinbaseaux: undefined, + coinbasevalue: undefined, coinbasetxn: undefined, default_witness_commitment: undefined, transactions: txs @@ -1243,11 +1263,16 @@ RPC.prototype._createTemplate = co(function* _createTemplate(version, coinbase, if (coinbase) { tx = attempt.toCoinbase(); - // We don't include the commitment - // output (see bip145). if (attempt.witness) { + // We don't include the commitment + // output (see bip145). output = tx.outputs.pop(); assert(output.script.isCommitment()); + + // Also not including the witness nonce. + tx.inputs[0].witness.length = 0; + tx.inputs[0].witness.compile(); + tx.refresh(); } @@ -1260,6 +1285,11 @@ RPC.prototype._createTemplate = co(function* _createTemplate(version, coinbase, sigops: tx.getSigopsCost() / scale | 0, weight: tx.getWeight() }; + } else { + json.coinbaseaux = { + flags: attempt.coinbaseFlags.toString('hex') + }; + json.coinbasevalue = attempt.getReward(); } if (rules && rules.indexOf('segwit') !== -1) diff --git a/lib/http/rpcbase.js b/lib/http/rpcbase.js index e9e8ef0a..cab7edac 100644 --- a/lib/http/rpcbase.js +++ b/lib/http/rpcbase.js @@ -33,6 +33,60 @@ function RPCBase() { util.inherits(RPCBase, EventEmitter); +/** + * RPC errors. + * @enum {Number} + * @default + */ + +RPCBase.errors = { + // Standard JSON-RPC 2.0 errors + INVALID_REQUEST: -32600, + METHOD_NOT_FOUND: -32601, + INVALID_PARAMS: -32602, + INTERNAL_ERROR: -32603, + PARSE_ERROR: -32700, + + // General application defined errors + MISC_ERROR: -1, + FORBIDDEN_BY_SAFE_MODE: -2, + TYPE_ERROR: -3, + INVALID_ADDRESS_OR_KEY: -5, + OUT_OF_MEMORY: -7, + INVALID_PARAMETER: -8, + DATABASE_ERROR: -20, + DESERIALIZATION_ERROR: -22, + VERIFY_ERROR: -25, + VERIFY_REJECTED: -26, + VERIFY_ALREADY_IN_CHAIN: -27, + IN_WARMUP: -28, + + // Aliases for backward compatibility + TRANSACTION_ERROR: -25, + TRANSACTION_REJECTED: -26, + TRANSACTION_ALREADY_IN_CHAIN: -27, + + // P2P client errors + CLIENT_NOT_CONNECTED: -9, + CLIENT_IN_INITIAL_DOWNLOAD: -10, + CLIENT_NODE_ALREADY_ADDED: -23, + CLIENT_NODE_NOT_ADDED: -24, + CLIENT_NODE_NOT_CONNECTED: -29, + CLIENT_INVALID_IP_OR_SUBNET: -30, + CLIENT_P2P_DISABLED: -31, + + // Wallet errors + WALLET_ERROR: -4, + WALLET_INSUFFICIENT_FUNDS: -6, + WALLET_INVALID_ACCOUNT_NAME: -11, + WALLET_KEYPOOL_RAN_OUT: -12, + WALLET_UNLOCK_NEEDED: -13, + WALLET_PASSPHRASE_INCORRECT: -14, + WALLET_WRONG_ENC_STATE: -15, + WALLET_ENCRYPTION_FAILED: -16, + WALLET_ALREADY_UNLOCKED: -17 +}; + /** * Magic string for signing. * @const {String}