From a87260c959fc8294c17c942005d414cd2c72892c Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Sun, 22 Oct 2017 15:51:30 -0700 Subject: [PATCH] http: rpc improvements. minor fixes. --- bin/cli | 17 ++++++- lib/http/rpc.js | 105 +++++++++++++++++++++++++++++++---------- lib/http/server.js | 21 +++++++-- lib/wallet/http.js | 29 ++++++++---- lib/wallet/rpc.js | 71 +++++++++++++++++++++++++--- lib/wallet/walletdb.js | 7 +-- 6 files changed, 196 insertions(+), 54 deletions(-) diff --git a/bin/cli b/bin/cli index cde162a7..b345b959 100755 --- a/bin/cli +++ b/bin/cli @@ -7,6 +7,13 @@ const util = require('../lib/utils/util'); const Client = require('../lib/http/client'); const Wallet = require('../lib/http/wallet'); +const ports = { + main: 8332, + testnet: 18332, + regtest: 48332, + simnet: 18556 +}; + const ANTIREPLAY = '' + '6a2e426974636f696e3a204120506565722d746f2d5065657' + '220456c656374726f6e696320436173682053797374656d'; @@ -536,7 +543,10 @@ CLI.prototype.handleWallet = async function handleWallet() { this.wallet = new Wallet({ url: this.config.str(['url', 'uri']), apiKey: this.config.str('api-key'), - network: this.config.str('network'), + host: this.config.str('http-host'), + port: this.config.uint('http-port') + || ports[this.config.str('network', '')] + || ports.main, id: this.config.str('id', 'primary'), token: this.config.str('token') }); @@ -695,7 +705,10 @@ CLI.prototype.handleNode = async function handleNode() { this.client = new Client({ url: this.config.str(['url', 'uri']), apiKey: this.config.str('api-key'), - network: this.config.str('network') + host: this.config.str('http-host'), + port: this.config.uint('http-port') + || ports[this.config.str('network', '')] + || ports.main }); switch (this.argv.shift()) { diff --git a/lib/http/rpc.js b/lib/http/rpc.js index 4957f263..2dc572b6 100644 --- a/lib/http/rpc.js +++ b/lib/http/rpc.js @@ -36,14 +36,54 @@ const pkg = require('../pkg'); const Lock = require('../utils/lock'); const RPCBase = bweb.RPC; const RPCError = bweb.RPCError; -const errs = bweb.errors; + +/* + * Constants + */ + +const errs = { + // Standard JSON-RPC 2.0 errors + INVALID_REQUEST: bweb.errors.INVALID_REQUEST, + METHOD_NOT_FOUND: bweb.errors.METHOD_NOT_FOUND, + INVALID_PARAMS: bweb.errors.INVALID_PARAMS, + INTERNAL_ERROR: bweb.errors.INTERNAL_ERROR, + PARSE_ERROR: bweb.errors.PARSE_ERROR, + + // 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, + + // 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 +}; + const MAGIC_STRING = 'Bitcoin Signed Message:\n'; +/** + * Bitcoin RPC + * @alias module:http.RPC + * @extends bweb.RPC + */ + class RPC extends RPCBase { /** - * Bitcoin Core RPC - * @alias module:http.RPC - * @constructor + * Create RPC. * @param {Node} node */ @@ -76,6 +116,41 @@ class RPC extends RPCBase { this.init(); } + getCode(err) { + switch (err.type) { + case 'RPCError': + return err.code; + case 'ValidationError': + return errors.TYPE_ERROR; + case 'EncodingError': + return errors.DESERIALIZATION_ERROR; + default: + return errors.INTERNAL_ERROR; + } + } + + handleCall(cmd, query) { + if (cmd.method !== 'getwork' + && cmd.method !== 'getblocktemplate' + && cmd.method !== 'getbestblockhash') { + this.logger.debug('Handling RPC call: %s.', cmd.method); + if (cmd.method !== 'submitblock' + && cmd.method !== 'getmemorypool') { + this.logger.debug(cmd.params); + } + } + + if (cmd.method === 'getwork') { + if (query.longpoll) + cmd.method = 'getworklp'; + } + } + + handleError(err) { + this.logger.error('RPC internal error.'); + this.logger.error(err); + } + init() { this.add('stop', this.stop); this.add('help', this.help); @@ -154,28 +229,6 @@ class RPC extends RPCBase { this.add('getmemoryinfo', this.getMemoryInfo); this.add('setloglevel', this.setLogLevel); - - this.on('error', (err) => { - this.logger.error('RPC internal error.'); - this.logger.error(err); - }); - - this.on('call', (cmd, query) => { - if (cmd.method !== 'getwork' - && cmd.method !== 'getblocktemplate' - && cmd.method !== 'getbestblockhash') { - this.logger.debug('Handling RPC call: %s.', cmd.method); - if (cmd.method !== 'submitblock' - && cmd.method !== 'getmemorypool') { - this.logger.debug(cmd.params); - } - } - - if (cmd.method === 'getwork') { - if (query.longpoll) - cmd.method = 'getworklp'; - } - }); } /* diff --git a/lib/http/server.js b/lib/http/server.js index 194177cf..5ae7e318 100644 --- a/lib/http/server.js +++ b/lib/http/server.js @@ -95,6 +95,17 @@ class HTTPServer extends Server { this.use(this.jsonRPC()); this.use(this.router()); + this.error((err, req, res) => { + const code = err.statusCode || 500; + res.json(code, { + error: { + type: err.type, + code: err.code, + message: err.message + } + }); + }); + this.get('/', async (req, res) => { const totalTX = this.mempool ? this.mempool.map.size : 0; const size = this.mempool ? this.mempool.getSize() : 0; @@ -163,7 +174,7 @@ class HTTPServer extends Server { const coin = await this.node.getCoin(hash, index); if (!coin) { - res.send(404); + res.json(404); return; } @@ -198,7 +209,7 @@ class HTTPServer extends Server { const meta = await this.node.getMeta(hash); if (!meta) { - res.send(404); + res.json(404); return; } @@ -256,14 +267,14 @@ class HTTPServer extends Server { const block = await this.chain.getBlock(hash); if (!block) { - res.send(404); + res.json(404); return; } const view = await this.chain.getBlockView(block); if (!view) { - res.send(404); + res.json(404); return; } @@ -306,7 +317,7 @@ class HTTPServer extends Server { const blocks = valid.u32('blocks'); if (!this.fees) { - res.send(200, { rate: this.network.feeRate }); + res.json(200, { rate: this.network.feeRate }); return; } diff --git a/lib/wallet/http.js b/lib/wallet/http.js index fd6086d5..12f06bb9 100644 --- a/lib/wallet/http.js +++ b/lib/wallet/http.js @@ -93,6 +93,17 @@ class HTTPServer extends Server { this.use(this.jsonRPC()); this.use(this.router()); + this.error((err, req, res) => { + const code = err.statusCode || 500; + res.json(code, { + error: { + type: err.type, + code: err.code, + message: err.message + } + }); + }); + this.hook(async (req, res) => { const valid = Validator.fromRequest(req); @@ -112,7 +123,7 @@ class HTTPServer extends Server { const wallet = await this.wdb.get(id); if (!wallet) { - res.send(404); + res.json(404); return; } @@ -131,7 +142,7 @@ class HTTPServer extends Server { } if (!wallet) { - res.send(404); + res.json(404); return; } @@ -234,7 +245,7 @@ class HTTPServer extends Server { const account = await req.wallet.getAccount(acct); if (!account) { - res.send(404); + res.json(404); return; } @@ -490,7 +501,7 @@ class HTTPServer extends Server { const block = await req.wallet.getBlock(height); if (!block) { - res.send(404); + res.json(404); return; } @@ -538,7 +549,7 @@ class HTTPServer extends Server { const key = await req.wallet.getKey(addr); if (!key) { - res.send(404); + res.json(404); return; } @@ -557,7 +568,7 @@ class HTTPServer extends Server { const key = await req.wallet.getPrivateKey(addr, passphrase); if (!key) { - res.send(404); + res.json(404); return; } @@ -598,7 +609,7 @@ class HTTPServer extends Server { const balance = await req.wallet.getBalance(acct); if (!balance) { - res.send(404); + res.json(404); return; } @@ -675,7 +686,7 @@ class HTTPServer extends Server { const coin = await req.wallet.getCoin(hash, index); if (!coin) { - res.send(404); + res.json(404); return; } @@ -764,7 +775,7 @@ class HTTPServer extends Server { const tx = await req.wallet.getTX(hash); if (!tx) { - res.send(404); + res.json(404); return; } diff --git a/lib/wallet/rpc.js b/lib/wallet/rpc.js index b8f9f19b..4a80ec0e 100644 --- a/lib/wallet/rpc.js +++ b/lib/wallet/rpc.js @@ -27,14 +27,56 @@ const Lock = require('../utils/lock'); const common = require('./common'); const RPCBase = bweb.RPC; const RPCError = bweb.RPCError; -const errs = bweb.errors; + +/* + * Constants + */ + +const errs = { + // Standard JSON-RPC 2.0 errors + INVALID_REQUEST: bweb.errors.INVALID_REQUEST, + METHOD_NOT_FOUND: bweb.errors.METHOD_NOT_FOUND, + INVALID_PARAMS: bweb.errors.INVALID_PARAMS, + INTERNAL_ERROR: bweb.errors.INTERNAL_ERROR, + PARSE_ERROR: bweb.errors.PARSE_ERROR, + + // 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, + + // 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 +}; + const MAGIC_STRING = 'Bitcoin Signed Message:\n'; +/** + * Wallet RPC + * @alias module:wallet.RPC + * @extends bweb.RPC + */ + class RPC extends RPCBase { /** - * Bitcoin Core RPC - * @alias module:wallet.RPC - * @constructor + * Create an RPC. * @param {WalletDB} wdb */ @@ -54,6 +96,21 @@ class RPC extends RPCBase { this.init(); } + getCode(err) { + switch (err.type) { + case 'RPCError': + return err.code; + case 'ValidationError': + return errors.TYPE_ERROR; + case 'EncodingError': + return errors.DESERIALIZATION_ERROR; + case 'FundingError': + return errors.WALLET_INSUFFICIENT_FUNDS; + default: + return errors.INTERNAL_ERROR; + } + } + init() { this.add('help', this.help); this.add('stop', this.stop); @@ -175,9 +232,9 @@ class RPC extends RPCBase { }; } -/* - * Wallet - */ + /* + * Wallet + */ async resendWalletTransactions(args, help) { if (help || args.length !== 0) diff --git a/lib/wallet/walletdb.js b/lib/wallet/walletdb.js index 4db37f22..1847b407 100644 --- a/lib/wallet/walletdb.js +++ b/lib/wallet/walletdb.js @@ -160,8 +160,7 @@ WalletDB.prototype._open = async function _open() { this.primary = wallet; this.rpc.wallet = wallet; - if (this.http && this.options.listen) - await this.http.open(); + await this.http.open(); }; /** @@ -172,9 +171,7 @@ WalletDB.prototype._open = async function _open() { WalletDB.prototype._close = async function _close() { await this.disconnect(); - - if (this.http && this.options.listen) - await this.http.close(); + await this.http.close(); for (const wallet of this.wallets.values()) { await wallet.destroy();