Skip to content

Commit 94db963

Browse files
author
MarcoFalke
committed
Merge bitcoin/bitcoin#23300: test: Implicitly sync after generate*, unless opted out
facc352 test: Implicitly sync after generate*, unless opted out (MarcoFalke) Pull request description: The most frequent failure in functional tests are intermittent races. Fixing such bugs is cumbersome because it involves: * Noticing the failure * Fetching and reading the log to determine the test case that failed * Adding a `self.sync_all()` where it was forgotten * Spamming out a pr and waiting for review, which is already sparse Also, writing a linter to catch those is not possible, nor is review effective in finding these bugs prior to merge. Fix all future intermittent races caused by a missing sync_block call by calling `sync_all` implicitly after each `generate*`, unless opted out. This ensures that the code is race-free (with regards to blocks) when the tests pass once, instead of our current approach where the code can never be guaranteed to be race-free. There are some scripted-diff cleanups (see bitcoin/bitcoin#22567), but they will be submitted in a follow-up to reduce the conflicts in this pull. ACKs for top commit: lsilva01: tACK facc352 on Ubuntu 20.04 brunoerg: tACK facc352 on MacOS 11.6 Tree-SHA512: 046a40a066b4a3bd28a3077bd654fa8887442dd1f0ec6fd11671865809ef02376f126eb667a1320ebd67b6e372c78c00dbf8bd25d86ed86f1d9a25363103ed97
2 parents 4914347 + facc352 commit 94db963

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+144
-175
lines changed

test/functional/example_test.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,8 +141,7 @@ def run_test(self):
141141
peer_messaging = self.nodes[0].add_p2p_connection(BaseNode())
142142

143143
# Generating a block on one of the nodes will get us out of IBD
144-
blocks = [int(self.generate(self.nodes[0], nblocks=1)[0], 16)]
145-
self.sync_all(self.nodes[0:2])
144+
blocks = [int(self.generate(self.nodes[0], sync_fun=lambda: self.sync_all(self.nodes[0:2]), nblocks=1)[0], 16)]
146145

147146
# Notice above how we called an RPC by calling a method with the same
148147
# name on the node object. Notice also how we used a keyword argument

test/functional/feature_abortnode.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,18 @@ def setup_network(self):
2626
# We'll connect the nodes later
2727

2828
def run_test(self):
29-
self.generate(self.nodes[0], 3)
29+
self.generate(self.nodes[0], 3, sync_fun=self.no_op)
3030
datadir = get_datadir_path(self.options.tmpdir, 0)
3131

3232
# Deleting the undo file will result in reorg failure
3333
os.unlink(os.path.join(datadir, self.chain, 'blocks', 'rev00000.dat'))
3434

3535
# Connecting to a node with a more work chain will trigger a reorg
3636
# attempt.
37-
self.generate(self.nodes[1], 3)
37+
self.generate(self.nodes[1], 3, sync_fun=self.no_op)
3838
with self.nodes[0].assert_debug_log(["Failed to disconnect block"]):
3939
self.connect_nodes(0, 1)
40-
self.generate(self.nodes[1], 1)
40+
self.generate(self.nodes[1], 1, sync_fun=self.no_op)
4141

4242
# Check that node0 aborted
4343
self.log.info("Waiting for crash")

test/functional/feature_bip68_sequence.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ def test_nonzero_locks(orig_tx, node, relayfee, use_height_lock):
275275
cur_time = int(time.time())
276276
for _ in range(10):
277277
self.nodes[0].setmocktime(cur_time + 600)
278-
self.generate(self.nodes[0], 1)
278+
self.generate(self.nodes[0], 1, sync_fun=self.no_op)
279279
cur_time += 600
280280

281281
assert tx2.hash in self.nodes[0].getrawmempool()
@@ -351,7 +351,7 @@ def test_nonzero_locks(orig_tx, node, relayfee, use_height_lock):
351351
# Reset the chain and get rid of the mocktimed-blocks
352352
self.nodes[0].setmocktime(0)
353353
self.nodes[0].invalidateblock(self.nodes[0].getblockhash(cur_height+1))
354-
self.generate(self.nodes[0], 10)
354+
self.generate(self.nodes[0], 10, sync_fun=self.no_op)
355355

356356
# Make sure that BIP68 isn't being used to validate blocks prior to
357357
# activation height. If more blocks are mined prior to this test
@@ -405,9 +405,9 @@ def activateCSV(self):
405405
min_activation_height = 432
406406
height = self.nodes[0].getblockcount()
407407
assert_greater_than(min_activation_height - height, 2)
408-
self.generate(self.nodes[0], min_activation_height - height - 2)
408+
self.generate(self.nodes[0], min_activation_height - height - 2, sync_fun=self.no_op)
409409
assert not softfork_active(self.nodes[0], 'csv')
410-
self.generate(self.nodes[0], 1)
410+
self.generate(self.nodes[0], 1, sync_fun=self.no_op)
411411
assert softfork_active(self.nodes[0], 'csv')
412412
self.sync_blocks()
413413

test/functional/feature_coinstatsindex.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ def _test_coin_stats_index(self):
228228
res9 = index_node.gettxoutsetinfo('muhash')
229229
assert_equal(res8, res9)
230230

231-
self.generate(index_node, 1)
231+
self.generate(index_node, 1, sync_fun=self.no_op)
232232
res10 = index_node.gettxoutsetinfo('muhash')
233233
assert(res8['txouts'] < res10['txouts'])
234234

@@ -254,7 +254,7 @@ def _test_reorg_index(self):
254254
assert_equal(index_node.gettxoutsetinfo('muhash')['height'], 110)
255255

256256
# Add two new blocks
257-
block = self.generate(index_node, 2)[1]
257+
block = self.generate(index_node, 2, sync_fun=self.no_op)[1]
258258
res = index_node.gettxoutsetinfo(hash_type='muhash', hash_or_height=None, use_index=False)
259259

260260
# Test that the result of the reorged block is not returned for its old block height

test/functional/feature_dbcrash.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ def run_test(self):
217217

218218
# Start by creating a lot of utxos on node3
219219
initial_height = self.nodes[3].getblockcount()
220-
utxo_list = create_confirmed_utxos(self, self.nodes[3].getnetworkinfo()['relayfee'], self.nodes[3], 5000)
220+
utxo_list = create_confirmed_utxos(self, self.nodes[3].getnetworkinfo()['relayfee'], self.nodes[3], 5000, sync_fun=self.no_op)
221221
self.log.info(f"Prepped {len(utxo_list)} utxo entries")
222222

223223
# Sync these blocks with the other nodes
@@ -258,6 +258,7 @@ def run_test(self):
258258
nblocks=min(10, current_height + 1 - self.nodes[3].getblockcount()),
259259
# new address to avoid mining a block that has just been invalidated
260260
address=self.nodes[3].getnewaddress(),
261+
sync_fun=self.no_op,
261262
))
262263
self.log.debug(f"Syncing {len(block_hashes)} new blocks...")
263264
self.sync_node3blocks(block_hashes)

test/functional/feature_fee_estimation.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ def initial_split(self, node):
237237

238238
# Mine
239239
while len(node.getrawmempool()) > 0:
240-
self.generate(node, 1)
240+
self.generate(node, 1, sync_fun=self.no_op)
241241

242242
# Repeatedly split those 2 outputs, doubling twice for each rep
243243
# Use txouts to monitor the available utxo, since these won't be tracked in wallet
@@ -247,12 +247,12 @@ def initial_split(self, node):
247247
while len(self.txouts) > 0:
248248
split_inputs(node, self.txouts, self.txouts2)
249249
while len(node.getrawmempool()) > 0:
250-
self.generate(node, 1)
250+
self.generate(node, 1, sync_fun=self.no_op)
251251
# Double txouts2 to txouts
252252
while len(self.txouts2) > 0:
253253
split_inputs(node, self.txouts2, self.txouts)
254254
while len(node.getrawmempool()) > 0:
255-
self.generate(node, 1)
255+
self.generate(node, 1, sync_fun=self.no_op)
256256
reps += 1
257257

258258
def sanity_check_estimates_range(self):

test/functional/feature_loadblock.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def set_test_params(self):
2929

3030
def run_test(self):
3131
self.nodes[1].setnetworkactive(state=False)
32-
self.generate(self.nodes[0], COINBASE_MATURITY)
32+
self.generate(self.nodes[0], COINBASE_MATURITY, sync_fun=self.no_op)
3333

3434
# Parsing the url of our node to get settings for config file
3535
data_dir = self.nodes[0].datadir

test/functional/feature_minchainwork.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,7 @@ def run_test(self):
5151

5252
num_blocks_to_generate = int((self.node_min_work[1] - starting_chain_work) / REGTEST_WORK_PER_BLOCK)
5353
self.log.info(f"Generating {num_blocks_to_generate} blocks on node0")
54-
hashes = self.generatetoaddress(self.nodes[0], num_blocks_to_generate,
55-
self.nodes[0].get_deterministic_priv_key().address)
54+
hashes = self.generate(self.nodes[0], num_blocks_to_generate, sync_fun=self.no_op)
5655

5756
self.log.info(f"Node0 current chain work: {self.nodes[0].getblockheader(hashes[-1])['chainwork']}")
5857

@@ -73,7 +72,7 @@ def run_test(self):
7372
assert_equal(self.nodes[2].getblockcount(), starting_blockcount)
7473

7574
self.log.info("Generating one more block")
76-
self.generatetoaddress(self.nodes[0], 1, self.nodes[0].get_deterministic_priv_key().address)
75+
self.generate(self.nodes[0], 1)
7776

7877
self.log.info("Verifying nodes are all synced")
7978

test/functional/feature_notifications.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ def run_test(self):
149149
# about newly confirmed bump2 and newly conflicted tx2.
150150
self.disconnect_nodes(0, 1)
151151
bump2 = self.nodes[0].bumpfee(tx2)["txid"]
152-
blockhash2 = self.generatetoaddress(self.nodes[0], 1, ADDRESS_BCRT1_UNSPENDABLE)[0]
152+
blockhash2 = self.generatetoaddress(self.nodes[0], 1, ADDRESS_BCRT1_UNSPENDABLE, sync_fun=self.no_op)[0]
153153
blockheight2 = self.nodes[0].getblockcount()
154154
assert_equal(self.nodes[0].gettransaction(bump2)["confirmations"], 1)
155155
assert_equal(tx2 in self.nodes[1].getrawmempool(), True)

test/functional/feature_pruning.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -118,9 +118,8 @@ def setup_nodes(self):
118118

119119
def create_big_chain(self):
120120
# Start by creating some coinbases we can spend later
121-
self.generate(self.nodes[1], 200)
122-
self.sync_blocks(self.nodes[0:2])
123-
self.generate(self.nodes[0], 150)
121+
self.generate(self.nodes[1], 200, sync_fun=lambda: self.sync_blocks(self.nodes[0:2]))
122+
self.generate(self.nodes[0], 150, sync_fun=self.no_op)
124123

125124
# Then mine enough full blocks to create more than 550MiB of data
126125
mine_large_blocks(self.nodes[0], 645)
@@ -211,7 +210,7 @@ def reorg_test(self):
211210
self.disconnect_nodes(1, 2)
212211

213212
self.log.info("Generating new longer chain of 300 more blocks")
214-
self.generate(self.nodes[1], 300)
213+
self.generate(self.nodes[1], 300, sync_fun=self.no_op)
215214

216215
self.log.info("Reconnect nodes")
217216
self.connect_nodes(0, 1)
@@ -263,7 +262,7 @@ def reorg_back(self):
263262
self.nodes[0].invalidateblock(curchainhash)
264263
assert_equal(self.nodes[0].getblockcount(), self.mainchainheight)
265264
assert_equal(self.nodes[0].getbestblockhash(), self.mainchainhash2)
266-
goalbesthash = self.generate(self.nodes[0], blocks_to_mine)[-1]
265+
goalbesthash = self.generate(self.nodes[0], blocks_to_mine, sync_fun=self.no_op)[-1]
267266
goalbestheight = first_reorg_height + 1
268267

269268
self.log.info("Verify node 2 reorged back to the main chain, some blocks of which it had to redownload")
@@ -306,7 +305,7 @@ def has_block(index):
306305
assert_equal(block1_details["nTx"], len(block1_details["tx"]))
307306

308307
# mine 6 blocks so we are at height 1001 (i.e., above PruneAfterHeight)
309-
self.generate(node, 6)
308+
self.generate(node, 6, sync_fun=self.no_op)
310309
assert_equal(node.getblockchaininfo()["blocks"], 1001)
311310

312311
# Pruned block should still know the number of transactions
@@ -337,7 +336,7 @@ def has_block(index):
337336
assert has_block(2), "blk00002.dat is still there, should be pruned by now"
338337

339338
# advance the tip so blk00002.dat and blk00003.dat can be pruned (the last 288 blocks should now be in blk00004.dat)
340-
self.generate(node, 288)
339+
self.generate(node, 288, sync_fun=self.no_op)
341340
prune(1000)
342341
assert not has_block(2), "blk00002.dat is still there, should be pruned by now"
343342
assert not has_block(3), "blk00003.dat is still there, should be pruned by now"

0 commit comments

Comments
 (0)