13
13
hash256 ,
14
14
msg_getcfcheckpt ,
15
15
msg_getcfheaders ,
16
+ msg_getcfilters ,
16
17
ser_uint256 ,
17
18
uint256_from_str ,
18
19
)
25
26
wait_until ,
26
27
)
27
28
29
+ class CFiltersClient (P2PInterface ):
30
+ def __init__ (self ):
31
+ super ().__init__ ()
32
+ # Store the cfilters received.
33
+ self .cfilters = []
34
+
35
+ def pop_cfilters (self ):
36
+ cfilters = self .cfilters
37
+ self .cfilters = []
38
+ return cfilters
39
+
40
+ def on_cfilter (self , message ):
41
+ """Store cfilters received in a list."""
42
+ self .cfilters .append (message )
43
+
28
44
class CompactFiltersTest (BitcoinTestFramework ):
29
45
def set_test_params (self ):
30
46
self .setup_clean_chain = True
@@ -37,8 +53,8 @@ def set_test_params(self):
37
53
38
54
def run_test (self ):
39
55
# Node 0 supports COMPACT_FILTERS, node 1 does not.
40
- node0 = self .nodes [0 ].add_p2p_connection (P2PInterface ())
41
- node1 = self .nodes [1 ].add_p2p_connection (P2PInterface ())
56
+ node0 = self .nodes [0 ].add_p2p_connection (CFiltersClient ())
57
+ node1 = self .nodes [1 ].add_p2p_connection (CFiltersClient ())
42
58
43
59
# Nodes 0 & 1 share the same first 999 blocks in the chain.
44
60
self .nodes [0 ].generate (999 )
@@ -112,7 +128,8 @@ def run_test(self):
112
128
)
113
129
node0 .send_and_ping (request )
114
130
response = node0 .last_message ['cfheaders' ]
115
- assert_equal (len (response .hashes ), 1000 )
131
+ main_cfhashes = response .hashes
132
+ assert_equal (len (main_cfhashes ), 1000 )
116
133
assert_equal (
117
134
compute_last_header (response .prev_header , response .hashes ),
118
135
int (main_cfcheckpt , 16 )
@@ -126,12 +143,50 @@ def run_test(self):
126
143
)
127
144
node0 .send_and_ping (request )
128
145
response = node0 .last_message ['cfheaders' ]
129
- assert_equal (len (response .hashes ), 1000 )
146
+ stale_cfhashes = response .hashes
147
+ assert_equal (len (stale_cfhashes ), 1000 )
130
148
assert_equal (
131
149
compute_last_header (response .prev_header , response .hashes ),
132
150
int (stale_cfcheckpt , 16 )
133
151
)
134
152
153
+ self .log .info ("Check that peers can fetch cfilters." )
154
+ stop_hash = self .nodes [0 ].getblockhash (10 )
155
+ request = msg_getcfilters (
156
+ filter_type = FILTER_TYPE_BASIC ,
157
+ start_height = 1 ,
158
+ stop_hash = int (stop_hash , 16 )
159
+ )
160
+ node0 .send_message (request )
161
+ node0 .sync_with_ping ()
162
+ response = node0 .pop_cfilters ()
163
+ assert_equal (len (response ), 10 )
164
+
165
+ self .log .info ("Check that cfilter responses are correct." )
166
+ for cfilter , cfhash , height in zip (response , main_cfhashes , range (1 , 11 )):
167
+ block_hash = self .nodes [0 ].getblockhash (height )
168
+ assert_equal (cfilter .filter_type , FILTER_TYPE_BASIC )
169
+ assert_equal (cfilter .block_hash , int (block_hash , 16 ))
170
+ computed_cfhash = uint256_from_str (hash256 (cfilter .filter_data ))
171
+ assert_equal (computed_cfhash , cfhash )
172
+
173
+ self .log .info ("Check that peers can fetch cfilters for stale blocks." )
174
+ request = msg_getcfilters (
175
+ filter_type = FILTER_TYPE_BASIC ,
176
+ start_height = 1000 ,
177
+ stop_hash = int (stale_block_hash , 16 )
178
+ )
179
+ node0 .send_message (request )
180
+ node0 .sync_with_ping ()
181
+ response = node0 .pop_cfilters ()
182
+ assert_equal (len (response ), 1 )
183
+
184
+ cfilter = response [0 ]
185
+ assert_equal (cfilter .filter_type , FILTER_TYPE_BASIC )
186
+ assert_equal (cfilter .block_hash , int (stale_block_hash , 16 ))
187
+ computed_cfhash = uint256_from_str (hash256 (cfilter .filter_data ))
188
+ assert_equal (computed_cfhash , stale_cfhashes [999 ])
189
+
135
190
self .log .info ("Requests to node 1 without NODE_COMPACT_FILTERS results in disconnection." )
136
191
requests = [
137
192
msg_getcfcheckpt (
@@ -143,6 +198,11 @@ def run_test(self):
143
198
start_height = 1000 ,
144
199
stop_hash = int (main_block_hash , 16 )
145
200
),
201
+ msg_getcfilters (
202
+ filter_type = FILTER_TYPE_BASIC ,
203
+ start_height = 1000 ,
204
+ stop_hash = int (main_block_hash , 16 )
205
+ ),
146
206
]
147
207
for request in requests :
148
208
node1 = self .nodes [1 ].add_p2p_connection (P2PInterface ())
@@ -151,6 +211,12 @@ def run_test(self):
151
211
152
212
self .log .info ("Check that invalid requests result in disconnection." )
153
213
requests = [
214
+ # Requesting too many filters results in disconnection.
215
+ msg_getcfilters (
216
+ filter_type = FILTER_TYPE_BASIC ,
217
+ start_height = 0 ,
218
+ stop_hash = int (main_block_hash , 16 )
219
+ ),
154
220
# Requesting too many filter headers results in disconnection.
155
221
msg_getcfheaders (
156
222
filter_type = FILTER_TYPE_BASIC ,
0 commit comments