1 #!/usr/bin/env python3
2 # Copyright (c) 2014-2022 The Limenka developers
3 # Distributed under the MIT software license, see the accompanying
4 # file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 """Test fee estimation code."""
6 from copy import deepcopy
7 from decimal import Decimal, ROUND_DOWN
8 import http.client
9 import json
10 import os
11 import random
12 import time
13 import urllib.parse
14 15 from test_framework.messages import (
16 COIN,
17 )
18 from test_framework.test_framework import LimenkaTestFramework
19 from test_framework.util import (
20 assert_equal,
21 assert_greater_than,
22 assert_greater_than_or_equal,
23 assert_raises_rpc_error,
24 satoshi_round,
25 )
26 from test_framework.wallet import MiniWallet
27 28 MAX_FILE_AGE = 60
29 SECONDS_PER_HOUR = 60 * 60
30 target_success_threshold = 0.8
31 32 def small_txpuzzle_randfee(
33 wallet, from_node, conflist, unconflist, amount, min_fee, fee_increment, batch_reqs
34 ):
35 """Create and send a transaction with a random fee using MiniWallet.
36 37 The function takes a list of confirmed outputs and unconfirmed outputs
38 and attempts to use the confirmed list first for its inputs.
39 It adds the newly created outputs to the unconfirmed list.
40 Returns (raw transaction, fee)."""
41 42 # It's best to exponentially distribute our random fees
43 # because the buckets are exponentially spaced.
44 # Exponentially distributed from 1-128 * fee_increment
45 rand_fee = float(fee_increment) * (1.1892 ** random.randint(0, 28))
46 # Total fee ranges from min_fee to min_fee + 127*fee_increment
47 fee = min_fee - fee_increment + satoshi_round(rand_fee, rounding=ROUND_DOWN)
48 utxos_to_spend = []
49 total_in = Decimal("0.00000000")
50 while total_in <= (amount + fee) and len(conflist) > 0:
51 t = conflist.pop(0)
52 total_in += t["value"]
53 utxos_to_spend.append(t)
54 while total_in <= (amount + fee) and len(unconflist) > 0:
55 t = unconflist.pop(0)
56 total_in += t["value"]
57 utxos_to_spend.append(t)
58 if total_in <= amount + fee:
59 raise RuntimeError(f"Insufficient funds: need {amount + fee}, have {total_in}")
60 tx = wallet.create_self_transfer_multi(
61 utxos_to_spend=utxos_to_spend,
62 fee_per_output=0,
63 )["tx"]
64 tx.vout[0].nValue = int((total_in - amount - fee) * COIN)
65 tx.vout.append(deepcopy(tx.vout[0]))
66 tx.vout[1].nValue = int(amount * COIN)
67 tx.rehash()
68 txid = tx.hash
69 tx_hex = tx.serialize().hex()
70 71 batch_reqs.append(from_node.sendrawtransaction.get_request(hexstring=tx_hex, maxfeerate=0))
72 unconflist.append({"txid": txid, "vout": 0, "value": total_in - amount - fee})
73 unconflist.append({"txid": txid, "vout": 1, "value": amount})
74 75 return (tx.get_vsize(), fee)
76 77 78 def rest_getfee(url, mode, target, status=200):
79 rest_uri = '/rest/fee/%s/%s.json' % (mode, target)
80 81 url = urllib.parse.urlparse(url)
82 conn = http.client.HTTPConnection(url.hostname, url.port)
83 conn.request('GET', rest_uri)
84 resp = conn.getresponse()
85 data = resp.read()
86 87 assert_equal(resp.status, status)
88 89 if status == 200:
90 return json.loads(data.decode('utf-8'), parse_float=Decimal)
91 else:
92 return data
93 94 def check_raw_estimates(node, fees_seen):
95 """Call estimaterawfee and verify that the estimates meet certain invariants."""
96 97 delta = 1.0e-6 # account for rounding error
98 for i in range(1, 26):
99 for _, e in node.estimaterawfee(i).items():
100 feerate = float(e["feerate"])
101 assert_greater_than(feerate, 0)
102 103 if feerate + delta < min(fees_seen) or feerate - delta > max(fees_seen):
104 raise AssertionError(
105 f"Estimated fee ({feerate}) out of range ({min(fees_seen)},{max(fees_seen)})"
106 )
107 108 109 def check_smart_estimates(node, fees_seen):
110 """Call estimatesmartfee and verify that the estimates meet certain invariants."""
111 112 delta = 1.0e-6 # account for rounding error
113 all_smart_estimates = [node.estimatesmartfee(i) for i in range(1, 26)]
114 mempoolMinFee = node.getmempoolinfo()["mempoolminfee"]
115 minRelaytxFee = node.getmempoolinfo()["minrelaytxfee"]
116 feerate_ceiling = max(max(fees_seen), float(mempoolMinFee), float(minRelaytxFee))
117 last_feerate = feerate_ceiling
118 for i, e in enumerate(all_smart_estimates): # estimate is for i+1
119 assert_equal(e, rest_getfee(node.url, 'unset', i+1))
120 121 feerate = float(e["feerate"])
122 assert_greater_than(feerate, 0)
123 assert_greater_than_or_equal(feerate, float(mempoolMinFee))
124 assert_greater_than_or_equal(feerate, float(minRelaytxFee))
125 126 if feerate + delta < min(fees_seen) or feerate - delta > feerate_ceiling:
127 raise AssertionError(
128 f"Estimated fee ({feerate}) out of range ({min(fees_seen)},{feerate_ceiling})"
129 )
130 if feerate - delta > last_feerate:
131 raise AssertionError(
132 f"Estimated fee ({feerate}) larger than last fee ({last_feerate}) for lower number of confirms"
133 )
134 last_feerate = feerate
135 136 if i == 0:
137 assert_equal(e["blocks"], 2)
138 else:
139 assert_greater_than_or_equal(i + 1, e["blocks"])
140 141 142 def check_estimates(node, fees_seen):
143 check_raw_estimates(node, fees_seen)
144 check_smart_estimates(node, fees_seen)
145 146 147 def make_tx(wallet, utxo, feerate):
148 """Create a 1in-1out transaction with a specific input and feerate (sat/vb)."""
149 return wallet.create_self_transfer(
150 utxo_to_spend=utxo,
151 fee_rate=Decimal(feerate * 1000) / COIN,
152 )
153 154 def check_fee_estimates_btw_modes(node, expected_conservative, expected_economical):
155 fee_est_conservative = node.estimatesmartfee(1, estimate_mode="conservative")['feerate']
156 fee_est_economical = node.estimatesmartfee(1, estimate_mode="economical")['feerate']
157 fee_est_default = node.estimatesmartfee(1)['feerate']
158 assert_equal(fee_est_conservative, expected_conservative)
159 assert_equal(fee_est_economical, expected_economical)
160 assert_equal(fee_est_default, expected_economical)
161 assert_equal(fee_est_conservative, rest_getfee(node.url, 'conservative', 1)['feerate'])
162 assert_equal(fee_est_economical, rest_getfee(node.url, 'economical', 1)['feerate'])
163 assert_equal(fee_est_default, rest_getfee(node.url, 'unset', 1)['feerate'])
164 165 166 def get_feerate_into_mempool(node, kB):
167 mempool_entries = list(node.getrawmempool(verbose=True).values())
168 for entry in mempool_entries:
169 entry['feerate_BTC/vB'] = entry['fees']['modified'] / entry['vsize']
170 mempool_entries.sort(key=lambda entry: entry['feerate_BTC/vB'], reverse=True)
171 bytes_remaining = kB * 1000
172 for entry in mempool_entries:
173 bytes_remaining -= entry['vsize']
174 if bytes_remaining <= 0:
175 return satoshi_round(entry['feerate_BTC/vB'] * 1000, rounding=ROUND_DOWN)
176 raise AssertionError('Entire mempool is smaller than %s kB' % (kB,))
177 178 179 class EstimateFeeTest(LimenkaTestFramework):
180 def set_test_params(self):
181 self.num_nodes = 3
182 # whitelist peers to speed up tx relay / mempool sync
183 self.noban_tx_relay = True
184 self.extra_args = [
185 ['-rest'],
186 ["-blockmaxweight=72000", "-rest"],
187 ["-blockmaxweight=36000"],
188 ]
189 190 def setup_network(self):
191 """
192 We'll setup the network to have 3 nodes that all mine with different parameters.
193 But first we need to use one node to create a lot of outputs
194 which we will use to generate our transactions.
195 """
196 self.add_nodes(3, extra_args=self.extra_args)
197 # Use node0 to mine blocks for input splitting
198 # Node1 mines small blocks but that are bigger than the expected transaction rate.
199 # NOTE: the CreateNewBlock code starts counting block weight at 4,000 weight,
200 # (68k weight is room enough for 120 or so transactions)
201 # Node2 is a stingy miner, that
202 # produces too small blocks (room for only 55 or so transactions)
203 204 def transact_and_mine(self, numblocks, mining_node):
205 min_fee = Decimal("0.00001")
206 # We will now mine numblocks blocks generating on average 100 transactions between each block
207 # We shuffle our confirmed txout set before each set of transactions
208 # small_txpuzzle_randfee will use the transactions that have inputs already in the chain when possible
209 # resorting to tx's that depend on the mempool when those run out
210 for _ in range(numblocks):
211 random.shuffle(self.confutxo)
212 batch_sendtx_reqs = []
213 for _ in range(random.randrange(100 - 50, 100 + 50)):
214 from_index = random.randint(1, 2)
215 (tx_bytes, fee) = small_txpuzzle_randfee(
216 self.wallet,
217 self.nodes[from_index],
218 self.confutxo,
219 self.memutxo,
220 Decimal("0.005"),
221 min_fee,
222 min_fee,
223 batch_sendtx_reqs,
224 )
225 tx_kbytes = tx_bytes / 1000.0
226 self.fees_per_kb.append(float(fee) / tx_kbytes)
227 for node in self.nodes:
228 node.batch(batch_sendtx_reqs)
229 self.sync_mempools(wait=0.1)
230 mined = mining_node.getblock(self.generate(mining_node, 1)[0], True)["tx"]
231 # update which txouts are confirmed
232 newmem = []
233 for utx in self.memutxo:
234 if utx["txid"] in mined:
235 self.confutxo.append(utx)
236 else:
237 newmem.append(utx)
238 self.memutxo = newmem
239 240 def initial_split(self, node):
241 """Split two coinbase UTxOs into many small coins"""
242 self.confutxo = self.wallet.send_self_transfer_multi(
243 from_node=node,
244 utxos_to_spend=[self.wallet.get_utxo() for _ in range(2)],
245 num_outputs=2048)['new_utxos']
246 while len(node.getrawmempool()) > 0:
247 self.generate(node, 1, sync_fun=self.no_op)
248 249 def sanity_check_estimates_range(self):
250 """Populate estimation buckets, assert estimates are in a sane range and
251 are strictly increasing as the target decreases."""
252 self.fees_per_kb = []
253 self.memutxo = []
254 self.log.info("Will output estimates for 1/2/3/6/15/25 blocks")
255 256 for _ in range(2):
257 self.log.info(
258 "Creating transactions and mining them with a block size that can't keep up"
259 )
260 # Create transactions and mine 10 small blocks with node 2, but create txs faster than we can mine
261 self.transact_and_mine(10, self.nodes[2])
262 check_estimates(self.nodes[1], self.fees_per_kb)
263 264 self.log.info(
265 "Creating transactions and mining them at a block size that is just big enough"
266 )
267 # Generate transactions while mining 10 more blocks, this time with node1
268 # which mines blocks with capacity just above the rate that transactions are being created
269 self.transact_and_mine(10, self.nodes[1])
270 check_estimates(self.nodes[1], self.fees_per_kb)
271 272 # Finish by mining a normal-sized block:
273 while len(self.nodes[1].getrawmempool()) > 0:
274 self.generate(self.nodes[1], 1)
275 276 self.log.info("Final estimates after emptying mempools")
277 check_estimates(self.nodes[1], self.fees_per_kb)
278 279 def test_feerate_dustrelayfee_common(self, node, multiplier, dust_mode, desc, expected_base):
280 dust_parameter = f"-dustdynamic={dust_mode}".replace('=3*', '=')
281 self.log.info(f"Test dust limit setting {dust_parameter} (fee estimation for {desc})")
282 self.restart_node(0, extra_args=[dust_parameter, '-dustrelayfee=0'])
283 assert_equal(node.getmempoolinfo()['dustdynamic'], dust_mode)
284 expected_dustrelayfee = satoshi_round(expected_base() * multiplier, rounding=ROUND_DOWN)
285 with node.busy_wait_for_debug_log([b'Updating dust feerate']):
286 mempool_info = node.getmempoolinfo()
287 assert mempool_info['dustrelayfee'] != expected_dustrelayfee
288 assert mempool_info['dustrelayfeefloor'] <= expected_dustrelayfee
289 node.mockscheduler(SECONDS_PER_HOUR)
290 mempool_info = node.getmempoolinfo()
291 assert_equal(mempool_info['dustrelayfee'], expected_dustrelayfee)
292 assert mempool_info['dustrelayfee'] > mempool_info['dustrelayfeefloor']
293 294 def test_feerate_dustrelayfee_target(self, node, multiplier, dustfee_target):
295 dust_mode = f"{multiplier}*target:{dustfee_target}"
296 self.test_feerate_dustrelayfee_common(node, multiplier, dust_mode, f'{dustfee_target} blocks', lambda: node.estimaterawfee(dustfee_target, target_success_threshold)['long']['feerate'])
297 298 def test_feerate_dustrelayfee_mempool(self, node, multiplier, dustfee_kB):
299 dust_mode = f"{multiplier}*mempool:{dustfee_kB}"
300 self.test_feerate_dustrelayfee_common(node, multiplier, dust_mode, f'{dustfee_kB} kB into mempool', lambda: get_feerate_into_mempool(node, dustfee_kB))
301 302 def test_feerate_dustrelayfee(self):
303 node = self.nodes[0]
304 305 # test dustdynamic=target:<blocks>
306 for dustfee_target in (2, 8, 1008):
307 for multiplier in (Decimal('0.5'), 1, 3, Decimal('3.3'), 10, Decimal('10.001')):
308 self.test_feerate_dustrelayfee_target(node, multiplier, dustfee_target)
309 310 # Fill mempool up
311 mempool_size = 0
312 batch_sendtx_reqs = []
313 min_fee = Decimal("0.00001")
314 while mempool_size < 52000:
315 (tx_bytes, fee) = small_txpuzzle_randfee(
316 self.wallet,
317 self.nodes[0],
318 self.confutxo,
319 self.memutxo,
320 Decimal("0.005"),
321 min_fee,
322 min_fee,
323 batch_sendtx_reqs,
324 )
325 mempool_size += tx_bytes
326 node.batch(batch_sendtx_reqs)
327 328 # test dustdynamic=mempool:<kB>
329 for dustfee_kB in (1, 10, 50):
330 for multiplier in (Decimal('0.5'), 1, 3, Decimal('3.3'), 10, Decimal('10.001')):
331 self.test_feerate_dustrelayfee_mempool(node, multiplier, dustfee_kB)
332 333 # Restore nodes to a normal state, wiping the mempool
334 self.stop_node(0)
335 (self.nodes[0].chain_path / 'mempool.dat').unlink()
336 self.start_node(0)
337 self.connect_nodes(1, 0)
338 self.connect_nodes(0, 2)
339 340 def test_estimates_with_highminrelaytxfee(self):
341 high_val = 3 * self.nodes[1].estimatesmartfee(2)["feerate"]
342 self.restart_node(1, extra_args=[f"-minrelaytxfee={high_val}", '-rest'])
343 check_smart_estimates(self.nodes[1], self.fees_per_kb)
344 self.restart_node(1)
345 346 def sanity_check_rbf_estimates(self, utxos):
347 """During 5 blocks, broadcast low fee transactions. Only 10% of them get
348 confirmed and the remaining ones get RBF'd with a high fee transaction at
349 the next block.
350 The block policy estimator should return the high feerate.
351 """
352 # The broadcaster and block producer
353 node = self.nodes[0]
354 miner = self.nodes[1]
355 # In sat/vb
356 low_feerate = 1
357 high_feerate = 10
358 # Cache the utxos of which to replace the spender after it failed to get
359 # confirmed
360 utxos_to_respend = []
361 txids_to_replace = []
362 363 assert_greater_than_or_equal(len(utxos), 250)
364 for _ in range(5):
365 # Broadcast 45 low fee transactions that will need to be RBF'd
366 txs = []
367 for _ in range(45):
368 u = utxos.pop(0)
369 tx = make_tx(self.wallet, u, low_feerate)
370 utxos_to_respend.append(u)
371 txids_to_replace.append(tx["txid"])
372 txs.append(tx)
373 # Broadcast 5 low fee transaction which don't need to
374 for _ in range(5):
375 tx = make_tx(self.wallet, utxos.pop(0), low_feerate)
376 txs.append(tx)
377 batch_send_tx = [node.sendrawtransaction.get_request(tx["hex"]) for tx in txs]
378 for n in self.nodes:
379 n.batch(batch_send_tx)
380 # Mine the transactions on another node
381 self.sync_mempools(wait=0.1, nodes=[node, miner])
382 for txid in txids_to_replace:
383 miner.prioritisetransaction(txid=txid, fee_delta=-COIN)
384 self.generate(miner, 1)
385 # RBF the low-fee transactions
386 while len(utxos_to_respend) > 0:
387 u = utxos_to_respend.pop(0)
388 tx = make_tx(self.wallet, u, high_feerate)
389 node.sendrawtransaction(tx["hex"])
390 txs.append(tx)
391 dec_txs = [res["result"] for res in node.batch([node.decoderawtransaction.get_request(tx["hex"]) for tx in txs])]
392 self.wallet.scan_txs(dec_txs)
393 394 395 # Mine the last replacement txs
396 self.sync_mempools(wait=0.1, nodes=[node, miner])
397 self.generate(miner, 1)
398 399 # Only 10% of the transactions were really confirmed with a low feerate,
400 # the rest needed to be RBF'd. We must return the 90% conf rate feerate.
401 high_feerate_kvb = Decimal(high_feerate) / COIN * 10 ** 3
402 est_feerate = node.estimatesmartfee(2)["feerate"]
403 assert_equal(est_feerate, high_feerate_kvb)
404 405 def test_old_fee_estimate_file(self):
406 # Get the initial fee rate while node is running
407 fee_rate = self.nodes[0].estimatesmartfee(1)["feerate"]
408 409 # Restart node to ensure fee_estimate.dat file is read
410 self.restart_node(0)
411 assert_equal(self.nodes[0].estimatesmartfee(1)["feerate"], fee_rate)
412 413 fee_dat = self.nodes[0].chain_path / "fee_estimates.dat"
414 415 # Stop the node and backdate the fee_estimates.dat file more than MAX_FILE_AGE
416 self.stop_node(0)
417 last_modified_time = time.time() - (MAX_FILE_AGE + 1) * SECONDS_PER_HOUR
418 os.utime(fee_dat, (last_modified_time, last_modified_time))
419 420 # Start node and ensure the fee_estimates.dat file was not read
421 self.start_node(0)
422 assert_equal(self.nodes[0].estimatesmartfee(1)["errors"], ["Insufficient data or no feerate found"])
423 424 425 def test_estimate_dat_is_flushed_periodically(self):
426 fee_dat = self.nodes[0].chain_path / "fee_estimates.dat"
427 os.remove(fee_dat) if os.path.exists(fee_dat) else None
428 429 # Verify that fee_estimates.dat does not exist
430 assert_equal(os.path.isfile(fee_dat), False)
431 432 # Verify if the string "Flushed fee estimates to fee_estimates.dat." is present in the debug log file.
433 # If present, it indicates that fee estimates have been successfully flushed to disk.
434 with self.nodes[0].assert_debug_log(expected_msgs=["Flushed fee estimates to fee_estimates.dat."], timeout=1):
435 # Mock the scheduler for an hour to flush fee estimates to fee_estimates.dat
436 self.nodes[0].mockscheduler(SECONDS_PER_HOUR)
437 438 # Verify that fee estimates were flushed and fee_estimates.dat file is created
439 assert_equal(os.path.isfile(fee_dat), True)
440 441 # Verify that the estimates remain the same if there are no blocks in the flush interval
442 block_hash_before = self.nodes[0].getbestblockhash()
443 fee_dat_initial_content = open(fee_dat, "rb").read()
444 with self.nodes[0].assert_debug_log(expected_msgs=["Flushed fee estimates to fee_estimates.dat."], timeout=1):
445 # Mock the scheduler for an hour to flush fee estimates to fee_estimates.dat
446 self.nodes[0].mockscheduler(SECONDS_PER_HOUR)
447 448 # Verify that there were no blocks in between the flush interval
449 assert_equal(block_hash_before, self.nodes[0].getbestblockhash())
450 451 fee_dat_current_content = open(fee_dat, "rb").read()
452 assert_equal(fee_dat_current_content, fee_dat_initial_content)
453 454 # Verify that the estimates remain the same after shutdown with no blocks before shutdown
455 self.restart_node(0)
456 fee_dat_current_content = open(fee_dat, "rb").read()
457 assert_equal(fee_dat_current_content, fee_dat_initial_content)
458 459 # Verify that the estimates are not the same if new blocks were produced in the flush interval
460 with self.nodes[0].assert_debug_log(expected_msgs=["Flushed fee estimates to fee_estimates.dat."], timeout=1):
461 # Mock the scheduler for an hour to flush fee estimates to fee_estimates.dat
462 self.generate(self.nodes[0], 5, sync_fun=self.no_op)
463 self.nodes[0].mockscheduler(SECONDS_PER_HOUR)
464 465 fee_dat_current_content = open(fee_dat, "rb").read()
466 assert fee_dat_current_content != fee_dat_initial_content
467 468 fee_dat_initial_content = fee_dat_current_content
469 470 # Generate blocks before shutdown and verify that the fee estimates are not the same
471 self.generate(self.nodes[0], 5, sync_fun=self.no_op)
472 self.restart_node(0)
473 fee_dat_current_content = open(fee_dat, "rb").read()
474 assert fee_dat_current_content != fee_dat_initial_content
475 476 477 def test_acceptstalefeeestimates_option(self):
478 # Get the initial fee rate while node is running
479 fee_rate = self.nodes[0].estimatesmartfee(1)["feerate"]
480 481 self.stop_node(0)
482 483 fee_dat = self.nodes[0].chain_path / "fee_estimates.dat"
484 485 # Stop the node and backdate the fee_estimates.dat file more than MAX_FILE_AGE
486 last_modified_time = time.time() - (MAX_FILE_AGE + 1) * SECONDS_PER_HOUR
487 os.utime(fee_dat, (last_modified_time, last_modified_time))
488 489 # Restart node with -acceptstalefeeestimates option to ensure fee_estimate.dat file is read
490 self.start_node(0,extra_args=["-acceptstalefeeestimates"])
491 assert_equal(self.nodes[0].estimatesmartfee(1)["feerate"], fee_rate)
492 493 def clear_estimates(self):
494 self.log.info("Restarting node with fresh estimation")
495 self.stop_node(0)
496 fee_dat = self.nodes[0].chain_path / "fee_estimates.dat"
497 os.remove(fee_dat)
498 self.start_node(0)
499 self.connect_nodes(0, 1)
500 self.connect_nodes(0, 2)
501 self.sync_blocks()
502 assert_equal(self.nodes[0].estimatesmartfee(1)["errors"], ["Insufficient data or no feerate found"])
503 504 def broadcast_and_mine(self, broadcaster, miner, feerate, count):
505 """Broadcast and mine some number of transactions with a specified fee rate."""
506 for _ in range(count):
507 self.wallet.send_self_transfer(from_node=broadcaster, fee_rate=feerate)
508 self.sync_mempools()
509 self.generate(miner, 1)
510 511 def test_estimation_modes(self):
512 low_feerate = Decimal("0.001")
513 high_feerate = Decimal("0.005")
514 tx_count = 24
515 # Broadcast and mine high fee transactions for the first 12 blocks.
516 for _ in range(12):
517 self.broadcast_and_mine(self.nodes[1], self.nodes[2], high_feerate, tx_count)
518 check_fee_estimates_btw_modes(self.nodes[0], high_feerate, high_feerate)
519 520 # We now track 12 blocks; short horizon stats will start decaying.
521 # Broadcast and mine low fee transactions for the next 4 blocks.
522 for _ in range(4):
523 self.broadcast_and_mine(self.nodes[1], self.nodes[2], low_feerate, tx_count)
524 # conservative mode will consider longer time horizons while economical mode does not
525 # Check the fee estimates for both modes after mining low fee transactions.
526 check_fee_estimates_btw_modes(self.nodes[0], high_feerate, low_feerate)
527 528 529 def run_test(self):
530 self.log.info("This test is time consuming, please be patient")
531 self.log.info("Splitting inputs so we can generate tx's")
532 533 # Split two coinbases into many small utxos
534 self.start_node(0)
535 self.wallet = MiniWallet(self.nodes[0])
536 self.initial_split(self.nodes[0])
537 self.log.info("Finished splitting")
538 539 # Now we can connect the other nodes, didn't want to connect them earlier
540 # so the estimates would not be affected by the splitting transactions
541 self.start_node(1)
542 self.start_node(2)
543 self.connect_nodes(1, 0)
544 self.connect_nodes(0, 2)
545 self.connect_nodes(2, 1)
546 self.sync_all()
547 548 self.log.info("Testing estimates with single transactions.")
549 self.sanity_check_estimates_range()
550 551 self.log.info("Test fee_estimates.dat is flushed periodically")
552 self.test_estimate_dat_is_flushed_periodically()
553 554 self.test_feerate_dustrelayfee()
555 556 # check that estimatesmartfee feerate is greater than or equal to maximum of mempoolminfee and minrelaytxfee
557 self.log.info(
558 "Test fee rate estimation after restarting node with high minrelaytxfee"
559 )
560 self.test_estimates_with_highminrelaytxfee()
561 562 self.log.info("Test acceptstalefeeestimates option")
563 self.test_acceptstalefeeestimates_option()
564 565 self.log.info("Test reading old fee_estimates.dat")
566 self.test_old_fee_estimate_file()
567 568 self.clear_estimates()
569 570 self.log.info("Testing estimates with RBF.")
571 self.sanity_check_rbf_estimates(self.confutxo + self.memutxo)
572 573 self.clear_estimates()
574 self.log.info("Test estimatesmartfee modes")
575 self.test_estimation_modes()
576 577 self.log.info("Testing that fee estimation is disabled in blocksonly.")
578 self.restart_node(0, ["-blocksonly"])
579 assert_raises_rpc_error(
580 -32603, "Fee estimation disabled", self.nodes[0].estimatesmartfee, 2
581 )
582 583 self.log.info("Bad REST requests")
584 assert rest_getfee(self.nodes[1].url, 'foobar', 2, 400).startswith(b'<MODE> must be one of <unset|economical|conservative>')
585 assert rest_getfee(self.nodes[1].url, 'conservative', -1, 400).startswith(b'Unable to parse confirmation target to int')
586 assert rest_getfee(self.nodes[1].url, 'conservative', 'abc', 400).startswith(b'Unable to parse confirmation target to int')
587 assert rest_getfee(self.nodes[1].url, 'conservative', 2**65, 400).startswith(b'Unable to parse confirmation target to int')
588 assert rest_getfee(self.nodes[1].url, 'conservative', 0, 400).startswith(b'Invalid confirmation target, must be in between ')
589 590 591 if __name__ == "__main__":
592 EstimateFeeTest(__file__).main()
593