Skip to content

Commit 6556da7

Browse files
committed
Merge bitcoin/bitcoin#21056: rpc: Add a -rpcwaittimeout parameter to limit time spent waiting
b9e76f1 rpc: Add test for -rpcwaittimeout (Christian Decker) f76cb10 rpc: Prefix rpcwaittimeout error with details on its nature (Christian Decker) c490e17 doc: Add release notes for the `-rpcwaittimeout` cli parameter (Christian Decker) a7fcc8e rpc: Add a `-rpcwaittimeout` parameter to limit time spent waiting (Christian Decker) Pull request description: Adds a new numeric `-rpcwaittimeout` that can be used to limit the time we spend waiting on the RPC server to appear. This is used by downstream projects to provide a bit of slack when `bitcoind`s RPC interface is not available right away. This makes the `-rpcwait` argument more useful, since we can now limit how long we'll ultimately wait, before potentially giving up and reporting an error to the caller. It was discussed in the context of the BTCPayServer wanting to have c-lightning wait for the RPC interface to become available but still have the option of giving up eventually ([4355]). I checked with laanwj whether this is already possible ([comment]), and whether this would be a welcome change. Initially I intended to repurpose the (optional) argument to `-rpcwait`, however I decided against it since it would potentially break existing configurations, using things like `rpcwait=1`, or `rpcwait=true` (the former would have an unintended short timeout, when old behavior was to wait indefinitely). ~Due to its simplicity I didn't implement a test for it yet, but if that's desired I can provide one.~ Test was added during reviews. [4355]: ElementsProject/lightning#4355 [comment]: ElementsProject/lightning#4355 (comment) ACKs for top commit: laanwj: Code review ACK b9e76f1 promag: ACK b9e76f1. Tree-SHA512: 3cd6728038ec7ca7c35c2e7ccb213bfbe963f99a49bb48bbc1e511c4dd23d9957c04f9af1f8ec57120e47b26eaf580b46817b099d5fc5083c98da7aa92db8638
2 parents 6a67366 + b9e76f1 commit 6556da7

File tree

3 files changed

+24
-4
lines changed

3 files changed

+24
-4
lines changed

doc/release-notes-21056.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
New bitcoin-cli settings
2+
------------------------
3+
4+
- A new `-rpcwaittimeout` argument to `bitcoin-cli` sets the timeout
5+
in seconds to use with `-rpcwait`. If the timeout expires,
6+
`bitcoin-cli` will report a failure. (#21056)

src/bitcoin-cli.cpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ UrlDecodeFn* const URL_DECODE = urlDecode;
4040

4141
static const char DEFAULT_RPCCONNECT[] = "127.0.0.1";
4242
static const int DEFAULT_HTTP_CLIENT_TIMEOUT=900;
43+
static constexpr int DEFAULT_WAIT_CLIENT_TIMEOUT = 0;
4344
static const bool DEFAULT_NAMED=false;
4445
static const int CONTINUE_EXECUTION=-1;
4546
static constexpr int8_t UNKNOWN_NETWORK{-1};
@@ -73,6 +74,7 @@ static void SetupCliArgs(ArgsManager& argsman)
7374
argsman.AddArg("-rpcport=<port>", strprintf("Connect to JSON-RPC on <port> (default: %u, testnet: %u, signet: %u, regtest: %u)", defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort(), signetBaseParams->RPCPort(), regtestBaseParams->RPCPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::OPTIONS);
7475
argsman.AddArg("-rpcuser=<user>", "Username for JSON-RPC connections", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
7576
argsman.AddArg("-rpcwait", "Wait for RPC server to start", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
77+
argsman.AddArg("-rpcwaittimeout=<n>", strprintf("Timeout in seconds to wait for the RPC server to start, or 0 for no timeout. (default: %d)", DEFAULT_WAIT_CLIENT_TIMEOUT), ArgsManager::ALLOW_INT, OptionsCategory::OPTIONS);
7678
argsman.AddArg("-rpcwallet=<walletname>", "Send RPC for non-default wallet on RPC server (needs to exactly match corresponding -wallet option passed to bitcoind). This changes the RPC endpoint used, e.g. http://127.0.0.1:8332/wallet/<walletname>", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
7779
argsman.AddArg("-stdin", "Read extra arguments from standard input, one per line until EOF/Ctrl-D (recommended for sensitive information such as passphrases). When combined with -stdinrpcpass, the first line from standard input is used for the RPC password.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
7880
argsman.AddArg("-stdinrpcpass", "Read RPC password from standard input as a single line. When combined with -stdin, the first line from standard input is used for the RPC password. When combined with -stdinwalletpassphrase, -stdinrpcpass consumes the first line, and -stdinwalletpassphrase consumes the second.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
@@ -794,6 +796,9 @@ static UniValue ConnectAndCallRPC(BaseRequestHandler* rh, const std::string& str
794796
UniValue response(UniValue::VOBJ);
795797
// Execute and handle connection failures with -rpcwait.
796798
const bool fWait = gArgs.GetBoolArg("-rpcwait", false);
799+
const int timeout = gArgs.GetArg("-rpcwaittimeout", DEFAULT_WAIT_CLIENT_TIMEOUT);
800+
const int64_t deadline = GetTime<std::chrono::seconds>().count() + timeout;
801+
797802
do {
798803
try {
799804
response = CallRPC(rh, strMethod, args, rpcwallet);
@@ -804,11 +809,12 @@ static UniValue ConnectAndCallRPC(BaseRequestHandler* rh, const std::string& str
804809
}
805810
}
806811
break; // Connection succeeded, no need to retry.
807-
} catch (const CConnectionFailed&) {
808-
if (fWait) {
809-
UninterruptibleSleep(std::chrono::milliseconds{1000});
812+
} catch (const CConnectionFailed& e) {
813+
const int64_t now = GetTime<std::chrono::seconds>().count();
814+
if (fWait && (timeout <= 0 || now < deadline)) {
815+
UninterruptibleSleep(std::chrono::seconds{1});
810816
} else {
811-
throw;
817+
throw CConnectionFailed(strprintf("timeout on transient error: %s", e.what()));
812818
}
813819
}
814820
} while (fWait);

test/functional/interface_bitcoin_cli.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@
1010
from test_framework.test_framework import BitcoinTestFramework
1111
from test_framework.util import (
1212
assert_equal,
13+
assert_greater_than_or_equal,
1314
assert_raises_process_error,
1415
assert_raises_rpc_error,
1516
get_auth_cookie,
1617
)
18+
import time
1719

1820
# The block reward of coinbaseoutput.nValue (50) BTC/block matures after
1921
# COINBASE_MATURITY (100) blocks. Therefore, after mining 101 blocks we expect
@@ -248,6 +250,12 @@ def run_test(self):
248250
self.nodes[0].wait_for_rpc_connection()
249251
assert_equal(blocks, BLOCKS + 25)
250252

253+
self.log.info("Test -rpcwait option waits at most -rpcwaittimeout seconds for startup")
254+
self.stop_node(0) # stop the node so we time out
255+
start_time = time.time()
256+
assert_raises_process_error(1, "Could not connect to the server", self.nodes[0].cli('-rpcwait', '-rpcwaittimeout=5').echo)
257+
assert_greater_than_or_equal(time.time(), start_time + 5)
258+
251259

252260
if __name__ == '__main__':
253261
TestBitcoinCli().main()

0 commit comments

Comments
 (0)