diff options
author | Wladimir J. van der Laan <laanwj@protonmail.com> | 2019-10-28 11:56:43 +0100 |
---|---|---|
committer | Wladimir J. van der Laan <laanwj@protonmail.com> | 2019-10-28 12:00:36 +0100 |
commit | 9ae468a6d5250c6d55b4879834179ceec1ecc180 (patch) | |
tree | fab39001507bbcc8a22943dbb54476e4dc8c3702 | |
parent | be50469217bd775c4305938634c32e5932f47841 (diff) | |
parent | faeb6665362e35f573ad715ade0ef2db62d71839 (diff) | |
download | bitcoin-9ae468a6d5250c6d55b4879834179ceec1ecc180.tar.xz |
Merge #17192: util: Add CHECK_NONFATAL and use it in src/rpc
faeb6665362e35f573ad715ade0ef2db62d71839 util: Add CHECK_NONFATAL and use it in src/rpc (MarcoFalke)
Pull request description:
Fixes #17181
Currently, we use `assert` in RPC code to document logic and code assumptions. However, it seems a bit extreme to abort all of Bitcoin Core on an assert failure in one of the RPC threads. I suggest to replace all `assert`s with a macro `CHECK_NONFATAL(condition)` that throws a runtime error when the condition evaluates to `false`. That runtime error will then be returned to the rpc caller and will include instructions to report the error to our issue tracker.
ACKs for top commit:
practicalswift:
ACK faeb6665362e35f573ad715ade0ef2db62d71839
laanwj:
ACK faeb6665362e35f573ad715ade0ef2db62d71839
ryanofsky:
Code review ACK faeb6665362e35f573ad715ade0ef2db62d71839
Tree-SHA512: 9b748715a5e0767ac11f1324a95a3a6ec672a0e0658013492219223bda83ce4b1b447fd8183bbb235f7df5ef7dddda7666ad569544b4d61cc65f232ca7a800ec
-rw-r--r-- | src/Makefile.am | 1 | ||||
-rw-r--r-- | src/rpc/blockchain.cpp | 3 | ||||
-rw-r--r-- | src/rpc/misc.cpp | 8 | ||||
-rw-r--r-- | src/rpc/util.h | 13 | ||||
-rw-r--r-- | src/util/check.h | 41 | ||||
-rwxr-xr-x | test/functional/rpc_misc.py | 7 |
6 files changed, 63 insertions, 10 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 2093ed15e2..82cc19d57c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -206,6 +206,7 @@ BITCOIN_CORE_H = \ undo.h \ util/bip32.h \ util/bytevectorhash.h \ + util/check.h \ util/error.h \ util/fees.h \ util/spanparsing.h \ diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 4bbd4aaf64..fac6bcd60d 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -10,11 +10,11 @@ #include <chain.h> #include <chainparams.h> #include <coins.h> -#include <node/coinstats.h> #include <consensus/validation.h> #include <core_io.h> #include <hash.h> #include <index/blockfilterindex.h> +#include <node/coinstats.h> #include <policy/feerate.h> #include <policy/policy.h> #include <policy/rbf.h> @@ -34,7 +34,6 @@ #include <validationinterface.h> #include <warnings.h> -#include <assert.h> #include <stdint.h> #include <univalue.h> diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index d289274a37..d73dd6e52d 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -3,15 +3,16 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include <key_io.h> #include <httpserver.h> +#include <key_io.h> #include <outputtype.h> #include <rpc/blockchain.h> #include <rpc/server.h> #include <rpc/util.h> #include <script/descriptor.h> -#include <util/system.h> +#include <util/check.h> #include <util/strencodings.h> +#include <util/system.h> #include <util/validation.h> #include <stdint.h> @@ -540,6 +541,7 @@ static UniValue echo(const JSONRPCRequest& request) throw std::runtime_error( RPCHelpMan{"echo|echojson ...", "\nSimply echo back the input arguments. This command is for testing.\n" + "\nIt will return an internal bug report when exactly 100 arguments are passed.\n" "\nThe difference between echo and echojson is that echojson has argument conversion enabled in the client-side table in " "bitcoin-cli and the GUI. There is no server-side difference.", {}, @@ -548,6 +550,8 @@ static UniValue echo(const JSONRPCRequest& request) }.ToString() ); + CHECK_NONFATAL(request.params.size() != 100); + return request.params; } diff --git a/src/rpc/util.h b/src/rpc/util.h index 72fc7b6286..ec36956c95 100644 --- a/src/rpc/util.h +++ b/src/rpc/util.h @@ -7,14 +7,15 @@ #include <node/transaction.h> #include <outputtype.h> -#include <pubkey.h> #include <protocol.h> +#include <pubkey.h> #include <rpc/protocol.h> #include <rpc/request.h> #include <script/script.h> #include <script/sign.h> #include <script/standard.h> #include <univalue.h> +#include <util/check.h> #include <string> #include <vector> @@ -146,7 +147,7 @@ struct RPCArg { m_oneline_description{oneline_description}, m_type_str{type_str} { - assert(type != Type::ARR && type != Type::OBJ); + CHECK_NONFATAL(type != Type::ARR && type != Type::OBJ); } RPCArg( @@ -165,7 +166,7 @@ struct RPCArg { m_oneline_description{oneline_description}, m_type_str{type_str} { - assert(type == Type::ARR || type == Type::OBJ); + CHECK_NONFATAL(type == Type::ARR || type == Type::OBJ); } bool IsOptional() const; @@ -194,14 +195,14 @@ struct RPCResult { explicit RPCResult(std::string result) : m_cond{}, m_result{std::move(result)} { - assert(!m_result.empty()); + CHECK_NONFATAL(!m_result.empty()); } RPCResult(std::string cond, std::string result) : m_cond{std::move(cond)}, m_result{std::move(result)} { - assert(!m_cond.empty()); - assert(!m_result.empty()); + CHECK_NONFATAL(!m_cond.empty()); + CHECK_NONFATAL(!m_result.empty()); } }; diff --git a/src/util/check.h b/src/util/check.h new file mode 100644 index 0000000000..d18887ae95 --- /dev/null +++ b/src/util/check.h @@ -0,0 +1,41 @@ +// Copyright (c) 2019 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_UTIL_CHECK_H +#define BITCOIN_UTIL_CHECK_H + +#include <tinyformat.h> + +#include <stdexcept> + +class NonFatalCheckError : public std::runtime_error +{ + using std::runtime_error::runtime_error; +}; + +/** + * Throw a NonFatalCheckError when the condition evaluates to false + * + * This should only be used + * - where the condition is assumed to be true, not for error handling or validating user input + * - where a failure to fulfill the condition is recoverable and does not abort the program + * + * For example in RPC code, where it is undersirable to crash the whole program, this can be generally used to replace + * asserts or recoverable logic errors. A NonFatalCheckError in RPC code is caught and passed as a string to the RPC + * caller, which can then report the issue to the developers. + */ +#define CHECK_NONFATAL(condition) \ + do { \ + if (!(condition)) { \ + throw NonFatalCheckError( \ + strprintf("%s:%d (%s)\n" \ + "Internal bug detected: '%s'\n" \ + "You may report this issue here: %s\n", \ + __FILE__, __LINE__, __func__, \ + (#condition), \ + PACKAGE_BUGREPORT)); \ + } \ + } while (false) + +#endif // BITCOIN_UTIL_CHECK_H diff --git a/test/functional/rpc_misc.py b/test/functional/rpc_misc.py index 8a3f8c6f06..3da9f05ca5 100755 --- a/test/functional/rpc_misc.py +++ b/test/functional/rpc_misc.py @@ -23,6 +23,13 @@ class RpcMiscTest(BitcoinTestFramework): def run_test(self): node = self.nodes[0] + self.log.info("test CHECK_NONFATAL") + assert_raises_rpc_error( + -1, + "Internal bug detected: 'request.params.size() != 100'", + lambda: node.echo(*[0] * 100), + ) + self.log.info("test getmemoryinfo") memory = node.getmemoryinfo()['locked'] assert_greater_than(memory['used'], 0) |