2017-03-09 23:14:55 +01:00
#!/usr/bin/env python3
2020-04-16 19:14:08 +02:00
# Copyright (c) 2014-2020 The Bitcoin Core developers
2017-03-09 23:14:55 +01:00
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
""" Test mempool persistence.
By default , bitcoind will dump mempool on shutdown and
then reload it on startup . This can be overridden with
2017-05-05 15:17:39 +02:00
the - persistmempool = 0 command line option .
2017-03-09 23:14:55 +01:00
Test is as follows :
2017-05-05 15:17:39 +02:00
- start node0 , node1 and node2 . node1 has - persistmempool = 0
2017-03-09 23:14:55 +01: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-05 15:17:39 +02:00
This tests that with - persistmempool = 0 , the mempool is not
2017-03-09 23:14:55 +01:00
dumped to disk when the node is shut down .
2017-05-05 15:17:39 +02:00
- Restart node0 with - persistmempool = 0. Verify that its mempool is
empty . Shutdown node0 . This tests that with - persistmempool = 0 ,
2017-03-09 23:14:55 +01:00
the mempool is not loaded from disk on start up .
2017-05-05 15:17:39 +02:00
- Restart node0 with - persistmempool . Verify that it has 5
transactions in its mempool . This tests that - persistmempool = 0
2017-03-09 23:14:55 +01:00
does not overwrite a previously valid mempool stored on disk .
2017-08-21 13:23:18 +02:00
- Remove node0 mempool . dat and verify savemempool RPC recreates it
2018-03-18 15:26:45 +01:00
and verify that node1 can load it and has 5 transactions in its
2017-08-21 13:23:18 +02:00
mempool .
- Verify that savemempool throws when the RPC is called if
node1 can ' t write to disk.
2017-03-09 23:14:55 +01:00
"""
2018-07-07 00:10:35 +02:00
from decimal import Decimal
2017-08-21 13:23:18 +02:00
import os
2019-09-18 19:36:29 +02:00
import time
2017-03-09 23:14:55 +01:00
2020-07-19 09:47:05 +02:00
from test_framework . p2p import P2PTxInvStore
2020-08-17 11:45:44 +02:00
from test_framework . test_framework import BitcoinTestFramework
2019-09-18 19:36:29 +02:00
from test_framework . util import (
assert_equal ,
assert_greater_than_or_equal ,
assert_raises_rpc_error ,
2020-03-17 18:39:25 +01:00
connect_nodes ,
disconnect_nodes ,
2019-09-18 19:36:29 +02:00
)
2017-03-09 23:14:55 +01:00
2018-12-12 02:51:13 +01:00
2017-03-09 23:14:55 +01:00
class MempoolPersistTest ( BitcoinTestFramework ) :
2017-06-10 00:21:21 +02:00
def set_test_params ( self ) :
2017-03-09 23:14:55 +01:00
self . num_nodes = 3
2017-05-05 15:17:39 +02:00
self . extra_args = [ [ ] , [ " -persistmempool=0 " ] , [ ] ]
2017-03-09 23:14:55 +01:00
2018-09-09 19:32:37 +02:00
def skip_test_if_missing_module ( self ) :
self . skip_if_no_wallet ( )
2017-03-09 23:14:55 +01:00
def run_test ( self ) :
self . log . debug ( " Send 5 transactions from node2 (to its own address) " )
2019-09-18 19:36:29 +02:00
tx_creation_time_lower = int ( time . time ( ) )
2020-08-03 01:10:56 +02:00
for _ in range ( 5 ) :
2018-12-12 02:51:13 +01:00
last_txid = self . nodes [ 2 ] . sendtoaddress ( self . nodes [ 2 ] . getnewaddress ( ) , Decimal ( " 10 " ) )
2017-12-06 18:08:40 +01:00
node2_balance = self . nodes [ 2 ] . getbalance ( )
2017-03-09 23:14:55 +01:00
self . sync_all ( )
2019-09-18 19:36:29 +02:00
tx_creation_time_higher = int ( time . time ( ) )
2017-03-09 23:14:55 +01: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 )
2018-12-12 02:51:13 +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 ' ] )
2019-09-18 19:36:29 +02:00
tx_creation_time = self . nodes [ 0 ] . getmempoolentry ( txid = last_txid ) [ ' time ' ]
assert_greater_than_or_equal ( tx_creation_time , tx_creation_time_lower )
assert_greater_than_or_equal ( tx_creation_time_higher , tx_creation_time )
2020-03-17 18:39:25 +01:00
# disconnect nodes & make a txn that remains in the unbroadcast set.
2020-05-01 23:03:51 +02:00
disconnect_nodes ( self . nodes [ 0 ] , 1 )
assert ( len ( self . nodes [ 0 ] . getpeerinfo ( ) ) == 0 )
assert ( len ( self . nodes [ 0 ] . p2ps ) == 0 )
2020-03-17 18:39:25 +01:00
self . nodes [ 0 ] . sendtoaddress ( self . nodes [ 2 ] . getnewaddress ( ) , Decimal ( " 12 " ) )
connect_nodes ( self . nodes [ 0 ] , 2 )
2017-12-06 18:08:40 +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-03-24 04:56:31 +01:00
self . stop_nodes ( )
2018-03-07 00:43:50 +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-06-09 22:35:17 +02:00
self . start_node ( 0 )
2017-12-06 18:08:40 +01:00
self . start_node ( 2 )
2020-05-05 02:06:38 +02:00
assert self . nodes [ 0 ] . getmempoolinfo ( ) [ " loaded " ] # start_node is blocking on the mempool being loaded
assert self . nodes [ 2 ] . getmempoolinfo ( ) [ " loaded " ]
2020-03-17 18:39:25 +01:00
assert_equal ( len ( self . nodes [ 0 ] . getrawmempool ( ) ) , 6 )
2019-02-01 23:09:36 +01:00
assert_equal ( len ( self . nodes [ 2 ] . getrawmempool ( ) ) , 5 )
2018-01-18 16:16:34 +01:00
# The others have loaded their mempool. If node_1 loaded anything, we'd probably notice by now:
2017-03-09 23:14:55 +01:00
assert_equal ( len ( self . nodes [ 1 ] . getrawmempool ( ) ) , 0 )
2018-12-12 02:51:13 +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 ' ] )
2019-09-18 19:36:29 +02:00
self . log . debug ( ' Verify time is loaded correctly ' )
assert_equal ( tx_creation_time , self . nodes [ 0 ] . getmempoolentry ( txid = last_txid ) [ ' time ' ] )
2017-12-06 18:08:40 +01:00
# Verify accounting of mempool transactions after restart is correct
2018-01-18 16:16:34 +01:00
self . nodes [ 2 ] . syncwithvalidationinterfacequeue ( ) # Flush mempool to wallet
2017-12-06 18:08:40 +01:00
assert_equal ( node2_balance , self . nodes [ 2 ] . getbalance ( ) )
2020-03-17 18:39:25 +01:00
# start node0 with wallet disabled so wallet transactions don't get resubmitted
2017-05-05 15:17:39 +02:00
self . log . debug ( " Stop-start node0 with -persistmempool=0. Verify that it doesn ' t load its mempool.dat file. " )
2017-03-24 04:56:31 +01:00
self . stop_nodes ( )
2020-03-17 18:39:25 +01:00
self . start_node ( 0 , extra_args = [ " -persistmempool=0 " , " -disablewallet " ] )
2020-05-05 02:06:38 +02:00
assert self . nodes [ 0 ] . getmempoolinfo ( ) [ " loaded " ]
2017-03-09 23:14:55 +01: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-03-24 04:56:31 +01:00
self . stop_nodes ( )
2017-06-09 22:35:17 +02:00
self . start_node ( 0 )
2020-05-05 02:06:38 +02:00
assert self . nodes [ 0 ] . getmempoolinfo ( ) [ " loaded " ]
2020-03-17 18:39:25 +01:00
assert_equal ( len ( self . nodes [ 0 ] . getrawmempool ( ) ) , 6 )
2017-03-09 23:14:55 +01:00
2019-08-22 18:17:31 +02: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-08-21 13:23:18 +02:00
self . log . debug ( " Remove the mempool.dat file. Verify that savemempool to disk via RPC re-creates it " )
os . remove ( mempooldat0 )
self . nodes [ 0 ] . savemempool ( )
assert os . path . isfile ( mempooldat0 )
2020-03-17 18:39:25 +01:00
self . log . debug ( " Stop nodes, make node1 use mempool.dat from node0. Verify it has 6 transactions " )
2017-08-21 13:23:18 +02:00
os . rename ( mempooldat0 , mempooldat1 )
self . stop_nodes ( )
self . start_node ( 1 , extra_args = [ ] )
2020-05-05 02:06:38 +02:00
assert self . nodes [ 1 ] . getmempoolinfo ( ) [ " loaded " ]
2020-03-17 18:39:25 +01:00
assert_equal ( len ( self . nodes [ 1 ] . getrawmempool ( ) ) , 6 )
2017-08-21 13:23:18 +02:00
self . log . debug ( " Prevent bitcoind from writing mempool.dat to disk. Verify that `savemempool` fails " )
2018-04-08 18:46:12 +02:00
# to test the exception we are creating a tmp folder called mempool.dat.new
2017-08-21 13:23:18 +02:00
# which is an implementation detail that could change and break this test
mempooldotnew1 = mempooldat1 + ' .new '
2018-04-08 18:46:12 +02:00
os . mkdir ( mempooldotnew1 )
2017-07-12 16:33:46 +02:00
assert_raises_rpc_error ( - 1 , " Unable to dump mempool to disk " , self . nodes [ 1 ] . savemempool )
2018-04-08 18:46:12 +02:00
os . rmdir ( mempooldotnew1 )
2020-03-17 18:39:25 +01:00
self . test_persist_unbroadcast ( )
def test_persist_unbroadcast ( self ) :
node0 = self . nodes [ 0 ]
self . start_node ( 0 )
# clear out mempool
node0 . generate ( 1 )
2020-05-01 23:03:51 +02:00
# ensure node0 doesn't have any connections
# make a transaction that will remain in the unbroadcast set
assert ( len ( node0 . getpeerinfo ( ) ) == 0 )
assert ( len ( node0 . p2ps ) == 0 )
2020-03-17 18:39:25 +01:00
node0 . sendtoaddress ( self . nodes [ 1 ] . getnewaddress ( ) , Decimal ( " 12 " ) )
# shutdown, then startup with wallet disabled
self . stop_nodes ( )
self . start_node ( 0 , extra_args = [ " -disablewallet " ] )
# check that txn gets broadcast due to unbroadcast logic
conn = node0 . add_p2p_connection ( P2PTxInvStore ( ) )
node0 . mockscheduler ( 16 * 60 ) # 15 min + 1 for buffer
2020-08-17 17:50:47 +02:00
self . wait_until ( lambda : len ( conn . get_invs ( ) ) == 1 )
2017-08-21 13:23:18 +02:00
2017-03-09 23:14:55 +01:00
if __name__ == ' __main__ ' :
MempoolPersistTest ( ) . main ( )