Changeset View
Changeset View
Standalone View
Standalone View
qa/rpc-tests/p2p-leaktests.py
Show All 13 Lines | |||||
received a VERACK. | received a VERACK. | ||||
This test connects to a node and sends it a few messages, trying to intice it | This test connects to a node and sends it a few messages, trying to intice it | ||||
into sending us something it shouldn't. | into sending us something it shouldn't. | ||||
''' | ''' | ||||
banscore = 10 | banscore = 10 | ||||
class CLazyNode(NodeConnCB): | class CLazyNode(NodeConnCB): | ||||
def __init__(self): | def __init__(self): | ||||
self.connection = None | self.connection = None | ||||
self.unexpected_msg = False | self.unexpected_msg = False | ||||
self.connected = False | self.connected = False | ||||
super().__init__() | super().__init__() | ||||
def add_connection(self, conn): | def add_connection(self, conn): | ||||
self.connection = conn | self.connection = conn | ||||
def send_message(self, message): | def send_message(self, message): | ||||
self.connection.send_message(message) | self.connection.send_message(message) | ||||
def bad_message(self, message): | def bad_message(self, message): | ||||
self.unexpected_msg = True | self.unexpected_msg = True | ||||
print("should not have received message: %s" % message.command) | print("should not have received message: %s" % message.command) | ||||
def on_open(self, conn): | def on_open(self, conn): | ||||
self.connected = True | self.connected = True | ||||
def on_version(self, conn, message): self.bad_message(message) | def on_version(self, conn, message): self.bad_message(message) | ||||
def on_verack(self, conn, message): self.bad_message(message) | def on_verack(self, conn, message): self.bad_message(message) | ||||
def on_reject(self, conn, message): self.bad_message(message) | def on_reject(self, conn, message): self.bad_message(message) | ||||
def on_inv(self, conn, message): self.bad_message(message) | def on_inv(self, conn, message): self.bad_message(message) | ||||
def on_addr(self, conn, message): self.bad_message(message) | def on_addr(self, conn, message): self.bad_message(message) | ||||
def on_alert(self, conn, message): self.bad_message(message) | def on_alert(self, conn, message): self.bad_message(message) | ||||
def on_getdata(self, conn, message): self.bad_message(message) | def on_getdata(self, conn, message): self.bad_message(message) | ||||
def on_getblocks(self, conn, message): self.bad_message(message) | def on_getblocks(self, conn, message): self.bad_message(message) | ||||
def on_tx(self, conn, message): self.bad_message(message) | def on_tx(self, conn, message): self.bad_message(message) | ||||
def on_block(self, conn, message): self.bad_message(message) | def on_block(self, conn, message): self.bad_message(message) | ||||
def on_getaddr(self, conn, message): self.bad_message(message) | def on_getaddr(self, conn, message): self.bad_message(message) | ||||
def on_headers(self, conn, message): self.bad_message(message) | def on_headers(self, conn, message): self.bad_message(message) | ||||
def on_getheaders(self, conn, message): self.bad_message(message) | def on_getheaders(self, conn, message): self.bad_message(message) | ||||
def on_ping(self, conn, message): self.bad_message(message) | def on_ping(self, conn, message): self.bad_message(message) | ||||
def on_mempool(self, conn): self.bad_message(message) | def on_mempool(self, conn): self.bad_message(message) | ||||
def on_pong(self, conn, message): self.bad_message(message) | def on_pong(self, conn, message): self.bad_message(message) | ||||
def on_feefilter(self, conn, message): self.bad_message(message) | def on_feefilter(self, conn, message): self.bad_message(message) | ||||
def on_sendheaders(self, conn, message): self.bad_message(message) | def on_sendheaders(self, conn, message): self.bad_message(message) | ||||
def on_sendcmpct(self, conn, message): self.bad_message(message) | def on_sendcmpct(self, conn, message): self.bad_message(message) | ||||
def on_cmpctblock(self, conn, message): self.bad_message(message) | def on_cmpctblock(self, conn, message): self.bad_message(message) | ||||
def on_getblocktxn(self, conn, message): self.bad_message(message) | def on_getblocktxn(self, conn, message): self.bad_message(message) | ||||
def on_blocktxn(self, conn, message): self.bad_message(message) | def on_blocktxn(self, conn, message): self.bad_message(message) | ||||
# Node that never sends a version. We'll use this to send a bunch of messages | # Node that never sends a version. We'll use this to send a bunch of messages | ||||
# anyway, and eventually get disconnected. | # anyway, and eventually get disconnected. | ||||
class CNodeNoVersionBan(CLazyNode): | class CNodeNoVersionBan(CLazyNode): | ||||
def __init__(self): | def __init__(self): | ||||
super().__init__() | super().__init__() | ||||
# send a bunch of veracks without sending a message. This should get us disconnected. | # send a bunch of veracks without sending a message. This should get us disconnected. | ||||
# NOTE: implementation-specific check here. Remove if bitcoind ban behavior changes | # NOTE: implementation-specific check here. Remove if bitcoind ban | ||||
# behavior changes | |||||
def on_open(self, conn): | def on_open(self, conn): | ||||
super().on_open(conn) | super().on_open(conn) | ||||
for i in range(banscore): | for i in range(banscore): | ||||
self.send_message(msg_verack()) | self.send_message(msg_verack()) | ||||
def on_reject(self, conn, message): pass | def on_reject(self, conn, message): pass | ||||
# Node that never sends a version. This one just sits idle and hopes to receive | # Node that never sends a version. This one just sits idle and hopes to receive | ||||
# any message (it shouldn't!) | # any message (it shouldn't!) | ||||
class CNodeNoVersionIdle(CLazyNode): | class CNodeNoVersionIdle(CLazyNode): | ||||
def __init__(self): | def __init__(self): | ||||
super().__init__() | super().__init__() | ||||
# Node that sends a version but not a verack. | # Node that sends a version but not a verack. | ||||
class CNodeNoVerackIdle(CLazyNode): | class CNodeNoVerackIdle(CLazyNode): | ||||
def __init__(self): | def __init__(self): | ||||
self.version_received = False | self.version_received = False | ||||
super().__init__() | super().__init__() | ||||
def on_reject(self, conn, message): pass | def on_reject(self, conn, message): pass | ||||
def on_verack(self, conn, message): pass | def on_verack(self, conn, message): pass | ||||
# When version is received, don't reply with a verack. Instead, see if the | # When version is received, don't reply with a verack. Instead, see if the | ||||
# node will give us a message that it shouldn't. This is not an exhaustive | # node will give us a message that it shouldn't. This is not an exhaustive | ||||
# list! | # list! | ||||
def on_version(self, conn, message): | def on_version(self, conn, message): | ||||
self.version_received = True | self.version_received = True | ||||
conn.send_message(msg_ping()) | conn.send_message(msg_ping()) | ||||
conn.send_message(msg_getaddr()) | conn.send_message(msg_getaddr()) | ||||
class P2PLeakTest(BitcoinTestFramework): | class P2PLeakTest(BitcoinTestFramework): | ||||
def __init__(self): | def __init__(self): | ||||
super().__init__() | super().__init__() | ||||
self.num_nodes = 1 | self.num_nodes = 1 | ||||
def setup_network(self): | def setup_network(self): | ||||
extra_args = [['-debug', '-banscore='+str(banscore)] | extra_args = [['-debug', '-banscore=' + str(banscore)] | ||||
for i in range(self.num_nodes)] | for i in range(self.num_nodes)] | ||||
self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, extra_args) | self.nodes = start_nodes( | ||||
self.num_nodes, self.options.tmpdir, extra_args) | |||||
def run_test(self): | def run_test(self): | ||||
no_version_bannode = CNodeNoVersionBan() | no_version_bannode = CNodeNoVersionBan() | ||||
no_version_idlenode = CNodeNoVersionIdle() | no_version_idlenode = CNodeNoVersionIdle() | ||||
no_verack_idlenode = CNodeNoVerackIdle() | no_verack_idlenode = CNodeNoVerackIdle() | ||||
connections = [] | connections = [] | ||||
connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], no_version_bannode, send_version=False)) | connections.append( | ||||
connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], no_version_idlenode, send_version=False)) | NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], no_version_bannode, send_version=False)) | ||||
connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], no_verack_idlenode)) | connections.append( | ||||
NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], no_version_idlenode, send_version=False)) | |||||
connections.append( | |||||
NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], no_verack_idlenode)) | |||||
no_version_bannode.add_connection(connections[0]) | no_version_bannode.add_connection(connections[0]) | ||||
no_version_idlenode.add_connection(connections[1]) | no_version_idlenode.add_connection(connections[1]) | ||||
no_verack_idlenode.add_connection(connections[2]) | no_verack_idlenode.add_connection(connections[2]) | ||||
NetworkThread().start() # Start up network handling in another thread | NetworkThread().start() # Start up network handling in another thread | ||||
assert(wait_until(lambda: no_version_bannode.connected and no_version_idlenode.connected and no_verack_idlenode.version_received, timeout=10)) | assert( | ||||
wait_until(lambda: no_version_bannode.connected and no_version_idlenode.connected and no_verack_idlenode.version_received, timeout=10)) | |||||
# Mine a block and make sure that it's not sent to the connected nodes | # Mine a block and make sure that it's not sent to the connected nodes | ||||
self.nodes[0].generate(1) | self.nodes[0].generate(1) | ||||
#Give the node enough time to possibly leak out a message | # Give the node enough time to possibly leak out a message | ||||
time.sleep(5) | time.sleep(5) | ||||
#This node should have been banned | # This node should have been banned | ||||
assert(no_version_bannode.connection.state == "closed") | assert(no_version_bannode.connection.state == "closed") | ||||
[conn.disconnect_node() for conn in connections] | [conn.disconnect_node() for conn in connections] | ||||
# Make sure no unexpected messages came in | # Make sure no unexpected messages came in | ||||
assert(no_version_bannode.unexpected_msg == False) | assert(no_version_bannode.unexpected_msg == False) | ||||
assert(no_version_idlenode.unexpected_msg == False) | assert(no_version_idlenode.unexpected_msg == False) | ||||
assert(no_verack_idlenode.unexpected_msg == False) | assert(no_verack_idlenode.unexpected_msg == False) | ||||
if __name__ == '__main__': | if __name__ == '__main__': | ||||
P2PLeakTest().main() | P2PLeakTest().main() |