From 9e51e196c14906fea850cef5a4d36360cd568fed Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 4 Jun 2018 13:58:02 +0930 Subject: [PATCH] gossipd: dev-set-max-scids-encode-size to artificially force "full" replies. We cap each reply at a single one, which forces the code into our recursion logic. Signed-off-by: Rusty Russell --- gossipd/gossip.c | 24 +++++++++++++++++++++++- gossipd/gossip_wire.csv | 4 ++++ lightningd/gossip_control.c | 34 ++++++++++++++++++++++++++++++++++ tests/test_lightningd.py | 15 +++++++++++++++ 4 files changed, 76 insertions(+), 1 deletion(-) diff --git a/gossipd/gossip.c b/gossipd/gossip.c index 5aa661d09..dfeee3d43 100644 --- a/gossipd/gossip.c +++ b/gossipd/gossip.c @@ -101,6 +101,10 @@ HTABLE_DEFINE_TYPE(struct important_peerid, important_peerid_eq, important_peerid_map); +#if DEVELOPER +static u32 max_scids_encode_bytes = -1U; +#endif + struct daemon { /* Who am I? */ struct pubkey id; @@ -439,6 +443,10 @@ static void encode_add_short_channel_id(u8 **encoded, static bool encode_short_channel_ids_end(u8 **encoded, size_t max_bytes) { +#if DEVELOPER + if (tal_len(*encoded) > max_scids_encode_bytes) + return false; +#endif return tal_len(*encoded) <= max_bytes; } @@ -2291,6 +2299,17 @@ fail: NULL))); goto out; } + +static struct io_plan *dev_set_max_scids_encode_size(struct io_conn *conn, + struct daemon *daemon, + const u8 *msg) +{ + if (!fromwire_gossip_dev_set_max_scids_encode_size(msg, + &max_scids_encode_bytes)) + master_badmsg(WIRE_GOSSIP_DEV_SET_MAX_SCIDS_ENCODE_SIZE, msg); + + return daemon_conn_read_next(conn, &daemon->master); +} #endif /* DEVELOPER */ static int make_listen_fd(int domain, void *addr, socklen_t len, bool mayfail) @@ -3518,11 +3537,15 @@ static struct io_plan *recv_req(struct io_conn *conn, struct daemon_conn *master case WIRE_GOSSIP_QUERY_CHANNEL_RANGE: return query_channel_range(conn, daemon, daemon->master.msg_in); + case WIRE_GOSSIP_DEV_SET_MAX_SCIDS_ENCODE_SIZE: + return dev_set_max_scids_encode_size(conn, daemon, + daemon->master.msg_in); #else case WIRE_GOSSIP_PING: case WIRE_GOSSIP_QUERY_SCIDS: case WIRE_GOSSIP_SEND_TIMESTAMP_FILTER: case WIRE_GOSSIP_QUERY_CHANNEL_RANGE: + case WIRE_GOSSIP_DEV_SET_MAX_SCIDS_ENCODE_SIZE: break; #endif /* !DEVELOPER */ @@ -3581,7 +3604,6 @@ int main(int argc, char *argv[]) timers_init(&daemon->timers, time_mono()); daemon->broadcast_interval = 30000; daemon->last_announce_timestamp = 0; - /* stdin == control */ daemon_conn_init(daemon, &daemon->master, STDIN_FILENO, recv_req, master_gone); diff --git a/gossipd/gossip_wire.csv b/gossipd/gossip_wire.csv index 2c4125831..7d2461632 100644 --- a/gossipd/gossip_wire.csv +++ b/gossipd/gossip_wire.csv @@ -186,6 +186,10 @@ gossip_query_channel_range_reply,,final_complete,bool gossip_query_channel_range_reply,,num,u16 gossip_query_channel_range_reply,,scids,num*struct short_channel_id +# Set artificial maximum reply_channel_range size. Master->gossipd +gossip_dev_set_max_scids_encode_size,3030 +gossip_dev_set_max_scids_encode_size,,max,u32 + # Given a short_channel_id, return the endpoints gossip_resolve_channel_request,3009 gossip_resolve_channel_request,,channel_id,struct short_channel_id diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index 60c2c6890..051f45d8e 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -139,6 +139,7 @@ static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) case WIRE_GOSSIP_QUERY_SCIDS: case WIRE_GOSSIP_QUERY_CHANNEL_RANGE: case WIRE_GOSSIP_SEND_TIMESTAMP_FILTER: + case WIRE_GOSSIP_DEV_SET_MAX_SCIDS_ENCODE_SIZE: case WIRE_GOSSIPCTL_PEER_DISCONNECT: case WIRE_GOSSIPCTL_PEER_IMPORTANT: case WIRE_GOSSIPCTL_PEER_DISCONNECTED: @@ -761,4 +762,37 @@ static const struct json_command dev_query_channel_range_command = { "Query {peerid} for short_channel_ids for {first} block + {num} blocks" }; AUTODATA(json_command, &dev_query_channel_range_command); + +static void json_dev_set_max_scids_encode_size(struct command *cmd, + const char *buffer, + const jsmntok_t *params) +{ + u8 *msg; + jsmntok_t *maxtok; + u32 max; + + if (!json_get_params(cmd, buffer, params, + "max", &maxtok, + NULL)) { + return; + } + + if (!json_tok_number(buffer, maxtok, &max)) { + command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "max must be a number"); + return; + } + + msg = towire_gossip_dev_set_max_scids_encode_size(NULL, max); + subd_send_msg(cmd->ld->gossip, take(msg)); + + command_success(cmd, null_response(cmd)); +} + +static const struct json_command dev_set_max_scids_encode_size = { + "dev-set-max-scids-encode-size", + json_dev_set_max_scids_encode_size, + "Set {max} bytes of short_channel_ids per reply_channel_range" +}; +AUTODATA(json_command, &dev_set_max_scids_encode_size); #endif /* DEVELOPER */ diff --git a/tests/test_lightningd.py b/tests/test_lightningd.py index a948bfb7d..8d2bd7b38 100644 --- a/tests/test_lightningd.py +++ b/tests/test_lightningd.py @@ -2714,6 +2714,21 @@ class LightningDTests(BaseLightningDTests): assert ret['final_complete'] assert len(ret['short_channel_ids']) == 0 + # Make l2 split reply into two. + l2.rpc.dev_set_max_scids_encode_size(max=9) + ret = l1.rpc.dev_query_channel_range(id=l2.info['id'], + first=0, + num=1000000) + + # It should definitely have split + assert ret['final_first_block'] != 0 or ret['final_num_blocks'] != 1000000 + assert ret['final_complete'] + assert len(ret['short_channel_ids']) == 2 + assert ret['short_channel_ids'][0] == scid12 + assert ret['short_channel_ids'][1] == scid23 + + l2.daemon.wait_for_log('queue_channel_ranges full: splitting') + @unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1") def test_query_short_channel_id(self): l1 = self.node_factory.get_node(options={'log-level': 'io'})