aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rwxr-xr-xtest/functional/feature_backwards_compatibility.py47
-rwxr-xr-xtest/functional/feature_loadblock.py8
-rwxr-xr-xtest/functional/feature_notifications.py68
-rwxr-xr-xtest/functional/feature_reindex.py6
-rwxr-xr-xtest/functional/mempool_persist.py10
-rwxr-xr-xtest/functional/p2p_blockfilters.py134
-rwxr-xr-xtest/functional/p2p_getdata.py51
-rwxr-xr-xtest/functional/test_framework/messages.py49
-rwxr-xr-xtest/functional/test_framework/mininode.py3
-rwxr-xr-xtest/functional/test_framework/test_node.py28
-rw-r--r--test/functional/test_framework/util.py6
-rwxr-xr-xtest/functional/test_runner.py2
-rwxr-xr-xtest/functional/wallet_abandonconflict.py7
-rwxr-xr-xtest/functional/wallet_basic.py9
-rwxr-xr-xtest/functional/wallet_upgradewallet.py6
-rwxr-xr-xtest/fuzz/test_runner.py1
-rwxr-xr-xtest/lint/lint-shell.sh6
17 files changed, 397 insertions, 44 deletions
diff --git a/test/functional/feature_backwards_compatibility.py b/test/functional/feature_backwards_compatibility.py
index 9cff79a42c..596ff206f2 100755
--- a/test/functional/feature_backwards_compatibility.py
+++ b/test/functional/feature_backwards_compatibility.py
@@ -6,7 +6,11 @@
Test various backwards compatibility scenarios. Download the previous node binaries:
-contrib/devtools/previous_release.sh -b v0.19.0.1 v0.18.1 v0.17.1
+contrib/devtools/previous_release.sh -b v0.19.1 v0.18.1 v0.17.1 v0.16.3 v0.15.2
+
+v0.15.2 is not required by this test, but it is used in wallet_upgradewallet.py.
+Due to a hardfork in regtest, it can't be used to sync nodes.
+
Due to RPC changes introduced in various versions the below tests
won't work for older versions without some patches or workarounds.
@@ -22,6 +26,7 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.descriptors import descsum_create
from test_framework.util import (
+ adjust_bitcoin_conf_for_pre_17,
assert_equal,
sync_blocks,
sync_mempools,
@@ -31,14 +36,15 @@ from test_framework.util import (
class BackwardsCompatibilityTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
- self.num_nodes = 5
+ self.num_nodes = 6
# Add new version after each release:
self.extra_args = [
["-addresstype=bech32"], # Pre-release: use to mine blocks
["-nowallet", "-walletrbf=1", "-addresstype=bech32"], # Pre-release: use to receive coins, swap wallets, etc
- ["-nowallet", "-walletrbf=1", "-addresstype=bech32"], # v0.19.0.1
+ ["-nowallet", "-walletrbf=1", "-addresstype=bech32"], # v0.19.1
["-nowallet", "-walletrbf=1", "-addresstype=bech32"], # v0.18.1
- ["-nowallet", "-walletrbf=1", "-addresstype=bech32"] # v0.17.1
+ ["-nowallet", "-walletrbf=1", "-addresstype=bech32"], # v0.17.1
+ ["-nowallet", "-walletrbf=1", "-addresstype=bech32"], # v0.16.3
]
def skip_test_if_missing_module(self):
@@ -49,10 +55,13 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
self.add_nodes(self.num_nodes, extra_args=self.extra_args, versions=[
None,
None,
- 190001,
+ 190100,
180100,
170100,
+ 160300,
])
+ # adapt bitcoin.conf, because older bitcoind's don't recognize config sections
+ adjust_bitcoin_conf_for_pre_17(self.nodes[5].bitcoinconf)
self.start_nodes()
@@ -65,10 +74,11 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
res = self.nodes[self.num_nodes - 1].getblockchaininfo()
assert_equal(res['blocks'], 101)
- node_master = self.nodes[self.num_nodes - 4]
- node_v19 = self.nodes[self.num_nodes - 3]
- node_v18 = self.nodes[self.num_nodes - 2]
- node_v17 = self.nodes[self.num_nodes - 1]
+ node_master = self.nodes[self.num_nodes - 5]
+ node_v19 = self.nodes[self.num_nodes - 4]
+ node_v18 = self.nodes[self.num_nodes - 3]
+ node_v17 = self.nodes[self.num_nodes - 2]
+ node_v16 = self.nodes[self.num_nodes - 1]
self.log.info("Test wallet backwards compatibility...")
# Create a number of wallets and open them in older versions:
@@ -167,6 +177,7 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
node_v19_wallets_dir = os.path.join(node_v19.datadir, "regtest/wallets")
node_v18_wallets_dir = os.path.join(node_v18.datadir, "regtest/wallets")
node_v17_wallets_dir = os.path.join(node_v17.datadir, "regtest/wallets")
+ node_v16_wallets_dir = os.path.join(node_v16.datadir, "regtest")
node_master.unloadwallet("w1")
node_master.unloadwallet("w2")
node_v19.unloadwallet("w1_v19")
@@ -174,6 +185,13 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
node_v18.unloadwallet("w1_v18")
node_v18.unloadwallet("w2_v18")
+ # Copy wallets to v0.16
+ for wallet in os.listdir(node_master_wallets_dir):
+ shutil.copytree(
+ os.path.join(node_master_wallets_dir, wallet),
+ os.path.join(node_v16_wallets_dir, wallet)
+ )
+
# Copy wallets to v0.17
for wallet in os.listdir(node_master_wallets_dir):
shutil.copytree(
@@ -292,10 +310,17 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
# assert_raises_rpc_error(-4, "Wallet loading failed.", node_v17.loadwallet, 'w3_v18')
# Instead, we stop node and try to launch it with the wallet:
- self.stop_node(self.num_nodes - 1)
+ self.stop_node(4)
node_v17.assert_start_raises_init_error(["-wallet=w3_v18"], "Error: Error loading w3_v18: Wallet requires newer version of Bitcoin Core")
node_v17.assert_start_raises_init_error(["-wallet=w3"], "Error: Error loading w3: Wallet requires newer version of Bitcoin Core")
- self.start_node(self.num_nodes - 1)
+ self.start_node(4)
+
+ # Open most recent wallet in v0.16 (no loadwallet RPC)
+ self.stop_node(5)
+ self.start_node(5, extra_args=["-wallet=w2"])
+ wallet = node_v16.get_wallet_rpc("w2")
+ info = wallet.getwalletinfo()
+ assert info['keypoolsize'] == 1
self.log.info("Test wallet upgrade path...")
# u1: regular wallet, created with v0.17
diff --git a/test/functional/feature_loadblock.py b/test/functional/feature_loadblock.py
index 1138b0f0ea..82f1331685 100755
--- a/test/functional/feature_loadblock.py
+++ b/test/functional/feature_loadblock.py
@@ -16,10 +16,8 @@ import sys
import tempfile
import urllib
-from test_framework.test_framework import (
- BitcoinTestFramework,
-)
-from test_framework.util import assert_equal, wait_until
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import assert_equal
class LoadblockTest(BitcoinTestFramework):
@@ -75,7 +73,7 @@ class LoadblockTest(BitcoinTestFramework):
self.log.info("Restart second, unsynced node with bootstrap file")
self.stop_node(1)
self.start_node(1, ["-loadblock=" + bootstrap_file])
- wait_until(lambda: self.nodes[1].getblockcount() == 100)
+ assert_equal(self.nodes[1].getblockcount(), 100) # start_node is blocking on all block files being imported
assert_equal(self.nodes[1].getblockchaininfo()['blocks'], 100)
assert_equal(self.nodes[0].getbestblockhash(), self.nodes[1].getbestblockhash())
diff --git a/test/functional/feature_notifications.py b/test/functional/feature_notifications.py
index b110a559c0..47200b6cc6 100755
--- a/test/functional/feature_notifications.py
+++ b/test/functional/feature_notifications.py
@@ -5,12 +5,14 @@
"""Test the -alertnotify, -blocknotify and -walletnotify options."""
import os
-from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE
+from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE, keyhash_to_p2pkh
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
wait_until,
connect_nodes,
+ disconnect_nodes,
+ hex_str_to_bytes,
)
# Linux allow all characters other than \x00
@@ -81,8 +83,72 @@ class NotificationsTest(BitcoinTestFramework):
# directory content should equal the generated transaction hashes
txids_rpc = list(map(lambda t: notify_outputname(self.wallet, t['txid']), self.nodes[1].listtransactions("*", block_count)))
assert_equal(sorted(txids_rpc), sorted(os.listdir(self.walletnotify_dir)))
+ for tx_file in os.listdir(self.walletnotify_dir):
+ os.remove(os.path.join(self.walletnotify_dir, tx_file))
+
+ # Conflicting transactions tests. Give node 0 same wallet seed as
+ # node 1, generate spends from node 0, and check notifications
+ # triggered by node 1
+ self.log.info("test -walletnotify with conflicting transactions")
+ self.nodes[0].sethdseed(seed=self.nodes[1].dumpprivkey(keyhash_to_p2pkh(hex_str_to_bytes(self.nodes[1].getwalletinfo()['hdseedid'])[::-1])))
+ self.nodes[0].rescanblockchain()
+ self.nodes[0].generatetoaddress(100, ADDRESS_BCRT1_UNSPENDABLE)
+
+ # Generate transaction on node 0, sync mempools, and check for
+ # notification on node 1.
+ tx1 = self.nodes[0].sendtoaddress(address=ADDRESS_BCRT1_UNSPENDABLE, amount=1, replaceable=True)
+ assert_equal(tx1 in self.nodes[0].getrawmempool(), True)
+ self.sync_mempools()
+ self.expect_wallet_notify([tx1])
+
+ # Generate bump transaction, sync mempools, and check for bump1
+ # notification. In the future, per
+ # https://github.com/bitcoin/bitcoin/pull/9371, it might be better
+ # to have notifications for both tx1 and bump1.
+ bump1 = self.nodes[0].bumpfee(tx1)["txid"]
+ assert_equal(bump1 in self.nodes[0].getrawmempool(), True)
+ self.sync_mempools()
+ self.expect_wallet_notify([bump1])
+
+ # Add bump1 transaction to new block, checking for a notification
+ # and the correct number of confirmations.
+ self.nodes[0].generatetoaddress(1, ADDRESS_BCRT1_UNSPENDABLE)
+ self.sync_blocks()
+ self.expect_wallet_notify([bump1])
+ assert_equal(self.nodes[1].gettransaction(bump1)["confirmations"], 1)
+
+ # Generate a second transaction to be bumped.
+ tx2 = self.nodes[0].sendtoaddress(address=ADDRESS_BCRT1_UNSPENDABLE, amount=1, replaceable=True)
+ assert_equal(tx2 in self.nodes[0].getrawmempool(), True)
+ self.sync_mempools()
+ self.expect_wallet_notify([tx2])
+
+ # Bump tx2 as bump2 and generate a block on node 0 while
+ # disconnected, then reconnect and check for notifications on node 1
+ # about newly confirmed bump2 and newly conflicted tx2. Currently
+ # only the bump2 notification is sent. Ideally, notifications would
+ # be sent both for bump2 and tx2, which was the previous behavior
+ # before being broken by an accidental change in PR
+ # https://github.com/bitcoin/bitcoin/pull/16624. The bug is reported
+ # in issue https://github.com/bitcoin/bitcoin/issues/18325.
+ disconnect_nodes(self.nodes[0], 1)
+ bump2 = self.nodes[0].bumpfee(tx2)["txid"]
+ self.nodes[0].generatetoaddress(1, ADDRESS_BCRT1_UNSPENDABLE)
+ assert_equal(self.nodes[0].gettransaction(bump2)["confirmations"], 1)
+ assert_equal(tx2 in self.nodes[1].getrawmempool(), True)
+ connect_nodes(self.nodes[0], 1)
+ self.sync_blocks()
+ self.expect_wallet_notify([bump2])
+ assert_equal(self.nodes[1].gettransaction(bump2)["confirmations"], 1)
# TODO: add test for `-alertnotify` large fork notifications
+ def expect_wallet_notify(self, tx_ids):
+ wait_until(lambda: len(os.listdir(self.walletnotify_dir)) >= len(tx_ids), timeout=10)
+ assert_equal(sorted(notify_outputname(self.wallet, tx_id) for tx_id in tx_ids), sorted(os.listdir(self.walletnotify_dir)))
+ for tx_file in os.listdir(self.walletnotify_dir):
+ os.remove(os.path.join(self.walletnotify_dir, tx_file))
+
+
if __name__ == '__main__':
NotificationsTest().main()
diff --git a/test/functional/feature_reindex.py b/test/functional/feature_reindex.py
index 940b403f9c..31cea8d1b7 100755
--- a/test/functional/feature_reindex.py
+++ b/test/functional/feature_reindex.py
@@ -10,10 +10,10 @@
"""
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import wait_until
+from test_framework.util import assert_equal
-class ReindexTest(BitcoinTestFramework):
+class ReindexTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 1
@@ -24,7 +24,7 @@ class ReindexTest(BitcoinTestFramework):
self.stop_nodes()
extra_args = [["-reindex-chainstate" if justchainstate else "-reindex"]]
self.start_nodes(extra_args)
- wait_until(lambda: self.nodes[0].getblockcount() == blockcount)
+ assert_equal(self.nodes[0].getblockcount(), blockcount) # start_node is blocking on reindex
self.log.info("Success")
def run_test(self):
diff --git a/test/functional/mempool_persist.py b/test/functional/mempool_persist.py
index 99003d2d1f..3969da2eb0 100755
--- a/test/functional/mempool_persist.py
+++ b/test/functional/mempool_persist.py
@@ -95,8 +95,8 @@ class MempoolPersistTest(BitcoinTestFramework):
self.start_node(1, extra_args=["-persistmempool=0"])
self.start_node(0)
self.start_node(2)
- wait_until(lambda: self.nodes[0].getmempoolinfo()["loaded"], timeout=1)
- wait_until(lambda: self.nodes[2].getmempoolinfo()["loaded"], timeout=1)
+ assert self.nodes[0].getmempoolinfo()["loaded"] # start_node is blocking on the mempool being loaded
+ assert self.nodes[2].getmempoolinfo()["loaded"]
assert_equal(len(self.nodes[0].getrawmempool()), 6)
assert_equal(len(self.nodes[2].getrawmempool()), 5)
# The others have loaded their mempool. If node_1 loaded anything, we'd probably notice by now:
@@ -117,13 +117,13 @@ class MempoolPersistTest(BitcoinTestFramework):
self.log.debug("Stop-start node0 with -persistmempool=0. Verify that it doesn't load its mempool.dat file.")
self.stop_nodes()
self.start_node(0, extra_args=["-persistmempool=0", "-disablewallet"])
- wait_until(lambda: self.nodes[0].getmempoolinfo()["loaded"])
+ assert self.nodes[0].getmempoolinfo()["loaded"]
assert_equal(len(self.nodes[0].getrawmempool()), 0)
self.log.debug("Stop-start node0. Verify that it has the transactions in its mempool.")
self.stop_nodes()
self.start_node(0)
- wait_until(lambda: self.nodes[0].getmempoolinfo()["loaded"])
+ assert self.nodes[0].getmempoolinfo()["loaded"]
assert_equal(len(self.nodes[0].getrawmempool()), 6)
mempooldat0 = os.path.join(self.nodes[0].datadir, self.chain, 'mempool.dat')
@@ -137,7 +137,7 @@ class MempoolPersistTest(BitcoinTestFramework):
os.rename(mempooldat0, mempooldat1)
self.stop_nodes()
self.start_node(1, extra_args=[])
- wait_until(lambda: self.nodes[1].getmempoolinfo()["loaded"])
+ assert self.nodes[1].getmempoolinfo()["loaded"]
assert_equal(len(self.nodes[1].getrawmempool()), 6)
self.log.debug("Prevent bitcoind from writing mempool.dat to disk. Verify that `savemempool` fails")
diff --git a/test/functional/p2p_blockfilters.py b/test/functional/p2p_blockfilters.py
new file mode 100755
index 0000000000..4d00a6dc07
--- /dev/null
+++ b/test/functional/p2p_blockfilters.py
@@ -0,0 +1,134 @@
+#!/usr/bin/env python3
+# Copyright (c) 2019 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Tests NODE_COMPACT_FILTERS (BIP 157/158).
+
+Tests that a node configured with -blockfilterindex and -peerblockfilters can serve
+cfcheckpts.
+"""
+
+from test_framework.messages import (
+ FILTER_TYPE_BASIC,
+ msg_getcfcheckpt,
+)
+from test_framework.mininode import P2PInterface
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal,
+ connect_nodes,
+ disconnect_nodes,
+ wait_until,
+)
+
+class CompactFiltersTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = True
+ self.rpc_timeout = 480
+ self.num_nodes = 2
+ self.extra_args = [
+ ["-blockfilterindex", "-peerblockfilters"],
+ ["-blockfilterindex"],
+ ]
+
+ def run_test(self):
+ # Node 0 supports COMPACT_FILTERS, node 1 does not.
+ node0 = self.nodes[0].add_p2p_connection(P2PInterface())
+ node1 = self.nodes[1].add_p2p_connection(P2PInterface())
+
+ # Nodes 0 & 1 share the same first 999 blocks in the chain.
+ self.nodes[0].generate(999)
+ self.sync_blocks(timeout=600)
+
+ # Stale blocks by disconnecting nodes 0 & 1, mining, then reconnecting
+ disconnect_nodes(self.nodes[0], 1)
+
+ self.nodes[0].generate(1)
+ wait_until(lambda: self.nodes[0].getblockcount() == 1000)
+ stale_block_hash = self.nodes[0].getblockhash(1000)
+
+ self.nodes[1].generate(1001)
+ wait_until(lambda: self.nodes[1].getblockcount() == 2000)
+
+ self.log.info("get cfcheckpt on chain to be re-orged out.")
+ request = msg_getcfcheckpt(
+ filter_type=FILTER_TYPE_BASIC,
+ stop_hash=int(stale_block_hash, 16)
+ )
+ node0.send_and_ping(message=request)
+ response = node0.last_message['cfcheckpt']
+ assert_equal(response.filter_type, request.filter_type)
+ assert_equal(response.stop_hash, request.stop_hash)
+ assert_equal(len(response.headers), 1)
+
+ self.log.info("Reorg node 0 to a new chain.")
+ connect_nodes(self.nodes[0], 1)
+ self.sync_blocks(timeout=600)
+
+ main_block_hash = self.nodes[0].getblockhash(1000)
+ assert main_block_hash != stale_block_hash, "node 0 chain did not reorganize"
+
+ self.log.info("Check that peers can fetch cfcheckpt on active chain.")
+ tip_hash = self.nodes[0].getbestblockhash()
+ request = msg_getcfcheckpt(
+ filter_type=FILTER_TYPE_BASIC,
+ stop_hash=int(tip_hash, 16)
+ )
+ node0.send_and_ping(request)
+ response = node0.last_message['cfcheckpt']
+ assert_equal(response.filter_type, request.filter_type)
+ assert_equal(response.stop_hash, request.stop_hash)
+
+ main_cfcheckpt = self.nodes[0].getblockfilter(main_block_hash, 'basic')['header']
+ tip_cfcheckpt = self.nodes[0].getblockfilter(tip_hash, 'basic')['header']
+ assert_equal(
+ response.headers,
+ [int(header, 16) for header in (main_cfcheckpt, tip_cfcheckpt)]
+ )
+
+ self.log.info("Check that peers can fetch cfcheckpt on stale chain.")
+ request = msg_getcfcheckpt(
+ filter_type=FILTER_TYPE_BASIC,
+ stop_hash=int(stale_block_hash, 16)
+ )
+ node0.send_and_ping(request)
+ response = node0.last_message['cfcheckpt']
+
+ stale_cfcheckpt = self.nodes[0].getblockfilter(stale_block_hash, 'basic')['header']
+ assert_equal(
+ response.headers,
+ [int(header, 16) for header in (stale_cfcheckpt,)]
+ )
+
+ self.log.info("Requests to node 1 without NODE_COMPACT_FILTERS results in disconnection.")
+ requests = [
+ msg_getcfcheckpt(
+ filter_type=FILTER_TYPE_BASIC,
+ stop_hash=int(main_block_hash, 16)
+ ),
+ ]
+ for request in requests:
+ node1 = self.nodes[1].add_p2p_connection(P2PInterface())
+ node1.send_message(request)
+ node1.wait_for_disconnect()
+
+ self.log.info("Check that invalid requests result in disconnection.")
+ requests = [
+ # Requesting unknown filter type results in disconnection.
+ msg_getcfcheckpt(
+ filter_type=255,
+ stop_hash=int(main_block_hash, 16)
+ ),
+ # Requesting unknown hash results in disconnection.
+ msg_getcfcheckpt(
+ filter_type=FILTER_TYPE_BASIC,
+ stop_hash=123456789,
+ ),
+ ]
+ for request in requests:
+ node0 = self.nodes[0].add_p2p_connection(P2PInterface())
+ node0.send_message(request)
+ node0.wait_for_disconnect()
+
+if __name__ == '__main__':
+ CompactFiltersTest().main()
diff --git a/test/functional/p2p_getdata.py b/test/functional/p2p_getdata.py
new file mode 100755
index 0000000000..fd94a09d80
--- /dev/null
+++ b/test/functional/p2p_getdata.py
@@ -0,0 +1,51 @@
+#!/usr/bin/env python3
+# Copyright (c) 2020 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Test GETDATA processing behavior"""
+from collections import defaultdict
+
+from test_framework.messages import (
+ CInv,
+ msg_getdata,
+)
+from test_framework.mininode import (
+ mininode_lock,
+ P2PInterface,
+)
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import wait_until
+
+class P2PStoreBlock(P2PInterface):
+
+ def __init__(self):
+ super().__init__()
+ self.blocks = defaultdict(int)
+
+ def on_block(self, message):
+ message.block.calc_sha256()
+ self.blocks[message.block.sha256] += 1
+
+class GetdataTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 1
+
+ def run_test(self):
+ self.nodes[0].add_p2p_connection(P2PStoreBlock())
+
+ self.log.info("test that an invalid GETDATA doesn't prevent processing of future messages")
+
+ # Send invalid message and verify that node responds to later ping
+ invalid_getdata = msg_getdata()
+ invalid_getdata.inv.append(CInv(t=0, h=0)) # INV type 0 is invalid.
+ self.nodes[0].p2ps[0].send_and_ping(invalid_getdata)
+
+ # Check getdata still works by fetching tip block
+ best_block = int(self.nodes[0].getbestblockhash(), 16)
+ good_getdata = msg_getdata()
+ good_getdata.inv.append(CInv(t=2, h=best_block))
+ self.nodes[0].p2ps[0].send_and_ping(good_getdata)
+ wait_until(lambda: self.nodes[0].p2ps[0].blocks[best_block] == 1, timeout=30, lock=mininode_lock)
+
+if __name__ == '__main__':
+ GetdataTest().main()
diff --git a/test/functional/test_framework/messages.py b/test/functional/test_framework/messages.py
index 4855f62a8f..ef5ef49eaf 100755
--- a/test/functional/test_framework/messages.py
+++ b/test/functional/test_framework/messages.py
@@ -57,6 +57,8 @@ MSG_FILTERED_BLOCK = 3
MSG_WITNESS_FLAG = 1 << 30
MSG_TYPE_MASK = 0xffffffff >> 2
+FILTER_TYPE_BASIC = 0
+
# Serialization/deserialization tools
def sha256(s):
return hashlib.new('sha256', s).digest()
@@ -1512,3 +1514,50 @@ class msg_no_witness_blocktxn(msg_blocktxn):
def serialize(self):
return self.block_transactions.serialize(with_witness=False)
+
+class msg_getcfcheckpt:
+ __slots__ = ("filter_type", "stop_hash")
+ msgtype = b"getcfcheckpt"
+
+ def __init__(self, filter_type, stop_hash):
+ self.filter_type = filter_type
+ self.stop_hash = stop_hash
+
+ def deserialize(self, f):
+ self.filter_type = struct.unpack("<B", f.read(1))[0]
+ self.stop_hash = deser_uint256(f)
+
+ def serialize(self):
+ r = b""
+ r += struct.pack("<B", self.filter_type)
+ r += ser_uint256(self.stop_hash)
+ return r
+
+ def __repr__(self):
+ return "msg_getcfcheckpt(filter_type={:#x}, stop_hash={:x})".format(
+ self.filter_type, self.stop_hash)
+
+class msg_cfcheckpt:
+ __slots__ = ("filter_type", "stop_hash", "headers")
+ msgtype = b"cfcheckpt"
+
+ def __init__(self, filter_type=None, stop_hash=None, headers=None):
+ self.filter_type = filter_type
+ self.stop_hash = stop_hash
+ self.headers = headers
+
+ def deserialize(self, f):
+ self.filter_type = struct.unpack("<B", f.read(1))[0]
+ self.stop_hash = deser_uint256(f)
+ self.headers = deser_uint256_vector(f)
+
+ def serialize(self):
+ r = b""
+ r += struct.pack("<B", self.filter_type)
+ r += ser_uint256(self.stop_hash)
+ r += ser_uint256_vector(self.headers)
+ return r
+
+ def __repr__(self):
+ return "msg_cfcheckpt(filter_type={:#x}, stop_hash={:x})".format(
+ self.filter_type, self.stop_hash)
diff --git a/test/functional/test_framework/mininode.py b/test/functional/test_framework/mininode.py
index 31cec66ee7..ba0391625e 100755
--- a/test/functional/test_framework/mininode.py
+++ b/test/functional/test_framework/mininode.py
@@ -31,6 +31,7 @@ from test_framework.messages import (
msg_block,
MSG_BLOCK,
msg_blocktxn,
+ msg_cfcheckpt,
msg_cmpctblock,
msg_feefilter,
msg_filteradd,
@@ -67,6 +68,7 @@ MESSAGEMAP = {
b"addr": msg_addr,
b"block": msg_block,
b"blocktxn": msg_blocktxn,
+ b"cfcheckpt": msg_cfcheckpt,
b"cmpctblock": msg_cmpctblock,
b"feefilter": msg_feefilter,
b"filteradd": msg_filteradd,
@@ -328,6 +330,7 @@ class P2PInterface(P2PConnection):
def on_addr(self, message): pass
def on_block(self, message): pass
def on_blocktxn(self, message): pass
+ def on_cfcheckpt(self, message): pass
def on_cmpctblock(self, message): pass
def on_feefilter(self, message): pass
def on_filteradd(self, message): pass
diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index 0c2ef83834..826faece68 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -110,7 +110,7 @@ class TestNode():
"--gen-suppressions=all", "--exit-on-first-error=yes",
"--error-exitcode=1", "--quiet"] + self.args
- if self.version is None or self.version >= 190000:
+ if self.version_is_at_least(190000):
self.args.append("-logthreadnames")
self.cli = TestNodeCLI(bitcoin_cli, self.datadir)
@@ -222,6 +222,27 @@ class TestNode():
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.getblockcount()
# If the call to getblockcount() succeeds then the RPC connection is up
+ if self.version_is_at_least(190000):
+ # getmempoolinfo.loaded is available since commit
+ # bb8ae2c (version 0.19.0)
+ wait_until(lambda: rpc.getmempoolinfo()['loaded'])
+ # Wait for the node to finish reindex, block import, and
+ # loading the mempool. Usually importing happens fast or
+ # even "immediate" when the node is started. However, there
+ # is no guarantee and sometimes ThreadImport might finish
+ # later. This is going to cause intermittent test failures,
+ # because generally the tests assume the node is fully
+ # ready after being started.
+ #
+ # For example, the node will reject block messages from p2p
+ # when it is still importing with the error "Unexpected
+ # block message received"
+ #
+ # The wait is done here to make tests as robust as possible
+ # and prevent racy tests and intermittent failures as much
+ # as possible. Some tests might not need this, but the
+ # overhead is trivial, and the added gurantees are worth
+ # the minimal performance cost.
self.log.debug("RPC successfully started")
if self.use_cli:
return
@@ -274,6 +295,9 @@ class TestNode():
wallet_path = "wallet/{}".format(urllib.parse.quote(wallet_name))
return RPCOverloadWrapper(self.rpc / wallet_path, descriptors=self.descriptors)
+ def version_is_at_least(self, ver):
+ return self.version is None or self.version >= ver
+
def stop_node(self, expected_stderr='', wait=0):
"""Stop the node."""
if not self.running:
@@ -281,7 +305,7 @@ class TestNode():
self.log.debug("Stopping node")
try:
# Do not use wait argument when testing older nodes, e.g. in feature_backwards_compatibility.py
- if self.version is None or self.version >= 180000:
+ if self.version_is_at_least(180000):
self.stop(wait=wait)
else:
self.stop()
diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py
index 20ab9ee464..7466a3cab3 100644
--- a/test/functional/test_framework/util.py
+++ b/test/functional/test_framework/util.py
@@ -399,7 +399,11 @@ def connect_nodes(from_connection, node_num):
from_connection.addnode(ip_port, "onetry")
# poll until version handshake complete to avoid race conditions
# with transaction relaying
- wait_until(lambda: all(peer['version'] != 0 for peer in from_connection.getpeerinfo()))
+ # See comments in net_processing:
+ # * Must have a version message before anything else
+ # * Must have a verack message before anything else
+ wait_until(lambda: all(peer['version'] != 0 for peer in from_connection.getpeerinfo()))
+ wait_until(lambda: all(peer['bytesrecv_per_msg'].pop('verack', 0) == 24 for peer in from_connection.getpeerinfo()))
def sync_blocks(rpc_connections, *, wait=1, timeout=60):
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 45f591ba4a..7821355e29 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -157,6 +157,7 @@ BASE_SCRIPTS = [
'rpc_deprecated.py',
'wallet_disable.py',
'p2p_addr_relay.py',
+ 'p2p_getdata.py',
'rpc_net.py',
'wallet_keypool.py',
'wallet_keypool.py --descriptors',
@@ -225,6 +226,7 @@ BASE_SCRIPTS = [
'feature_loadblock.py',
'p2p_dos_header_tree.py',
'p2p_unrequested_blocks.py',
+ 'p2p_blockfilters.py',
'feature_includeconf.py',
'feature_asmap.py',
'mempool_unbroadcast.py',
diff --git a/test/functional/wallet_abandonconflict.py b/test/functional/wallet_abandonconflict.py
index c7b19081c8..90d17a806c 100755
--- a/test/functional/wallet_abandonconflict.py
+++ b/test/functional/wallet_abandonconflict.py
@@ -18,7 +18,6 @@ from test_framework.util import (
assert_raises_rpc_error,
connect_nodes,
disconnect_nodes,
- wait_until,
)
@@ -98,7 +97,7 @@ class AbandonConflictTest(BitcoinTestFramework):
# TODO: redo with eviction
self.stop_node(0)
self.start_node(0, extra_args=["-minrelaytxfee=0.0001"])
- wait_until(lambda: self.nodes[0].getmempoolinfo()['loaded'])
+ assert self.nodes[0].getmempoolinfo()['loaded']
# Verify txs no longer in either node's mempool
assert_equal(len(self.nodes[0].getrawmempool()), 0)
@@ -126,7 +125,7 @@ class AbandonConflictTest(BitcoinTestFramework):
# Verify that even with a low min relay fee, the tx is not reaccepted from wallet on startup once abandoned
self.stop_node(0)
self.start_node(0, extra_args=["-minrelaytxfee=0.00001"])
- wait_until(lambda: self.nodes[0].getmempoolinfo()['loaded'])
+ assert self.nodes[0].getmempoolinfo()['loaded']
assert_equal(len(self.nodes[0].getrawmempool()), 0)
assert_equal(self.nodes[0].getbalance(), balance)
@@ -148,7 +147,7 @@ class AbandonConflictTest(BitcoinTestFramework):
# Remove using high relay fee again
self.stop_node(0)
self.start_node(0, extra_args=["-minrelaytxfee=0.0001"])
- wait_until(lambda: self.nodes[0].getmempoolinfo()['loaded'])
+ assert self.nodes[0].getmempoolinfo()['loaded']
assert_equal(len(self.nodes[0].getrawmempool()), 0)
newbalance = self.nodes[0].getbalance()
assert_equal(newbalance, balance - Decimal("24.9996"))
diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py
index 2dddbf2cf3..9e295af330 100755
--- a/test/functional/wallet_basic.py
+++ b/test/functional/wallet_basic.py
@@ -4,7 +4,6 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test the wallet."""
from decimal import Decimal
-import time
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
@@ -466,12 +465,8 @@ class WalletTest(BitcoinTestFramework):
extra_args = ["-walletrejectlongchains", "-limitancestorcount=" + str(2 * chainlimit)]
self.start_node(0, extra_args=extra_args)
- # wait for loadmempool
- timeout = 10
- while (timeout > 0 and len(self.nodes[0].getrawmempool()) < chainlimit * 2):
- time.sleep(0.5)
- timeout -= 0.5
- assert_equal(len(self.nodes[0].getrawmempool()), chainlimit * 2)
+ # wait until the wallet has submitted all transactions to the mempool
+ wait_until(lambda: len(self.nodes[0].getrawmempool()) == chainlimit * 2)
node0_balance = self.nodes[0].getbalance()
# With walletrejectlongchains we will not create the tx and store it in our wallet.
diff --git a/test/functional/wallet_upgradewallet.py b/test/functional/wallet_upgradewallet.py
index e7e71bf3f6..bb81746715 100755
--- a/test/functional/wallet_upgradewallet.py
+++ b/test/functional/wallet_upgradewallet.py
@@ -4,9 +4,11 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""upgradewallet RPC functional test
-Test upgradewallet RPC. Download v0.15.2 v0.16.3 node binaries:
+Test upgradewallet RPC. Download node binaries:
-contrib/devtools/previous_release.sh -b v0.15.2 v0.16.3
+contrib/devtools/previous_release.sh -b v0.19.1 v0.18.1 v0.17.1 v0.16.3 v0.15.2
+
+Only v0.15.2 and v0.16.3 are required by this test. The others are used in feature_backwards_compatibility.py
"""
import os
diff --git a/test/fuzz/test_runner.py b/test/fuzz/test_runner.py
index e2454c4237..56b18752ec 100755
--- a/test/fuzz/test_runner.py
+++ b/test/fuzz/test_runner.py
@@ -38,6 +38,7 @@ def main():
)
parser.add_argument(
'--par',
+ '-j',
type=int,
default=4,
help='How many targets to merge or execute in parallel.',
diff --git a/test/lint/lint-shell.sh b/test/lint/lint-shell.sh
index 2bb76ec286..563e076b35 100755
--- a/test/lint/lint-shell.sh
+++ b/test/lint/lint-shell.sh
@@ -35,8 +35,9 @@ if ! command -v shellcheck > /dev/null; then
exit $EXIT_CODE
fi
+SHELLCHECK_CMD=(shellcheck --external-sources --check-sourced)
EXCLUDE="--exclude=$(IFS=','; echo "${disabled[*]}")"
-if ! shellcheck "$EXCLUDE" $(git ls-files -- '*.sh' | grep -vE 'src/(leveldb|secp256k1|univalue)/'); then
+if ! "${SHELLCHECK_CMD[@]}" "$EXCLUDE" $(git ls-files -- '*.sh' | grep -vE 'src/(leveldb|secp256k1|univalue)/'); then
EXIT_CODE=1
fi
@@ -46,14 +47,13 @@ if ! command -v yq > /dev/null; then
fi
EXCLUDE_GITIAN=${EXCLUDE}",$(IFS=','; echo "${disabled_gitian[*]}")"
-SHELLCHECK_CMD="shellcheck --external-sources --check-sourced $EXCLUDE_GITIAN"
for descriptor in $(git ls-files -- 'contrib/gitian-descriptors/*.yml')
do
script=$(basename "$descriptor")
# Use #!/bin/bash as gitian-builder/bin/gbuild does to complete a script.
echo "#!/bin/bash" > $script
yq -r .script "$descriptor" >> $script
- if ! $SHELLCHECK_CMD $script; then
+ if ! "${SHELLCHECK_CMD[@]}" "$EXCLUDE_GITIAN" $script; then
EXIT_CODE=1
fi
rm $script