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.
pull/27622/head
ismaelsadeeq 1 year ago
parent cf219f29f3
commit d2b39e09bc

@ -7,6 +7,7 @@ from copy import deepcopy
from decimal import Decimal from decimal import Decimal
import os import os
import random import random
import time
from test_framework.messages import ( from test_framework.messages import (
COIN, COIN,
@ -21,6 +22,8 @@ from test_framework.util import (
) )
from test_framework.wallet import MiniWallet from test_framework.wallet import MiniWallet
MAX_FILE_AGE = 60
SECONDS_PER_HOUR = 60 * 60
def small_txpuzzle_randfee( def small_txpuzzle_randfee(
wallet, from_node, conflist, unconflist, amount, min_fee, fee_increment, batch_reqs wallet, from_node, conflist, unconflist, amount, min_fee, fee_increment, batch_reqs
@ -290,6 +293,95 @@ class EstimateFeeTest(BitcoinTestFramework):
est_feerate = node.estimatesmartfee(2)["feerate"] est_feerate = node.estimatesmartfee(2)["feerate"]
assert_equal(est_feerate, high_feerate_kvb) assert_equal(est_feerate, high_feerate_kvb)
def test_old_fee_estimate_file(self):
# Get the initial fee rate while node is running
fee_rate = self.nodes[0].estimatesmartfee(1)["feerate"]
# Restart node to ensure fee_estimate.dat file is read
self.restart_node(0)
assert_equal(self.nodes[0].estimatesmartfee(1)["feerate"], fee_rate)
fee_dat = self.nodes[0].chain_path / "fee_estimates.dat"
# Stop the node and backdate the fee_estimates.dat file more than MAX_FILE_AGE
self.stop_node(0)
last_modified_time = time.time() - (MAX_FILE_AGE + 1) * SECONDS_PER_HOUR
os.utime(fee_dat, (last_modified_time, last_modified_time))
# Start node and ensure the fee_estimates.dat file was not read
self.start_node(0)
assert_equal(self.nodes[0].estimatesmartfee(1)["errors"], ["Insufficient data or no feerate found"])
def test_estimate_dat_is_flushed_periodically(self):
fee_dat = self.nodes[0].chain_path / "fee_estimates.dat"
os.remove(fee_dat) if os.path.exists(fee_dat) else None
# Verify that fee_estimates.dat does not exist
assert_equal(os.path.isfile(fee_dat), False)
# Verify if the string "Flushed fee estimates to fee_estimates.dat." is present in the debug log file.
# If present, it indicates that fee estimates have been successfully flushed to disk.
with self.nodes[0].assert_debug_log(expected_msgs=["Flushed fee estimates to fee_estimates.dat."], timeout=1):
# Mock the scheduler for an hour to flush fee estimates to fee_estimates.dat
self.nodes[0].mockscheduler(SECONDS_PER_HOUR)
# Verify that fee estimates were flushed and fee_estimates.dat file is created
assert_equal(os.path.isfile(fee_dat), True)
# Verify that the estimates remain the same if there are no blocks in the flush interval
block_hash_before = self.nodes[0].getbestblockhash()
fee_dat_initial_content = open(fee_dat, "rb").read()
with self.nodes[0].assert_debug_log(expected_msgs=["Flushed fee estimates to fee_estimates.dat."], timeout=1):
# Mock the scheduler for an hour to flush fee estimates to fee_estimates.dat
self.nodes[0].mockscheduler(SECONDS_PER_HOUR)
# Verify that there were no blocks in between the flush interval
assert_equal(block_hash_before, self.nodes[0].getbestblockhash())
fee_dat_current_content = open(fee_dat, "rb").read()
assert_equal(fee_dat_current_content, fee_dat_initial_content)
# Verify that the estimates remain the same after shutdown with no blocks before shutdown
self.restart_node(0)
fee_dat_current_content = open(fee_dat, "rb").read()
assert_equal(fee_dat_current_content, fee_dat_initial_content)
# Verify that the estimates are not the same if new blocks were produced in the flush interval
with self.nodes[0].assert_debug_log(expected_msgs=["Flushed fee estimates to fee_estimates.dat."], timeout=1):
# Mock the scheduler for an hour to flush fee estimates to fee_estimates.dat
self.generate(self.nodes[0], 5, sync_fun=self.no_op)
self.nodes[0].mockscheduler(SECONDS_PER_HOUR)
fee_dat_current_content = open(fee_dat, "rb").read()
assert fee_dat_current_content != fee_dat_initial_content
fee_dat_initial_content = fee_dat_current_content
# Generate blocks before shutdown and verify that the fee estimates are not the same
self.generate(self.nodes[0], 5, sync_fun=self.no_op)
self.restart_node(0)
fee_dat_current_content = open(fee_dat, "rb").read()
assert fee_dat_current_content != fee_dat_initial_content
def test_acceptstalefeeestimates_option(self):
# Get the initial fee rate while node is running
fee_rate = self.nodes[0].estimatesmartfee(1)["feerate"]
self.stop_node(0)
fee_dat = self.nodes[0].chain_path / "fee_estimates.dat"
# Stop the node and backdate the fee_estimates.dat file more than MAX_FILE_AGE
last_modified_time = time.time() - (MAX_FILE_AGE + 1) * SECONDS_PER_HOUR
os.utime(fee_dat, (last_modified_time, last_modified_time))
# Restart node with -acceptstalefeeestimates option to ensure fee_estimate.dat file is read
self.start_node(0,extra_args=["-acceptstalefeeestimates"])
assert_equal(self.nodes[0].estimatesmartfee(1)["feerate"], fee_rate)
def run_test(self): def run_test(self):
self.log.info("This test is time consuming, please be patient") self.log.info("This test is time consuming, please be patient")
self.log.info("Splitting inputs so we can generate tx's") self.log.info("Splitting inputs so we can generate tx's")
@ -312,12 +404,21 @@ class EstimateFeeTest(BitcoinTestFramework):
self.log.info("Testing estimates with single transactions.") self.log.info("Testing estimates with single transactions.")
self.sanity_check_estimates_range() self.sanity_check_estimates_range()
self.log.info("Test fee_estimates.dat is flushed periodically")
self.test_estimate_dat_is_flushed_periodically()
# check that the effective feerate is greater than or equal to the mempoolminfee even for high mempoolminfee # check that the effective feerate is greater than or equal to the mempoolminfee even for high mempoolminfee
self.log.info( self.log.info(
"Test fee rate estimation after restarting node with high MempoolMinFee" "Test fee rate estimation after restarting node with high MempoolMinFee"
) )
self.test_feerate_mempoolminfee() self.test_feerate_mempoolminfee()
self.log.info("Test acceptstalefeeestimates option")
self.test_acceptstalefeeestimates_option()
self.log.info("Test reading old fee_estimates.dat")
self.test_old_fee_estimate_file()
self.log.info("Restarting node with fresh estimation") self.log.info("Restarting node with fresh estimation")
self.stop_node(0) self.stop_node(0)
fee_dat = os.path.join(self.nodes[0].datadir, self.chain, "fee_estimates.dat") fee_dat = os.path.join(self.nodes[0].datadir, self.chain, "fee_estimates.dat")

Loading…
Cancel
Save