From 39c9dcbafc6ee8c382d69a6068648828f04f8b58 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 18 Sep 2019 10:35:10 +0930 Subject: [PATCH] ratelimit: adjust based on --dev-fast-gossip, test. Signed-off-by: Rusty Russell --- gossipd/gossip_constants.h | 4 +++ gossipd/routing.c | 12 ++++++--- tests/test_gossip.py | 50 +++++++++++++++++++++++++++++++++++++- 3 files changed, 61 insertions(+), 5 deletions(-) diff --git a/gossipd/gossip_constants.h b/gossipd/gossip_constants.h index eb0764a3c..8b7f9b681 100644 --- a/gossipd/gossip_constants.h +++ b/gossipd/gossip_constants.h @@ -77,4 +77,8 @@ #define GOSSIP_BEFORE_DEADLINE(dev_fast_gossip_flag) \ DEV_FAST_GOSSIP(dev_fast_gossip_flag, 30, 24*60*60) +/* How many seconds per token? Normally 1 hour. */ +#define GOSSIP_TOKEN_TIME(dev_fast_gossip_flag) \ + DEV_FAST_GOSSIP(dev_fast_gossip_flag, 1, 3600) + #endif /* LIGHTNING_GOSSIPD_GOSSIP_CONSTANTS_H */ diff --git a/gossipd/routing.c b/gossipd/routing.c index 0b93c26c7..b82895cca 100644 --- a/gossipd/routing.c +++ b/gossipd/routing.c @@ -41,14 +41,16 @@ struct pending_node_announce { #define TOKENS_PER_MSG 24 #define TOKEN_MAX (24 * 4) -static bool ratelimit(u8 *tokens, u32 prev_timestamp, u32 new_timestamp) +static bool ratelimit(const struct routing_state *rstate, + u8 *tokens, u32 prev_timestamp, u32 new_timestamp) { u64 num_tokens; assert(new_timestamp >= prev_timestamp); /* First, top up tokens, avoiding overflow. */ - num_tokens = *tokens + (new_timestamp - prev_timestamp) / 3600; + num_tokens = *tokens + ((new_timestamp - prev_timestamp) + / GOSSIP_TOKEN_TIME(rstate->dev_fast_gossip)); if (num_tokens > TOKEN_MAX) num_tokens = TOKEN_MAX; *tokens = num_tokens; @@ -2013,7 +2015,8 @@ bool routing_add_channel_update(struct routing_state *rstate, } /* Make sure it's not spamming us. */ - if (!ratelimit(&hc->tokens, hc->bcast.timestamp, timestamp)) { + if (!ratelimit(rstate, + &hc->tokens, hc->bcast.timestamp, timestamp)) { status_debug("Ignoring spammy update for %s/%u" " (last %u, now %u)", type_to_string(tmpctx, @@ -2364,7 +2367,8 @@ bool routing_add_node_announcement(struct routing_state *rstate, } /* Make sure it's not spamming us. */ - if (!ratelimit(&node->tokens, node->bcast.timestamp, timestamp)) { + if (!ratelimit(rstate, + &node->tokens, node->bcast.timestamp, timestamp)) { status_debug("Ignoring spammy nannounce for %s" " (last %u, now %u)", type_to_string(tmpctx, diff --git a/tests/test_gossip.py b/tests/test_gossip.py index 470d7d369..d1e8abf9d 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -1376,7 +1376,7 @@ def test_gossip_store_compact_on_load(node_factory, bitcoind): l2.restart() - wait_for(lambda: l2.daemon.is_in_log('gossip_store_compact_offline: [5-8] deleted, 9 copied')) + wait_for(lambda: l2.daemon.is_in_log(r'gossip_store_compact_offline: [5-8] deleted, 9 copied')) wait_for(lambda: l2.daemon.is_in_log(r'gossip_store: Read 1/4/2/0 cannounce/cupdate/nannounce/cdelete from store \(0 deleted\) in 1446 bytes')) @@ -1454,3 +1454,51 @@ def test_gossip_no_backtalk(node_factory): # With DEVELOPER, this is long enough for gossip flush. time.sleep(2) assert not l3.daemon.is_in_log(r'\[OUT\] 0100') + + +@unittest.skipIf(not DEVELOPER, "Needs --dev-gossip") +def test_gossip_ratelimit(node_factory): + # These make the channel exist, but we use our own gossip. + l1, l2 = node_factory.line_graph(2, wait_for_announce=True) + + # Here are some ones I generated earlier (by removing gossip + # ratelimiting) + l3 = node_factory.get_node(options={'dev-gossip-time': 1568096251}) + subprocess.run(['devtools/gossipwith', + '--max-messages=0', + '{}@localhost:{}'.format(l3.info['id'], l3.port), + # announcement + '0100987b271fc95a37dbed78e6159e0ab792cda64603780454ce80832b4e31f63a6760abc8fdc53be35bb7cfccd125ee3d15b4fbdfb42165098970c19c7822bb413f46390e0c043c777226927eacd2186a03f064e4bdc30f891cb6e4990af49967d34b338755e99d728987e3d49227815e17f3ab40092434a59e33548e870071176db7d44d8c8f4c4cac27ae6554eb9350e97d47617e3a1355296c78e8234446fa2f138ad1b03439f18520227fb9e9eb92689b3a0ed36e6764f5a41777e9a2a4ce1026d19a4e4d8f7715c13ac2d6bf3238608a1ccf9afd91f774d84d170d9edddebf7460c54d49bd6cd81410bc3eeeba2b7278b1b5f7e748d77d793f31086847d582000006226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f0000670000010001022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d590266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c0351802e3bd38009866c9da8ec4aa99cc4ea9c6c0dd46df15c61ef0ce1f271291714e5702324266de8403b3ab157a09f1f784d587af61831c998c151bcc21bb74c2b2314b', + # first update is free + '010225bfd9c5e2c5660188a14deb4002cd645ee67f00ad3b82146e46711ec460cb0c6819fdd1c680cb6d24e3906679ef071f13243a04a123e4b83310ebf0518ffd4206226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f00006700000100015d773ffb010100060000000000000000000000010000000a000000003b023380'], + check=True, timeout=TIMEOUT) + + # Wait for it to process channel. + wait_for(lambda: [c['fee_per_millionth'] for c in l3.rpc.listchannels()['channels']] == [10]) + + subprocess.run(['devtools/gossipwith', + '--max-messages=0', + '{}@localhost:{}'.format(l3.info['id'], l3.port), + # next 4 are let through... + '01023a892ad9c9953a54ad3b8e2e03a93d1c973241b62f9a5cd1f17d5cdf08de0e8b4fcd24aa8bd45a48b788fe9dab3d416f28dfa390bc900ec0176ec5bd1afd435706226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f00006700000100015d77400001010006000000000000000000000014000003e9000000003b023380', + '010245966763623ebc16796165263d4b21711ef04ebf3929491e695ff89ed2b8ccc0668ceb9e35e0ff5b8901d95732a119c1ed84ac99861daa2de462118f7b70049f06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f00006700000100015d77400101010006000000000000000000000014000003ea000000003b023380', + '0102c479b7684b9db496b844f6925f4ffd8a27c5840a020d1b537623c1545dcd8e195776381bbf51213e541a853a4a49a0faf84316e7ccca5e7074901a96bbabe04e06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f00006700000100015d77400201010006000000000000000000000014000003eb000000003b023380', + # timestamp=1568096259, fee_proportional_millionths=1004 + '01024b866012d995d3d7aec7b7218a283de2d03492dbfa21e71dd546ec2e36c3d4200453420aa02f476f99c73fe1e223ea192f5fa544b70a8319f2a216f1513d503d06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f00006700000100015d77400301010006000000000000000000000014000003ec000000003b023380', + # update 5 marks you as a nasty spammer! + '01025b5b5a0daed874ab02bd3356d38190ff46bbaf5f10db5067da70f3ca203480ca78059e6621c6143f3da4e454d0adda6d01a9980ed48e71ccd0c613af73570a7106226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f00006700000100015d77400401010006000000000000000000000014000003ed000000003b023380'], + check=True, timeout=TIMEOUT) + + wait_for(lambda: [c['fee_per_millionth'] for c in l3.rpc.listchannels()['channels']] == [1004]) + + # 24 seconds later, it will accept another. + l3.rpc.call('dev-gossip-set-time', [1568096251 + 24]) + + subprocess.run(['devtools/gossipwith', + '--max-messages=0', + '{}@localhost:{}'.format(l3.info['id'], l3.port), + # update 6: timestamp=1568096284 fee_proportional_millionths=1006 + '010282d24bcd984956bd9b891848404ee59d89643923b21641d2c2c0770a51b8f5da00cef82458add970f0b654aa4c8d54f68a9a1cc6470a35810303b09437f1f73d06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f00006700000100015d77401c01010006000000000000000000000014000003ee000000003b023380'], + check=True, timeout=TIMEOUT) + + wait_for(lambda: [c['fee_per_millionth'] for c in l3.rpc.listchannels()['channels']] == [1006])