aboutsummaryrefslogtreecommitdiff
path: root/test/functional
diff options
context:
space:
mode:
Diffstat (limited to 'test/functional')
-rwxr-xr-xtest/functional/feature_dbcrash.py55
-rwxr-xr-xtest/functional/feature_index_prune.py6
-rwxr-xr-xtest/functional/feature_minchainwork.py8
-rwxr-xr-xtest/functional/feature_pruning.py2
-rwxr-xr-xtest/functional/mempool_accept.py4
-rwxr-xr-xtest/functional/p2p_block_sync.py37
-rwxr-xr-xtest/functional/rpc_createmultisig.py11
-rwxr-xr-xtest/functional/rpc_getblockfrompeer.py13
-rwxr-xr-xtest/functional/rpc_rawtransaction.py5
-rw-r--r--test/functional/test_framework/wallet.py11
-rwxr-xr-xtest/functional/test_runner.py1
11 files changed, 113 insertions, 40 deletions
diff --git a/test/functional/feature_dbcrash.py b/test/functional/feature_dbcrash.py
index 3e60efbb3c..a3a5e9e27d 100755
--- a/test/functional/feature_dbcrash.py
+++ b/test/functional/feature_dbcrash.py
@@ -30,17 +30,17 @@ import http.client
import random
import time
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.messages import (
COIN,
- COutPoint,
- CTransaction,
- CTxIn,
- CTxOut,
)
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
- create_confirmed_utxos,
+)
+from test_framework.wallet import (
+ MiniWallet,
+ getnewdestination,
)
@@ -66,13 +66,9 @@ class ChainstateWriteCrashTest(BitcoinTestFramework):
self.node3_args = ["-blockmaxweight=4000000", "-acceptnonstdtxn"]
self.extra_args = [self.node0_args, self.node1_args, self.node2_args, self.node3_args]
- def skip_test_if_missing_module(self):
- self.skip_if_no_wallet()
-
def setup_network(self):
self.add_nodes(self.num_nodes, extra_args=self.extra_args)
self.start_nodes()
- self.import_deterministic_coinbase_privkeys()
# Leave them unconnected, we'll use submitblock directly in this test
def restart_node(self, node_index, expected_tip):
@@ -190,34 +186,36 @@ class ChainstateWriteCrashTest(BitcoinTestFramework):
num_transactions = 0
random.shuffle(utxo_list)
while len(utxo_list) >= 2 and num_transactions < count:
- tx = CTransaction()
- input_amount = 0
- for _ in range(2):
- utxo = utxo_list.pop()
- tx.vin.append(CTxIn(COutPoint(int(utxo['txid'], 16), utxo['vout'])))
- input_amount += int(utxo['amount'] * COIN)
- output_amount = (input_amount - FEE) // 3
-
- if output_amount <= 0:
+ utxos_to_spend = [utxo_list.pop() for _ in range(2)]
+ input_amount = int(sum([utxo['value'] for utxo in utxos_to_spend]) * COIN)
+ if input_amount < FEE:
# Sanity check -- if we chose inputs that are too small, skip
continue
- for _ in range(3):
- tx.vout.append(CTxOut(output_amount, bytes.fromhex(utxo['scriptPubKey'])))
+ tx = self.wallet.create_self_transfer_multi(
+ from_node=node,
+ utxos_to_spend=utxos_to_spend,
+ num_outputs=3,
+ fee_per_output=FEE // 3)
- # Sign and send the transaction to get into the mempool
- tx_signed_hex = node.signrawtransactionwithwallet(tx.serialize().hex())['hex']
- node.sendrawtransaction(tx_signed_hex)
+ # Send the transaction to get into the mempool (skip fee-checks to run faster)
+ node.sendrawtransaction(hexstring=tx.serialize().hex(), maxfeerate=0)
num_transactions += 1
def run_test(self):
+ self.wallet = MiniWallet(self.nodes[3])
+ self.wallet.rescan_utxos()
+ initial_height = self.nodes[3].getblockcount()
+ self.generate(self.nodes[3], COINBASE_MATURITY, sync_fun=self.no_op)
+
# Track test coverage statistics
self.restart_counts = [0, 0, 0] # Track the restarts for nodes 0-2
self.crashed_on_restart = 0 # Track count of crashes during recovery
# Start by creating a lot of utxos on node3
- initial_height = self.nodes[3].getblockcount()
- utxo_list = create_confirmed_utxos(self, self.nodes[3].getnetworkinfo()['relayfee'], self.nodes[3], 5000, sync_fun=self.no_op)
+ utxo_list = self.wallet.send_self_transfer_multi(from_node=self.nodes[3], num_outputs=5000)['new_utxos']
+ self.generate(self.nodes[3], 1, sync_fun=self.no_op)
+ assert_equal(len(self.nodes[3].getrawmempool()), 0)
self.log.info(f"Prepped {len(utxo_list)} utxo entries")
# Sync these blocks with the other nodes
@@ -257,13 +255,14 @@ class ChainstateWriteCrashTest(BitcoinTestFramework):
self.nodes[3],
nblocks=min(10, current_height + 1 - self.nodes[3].getblockcount()),
# new address to avoid mining a block that has just been invalidated
- address=self.nodes[3].getnewaddress(),
+ address=getnewdestination()[2],
sync_fun=self.no_op,
))
self.log.debug(f"Syncing {len(block_hashes)} new blocks...")
self.sync_node3blocks(block_hashes)
- utxo_list = self.nodes[3].listunspent()
- self.log.debug(f"Node3 utxo count: {len(utxo_list)}")
+ self.wallet.rescan_utxos()
+ utxo_list = self.wallet.get_utxos()
+ self.log.debug(f"MiniWallet utxo count: {len(utxo_list)}")
# Check that the utxo hashes agree with node3
# Useful side effect: each utxo cache gets flushed here, so that we
diff --git a/test/functional/feature_index_prune.py b/test/functional/feature_index_prune.py
index 3ee6a8036c..bc85e43a57 100755
--- a/test/functional/feature_index_prune.py
+++ b/test/functional/feature_index_prune.py
@@ -73,7 +73,7 @@ class FeatureIndexPruneTest(BitcoinTestFramework):
pruneheight_new = node.pruneblockchain(400)
# the prune heights used here and below are magic numbers that are determined by the
# thresholds at which block files wrap, so they depend on disk serialization and default block file size.
- assert_equal(pruneheight_new, 249)
+ assert_equal(pruneheight_new, 248)
self.log.info("check if we can access the tips blockfilter and coinstats when we have pruned some blocks")
tip = self.nodes[0].getbestblockhash()
@@ -108,7 +108,7 @@ class FeatureIndexPruneTest(BitcoinTestFramework):
self.log.info("prune exactly up to the indices best blocks while the indices are disabled")
for i in range(3):
pruneheight_2 = self.nodes[i].pruneblockchain(1000)
- assert_equal(pruneheight_2, 751)
+ assert_equal(pruneheight_2, 750)
# Restart the nodes again with the indices activated
self.restart_node(i, extra_args=self.extra_args[i])
@@ -142,7 +142,7 @@ class FeatureIndexPruneTest(BitcoinTestFramework):
for node in self.nodes[:2]:
with node.assert_debug_log(['limited pruning to height 2489']):
pruneheight_new = node.pruneblockchain(2500)
- assert_equal(pruneheight_new, 2006)
+ assert_equal(pruneheight_new, 2005)
self.log.info("ensure that prune locks don't prevent indices from failing in a reorg scenario")
with self.nodes[0].assert_debug_log(['basic block filter index prune lock moved back to 2480']):
diff --git a/test/functional/feature_minchainwork.py b/test/functional/feature_minchainwork.py
index f6432ed20e..fa10855a98 100755
--- a/test/functional/feature_minchainwork.py
+++ b/test/functional/feature_minchainwork.py
@@ -106,5 +106,13 @@ class MinimumChainWorkTest(BitcoinTestFramework):
# not be exercising the logic we want!)
assert_equal(self.nodes[2].getblockchaininfo()['initialblockdownload'], True)
+ self.log.info("Test -minimumchainwork with a non-hex value")
+ self.stop_node(0)
+ self.nodes[0].assert_start_raises_init_error(
+ ["-minimumchainwork=test"],
+ expected_msg='Error: Invalid non-hex (test) minimum chain work value specified',
+ )
+
+
if __name__ == '__main__':
MinimumChainWorkTest().main()
diff --git a/test/functional/feature_pruning.py b/test/functional/feature_pruning.py
index 77524e85a3..7dbeccbc09 100755
--- a/test/functional/feature_pruning.py
+++ b/test/functional/feature_pruning.py
@@ -291,7 +291,7 @@ class PruneTest(BitcoinTestFramework):
def prune(index):
ret = node.pruneblockchain(height=height(index))
- assert_equal(ret, node.getblockchaininfo()['pruneheight'])
+ assert_equal(ret + 1, node.getblockchaininfo()['pruneheight'])
def has_block(index):
return os.path.isfile(os.path.join(self.nodes[node_number].datadir, self.chain, "blocks", f"blk{index:05}.dat"))
diff --git a/test/functional/mempool_accept.py b/test/functional/mempool_accept.py
index d3961e753d..85464b8d0d 100755
--- a/test/functional/mempool_accept.py
+++ b/test/functional/mempool_accept.py
@@ -76,7 +76,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
tx.vout[0].nValue = int(0.3 * COIN)
tx.vout[1].nValue = int(49 * COIN)
raw_tx_in_block = tx.serialize().hex()
- txid_in_block = self.wallet.sendrawtransaction(from_node=node, tx_hex=raw_tx_in_block, maxfeerate=0)
+ txid_in_block = self.wallet.sendrawtransaction(from_node=node, tx_hex=raw_tx_in_block)
self.generate(node, 1)
self.mempool_size = 0
self.check_mempool_result(
@@ -166,7 +166,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
tx.vin[1].prevout = COutPoint(hash=int(txid_1, 16), n=0)
tx.vout[0].nValue = int(0.1 * COIN)
raw_tx_spend_both = tx.serialize().hex()
- txid_spend_both = self.wallet.sendrawtransaction(from_node=node, tx_hex=raw_tx_spend_both, maxfeerate=0)
+ txid_spend_both = self.wallet.sendrawtransaction(from_node=node, tx_hex=raw_tx_spend_both)
self.generate(node, 1)
self.mempool_size = 0
# Now see if we can add the coins back to the utxo set by sending the exact txs again
diff --git a/test/functional/p2p_block_sync.py b/test/functional/p2p_block_sync.py
new file mode 100755
index 0000000000..d821edc1b1
--- /dev/null
+++ b/test/functional/p2p_block_sync.py
@@ -0,0 +1,37 @@
+#!/usr/bin/env python3
+# Copyright (c) 2022 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 block download
+
+Ensure that even in IBD, we'll eventually sync chain from inbound peers
+(whether we have only inbound peers or both inbound and outbound peers).
+"""
+
+from test_framework.test_framework import BitcoinTestFramework
+
+class BlockSyncTest(BitcoinTestFramework):
+
+ def set_test_params(self):
+ self.setup_clean_chain = True
+ self.num_nodes = 3
+
+ def setup_network(self):
+ self.setup_nodes()
+ # Construct a network:
+ # node0 -> node1 -> node2
+ # So node1 has both an inbound and outbound peer.
+ # In our test, we will mine a block on node0, and ensure that it makes
+ # to to both node1 and node2.
+ self.connect_nodes(0, 1)
+ self.connect_nodes(1, 2)
+
+ def run_test(self):
+ self.log.info("Setup network: node0->node1->node2")
+ self.log.info("Mining one block on node0 and verify all nodes sync")
+ self.generate(self.nodes[0], 1)
+ self.log.info("Success!")
+
+
+if __name__ == '__main__':
+ BlockSyncTest().main()
diff --git a/test/functional/rpc_createmultisig.py b/test/functional/rpc_createmultisig.py
index 1695acaaa8..716ee8f7ef 100755
--- a/test/functional/rpc_createmultisig.py
+++ b/test/functional/rpc_createmultisig.py
@@ -91,15 +91,17 @@ class RpcCreateMultiSigTest(BitcoinTestFramework):
assert 'warnings' not in result
# Generate addresses with the segwit types. These should all make legacy addresses
+ err_msg = ["Unable to make chosen address type, please ensure no uncompressed public keys are present."]
+
for addr_type in ['bech32', 'p2sh-segwit']:
- result = self.nodes[0].createmultisig(2, keys, addr_type)
+ result = self.nodes[0].createmultisig(nrequired=2, keys=keys, address_type=addr_type)
assert_equal(legacy_addr, result['address'])
- assert_equal(result['warnings'], ["Unable to make chosen address type, please ensure no uncompressed public keys are present."])
+ assert_equal(result['warnings'], err_msg)
if self.is_bdb_compiled():
- result = wmulti0.addmultisigaddress(2, keys, '', addr_type)
+ result = wmulti0.addmultisigaddress(nrequired=2, keys=keys, address_type=addr_type)
assert_equal(legacy_addr, result['address'])
- assert_equal(result['warnings'], ["Unable to make chosen address type, please ensure no uncompressed public keys are present."])
+ assert_equal(result['warnings'], err_msg)
self.log.info('Testing sortedmulti descriptors with BIP 67 test vectors')
with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data/rpc_bip67.json'), encoding='utf-8') as f:
@@ -173,6 +175,7 @@ class RpcCreateMultiSigTest(BitcoinTestFramework):
desc = descsum_create(desc)
msig = node2.createmultisig(self.nsigs, self.pub, self.output_type)
+ assert 'warnings' not in msig
madd = msig["address"]
mredeem = msig["redeemScript"]
assert_equal(desc, msig['descriptor'])
diff --git a/test/functional/rpc_getblockfrompeer.py b/test/functional/rpc_getblockfrompeer.py
index b65322d920..a7628b5591 100755
--- a/test/functional/rpc_getblockfrompeer.py
+++ b/test/functional/rpc_getblockfrompeer.py
@@ -5,6 +5,11 @@
"""Test the getblockfrompeer RPC."""
from test_framework.authproxy import JSONRPCException
+from test_framework.messages import NODE_WITNESS
+from test_framework.p2p import (
+ P2P_SERVICES,
+ P2PInterface,
+)
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
@@ -58,6 +63,13 @@ class GetBlockFromPeerTest(BitcoinTestFramework):
self.log.info("Non-existent peer generates error")
assert_raises_rpc_error(-1, "Peer does not exist", self.nodes[0].getblockfrompeer, short_tip, peer_0_peer_1_id + 1)
+ self.log.info("Fetching from pre-segwit peer generates error")
+ self.nodes[0].add_p2p_connection(P2PInterface(), services=P2P_SERVICES & ~NODE_WITNESS)
+ peers = self.nodes[0].getpeerinfo()
+ assert_equal(len(peers), 2)
+ presegwit_peer_id = peers[1]["id"]
+ assert_raises_rpc_error(-1, "Pre-SegWit peer", self.nodes[0].getblockfrompeer, short_tip, presegwit_peer_id)
+
self.log.info("Successful fetch")
result = self.nodes[0].getblockfrompeer(short_tip, peer_0_peer_1_id)
self.wait_until(lambda: self.check_for_block(short_tip), timeout=1)
@@ -66,5 +78,6 @@ class GetBlockFromPeerTest(BitcoinTestFramework):
self.log.info("Don't fetch blocks we already have")
assert_raises_rpc_error(-1, "Block already downloaded", self.nodes[0].getblockfrompeer, short_tip, peer_0_peer_1_id)
+
if __name__ == '__main__':
GetBlockFromPeerTest().main()
diff --git a/test/functional/rpc_rawtransaction.py b/test/functional/rpc_rawtransaction.py
index fecb8310b9..1f95814e18 100755
--- a/test/functional/rpc_rawtransaction.py
+++ b/test/functional/rpc_rawtransaction.py
@@ -17,6 +17,7 @@ from decimal import Decimal
from test_framework.blocktools import COINBASE_MATURITY
from test_framework.messages import (
+ BIP125_SEQUENCE_NUMBER,
CTransaction,
tx_from_hex,
)
@@ -220,6 +221,10 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_raises_rpc_error(-8, "Invalid parameter, key-value pair must contain exactly one key", self.nodes[0].createrawtransaction, [], [{'a': 1, 'b': 2}])
assert_raises_rpc_error(-8, "Invalid parameter, key-value pair not an object as expected", self.nodes[0].createrawtransaction, [], [['key-value pair1'], ['2']])
+ # Test `createrawtransaction` mismatch between sequence number(s) and `replaceable` option
+ assert_raises_rpc_error(-8, "Invalid parameter combination: Sequence number(s) contradict replaceable option",
+ self.nodes[0].createrawtransaction, [{'txid': TXID, 'vout': 0, 'sequence': BIP125_SEQUENCE_NUMBER+1}], {}, 0, True)
+
# Test `createrawtransaction` invalid `locktime`
assert_raises_rpc_error(-3, "Expected type number", self.nodes[0].createrawtransaction, [], {}, 'foo')
assert_raises_rpc_error(-8, "Invalid parameter, locktime out of range", self.nodes[0].createrawtransaction, [], {}, -1)
diff --git a/test/functional/test_framework/wallet.py b/test/functional/test_framework/wallet.py
index 6901bcfe66..336b2afe26 100644
--- a/test/functional/test_framework/wallet.py
+++ b/test/functional/test_framework/wallet.py
@@ -167,6 +167,13 @@ class MiniWallet:
else:
return self._utxos[index]
+ def get_utxos(self, *, mark_as_spent=True):
+ """Returns the list of all utxos and optionally mark them as spent"""
+ utxos = deepcopy(self._utxos)
+ if mark_as_spent:
+ self._utxos = []
+ return utxos
+
def send_self_transfer(self, **kwargs):
"""Create and send a tx with the specified fee_rate. Fee may be exact or at most one satoshi higher than needed."""
tx = self.create_self_transfer(**kwargs)
@@ -270,8 +277,8 @@ class MiniWallet:
return {'txid': tx.rehash(), 'wtxid': tx.getwtxid(), 'hex': tx_hex, 'tx': tx}
- def sendrawtransaction(self, *, from_node, tx_hex, **kwargs):
- txid = from_node.sendrawtransaction(hexstring=tx_hex, **kwargs)
+ def sendrawtransaction(self, *, from_node, tx_hex, maxfeerate=0, **kwargs):
+ txid = from_node.sendrawtransaction(hexstring=tx_hex, maxfeerate=maxfeerate, **kwargs)
self.scan_tx(from_node.decoderawtransaction(tx_hex))
return txid
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 40e08c3f1f..6a44f9d21d 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -157,6 +157,7 @@ BASE_SCRIPTS = [
'wallet_avoidreuse.py --descriptors',
'mempool_reorg.py',
'mempool_persist.py',
+ 'p2p_block_sync.py',
'wallet_multiwallet.py --legacy-wallet',
'wallet_multiwallet.py --descriptors',
'wallet_multiwallet.py --usecli',