Skip to content

Commit

Permalink
test: ensure old fee_estimate.dat not read on restart and flushed
Browse files Browse the repository at this point in the history
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
  • Loading branch information
ismaelsadeeq authored and fanquake committed Sep 26, 2023
1 parent 01f8ee4 commit cb5512d
Showing 1 changed file with 101 additions and 0 deletions.
101 changes: 101 additions & 0 deletions test/functional/feature_fee_estimation.py
Expand Up @@ -7,6 +7,7 @@
from decimal import Decimal
import os
import random
import time

from test_framework.messages import (
COIN,
Expand All @@ -21,6 +22,8 @@
)
from test_framework.wallet import MiniWallet

MAX_FILE_AGE = 60
SECONDS_PER_HOUR = 60 * 60

def small_txpuzzle_randfee(
wallet, from_node, conflist, unconflist, amount, min_fee, fee_increment
Expand Down Expand Up @@ -273,6 +276,95 @@ def sanity_check_rbf_estimates(self, utxos):
est_feerate = node.estimatesmartfee(2)["feerate"]
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):
self.log.info("This test is time consuming, please be patient")
self.log.info("Splitting inputs so we can generate tx's")
Expand All @@ -296,12 +388,21 @@ def run_test(self):
self.log.info("Testing estimates with single transactions.")
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
self.log.info(
"Test fee rate estimation after restarting node with high 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.stop_node(0)
fee_dat = os.path.join(self.nodes[0].datadir, self.chain, "fee_estimates.dat")
Expand Down

0 comments on commit cb5512d

Please sign in to comment.