aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rwxr-xr-xtest/functional/feature_backwards_compatibility.py4
-rwxr-xr-xtest/functional/feature_dbcrash.py4
-rwxr-xr-xtest/functional/feature_fee_estimation.py6
-rwxr-xr-xtest/functional/feature_filelock.py4
-rwxr-xr-xtest/functional/feature_notifications.py4
-rwxr-xr-xtest/functional/feature_pruning.py10
-rwxr-xr-xtest/functional/feature_signet.py70
-rwxr-xr-xtest/functional/interface_bitcoin_cli.py9
-rwxr-xr-xtest/functional/interface_rpc.py2
-rwxr-xr-xtest/functional/interface_zmq.py320
-rwxr-xr-xtest/functional/mempool_accept.py13
-rwxr-xr-xtest/functional/mempool_compatibility.py2
-rwxr-xr-xtest/functional/p2p_blocksonly.py24
-rwxr-xr-xtest/functional/p2p_compactblocks.py31
-rwxr-xr-xtest/functional/p2p_feefilter.py28
-rwxr-xr-xtest/functional/p2p_getaddr_caching.py47
-rwxr-xr-xtest/functional/p2p_segwit.py5
-rwxr-xr-xtest/functional/rpc_createmultisig.py3
-rwxr-xr-xtest/functional/rpc_fundrawtransaction.py2
-rwxr-xr-xtest/functional/rpc_net.py14
-rwxr-xr-xtest/functional/rpc_psbt.py3
-rwxr-xr-xtest/functional/rpc_txoutproof.py86
-rwxr-xr-xtest/functional/test_framework/messages.py16
-rwxr-xr-xtest/functional/test_framework/p2p.py1
-rw-r--r--test/functional/test_framework/wallet.py68
-rwxr-xr-xtest/functional/test_runner.py2
-rwxr-xr-xtest/functional/tool_wallet.py6
-rwxr-xr-xtest/functional/wallet_backup.py8
-rwxr-xr-xtest/functional/wallet_basic.py3
-rwxr-xr-xtest/functional/wallet_dump.py2
-rwxr-xr-xtest/functional/wallet_import_rescan.py4
-rwxr-xr-xtest/functional/wallet_multiwallet.py20
-rwxr-xr-xtest/functional/wallet_send.py339
-rwxr-xr-xtest/functional/wallet_startup.py10
-rwxr-xr-xtest/functional/wallet_upgradewallet.py2
-rw-r--r--test/sanitizer_suppressions/tsan2
36 files changed, 982 insertions, 192 deletions
diff --git a/test/functional/feature_backwards_compatibility.py b/test/functional/feature_backwards_compatibility.py
index dd17e888f9..daefb161ac 100755
--- a/test/functional/feature_backwards_compatibility.py
+++ b/test/functional/feature_backwards_compatibility.py
@@ -36,12 +36,12 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
self.num_nodes = 6
# Add new version after each release:
self.extra_args = [
- ["-addresstype=bech32"], # Pre-release: use to mine blocks
+ ["-addresstype=bech32", "-wallet="], # 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.1
["-nowallet", "-walletrbf=1", "-addresstype=bech32"], # v0.18.1
["-nowallet", "-walletrbf=1", "-addresstype=bech32"], # v0.17.2
- ["-nowallet", "-walletrbf=1", "-addresstype=bech32"], # v0.16.3
+ ["-nowallet", "-walletrbf=1", "-addresstype=bech32", "-wallet=wallet.dat"], # v0.16.3
]
def skip_test_if_missing_module(self):
diff --git a/test/functional/feature_dbcrash.py b/test/functional/feature_dbcrash.py
index 7a2e35c095..4bc47141a1 100755
--- a/test/functional/feature_dbcrash.py
+++ b/test/functional/feature_dbcrash.py
@@ -56,7 +56,7 @@ class ChainstateWriteCrashTest(BitcoinTestFramework):
# Set -maxmempool=0 to turn off mempool memory sharing with dbcache
# Set -rpcservertimeout=900 to reduce socket disconnects in this
# long-running test
- self.base_args = ["-limitdescendantsize=0", "-maxmempool=0", "-rpcservertimeout=900", "-dbbatchsize=200000"]
+ self.base_args = ["-limitdescendantsize=0", "-maxmempool=0", "-rpcservertimeout=900", "-dbbatchsize=200000", "-wallet="]
# Set different crash ratios and cache sizes. Note that not all of
# -dbcache goes to the in-memory coins cache.
@@ -66,7 +66,7 @@ class ChainstateWriteCrashTest(BitcoinTestFramework):
# Node3 is a normal node with default args, except will mine full blocks
# and non-standard txs (e.g. txs with "dust" outputs)
- self.node3_args = ["-blockmaxweight=4000000", "-acceptnonstdtxn"]
+ self.node3_args = ["-blockmaxweight=4000000", "-acceptnonstdtxn", "-wallet="]
self.extra_args = [self.node0_args, self.node1_args, self.node2_args, self.node3_args]
def skip_test_if_missing_module(self):
diff --git a/test/functional/feature_fee_estimation.py b/test/functional/feature_fee_estimation.py
index 702a1d9995..d89eeec400 100755
--- a/test/functional/feature_fee_estimation.py
+++ b/test/functional/feature_fee_estimation.py
@@ -145,9 +145,9 @@ class EstimateFeeTest(BitcoinTestFramework):
# mine non-standard txs (e.g. txs with "dust" outputs)
# Force fSendTrickle to true (via whitelist.noban)
self.extra_args = [
- ["-acceptnonstdtxn", "-whitelist=noban@127.0.0.1"],
- ["-acceptnonstdtxn", "-whitelist=noban@127.0.0.1", "-blockmaxweight=68000"],
- ["-acceptnonstdtxn", "-whitelist=noban@127.0.0.1", "-blockmaxweight=32000"],
+ ["-acceptnonstdtxn", "-whitelist=noban@127.0.0.1", "-wallet="],
+ ["-acceptnonstdtxn", "-whitelist=noban@127.0.0.1", "-blockmaxweight=68000", "-wallet="],
+ ["-acceptnonstdtxn", "-whitelist=noban@127.0.0.1", "-blockmaxweight=32000", "-wallet="],
]
def skip_test_if_missing_module(self):
diff --git a/test/functional/feature_filelock.py b/test/functional/feature_filelock.py
index b56ffe179f..e4ceb62c94 100755
--- a/test/functional/feature_filelock.py
+++ b/test/functional/feature_filelock.py
@@ -15,7 +15,7 @@ class FilelockTest(BitcoinTestFramework):
def setup_network(self):
self.add_nodes(self.num_nodes, extra_args=None)
- self.nodes[0].start([])
+ self.nodes[0].start(['-wallet='])
self.nodes[0].wait_for_rpc_connection()
def run_test(self):
@@ -30,7 +30,7 @@ class FilelockTest(BitcoinTestFramework):
wallet_dir = os.path.join(datadir, 'wallets')
self.log.info("Check that we can't start a second bitcoind instance using the same wallet")
expected_msg = "Error: Error initializing wallet database environment"
- self.nodes[1].assert_start_raises_init_error(extra_args=['-walletdir={}'.format(wallet_dir), '-noserver'], expected_msg=expected_msg, match=ErrorMatch.PARTIAL_REGEX)
+ self.nodes[1].assert_start_raises_init_error(extra_args=['-walletdir={}'.format(wallet_dir), '-wallet=', '-noserver'], expected_msg=expected_msg, match=ErrorMatch.PARTIAL_REGEX)
if __name__ == '__main__':
FilelockTest().main()
diff --git a/test/functional/feature_notifications.py b/test/functional/feature_notifications.py
index 3497b49a19..20020c3237 100755
--- a/test/functional/feature_notifications.py
+++ b/test/functional/feature_notifications.py
@@ -18,7 +18,7 @@ from test_framework.util import (
# Windows disallow control characters (0-31) and /\?%:|"<>
FILE_CHAR_START = 32 if os.name == 'nt' else 1
FILE_CHAR_END = 128
-FILE_CHAR_BLOCKLIST = '/\\?%*:|"<>' if os.name == 'nt' else '/'
+FILE_CHARS_DISALLOWED = '/\\?%*:|"<>' if os.name == 'nt' else '/'
def notify_outputname(walletname, txid):
@@ -31,7 +31,7 @@ class NotificationsTest(BitcoinTestFramework):
self.setup_clean_chain = True
def setup_network(self):
- self.wallet = ''.join(chr(i) for i in range(FILE_CHAR_START, FILE_CHAR_END) if chr(i) not in FILE_CHAR_BLOCKLIST)
+ self.wallet = ''.join(chr(i) for i in range(FILE_CHAR_START, FILE_CHAR_END) if chr(i) not in FILE_CHARS_DISALLOWED)
self.alertnotify_dir = os.path.join(self.options.tmpdir, "alertnotify")
self.blocknotify_dir = os.path.join(self.options.tmpdir, "blocknotify")
self.walletnotify_dir = os.path.join(self.options.tmpdir, "walletnotify")
diff --git a/test/functional/feature_pruning.py b/test/functional/feature_pruning.py
index db408ab67a..df54ae777b 100755
--- a/test/functional/feature_pruning.py
+++ b/test/functional/feature_pruning.py
@@ -81,16 +81,16 @@ class PruneTest(BitcoinTestFramework):
# Create nodes 0 and 1 to mine.
# Create node 2 to test pruning.
- self.full_node_default_args = ["-maxreceivebuffer=20000", "-checkblocks=5"]
+ self.full_node_default_args = ["-maxreceivebuffer=20000", "-checkblocks=5", "-wallet="]
# Create nodes 3 and 4 to test manual pruning (they will be re-started with manual pruning later)
# Create nodes 5 to test wallet in prune mode, but do not connect
self.extra_args = [
self.full_node_default_args,
self.full_node_default_args,
- ["-maxreceivebuffer=20000", "-prune=550"],
- ["-maxreceivebuffer=20000"],
- ["-maxreceivebuffer=20000"],
- ["-prune=550"],
+ ["-wallet=", "-maxreceivebuffer=20000", "-prune=550"],
+ ["-wallet=", "-maxreceivebuffer=20000"],
+ ["-wallet=", "-maxreceivebuffer=20000"],
+ ["-wallet=", "-prune=550"],
]
self.rpc_timeout = 120
diff --git a/test/functional/feature_signet.py b/test/functional/feature_signet.py
new file mode 100755
index 0000000000..a0e7f3ee6e
--- /dev/null
+++ b/test/functional/feature_signet.py
@@ -0,0 +1,70 @@
+#!/usr/bin/env python3
+# Copyright (c) 2019-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 basic signet functionality"""
+
+from decimal import Decimal
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import assert_equal
+
+signet_blocks = [
+ '00000020f61eee3b63a380a477a063af32b2bbc97c9ff9f01f2c4225e973988108000000f575c83235984e7dc4afc1f30944c170462e84437ab6f2d52e16878a79e4678bd1914d5fae77031eccf4070001010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025151feffffff0200f2052a010000001600149243f727dd5343293eb83174324019ec16c2630f0000000000000000776a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf94c4fecc7daa2490047304402205e423a8754336ca99dbe16509b877ef1bf98d008836c725005b3c787c41ebe46022047246e4467ad7cc7f1ad98662afcaf14c115e0095a227c7b05c5182591c23e7e01000120000000000000000000000000000000000000000000000000000000000000000000000000',
+ '00000020533b53ded9bff4adc94101d32400a144c54edc5ed492a3b26c63b2d686000000b38fef50592017cfafbcab88eb3d9cf50b2c801711cad8299495d26df5e54812e7914d5fae77031ecfdd0b0001010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025251feffffff0200f2052a01000000160014fd09839740f0e0b4fc6d5e2527e4022aa9b89dfa0000000000000000776a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf94c4fecc7daa24900473044022031d64a1692cdad1fc0ced69838169fe19ae01be524d831b95fcf5ea4e6541c3c02204f9dea0801df8b4d0cd0857c62ab35c6c25cc47c930630dc7fe723531daa3e9b01000120000000000000000000000000000000000000000000000000000000000000000000000000',
+ '000000202960f3752f0bfa8858a3e333294aedc7808025e868c9dc03e71d88bb320000007765fcd3d5b4966beb338bba2675dc2cf2ad28d4ad1d83bdb6f286e7e27ac1f807924d5fae77031e81d60b0001010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025351feffffff0200f2052a010000001600141e5fb426042692ae0e87c070e78c39307a5661c20000000000000000776a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf94c4fecc7daa2490047304402205de93694763a42954865bcf1540cb82958bc62d0ec4eee02070fb7937cd037f4022067f333753bce47b10bc25eb6e1f311482e994c862a7e0b2d41ab1c8679fd1b1101000120000000000000000000000000000000000000000000000000000000000000000000000000',
+ '00000020b06443a13ae1d3d50faef5ecad38c6818194dc46abca3e972e2aacdae800000069a5829097e80fee00ac49a56ea9f82d741a6af84d32b3bc455cf31871e2a8ac27924d5fae77031e9c91050001010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025451feffffff0200f2052a0100000016001430db2f8225dcf7751361ab38735de08190318cb70000000000000000776a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf94c4fecc7daa2490047304402200936f5f9872f6df5dd242026ad52241a68423f7f682e79169a8d85a374eab9b802202cd2979c48b321b3453e65e8f92460db3fca93cbea8539b450c959f4fbe630c601000120000000000000000000000000000000000000000000000000000000000000000000000000',
+ '000000207ed403758a4f228a1939418a155e2ebd4ae6b26e5ffd0ae433123f7694010000542e80b609c5bc58af5bdf492e26d4f60cd43a3966c2e063c50444c29b3757a636924d5fae77031ee8601d0001010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025551feffffff0200f2052a01000000160014edc207e014df34fa3885dff97d1129d356e1186a0000000000000000776a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf94c4fecc7daa24900473044022021a3656609f85a66a2c5672ed9322c2158d57251040d2716ed202a1fe14f0c12022057d68bc6611f7a9424a7e00bbf3e27e6ae6b096f60bac624a094bc97a59aa1ff01000120000000000000000000000000000000000000000000000000000000000000000000000000',
+ '000000205bea0a88d1422c3df08d766ad72df95084d0700e6f873b75dd4e986c7703000002b57516d33ed60c2bdd9f93d6d5614083324c837e68e5ba6e04287a7285633585924d5fae77031ed171960001010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025651feffffff0200f2052a010000001600143ae612599cf96f2442ce572633e0251116eaa52f0000000000000000776a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf94c4fecc7daa24900473044022059a7c54de76bfdbb1dd44c78ea2dbd2bb4e97f4abad38965f41e76433e56423c022054bf17f04fe17415c0141f60eebd2b839200f574d8ad8d55a0917b92b0eb913401000120000000000000000000000000000000000000000000000000000000000000000000000000',
+ '00000020daf3b60d374b19476461f97540498dcfa2eb7016238ec6b1d022f82fb60100007a7ae65b53cb988c2ec92d2384996713821d5645ffe61c9acea60da75cd5edfa1a944d5fae77031e9dbb050001010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025751feffffff0200f2052a01000000160014ef2dceae02e35f8137de76768ae3345d99ca68860000000000000000776a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf94c4fecc7daa2490047304402202b3f946d6447f9bf17d00f3696cede7ee70b785495e5498274ee682a493befd5022045fc0bcf9332243168b5d35507175f9f374a8eba2336873885d12aada67ea5f601000120000000000000000000000000000000000000000000000000000000000000000000000000',
+ '00000020457cc5f3c2e1a5655bc20e20e48d33e1b7ea68786c614032b5c518f0b6000000541f36942d82c6e7248275ff15c8933487fbe1819c67a9ecc0f4b70bb7e6cf672a944d5fae77031e8f39860001010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025851feffffff0200f2052a0100000016001472a27906947c06d034b38ba2fa13c6391a4832790000000000000000776a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf94c4fecc7daa2490047304402202d62805ce60cbd60591f97f949b5ea5bd7e2307bcde343e6ea8394da92758e72022053a25370b0aa20da100189b7899a8f8675a0fdc60e38ece6b8a4f98edd94569e01000120000000000000000000000000000000000000000000000000000000000000000000000000',
+ '00000020a2eb61eb4f3831baa3a3363e1b42db4462663f756f07423e81ed30322102000077224de7dea0f8d0ec22b1d2e2e255f0a987b96fe7200e1a2e6373f48a2f5b7894954d5fae77031e36867e0001010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025951feffffff0200f2052a01000000160014aa0ad9f26801258382e0734dceec03a4a75f60240000000000000000776a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf94c4fecc7daa2490047304402206fa0d59990eed369bd7375767c9a6c9369fae209152b8674e520da270605528c0220749eed3b12dbe3f583f505d21803e4aef59c8e24c5831951eafa4f15a8f92c4e01000120000000000000000000000000000000000000000000000000000000000000000000000000',
+ '00000020a868e8514be5e46dabd6a122132f423f36a43b716a40c394e2a8d063e1010000f4c6c717e99d800c699c25a2006a75a0c5c09f432a936f385e6fce139cdbd1a5e9964d5fae77031e7d026e0001010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025a51feffffff0200f2052a01000000160014aaa671c82b138e3b8f510cd801e5f2bd0aa305940000000000000000776a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf94c4fecc7daa24900473044022042309f4c3c7a1a2ac8c24f890f962df1c0086cec10be0868087cfc427520cb2702201dafee8911c269b7e786e242045bb57cef3f5b0f177010c6159abae42f646cc501000120000000000000000000000000000000000000000000000000000000000000000000000000',
+]
+
+
+class SignetBasicTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.chain = "signet"
+ self.num_nodes = 6
+ self.setup_clean_chain = True
+ shared_args1 = ["-signetchallenge=51"] # OP_TRUE
+ shared_args2 = [] # default challenge
+ # we use the exact same challenge except we do it as a 2-of-2, which means it should fail
+ shared_args3 = ["-signetchallenge=522103ad5e0edad18cb1f0fc0d28a3d4f1f3e445640337489abb10404f2d1e086be430210359ef5021964fe22d6f8e05b2463c9540ce96883fe3b278760f048f5189f2e6c452ae"]
+
+ self.extra_args = [
+ shared_args1, shared_args1,
+ shared_args2, shared_args2,
+ shared_args3, shared_args3,
+ ]
+
+ def run_test(self):
+ self.log.info("basic tests using OP_TRUE challenge")
+
+ self.log.info('getmininginfo')
+ mining_info = self.nodes[0].getmininginfo()
+ assert_equal(mining_info['blocks'], 0)
+ assert_equal(mining_info['chain'], 'signet')
+ assert 'currentblocktx' not in mining_info
+ assert 'currentblockweight' not in mining_info
+ assert_equal(mining_info['networkhashps'], Decimal('0'))
+ assert_equal(mining_info['pooledtx'], 0)
+
+ self.nodes[0].generate(1)
+
+ self.log.info("pregenerated signet blocks check")
+
+ height = 0
+ for block in signet_blocks:
+ assert_equal(self.nodes[2].submitblock(block), None)
+ height += 1
+ assert_equal(self.nodes[2].getblockcount(), height)
+
+ self.log.info("pregenerated signet blocks check (incompatible solution)")
+
+ assert_equal(self.nodes[4].submitblock(signet_blocks[0]), 'bad-signet-blksig')
+
+
+if __name__ == '__main__':
+ SignetBasicTest().main()
diff --git a/test/functional/interface_bitcoin_cli.py b/test/functional/interface_bitcoin_cli.py
index 80003aca0d..81c007c27b 100755
--- a/test/functional/interface_bitcoin_cli.py
+++ b/test/functional/interface_bitcoin_cli.py
@@ -71,7 +71,14 @@ class TestBitcoinCli(BitcoinTestFramework):
assert_equal(cli_get_info['blocks'], blockchain_info['blocks'])
assert_equal(cli_get_info['headers'], blockchain_info['headers'])
assert_equal(cli_get_info['timeoffset'], network_info['timeoffset'])
- assert_equal(cli_get_info['connections'], network_info['connections'])
+ assert_equal(
+ cli_get_info['connections'],
+ {
+ 'in': network_info['connections_in'],
+ 'out': network_info['connections_out'],
+ 'total': network_info['connections']
+ }
+ )
assert_equal(cli_get_info['proxy'], network_info['networks'][0]['proxy'])
assert_equal(cli_get_info['difficulty'], blockchain_info['difficulty'])
assert_equal(cli_get_info['chain'], blockchain_info['chain'])
diff --git a/test/functional/interface_rpc.py b/test/functional/interface_rpc.py
index ac2e73fc5c..9c877aaeae 100755
--- a/test/functional/interface_rpc.py
+++ b/test/functional/interface_rpc.py
@@ -45,7 +45,7 @@ class RPCInterfaceTest(BitcoinTestFramework):
# work fine.
{"method": "invalidmethod", "id": 2},
# Another call that should succeed.
- {"method": "getbestblockhash", "id": 3},
+ {"method": "getblockhash", "id": 3, "params": [0]},
])
result_by_id = {}
diff --git a/test/functional/interface_zmq.py b/test/functional/interface_zmq.py
index ef4780cacb..17032a3b83 100755
--- a/test/functional/interface_zmq.py
+++ b/test/functional/interface_zmq.py
@@ -5,13 +5,24 @@
"""Test the ZMQ notification interface."""
import struct
-from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE
+from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE, ADDRESS_BCRT1_P2WSH_OP_TRUE
+from test_framework.blocktools import create_block, create_coinbase, add_witness_commitment
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.messages import CTransaction, hash256
-from test_framework.util import assert_equal, connect_nodes
+from test_framework.messages import CTransaction, hash256, FromHex
+from test_framework.util import (
+ assert_equal,
+ connect_nodes,
+ assert_raises_rpc_error,
+)
from io import BytesIO
from time import sleep
+# Test may be skipped and not have zmq installed
+try:
+ import zmq
+except ImportError:
+ pass
+
def hash256_reversed(byte_str):
return hash256(byte_str)[::-1]
@@ -21,7 +32,6 @@ class ZMQSubscriber:
self.socket = socket
self.topic = topic
- import zmq
self.socket.setsockopt(zmq.SUBSCRIBE, self.topic)
def receive(self):
@@ -33,6 +43,22 @@ class ZMQSubscriber:
self.sequence += 1
return body
+ def receive_sequence(self):
+ topic, body, seq = self.socket.recv_multipart()
+ # Topic should match the subscriber topic.
+ assert_equal(topic, self.topic)
+ # Sequence should be incremental.
+ assert_equal(struct.unpack('<I', seq)[-1], self.sequence)
+ self.sequence += 1
+ hash = body[:32].hex()
+ label = chr(body[32])
+ mempool_sequence = None if len(body) != 32+1+8 else struct.unpack("<Q", body[32+1:])[0]
+ if mempool_sequence is not None:
+ assert label == "A" or label == "R"
+ else:
+ assert label == "D" or label == "C"
+ return (hash, label, mempool_sequence)
+
class ZMQTest (BitcoinTestFramework):
def set_test_params(self):
@@ -43,10 +69,11 @@ class ZMQTest (BitcoinTestFramework):
self.skip_if_no_bitcoind_zmq()
def run_test(self):
- import zmq
self.ctx = zmq.Context()
try:
self.test_basic()
+ self.test_sequence()
+ self.test_mempool_sync()
self.test_reorg()
finally:
# Destroy the ZMQ context.
@@ -54,7 +81,6 @@ class ZMQTest (BitcoinTestFramework):
self.ctx.destroy(linger=None)
def test_basic(self):
- import zmq
# Invalid zmq arguments don't take down the node, see #17185.
self.restart_node(0, ["-zmqpubrawtx=foo", "-zmqpubhashtx=bar"])
@@ -146,7 +172,6 @@ class ZMQTest (BitcoinTestFramework):
self.log.info("Skipping reorg test because wallet is disabled")
return
- import zmq
address = 'tcp://127.0.0.1:28333'
services = [b"hashblock", b"hashtx"]
@@ -177,8 +202,8 @@ class ZMQTest (BitcoinTestFramework):
assert_equal(hashtx.receive().hex(), payment_txid)
assert_equal(hashtx.receive().hex(), disconnect_cb)
- # Generate 2 blocks in nodes[1]
- connect_blocks = self.nodes[1].generatetoaddress(2, ADDRESS_BCRT1_UNSPENDABLE)
+ # Generate 2 blocks in nodes[1] to a different address to ensure split
+ connect_blocks = self.nodes[1].generatetoaddress(2, ADDRESS_BCRT1_P2WSH_OP_TRUE)
# nodes[0] will reorg chain after connecting back nodes[1]
connect_nodes(self.nodes[0], 1)
@@ -204,5 +229,282 @@ class ZMQTest (BitcoinTestFramework):
# And the current tip
assert_equal(hashtx.receive().hex(), self.nodes[1].getblock(connect_blocks[0])["tx"][0])
+ def test_sequence(self):
+ """
+ Sequence zmq notifications give every blockhash and txhash in order
+ of processing, regardless of IBD, re-orgs, etc.
+ Format of messages:
+ <32-byte hash>C : Blockhash connected
+ <32-byte hash>D : Blockhash disconnected
+ <32-byte hash>R<8-byte LE uint> : Transactionhash removed from mempool for non-block inclusion reason
+ <32-byte hash>A<8-byte LE uint> : Transactionhash added mempool
+ """
+ self.log.info("Testing 'sequence' publisher")
+ address = 'tcp://127.0.0.1:28333'
+ socket = self.ctx.socket(zmq.SUB)
+ socket.set(zmq.RCVTIMEO, 60000)
+ seq = ZMQSubscriber(socket, b'sequence')
+
+ self.restart_node(0, ['-zmqpub%s=%s' % (seq.topic.decode(), address)])
+ socket.connect(address)
+ # Relax so that the subscriber is ready before publishing zmq messages
+ sleep(0.2)
+
+ # Mempool sequence number starts at 1
+ seq_num = 1
+
+ # Generate 1 block in nodes[0] and receive all notifications
+ dc_block = self.nodes[0].generatetoaddress(1, ADDRESS_BCRT1_UNSPENDABLE)[0]
+
+ # Note: We are not notified of any block transactions, coinbase or mined
+ assert_equal((self.nodes[0].getbestblockhash(), "C", None), seq.receive_sequence())
+
+ # Generate 2 blocks in nodes[1] to a different address to ensure a chain split
+ self.nodes[1].generatetoaddress(2, ADDRESS_BCRT1_P2WSH_OP_TRUE)
+
+ # nodes[0] will reorg chain after connecting back nodes[1]
+ connect_nodes(self.nodes[0], 1)
+
+ # Then we receive all block (dis)connect notifications for the 2 block reorg
+ assert_equal((dc_block, "D", None), seq.receive_sequence())
+ block_count = self.nodes[1].getblockcount()
+ assert_equal((self.nodes[1].getblockhash(block_count-1), "C", None), seq.receive_sequence())
+ assert_equal((self.nodes[1].getblockhash(block_count), "C", None), seq.receive_sequence())
+
+ # Rest of test requires wallet functionality
+ if self.is_wallet_compiled():
+ self.log.info("Wait for tx from second node")
+ payment_txid = self.nodes[1].sendtoaddress(address=self.nodes[0].getnewaddress(), amount=5.0, replaceable=True)
+ self.sync_all()
+ self.log.info("Testing sequence notifications with mempool sequence values")
+
+ # Should receive the broadcasted txid.
+ assert_equal((payment_txid, "A", seq_num), seq.receive_sequence())
+ seq_num += 1
+
+ self.log.info("Testing RBF notification")
+ # Replace it to test eviction/addition notification
+ rbf_info = self.nodes[1].bumpfee(payment_txid)
+ self.sync_all()
+ assert_equal((payment_txid, "R", seq_num), seq.receive_sequence())
+ seq_num += 1
+ assert_equal((rbf_info["txid"], "A", seq_num), seq.receive_sequence())
+ seq_num += 1
+
+ # Doesn't get published when mined, make a block and tx to "flush" the possibility
+ # though the mempool sequence number does go up by the number of transactions
+ # removed from the mempool by the block mining it.
+ mempool_size = len(self.nodes[0].getrawmempool())
+ c_block = self.nodes[0].generatetoaddress(1, ADDRESS_BCRT1_UNSPENDABLE)[0]
+ self.sync_all()
+ # Make sure the number of mined transactions matches the number of txs out of mempool
+ mempool_size_delta = mempool_size - len(self.nodes[0].getrawmempool())
+ assert_equal(len(self.nodes[0].getblock(c_block)["tx"])-1, mempool_size_delta)
+ seq_num += mempool_size_delta
+ payment_txid_2 = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1.0)
+ self.sync_all()
+ assert_equal((c_block, "C", None), seq.receive_sequence())
+ assert_equal((payment_txid_2, "A", seq_num), seq.receive_sequence())
+ seq_num += 1
+
+ # Spot check getrawmempool results that they only show up when asked for
+ assert type(self.nodes[0].getrawmempool()) is list
+ assert type(self.nodes[0].getrawmempool(mempool_sequence=False)) is list
+ assert "mempool_sequence" not in self.nodes[0].getrawmempool(verbose=True)
+ assert_raises_rpc_error(-8, "Verbose results cannot contain mempool sequence values.", self.nodes[0].getrawmempool, True, True)
+ assert_equal(self.nodes[0].getrawmempool(mempool_sequence=True)["mempool_sequence"], seq_num)
+
+ self.log.info("Testing reorg notifications")
+ # Manually invalidate the last block to test mempool re-entry
+ # N.B. This part could be made more lenient in exact ordering
+ # since it greatly depends on inner-workings of blocks/mempool
+ # during "deep" re-orgs. Probably should "re-construct"
+ # blockchain/mempool state from notifications instead.
+ block_count = self.nodes[0].getblockcount()
+ best_hash = self.nodes[0].getbestblockhash()
+ self.nodes[0].invalidateblock(best_hash)
+ sleep(2) # Bit of room to make sure transaction things happened
+
+ # Make sure getrawmempool mempool_sequence results aren't "queued" but immediately reflective
+ # of the time they were gathered.
+ assert self.nodes[0].getrawmempool(mempool_sequence=True)["mempool_sequence"] > seq_num
+
+ assert_equal((best_hash, "D", None), seq.receive_sequence())
+ assert_equal((rbf_info["txid"], "A", seq_num), seq.receive_sequence())
+ seq_num += 1
+
+ # Other things may happen but aren't wallet-deterministic so we don't test for them currently
+ self.nodes[0].reconsiderblock(best_hash)
+ self.nodes[1].generatetoaddress(1, ADDRESS_BCRT1_UNSPENDABLE)
+ self.sync_all()
+
+ self.log.info("Evict mempool transaction by block conflict")
+ orig_txid = self.nodes[0].sendtoaddress(address=self.nodes[0].getnewaddress(), amount=1.0, replaceable=True)
+
+ # More to be simply mined
+ more_tx = []
+ for _ in range(5):
+ more_tx.append(self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 0.1))
+
+ raw_tx = self.nodes[0].getrawtransaction(orig_txid)
+ bump_info = self.nodes[0].bumpfee(orig_txid)
+ # Mine the pre-bump tx
+ block = create_block(int(self.nodes[0].getbestblockhash(), 16), create_coinbase(self.nodes[0].getblockcount()+1))
+ tx = FromHex(CTransaction(), raw_tx)
+ block.vtx.append(tx)
+ for txid in more_tx:
+ tx = FromHex(CTransaction(), self.nodes[0].getrawtransaction(txid))
+ block.vtx.append(tx)
+ add_witness_commitment(block)
+ block.solve()
+ assert_equal(self.nodes[0].submitblock(block.serialize().hex()), None)
+ tip = self.nodes[0].getbestblockhash()
+ assert_equal(int(tip, 16), block.sha256)
+ orig_txid_2 = self.nodes[0].sendtoaddress(address=self.nodes[0].getnewaddress(), amount=1.0, replaceable=True)
+
+ # Flush old notifications until evicted tx original entry
+ (hash_str, label, mempool_seq) = seq.receive_sequence()
+ while hash_str != orig_txid:
+ (hash_str, label, mempool_seq) = seq.receive_sequence()
+ mempool_seq += 1
+
+ # Added original tx
+ assert_equal(label, "A")
+ # More transactions to be simply mined
+ for i in range(len(more_tx)):
+ assert_equal((more_tx[i], "A", mempool_seq), seq.receive_sequence())
+ mempool_seq += 1
+ # Bumped by rbf
+ assert_equal((orig_txid, "R", mempool_seq), seq.receive_sequence())
+ mempool_seq += 1
+ assert_equal((bump_info["txid"], "A", mempool_seq), seq.receive_sequence())
+ mempool_seq += 1
+ # Conflict announced first, then block
+ assert_equal((bump_info["txid"], "R", mempool_seq), seq.receive_sequence())
+ mempool_seq += 1
+ assert_equal((tip, "C", None), seq.receive_sequence())
+ mempool_seq += len(more_tx)
+ # Last tx
+ assert_equal((orig_txid_2, "A", mempool_seq), seq.receive_sequence())
+ mempool_seq += 1
+ self.nodes[0].generatetoaddress(1, ADDRESS_BCRT1_UNSPENDABLE)
+ self.sync_all() # want to make sure we didn't break "consensus" for other tests
+
+ def test_mempool_sync(self):
+ """
+ Use sequence notification plus getrawmempool sequence results to "sync mempool"
+ """
+ if not self.is_wallet_compiled():
+ self.log.info("Skipping mempool sync test")
+ return
+
+ self.log.info("Testing 'mempool sync' usage of sequence notifier")
+ address = 'tcp://127.0.0.1:28333'
+ socket = self.ctx.socket(zmq.SUB)
+ socket.set(zmq.RCVTIMEO, 60000)
+ seq = ZMQSubscriber(socket, b'sequence')
+
+ self.restart_node(0, ['-zmqpub%s=%s' % (seq.topic.decode(), address)])
+ connect_nodes(self.nodes[0], 1)
+ socket.connect(address)
+ # Relax so that the subscriber is ready before publishing zmq messages
+ sleep(0.2)
+
+ # In-memory counter, should always start at 1
+ next_mempool_seq = self.nodes[0].getrawmempool(mempool_sequence=True)["mempool_sequence"]
+ assert_equal(next_mempool_seq, 1)
+
+ # Some transactions have been happening but we aren't consuming zmq notifications yet
+ # or we lost a ZMQ message somehow and want to start over
+ txids = []
+ num_txs = 5
+ for _ in range(num_txs):
+ txids.append(self.nodes[1].sendtoaddress(address=self.nodes[0].getnewaddress(), amount=1.0, replaceable=True))
+ self.sync_all()
+
+ # 1) Consume backlog until we get a mempool sequence number
+ (hash_str, label, zmq_mem_seq) = seq.receive_sequence()
+ while zmq_mem_seq is None:
+ (hash_str, label, zmq_mem_seq) = seq.receive_sequence()
+
+ assert label == "A" or label == "R"
+ assert hash_str is not None
+
+ # 2) We need to "seed" our view of the mempool
+ mempool_snapshot = self.nodes[0].getrawmempool(mempool_sequence=True)
+ mempool_view = set(mempool_snapshot["txids"])
+ get_raw_seq = mempool_snapshot["mempool_sequence"]
+ assert_equal(get_raw_seq, 6)
+ # Snapshot may be too old compared to zmq message we read off latest
+ while zmq_mem_seq >= get_raw_seq:
+ sleep(2)
+ mempool_snapshot = self.nodes[0].getrawmempool(mempool_sequence=True)
+ mempool_view = set(mempool_snapshot["txids"])
+ get_raw_seq = mempool_snapshot["mempool_sequence"]
+
+ # Things continue to happen in the "interim" while waiting for snapshot results
+ # We have node 0 do all these to avoid p2p races with RBF announcements
+ for _ in range(num_txs):
+ txids.append(self.nodes[0].sendtoaddress(address=self.nodes[0].getnewaddress(), amount=0.1, replaceable=True))
+ self.nodes[0].bumpfee(txids[-1])
+ self.sync_all()
+ self.nodes[0].generatetoaddress(1, ADDRESS_BCRT1_UNSPENDABLE)
+ final_txid = self.nodes[0].sendtoaddress(address=self.nodes[0].getnewaddress(), amount=0.1, replaceable=True)
+
+ # 3) Consume ZMQ backlog until we get to "now" for the mempool snapshot
+ while True:
+ if zmq_mem_seq == get_raw_seq - 1:
+ break
+ (hash_str, label, mempool_sequence) = seq.receive_sequence()
+ if mempool_sequence is not None:
+ zmq_mem_seq = mempool_sequence
+ if zmq_mem_seq > get_raw_seq:
+ raise Exception("We somehow jumped mempool sequence numbers! zmq_mem_seq: {} > get_raw_seq: {}".format(zmq_mem_seq, get_raw_seq))
+
+ # 4) Moving forward, we apply the delta to our local view
+ # remaining txs(5) + 1 rbf(A+R) + 1 block connect + 1 final tx
+ expected_sequence = get_raw_seq
+ r_gap = 0
+ for _ in range(num_txs + 2 + 1 + 1):
+ (hash_str, label, mempool_sequence) = seq.receive_sequence()
+ if mempool_sequence is not None:
+ if mempool_sequence != expected_sequence:
+ # Detected "R" gap, means this a conflict eviction, and mempool tx are being evicted before its
+ # position in the incoming block message "C"
+ if label == "R":
+ assert mempool_sequence > expected_sequence
+ r_gap += mempool_sequence - expected_sequence
+ else:
+ raise Exception("WARNING: txhash has unexpected mempool sequence value: {} vs expected {}".format(mempool_sequence, expected_sequence))
+ if label == "A":
+ assert hash_str not in mempool_view
+ mempool_view.add(hash_str)
+ expected_sequence = mempool_sequence + 1
+ elif label == "R":
+ assert hash_str in mempool_view
+ mempool_view.remove(hash_str)
+ expected_sequence = mempool_sequence + 1
+ elif label == "C":
+ # (Attempt to) remove all txids from known block connects
+ block_txids = self.nodes[0].getblock(hash_str)["tx"][1:]
+ for txid in block_txids:
+ if txid in mempool_view:
+ expected_sequence += 1
+ mempool_view.remove(txid)
+ expected_sequence -= r_gap
+ r_gap = 0
+ elif label == "D":
+ # Not useful for mempool tracking per se
+ continue
+ else:
+ raise Exception("Unexpected ZMQ sequence label!")
+
+ assert_equal(self.nodes[0].getrawmempool(), [final_txid])
+ assert_equal(self.nodes[0].getrawmempool(mempool_sequence=True)["mempool_sequence"], expected_sequence)
+
+ # 5) If you miss a zmq/mempool sequence number, go back to step (2)
+
+ self.nodes[0].generatetoaddress(1, ADDRESS_BCRT1_UNSPENDABLE)
+
if __name__ == '__main__':
ZMQTest().main()
diff --git a/test/functional/mempool_accept.py b/test/functional/mempool_accept.py
index 6df8f1c3ec..57a059b7f7 100755
--- a/test/functional/mempool_accept.py
+++ b/test/functional/mempool_accept.py
@@ -4,6 +4,7 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test mempool acceptance of raw transactions."""
+from decimal import Decimal
from io import BytesIO
import math
@@ -91,20 +92,22 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0)))
txid_0 = tx.rehash()
self.check_mempool_result(
- result_expected=[{'txid': txid_0, 'allowed': True}],
+ result_expected=[{'txid': txid_0, 'allowed': True, 'vsize': tx.get_vsize(), 'fees': {'base': Decimal(str(fee))}}],
rawtxs=[raw_tx_0],
)
self.log.info('A final transaction not in the mempool')
coin = coins.pop() # Pick a random coin(base) to spend
+ output_amount = 0.025
raw_tx_final = node.signrawtransactionwithwallet(node.createrawtransaction(
inputs=[{'txid': coin['txid'], 'vout': coin['vout'], "sequence": 0xffffffff}], # SEQUENCE_FINAL
- outputs=[{node.getnewaddress(): 0.025}],
+ outputs=[{node.getnewaddress(): output_amount}],
locktime=node.getblockcount() + 2000, # Can be anything
))['hex']
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_final)))
+ fee_expected = int(coin['amount']) - output_amount
self.check_mempool_result(
- result_expected=[{'txid': tx.rehash(), 'allowed': True}],
+ result_expected=[{'txid': tx.rehash(), 'allowed': True, 'vsize': tx.get_vsize(), 'fees': {'base': Decimal(str(fee_expected))}}],
rawtxs=[tx.serialize().hex()],
maxfeerate=0,
)
@@ -127,7 +130,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0)))
txid_0 = tx.rehash()
self.check_mempool_result(
- result_expected=[{'txid': txid_0, 'allowed': True}],
+ result_expected=[{'txid': txid_0, 'allowed': True, 'vsize': tx.get_vsize(), 'fees': {'base': Decimal(str(2 * fee))}}],
rawtxs=[raw_tx_0],
)
@@ -187,7 +190,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference)))
# Reference tx should be valid on itself
self.check_mempool_result(
- result_expected=[{'txid': tx.rehash(), 'allowed': True}],
+ result_expected=[{'txid': tx.rehash(), 'allowed': True, 'vsize': tx.get_vsize(), 'fees': { 'base': Decimal(str(0.1 - 0.05))}}],
rawtxs=[tx.serialize().hex()],
maxfeerate=0,
)
diff --git a/test/functional/mempool_compatibility.py b/test/functional/mempool_compatibility.py
index ad29d389e2..fd3dd47e2d 100755
--- a/test/functional/mempool_compatibility.py
+++ b/test/functional/mempool_compatibility.py
@@ -31,7 +31,7 @@ class MempoolCompatibilityTest(BitcoinTestFramework):
150200, # oldest version supported by the test framework
None,
])
- self.start_nodes()
+ self.start_nodes([[], ["-wallet="]])
self.import_deterministic_coinbase_privkeys()
def run_test(self):
diff --git a/test/functional/p2p_blocksonly.py b/test/functional/p2p_blocksonly.py
index 2bc44f81f6..646baa1550 100755
--- a/test/functional/p2p_blocksonly.py
+++ b/test/functional/p2p_blocksonly.py
@@ -57,29 +57,33 @@ class P2PBlocksOnly(BitcoinTestFramework):
tx_relay_peer.wait_for_tx(txid)
assert_equal(self.nodes[0].getmempoolinfo()['size'], 1)
- self.log.info('Check that txs from forcerelay peers are not rejected and relayed to others')
- self.log.info("Restarting node 0 with forcerelay permission and blocksonly")
- self.restart_node(0, ["-persistmempool=0", "-whitelist=127.0.0.1", "-whitelistforcerelay", "-blocksonly"])
+ self.log.info('Check that txs from peers with relay-permission are not rejected and relayed to others')
+ self.log.info("Restarting node 0 with relay permission and blocksonly")
+ self.restart_node(0, ["-persistmempool=0", "-whitelist=relay@127.0.0.1", "-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', 'download'])
+ assert_equal(peer_1_info['permissions'], ['relay'])
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', 'download'])
+ assert_equal(peer_2_info['permissions'], ['relay'])
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 forcerelay first_peer is relayed to others (ie.second_peer)')
+ self.log.info('Check that the tx from first_peer with relay-permission is relayed to others (ie.second_peer)')
with self.nodes[0].assert_debug_log(["received getdata"]):
+ # Note that normally, first_peer would never send us transactions since we're a blocksonly node.
+ # By activating blocksonly, we explicitly tell our peers that they should not send us transactions,
+ # and Bitcoin Core respects that choice and will not send transactions.
+ # But if, for some reason, first_peer decides to relay transactions to us anyway, we should relay them to
+ # second_peer since we gave relay permission to first_peer.
+ # See https://github.com/bitcoin/bitcoin/issues/19943 for details.
first_peer.send_message(msg_tx(FromHex(CTransaction(), sigtx)))
- self.log.info('Check that the forcerelay peer is still connected after sending the transaction')
+ self.log.info('Check that the peer with relay-permission 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("Forcerelay peer's transaction is accepted and relayed")
+ self.log.info("Relay-permission peer's transaction is accepted and relayed")
if __name__ == '__main__':
diff --git a/test/functional/p2p_compactblocks.py b/test/functional/p2p_compactblocks.py
index fdae7fb68b..506b480039 100755
--- a/test/functional/p2p_compactblocks.py
+++ b/test/functional/p2p_compactblocks.py
@@ -188,28 +188,21 @@ class CompactBlocksTest(BitcoinTestFramework):
test_node.request_headers_and_sync(locator=[tip])
# Now try a SENDCMPCT message with too-high version
- sendcmpct = msg_sendcmpct()
- sendcmpct.version = preferred_version + 1
- sendcmpct.announce = True
- test_node.send_and_ping(sendcmpct)
+ test_node.send_and_ping(msg_sendcmpct(announce=True, version=preferred_version+1))
check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" not in p.last_message)
# Headers sync before next test.
test_node.request_headers_and_sync(locator=[tip])
# Now try a SENDCMPCT message with valid version, but announce=False
- sendcmpct.version = preferred_version
- sendcmpct.announce = False
- test_node.send_and_ping(sendcmpct)
+ test_node.send_and_ping(msg_sendcmpct(announce=False, version=preferred_version))
check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" not in p.last_message)
# Headers sync before next test.
test_node.request_headers_and_sync(locator=[tip])
# Finally, try a SENDCMPCT message with announce=True
- sendcmpct.version = preferred_version
- sendcmpct.announce = True
- test_node.send_and_ping(sendcmpct)
+ test_node.send_and_ping(msg_sendcmpct(announce=True, version=preferred_version))
check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" in p.last_message)
# Try one more time (no headers sync should be needed!)
@@ -220,23 +213,17 @@ class CompactBlocksTest(BitcoinTestFramework):
check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" in p.last_message)
# Try one more time, after sending a version-1, announce=false message.
- sendcmpct.version = preferred_version - 1
- sendcmpct.announce = False
- test_node.send_and_ping(sendcmpct)
+ test_node.send_and_ping(msg_sendcmpct(announce=False, version=preferred_version-1))
check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" in p.last_message)
# Now turn off announcements
- sendcmpct.version = preferred_version
- sendcmpct.announce = False
- test_node.send_and_ping(sendcmpct)
+ test_node.send_and_ping(msg_sendcmpct(announce=False, version=preferred_version))
check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" not in p.last_message and "headers" in p.last_message)
if old_node is not None:
# Verify that a peer using an older protocol version can receive
# announcements from this node.
- sendcmpct.version = preferred_version - 1
- sendcmpct.announce = True
- old_node.send_and_ping(sendcmpct)
+ old_node.send_and_ping(msg_sendcmpct(announce=True, version=preferred_version-1))
# Header sync
old_node.request_headers_and_sync(locator=[tip])
check_announcement_of_new_block(node, old_node, lambda p: "cmpctblock" in p.last_message)
@@ -729,11 +716,7 @@ class CompactBlocksTest(BitcoinTestFramework):
node = self.nodes[0]
tip = node.getbestblockhash()
peer.get_headers(locator=[int(tip, 16)], hashstop=0)
-
- msg = msg_sendcmpct()
- msg.version = peer.cmpct_version
- msg.announce = True
- peer.send_and_ping(msg)
+ peer.send_and_ping(msg_sendcmpct(announce=True, version=peer.cmpct_version))
def test_compactblock_reconstruction_multiple_peers(self, stalling_peer, delivery_peer):
node = self.nodes[0]
diff --git a/test/functional/p2p_feefilter.py b/test/functional/p2p_feefilter.py
index 0c07b56a69..ea066a984d 100755
--- a/test/functional/p2p_feefilter.py
+++ b/test/functional/p2p_feefilter.py
@@ -10,10 +10,7 @@ from test_framework.messages import MSG_TX, MSG_WTX, msg_feefilter
from test_framework.p2p import P2PInterface, p2p_lock
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal
-
-
-def hashToHex(hash):
- return format(hash, '064x')
+from test_framework.wallet import MiniWallet
class FeefilterConn(P2PInterface):
@@ -35,7 +32,7 @@ class TestP2PConn(P2PInterface):
def on_inv(self, message):
for i in message.inv:
if (i.type == MSG_TX) or (i.type == MSG_WTX):
- self.txinvs.append(hashToHex(i.hash))
+ self.txinvs.append('{:064x}'.format(i.hash))
def wait_for_invs_to_match(self, invs_expected):
invs_expected.sort()
@@ -61,9 +58,6 @@ class FeeFilterTest(BitcoinTestFramework):
"-whitelist=noban@127.0.0.1",
]] * self.num_nodes
- def skip_test_if_missing_module(self):
- self.skip_if_no_wallet()
-
def run_test(self):
self.test_feefilter_forcerelay()
self.test_feefilter()
@@ -83,12 +77,15 @@ class FeeFilterTest(BitcoinTestFramework):
def test_feefilter(self):
node1 = self.nodes[1]
node0 = self.nodes[0]
+ miniwallet = MiniWallet(node1)
+ # Add enough mature utxos to the wallet, so that all txs spend confirmed coins
+ miniwallet.generate(5)
+ node1.generate(100)
conn = self.nodes[0].add_p2p_connection(TestP2PConn())
self.log.info("Test txs paying 0.2 sat/byte are received by test connection")
- node1.settxfee(Decimal("0.00000200"))
- txids = [node1.sendtoaddress(node1.getnewaddress(), 1) for _ in range(3)]
+ txids = [miniwallet.send_self_transfer(fee_rate=Decimal('0.00000200'), from_node=node1)['wtxid'] for _ in range(3)]
conn.wait_for_invs_to_match(txids)
conn.clear_invs()
@@ -96,14 +93,12 @@ class FeeFilterTest(BitcoinTestFramework):
conn.send_and_ping(msg_feefilter(150))
self.log.info("Test txs paying 0.15 sat/byte are received by test connection")
- node1.settxfee(Decimal("0.00000150"))
- txids = [node1.sendtoaddress(node1.getnewaddress(), 1) for _ in range(3)]
+ txids = [miniwallet.send_self_transfer(fee_rate=Decimal('0.00000150'), from_node=node1)['wtxid'] for _ in range(3)]
conn.wait_for_invs_to_match(txids)
conn.clear_invs()
self.log.info("Test txs paying 0.1 sat/byte are no longer received by test connection")
- node1.settxfee(Decimal("0.00000100"))
- [node1.sendtoaddress(node1.getnewaddress(), 1) for _ in range(3)]
+ txids = [miniwallet.send_self_transfer(fee_rate=Decimal('0.00000100'), from_node=node1)['wtxid'] for _ in range(3)]
self.sync_mempools() # must be sure node 0 has received all txs
# Send one transaction from node0 that should be received, so that we
@@ -113,14 +108,13 @@ class FeeFilterTest(BitcoinTestFramework):
# to 35 entries in an inv, which means that when this next transaction
# is eligible for relay, the prior transactions from node1 are eligible
# as well.
- node0.settxfee(Decimal("0.00020000"))
- txids = [node0.sendtoaddress(node0.getnewaddress(), 1)]
+ txids = [miniwallet.send_self_transfer(fee_rate=Decimal('0.00020000'), from_node=node0)['wtxid'] for _ in range(3)]
conn.wait_for_invs_to_match(txids)
conn.clear_invs()
self.log.info("Remove fee filter and check txs are received again")
conn.send_and_ping(msg_feefilter(0))
- txids = [node1.sendtoaddress(node1.getnewaddress(), 1) for _ in range(3)]
+ txids = [miniwallet.send_self_transfer(fee_rate=Decimal('0.00020000'), from_node=node1)['wtxid'] for _ in range(3)]
conn.wait_for_invs_to_match(txids)
conn.clear_invs()
diff --git a/test/functional/p2p_getaddr_caching.py b/test/functional/p2p_getaddr_caching.py
index 6622ea9ec2..2b75ad5175 100755
--- a/test/functional/p2p_getaddr_caching.py
+++ b/test/functional/p2p_getaddr_caching.py
@@ -5,13 +5,8 @@
"""Test addr response caching"""
import time
-from test_framework.messages import (
- CAddress,
- NODE_NETWORK,
- NODE_WITNESS,
- msg_addr,
- msg_getaddr,
-)
+
+from test_framework.messages import msg_getaddr
from test_framework.p2p import (
P2PInterface,
p2p_lock
@@ -21,21 +16,9 @@ from test_framework.util import (
assert_equal,
)
+# As defined in net_processing.
MAX_ADDR_TO_SEND = 1000
-
-def gen_addrs(n):
- addrs = []
- for i in range(n):
- addr = CAddress()
- addr.time = int(time.time())
- addr.nServices = NODE_NETWORK | NODE_WITNESS
- # Use first octets to occupy different AddrMan buckets
- first_octet = i >> 8
- second_octet = i % 256
- addr.ip = "{}.{}.1.1".format(first_octet, second_octet)
- addr.port = 8333
- addrs.append(addr)
- return addrs
+MAX_PCT_ADDR_TO_SEND = 23
class AddrReceiver(P2PInterface):
@@ -62,18 +45,16 @@ class AddrTest(BitcoinTestFramework):
self.num_nodes = 1
def run_test(self):
- self.log.info('Create connection that sends and requests addr messages')
- addr_source = self.nodes[0].add_p2p_connection(P2PInterface())
-
- msg_send_addrs = msg_addr()
self.log.info('Fill peer AddrMan with a lot of records')
- # Since these addrs are sent from the same source, not all of them will be stored,
- # because we allocate a limited number of AddrMan buckets per addr source.
- total_addrs = 10000
- addrs = gen_addrs(total_addrs)
- for i in range(int(total_addrs/MAX_ADDR_TO_SEND)):
- msg_send_addrs.addrs = addrs[i * MAX_ADDR_TO_SEND:(i + 1) * MAX_ADDR_TO_SEND]
- addr_source.send_and_ping(msg_send_addrs)
+ for i in range(10000):
+ first_octet = i >> 8
+ second_octet = i % 256
+ a = "{}.{}.1.1".format(first_octet, second_octet)
+ self.nodes[0].addpeeraddress(a, 8333)
+
+ # Need to make sure we hit MAX_ADDR_TO_SEND records in the addr response later because
+ # only a fraction of all known addresses can be cached and returned.
+ assert(len(self.nodes[0].getnodeaddresses(0)) > int(MAX_ADDR_TO_SEND / (MAX_PCT_ADDR_TO_SEND / 100)))
responses = []
self.log.info('Send many addr requests within short time to receive same response')
@@ -89,7 +70,7 @@ class AddrTest(BitcoinTestFramework):
responses.append(addr_receiver.get_received_addrs())
for response in responses[1:]:
assert_equal(response, responses[0])
- assert(len(response) < MAX_ADDR_TO_SEND)
+ assert(len(response) == MAX_ADDR_TO_SEND)
cur_mock_time += 3 * 24 * 60 * 60
self.nodes[0].setmocktime(cur_mock_time)
diff --git a/test/functional/p2p_segwit.py b/test/functional/p2p_segwit.py
index 247e69d7d7..e190df403a 100755
--- a/test/functional/p2p_segwit.py
+++ b/test/functional/p2p_segwit.py
@@ -3,6 +3,7 @@
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test segwit transactions and blocks on P2P network."""
+from decimal import Decimal
import math
import random
import struct
@@ -695,13 +696,13 @@ class SegWitTest(BitcoinTestFramework):
if not self.segwit_active:
# Just check mempool acceptance, but don't add the transaction to the mempool, since witness is disallowed
# in blocks and the tx is impossible to mine right now.
- assert_equal(self.nodes[0].testmempoolaccept([tx3.serialize_with_witness().hex()]), [{'txid': tx3.hash, 'allowed': True}])
+ assert_equal(self.nodes[0].testmempoolaccept([tx3.serialize_with_witness().hex()]), [{'txid': tx3.hash, 'allowed': True, 'vsize': tx3.get_vsize(), 'fees': { 'base': Decimal('0.00001000')}}])
# Create the same output as tx3, but by replacing tx
tx3_out = tx3.vout[0]
tx3 = tx
tx3.vout = [tx3_out]
tx3.rehash()
- assert_equal(self.nodes[0].testmempoolaccept([tx3.serialize_with_witness().hex()]), [{'txid': tx3.hash, 'allowed': True}])
+ assert_equal(self.nodes[0].testmempoolaccept([tx3.serialize_with_witness().hex()]), [{'txid': tx3.hash, 'allowed': True, 'vsize': tx3.get_vsize(), 'fees': { 'base': Decimal('0.00011000')}}])
test_transaction_acceptance(self.nodes[0], self.test_node, tx3, with_witness=True, accepted=True)
self.nodes[0].generate(1)
diff --git a/test/functional/rpc_createmultisig.py b/test/functional/rpc_createmultisig.py
index 3c81a4a4e2..f19c60dc36 100755
--- a/test/functional/rpc_createmultisig.py
+++ b/test/functional/rpc_createmultisig.py
@@ -129,7 +129,8 @@ class RpcCreateMultiSigTest(BitcoinTestFramework):
try:
node1.loadwallet('wmulti')
except JSONRPCException as e:
- if e.error['code'] == -18 and 'Wallet wmulti not found' in e.error['message']:
+ path = os.path.join(self.options.tmpdir, "node1", "regtest", "wallets", "wmulti")
+ if e.error['code'] == -18 and "Wallet file verification failed. Failed to load database path '{}'. Path does not exist.".format(path) in e.error['message']:
node1.createwallet(wallet_name='wmulti', disable_private_keys=True)
else:
raise
diff --git a/test/functional/rpc_fundrawtransaction.py b/test/functional/rpc_fundrawtransaction.py
index 2a0971b808..6dcbec2714 100755
--- a/test/functional/rpc_fundrawtransaction.py
+++ b/test/functional/rpc_fundrawtransaction.py
@@ -224,7 +224,7 @@ class RawTransactionsTest(BitcoinTestFramework):
dec_tx = self.nodes[2].decoderawtransaction(rawtx)
assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])
- assert_raises_rpc_error(-5, "changeAddress must be a valid bitcoin address", self.nodes[2].fundrawtransaction, rawtx, {'changeAddress':'foobar'})
+ assert_raises_rpc_error(-5, "Change address must be a valid bitcoin address", self.nodes[2].fundrawtransaction, rawtx, {'changeAddress':'foobar'})
def test_valid_change_address(self):
self.log.info("Test fundrawtxn with a provided change address")
diff --git a/test/functional/rpc_net.py b/test/functional/rpc_net.py
index 506c77c567..bc0e5b458e 100755
--- a/test/functional/rpc_net.py
+++ b/test/functional/rpc_net.py
@@ -102,8 +102,11 @@ class NetTest(BitcoinTestFramework):
def test_getnetworkinfo(self):
self.log.info("Test getnetworkinfo")
- assert_equal(self.nodes[0].getnetworkinfo()['networkactive'], True)
- assert_equal(self.nodes[0].getnetworkinfo()['connections'], 2)
+ info = self.nodes[0].getnetworkinfo()
+ assert_equal(info['networkactive'], True)
+ assert_equal(info['connections'], 2)
+ assert_equal(info['connections_in'], 1)
+ assert_equal(info['connections_out'], 1)
with self.nodes[0].assert_debug_log(expected_msgs=['SetNetworkActive: false\n']):
self.nodes[0].setnetworkactive(state=False)
@@ -117,8 +120,11 @@ class NetTest(BitcoinTestFramework):
connect_nodes(self.nodes[0], 1)
connect_nodes(self.nodes[1], 0)
- assert_equal(self.nodes[0].getnetworkinfo()['networkactive'], True)
- assert_equal(self.nodes[0].getnetworkinfo()['connections'], 2)
+ info = self.nodes[0].getnetworkinfo()
+ assert_equal(info['networkactive'], True)
+ assert_equal(info['connections'], 2)
+ assert_equal(info['connections_in'], 1)
+ assert_equal(info['connections_out'], 1)
# check the `servicesnames` field
network_info = [node.getnetworkinfo() for node in self.nodes]
diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py
index 1c7dc98d16..781a49dfac 100755
--- a/test/functional/rpc_psbt.py
+++ b/test/functional/rpc_psbt.py
@@ -94,6 +94,9 @@ class PSBTTest(BitcoinTestFramework):
psbtx1 = self.nodes[0].walletcreatefundedpsbt([{"txid": utxo1['txid'], "vout": utxo1['vout']}], {self.nodes[2].getnewaddress():90}, 0, {"add_inputs": True})['psbt']
assert_equal(len(self.nodes[0].decodepsbt(psbtx1)['tx']['vin']), 2)
+ # Inputs argument can be null
+ self.nodes[0].walletcreatefundedpsbt(None, {self.nodes[2].getnewaddress():10})
+
# Node 1 should not be able to add anything to it but still return the psbtx same as before
psbtx = self.nodes[1].walletprocesspsbt(psbtx1)['psbt']
assert_equal(psbtx1, psbtx)
diff --git a/test/functional/rpc_txoutproof.py b/test/functional/rpc_txoutproof.py
index ca8be42d3b..93fb62c5d6 100755
--- a/test/functional/rpc_txoutproof.py
+++ b/test/functional/rpc_txoutproof.py
@@ -6,41 +6,31 @@
from test_framework.messages import CMerkleBlock, FromHex, ToHex
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal, assert_raises_rpc_error, connect_nodes
+from test_framework.util import assert_equal, assert_raises_rpc_error
+from test_framework.wallet import MiniWallet
+
class MerkleBlockTest(BitcoinTestFramework):
def set_test_params(self):
- self.num_nodes = 4
+ self.num_nodes = 2
self.setup_clean_chain = True
- # Nodes 0/1 are "wallet" nodes, Nodes 2/3 are used for testing
- self.extra_args = [[], [], [], ["-txindex"]]
-
- def skip_test_if_missing_module(self):
- self.skip_if_no_wallet()
-
- def setup_network(self):
- self.setup_nodes()
- connect_nodes(self.nodes[0], 1)
- connect_nodes(self.nodes[0], 2)
- connect_nodes(self.nodes[0], 3)
-
- self.sync_all()
+ self.extra_args = [
+ [],
+ ["-txindex"],
+ ]
def run_test(self):
- self.log.info("Mining blocks...")
- self.nodes[0].generate(105)
+ miniwallet = MiniWallet(self.nodes[0])
+ # Add enough mature utxos to the wallet, so that all txs spend confirmed coins
+ miniwallet.generate(5)
+ self.nodes[0].generate(100)
self.sync_all()
chain_height = self.nodes[1].getblockcount()
assert_equal(chain_height, 105)
- assert_equal(self.nodes[1].getbalance(), 0)
- assert_equal(self.nodes[2].getbalance(), 0)
-
- node0utxos = self.nodes[0].listunspent(1)
- tx1 = self.nodes[0].createrawtransaction([node0utxos.pop()], {self.nodes[1].getnewaddress(): 49.99})
- txid1 = self.nodes[0].sendrawtransaction(self.nodes[0].signrawtransactionwithwallet(tx1)["hex"])
- tx2 = self.nodes[0].createrawtransaction([node0utxos.pop()], {self.nodes[1].getnewaddress(): 49.99})
- txid2 = self.nodes[0].sendrawtransaction(self.nodes[0].signrawtransactionwithwallet(tx2)["hex"])
+
+ txid1 = miniwallet.send_self_transfer(from_node=self.nodes[0])['txid']
+ txid2 = miniwallet.send_self_transfer(from_node=self.nodes[0])['txid']
# This will raise an exception because the transaction is not yet in a block
assert_raises_rpc_error(-5, "Transaction not yet in block", self.nodes[0].gettxoutproof, [txid1])
@@ -53,50 +43,54 @@ class MerkleBlockTest(BitcoinTestFramework):
txlist.append(blocktxn[1])
txlist.append(blocktxn[2])
- assert_equal(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid1])), [txid1])
- assert_equal(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid1, txid2])), txlist)
- assert_equal(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid1, txid2], blockhash)), txlist)
+ assert_equal(self.nodes[0].verifytxoutproof(self.nodes[0].gettxoutproof([txid1])), [txid1])
+ assert_equal(self.nodes[0].verifytxoutproof(self.nodes[0].gettxoutproof([txid1, txid2])), txlist)
+ assert_equal(self.nodes[0].verifytxoutproof(self.nodes[0].gettxoutproof([txid1, txid2], blockhash)), txlist)
- txin_spent = self.nodes[1].listunspent(1).pop()
- tx3 = self.nodes[1].createrawtransaction([txin_spent], {self.nodes[0].getnewaddress(): 49.98})
- txid3 = self.nodes[0].sendrawtransaction(self.nodes[1].signrawtransactionwithwallet(tx3)["hex"])
+ txin_spent = miniwallet.get_utxo() # Get the change from txid2
+ tx3 = miniwallet.send_self_transfer(from_node=self.nodes[0], utxo_to_spend=txin_spent)
+ txid3 = tx3['txid']
self.nodes[0].generate(1)
self.sync_all()
txid_spent = txin_spent["txid"]
- txid_unspent = txid1 if txin_spent["txid"] != txid1 else txid2
+ txid_unspent = txid1 # Input was change from txid2, so txid1 should be unspent
# Invalid txids
- assert_raises_rpc_error(-8, "txid must be of length 64 (not 32, for '00000000000000000000000000000000')", self.nodes[2].gettxoutproof, ["00000000000000000000000000000000"], blockhash)
- assert_raises_rpc_error(-8, "txid must be hexadecimal string (not 'ZZZ0000000000000000000000000000000000000000000000000000000000000')", self.nodes[2].gettxoutproof, ["ZZZ0000000000000000000000000000000000000000000000000000000000000"], blockhash)
+ assert_raises_rpc_error(-8, "txid must be of length 64 (not 32, for '00000000000000000000000000000000')", self.nodes[0].gettxoutproof, ["00000000000000000000000000000000"], blockhash)
+ assert_raises_rpc_error(-8, "txid must be hexadecimal string (not 'ZZZ0000000000000000000000000000000000000000000000000000000000000')", self.nodes[0].gettxoutproof, ["ZZZ0000000000000000000000000000000000000000000000000000000000000"], blockhash)
# Invalid blockhashes
- assert_raises_rpc_error(-8, "blockhash must be of length 64 (not 32, for '00000000000000000000000000000000')", self.nodes[2].gettxoutproof, [txid_spent], "00000000000000000000000000000000")
- assert_raises_rpc_error(-8, "blockhash must be hexadecimal string (not 'ZZZ0000000000000000000000000000000000000000000000000000000000000')", self.nodes[2].gettxoutproof, [txid_spent], "ZZZ0000000000000000000000000000000000000000000000000000000000000")
+ assert_raises_rpc_error(-8, "blockhash must be of length 64 (not 32, for '00000000000000000000000000000000')", self.nodes[0].gettxoutproof, [txid_spent], "00000000000000000000000000000000")
+ assert_raises_rpc_error(-8, "blockhash must be hexadecimal string (not 'ZZZ0000000000000000000000000000000000000000000000000000000000000')", self.nodes[0].gettxoutproof, [txid_spent], "ZZZ0000000000000000000000000000000000000000000000000000000000000")
# We can't find the block from a fully-spent tx
- assert_raises_rpc_error(-5, "Transaction not yet in block", self.nodes[2].gettxoutproof, [txid_spent])
+ assert_raises_rpc_error(-5, "Transaction not yet in block", self.nodes[0].gettxoutproof, [txid_spent])
# We can get the proof if we specify the block
- assert_equal(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid_spent], blockhash)), [txid_spent])
+ assert_equal(self.nodes[0].verifytxoutproof(self.nodes[0].gettxoutproof([txid_spent], blockhash)), [txid_spent])
# We can't get the proof if we specify a non-existent block
- assert_raises_rpc_error(-5, "Block not found", self.nodes[2].gettxoutproof, [txid_spent], "0000000000000000000000000000000000000000000000000000000000000000")
+ assert_raises_rpc_error(-5, "Block not found", self.nodes[0].gettxoutproof, [txid_spent], "0000000000000000000000000000000000000000000000000000000000000000")
# We can get the proof if the transaction is unspent
- assert_equal(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid_unspent])), [txid_unspent])
+ assert_equal(self.nodes[0].verifytxoutproof(self.nodes[0].gettxoutproof([txid_unspent])), [txid_unspent])
# We can get the proof if we provide a list of transactions and one of them is unspent. The ordering of the list should not matter.
- assert_equal(sorted(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid1, txid2]))), sorted(txlist))
- assert_equal(sorted(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid2, txid1]))), sorted(txlist))
+ assert_equal(sorted(self.nodes[0].verifytxoutproof(self.nodes[0].gettxoutproof([txid1, txid2]))), sorted(txlist))
+ assert_equal(sorted(self.nodes[0].verifytxoutproof(self.nodes[0].gettxoutproof([txid2, txid1]))), sorted(txlist))
# We can always get a proof if we have a -txindex
- assert_equal(self.nodes[2].verifytxoutproof(self.nodes[3].gettxoutproof([txid_spent])), [txid_spent])
+ assert_equal(self.nodes[0].verifytxoutproof(self.nodes[1].gettxoutproof([txid_spent])), [txid_spent])
# We can't get a proof if we specify transactions from different blocks
- assert_raises_rpc_error(-5, "Not all transactions found in specified or retrieved block", self.nodes[2].gettxoutproof, [txid1, txid3])
+ assert_raises_rpc_error(-5, "Not all transactions found in specified or retrieved block", self.nodes[0].gettxoutproof, [txid1, txid3])
+ # Test empty list
+ assert_raises_rpc_error(-5, "Transaction not yet in block", self.nodes[0].gettxoutproof, [])
+ # Test duplicate txid
+ assert_raises_rpc_error(-8, 'Invalid parameter, duplicated txid', self.nodes[0].gettxoutproof, [txid1, txid1])
# Now we'll try tweaking a proof.
- proof = self.nodes[3].gettxoutproof([txid1, txid2])
+ proof = self.nodes[1].gettxoutproof([txid1, txid2])
assert txid1 in self.nodes[0].verifytxoutproof(proof)
assert txid2 in self.nodes[1].verifytxoutproof(proof)
tweaked_proof = FromHex(CMerkleBlock(), proof)
# Make sure that our serialization/deserialization is working
- assert txid1 in self.nodes[2].verifytxoutproof(ToHex(tweaked_proof))
+ assert txid1 in self.nodes[0].verifytxoutproof(ToHex(tweaked_proof))
# Check to see if we can go up the merkle tree and pass this off as a
# single-transaction block
diff --git a/test/functional/test_framework/messages.py b/test/functional/test_framework/messages.py
index b4e609df3a..00cf1ef66d 100755
--- a/test/functional/test_framework/messages.py
+++ b/test/functional/test_framework/messages.py
@@ -22,6 +22,7 @@ from codecs import encode
import copy
import hashlib
from io import BytesIO
+import math
import random
import socket
import struct
@@ -67,6 +68,8 @@ MSG_WITNESS_TX = MSG_TX | MSG_WITNESS_FLAG
FILTER_TYPE_BASIC = 0
+WITNESS_SCALE_FACTOR = 4
+
# Serialization/deserialization tools
def sha256(s):
return hashlib.new('sha256', s).digest()
@@ -537,6 +540,13 @@ class CTransaction:
return False
return True
+ # Calculate the virtual transaction size using witness and non-witness
+ # serialization size (does NOT use sigops).
+ def get_vsize(self):
+ with_witness_size = len(self.serialize_with_witness())
+ without_witness_size = len(self.serialize_without_witness())
+ return math.ceil(((WITNESS_SCALE_FACTOR - 1) * without_witness_size + with_witness_size) / WITNESS_SCALE_FACTOR)
+
def __repr__(self):
return "CTransaction(nVersion=%i vin=%s vout=%s wit=%s nLockTime=%i)" \
% (self.nVersion, repr(self.vin), repr(self.vout), repr(self.wit), self.nLockTime)
@@ -1462,9 +1472,9 @@ class msg_sendcmpct:
__slots__ = ("announce", "version")
msgtype = b"sendcmpct"
- def __init__(self):
- self.announce = False
- self.version = 1
+ def __init__(self, announce=False, version=1):
+ self.announce = announce
+ self.version = version
def deserialize(self, f):
self.announce = struct.unpack("<?", f.read(1))[0]
diff --git a/test/functional/test_framework/p2p.py b/test/functional/test_framework/p2p.py
index 963a507f53..5f9b316b18 100755
--- a/test/functional/test_framework/p2p.py
+++ b/test/functional/test_framework/p2p.py
@@ -109,6 +109,7 @@ MAGIC_BYTES = {
"mainnet": b"\xf9\xbe\xb4\xd9", # mainnet
"testnet3": b"\x0b\x11\x09\x07", # testnet3
"regtest": b"\xfa\xbf\xb5\xda", # regtest
+ "signet": b"\x0a\x03\xcf\x40", # signet
}
diff --git a/test/functional/test_framework/wallet.py b/test/functional/test_framework/wallet.py
new file mode 100644
index 0000000000..39b3bf2a5b
--- /dev/null
+++ b/test/functional/test_framework/wallet.py
@@ -0,0 +1,68 @@
+#!/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.
+"""A limited-functionality wallet, which may replace a real wallet in tests"""
+
+from decimal import Decimal
+from test_framework.address import ADDRESS_BCRT1_P2WSH_OP_TRUE
+from test_framework.messages import (
+ COIN,
+ COutPoint,
+ CTransaction,
+ CTxIn,
+ CTxInWitness,
+ CTxOut,
+)
+from test_framework.script import (
+ CScript,
+ OP_TRUE,
+)
+from test_framework.util import (
+ assert_equal,
+ hex_str_to_bytes,
+ satoshi_round,
+)
+
+
+class MiniWallet:
+ def __init__(self, test_node):
+ self._test_node = test_node
+ self._utxos = []
+ self._address = ADDRESS_BCRT1_P2WSH_OP_TRUE
+ self._scriptPubKey = hex_str_to_bytes(self._test_node.validateaddress(self._address)['scriptPubKey'])
+
+ def generate(self, num_blocks):
+ """Generate blocks with coinbase outputs to the internal address, and append the outputs to the internal list"""
+ blocks = self._test_node.generatetoaddress(num_blocks, self._address)
+ for b in blocks:
+ cb_tx = self._test_node.getblock(blockhash=b, verbosity=2)['tx'][0]
+ self._utxos.append({'txid': cb_tx['txid'], 'vout': 0, 'value': cb_tx['vout'][0]['value']})
+ return blocks
+
+ def get_utxo(self):
+ """Return the last utxo. Can be used to get the change output immediately after a send_self_transfer"""
+ return self._utxos.pop()
+
+ def send_self_transfer(self, *, fee_rate=Decimal("0.003"), from_node, utxo_to_spend=None):
+ """Create and send a tx with the specified fee_rate. Fee may be exact or at most one satoshi higher than needed."""
+ self._utxos = sorted(self._utxos, key=lambda k: k['value'])
+ utxo_to_spend = utxo_to_spend or self._utxos.pop() # Pick the largest utxo (if none provided) and hope it covers the fee
+ vsize = Decimal(96)
+ send_value = satoshi_round(utxo_to_spend['value'] - fee_rate * (vsize / 1000))
+ fee = utxo_to_spend['value'] - send_value
+ assert send_value > 0
+
+ tx = CTransaction()
+ tx.vin = [CTxIn(COutPoint(int(utxo_to_spend['txid'], 16), utxo_to_spend['vout']))]
+ tx.vout = [CTxOut(int(send_value * COIN), self._scriptPubKey)]
+ tx.wit.vtxinwit = [CTxInWitness()]
+ tx.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE])]
+ tx_hex = tx.serialize().hex()
+
+ txid = from_node.sendrawtransaction(tx_hex)
+ self._utxos.append({'txid': txid, 'vout': 0, 'value': send_value})
+ tx_info = from_node.getmempoolentry(txid)
+ assert_equal(tx_info['vsize'], vsize)
+ assert_equal(tx_info['fee'], fee)
+ return {'txid': txid, 'wtxid': tx_info['wtxid'], 'hex': tx_hex}
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 578afe5f30..6c3d50df93 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -208,6 +208,7 @@ BASE_SCRIPTS = [
'rpc_bind.py --ipv6',
'rpc_bind.py --nonloopback',
'mining_basic.py',
+ 'feature_signet.py',
'wallet_bumpfee.py',
'wallet_implicitsegwit.py',
'rpc_named_arguments.py',
@@ -225,6 +226,7 @@ BASE_SCRIPTS = [
'rpc_estimatefee.py',
'rpc_getblockstats.py',
'wallet_create_tx.py',
+ 'wallet_send.py',
'p2p_fingerprint.py',
'feature_uacomment.py',
'wallet_coinbase_category.py',
diff --git a/test/functional/tool_wallet.py b/test/functional/tool_wallet.py
index 18f0beb598..fa5b5c10ff 100755
--- a/test/functional/tool_wallet.py
+++ b/test/functional/tool_wallet.py
@@ -70,12 +70,14 @@ class ToolWalletTest(BitcoinTestFramework):
self.assert_raises_tool_error('Invalid command: help', 'help')
self.assert_raises_tool_error('Error: two methods provided (info and create). Only one method should be provided.', 'info', 'create')
self.assert_raises_tool_error('Error parsing command line arguments: Invalid parameter -foo', '-foo')
+ locked_dir = os.path.join(self.options.tmpdir, "node0", "regtest", "wallets")
self.assert_raises_tool_error(
- 'Error loading wallet.dat. Is wallet being used by another process?',
+ 'Error initializing wallet database environment "{}"!'.format(locked_dir),
'-wallet=wallet.dat',
'info',
)
- self.assert_raises_tool_error('Error: no wallet file at nonexistent.dat', '-wallet=nonexistent.dat', 'info')
+ path = os.path.join(self.options.tmpdir, "node0", "regtest", "wallets", "nonexistent.dat")
+ self.assert_raises_tool_error("Failed to load database path '{}'. Path does not exist.".format(path), '-wallet=nonexistent.dat', 'info')
def test_tool_wallet_info(self):
# Stop the node to close the wallet to call the info command.
diff --git a/test/functional/wallet_backup.py b/test/functional/wallet_backup.py
index 4766355335..bcbac18d57 100755
--- a/test/functional/wallet_backup.py
+++ b/test/functional/wallet_backup.py
@@ -50,10 +50,10 @@ class WalletBackupTest(BitcoinTestFramework):
# nodes 1, 2,3 are spenders, let's give them a keypool=100
# whitelist all peers to speed up tx relay / mempool sync
self.extra_args = [
- ["-whitelist=noban@127.0.0.1", "-keypool=100"],
- ["-whitelist=noban@127.0.0.1", "-keypool=100"],
- ["-whitelist=noban@127.0.0.1", "-keypool=100"],
- ["-whitelist=noban@127.0.0.1"],
+ ["-whitelist=noban@127.0.0.1", "-keypool=100", "-wallet="],
+ ["-whitelist=noban@127.0.0.1", "-keypool=100", "-wallet="],
+ ["-whitelist=noban@127.0.0.1", "-keypool=100", "-wallet="],
+ ["-whitelist=noban@127.0.0.1", "-wallet="],
]
self.rpc_timeout = 120
diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py
index 147c43f2f7..bb208341a0 100755
--- a/test/functional/wallet_basic.py
+++ b/test/functional/wallet_basic.py
@@ -596,6 +596,9 @@ class WalletTest(BitcoinTestFramework):
# wait until the wallet has submitted all transactions to the mempool
self.wait_until(lambda: len(self.nodes[0].getrawmempool()) == chainlimit * 2)
+ # Prevent potential race condition when calling wallet RPCs right after restart
+ self.nodes[0].syncwithvalidationinterfacequeue()
+
node0_balance = self.nodes[0].getbalance()
# With walletrejectlongchains we will not create the tx and store it in our wallet.
assert_raises_rpc_error(-6, "Transaction has too long of a mempool chain", self.nodes[0].sendtoaddress, sending_addr, node0_balance - Decimal('0.01'))
diff --git a/test/functional/wallet_dump.py b/test/functional/wallet_dump.py
index 06f01ef191..09581d864b 100755
--- a/test/functional/wallet_dump.py
+++ b/test/functional/wallet_dump.py
@@ -95,7 +95,7 @@ def read_dump(file_name, addrs, script_addrs, hd_master_addr_old):
class WalletDumpTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
- self.extra_args = [["-keypool=90", "-addresstype=legacy"]]
+ self.extra_args = [["-keypool=90", "-addresstype=legacy", "-wallet=dump"]]
self.rpc_timeout = 120
def skip_test_if_missing_module(self):
diff --git a/test/functional/wallet_import_rescan.py b/test/functional/wallet_import_rescan.py
index 4ff7f1d525..87deaded09 100755
--- a/test/functional/wallet_import_rescan.py
+++ b/test/functional/wallet_import_rescan.py
@@ -151,7 +151,7 @@ class ImportRescanTest(BitcoinTestFramework):
self.skip_if_no_wallet()
def setup_network(self):
- self.extra_args = [[] for _ in range(self.num_nodes)]
+ self.extra_args = [["-wallet="] for _ in range(self.num_nodes)]
for i, import_node in enumerate(IMPORT_NODES, 2):
if import_node.prune:
self.extra_args[i] += ["-prune=1"]
@@ -159,7 +159,7 @@ class ImportRescanTest(BitcoinTestFramework):
self.add_nodes(self.num_nodes, extra_args=self.extra_args)
# Import keys with pruning disabled
- self.start_nodes(extra_args=[[]] * self.num_nodes)
+ self.start_nodes(extra_args=[["-wallet="]] * self.num_nodes)
for n in self.nodes:
n.importprivkey(privkey=n.get_deterministic_priv_key().key, label='coinbase')
self.stop_nodes()
diff --git a/test/functional/wallet_multiwallet.py b/test/functional/wallet_multiwallet.py
index 5c9d7ff629..aaf050ebf7 100755
--- a/test/functional/wallet_multiwallet.py
+++ b/test/functional/wallet_multiwallet.py
@@ -43,6 +43,7 @@ class MultiWalletTest(BitcoinTestFramework):
self.setup_clean_chain = True
self.num_nodes = 2
self.rpc_timeout = 120
+ self.extra_args = [["-wallet="], ["-wallet="]]
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
@@ -82,7 +83,7 @@ class MultiWalletTest(BitcoinTestFramework):
os.rename(wallet_dir("wallet.dat"), wallet_dir("w8"))
# create another dummy wallet for use in testing backups later
- self.start_node(0, [])
+ self.start_node(0, ["-wallet="])
self.stop_nodes()
empty_wallet = os.path.join(self.options.tmpdir, 'empty.dat')
os.rename(wallet_dir("wallet.dat"), empty_wallet)
@@ -152,7 +153,7 @@ class MultiWalletTest(BitcoinTestFramework):
competing_wallet_dir = os.path.join(self.options.tmpdir, 'competing_walletdir')
os.mkdir(competing_wallet_dir)
- self.restart_node(0, ['-walletdir=' + competing_wallet_dir])
+ self.restart_node(0, ['-walletdir=' + competing_wallet_dir, '-wallet='])
exp_stderr = r"Error: Error initializing wallet database environment \"\S+competing_walletdir\"!"
self.nodes[1].assert_start_raises_init_error(['-walletdir=' + competing_wallet_dir], exp_stderr, match=ErrorMatch.PARTIAL_REGEX)
@@ -244,13 +245,16 @@ class MultiWalletTest(BitcoinTestFramework):
assert_equal(set(self.nodes[0].listwallets()), set(wallet_names))
# Fail to load if wallet doesn't exist
- assert_raises_rpc_error(-18, 'Wallet wallets not found.', self.nodes[0].loadwallet, 'wallets')
+ path = os.path.join(self.options.tmpdir, "node0", "regtest", "wallets", "wallets")
+ assert_raises_rpc_error(-18, "Wallet file verification failed. Failed to load database path '{}'. Path does not exist.".format(path), self.nodes[0].loadwallet, 'wallets')
# Fail to load duplicate wallets
- assert_raises_rpc_error(-4, 'Wallet file verification failed. Error loading wallet w1. Duplicate -wallet filename specified.', self.nodes[0].loadwallet, wallet_names[0])
+ path = os.path.join(self.options.tmpdir, "node0", "regtest", "wallets", "w1", "wallet.dat")
+ assert_raises_rpc_error(-4, "Wallet file verification failed. Refusing to load database. Data file '{}' is already loaded.".format(path), self.nodes[0].loadwallet, wallet_names[0])
# Fail to load duplicate wallets by different ways (directory and filepath)
- assert_raises_rpc_error(-4, "Wallet file verification failed. Error loading wallet wallet.dat. Duplicate -wallet filename specified.", self.nodes[0].loadwallet, 'wallet.dat')
+ path = os.path.join(self.options.tmpdir, "node0", "regtest", "wallets", "wallet.dat")
+ assert_raises_rpc_error(-4, "Wallet file verification failed. Refusing to load database. Data file '{}' is already loaded.".format(path), self.nodes[0].loadwallet, 'wallet.dat')
# Fail to load if one wallet is a copy of another
assert_raises_rpc_error(-4, "BerkeleyDatabase: Can't open database w8_copy (duplicates fileid", self.nodes[0].loadwallet, 'w8_copy')
@@ -264,12 +268,14 @@ class MultiWalletTest(BitcoinTestFramework):
# Fail to load if a directory is specified that doesn't contain a wallet
os.mkdir(wallet_dir('empty_wallet_dir'))
- assert_raises_rpc_error(-18, "Directory empty_wallet_dir does not contain a wallet.dat file", self.nodes[0].loadwallet, 'empty_wallet_dir')
+ path = os.path.join(self.options.tmpdir, "node0", "regtest", "wallets", "empty_wallet_dir")
+ assert_raises_rpc_error(-18, "Wallet file verification failed. Failed to load database path '{}'. Data is not in recognized format.".format(path), self.nodes[0].loadwallet, 'empty_wallet_dir')
self.log.info("Test dynamic wallet creation.")
# Fail to create a wallet if it already exists.
- assert_raises_rpc_error(-4, "Wallet w2 already exists.", self.nodes[0].createwallet, 'w2')
+ path = os.path.join(self.options.tmpdir, "node0", "regtest", "wallets", "w2")
+ assert_raises_rpc_error(-4, "Failed to create database path '{}'. Database already exists.".format(path), self.nodes[0].createwallet, 'w2')
# Successfully create a wallet with a new name
loadwallet_name = self.nodes[0].createwallet('w9')
diff --git a/test/functional/wallet_send.py b/test/functional/wallet_send.py
new file mode 100755
index 0000000000..b64d2030a4
--- /dev/null
+++ b/test/functional/wallet_send.py
@@ -0,0 +1,339 @@
+#!/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 the send RPC command."""
+
+from decimal import Decimal, getcontext
+from test_framework.authproxy import JSONRPCException
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal,
+ assert_fee_amount,
+ assert_greater_than,
+ assert_raises_rpc_error
+)
+
+class WalletSendTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 2
+ # whitelist all peers to speed up tx relay / mempool sync
+ self.extra_args = [
+ ["-whitelist=127.0.0.1","-walletrbf=1"],
+ ["-whitelist=127.0.0.1","-walletrbf=1"],
+ ]
+ getcontext().prec = 8 # Satoshi precision for Decimal
+
+ def skip_test_if_missing_module(self):
+ self.skip_if_no_wallet()
+
+ def test_send(self, from_wallet, to_wallet=None, amount=None, data=None,
+ arg_conf_target=None, arg_estimate_mode=None,
+ conf_target=None, estimate_mode=None, add_to_wallet=None,psbt=None,
+ inputs=None,add_inputs=None,change_address=None,change_position=None,change_type=None,
+ include_watching=None,locktime=None,lock_unspents=None,replaceable=None,subtract_fee_from_outputs=None,
+ expect_error=None):
+ assert (amount is None) != (data is None)
+
+ from_balance_before = from_wallet.getbalance()
+ if to_wallet is None:
+ assert amount is None
+ else:
+ to_untrusted_pending_before = to_wallet.getbalances()["mine"]["untrusted_pending"]
+
+ if amount:
+ dest = to_wallet.getnewaddress()
+ outputs = {dest: amount}
+ else:
+ outputs = {"data": data}
+
+ # Construct options dictionary
+ options = {}
+ if add_to_wallet is not None:
+ options["add_to_wallet"] = add_to_wallet
+ else:
+ if psbt:
+ add_to_wallet = False
+ else:
+ add_to_wallet = from_wallet.getwalletinfo()["private_keys_enabled"] # Default value
+ if psbt is not None:
+ options["psbt"] = psbt
+ if conf_target is not None:
+ options["conf_target"] = conf_target
+ if estimate_mode is not None:
+ options["estimate_mode"] = estimate_mode
+ if inputs is not None:
+ options["inputs"] = inputs
+ if add_inputs is not None:
+ options["add_inputs"] = add_inputs
+ if change_address is not None:
+ options["change_address"] = change_address
+ if change_position is not None:
+ options["change_position"] = change_position
+ if change_type is not None:
+ options["change_type"] = change_type
+ if include_watching is not None:
+ options["include_watching"] = include_watching
+ if locktime is not None:
+ options["locktime"] = locktime
+ if lock_unspents is not None:
+ options["lock_unspents"] = lock_unspents
+ if replaceable is None:
+ replaceable = True # default
+ else:
+ options["replaceable"] = replaceable
+ if subtract_fee_from_outputs is not None:
+ options["subtract_fee_from_outputs"] = subtract_fee_from_outputs
+
+ if len(options.keys()) == 0:
+ options = None
+
+ if expect_error is None:
+ res = from_wallet.send(outputs=outputs, conf_target=arg_conf_target, estimate_mode=arg_estimate_mode, options=options)
+ else:
+ try:
+ assert_raises_rpc_error(expect_error[0],expect_error[1],from_wallet.send,
+ outputs=outputs,conf_target=arg_conf_target,estimate_mode=arg_estimate_mode,options=options)
+ except AssertionError:
+ # Provide debug info if the test fails
+ self.log.error("Unexpected successful result:")
+ self.log.error(options)
+ res = from_wallet.send(outputs=outputs,conf_target=arg_conf_target,estimate_mode=arg_estimate_mode,options=options)
+ self.log.error(res)
+ if "txid" in res and add_to_wallet:
+ self.log.error("Transaction details:")
+ try:
+ tx = from_wallet.gettransaction(res["txid"])
+ self.log.error(tx)
+ self.log.error("testmempoolaccept (transaction may already be in mempool):")
+ self.log.error(from_wallet.testmempoolaccept([tx["hex"]]))
+ except JSONRPCException as exc:
+ self.log.error(exc)
+
+ raise
+
+ return
+
+ if locktime:
+ return res
+
+ if from_wallet.getwalletinfo()["private_keys_enabled"] and not include_watching:
+ assert_equal(res["complete"], True)
+ assert "txid" in res
+ else:
+ assert_equal(res["complete"], False)
+ assert not "txid" in res
+ assert "psbt" in res
+
+ if add_to_wallet and not include_watching:
+ # Ensure transaction exists in the wallet:
+ tx = from_wallet.gettransaction(res["txid"])
+ assert tx
+ assert_equal(tx["bip125-replaceable"], "yes" if replaceable else "no")
+ # Ensure transaction exists in the mempool:
+ tx = from_wallet.getrawtransaction(res["txid"],True)
+ assert tx
+ if amount:
+ if subtract_fee_from_outputs:
+ assert_equal(from_balance_before - from_wallet.getbalance(), amount)
+ else:
+ assert_greater_than(from_balance_before - from_wallet.getbalance(), amount)
+ else:
+ assert next((out for out in tx["vout"] if out["scriptPubKey"]["asm"] == "OP_RETURN 35"), None)
+ else:
+ assert_equal(from_balance_before, from_wallet.getbalance())
+
+ if to_wallet:
+ self.sync_mempools()
+ if add_to_wallet:
+ if not subtract_fee_from_outputs:
+ assert_equal(to_wallet.getbalances()["mine"]["untrusted_pending"], to_untrusted_pending_before + Decimal(amount if amount else 0))
+ else:
+ assert_equal(to_wallet.getbalances()["mine"]["untrusted_pending"], to_untrusted_pending_before)
+
+ return res
+
+ def run_test(self):
+ self.log.info("Setup wallets...")
+ # w0 is a wallet with coinbase rewards
+ w0 = self.nodes[0].get_wallet_rpc("")
+ # w1 is a regular wallet
+ self.nodes[1].createwallet(wallet_name="w1")
+ w1 = self.nodes[1].get_wallet_rpc("w1")
+ # w2 contains the private keys for w3
+ self.nodes[1].createwallet(wallet_name="w2")
+ w2 = self.nodes[1].get_wallet_rpc("w2")
+ # w3 is a watch-only wallet, based on w2
+ self.nodes[1].createwallet(wallet_name="w3",disable_private_keys=True)
+ w3 = self.nodes[1].get_wallet_rpc("w3")
+ for _ in range(3):
+ a2_receive = w2.getnewaddress()
+ a2_change = w2.getrawchangeaddress() # doesn't actually use change derivation
+ res = w3.importmulti([{
+ "desc": w2.getaddressinfo(a2_receive)["desc"],
+ "timestamp": "now",
+ "keypool": True,
+ "watchonly": True
+ },{
+ "desc": w2.getaddressinfo(a2_change)["desc"],
+ "timestamp": "now",
+ "keypool": True,
+ "internal": True,
+ "watchonly": True
+ }])
+ assert_equal(res, [{"success": True}, {"success": True}])
+
+ w0.sendtoaddress(a2_receive, 10) # fund w3
+ self.nodes[0].generate(1)
+ self.sync_blocks()
+
+ # w4 has private keys enabled, but only contains watch-only keys (from w2)
+ self.nodes[1].createwallet(wallet_name="w4",disable_private_keys=False)
+ w4 = self.nodes[1].get_wallet_rpc("w4")
+ for _ in range(3):
+ a2_receive = w2.getnewaddress()
+ res = w4.importmulti([{
+ "desc": w2.getaddressinfo(a2_receive)["desc"],
+ "timestamp": "now",
+ "keypool": False,
+ "watchonly": True
+ }])
+ assert_equal(res, [{"success": True}])
+
+ w0.sendtoaddress(a2_receive, 10) # fund w4
+ self.nodes[0].generate(1)
+ self.sync_blocks()
+
+ self.log.info("Send to address...")
+ self.test_send(from_wallet=w0, to_wallet=w1, amount=1)
+ self.test_send(from_wallet=w0, to_wallet=w1, amount=1, add_to_wallet=True)
+
+ self.log.info("Don't broadcast...")
+ res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, add_to_wallet=False)
+ assert(res["hex"])
+
+ self.log.info("Return PSBT...")
+ res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, psbt=True)
+ assert(res["psbt"])
+
+ self.log.info("Create transaction that spends to address, but don't broadcast...")
+ self.test_send(from_wallet=w0, to_wallet=w1, amount=1, add_to_wallet=False)
+ # conf_target & estimate_mode can be set as argument or option
+ res1 = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_conf_target=1, arg_estimate_mode="economical", add_to_wallet=False)
+ res2 = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, conf_target=1, estimate_mode="economical", add_to_wallet=False)
+ assert_equal(self.nodes[1].decodepsbt(res1["psbt"])["fee"],
+ self.nodes[1].decodepsbt(res2["psbt"])["fee"])
+ # but not at the same time
+ self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_conf_target=1, arg_estimate_mode="economical",
+ conf_target=1, estimate_mode="economical", add_to_wallet=False, expect_error=(-8,"Use either conf_target and estimate_mode or the options dictionary to control fee rate"))
+
+ self.log.info("Create PSBT from watch-only wallet w3, sign with w2...")
+ res = self.test_send(from_wallet=w3, to_wallet=w1, amount=1)
+ res = w2.walletprocesspsbt(res["psbt"])
+ assert res["complete"]
+
+ self.log.info("Create PSBT from wallet w4 with watch-only keys, sign with w2...")
+ self.test_send(from_wallet=w4, to_wallet=w1, amount=1, expect_error=(-4, "Insufficient funds"))
+ res = self.test_send(from_wallet=w4, to_wallet=w1, amount=1, include_watching=True, add_to_wallet=False)
+ res = w2.walletprocesspsbt(res["psbt"])
+ assert res["complete"]
+
+ self.log.info("Create OP_RETURN...")
+ self.test_send(from_wallet=w0, to_wallet=w1, amount=1)
+ self.test_send(from_wallet=w0, data="Hello World", expect_error=(-8, "Data must be hexadecimal string (not 'Hello World')"))
+ self.test_send(from_wallet=w0, data="23")
+ res = self.test_send(from_wallet=w3, data="23")
+ res = w2.walletprocesspsbt(res["psbt"])
+ assert res["complete"]
+
+ self.log.info("Set fee rate...")
+ res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, conf_target=2, estimate_mode="sat/b", add_to_wallet=False)
+ fee = self.nodes[1].decodepsbt(res["psbt"])["fee"]
+ assert_fee_amount(fee, Decimal(len(res["hex"]) / 2), Decimal("0.00002"))
+ self.test_send(from_wallet=w0, to_wallet=w1, amount=1, conf_target=-1, estimate_mode="sat/b",
+ expect_error=(-3, "Amount out of range"))
+ # Fee rate of 0.1 satoshi per byte should throw an error
+ # TODO: error should say 1.000 sat/b
+ self.test_send(from_wallet=w0, to_wallet=w1, amount=1, conf_target=0.1, estimate_mode="sat/b",
+ expect_error=(-4, "Fee rate (0.00000100 BTC/kB) is lower than the minimum fee rate setting (0.00001000 BTC/kB)"))
+
+ self.test_send(from_wallet=w0, to_wallet=w1, amount=1, conf_target=0.000001, estimate_mode="BTC/KB",
+ expect_error=(-4, "Fee rate (0.00000100 BTC/kB) is lower than the minimum fee rate setting (0.00001000 BTC/kB)"))
+
+ # TODO: Return hex if fee rate is below -maxmempool
+ # res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, conf_target=0.1, estimate_mode="sat/b", add_to_wallet=False)
+ # assert res["hex"]
+ # hex = res["hex"]
+ # res = self.nodes[0].testmempoolaccept([hex])
+ # assert not res[0]["allowed"]
+ # assert_equal(res[0]["reject-reason"], "...") # low fee
+ # assert_fee_amount(fee, Decimal(len(res["hex"]) / 2), Decimal("0.000001"))
+
+ self.log.info("If inputs are specified, do not automatically add more...")
+ res = self.test_send(from_wallet=w0, to_wallet=w1, amount=51, inputs=[], add_to_wallet=False)
+ assert res["complete"]
+ utxo1 = w0.listunspent()[0]
+ assert_equal(utxo1["amount"], 50)
+ self.test_send(from_wallet=w0, to_wallet=w1, amount=51, inputs=[utxo1],
+ expect_error=(-4, "Insufficient funds"))
+ self.test_send(from_wallet=w0, to_wallet=w1, amount=51, inputs=[utxo1], add_inputs=False,
+ expect_error=(-4, "Insufficient funds"))
+ res = self.test_send(from_wallet=w0, to_wallet=w1, amount=51, inputs=[utxo1], add_inputs=True, add_to_wallet=False)
+ assert res["complete"]
+
+ self.log.info("Manual change address and position...")
+ self.test_send(from_wallet=w0, to_wallet=w1, amount=1, change_address="not an address",
+ expect_error=(-5, "Change address must be a valid bitcoin address"))
+ change_address = w0.getnewaddress()
+ self.test_send(from_wallet=w0, to_wallet=w1, amount=1, add_to_wallet=False, change_address=change_address)
+ assert res["complete"]
+ res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, add_to_wallet=False, change_address=change_address, change_position=0)
+ assert res["complete"]
+ assert_equal(self.nodes[0].decodepsbt(res["psbt"])["tx"]["vout"][0]["scriptPubKey"]["addresses"], [change_address])
+ res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, add_to_wallet=False, change_type="legacy", change_position=0)
+ assert res["complete"]
+ change_address = self.nodes[0].decodepsbt(res["psbt"])["tx"]["vout"][0]["scriptPubKey"]["addresses"][0]
+ assert change_address[0] == "m" or change_address[0] == "n"
+
+ self.log.info("Set lock time...")
+ height = self.nodes[0].getblockchaininfo()["blocks"]
+ res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, locktime=height + 1)
+ assert res["complete"]
+ assert res["txid"]
+ txid = res["txid"]
+ # Although the wallet finishes the transaction, it can't be added to the mempool yet:
+ hex = self.nodes[0].gettransaction(res["txid"])["hex"]
+ res = self.nodes[0].testmempoolaccept([hex])
+ assert not res[0]["allowed"]
+ assert_equal(res[0]["reject-reason"], "non-final")
+ # It shouldn't be confirmed in the next block
+ self.nodes[0].generate(1)
+ assert_equal(self.nodes[0].gettransaction(txid)["confirmations"], 0)
+ # The mempool should allow it now:
+ res = self.nodes[0].testmempoolaccept([hex])
+ assert res[0]["allowed"]
+ # Don't wait for wallet to add it to the mempool:
+ res = self.nodes[0].sendrawtransaction(hex)
+ self.nodes[0].generate(1)
+ assert_equal(self.nodes[0].gettransaction(txid)["confirmations"], 1)
+
+ self.log.info("Lock unspents...")
+ utxo1 = w0.listunspent()[0]
+ assert_greater_than(utxo1["amount"], 1)
+ res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, inputs=[utxo1], add_to_wallet=False, lock_unspents=True)
+ assert res["complete"]
+ locked_coins = w0.listlockunspent()
+ assert_equal(len(locked_coins), 1)
+ # Locked coins are automatically unlocked when manually selected
+ self.test_send(from_wallet=w0, to_wallet=w1, amount=1, inputs=[utxo1],add_to_wallet=False)
+
+ self.log.info("Replaceable...")
+ self.test_send(from_wallet=w0, to_wallet=w1, amount=1, add_to_wallet=False, replaceable=True)
+ self.test_send(from_wallet=w0, to_wallet=w1, amount=1, add_to_wallet=False, replaceable=False)
+
+ self.log.info("Subtract fee from output")
+ self.test_send(from_wallet=w0, to_wallet=w1, amount=1, subtract_fee_from_outputs=[0])
+
+
+if __name__ == '__main__':
+ WalletSendTest().main()
diff --git a/test/functional/wallet_startup.py b/test/functional/wallet_startup.py
index cfc4edb8ee..d3119925f7 100755
--- a/test/functional/wallet_startup.py
+++ b/test/functional/wallet_startup.py
@@ -26,6 +26,16 @@ class WalletStartupTest(BitcoinTestFramework):
self.start_nodes()
def run_test(self):
+ self.log.info('Should start without any wallets')
+ assert_equal(self.nodes[0].listwallets(), [])
+ assert_equal(self.nodes[0].listwalletdir(), {'wallets': []})
+
+ self.log.info('New default wallet should load by default when there are no other wallets')
+ self.nodes[0].createwallet(wallet_name='', load_on_startup=False)
+ self.restart_node(0)
+ assert_equal(self.nodes[0].listwallets(), [''])
+
+ self.log.info('Test load on startup behavior')
self.nodes[0].createwallet(wallet_name='w0', load_on_startup=True)
self.nodes[0].createwallet(wallet_name='w1', load_on_startup=False)
self.nodes[0].createwallet(wallet_name='w2', load_on_startup=True)
diff --git a/test/functional/wallet_upgradewallet.py b/test/functional/wallet_upgradewallet.py
index 69aa603c6b..031da8da81 100755
--- a/test/functional/wallet_upgradewallet.py
+++ b/test/functional/wallet_upgradewallet.py
@@ -27,7 +27,7 @@ class UpgradeWalletTest(BitcoinTestFramework):
self.setup_clean_chain = True
self.num_nodes = 3
self.extra_args = [
- ["-addresstype=bech32"], # current wallet version
+ ["-addresstype=bech32", "-wallet="], # current wallet version
["-usehd=1"], # v0.16.3 wallet
["-usehd=0"] # v0.15.2 wallet
]
diff --git a/test/sanitizer_suppressions/tsan b/test/sanitizer_suppressions/tsan
index 3ba2b2a103..625085c55b 100644
--- a/test/sanitizer_suppressions/tsan
+++ b/test/sanitizer_suppressions/tsan
@@ -11,7 +11,7 @@ mutex:CConnman::ThreadOpenConnections
mutex:CConnman::ThreadOpenAddedConnections
mutex:CConnman::SocketHandler
mutex:UpdateTip
-mutex:PeerLogicValidation::UpdatedBlockTip
+mutex:PeerManager::UpdatedBlockTip
mutex:g_best_block_mutex
# race (TODO fix)
race:CConnman::WakeMessageHandler