Skip to content

Commit cb5512d

Browse files
ismaelsadeeqfanquake
authored andcommitted
test: ensure old fee_estimate.dat not read on restart and flushed
This commit adds tests to ensure that old fee_estimates.dat files are not read and that fee_estimates are periodically flushed to the fee_estimates.dat file. Additionaly it tests the -regtestonly option -acceptstalefeeestimates. Github-Pull: #27622 Rebased-From: d2b39e0
1 parent 01f8ee4 commit cb5512d

File tree

1 file changed

+101
-0
lines changed

1 file changed

+101
-0
lines changed

test/functional/feature_fee_estimation.py

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from decimal import Decimal
88
import os
99
import random
10+
import time
1011

1112
from test_framework.messages import (
1213
COIN,
@@ -21,6 +22,8 @@
2122
)
2223
from test_framework.wallet import MiniWallet
2324

25+
MAX_FILE_AGE = 60
26+
SECONDS_PER_HOUR = 60 * 60
2427

2528
def small_txpuzzle_randfee(
2629
wallet, from_node, conflist, unconflist, amount, min_fee, fee_increment
@@ -273,6 +276,95 @@ def sanity_check_rbf_estimates(self, utxos):
273276
est_feerate = node.estimatesmartfee(2)["feerate"]
274277
assert_equal(est_feerate, high_feerate_kvb)
275278

279+
def test_old_fee_estimate_file(self):
280+
# Get the initial fee rate while node is running
281+
fee_rate = self.nodes[0].estimatesmartfee(1)["feerate"]
282+
283+
# Restart node to ensure fee_estimate.dat file is read
284+
self.restart_node(0)
285+
assert_equal(self.nodes[0].estimatesmartfee(1)["feerate"], fee_rate)
286+
287+
fee_dat = self.nodes[0].chain_path / "fee_estimates.dat"
288+
289+
# Stop the node and backdate the fee_estimates.dat file more than MAX_FILE_AGE
290+
self.stop_node(0)
291+
last_modified_time = time.time() - (MAX_FILE_AGE + 1) * SECONDS_PER_HOUR
292+
os.utime(fee_dat, (last_modified_time, last_modified_time))
293+
294+
# Start node and ensure the fee_estimates.dat file was not read
295+
self.start_node(0)
296+
assert_equal(self.nodes[0].estimatesmartfee(1)["errors"], ["Insufficient data or no feerate found"])
297+
298+
299+
def test_estimate_dat_is_flushed_periodically(self):
300+
fee_dat = self.nodes[0].chain_path / "fee_estimates.dat"
301+
os.remove(fee_dat) if os.path.exists(fee_dat) else None
302+
303+
# Verify that fee_estimates.dat does not exist
304+
assert_equal(os.path.isfile(fee_dat), False)
305+
306+
# Verify if the string "Flushed fee estimates to fee_estimates.dat." is present in the debug log file.
307+
# If present, it indicates that fee estimates have been successfully flushed to disk.
308+
with self.nodes[0].assert_debug_log(expected_msgs=["Flushed fee estimates to fee_estimates.dat."], timeout=1):
309+
# Mock the scheduler for an hour to flush fee estimates to fee_estimates.dat
310+
self.nodes[0].mockscheduler(SECONDS_PER_HOUR)
311+
312+
# Verify that fee estimates were flushed and fee_estimates.dat file is created
313+
assert_equal(os.path.isfile(fee_dat), True)
314+
315+
# Verify that the estimates remain the same if there are no blocks in the flush interval
316+
block_hash_before = self.nodes[0].getbestblockhash()
317+
fee_dat_initial_content = open(fee_dat, "rb").read()
318+
with self.nodes[0].assert_debug_log(expected_msgs=["Flushed fee estimates to fee_estimates.dat."], timeout=1):
319+
# Mock the scheduler for an hour to flush fee estimates to fee_estimates.dat
320+
self.nodes[0].mockscheduler(SECONDS_PER_HOUR)
321+
322+
# Verify that there were no blocks in between the flush interval
323+
assert_equal(block_hash_before, self.nodes[0].getbestblockhash())
324+
325+
fee_dat_current_content = open(fee_dat, "rb").read()
326+
assert_equal(fee_dat_current_content, fee_dat_initial_content)
327+
328+
# Verify that the estimates remain the same after shutdown with no blocks before shutdown
329+
self.restart_node(0)
330+
fee_dat_current_content = open(fee_dat, "rb").read()
331+
assert_equal(fee_dat_current_content, fee_dat_initial_content)
332+
333+
# Verify that the estimates are not the same if new blocks were produced in the flush interval
334+
with self.nodes[0].assert_debug_log(expected_msgs=["Flushed fee estimates to fee_estimates.dat."], timeout=1):
335+
# Mock the scheduler for an hour to flush fee estimates to fee_estimates.dat
336+
self.generate(self.nodes[0], 5, sync_fun=self.no_op)
337+
self.nodes[0].mockscheduler(SECONDS_PER_HOUR)
338+
339+
fee_dat_current_content = open(fee_dat, "rb").read()
340+
assert fee_dat_current_content != fee_dat_initial_content
341+
342+
fee_dat_initial_content = fee_dat_current_content
343+
344+
# Generate blocks before shutdown and verify that the fee estimates are not the same
345+
self.generate(self.nodes[0], 5, sync_fun=self.no_op)
346+
self.restart_node(0)
347+
fee_dat_current_content = open(fee_dat, "rb").read()
348+
assert fee_dat_current_content != fee_dat_initial_content
349+
350+
351+
def test_acceptstalefeeestimates_option(self):
352+
# Get the initial fee rate while node is running
353+
fee_rate = self.nodes[0].estimatesmartfee(1)["feerate"]
354+
355+
self.stop_node(0)
356+
357+
fee_dat = self.nodes[0].chain_path / "fee_estimates.dat"
358+
359+
# Stop the node and backdate the fee_estimates.dat file more than MAX_FILE_AGE
360+
last_modified_time = time.time() - (MAX_FILE_AGE + 1) * SECONDS_PER_HOUR
361+
os.utime(fee_dat, (last_modified_time, last_modified_time))
362+
363+
# Restart node with -acceptstalefeeestimates option to ensure fee_estimate.dat file is read
364+
self.start_node(0,extra_args=["-acceptstalefeeestimates"])
365+
assert_equal(self.nodes[0].estimatesmartfee(1)["feerate"], fee_rate)
366+
367+
276368
def run_test(self):
277369
self.log.info("This test is time consuming, please be patient")
278370
self.log.info("Splitting inputs so we can generate tx's")
@@ -296,12 +388,21 @@ def run_test(self):
296388
self.log.info("Testing estimates with single transactions.")
297389
self.sanity_check_estimates_range()
298390

391+
self.log.info("Test fee_estimates.dat is flushed periodically")
392+
self.test_estimate_dat_is_flushed_periodically()
393+
299394
# check that the effective feerate is greater than or equal to the mempoolminfee even for high mempoolminfee
300395
self.log.info(
301396
"Test fee rate estimation after restarting node with high MempoolMinFee"
302397
)
303398
self.test_feerate_mempoolminfee()
304399

400+
self.log.info("Test acceptstalefeeestimates option")
401+
self.test_acceptstalefeeestimates_option()
402+
403+
self.log.info("Test reading old fee_estimates.dat")
404+
self.test_old_fee_estimate_file()
405+
305406
self.log.info("Restarting node with fresh estimation")
306407
self.stop_node(0)
307408
fee_dat = os.path.join(self.nodes[0].datadir, self.chain, "fee_estimates.dat")

0 commit comments

Comments
 (0)