14
14
from test_framework .blocktools import (
15
15
create_block ,
16
16
create_coinbase ,
17
+ add_witness_commitment ,
17
18
)
18
19
19
20
from test_framework .test_framework import BitcoinTestFramework
24
25
25
26
from test_framework .messages import (
26
27
BLOCK_HEADER_SIZE ,
28
+ uint256_from_compact ,
29
+ )
30
+
31
+ from test_framework .wallet import (
32
+ MiniWallet ,
27
33
)
28
34
29
35
def assert_template (node , block , expect , * , rehash = True , submit = True , solve = True , expect_submit = None ):
@@ -118,6 +124,11 @@ def nbits_test(self, node, block):
118
124
bad_block .nBits = 469762303 # impossible in the real world
119
125
assert_template (node , bad_block , "bad-diffbits" , solve = False , expect_submit = "high-hash" )
120
126
127
+ self .log .info ("Lowering nBits should make the block invalid" )
128
+ bad_block = copy .deepcopy (block )
129
+ bad_block .nBits -= 1
130
+ assert_template (node , bad_block , "bad-diffbits" )
131
+
121
132
def merkle_root_test (self , node , block ):
122
133
self .log .info ("Bad merkle root" )
123
134
bad_block = copy .deepcopy (block )
@@ -140,6 +151,117 @@ def current_tip_test(self, node, block):
140
151
141
152
assert_template (node , bad_block , "inconclusive-not-best-prevblk" , expect_submit = "prev-blk-not-found" )
142
153
154
+ def pow_test (self , node , block ):
155
+ '''Modifies block with the generated PoW'''
156
+ self .log .info ("Generate a block" )
157
+ target = uint256_from_compact (block .nBits )
158
+ # Ensure that it doesn't meet the target by coincidence
159
+ while block .sha256 <= target :
160
+ block .nNonce += 1
161
+ block .rehash ()
162
+ self .log .debug ("Found a nonce" )
163
+
164
+ self .log .info ("A block template doesn't need PoW" )
165
+ assert_template (node , block , None )
166
+
167
+ self .log .info ("Add proof of work" )
168
+ block .solve ()
169
+ assert_template (node , block , None )
170
+
171
+ def submit_test (self , node , block_0_height , block ):
172
+ self .log .info ("getblocktemplate call in previous tests did not submit the block" )
173
+ assert_equal (node .getblockcount (), block_0_height + 1 )
174
+
175
+ self .log .info ("Submitting this block should succeed" )
176
+ assert_equal (node .submitblock (block .serialize ().hex ()), None )
177
+ node .waitforblockheight (2 )
178
+
179
+ def transaction_test (self , node , block_0_height , tx ):
180
+ self .log .info ("make block template with a transaction" )
181
+
182
+ block_1 = node .getblock (node .getblockhash (block_0_height + 1 ))
183
+ block_2_hash = node .getblockhash (block_0_height + 2 )
184
+
185
+ block_3 = create_block (
186
+ int (block_2_hash , 16 ),
187
+ create_coinbase (block_0_height + 3 ),
188
+ block_1 ["mediantime" ] + 1 ,
189
+ txlist = [tx ["hex" ]],
190
+ )
191
+ assert_equal (len (block_3 .vtx ), 2 )
192
+ add_witness_commitment (block_3 )
193
+ block_3 .solve ()
194
+ assert_template (node , block_3 , None )
195
+
196
+ self .log .info ("checking block validity did not update the UTXO set" )
197
+ # Call again to ensure the UTXO set wasn't updated
198
+ assert_template (node , block_3 , None )
199
+
200
+ def overspending_transaction_test (self , node , block_0_height , tx ):
201
+ self .log .info ("Add an transaction that spends too much" )
202
+
203
+ block_1 = node .getblock (node .getblockhash (block_0_height + 1 ))
204
+ block_2_hash = node .getblockhash (block_0_height + 2 )
205
+
206
+ bad_tx = copy .deepcopy (tx )
207
+ bad_tx ["tx" ].vout [0 ].nValue = 10000000000
208
+ bad_tx_hex = bad_tx ["tx" ].serialize ().hex ()
209
+ assert_equal (
210
+ node .testmempoolaccept ([bad_tx_hex ])[0 ]["reject-reason" ],
211
+ "bad-txns-in-belowout" ,
212
+ )
213
+ block_3 = create_block (
214
+ int (block_2_hash , 16 ),
215
+ create_coinbase (block_0_height + 3 ),
216
+ block_1 ["mediantime" ] + 1 ,
217
+ txlist = [bad_tx_hex ],
218
+ )
219
+ assert_equal (len (block_3 .vtx ), 2 )
220
+ add_witness_commitment (block_3 )
221
+ block_3 .solve ()
222
+
223
+ assert_template (node , block_3 , "bad-txns-in-belowout" )
224
+
225
+ def spend_twice_test (self , node , block_0_height , tx ):
226
+ block_1 = node .getblock (node .getblockhash (block_0_height + 1 ))
227
+ block_2_hash = node .getblockhash (block_0_height + 2 )
228
+
229
+ self .log .info ("Can't spend coins twice" )
230
+ tx_hex = tx ["tx" ].serialize ().hex ()
231
+ tx_2 = copy .deepcopy (tx )
232
+ tx_2_hex = tx_2 ["tx" ].serialize ().hex ()
233
+ # Nothing wrong with these transactions individually
234
+ assert_equal (node .testmempoolaccept ([tx_hex ])[0 ]["allowed" ], True )
235
+ assert_equal (node .testmempoolaccept ([tx_2_hex ])[0 ]["allowed" ], True )
236
+ # But can't be combined
237
+ assert_equal (
238
+ node .testmempoolaccept ([tx_hex , tx_2_hex ])[0 ]["package-error" ],
239
+ "package-contains-duplicates" ,
240
+ )
241
+ block_3 = create_block (
242
+ int (block_2_hash , 16 ),
243
+ create_coinbase (block_0_height + 3 ),
244
+ block_1 ["mediantime" ] + 1 ,
245
+ txlist = [tx_hex , tx_2_hex ],
246
+ )
247
+ assert_equal (len (block_3 .vtx ), 3 )
248
+ add_witness_commitment (block_3 )
249
+
250
+ assert_template (node , block_3 , "bad-txns-inputs-missingorspent" , submit = False )
251
+
252
+ return block_3
253
+
254
+ def parallel_test (self , node , block_3 ):
255
+ # Ensure that getblocktemplate can be called concurrently by many threads.
256
+ self .log .info ("Check blocks in parallel" )
257
+ check_50_blocks = lambda n : [
258
+ assert_template (n , block_3 , "bad-txns-inputs-missingorspent" , submit = False )
259
+ for _ in range (50 )
260
+ ]
261
+ rpcs = [node .cli for _ in range (6 )]
262
+ with ThreadPoolExecutor (max_workers = len (rpcs )) as threads :
263
+ list (threads .map (check_50_blocks , rpcs ))
264
+
143
265
def run_test (self ):
144
266
node = self .nodes [0 ]
145
267
@@ -164,6 +286,17 @@ def run_test(self):
164
286
self .merkle_root_test (node , block_2 )
165
287
self .bad_timestamp_test (node , block_2 )
166
288
self .current_tip_test (node , block_2 )
289
+ # This sets the PoW for the next test
290
+ self .pow_test (node , block_2 )
291
+ self .submit_test (node , block_0_height , block_2 )
292
+
293
+ self .log .info ("Generate a transaction" )
294
+ tx = MiniWallet (node ).create_self_transfer ()
295
+
296
+ self .transaction_test (node , block_0_height , tx )
297
+ self .overspending_transaction_test (node , block_0_height , tx )
298
+ block_3 = self .spend_twice_test (node , block_0_height , tx )
299
+ self .parallel_test (node , block_3 )
167
300
168
301
if __name__ == "__main__" :
169
302
MiningTemplateVerificationTest (__file__ ).main ()
0 commit comments