Skip to content

Commit

Permalink
Merge bitcoin#21056: rpc: Add a -rpcwaittimeout parameter to limit …
Browse files Browse the repository at this point in the history
…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
  • Loading branch information
laanwj authored and vijaydasmp committed Dec 11, 2023
1 parent f17e403 commit b2f6adb
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 4 deletions.
6 changes: 6 additions & 0 deletions doc/release-notes-21056.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
New bitcoin-cli settings
------------------------

- A new `-rpcwaittimeout` argument to `bitcoin-cli` sets the timeout
in seconds to use with `-rpcwait`. If the timeout expires,
`bitcoin-cli` will report a failure. (#21056)
14 changes: 10 additions & 4 deletions src/bitcoin-cli.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ UrlDecodeFn* const URL_DECODE = urlDecode;

static const char DEFAULT_RPCCONNECT[] = "127.0.0.1";
static const int DEFAULT_HTTP_CLIENT_TIMEOUT=900;
static constexpr int DEFAULT_WAIT_CLIENT_TIMEOUT = 0;
static const bool DEFAULT_NAMED=false;
static const int CONTINUE_EXECUTION=-1;

Expand Down Expand Up @@ -69,6 +70,7 @@ static void SetupCliArgs(ArgsManager& argsman)
argsman.AddArg("-rpcuser=<user>", "Username for JSON-RPC connections", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-rpcwait", "Wait for RPC server to start", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-rpcwallet=<walletname>", "Send RPC for non-default wallet on RPC server (needs to exactly match corresponding -wallet option passed to dashd). This changes the RPC endpoint used, e.g. http://127.0.0.1:9998/wallet/<walletname>", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
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);
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);
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);
argsman.AddArg("-stdinwalletpassphrase", "Read wallet passphrase from standard input as a single line. When combined with -stdin, the first line from standard input is used for the wallet passphrase.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
Expand Down Expand Up @@ -705,6 +707,9 @@ static UniValue ConnectAndCallRPC(BaseRequestHandler* rh, const std::string& str
UniValue response(UniValue::VOBJ);
// Execute and handle connection failures with -rpcwait.
const bool fWait = gArgs.GetBoolArg("-rpcwait", false);
const int timeout = gArgs.GetArg("-rpcwaittimeout", DEFAULT_WAIT_CLIENT_TIMEOUT);
const int64_t deadline = GetTime<std::chrono::seconds>().count() + timeout;

do {
try {
response = CallRPC(rh, strMethod, args, rpcwallet);
Expand All @@ -715,11 +720,12 @@ static UniValue ConnectAndCallRPC(BaseRequestHandler* rh, const std::string& str
}
}
break; // Connection succeeded, no need to retry.
} catch (const CConnectionFailed&) {
if (fWait) {
UninterruptibleSleep(std::chrono::milliseconds{1000});
} catch (const CConnectionFailed& e) {
const int64_t now = GetTime<std::chrono::seconds>().count();
if (fWait && (timeout <= 0 || now < deadline)) {
UninterruptibleSleep(std::chrono::seconds{1});
} else {
throw;
throw CConnectionFailed(strprintf("timeout on transient error: %s", e.what()));
}
}
} while (fWait);
Expand Down
8 changes: 8 additions & 0 deletions test/functional/interface_bitcoin_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
assert_greater_than_or_equal,
assert_raises_process_error,
assert_raises_rpc_error,
get_auth_cookie,
)
import time

# The block reward of coinbaseoutput.nValue (500) DASH/block matures after
# COINBASE_MATURITY (100) blocks. Therefore, after mining 101 blocks we expect
Expand Down Expand Up @@ -247,6 +249,12 @@ def run_test(self):
self.nodes[0].wait_for_rpc_connection()
assert_equal(blocks, BLOCKS + 25)

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


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

0 comments on commit b2f6adb

Please sign in to comment.