From 0d64b8f709b4655d8702f810d4876cd8d96ded82 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Wed, 30 Jun 2021 11:52:40 -0700 Subject: [PATCH 1/5] Rate limit the processing of incoming addr messages While limitations on the influence of attackers on addrman already exist (affected buckets are restricted to a subset based on incoming IP / network group), there is no reason to permit them to let them feed us addresses at more than a multiple of the normal network rate. This commit introduces a "token bucket" rate limiter for the processing of addresses in incoming ADDR and ADDRV2 messages. Every connection gets an associated token bucket. Processing an address in an ADDR or ADDRV2 message from non-whitelisted peers consumes a token from the bucket. If the bucket is empty, the address is ignored (it is not forwarded or processed). The token counter increases at a rate of 0.1 tokens per second, and will accrue up to a maximum of 1000 tokens (the maximum we accept in a single ADDR or ADDRV2). When a GETADDR is sent to a peer, it immediately gets 1000 additional tokens, as we actively desire many addresses from such peers (this may temporarily cause the token count to exceed 1000). The rate limit of 0.1 addr/s was chosen based on observation of honest nodes on the network. Activity in general from most nodes is either 0, or up to a maximum around 0.025 addr/s for recent Bitcoin Core nodes. A few (self-identified, through subver) crawler nodes occasionally exceed 0.1 addr/s. --- src/net_permissions.h | 3 ++- src/net_processing.cpp | 32 +++++++++++++++++++++++++ test/functional/p2p_addr_relay.py | 3 ++- test/functional/p2p_addrv2_relay.py | 1 + test/functional/p2p_invalid_messages.py | 1 + 5 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/net_permissions.h b/src/net_permissions.h index c00689465e1..bc979e3792c 100644 --- a/src/net_permissions.h +++ b/src/net_permissions.h @@ -31,7 +31,8 @@ enum class NetPermissionFlags : uint32_t { NoBan = (1U << 4) | Download, // Can query the mempool Mempool = (1U << 5), - // Can request addrs without hitting a privacy-preserving cache + // Can request addrs without hitting a privacy-preserving cache, and send us + // unlimited amounts of addrs. Addr = (1U << 7), // True if the user did not specifically set fine grained permissions diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 315d2ac5cd8..8d24b008877 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -155,6 +155,13 @@ static constexpr uint32_t MAX_GETCFHEADERS_SIZE = 2000; static constexpr size_t MAX_PCT_ADDR_TO_SEND = 23; /** The maximum number of address records permitted in an ADDR message. */ static constexpr size_t MAX_ADDR_TO_SEND{1000}; +/** The maximum rate of address records we're willing to process on average. Can be bypassed using + * the NetPermissionFlags::Addr permission. */ +static constexpr double MAX_ADDR_RATE_PER_SECOND{0.1}; +/** The soft limit of the address processing token bucket (the regular MAX_ADDR_RATE_PER_SECOND + * based increments won't go above this, but the MAX_ADDR_TO_SEND increment following GETADDR + * is exempt from this limit. */ +static constexpr size_t MAX_ADDR_PROCESSING_TOKEN_BUCKET{MAX_ADDR_TO_SEND}; // Internal stuff namespace { @@ -233,6 +240,11 @@ struct Peer { std::atomic_bool m_wants_addrv2{false}; /** Whether this peer has already sent us a getaddr message. */ bool m_getaddr_recvd{false}; + /** Number of addr messages that can be processed from this peer. Start at 1 to + * permit self-announcement. */ + double m_addr_token_bucket{1.0}; + /** When m_addr_token_bucket was last updated */ + std::chrono::microseconds m_addr_token_timestamp{GetTime()}; /** Set of txids to reconsider once their parent transactions have been accepted **/ std::set m_orphan_work_set GUARDED_BY(g_cs_orphans); @@ -2583,6 +2595,9 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, // Get recent addresses m_connman.PushMessage(&pfrom, CNetMsgMaker(greatest_common_version).Make(NetMsgType::GETADDR)); peer->m_getaddr_sent = true; + // When requesting a getaddr, accept an additional MAX_ADDR_TO_SEND addresses in response + // (bypassing the MAX_ADDR_PROCESSING_TOKEN_BUCKET limit). + peer->m_addr_token_bucket += MAX_ADDR_TO_SEND; } if (!pfrom.IsInboundConn()) { @@ -2777,11 +2792,28 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, std::vector vAddrOk; int64_t nNow = GetAdjustedTime(); int64_t nSince = nNow - 10 * 60; + + // Update/increment addr rate limiting bucket. + const auto current_time = GetTime(); + if (peer->m_addr_token_bucket < MAX_ADDR_PROCESSING_TOKEN_BUCKET) { + // Don't increment bucket if it's already full + const auto time_diff = std::max(current_time - peer->m_addr_token_timestamp, 0us); + const double increment = CountSecondsDouble(time_diff) * MAX_ADDR_RATE_PER_SECOND; + peer->m_addr_token_bucket = std::min(peer->m_addr_token_bucket + increment, MAX_ADDR_PROCESSING_TOKEN_BUCKET); + } + peer->m_addr_token_timestamp = current_time; + + const bool rate_limited = !pfrom.HasPermission(NetPermissionFlags::Addr); for (CAddress& addr : vAddr) { if (interruptMsgProc) return; + // Apply rate limiting. + if (rate_limited) { + if (peer->m_addr_token_bucket < 1.0) break; + peer->m_addr_token_bucket -= 1.0; + } // We only bother storing full nodes, though this may include // things which we would not make an outbound connection to, in // part because we may make feeler connections to them. diff --git a/test/functional/p2p_addr_relay.py b/test/functional/p2p_addr_relay.py index 1a414959b90..cd668dc3152 100755 --- a/test/functional/p2p_addr_relay.py +++ b/test/functional/p2p_addr_relay.py @@ -53,6 +53,7 @@ class AddrTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 1 + self.extra_args = [["-whitelist=addr@127.0.0.1"]] def run_test(self): self.oversized_addr_test() @@ -191,7 +192,7 @@ class AddrTest(BitcoinTestFramework): def blocksonly_mode_tests(self): self.log.info('Test addr relay in -blocksonly mode') - self.restart_node(0, ["-blocksonly"]) + self.restart_node(0, ["-blocksonly", "-whitelist=addr@127.0.0.1"]) self.mocktime = int(time.time()) self.log.info('Check that we send getaddr messages') diff --git a/test/functional/p2p_addrv2_relay.py b/test/functional/p2p_addrv2_relay.py index c006a6c24f8..9d36bc03a3b 100755 --- a/test/functional/p2p_addrv2_relay.py +++ b/test/functional/p2p_addrv2_relay.py @@ -53,6 +53,7 @@ class AddrTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 + self.extra_args = [["-whitelist=addr@127.0.0.1"]] def run_test(self): self.log.info('Create connection that sends addrv2 messages') diff --git a/test/functional/p2p_invalid_messages.py b/test/functional/p2p_invalid_messages.py index 788a81d4af0..9c345063205 100755 --- a/test/functional/p2p_invalid_messages.py +++ b/test/functional/p2p_invalid_messages.py @@ -58,6 +58,7 @@ class InvalidMessagesTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 1 self.setup_clean_chain = True + self.extra_args = [["-whitelist=addr@127.0.0.1"]] def run_test(self): self.test_buffer() From 5648138f5949013331c017c740646e2f4013bc24 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Thu, 15 Jul 2021 12:59:23 -0700 Subject: [PATCH 2/5] Randomize the order of addr processing --- src/net_processing.cpp | 1 + test/functional/p2p_addrv2_relay.py | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 8d24b008877..79ef11a7904 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -2804,6 +2804,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, peer->m_addr_token_timestamp = current_time; const bool rate_limited = !pfrom.HasPermission(NetPermissionFlags::Addr); + Shuffle(vAddr.begin(), vAddr.end(), FastRandomContext()); for (CAddress& addr : vAddr) { if (interruptMsgProc) diff --git a/test/functional/p2p_addrv2_relay.py b/test/functional/p2p_addrv2_relay.py index 9d36bc03a3b..32c1d42b1cf 100755 --- a/test/functional/p2p_addrv2_relay.py +++ b/test/functional/p2p_addrv2_relay.py @@ -42,7 +42,9 @@ class AddrReceiver(P2PInterface): super().__init__(support_addrv2 = True) def on_addrv2(self, message): - if ADDRS == message.addrs: + expected_set = set((addr.ip, addr.port) for addr in ADDRS) + received_set = set((addr.ip, addr.port) for addr in message.addrs) + if expected_set == received_set: self.addrv2_received_and_checked = True def wait_for_addrv2(self): From b4ece8a1cda69cc268d39d21bba59c51fa2fb9ed Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Thu, 1 Jul 2021 16:02:05 -0700 Subject: [PATCH 3/5] Functional tests for addr rate limiting --- test/functional/p2p_addr_relay.py | 80 +++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/test/functional/p2p_addr_relay.py b/test/functional/p2p_addr_relay.py index cd668dc3152..b53231b6df4 100755 --- a/test/functional/p2p_addr_relay.py +++ b/test/functional/p2p_addr_relay.py @@ -17,7 +17,10 @@ from test_framework.p2p import P2PInterface from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, + assert_greater_than_or_equal, ) +import os +import random import time @@ -60,6 +63,7 @@ class AddrTest(BitcoinTestFramework): self.relay_tests() self.getaddr_tests() self.blocksonly_mode_tests() + self.rate_limit_tests() def setup_addr_msg(self, num): addrs = [] @@ -76,6 +80,19 @@ class AddrTest(BitcoinTestFramework): msg.addrs = addrs return msg + def setup_rand_addr_msg(self, num): + addrs = [] + for i in range(num): + addr = CAddress() + addr.time = self.mocktime + i + addr.nServices = NODE_NETWORK | NODE_WITNESS + addr.ip = f"{random.randrange(128,169)}.{random.randrange(1,255)}.{random.randrange(1,255)}.{random.randrange(1,255)}" + addr.port = 8333 + addrs.append(addr) + msg = msg_addr() + msg.addrs = addrs + return msg + def send_addr_msg(self, source, msg, receivers): source.send_and_ping(msg) # pop m_next_addr_send timer @@ -208,6 +225,69 @@ class AddrTest(BitcoinTestFramework): self.nodes[0].disconnect_p2ps() + def rate_limit_tests(self): + + for contype, tokens, no_relay in [("outbound-full-relay", 1001, False), ("block-relay-only", 0, True), ("inbound", 1, False)]: + self.log.info(f'Test rate limiting of addr processing for {contype} peers') + self.stop_node(0) + os.remove(os.path.join(self.nodes[0].datadir, "regtest", "peers.dat")) + self.start_node(0, []) + self.mocktime = int(time.time()) + self.nodes[0].setmocktime(self.mocktime) + if contype == "inbound": + peer = self.nodes[0].add_p2p_connection(AddrReceiver()) + else: + peer = self.nodes[0].add_outbound_p2p_connection(AddrReceiver(), p2p_idx=0, connection_type=contype) + + # Check that we start off with empty addrman + addr_count_0 = len(self.nodes[0].getnodeaddresses(0)) + assert_equal(addr_count_0, 0) + + # Send 600 addresses. For all but the block-relay-only peer this should result in at least 1 address. + peer.send_and_ping(self.setup_rand_addr_msg(600)) + addr_count_1 = len(self.nodes[0].getnodeaddresses(0)) + assert_greater_than_or_equal(tokens, addr_count_1) + assert_greater_than_or_equal(addr_count_0 + 600, addr_count_1) + assert_equal(addr_count_1 > addr_count_0, tokens > 0) + + # Send 600 more addresses. For the outbound-full-relay peer (which we send a GETADDR, and thus will + # process up to 1001 incoming addresses), this means more entries will appear. + peer.send_and_ping(self.setup_rand_addr_msg(600)) + addr_count_2 = len(self.nodes[0].getnodeaddresses(0)) + assert_greater_than_or_equal(tokens, addr_count_2) + assert_greater_than_or_equal(addr_count_1 + 600, addr_count_2) + assert_equal(addr_count_2 > addr_count_1, tokens > 600) + + # Send 10 more. As we reached the processing limit for all nodes, this should have no effect. + peer.send_and_ping(self.setup_rand_addr_msg(10)) + addr_count_3 = len(self.nodes[0].getnodeaddresses(0)) + assert_greater_than_or_equal(tokens, addr_count_3) + assert_equal(addr_count_2, addr_count_3) + + # Advance the time by 100 seconds, permitting the processing of 10 more addresses. Send 200, + # but verify that no more than 10 are processed. + self.mocktime += 100 + self.nodes[0].setmocktime(self.mocktime) + new_tokens = 0 if no_relay else 10 + tokens += new_tokens + peer.send_and_ping(self.setup_rand_addr_msg(200)) + addr_count_4 = len(self.nodes[0].getnodeaddresses(0)) + assert_greater_than_or_equal(tokens, addr_count_4) + assert_greater_than_or_equal(addr_count_3 + new_tokens, addr_count_4) + + # Advance the time by 1000 seconds, permitting the processing of 100 more addresses. Send 200, + # but verify that no more than 100 are processed (and at least some). + self.mocktime += 1000 + self.nodes[0].setmocktime(self.mocktime) + new_tokens = 0 if no_relay else 100 + tokens += new_tokens + peer.send_and_ping(self.setup_rand_addr_msg(200)) + addr_count_5 = len(self.nodes[0].getnodeaddresses(0)) + assert_greater_than_or_equal(tokens, addr_count_5) + assert_greater_than_or_equal(addr_count_4 + new_tokens, addr_count_5) + assert_equal(addr_count_5 > addr_count_4, not no_relay) + + self.nodes[0].disconnect_p2ps() if __name__ == '__main__': AddrTest().main() From f424d601e1b6870e20bc60f5ccba36d2e210377b Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Wed, 7 Jul 2021 11:44:40 -0700 Subject: [PATCH 4/5] Add logging and addr rate limiting statistics Includes logging improvements by Vasil Dimov and John Newbery. --- src/net_processing.cpp | 23 ++++++++++++++++++++++- src/net_processing.h | 2 ++ src/rpc/net.cpp | 2 ++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 79ef11a7904..9c4544df215 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -245,6 +245,10 @@ struct Peer { double m_addr_token_bucket{1.0}; /** When m_addr_token_bucket was last updated */ std::chrono::microseconds m_addr_token_timestamp{GetTime()}; + /** Total number of addresses that were dropped due to rate limiting. */ + std::atomic m_addr_rate_limited{0}; + /** Total number of addresses that were processed (excludes rate limited ones). */ + std::atomic m_addr_processed{0}; /** Set of txids to reconsider once their parent transactions have been accepted **/ std::set m_orphan_work_set GUARDED_BY(g_cs_orphans); @@ -1251,6 +1255,8 @@ bool PeerManagerImpl::GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) c } stats.m_ping_wait = ping_wait; + stats.m_addr_processed = peer->m_addr_processed.load(); + stats.m_addr_rate_limited = peer->m_addr_rate_limited.load(); return true; } @@ -2804,6 +2810,8 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, peer->m_addr_token_timestamp = current_time; const bool rate_limited = !pfrom.HasPermission(NetPermissionFlags::Addr); + uint64_t num_proc = 0; + uint64_t num_rate_limit = 0; Shuffle(vAddr.begin(), vAddr.end(), FastRandomContext()); for (CAddress& addr : vAddr) { @@ -2812,7 +2820,10 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, // Apply rate limiting. if (rate_limited) { - if (peer->m_addr_token_bucket < 1.0) break; + if (peer->m_addr_token_bucket < 1.0) { + ++num_rate_limit; + continue; + } peer->m_addr_token_bucket -= 1.0; } // We only bother storing full nodes, though this may include @@ -2828,6 +2839,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, // Do not process banned/discouraged addresses beyond remembering we received them continue; } + ++num_proc; bool fReachable = IsReachable(addr); if (addr.nTime > nSince && !peer->m_getaddr_sent && vAddr.size() <= 10 && addr.IsRoutable()) { // Relay to a limited number of other nodes @@ -2837,6 +2849,15 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, if (fReachable) vAddrOk.push_back(addr); } + peer->m_addr_processed += num_proc; + peer->m_addr_rate_limited += num_rate_limit; + LogPrint(BCLog::NET, "Received addr: %u addresses (%u processed, %u rate-limited) from peer=%d%s\n", + vAddr.size(), + num_proc, + num_rate_limit, + pfrom.GetId(), + fLogIPs ? ", peeraddr=" + pfrom.addr.ToString() : ""); + m_addrman.Add(vAddrOk, pfrom.addr, 2 * 60 * 60); if (vAddr.size() < 1000) peer->m_getaddr_sent = false; if (pfrom.IsAddrFetchConn()) { diff --git a/src/net_processing.h b/src/net_processing.h index d5801aadd34..c537efb5db9 100644 --- a/src/net_processing.h +++ b/src/net_processing.h @@ -29,6 +29,8 @@ struct CNodeStateStats { int m_starting_height = -1; std::chrono::microseconds m_ping_wait; std::vector vHeightInFlight; + uint64_t m_addr_processed = 0; + uint64_t m_addr_rate_limited = 0; }; class PeerManager : public CValidationInterface, public NetEventsInterface diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index 3013c76825e..083ba31b712 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -242,6 +242,8 @@ static RPCHelpMan getpeerinfo() heights.push_back(height); } obj.pushKV("inflight", heights); + obj.pushKV("addr_processed", statestats.m_addr_processed); + obj.pushKV("addr_rate_limited", statestats.m_addr_rate_limited); } UniValue permissions(UniValue::VARR); for (const auto& permission : NetPermissions::ToStrings(stats.m_permissionFlags)) { From a4bcd687c934d47aa3922334e97e579caf5f8124 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Wed, 14 Jul 2021 11:58:50 -0700 Subject: [PATCH 5/5] Improve tests using statistics --- test/functional/p2p_addr_relay.py | 108 ++++++++++++++++-------------- 1 file changed, 58 insertions(+), 50 deletions(-) diff --git a/test/functional/p2p_addr_relay.py b/test/functional/p2p_addr_relay.py index b53231b6df4..ff1d85a9be7 100755 --- a/test/functional/p2p_addr_relay.py +++ b/test/functional/p2p_addr_relay.py @@ -13,13 +13,12 @@ from test_framework.messages import ( msg_addr, msg_getaddr ) -from test_framework.p2p import P2PInterface -from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import ( - assert_equal, - assert_greater_than_or_equal, +from test_framework.p2p import ( + P2PInterface, + p2p_lock, ) -import os +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import assert_equal import random import time @@ -27,6 +26,7 @@ import time class AddrReceiver(P2PInterface): num_ipv4_received = 0 test_addr_contents = False + _tokens = 1 def __init__(self, test_addr_contents=False): super().__init__() @@ -43,6 +43,20 @@ class AddrReceiver(P2PInterface): raise AssertionError("Invalid addr.port of {} (8333-8342 expected)".format(addr.port)) assert addr.ip.startswith('123.123.123.') + def on_getaddr(self, message): + # When the node sends us a getaddr, it increments the addr relay tokens for the connection by 1000 + self._tokens += 1000 + + @property + def tokens(self): + with p2p_lock: + return self._tokens + + def increment_tokens(self, n): + # When we move mocktime forward, the node increments the addr relay tokens for its peers + with p2p_lock: + self._tokens += n + def addr_received(self): return self.num_ipv4_received != 0 @@ -225,67 +239,61 @@ class AddrTest(BitcoinTestFramework): self.nodes[0].disconnect_p2ps() + def send_addrs_and_test_rate_limiting(self, peer, no_relay, new_addrs, total_addrs): + """Send an addr message and check that the number of addresses processed and rate-limited is as expected""" + + peer.send_and_ping(self.setup_rand_addr_msg(new_addrs)) + + peerinfo = self.nodes[0].getpeerinfo()[0] + addrs_processed = peerinfo['addr_processed'] + addrs_rate_limited = peerinfo['addr_rate_limited'] + self.log.debug(f"addrs_processed = {addrs_processed}, addrs_rate_limited = {addrs_rate_limited}") + + if no_relay: + assert_equal(addrs_processed, 0) + assert_equal(addrs_rate_limited, 0) + else: + assert_equal(addrs_processed, min(total_addrs, peer.tokens)) + assert_equal(addrs_rate_limited, max(0, total_addrs - peer.tokens)) + def rate_limit_tests(self): - for contype, tokens, no_relay in [("outbound-full-relay", 1001, False), ("block-relay-only", 0, True), ("inbound", 1, False)]: + self.mocktime = int(time.time()) + self.restart_node(0, []) + self.nodes[0].setmocktime(self.mocktime) + + for contype, no_relay in [("outbound-full-relay", False), ("block-relay-only", True), ("inbound", False)]: self.log.info(f'Test rate limiting of addr processing for {contype} peers') - self.stop_node(0) - os.remove(os.path.join(self.nodes[0].datadir, "regtest", "peers.dat")) - self.start_node(0, []) - self.mocktime = int(time.time()) - self.nodes[0].setmocktime(self.mocktime) if contype == "inbound": peer = self.nodes[0].add_p2p_connection(AddrReceiver()) else: peer = self.nodes[0].add_outbound_p2p_connection(AddrReceiver(), p2p_idx=0, connection_type=contype) - # Check that we start off with empty addrman - addr_count_0 = len(self.nodes[0].getnodeaddresses(0)) - assert_equal(addr_count_0, 0) - - # Send 600 addresses. For all but the block-relay-only peer this should result in at least 1 address. - peer.send_and_ping(self.setup_rand_addr_msg(600)) - addr_count_1 = len(self.nodes[0].getnodeaddresses(0)) - assert_greater_than_or_equal(tokens, addr_count_1) - assert_greater_than_or_equal(addr_count_0 + 600, addr_count_1) - assert_equal(addr_count_1 > addr_count_0, tokens > 0) + # Send 600 addresses. For all but the block-relay-only peer this should result in addresses being processed. + self.send_addrs_and_test_rate_limiting(peer, no_relay, 600, 600) # Send 600 more addresses. For the outbound-full-relay peer (which we send a GETADDR, and thus will - # process up to 1001 incoming addresses), this means more entries will appear. - peer.send_and_ping(self.setup_rand_addr_msg(600)) - addr_count_2 = len(self.nodes[0].getnodeaddresses(0)) - assert_greater_than_or_equal(tokens, addr_count_2) - assert_greater_than_or_equal(addr_count_1 + 600, addr_count_2) - assert_equal(addr_count_2 > addr_count_1, tokens > 600) + # process up to 1001 incoming addresses), this means more addresses will be processed. + self.send_addrs_and_test_rate_limiting(peer, no_relay, 600, 1200) - # Send 10 more. As we reached the processing limit for all nodes, this should have no effect. - peer.send_and_ping(self.setup_rand_addr_msg(10)) - addr_count_3 = len(self.nodes[0].getnodeaddresses(0)) - assert_greater_than_or_equal(tokens, addr_count_3) - assert_equal(addr_count_2, addr_count_3) + # Send 10 more. As we reached the processing limit for all nodes, no more addresses should be procesesd. + self.send_addrs_and_test_rate_limiting(peer, no_relay, 10, 1210) - # Advance the time by 100 seconds, permitting the processing of 10 more addresses. Send 200, - # but verify that no more than 10 are processed. + # Advance the time by 100 seconds, permitting the processing of 10 more addresses. + # Send 200 and verify that 10 are processed. self.mocktime += 100 self.nodes[0].setmocktime(self.mocktime) - new_tokens = 0 if no_relay else 10 - tokens += new_tokens - peer.send_and_ping(self.setup_rand_addr_msg(200)) - addr_count_4 = len(self.nodes[0].getnodeaddresses(0)) - assert_greater_than_or_equal(tokens, addr_count_4) - assert_greater_than_or_equal(addr_count_3 + new_tokens, addr_count_4) + peer.increment_tokens(10) - # Advance the time by 1000 seconds, permitting the processing of 100 more addresses. Send 200, - # but verify that no more than 100 are processed (and at least some). + self.send_addrs_and_test_rate_limiting(peer, no_relay, 200, 1410) + + # Advance the time by 1000 seconds, permitting the processing of 100 more addresses. + # Send 200 and verify that 100 are processed. self.mocktime += 1000 self.nodes[0].setmocktime(self.mocktime) - new_tokens = 0 if no_relay else 100 - tokens += new_tokens - peer.send_and_ping(self.setup_rand_addr_msg(200)) - addr_count_5 = len(self.nodes[0].getnodeaddresses(0)) - assert_greater_than_or_equal(tokens, addr_count_5) - assert_greater_than_or_equal(addr_count_4 + new_tokens, addr_count_5) - assert_equal(addr_count_5 > addr_count_4, not no_relay) + peer.increment_tokens(100) + + self.send_addrs_and_test_rate_limiting(peer, no_relay, 200, 1610) self.nodes[0].disconnect_p2ps()