Skip to content

Commit cfcef60

Browse files
committed
test: Add functional tests for sendtxrcncl from inbound
1 parent b99ee9d commit cfcef60

File tree

4 files changed

+195
-0
lines changed

4 files changed

+195
-0
lines changed

test/functional/p2p_sendtxrcncl.py

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
#!/usr/bin/env python3
2+
# Copyright (c) 2022 The Bitcoin Core developers
3+
# Distributed under the MIT software license, see the accompanying
4+
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
5+
"""Test SENDTXRCNCL message
6+
"""
7+
8+
from test_framework.messages import (
9+
msg_sendtxrcncl,
10+
msg_verack,
11+
msg_version,
12+
msg_wtxidrelay,
13+
)
14+
from test_framework.p2p import (
15+
P2PInterface,
16+
P2P_SERVICES,
17+
P2P_SUBVERSION,
18+
P2P_VERSION,
19+
)
20+
from test_framework.test_framework import BitcoinTestFramework
21+
from test_framework.util import assert_equal
22+
23+
class PeerNoVerack(P2PInterface):
24+
def __init__(self, wtxidrelay=True):
25+
super().__init__(wtxidrelay=wtxidrelay)
26+
27+
def on_version(self, message):
28+
# Avoid sending verack in response to version.
29+
# When calling add_p2p_connection, wait_for_verack=False must be set (see
30+
# comment in add_p2p_connection).
31+
if message.nVersion >= 70016 and self.wtxidrelay:
32+
self.send_message(msg_wtxidrelay())
33+
34+
class SendTxrcnclReceiver(P2PInterface):
35+
def __init__(self):
36+
super().__init__()
37+
self.sendtxrcncl_msg_received = None
38+
39+
def on_sendtxrcncl(self, message):
40+
self.sendtxrcncl_msg_received = message
41+
42+
class PeerTrackMsgOrder(P2PInterface):
43+
def __init__(self):
44+
super().__init__()
45+
self.messages = []
46+
47+
def on_message(self, message):
48+
super().on_message(message)
49+
self.messages.append(message)
50+
51+
def create_sendtxrcncl_msg(initiator=True):
52+
sendtxrcncl_msg = msg_sendtxrcncl()
53+
sendtxrcncl_msg.initiator = initiator
54+
sendtxrcncl_msg.responder = not initiator
55+
sendtxrcncl_msg.version = 1
56+
sendtxrcncl_msg.salt = 2
57+
return sendtxrcncl_msg
58+
59+
class SendTxRcnclTest(BitcoinTestFramework):
60+
def set_test_params(self):
61+
self.num_nodes = 1
62+
self.extra_args = [['-txreconciliation']]
63+
64+
def run_test(self):
65+
self.log.info('SENDTXRCNCL sent to an inbound')
66+
peer = self.nodes[0].add_p2p_connection(SendTxrcnclReceiver(), send_version=True, wait_for_verack=True)
67+
assert peer.sendtxrcncl_msg_received
68+
assert not peer.sendtxrcncl_msg_received.initiator
69+
assert peer.sendtxrcncl_msg_received.responder
70+
assert_equal(peer.sendtxrcncl_msg_received.version, 1)
71+
peer.peer_disconnect()
72+
73+
self.log.info('SENDTXRCNCL should be sent before VERACK')
74+
peer = self.nodes[0].add_p2p_connection(PeerTrackMsgOrder(), send_version=True, wait_for_verack=True)
75+
peer.wait_for_verack()
76+
verack_index = [i for i, msg in enumerate(peer.messages) if msg.msgtype == b'verack'][0]
77+
sendtxrcncl_index = [i for i, msg in enumerate(peer.messages) if msg.msgtype == b'sendtxrcncl'][0]
78+
assert(sendtxrcncl_index < verack_index)
79+
peer.peer_disconnect()
80+
81+
self.log.info('SENDTXRCNCL on pre-WTXID version should not be sent')
82+
peer = self.nodes[0].add_p2p_connection(SendTxrcnclReceiver(), send_version=False, wait_for_verack=False)
83+
pre_wtxid_version_msg = msg_version()
84+
pre_wtxid_version_msg.nVersion = 70015
85+
pre_wtxid_version_msg.strSubVer = P2P_SUBVERSION
86+
pre_wtxid_version_msg.nServices = P2P_SERVICES
87+
pre_wtxid_version_msg.relay = 1
88+
peer.send_message(pre_wtxid_version_msg)
89+
peer.wait_for_verack()
90+
assert not peer.sendtxrcncl_msg_received
91+
peer.peer_disconnect()
92+
93+
self.log.info('SENDTXRCNCL for fRelay=false should not be sent')
94+
peer = self.nodes[0].add_p2p_connection(SendTxrcnclReceiver(), send_version=False, wait_for_verack=False)
95+
no_txrelay_version_msg = msg_version()
96+
no_txrelay_version_msg.nVersion = P2P_VERSION
97+
no_txrelay_version_msg.strSubVer = P2P_SUBVERSION
98+
no_txrelay_version_msg.nServices = P2P_SERVICES
99+
no_txrelay_version_msg.relay = 0
100+
peer.send_message(no_txrelay_version_msg)
101+
peer.wait_for_verack()
102+
assert not peer.sendtxrcncl_msg_received
103+
peer.peer_disconnect()
104+
105+
self.log.info('valid SENDTXRCNCL received')
106+
peer = self.nodes[0].add_p2p_connection(PeerNoVerack(), send_version=True, wait_for_verack=False)
107+
peer.send_message(create_sendtxrcncl_msg())
108+
self.wait_until(lambda : "sendtxrcncl" in self.nodes[0].getpeerinfo()[-1]["bytesrecv_per_msg"])
109+
self.log.info('second SENDTXRCNCL triggers a disconnect')
110+
peer.send_message(create_sendtxrcncl_msg())
111+
peer.wait_for_disconnect()
112+
113+
self.log.info('SENDTXRCNCL with initiator=responder=0 triggers a disconnect')
114+
sendtxrcncl_no_role = create_sendtxrcncl_msg()
115+
sendtxrcncl_no_role.initiator = False
116+
sendtxrcncl_no_role.responder = False
117+
peer = self.nodes[0].add_p2p_connection(PeerNoVerack(), send_version=True, wait_for_verack=False)
118+
peer.send_message(sendtxrcncl_no_role)
119+
peer.wait_for_disconnect()
120+
121+
self.log.info('SENDTXRCNCL with initiator=0 and responder=1 from inbound triggers a disconnect')
122+
sendtxrcncl_wrong_role = create_sendtxrcncl_msg(initiator=False)
123+
peer = self.nodes[0].add_p2p_connection(PeerNoVerack(), send_version=True, wait_for_verack=False)
124+
peer.send_message(sendtxrcncl_wrong_role)
125+
peer.wait_for_disconnect()
126+
127+
self.log.info('SENDTXRCNCL with version=0 triggers a disconnect')
128+
sendtxrcncl_low_version = create_sendtxrcncl_msg()
129+
sendtxrcncl_low_version.version = 0
130+
peer = self.nodes[0].add_p2p_connection(PeerNoVerack(), send_version=True, wait_for_verack=False)
131+
peer.send_message(sendtxrcncl_low_version)
132+
peer.wait_for_disconnect()
133+
134+
self.log.info('sending SENDTXRCNCL after sending VERACK triggers a disconnect')
135+
# We use PeerNoVerack even though verack is sent right after, to make sure it was actually
136+
# sent before sendtxrcncl is sent.
137+
peer = self.nodes[0].add_p2p_connection(PeerNoVerack(), send_version=True, wait_for_verack=False)
138+
peer.send_and_ping(msg_verack())
139+
peer.send_message(create_sendtxrcncl_msg())
140+
peer.wait_for_disconnect()
141+
142+
self.log.info('SENDTXRCNCL without WTXIDRELAY is ignored (recon state is erased after VERACK)')
143+
peer = self.nodes[0].add_p2p_connection(PeerNoVerack(wtxidrelay=False), send_version=True, wait_for_verack=False)
144+
with self.nodes[0].assert_debug_log(['Forget txreconciliation state of peer']):
145+
peer.send_message(create_sendtxrcncl_msg())
146+
peer.send_message(msg_verack())
147+
peer.peer_disconnect()
148+
149+
self.log.info('SENDTXRCNCL not sent if -txreconciliation flag is not set')
150+
self.restart_node(0, [])
151+
peer = self.nodes[0].add_p2p_connection(SendTxrcnclReceiver(), send_version=True, wait_for_verack=True)
152+
assert not peer.sendtxrcncl_msg_received
153+
peer.peer_disconnect()
154+
155+
self.log.info('SENDTXRCNCL not sent if blocksonly is set')
156+
self.restart_node(0, ["-txreconciliation", "-blocksonly"])
157+
peer = self.nodes[0].add_p2p_connection(SendTxrcnclReceiver(), send_version=True, wait_for_verack=True)
158+
assert not peer.sendtxrcncl_msg_received
159+
peer.peer_disconnect()
160+
161+
162+
if __name__ == '__main__':
163+
SendTxRcnclTest().main()

test/functional/test_framework/messages.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1838,3 +1838,31 @@ def serialize(self):
18381838
def __repr__(self):
18391839
return "msg_cfcheckpt(filter_type={:#x}, stop_hash={:x})".format(
18401840
self.filter_type, self.stop_hash)
1841+
1842+
class msg_sendtxrcncl:
1843+
__slots__ = ("initiator", "responder", "version", "salt")
1844+
msgtype = b"sendtxrcncl"
1845+
1846+
def __init__(self):
1847+
self.initiator = False
1848+
self.responder = False
1849+
self.version = 0
1850+
self.salt = 0
1851+
1852+
def deserialize(self, f):
1853+
self.initiator = struct.unpack("<?", f.read(1))[0]
1854+
self.responder = struct.unpack("<?", f.read(1))[0]
1855+
self.version = struct.unpack("<I", f.read(4))[0]
1856+
self.salt = struct.unpack("<Q", f.read(8))[0]
1857+
1858+
def serialize(self):
1859+
r = b""
1860+
r += struct.pack("<?", self.initiator)
1861+
r += struct.pack("<?", self.responder)
1862+
r += struct.pack("<I", self.version)
1863+
r += struct.pack("<Q", self.salt)
1864+
return r
1865+
1866+
def __repr__(self):
1867+
return "msg_sendtxrcncl(initiator=%i, responder=%i, version=%lu, salt=%lu)" %\
1868+
(self.initiator, self.responder, self.version, self.salt)

test/functional/test_framework/p2p.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
msg_sendaddrv2,
6363
msg_sendcmpct,
6464
msg_sendheaders,
65+
msg_sendtxrcncl,
6566
msg_tx,
6667
MSG_TX,
6768
MSG_TYPE_MASK,
@@ -126,6 +127,7 @@
126127
b"sendaddrv2": msg_sendaddrv2,
127128
b"sendcmpct": msg_sendcmpct,
128129
b"sendheaders": msg_sendheaders,
130+
b"sendtxrcncl": msg_sendtxrcncl,
129131
b"tx": msg_tx,
130132
b"verack": msg_verack,
131133
b"version": msg_version,
@@ -421,6 +423,7 @@ def on_pong(self, message): pass
421423
def on_sendaddrv2(self, message): pass
422424
def on_sendcmpct(self, message): pass
423425
def on_sendheaders(self, message): pass
426+
def on_sendtxrcncl(self, message): pass
424427
def on_tx(self, message): pass
425428
def on_wtxidrelay(self, message): pass
426429

test/functional/test_runner.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,7 @@
318318
'rpc_deriveaddresses.py --usecli',
319319
'p2p_ping.py',
320320
'rpc_scanblocks.py',
321+
'p2p_sendtxrcncl.py',
321322
'rpc_scantxoutset.py',
322323
'feature_txindex_compatibility.py',
323324
'feature_unsupported_utxo_db.py',

0 commit comments

Comments
 (0)