|
24 | 24 |
|
25 | 25 | MAX_FILE_AGE = 60
|
26 | 26 | SECONDS_PER_HOUR = 60 * 60
|
| 27 | +target_success_threshold = 0.8 |
27 | 28 |
|
28 | 29 | def small_txpuzzle_randfee(
|
29 | 30 | wallet, from_node, conflist, unconflist, amount, min_fee, fee_increment, batch_reqs
|
@@ -137,6 +138,19 @@ def check_fee_estimates_btw_modes(node, expected_conservative, expected_economic
|
137 | 138 | assert_equal(fee_est_default, expected_economical)
|
138 | 139 |
|
139 | 140 |
|
| 141 | +def get_feerate_into_mempool(node, kB): |
| 142 | + mempool_entries = list(node.getrawmempool(verbose=True).values()) |
| 143 | + for entry in mempool_entries: |
| 144 | + entry['feerate_BTC/vB'] = entry['fees']['modified'] / entry['vsize'] |
| 145 | + mempool_entries.sort(key=lambda entry: entry['feerate_BTC/vB'], reverse=True) |
| 146 | + bytes_remaining = kB * 1000 |
| 147 | + for entry in mempool_entries: |
| 148 | + bytes_remaining -= entry['vsize'] |
| 149 | + if bytes_remaining <= 0: |
| 150 | + return satoshi_round(entry['feerate_BTC/vB'] * 1000) |
| 151 | + raise AssertionError('Entire mempool is smaller than %s kB' % (kB,)) |
| 152 | + |
| 153 | + |
140 | 154 | class EstimateFeeTest(BitcoinTestFramework):
|
141 | 155 | def set_test_params(self):
|
142 | 156 | self.num_nodes = 3
|
@@ -237,6 +251,62 @@ def sanity_check_estimates_range(self):
|
237 | 251 | self.log.info("Final estimates after emptying mempools")
|
238 | 252 | check_estimates(self.nodes[1], self.fees_per_kb)
|
239 | 253 |
|
| 254 | + def test_feerate_dustrelayfee(self): |
| 255 | + node = self.nodes[0] |
| 256 | + |
| 257 | + # test dustdynamic=target:<blocks> |
| 258 | + for dustfee_target in (2, 8, 1008): |
| 259 | + dust_mode = f"target:{dustfee_target}" |
| 260 | + dust_parameter = f"-dustdynamic={dust_mode}" |
| 261 | + self.log.info(f"Test dust limit setting {dust_parameter} (fee estimation for {dustfee_target} blocks)") |
| 262 | + self.restart_node(0, extra_args=[dust_parameter]) |
| 263 | + assert_equal(node.getmempoolinfo()['dustdynamic'], dust_mode) |
| 264 | + with node.busy_wait_for_debug_log([b'Updating dust feerate']): |
| 265 | + node.mockscheduler(SECONDS_PER_HOUR) |
| 266 | + fee_est = node.estimaterawfee(dustfee_target, target_success_threshold)['long']['feerate'] |
| 267 | + mempool_info = node.getmempoolinfo() |
| 268 | + assert_equal(mempool_info['dustrelayfee'], fee_est) |
| 269 | + assert mempool_info['dustrelayfee'] > mempool_info['dustrelayfeefloor'] |
| 270 | + |
| 271 | + # Fill mempool up |
| 272 | + mempool_size = 0 |
| 273 | + batch_sendtx_reqs = [] |
| 274 | + min_fee = Decimal("0.00001") |
| 275 | + while mempool_size < 52000: |
| 276 | + (tx_bytes, fee) = small_txpuzzle_randfee( |
| 277 | + self.wallet, |
| 278 | + self.nodes[0], |
| 279 | + self.confutxo, |
| 280 | + self.memutxo, |
| 281 | + Decimal("0.005"), |
| 282 | + min_fee, |
| 283 | + min_fee, |
| 284 | + batch_sendtx_reqs, |
| 285 | + ) |
| 286 | + mempool_size += tx_bytes |
| 287 | + node.batch(batch_sendtx_reqs) |
| 288 | + |
| 289 | + # test dustdynamic=mempool:<kB> |
| 290 | + for dustfee_kB in (1, 10, 50): |
| 291 | + dust_mode = f"mempool:{dustfee_kB}" |
| 292 | + dust_parameter = f"-dustdynamic={dust_mode}" |
| 293 | + self.log.info(f"Test dust limit setting {dust_parameter} (fee estimation for {dustfee_kB} kB into mempool)") |
| 294 | + self.restart_node(0, extra_args=[dust_parameter]) |
| 295 | + assert_equal(node.getmempoolinfo()['dustdynamic'], dust_mode) |
| 296 | + with node.busy_wait_for_debug_log([b'Updating dust feerate']): |
| 297 | + node.mockscheduler(SECONDS_PER_HOUR) |
| 298 | + feerate_into_mempool = get_feerate_into_mempool(node, dustfee_kB) |
| 299 | + mempool_info = node.getmempoolinfo() |
| 300 | + assert_equal(mempool_info['dustrelayfee'], feerate_into_mempool) |
| 301 | + assert mempool_info['dustrelayfee'] > mempool_info['dustrelayfeefloor'] |
| 302 | + |
| 303 | + # Restore nodes to a normal state, wiping the mempool |
| 304 | + self.stop_node(0) |
| 305 | + (self.nodes[0].chain_path / 'mempool.dat').unlink() |
| 306 | + self.start_node(0) |
| 307 | + self.connect_nodes(1, 0) |
| 308 | + self.connect_nodes(0, 2) |
| 309 | + |
240 | 310 | def test_feerate_mempoolminfee(self):
|
241 | 311 | high_val = 3 * self.nodes[1].estimatesmartfee(1)["feerate"]
|
242 | 312 | self.restart_node(1, extra_args=[f"-minrelaytxfee={high_val}"])
|
@@ -450,6 +520,8 @@ def run_test(self):
|
450 | 520 | self.log.info("Test fee_estimates.dat is flushed periodically")
|
451 | 521 | self.test_estimate_dat_is_flushed_periodically()
|
452 | 522 |
|
| 523 | + self.test_feerate_dustrelayfee() |
| 524 | + |
453 | 525 | # check that the effective feerate is greater than or equal to the mempoolminfee even for high mempoolminfee
|
454 | 526 | self.log.info(
|
455 | 527 | "Test fee rate estimation after restarting node with high MempoolMinFee"
|
|
0 commit comments