aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rwxr-xr-xtest/functional/mempool_packages.py10
-rwxr-xr-xtest/functional/mempool_unbroadcast.py12
-rwxr-xr-xtest/functional/p2p_blocksonly.py23
-rwxr-xr-xtest/functional/test_framework/mininode.py10
-rwxr-xr-xtest/functional/test_framework/test_node.py13
-rwxr-xr-xtest/functional/wallet_hd.py96
6 files changed, 162 insertions, 2 deletions
diff --git a/test/functional/mempool_packages.py b/test/functional/mempool_packages.py
index a07dad18d6..5b7216b253 100755
--- a/test/functional/mempool_packages.py
+++ b/test/functional/mempool_packages.py
@@ -7,6 +7,7 @@
from decimal import Decimal
from test_framework.messages import COIN
+from test_framework.mininode import P2PTxInvStore
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
@@ -58,6 +59,7 @@ class MempoolPackagesTest(BitcoinTestFramework):
def run_test(self):
# Mine some blocks and have them mature.
+ self.nodes[0].add_p2p_connection(P2PTxInvStore()) # keep track of invs
self.nodes[0].generate(101)
utxo = self.nodes[0].listunspent(10)
txid = utxo[0]['txid']
@@ -72,6 +74,10 @@ class MempoolPackagesTest(BitcoinTestFramework):
value = sent_value
chain.append(txid)
+ # Wait until mempool transactions have passed initial broadcast (sent inv and received getdata)
+ # Otherwise, getrawmempool may be inconsistent with getmempoolentry if unbroadcast changes in between
+ self.nodes[0].p2p.wait_for_broadcast(chain)
+
# Check mempool has MAX_ANCESTORS transactions in it, and descendant and ancestor
# count and fees should look correct
mempool = self.nodes[0].getrawmempool(True)
@@ -212,6 +218,10 @@ class MempoolPackagesTest(BitcoinTestFramework):
for tx in chain[:MAX_ANCESTORS_CUSTOM]:
assert tx in mempool1
# TODO: more detailed check of node1's mempool (fees etc.)
+ # check transaction unbroadcast info (should be false if in both mempools)
+ mempool = self.nodes[0].getrawmempool(True)
+ for tx in mempool:
+ assert_equal(mempool[tx]['unbroadcast'], False)
# TODO: test ancestor size limits
diff --git a/test/functional/mempool_unbroadcast.py b/test/functional/mempool_unbroadcast.py
index a561f28b91..dedf5b8a47 100755
--- a/test/functional/mempool_unbroadcast.py
+++ b/test/functional/mempool_unbroadcast.py
@@ -53,6 +53,13 @@ class MempoolUnbroadcastTest(BitcoinTestFramework):
txFS = node.signrawtransactionwithwallet(txF["hex"])
rpc_tx_hsh = node.sendrawtransaction(txFS["hex"])
+ # check transactions are in unbroadcast using rpc
+ mempoolinfo = self.nodes[0].getmempoolinfo()
+ assert_equal(mempoolinfo['unbroadcastcount'], 2)
+ mempool = self.nodes[0].getrawmempool(True)
+ for tx in mempool:
+ assert_equal(mempool[tx]['unbroadcast'], True)
+
# check that second node doesn't have these two txns
mempool = self.nodes[1].getrawmempool()
assert rpc_tx_hsh not in mempool
@@ -71,6 +78,11 @@ class MempoolUnbroadcastTest(BitcoinTestFramework):
assert rpc_tx_hsh in mempool
assert wallet_tx_hsh in mempool
+ # check that transactions are no longer in first node's unbroadcast set
+ mempool = self.nodes[0].getrawmempool(True)
+ for tx in mempool:
+ assert_equal(mempool[tx]['unbroadcast'], False)
+
self.log.info("Add another connection & ensure transactions aren't broadcast again")
conn = node.add_p2p_connection(P2PTxInvStore())
diff --git a/test/functional/p2p_blocksonly.py b/test/functional/p2p_blocksonly.py
index 3258a38e3c..c155dda664 100755
--- a/test/functional/p2p_blocksonly.py
+++ b/test/functional/p2p_blocksonly.py
@@ -57,6 +57,29 @@ class P2PBlocksOnly(BitcoinTestFramework):
self.nodes[0].p2p.wait_for_tx(txid)
assert_equal(self.nodes[0].getmempoolinfo()['size'], 1)
+ self.log.info('Check that txs from whitelisted peers are not rejected and relayed to others')
+ self.log.info("Restarting node 0 with whitelist permission and blocksonly")
+ self.restart_node(0, ["-persistmempool=0", "-whitelist=127.0.0.1", "-whitelistforcerelay", "-blocksonly"])
+ assert_equal(self.nodes[0].getrawmempool(),[])
+ first_peer = self.nodes[0].add_p2p_connection(P2PInterface())
+ second_peer = self.nodes[0].add_p2p_connection(P2PInterface())
+ peer_1_info = self.nodes[0].getpeerinfo()[0]
+ assert_equal(peer_1_info['whitelisted'], True)
+ assert_equal(peer_1_info['permissions'], ['noban', 'forcerelay', 'relay', 'mempool'])
+ peer_2_info = self.nodes[0].getpeerinfo()[1]
+ assert_equal(peer_2_info['whitelisted'], True)
+ assert_equal(peer_2_info['permissions'], ['noban', 'forcerelay', 'relay', 'mempool'])
+ assert_equal(self.nodes[0].testmempoolaccept([sigtx])[0]['allowed'], True)
+ txid = self.nodes[0].testmempoolaccept([sigtx])[0]['txid']
+
+ self.log.info('Check that the tx from whitelisted first_peer is relayed to others (ie.second_peer)')
+ with self.nodes[0].assert_debug_log(["received getdata"]):
+ first_peer.send_message(msg_tx(FromHex(CTransaction(), sigtx)))
+ self.log.info('Check that the whitelisted peer is still connected after sending the transaction')
+ assert_equal(first_peer.is_connected, True)
+ second_peer.wait_for_tx(txid)
+ assert_equal(self.nodes[0].getmempoolinfo()['size'], 1)
+ self.log.info("Whitelisted peer's transaction is accepted and relayed")
if __name__ == '__main__':
P2PBlocksOnly().main()
diff --git a/test/functional/test_framework/mininode.py b/test/functional/test_framework/mininode.py
index bbd7350bf1..95a63717d6 100755
--- a/test/functional/test_framework/mininode.py
+++ b/test/functional/test_framework/mininode.py
@@ -645,6 +645,7 @@ class P2PTxInvStore(P2PInterface):
self.tx_invs_received = defaultdict(int)
def on_inv(self, message):
+ super().on_inv(message) # Send getdata in response.
# Store how many times invs have been received for each tx.
for i in message.inv:
if i.type == MSG_TX:
@@ -654,3 +655,12 @@ class P2PTxInvStore(P2PInterface):
def get_invs(self):
with mininode_lock:
return list(self.tx_invs_received.keys())
+
+ def wait_for_broadcast(self, txns, timeout=60):
+ """Waits for the txns (list of txids) to complete initial broadcast.
+ The mempool should mark unbroadcast=False for these transactions.
+ """
+ # Wait until invs have been received (and getdatas sent) for each txid.
+ self.wait_until(lambda: set(self.get_invs()) == set([int(tx, 16) for tx in txns]), timeout)
+ # Flush messages and wait for the getdatas to be processed
+ self.sync_with_ping()
diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index d52aff6f7e..ebc0501e11 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -219,7 +219,12 @@ class TestNode():
raise FailedToStartError(self._node_msg(
'bitcoind exited with status {} during initialization'.format(self.process.returncode)))
try:
- rpc = get_rpc_proxy(rpc_url(self.datadir, self.index, self.chain, self.rpchost), self.index, timeout=self.rpc_timeout, coveragedir=self.coverage_dir)
+ rpc = get_rpc_proxy(
+ rpc_url(self.datadir, self.index, self.chain, self.rpchost),
+ self.index,
+ timeout=self.rpc_timeout // 2, # Shorter timeout to allow for one retry in case of ETIMEDOUT
+ coveragedir=self.coverage_dir,
+ )
rpc.getblockcount()
# If the call to getblockcount() succeeds then the RPC connection is up
if self.version_is_at_least(190000):
@@ -260,7 +265,11 @@ class TestNode():
# succeeds. Try again to properly raise the FailedToStartError
pass
except OSError as e:
- if e.errno != errno.ECONNREFUSED: # Port not yet open?
+ if e.errno == errno.ETIMEDOUT:
+ pass # Treat identical to ConnectionResetError
+ elif e.errno == errno.ECONNREFUSED:
+ pass # Port not yet open?
+ else:
raise # unknown OS error
except ValueError as e: # cookie file not found and no rpcuser or rpcpassword; bitcoind is still starting
if "No RPC credentials" not in str(e):
diff --git a/test/functional/wallet_hd.py b/test/functional/wallet_hd.py
index 09f89eb59d..5b083a5398 100755
--- a/test/functional/wallet_hd.py
+++ b/test/functional/wallet_hd.py
@@ -170,5 +170,101 @@ class WalletHDTest(BitcoinTestFramework):
assert_raises_rpc_error(-5, "Already have this key", self.nodes[1].sethdseed, False, new_seed)
assert_raises_rpc_error(-5, "Already have this key", self.nodes[1].sethdseed, False, self.nodes[1].dumpprivkey(self.nodes[1].getnewaddress()))
+ self.log.info('Test sethdseed restoring with keys outside of the initial keypool')
+ self.nodes[0].generate(10)
+ # Restart node 1 with keypool of 3 and a different wallet
+ self.nodes[1].createwallet(wallet_name='origin', blank=True)
+ self.stop_node(1)
+ self.start_node(1, extra_args=['-keypool=3', '-wallet=origin'])
+ connect_nodes(self.nodes[0], 1)
+
+ # sethdseed restoring and seeing txs to addresses out of the keypool
+ origin_rpc = self.nodes[1].get_wallet_rpc('origin')
+ seed = self.nodes[0].dumpprivkey(self.nodes[0].getnewaddress())
+ origin_rpc.sethdseed(True, seed)
+
+ self.nodes[1].createwallet(wallet_name='restore', blank=True)
+ restore_rpc = self.nodes[1].get_wallet_rpc('restore')
+ restore_rpc.sethdseed(True, seed) # Set to be the same seed as origin_rpc
+ restore_rpc.sethdseed(True) # Rotate to a new seed, making original `seed` inactive
+
+ self.nodes[1].createwallet(wallet_name='restore2', blank=True)
+ restore2_rpc = self.nodes[1].get_wallet_rpc('restore2')
+ restore2_rpc.sethdseed(True, seed) # Set to be the same seed as origin_rpc
+ restore2_rpc.sethdseed(True) # Rotate to a new seed, making original `seed` inactive
+
+ # Check persistence of inactive seed by reloading restore. restore2 is still loaded to test the case where the wallet is not reloaded
+ restore_rpc.unloadwallet()
+ self.nodes[1].loadwallet('restore')
+ restore_rpc = self.nodes[1].get_wallet_rpc('restore')
+
+ # Empty origin keypool and get an address that is beyond the initial keypool
+ origin_rpc.getnewaddress()
+ origin_rpc.getnewaddress()
+ last_addr = origin_rpc.getnewaddress() # Last address of initial keypool
+ addr = origin_rpc.getnewaddress() # First address beyond initial keypool
+
+ # Check that the restored seed has last_addr but does not have addr
+ info = restore_rpc.getaddressinfo(last_addr)
+ assert_equal(info['ismine'], True)
+ info = restore_rpc.getaddressinfo(addr)
+ assert_equal(info['ismine'], False)
+ info = restore2_rpc.getaddressinfo(last_addr)
+ assert_equal(info['ismine'], True)
+ info = restore2_rpc.getaddressinfo(addr)
+ assert_equal(info['ismine'], False)
+ # Check that the origin seed has addr
+ info = origin_rpc.getaddressinfo(addr)
+ assert_equal(info['ismine'], True)
+
+ # Send a transaction to addr, which is out of the initial keypool.
+ # The wallet that has set a new seed (restore_rpc) should not detect this transaction.
+ txid = self.nodes[0].sendtoaddress(addr, 1)
+ origin_rpc.sendrawtransaction(self.nodes[0].gettransaction(txid)['hex'])
+ self.nodes[0].generate(1)
+ origin_rpc.gettransaction(txid)
+ assert_raises_rpc_error(-5, 'Invalid or non-wallet transaction id', restore_rpc.gettransaction, txid)
+ out_of_kp_txid = txid
+
+ # Send a transaction to last_addr, which is in the initial keypool.
+ # The wallet that has set a new seed (restore_rpc) should detect this transaction and generate 3 new keys from the initial seed.
+ # The previous transaction (out_of_kp_txid) should still not be detected as a rescan is required.
+ txid = self.nodes[0].sendtoaddress(last_addr, 1)
+ origin_rpc.sendrawtransaction(self.nodes[0].gettransaction(txid)['hex'])
+ self.nodes[0].generate(1)
+ origin_rpc.gettransaction(txid)
+ restore_rpc.gettransaction(txid)
+ assert_raises_rpc_error(-5, 'Invalid or non-wallet transaction id', restore_rpc.gettransaction, out_of_kp_txid)
+ restore2_rpc.gettransaction(txid)
+ assert_raises_rpc_error(-5, 'Invalid or non-wallet transaction id', restore2_rpc.gettransaction, out_of_kp_txid)
+
+ # After rescanning, restore_rpc should now see out_of_kp_txid and generate an additional key.
+ # addr should now be part of restore_rpc and be ismine
+ restore_rpc.rescanblockchain()
+ restore_rpc.gettransaction(out_of_kp_txid)
+ info = restore_rpc.getaddressinfo(addr)
+ assert_equal(info['ismine'], True)
+ restore2_rpc.rescanblockchain()
+ restore2_rpc.gettransaction(out_of_kp_txid)
+ info = restore2_rpc.getaddressinfo(addr)
+ assert_equal(info['ismine'], True)
+
+ # Check again that 3 keys were derived.
+ # Empty keypool and get an address that is beyond the initial keypool
+ origin_rpc.getnewaddress()
+ origin_rpc.getnewaddress()
+ last_addr = origin_rpc.getnewaddress()
+ addr = origin_rpc.getnewaddress()
+
+ # Check that the restored seed has last_addr but does not have addr
+ info = restore_rpc.getaddressinfo(last_addr)
+ assert_equal(info['ismine'], True)
+ info = restore_rpc.getaddressinfo(addr)
+ assert_equal(info['ismine'], False)
+ info = restore2_rpc.getaddressinfo(last_addr)
+ assert_equal(info['ismine'], True)
+ info = restore2_rpc.getaddressinfo(addr)
+ assert_equal(info['ismine'], False)
+
if __name__ == '__main__':
WalletHDTest().main ()