mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-21 14:34:49 +01:00
qa: test fee estimation with replacement transactions
Signed-off-by: Antoine Poinsot <darosior@protonmail.com>
This commit is contained in:
parent
053415b297
commit
4556406562
1 changed files with 81 additions and 0 deletions
|
@ -4,6 +4,7 @@
|
|||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
"""Test fee estimation code."""
|
||||
from decimal import Decimal
|
||||
import os
|
||||
import random
|
||||
|
||||
from test_framework.messages import (
|
||||
|
@ -155,6 +156,21 @@ def check_estimates(node, fees_seen):
|
|||
check_raw_estimates(node, fees_seen)
|
||||
check_smart_estimates(node, fees_seen)
|
||||
|
||||
|
||||
def send_tx(node, utxo, feerate):
|
||||
"""Broadcast a 1in-1out transaction with a specific input and feerate (sat/vb)."""
|
||||
overhead, op, scriptsig, nseq, value, spk = 10, 36, 5, 4, 8, 24
|
||||
tx_size = overhead + op + scriptsig + nseq + value + spk
|
||||
fee = tx_size * feerate
|
||||
|
||||
tx = CTransaction()
|
||||
tx.vin = [CTxIn(COutPoint(int(utxo["txid"], 16), utxo["vout"]), SCRIPT_SIG[utxo["vout"]])]
|
||||
tx.vout = [CTxOut(int(utxo["amount"] * COIN) - fee, P2SH_1)]
|
||||
txid = node.sendrawtransaction(tx.serialize().hex())
|
||||
|
||||
return txid
|
||||
|
||||
|
||||
class EstimateFeeTest(BitcoinTestFramework):
|
||||
def set_test_params(self):
|
||||
self.num_nodes = 3
|
||||
|
@ -271,6 +287,60 @@ class EstimateFeeTest(BitcoinTestFramework):
|
|||
high_val = 3*self.nodes[1].estimatesmartfee(1)['feerate']
|
||||
self.restart_node(1, extra_args=[f'-minrelaytxfee={high_val}'])
|
||||
check_estimates(self.nodes[1], self.fees_per_kb)
|
||||
self.restart_node(1)
|
||||
|
||||
def sanity_check_rbf_estimates(self, utxos):
|
||||
"""During 5 blocks, broadcast low fee transactions. Only 10% of them get
|
||||
confirmed and the remaining ones get RBF'd with a high fee transaction at
|
||||
the next block.
|
||||
The block policy estimator should return the high feerate.
|
||||
"""
|
||||
# The broadcaster and block producer
|
||||
node = self.nodes[0]
|
||||
miner = self.nodes[1]
|
||||
# In sat/vb
|
||||
low_feerate = 1
|
||||
high_feerate = 10
|
||||
# Cache the utxos of which to replace the spender after it failed to get
|
||||
# confirmed
|
||||
utxos_to_respend = []
|
||||
txids_to_replace = []
|
||||
|
||||
assert len(utxos) >= 250
|
||||
for _ in range(5):
|
||||
# Broadcast 45 low fee transactions that will need to be RBF'd
|
||||
for _ in range(45):
|
||||
u = utxos.pop(0)
|
||||
txid = send_tx(node, u, low_feerate)
|
||||
utxos_to_respend.append(u)
|
||||
txids_to_replace.append(txid)
|
||||
# Broadcast 5 low fee transaction which don't need to
|
||||
for _ in range(5):
|
||||
send_tx(node, utxos.pop(0), low_feerate)
|
||||
# Mine the transactions on another node
|
||||
self.sync_mempools(wait=.1, nodes=[node, miner])
|
||||
for txid in txids_to_replace:
|
||||
miner.prioritisetransaction(txid=txid, fee_delta=-COIN)
|
||||
self.generate(miner, 1)
|
||||
self.sync_blocks(wait=.1, nodes=[node, miner])
|
||||
# RBF the low-fee transactions
|
||||
while True:
|
||||
try:
|
||||
u = utxos_to_respend.pop(0)
|
||||
send_tx(node, u, high_feerate)
|
||||
except IndexError:
|
||||
break
|
||||
|
||||
# Mine the last replacement txs
|
||||
self.sync_mempools(wait=.1, nodes=[node, miner])
|
||||
self.generate(miner, 1)
|
||||
self.sync_blocks(wait=.1, nodes=[node, miner])
|
||||
|
||||
# Only 10% of the transactions were really confirmed with a low feerate,
|
||||
# the rest needed to be RBF'd. We must return the 90% conf rate feerate.
|
||||
high_feerate_kvb = Decimal(high_feerate) / COIN * 10**3
|
||||
est_feerate = node.estimatesmartfee(2)["feerate"]
|
||||
assert est_feerate == high_feerate_kvb
|
||||
|
||||
def run_test(self):
|
||||
self.log.info("This test is time consuming, please be patient")
|
||||
|
@ -297,6 +367,17 @@ class EstimateFeeTest(BitcoinTestFramework):
|
|||
self.log.info("Test fee rate estimation after restarting node with high MempoolMinFee")
|
||||
self.test_feerate_mempoolminfee()
|
||||
|
||||
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")
|
||||
os.remove(fee_dat)
|
||||
self.start_node(0)
|
||||
self.connect_nodes(0, 1)
|
||||
self.connect_nodes(0, 2)
|
||||
|
||||
self.log.info("Testing estimates with RBF.")
|
||||
self.sanity_check_rbf_estimates(self.confutxo + self.memutxo)
|
||||
|
||||
self.log.info("Testing that fee estimation is disabled in blocksonly.")
|
||||
self.restart_node(0, ["-blocksonly"])
|
||||
assert_raises_rpc_error(-32603, "Fee estimation disabled",
|
||||
|
|
Loading…
Add table
Reference in a new issue