Skip to content

Commit 9ae468a

Browse files
committed
Merge #17192: util: Add CHECK_NONFATAL and use it in src/rpc
faeb666 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 faeb666 laanwj: ACK faeb666 ryanofsky: Code review ACK faeb666 Tree-SHA512: 9b748715a5e0767ac11f1324a95a3a6ec672a0e0658013492219223bda83ce4b1b447fd8183bbb235f7df5ef7dddda7666ad569544b4d61cc65f232ca7a800ec
2 parents be50469 + faeb666 commit 9ae468a

File tree

6 files changed

+63
-10
lines changed

6 files changed

+63
-10
lines changed

src/Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ BITCOIN_CORE_H = \
206206
undo.h \
207207
util/bip32.h \
208208
util/bytevectorhash.h \
209+
util/check.h \
209210
util/error.h \
210211
util/fees.h \
211212
util/spanparsing.h \

src/rpc/blockchain.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@
1010
#include <chain.h>
1111
#include <chainparams.h>
1212
#include <coins.h>
13-
#include <node/coinstats.h>
1413
#include <consensus/validation.h>
1514
#include <core_io.h>
1615
#include <hash.h>
1716
#include <index/blockfilterindex.h>
17+
#include <node/coinstats.h>
1818
#include <policy/feerate.h>
1919
#include <policy/policy.h>
2020
#include <policy/rbf.h>
@@ -34,7 +34,6 @@
3434
#include <validationinterface.h>
3535
#include <warnings.h>
3636

37-
#include <assert.h>
3837
#include <stdint.h>
3938

4039
#include <univalue.h>

src/rpc/misc.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,16 @@
33
// Distributed under the MIT software license, see the accompanying
44
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
55

6-
#include <key_io.h>
76
#include <httpserver.h>
7+
#include <key_io.h>
88
#include <outputtype.h>
99
#include <rpc/blockchain.h>
1010
#include <rpc/server.h>
1111
#include <rpc/util.h>
1212
#include <script/descriptor.h>
13-
#include <util/system.h>
13+
#include <util/check.h>
1414
#include <util/strencodings.h>
15+
#include <util/system.h>
1516
#include <util/validation.h>
1617

1718
#include <stdint.h>
@@ -540,6 +541,7 @@ static UniValue echo(const JSONRPCRequest& request)
540541
throw std::runtime_error(
541542
RPCHelpMan{"echo|echojson ...",
542543
"\nSimply echo back the input arguments. This command is for testing.\n"
544+
"\nIt will return an internal bug report when exactly 100 arguments are passed.\n"
543545
"\nThe difference between echo and echojson is that echojson has argument conversion enabled in the client-side table in "
544546
"bitcoin-cli and the GUI. There is no server-side difference.",
545547
{},
@@ -548,6 +550,8 @@ static UniValue echo(const JSONRPCRequest& request)
548550
}.ToString()
549551
);
550552

553+
CHECK_NONFATAL(request.params.size() != 100);
554+
551555
return request.params;
552556
}
553557

src/rpc/util.h

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,15 @@
77

88
#include <node/transaction.h>
99
#include <outputtype.h>
10-
#include <pubkey.h>
1110
#include <protocol.h>
11+
#include <pubkey.h>
1212
#include <rpc/protocol.h>
1313
#include <rpc/request.h>
1414
#include <script/script.h>
1515
#include <script/sign.h>
1616
#include <script/standard.h>
1717
#include <univalue.h>
18+
#include <util/check.h>
1819

1920
#include <string>
2021
#include <vector>
@@ -146,7 +147,7 @@ struct RPCArg {
146147
m_oneline_description{oneline_description},
147148
m_type_str{type_str}
148149
{
149-
assert(type != Type::ARR && type != Type::OBJ);
150+
CHECK_NONFATAL(type != Type::ARR && type != Type::OBJ);
150151
}
151152

152153
RPCArg(
@@ -165,7 +166,7 @@ struct RPCArg {
165166
m_oneline_description{oneline_description},
166167
m_type_str{type_str}
167168
{
168-
assert(type == Type::ARR || type == Type::OBJ);
169+
CHECK_NONFATAL(type == Type::ARR || type == Type::OBJ);
169170
}
170171

171172
bool IsOptional() const;
@@ -194,14 +195,14 @@ struct RPCResult {
194195
explicit RPCResult(std::string result)
195196
: m_cond{}, m_result{std::move(result)}
196197
{
197-
assert(!m_result.empty());
198+
CHECK_NONFATAL(!m_result.empty());
198199
}
199200

200201
RPCResult(std::string cond, std::string result)
201202
: m_cond{std::move(cond)}, m_result{std::move(result)}
202203
{
203-
assert(!m_cond.empty());
204-
assert(!m_result.empty());
204+
CHECK_NONFATAL(!m_cond.empty());
205+
CHECK_NONFATAL(!m_result.empty());
205206
}
206207
};
207208

src/util/check.h

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright (c) 2019 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#ifndef BITCOIN_UTIL_CHECK_H
6+
#define BITCOIN_UTIL_CHECK_H
7+
8+
#include <tinyformat.h>
9+
10+
#include <stdexcept>
11+
12+
class NonFatalCheckError : public std::runtime_error
13+
{
14+
using std::runtime_error::runtime_error;
15+
};
16+
17+
/**
18+
* Throw a NonFatalCheckError when the condition evaluates to false
19+
*
20+
* This should only be used
21+
* - where the condition is assumed to be true, not for error handling or validating user input
22+
* - where a failure to fulfill the condition is recoverable and does not abort the program
23+
*
24+
* For example in RPC code, where it is undersirable to crash the whole program, this can be generally used to replace
25+
* asserts or recoverable logic errors. A NonFatalCheckError in RPC code is caught and passed as a string to the RPC
26+
* caller, which can then report the issue to the developers.
27+
*/
28+
#define CHECK_NONFATAL(condition) \
29+
do { \
30+
if (!(condition)) { \
31+
throw NonFatalCheckError( \
32+
strprintf("%s:%d (%s)\n" \
33+
"Internal bug detected: '%s'\n" \
34+
"You may report this issue here: %s\n", \
35+
__FILE__, __LINE__, __func__, \
36+
(#condition), \
37+
PACKAGE_BUGREPORT)); \
38+
} \
39+
} while (false)
40+
41+
#endif // BITCOIN_UTIL_CHECK_H

test/functional/rpc_misc.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@ def set_test_params(self):
2323
def run_test(self):
2424
node = self.nodes[0]
2525

26+
self.log.info("test CHECK_NONFATAL")
27+
assert_raises_rpc_error(
28+
-1,
29+
"Internal bug detected: 'request.params.size() != 100'",
30+
lambda: node.echo(*[0] * 100),
31+
)
32+
2633
self.log.info("test getmemoryinfo")
2734
memory = node.getmemoryinfo()['locked']
2835
assert_greater_than(memory['used'], 0)

0 commit comments

Comments
 (0)