2017-05-03 09:23:39 +02:00
#!/usr/bin/env python3
2023-08-16 19:27:31 +02:00
# Copyright (c) 2014-2020 The Bitcoin Core developers
2017-05-03 09:23:39 +02:00
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
""" Test mempool persistence.
2020-06-11 10:39:04 +02:00
By default , dashd will dump mempool on shutdown and
2017-05-03 09:23:39 +02:00
then reload it on startup . This can be overridden with
2017-05-08 20:13:47 +02:00
the - persistmempool = 0 command line option .
2017-05-03 09:23:39 +02:00
Test is as follows :
2017-05-08 20:13:47 +02:00
- start node0 , node1 and node2 . node1 has - persistmempool = 0
2017-05-03 09:23:39 +02:00
- create 5 transactions on node2 to its own address . Note that these
are not sent to node0 or node1 addresses because we don ' t want
them to be saved in the wallet .
- check that node0 and node1 have 5 transactions in their mempools
- shutdown all nodes .
- startup node0 . Verify that it still has 5 transactions
in its mempool . Shutdown node0 . This tests that by default the
mempool is persistent .
- startup node1 . Verify that its mempool is empty . Shutdown node1 .
2017-05-08 20:13:47 +02:00
This tests that with - persistmempool = 0 , the mempool is not
2017-05-03 09:23:39 +02:00
dumped to disk when the node is shut down .
2017-05-08 20:13:47 +02:00
- Restart node0 with - persistmempool = 0. Verify that its mempool is
empty . Shutdown node0 . This tests that with - persistmempool = 0 ,
2017-05-03 09:23:39 +02:00
the mempool is not loaded from disk on start up .
2017-05-08 20:13:47 +02:00
- Restart node0 with - persistmempool . Verify that it has 5
transactions in its mempool . This tests that - persistmempool = 0
2017-05-03 09:23:39 +02:00
does not overwrite a previously valid mempool stored on disk .
2017-09-06 22:48:00 +02:00
- Remove node0 mempool . dat and verify savemempool RPC recreates it
2018-03-21 16:16:28 +01:00
and verify that node1 can load it and has 5 transactions in its
2017-09-06 22:48:00 +02:00
mempool .
- Verify that savemempool throws when the RPC is called if
node1 can ' t write to disk.
2017-05-03 09:23:39 +02:00
"""
Merge #13054: tests: Enable automatic detection of undefined names in Python tests scripts. Remove wildcard imports.
68400d8b96 tests: Use explicit imports (practicalswift)
Pull request description:
Enable automatic detection of undefined names in Python tests scripts. Remove wildcard imports.
Wildcard imports make it unclear which names are present in the namespace, confusing both readers and many automated tools.
An additional benefit of not using wildcard imports in tests scripts is that readers of a test script then can infer the rough testing scope just by looking at the imports.
Before this commit:
```
$ contrib/devtools/lint-python.sh | head -10
./test/functional/feature_rbf.py:8:1: F403 'from test_framework.util import *' used; unable to detect undefined names
./test/functional/feature_rbf.py:9:1: F403 'from test_framework.script import *' used; unable to detect undefined names
./test/functional/feature_rbf.py:10:1: F403 'from test_framework.mininode import *' used; unable to detect undefined names
./test/functional/feature_rbf.py:15:12: F405 bytes_to_hex_str may be undefined, or defined from star imports: test_framework.mininode, test_framework.script, test_framework.util
./test/functional/feature_rbf.py:17:58: F405 CScript may be undefined, or defined from star imports: test_framework.mininode, test_framework.script, test_framework.util
./test/functional/feature_rbf.py:25:13: F405 COIN may be undefined, or defined from star imports: test_framework.mininode, test_framework.script, test_framework.util
./test/functional/feature_rbf.py:26:31: F405 satoshi_round may be undefined, or defined from star imports: test_framework.mininode, test_framework.script, test_framework.util
./test/functional/feature_rbf.py:26:60: F405 COIN may be undefined, or defined from star imports: test_framework.mininode, test_framework.script, test_framework.util
./test/functional/feature_rbf.py:30:41: F405 satoshi_round may be undefined, or defined from star imports: test_framework.mininode, test_framework.script, test_framework.util
./test/functional/feature_rbf.py:30:68: F405 COIN may be undefined, or defined from star imports: test_framework.mininode, test_framework.script, test_framework.util
$
```
After this commit:
```
$ contrib/devtools/lint-python.sh | head -10
$
```
Tree-SHA512: 3f826d39cffb6438388e5efcb20a9622ff8238247e882d68f7b38609877421b2a8e10e9229575f8eb6a8fa42dec4256986692e92922c86171f750a0e887438d9
2018-08-13 14:24:43 +02:00
from decimal import Decimal
2017-09-06 22:48:00 +02:00
import os
2017-05-03 09:23:39 +02:00
2024-01-15 20:35:29 +01:00
# from test_framework.p2p import P2PTxInvStore
2017-05-03 09:23:39 +02:00
from test_framework . test_framework import BitcoinTestFramework
2019-10-02 16:55:03 +02:00
from test_framework . util import (
assert_equal ,
2022-09-24 14:36:35 +02:00
assert_greater_than_or_equal , assert_raises_rpc_error ,
2019-10-02 16:55:03 +02:00
)
2021-09-20 15:22:15 +02:00
from test_framework . wallet import MiniWallet
2017-05-03 09:23:39 +02:00
2018-12-14 20:16:02 +01:00
2017-05-03 09:23:39 +02:00
class MempoolPersistTest ( BitcoinTestFramework ) :
2017-09-01 18:47:13 +02:00
def set_test_params ( self ) :
2017-05-03 09:23:39 +02:00
self . num_nodes = 3
2017-05-08 20:13:47 +02:00
self . extra_args = [ [ ] , [ " -persistmempool=0 " ] , [ ] ]
2017-05-03 09:23:39 +02:00
def run_test ( self ) :
2021-09-20 15:22:15 +02:00
self . mini_wallet = MiniWallet ( self . nodes [ 2 ] )
self . mini_wallet . rescan_utxos ( )
if self . is_sqlite_compiled ( ) :
self . nodes [ 2 ] . createwallet (
wallet_name = " watch " ,
descriptors = True ,
disable_private_keys = True ,
load_on_startup = False ,
)
wallet_watch = self . nodes [ 2 ] . get_wallet_rpc ( " watch " )
assert_equal ( [ { ' success ' : True } ] , wallet_watch . importdescriptors ( [ { ' desc ' : self . mini_wallet . get_descriptor ( ) , ' timestamp ' : 0 } ] ) )
2017-05-03 09:23:39 +02:00
self . log . debug ( " Send 5 transactions from node2 (to its own address) " )
2019-10-02 16:55:03 +02:00
tx_creation_time_lower = self . mocktime
2020-08-11 02:50:34 +02:00
for _ in range ( 5 ) :
2021-09-20 15:22:15 +02:00
last_txid = self . mini_wallet . send_self_transfer ( from_node = self . nodes [ 2 ] ) [ " txid " ]
if self . is_sqlite_compiled ( ) :
self . nodes [ 2 ] . syncwithvalidationinterfacequeue ( ) # Flush mempool to wallet
node2_balance = wallet_watch . getbalance ( )
2017-05-03 09:23:39 +02:00
self . sync_all ( )
2019-10-02 16:55:03 +02:00
tx_creation_time_higher = self . mocktime
2017-05-03 09:23:39 +02:00
self . log . debug ( " Verify that node0 and node1 have 5 transactions in their mempools " )
assert_equal ( len ( self . nodes [ 0 ] . getrawmempool ( ) ) , 5 )
assert_equal ( len ( self . nodes [ 1 ] . getrawmempool ( ) ) , 5 )
2021-02-08 20:36:33 +01:00
total_fee_old = self . nodes [ 0 ] . getmempoolinfo ( ) [ ' total_fee ' ]
2018-12-14 20:16:02 +01:00
self . log . debug ( " Prioritize a transaction on node0 " )
fees = self . nodes [ 0 ] . getmempoolentry ( txid = last_txid ) [ ' fees ' ]
assert_equal ( fees [ ' base ' ] , fees [ ' modified ' ] )
self . nodes [ 0 ] . prioritisetransaction ( txid = last_txid , fee_delta = 1000 )
fees = self . nodes [ 0 ] . getmempoolentry ( txid = last_txid ) [ ' fees ' ]
assert_equal ( fees [ ' base ' ] + Decimal ( ' 0.00001000 ' ) , fees [ ' modified ' ] )
2021-02-08 20:36:33 +01:00
self . log . info ( ' Check the total base fee is unchanged after prioritisetransaction ' )
assert_equal ( total_fee_old , self . nodes [ 0 ] . getmempoolinfo ( ) [ ' total_fee ' ] )
assert_equal ( total_fee_old , sum ( v [ ' fees ' ] [ ' base ' ] for k , v in self . nodes [ 0 ] . getrawmempool ( verbose = True ) . items ( ) ) )
2021-09-20 15:22:15 +02:00
last_entry = self . nodes [ 0 ] . getmempoolentry ( txid = last_txid )
tx_creation_time = last_entry [ ' time ' ]
2019-10-02 16:55:03 +02:00
assert_greater_than_or_equal ( tx_creation_time , tx_creation_time_lower )
assert_greater_than_or_equal ( tx_creation_time_higher , tx_creation_time )
2022-05-06 06:04:50 +02:00
# disconnect nodes & make a txn that remains in the unbroadcast set.
2020-05-30 18:22:03 +02:00
self . disconnect_nodes ( 0 , 1 )
2021-09-20 15:22:15 +02:00
assert_equal ( len ( self . nodes [ 0 ] . getpeerinfo ( ) ) , 0 )
assert_equal ( len ( self . nodes [ 0 ] . p2ps ) , 0 )
self . mini_wallet . send_self_transfer ( from_node = self . nodes [ 0 ] )
2022-05-06 06:04:50 +02:00
2017-12-11 16:12:06 +01:00
self . log . debug ( " Stop-start the nodes. Verify that node0 has the transactions in its mempool and node1 does not. Verify that node2 calculates its balance correctly after loading wallet transactions. " )
2017-06-02 12:08:48 +02:00
self . stop_nodes ( )
2018-03-07 16:29:57 +01:00
# Give this node a head-start, so we can be "extra-sure" that it didn't load anything later
# Also don't store the mempool, to keep the datadir clean
self . start_node ( 1 , extra_args = [ " -persistmempool=0 " ] )
2017-09-01 18:47:13 +02:00
self . start_node ( 0 )
2017-12-11 16:12:06 +01:00
self . start_node ( 2 )
2022-09-07 20:48:44 +02:00
assert self . nodes [ 0 ] . getmempoolinfo ( ) [ " loaded " ] # start_node is blocking on the mempool being loaded
assert self . nodes [ 2 ] . getmempoolinfo ( ) [ " loaded " ]
2022-05-06 06:04:50 +02:00
assert_equal ( len ( self . nodes [ 0 ] . getrawmempool ( ) ) , 6 )
2019-05-01 16:06:11 +02:00
assert_equal ( len ( self . nodes [ 2 ] . getrawmempool ( ) ) , 5 )
2018-01-29 10:35:53 +01:00
# The others have loaded their mempool. If node_1 loaded anything, we'd probably notice by now:
2017-05-03 09:23:39 +02:00
assert_equal ( len ( self . nodes [ 1 ] . getrawmempool ( ) ) , 0 )
2018-12-14 20:16:02 +01:00
self . log . debug ( ' Verify prioritization is loaded correctly ' )
fees = self . nodes [ 0 ] . getmempoolentry ( txid = last_txid ) [ ' fees ' ]
assert_equal ( fees [ ' base ' ] + Decimal ( ' 0.00001000 ' ) , fees [ ' modified ' ] )
2021-09-20 15:22:15 +02:00
self . log . debug ( ' Verify all fields are loaded correctly ' )
assert_equal ( last_entry , self . nodes [ 0 ] . getmempoolentry ( txid = last_txid ) )
2019-10-02 16:55:03 +02:00
2017-12-11 16:12:06 +01:00
# Verify accounting of mempool transactions after restart is correct
2021-09-20 15:22:15 +02:00
if self . is_sqlite_compiled ( ) :
self . nodes [ 2 ] . loadwallet ( " watch " )
wallet_watch = self . nodes [ 2 ] . get_wallet_rpc ( " watch " )
self . nodes [ 2 ] . syncwithvalidationinterfacequeue ( ) # Flush mempool to wallet
assert_equal ( node2_balance , wallet_watch . getbalance ( ) )
2017-12-11 16:12:06 +01:00
2017-05-08 20:13:47 +02:00
self . log . debug ( " Stop-start node0 with -persistmempool=0. Verify that it doesn ' t load its mempool.dat file. " )
2017-06-02 12:08:48 +02:00
self . stop_nodes ( )
2021-09-20 15:22:15 +02:00
self . start_node ( 0 , extra_args = [ " -persistmempool=0 " ] )
2022-09-07 20:48:44 +02:00
assert self . nodes [ 0 ] . getmempoolinfo ( ) [ " loaded " ]
2017-05-03 09:23:39 +02:00
assert_equal ( len ( self . nodes [ 0 ] . getrawmempool ( ) ) , 0 )
self . log . debug ( " Stop-start node0. Verify that it has the transactions in its mempool. " )
2017-06-02 12:08:48 +02:00
self . stop_nodes ( )
2017-09-01 18:47:13 +02:00
self . start_node ( 0 )
2022-09-07 20:48:44 +02:00
assert self . nodes [ 0 ] . getmempoolinfo ( ) [ " loaded " ]
2022-05-06 06:04:50 +02:00
assert_equal ( len ( self . nodes [ 0 ] . getrawmempool ( ) ) , 6 )
2017-05-03 09:23:39 +02:00
2021-01-22 15:58:07 +01:00
mempooldat0 = os . path . join ( self . nodes [ 0 ] . datadir , self . chain , ' mempool.dat ' )
mempooldat1 = os . path . join ( self . nodes [ 1 ] . datadir , self . chain , ' mempool.dat ' )
2017-09-06 22:48:00 +02:00
self . log . debug ( " Remove the mempool.dat file. Verify that savemempool to disk via RPC re-creates it " )
os . remove ( mempooldat0 )
2021-10-30 20:18:05 +02:00
result0 = self . nodes [ 0 ] . savemempool ( )
2017-09-06 22:48:00 +02:00
assert os . path . isfile ( mempooldat0 )
2021-10-30 20:18:05 +02:00
assert_equal ( result0 [ ' filename ' ] , mempooldat0 )
2017-09-06 22:48:00 +02:00
2022-05-06 06:04:50 +02:00
self . log . debug ( " Stop nodes, make node1 use mempool.dat from node0. Verify it has 6 transactions " )
2017-09-06 22:48:00 +02:00
os . rename ( mempooldat0 , mempooldat1 )
self . stop_nodes ( )
self . start_node ( 1 , extra_args = [ ] )
2022-09-07 20:48:44 +02:00
assert self . nodes [ 1 ] . getmempoolinfo ( ) [ " loaded " ]
2022-05-06 06:04:50 +02:00
assert_equal ( len ( self . nodes [ 1 ] . getrawmempool ( ) ) , 6 )
2017-09-06 22:48:00 +02:00
2020-06-11 10:39:04 +02:00
self . log . debug ( " Prevent dashd from writing mempool.dat to disk. Verify that `savemempool` fails " )
2018-04-09 20:37:01 +02:00
# to test the exception we are creating a tmp folder called mempool.dat.new
2017-09-06 22:48:00 +02:00
# which is an implementation detail that could change and break this test
mempooldotnew1 = mempooldat1 + ' .new '
2018-04-09 20:37:01 +02:00
os . mkdir ( mempooldotnew1 )
2017-09-06 22:48:00 +02:00
assert_raises_rpc_error ( - 1 , " Unable to dump mempool to disk " , self . nodes [ 1 ] . savemempool )
2018-04-09 20:37:01 +02:00
os . rmdir ( mempooldotnew1 )
2017-09-06 22:48:00 +02:00
2022-05-06 06:04:50 +02:00
self . test_persist_unbroadcast ( )
def test_persist_unbroadcast ( self ) :
node0 = self . nodes [ 0 ]
self . start_node ( 0 )
# clear out mempool
2024-09-26 21:17:04 +02:00
self . generate ( node0 , 1 , sync_fun = self . no_op )
2022-05-06 06:04:50 +02:00
2020-05-30 18:22:03 +02:00
# ensure node0 doesn't have any connections
# make a transaction that will remain in the unbroadcast set
2021-09-20 15:22:15 +02:00
assert_equal ( len ( node0 . getpeerinfo ( ) ) , 0 )
assert_equal ( len ( node0 . p2ps ) , 0 )
self . mini_wallet . send_self_transfer ( from_node = node0 )
2022-05-06 06:04:50 +02:00
# shutdown, then startup with wallet disabled
2021-09-20 15:22:15 +02:00
self . restart_node ( 0 , extra_args = [ " -disablewallet " ] )
2022-05-06 06:04:50 +02:00
# check that txn gets broadcast due to unbroadcast logic
# conn = node0.add_p2p_connection(P2PTxInvStore())
2021-09-20 15:22:15 +02:00
# node0.mockscheduler(16 * 60) # 15 min + 1 for buffer
2020-08-27 08:21:53 +02:00
# self.wait_until(lambda: len(conn.get_invs()) == 1)
2018-04-09 20:37:01 +02:00
2021-09-20 15:22:15 +02:00
if __name__ == " __main__ " :
2017-05-03 09:23:39 +02:00
MempoolPersistTest ( ) . main ( )