forked from bitcoin/bitcoin
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
merge bitcoin#22387: Rate limit the processing of rumoured addresses
- Loading branch information
Showing
7 changed files
with
155 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,16 +12,19 @@ | |
msg_addr, | ||
msg_getaddr | ||
) | ||
from test_framework.p2p import P2PInterface | ||
from test_framework.test_framework import BitcoinTestFramework | ||
from test_framework.util import ( | ||
assert_equal, | ||
from test_framework.p2p import ( | ||
P2PInterface, | ||
p2p_lock, | ||
) | ||
from test_framework.test_framework import BitcoinTestFramework | ||
from test_framework.util import assert_equal | ||
import random | ||
|
||
|
||
class AddrReceiver(P2PInterface): | ||
num_ipv4_received = 0 | ||
test_addr_contents = False | ||
_tokens = 1 | ||
|
||
def __init__(self, test_addr_contents=False): | ||
super().__init__() | ||
|
@@ -38,6 +41,20 @@ def on_addr(self, message): | |
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 | ||
|
||
|
@@ -50,12 +67,14 @@ class AddrTest(BitcoinTestFramework): | |
|
||
def set_test_params(self): | ||
self.num_nodes = 1 | ||
self.extra_args = [["[email protected]"]] | ||
|
||
def run_test(self): | ||
self.oversized_addr_test() | ||
self.relay_tests() | ||
self.getaddr_tests() | ||
self.blocksonly_mode_tests() | ||
self.rate_limit_tests() | ||
|
||
def setup_addr_msg(self, num): | ||
addrs = [] | ||
|
@@ -72,6 +91,19 @@ def setup_addr_msg(self, num): | |
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 | ||
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 | ||
|
@@ -186,7 +218,7 @@ def getaddr_tests(self): | |
|
||
def blocksonly_mode_tests(self): | ||
self.log.info('Test addr relay in -blocksonly mode') | ||
self.restart_node(0, ["-blocksonly"]) | ||
self.restart_node(0, ["-blocksonly", "[email protected]"]) | ||
|
||
self.log.info('Check that we send getaddr messages') | ||
full_outbound_peer = self.nodes[0].add_outbound_p2p_connection(AddrReceiver(), p2p_idx=0, connection_type="outbound-full-relay") | ||
|
@@ -203,6 +235,59 @@ def blocksonly_mode_tests(self): | |
|
||
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): | ||
|
||
self.restart_node(0, []) | ||
|
||
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') | ||
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) | ||
|
||
# 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 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, 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 and verify that 10 are processed. | ||
self.bump_mocktime(100) | ||
peer.increment_tokens(10) | ||
|
||
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.bump_mocktime(1000) | ||
peer.increment_tokens(100) | ||
|
||
self.send_addrs_and_test_rate_limiting(peer, no_relay, 200, 1610) | ||
|
||
self.nodes[0].disconnect_p2ps() | ||
|
||
if __name__ == '__main__': | ||
AddrTest().main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,7 +27,9 @@ def __init__(self): | |
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): | ||
|
@@ -38,6 +40,7 @@ class AddrTest(BitcoinTestFramework): | |
def set_test_params(self): | ||
self.setup_clean_chain = True | ||
self.num_nodes = 1 | ||
self.extra_args = [["[email protected]"]] | ||
|
||
def run_test(self): | ||
for i in range(10): | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -46,6 +46,7 @@ class InvalidMessagesTest(BitcoinTestFramework): | |
def set_test_params(self): | ||
self.num_nodes = 1 | ||
self.setup_clean_chain = True | ||
self.extra_args = [["[email protected]"]] | ||
|
||
def run_test(self): | ||
self.test_buffer() | ||
|