diff --git a/README.md b/README.md index d8bb4bcdecf6..eacca84e3339 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ Don't hesitate to reach out to us on IRC at [#lightning-dev @ freenode.net][irc1 ## Getting Started -c-lightning only works on Linux and Mac OS, and requires a locally (or remotely) running `bitcoind` (version 0.16 or above) that is fully caught up with the network you're testing on. +c-lightning only works on Linux and Mac OS, and requires a locally (or remotely) running `bitcoind` (version 0.16 or above) that is fully caught up with the network you're running on, and relays transactions (ie with `blocksonly=0`). Pruning (`prune=n` option in `bitcoin.conf`) is partially supported, see [here](#pruning) for more details. ### Installation diff --git a/contrib/pyln-testing/pyln/testing/utils.py b/contrib/pyln-testing/pyln/testing/utils.py index 983019f13de4..bbc7e80c97d3 100644 --- a/contrib/pyln-testing/pyln/testing/utils.py +++ b/contrib/pyln-testing/pyln/testing/utils.py @@ -1026,8 +1026,8 @@ def get_nodes(self, num_nodes, opts=None): def get_node(self, node_id=None, options=None, dbfile=None, feerates=(15000, 11000, 7500, 3750), start=True, - wait_for_bitcoind_sync=True, expect_fail=False, - cleandir=True, **kwargs): + wait_for_bitcoind_sync=True, may_fail=False, + expect_fail=False, cleandir=True, **kwargs): node_id = self.get_node_id() if not node_id else node_id port = self.get_next_port() @@ -1043,7 +1043,8 @@ def get_node(self, node_id=None, options=None, dbfile=None, db = self.db_provider.get_db(os.path.join(lightning_dir, TEST_NETWORK), self.testname, node_id) node = self.node_cls( node_id, lightning_dir, self.bitcoind, self.executor, db=db, - port=port, options=options, **kwargs + port=port, options=options, may_fail=may_fail or expect_fail, + **kwargs ) # Regtest estimatefee are unusable, so override. diff --git a/plugins/bcli.c b/plugins/bcli.c index 2aae8acc2d0f..db6a3ae68c6b 100644 --- a/plugins/bcli.c +++ b/plugins/bcli.c @@ -40,6 +40,9 @@ struct bitcoind { /* -datadir arg for bitcoin-cli. */ char *datadir; + /* bitcoind's version, used for compatibility checks. */ + u32 version; + /* Is bitcoind synced? If not, we retry. */ bool synced; @@ -834,14 +837,63 @@ static void bitcoind_failure(struct plugin *p, const char *error_message) args_string(cmd, cmd)); } -static void wait_for_bitcoind(struct plugin *p) +/* Do some sanity checks on bitcoind based on the output of `getnetworkinfo`. */ +static void parse_getnetworkinfo_result(struct plugin *p, const char *buf) +{ + const jsmntok_t *result, *versiontok, *relaytok; + bool valid, tx_relay; + u32 min_version = 160000; + + result = json_parse_input(NULL, + buf, strlen(buf), + &valid); + if (!result || !valid) + plugin_err(p, "Invalid response to '%s': '%s'. Can not " + "continue without proceeding to sanity checks.", + gather_args(bitcoind, "getnetworkinfo", NULL), buf); + + /* Check that we have a fully-featured `estimatesmartfee`. */ + versiontok = json_get_member(buf, result, "version"); + if (!versiontok) + plugin_err(p, "No 'version' in '%s' ? Got '%s'. Can not" + " continue without proceeding to sanity checks.", + gather_args(bitcoind, "getnetworkinfo", NULL), buf); + + if (!json_to_u32(buf, versiontok, &bitcoind->version)) + plugin_err(p, "Invalid 'version' in '%s' ? Got '%s'. Can not" + " continue without proceeding to sanity checks.", + gather_args(bitcoind, "getnetworkinfo", NULL), buf); + + if (bitcoind->version < min_version) + plugin_err(p, "Unsupported bitcoind version %"PRIu32", at least" + " %"PRIu32" required.", bitcoind->version, min_version); + + /* We don't support 'blocksonly', as we rely on transaction relay for fee + * estimates. */ + relaytok = json_get_member(buf, result, "localrelay"); + if (!relaytok || !json_to_bool(buf, relaytok, &tx_relay)) + plugin_err(p, "No 'localrelay' in '%s' ? Got '%s'. Can not" + " continue without proceeding to sanity checks.", + gather_args(bitcoind, "getnetworkinfo", NULL), buf); + + if (!tx_relay) + plugin_err(p, "The 'blocksonly' mode of bitcoind, or any option " + "deactivating transaction relay is not supported."); + + tal_free(result); +} + +static void wait_and_check_bitcoind(struct plugin *p) { int from, status, ret; pid_t child; - const char **cmd = gather_args(bitcoind, "echo", NULL); + const char **cmd = gather_args(bitcoind, "getnetworkinfo", NULL); bool printed = false; + char *output = NULL; for (;;) { + tal_free(output); + child = pipecmdarr(NULL, &from, &from, cast_const2(char **,cmd)); if (child < 0) { if (errno == ENOENT) @@ -850,7 +902,7 @@ static void wait_for_bitcoind(struct plugin *p) plugin_err(p, "%s exec failed: %s", cmd[0], strerror(errno)); } - char *output = grab_fd(cmd, from); + output = grab_fd(cmd, from); while ((ret = waitpid(child, &status, 0)) < 0 && errno == EINTR); if (ret != child) @@ -881,13 +933,16 @@ static void wait_for_bitcoind(struct plugin *p) } sleep(1); } + + parse_getnetworkinfo_result(p, output); + tal_free(cmd); } static void init(struct plugin *p, const char *buffer UNUSED, const jsmntok_t *config UNUSED) { - wait_for_bitcoind(p); + wait_and_check_bitcoind(p); plugin_log(p, LOG_INFORM, "bitcoin-cli initialized and connected to bitcoind."); } diff --git a/tests/test_misc.py b/tests/test_misc.py index d235837cb235..ee7c11a443a2 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -128,6 +128,18 @@ def crash_bitcoincli(r): bitcoind.generate_block(5) sync_blockheight(bitcoind, [l1]) + # We refuse to start if bitcoind is in `blocksonly` + l1.stop() + bitcoind.stop() + bitcoind.cmd_line += ["-blocksonly"] + bitcoind.start() + + l2 = node_factory.get_node(start=False, expect_fail=True) + with pytest.raises(ValueError): + l2.start(stderr=subprocess.PIPE) + assert l2.daemon.is_in_stderr(r".*deactivating transaction relay is not" + " supported.") is not None + def test_bitcoin_ibd(node_factory, bitcoind): """Test that we recognize bitcoin in initial download mode"""