diff --git a/connectd/connectd.c b/connectd/connectd.c index c6e06a585815..b7c8134ee5dd 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -2014,6 +2014,10 @@ static void dev_connect_memleak(struct daemon *daemon, const u8 *msg) struct htable *memtable; bool found_leak; + /* As a side-effect, this tells us lightningd will be unresponsive, + * so don't complain (and break CI!) if it's slow. */ + daemon->dev_lightningd_is_slow = true; + memtable = memleak_start(tmpctx); memleak_ptr(memtable, msg); @@ -2465,6 +2469,7 @@ int main(int argc, char *argv[]) daemon->dev_suppress_gossip = false; daemon->custom_msgs = NULL; daemon->dev_exhausted_fds = false; + daemon->dev_lightningd_is_slow = false; /* We generally allow 1MB per second per peer, except for dev testing */ daemon->gossip_stream_limit = 1000000; daemon->scid_htable = new_htable(daemon, scid_htable); diff --git a/connectd/connectd.h b/connectd/connectd.h index 09778e2d5015..dd7c736786e4 100644 --- a/connectd/connectd.h +++ b/connectd/connectd.h @@ -356,6 +356,8 @@ struct daemon { bool dev_no_reconnect; /* --dev-fast-reconnect */ bool dev_fast_reconnect; + /* Don't complain about lightningd being unresponsive. */ + bool dev_lightningd_is_slow; }; /* Called by io_tor_connect once it has a connection out. */ diff --git a/connectd/multiplex.c b/connectd/multiplex.c index b657a693a1d6..f53f3927d990 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -1150,7 +1150,7 @@ static struct io_plan *write_to_subd(struct io_conn *subd_conn, if (subd->peer->peer_in_lastmsg != -1) { u64 msec = time_to_msec(timemono_between(time_mono(), subd->peer->peer_in_lasttime)); - if (msec > 5000) + if (msec > 5000 && !subd->peer->daemon->dev_lightningd_is_slow) status_peer_broken(&subd->peer->id, "wake delay for %s: %"PRIu64"msec", peer_wire_name(subd->peer->peer_in_lastmsg), diff --git a/lightningd/channel_gossip.c b/lightningd/channel_gossip.c index 79b12ffc5f02..38532afa9959 100644 --- a/lightningd/channel_gossip.c +++ b/lightningd/channel_gossip.c @@ -115,6 +115,8 @@ static struct state_transition allowed_transitions[] = { "Unannounced channel closed onchain." }, { CGOSSIP_CHANNEL_UNANNOUNCED_DYING, CGOSSIP_CHANNEL_ANNOUNCED_DYING, "Unannounced closing channel reached announce depth." }, + { CGOSSIP_CHANNEL_UNANNOUNCED_DYING, CGOSSIP_CHANNEL_ANNOUNCED_DEAD, + "Unannounced closing channel reached announce depth and was closed in same block." }, { CGOSSIP_WAITING_FOR_ANNOUNCE_DEPTH, CGOSSIP_ANNOUNCED, "Channel fully announced" }, { CGOSSIP_WAITING_FOR_MATCHING_PEER_SIGS, CGOSSIP_ANNOUNCED, diff --git a/lightningd/memdump.c b/lightningd/memdump.c index ec0c49cab44c..cbd1b31d37da 100644 --- a/lightningd/memdump.c +++ b/lightningd/memdump.c @@ -83,6 +83,7 @@ static const struct json_command dev_memdump_command = { }; AUTODATA(json_command, &dev_memdump_command); + static void memleak_log(struct logger *log, const char *fmt, ...) { va_list ap; @@ -91,9 +92,29 @@ static void memleak_log(struct logger *log, const char *fmt, ...) va_end(ap); } -static void finish_report(const struct leak_detect *leaks) +static bool lightningd_check_leaks(struct command *cmd) { + struct lightningd *ld = cmd->ld; + struct htable *memtable; + + /* Enter everything, except this cmd and its jcon */ + memtable = memleak_start(cmd); + + /* This command is not a leak! */ + memleak_ptr(memtable, cmd); + memleak_ignore_children(memtable, cmd); + + /* Now delete ld and those which it has pointers to. */ + memleak_scan_obj(memtable, ld); + + return dump_memleak(memtable, memleak_log, ld->log); +} + +static void finish_report(struct leak_detect *leaks) +{ + bool found_leak; struct json_stream *response; + const u8 *msg; /* If it timed out, we free ourselved and exit! */ if (!leaks->cmd) { @@ -101,6 +122,18 @@ static void finish_report(const struct leak_detect *leaks) return; } + /* Check for our own leaks. */ + if (lightningd_check_leaks(leaks->cmd)) + tal_arr_expand(&leaks->leakers, "lightningd"); + + /* Check hsmd for leaks. */ + msg = hsm_sync_req(tmpctx, leaks->cmd->ld, take(towire_hsmd_dev_memleak(NULL))); + if (!fromwire_hsmd_dev_memleak_reply(msg, &found_leak)) + fatal("Bad HSMD_DEV_MEMLEAK_REPLY: %s", tal_hex(tmpctx, msg)); + + if (found_leak) + report_subd_memleak(leaks, leaks->cmd->ld->hsm); + response = json_stream_success(leaks->cmd); json_array_start(response, "leaks"); for (size_t num_leakers = 0; @@ -177,32 +210,12 @@ static void connect_dev_memleak_done(struct subd *connectd, report_subd_memleak(leaks, connectd); } -static bool lightningd_check_leaks(struct command *cmd) -{ - struct lightningd *ld = cmd->ld; - struct htable *memtable; - - /* Enter everything, except this cmd and its jcon */ - memtable = memleak_start(cmd); - - /* This command is not a leak! */ - memleak_ptr(memtable, cmd); - memleak_ignore_children(memtable, cmd); - - /* Now delete ld and those which it has pointers to. */ - memleak_scan_obj(memtable, ld); - - return dump_memleak(memtable, memleak_log, ld->log); -} - static struct command_result *json_memleak(struct command *cmd, const char *buffer, const jsmntok_t *obj UNNEEDED, const jsmntok_t *params) { struct lightningd *ld = cmd->ld; - const u8 *msg; - bool found_leak; struct leak_detect *leaks; if (!param_check(cmd, buffer, params, NULL)) @@ -221,19 +234,9 @@ static struct command_result *json_memleak(struct command *cmd, leaks->num_outstanding_requests = 0; leaks->leakers = tal_arr(leaks, const char *, 0); - /* Check for our own leaks. */ - if (lightningd_check_leaks(cmd)) - tal_arr_expand(&leaks->leakers, "lightningd"); - - /* hsmd is sync, so do that first. */ - msg = hsm_sync_req(tmpctx, cmd->ld, take(towire_hsmd_dev_memleak(NULL))); - if (!fromwire_hsmd_dev_memleak_reply(msg, &found_leak)) - fatal("Bad HSMD_DEV_MEMLEAK_REPLY: %s", tal_hex(tmpctx, msg)); - - if (found_leak) - report_subd_memleak(leaks, ld->hsm); - - /* Now do all the async ones. */ + /* Now do all the async ones. By doing connectd first, it + * has the side-effect of suppressing the complaint it makes + * about us being unresponsive. */ start_leak_request(subd_req(ld->connectd, ld->connectd, take(towire_connectd_dev_memleak(NULL)), -1, 0, connect_dev_memleak_done, leaks), diff --git a/tests/test_coinmoves.py b/tests/test_coinmoves.py index 21c6ea97e903..04ee10705f45 100644 --- a/tests/test_coinmoves.py +++ b/tests/test_coinmoves.py @@ -894,6 +894,8 @@ def test_coinmoves_unilateral_htlc_timeout(node_factory, bitcoind): line = l1.daemon.wait_for_log('Resolved OUR_UNILATERAL/OUR_HTLC by our proposal OUR_HTLC_TIMEOUT_TX') htlc_timeout_txid = re.search(r'by our proposal OUR_HTLC_TIMEOUT_TX \(([0-9a-f]{64})\)', line).group(1) + # Usually 6358000, but if we're lucky it's 6366000. + htlc_timeout_change_msats = bitcoind.rpc.gettxout(htlc_timeout_txid, 1)['value'] * 100_000_000_000 expected_chain1 += [{'account_id': 'wallet', 'blockheight': 115, 'credit_msat': 0, @@ -906,10 +908,10 @@ def test_coinmoves_unilateral_htlc_timeout(node_factory, bitcoind): # Change {'account_id': 'wallet', 'blockheight': 115, - 'credit_msat': (15579000 + 6358000) - anchor_change_msats, + 'credit_msat': htlc_timeout_change_msats, 'debit_msat': 0, 'extra_tags': [], - 'output_msat': (15579000 + 6358000) - anchor_change_msats, + 'output_msat': htlc_timeout_change_msats, 'primary_tag': 'deposit', 'utxo': f"{htlc_timeout_txid}:1"}, {'account_id': fundchannel['channel_id'], diff --git a/tests/test_connection.py b/tests/test_connection.py index ae0473b2e89f..cd93ff7662a3 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -4719,8 +4719,11 @@ def test_injectonionmessage(node_factory): def test_connect_ratelimit(node_factory, bitcoind): """l1 has 5 peers, restarts, make sure we limit""" + # Sending nodes SIGSTOP at the wrong time makes connectd complain about + # how long operations took! nodes = node_factory.get_nodes(6, - opts=[{'dev-limit-connections-inflight': None, 'may_reconnect': True}] + [{'may_reconnect': True}] * 5) + opts=[{'dev-limit-connections-inflight': None, 'may_reconnect': True}] + + [{'may_reconnect': True, 'broken_log': "connectd: wake delay for"}] * 5) l1 = nodes[0] nodes = nodes[1:]