aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/functional/data/rpc_psbt.json3
-rwxr-xr-xtest/functional/example_test.py4
-rwxr-xr-xtest/functional/feature_dbcrash.py1
-rwxr-xr-xtest/functional/feature_fee_estimation.py8
-rwxr-xr-xtest/functional/feature_pruning.py2
-rwxr-xr-xtest/functional/interface_zmq.py9
-rwxr-xr-xtest/functional/mempool_resurrect.py5
-rwxr-xr-xtest/functional/p2p_invalid_messages.py175
-rwxr-xr-xtest/functional/p2p_node_network_limited.py4
-rwxr-xr-xtest/functional/rpc_help.py15
-rwxr-xr-xtest/functional/rpc_psbt.py47
-rw-r--r--test/functional/test_framework/blocktools.py2
-rwxr-xr-xtest/functional/test_framework/mininode.py15
-rwxr-xr-xtest/functional/test_framework/test_framework.py7
-rwxr-xr-xtest/functional/test_framework/test_node.py60
-rwxr-xr-xtest/functional/test_runner.py55
-rwxr-xr-xtest/functional/wallet_basic.py25
-rwxr-xr-xtest/functional/wallet_import_rescan.py6
-rwxr-xr-xtest/functional/wallet_importmulti.py174
-rwxr-xr-xtest/functional/wallet_listreceivedby.py12
-rwxr-xr-xtest/lint/lint-circular-dependencies.sh3
-rwxr-xr-xtest/lint/lint-format-strings.py4
-rwxr-xr-xtest/lint/lint-locale-dependence.sh20
23 files changed, 549 insertions, 107 deletions
diff --git a/test/functional/data/rpc_psbt.json b/test/functional/data/rpc_psbt.json
index dd40a096de..57f2608ee9 100644
--- a/test/functional/data/rpc_psbt.json
+++ b/test/functional/data/rpc_psbt.json
@@ -17,7 +17,8 @@
"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",
- "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"
+ "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"
],
"valid" : [
"cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAAAA",
diff --git a/test/functional/example_test.py b/test/functional/example_test.py
index 3f15367a75..be3544ee74 100755
--- a/test/functional/example_test.py
+++ b/test/functional/example_test.py
@@ -164,13 +164,13 @@ class ExampleTest(BitcoinTestFramework):
self.tip = int(self.nodes[0].getbestblockhash(), 16)
self.block_time = self.nodes[0].getblock(self.nodes[0].getbestblockhash())['time'] + 1
- height = 1
+ height = self.nodes[0].getblockcount()
for i in range(10):
# Use the mininode and blocktools functionality to manually build a block
# Calling the generate() rpc is easier, but this allows us to exactly
# control the blocks and transactions.
- block = create_block(self.tip, create_coinbase(height), self.block_time)
+ block = create_block(self.tip, create_coinbase(height+1), self.block_time)
block.solve()
block_message = msg_block(block)
# Send message is used to send a P2P message to the node over our P2PInterface
diff --git a/test/functional/feature_dbcrash.py b/test/functional/feature_dbcrash.py
index ae1eacf2d7..70d67aa53a 100755
--- a/test/functional/feature_dbcrash.py
+++ b/test/functional/feature_dbcrash.py
@@ -69,6 +69,7 @@ class ChainstateWriteCrashTest(BitcoinTestFramework):
def setup_network(self):
self.add_nodes(self.num_nodes, extra_args=self.extra_args)
self.start_nodes()
+ self.import_deterministic_coinbase_privkeys()
# Leave them unconnected, we'll use submitblock directly in this test
def restart_node(self, node_index, expected_tip):
diff --git a/test/functional/feature_fee_estimation.py b/test/functional/feature_fee_estimation.py
index aaab4279b5..b68e46adbc 100755
--- a/test/functional/feature_fee_estimation.py
+++ b/test/functional/feature_fee_estimation.py
@@ -144,6 +144,9 @@ class EstimateFeeTest(BitcoinTestFramework):
# (68k weight is room enough for 120 or so transactions)
# Node2 is a stingy miner, that
# produces too small blocks (room for only 55 or so transactions)
+ self.start_nodes()
+ self.import_deterministic_coinbase_privkeys()
+ self.stop_nodes()
def transact_and_mine(self, numblocks, mining_node):
min_fee = Decimal("0.00001")
@@ -171,11 +174,6 @@ class EstimateFeeTest(BitcoinTestFramework):
newmem.append(utx)
self.memutxo = newmem
- def import_deterministic_coinbase_privkeys(self):
- self.start_nodes()
- super().import_deterministic_coinbase_privkeys()
- self.stop_nodes()
-
def run_test(self):
self.log.info("This test is time consuming, please be patient")
self.log.info("Splitting inputs so we can generate tx's")
diff --git a/test/functional/feature_pruning.py b/test/functional/feature_pruning.py
index 772151dc4b..c820ca33e2 100755
--- a/test/functional/feature_pruning.py
+++ b/test/functional/feature_pruning.py
@@ -63,6 +63,8 @@ class PruneTest(BitcoinTestFramework):
def setup_nodes(self):
self.add_nodes(self.num_nodes, self.extra_args)
self.start_nodes()
+ for n in self.nodes:
+ n.importprivkey(privkey=n.get_deterministic_priv_key().key, label='coinbase', rescan=False)
def create_big_chain(self):
# Start by creating some coinbases we can spend later
diff --git a/test/functional/interface_zmq.py b/test/functional/interface_zmq.py
index 48136a0108..94fea37090 100755
--- a/test/functional/interface_zmq.py
+++ b/test/functional/interface_zmq.py
@@ -69,6 +69,7 @@ class ZMQTest (BitcoinTestFramework):
]
self.add_nodes(self.num_nodes, self.extra_args)
self.start_nodes()
+ self.import_deterministic_coinbase_privkeys()
def run_test(self):
try:
@@ -121,10 +122,10 @@ class ZMQTest (BitcoinTestFramework):
self.log.info("Test the getzmqnotifications RPC")
assert_equal(self.nodes[0].getzmqnotifications(), [
- {"type": "pubhashblock", "address": ADDRESS},
- {"type": "pubhashtx", "address": ADDRESS},
- {"type": "pubrawblock", "address": ADDRESS},
- {"type": "pubrawtx", "address": ADDRESS},
+ {"type": "pubhashblock", "address": ADDRESS, "hwm": 1000},
+ {"type": "pubhashtx", "address": ADDRESS, "hwm": 1000},
+ {"type": "pubrawblock", "address": ADDRESS, "hwm": 1000},
+ {"type": "pubrawtx", "address": ADDRESS, "hwm": 1000},
])
assert_equal(self.nodes[1].getzmqnotifications(), [])
diff --git a/test/functional/mempool_resurrect.py b/test/functional/mempool_resurrect.py
index d035ca907a..845beb551e 100755
--- a/test/functional/mempool_resurrect.py
+++ b/test/functional/mempool_resurrect.py
@@ -47,12 +47,11 @@ class MempoolCoinbaseTest(BitcoinTestFramework):
tx = self.nodes[0].gettransaction(txid)
assert(tx["confirmations"] > 0)
- # Use invalidateblock to re-org back; all transactions should
- # end up unconfirmed and back in the mempool
+ # Use invalidateblock to re-org back
for node in self.nodes:
node.invalidateblock(blocks[0])
- # mempool should be empty, all txns confirmed
+ # All txns should be back in mempool with 0 confirmations
assert_equal(set(self.nodes[0].getrawmempool()), set(spends1_id+spends2_id))
for txid in spends1_id+spends2_id:
tx = self.nodes[0].gettransaction(txid)
diff --git a/test/functional/p2p_invalid_messages.py b/test/functional/p2p_invalid_messages.py
new file mode 100755
index 0000000000..85f035628f
--- /dev/null
+++ b/test/functional/p2p_invalid_messages.py
@@ -0,0 +1,175 @@
+#!/usr/bin/env python3
+# Copyright (c) 2015-2018 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 node responses to invalid network messages."""
+import struct
+
+from test_framework import messages
+from test_framework.mininode import P2PDataStore
+from test_framework.test_framework import BitcoinTestFramework
+
+
+class msg_unrecognized:
+ """Nonsensical message. Modeled after similar types in test_framework.messages."""
+
+ command = b'badmsg'
+
+ def __init__(self, str_data):
+ self.str_data = str_data.encode() if not isinstance(str_data, bytes) else str_data
+
+ def serialize(self):
+ return messages.ser_string(self.str_data)
+
+ def __repr__(self):
+ return "{}(data={})".format(self.command, self.str_data)
+
+
+class msg_nametoolong(msg_unrecognized):
+
+ command = b'thisnameiswayyyyyyyyytoolong'
+
+
+class InvalidMessagesTest(BitcoinTestFramework):
+
+ def set_test_params(self):
+ self.num_nodes = 1
+ self.setup_clean_chain = True
+
+ def run_test(self):
+ """
+ 0. Send a bunch of large (4MB) messages of an unrecognized type. Check to see
+ that it isn't an effective DoS against the node.
+
+ 1. Send an oversized (4MB+) message and check that we're disconnected.
+
+ 2. Send a few messages with an incorrect data size in the header, ensure the
+ messages are ignored.
+
+ 3. Send an unrecognized message with a command name longer than 12 characters.
+
+ """
+ node = self.nodes[0]
+ self.node = node
+ node.add_p2p_connection(P2PDataStore())
+ conn2 = node.add_p2p_connection(P2PDataStore())
+
+ msg_limit = 4 * 1000 * 1000 # 4MB, per MAX_PROTOCOL_MESSAGE_LENGTH
+ valid_data_limit = msg_limit - 5 # Account for the 4-byte length prefix
+
+ #
+ # 0.
+ #
+ # Send as large a message as is valid, ensure we aren't disconnected but
+ # also can't exhaust resources.
+ #
+ msg_at_size = msg_unrecognized("b" * valid_data_limit)
+ assert len(msg_at_size.serialize()) == msg_limit
+
+ with node.assert_memory_usage_stable(perc_increase_allowed=0.03):
+ self.log.info(
+ "Sending a bunch of large, junk messages to test "
+ "memory exhaustion. May take a bit...")
+
+ # Run a bunch of times to test for memory exhaustion.
+ for _ in range(200):
+ node.p2p.send_message(msg_at_size)
+
+ # Check that, even though the node is being hammered by nonsense from one
+ # connection, it can still service other peers in a timely way.
+ for _ in range(20):
+ conn2.sync_with_ping(timeout=2)
+
+ # Peer 1, despite serving up a bunch of nonsense, should still be connected.
+ self.log.info("Waiting for node to drop junk messages.")
+ node.p2p.sync_with_ping(timeout=8)
+ assert node.p2p.is_connected
+
+ #
+ # 1.
+ #
+ # Send an oversized message, ensure we're disconnected.
+ #
+ msg_over_size = msg_unrecognized("b" * (valid_data_limit + 1))
+ assert len(msg_over_size.serialize()) == (msg_limit + 1)
+
+ with node.assert_debug_log(["Oversized message from peer=0, disconnecting"]):
+ # An unknown message type (or *any* message type) over
+ # MAX_PROTOCOL_MESSAGE_LENGTH should result in a disconnect.
+ node.p2p.send_message(msg_over_size)
+ node.p2p.wait_for_disconnect(timeout=4)
+
+ node.disconnect_p2ps()
+ conn = node.add_p2p_connection(P2PDataStore())
+ conn.wait_for_verack()
+
+ #
+ # 2.
+ #
+ # Send messages with an incorrect data size in the header.
+ #
+ actual_size = 100
+ msg = msg_unrecognized("b" * actual_size)
+
+ # TODO: handle larger-than cases. I haven't been able to pin down what behavior to expect.
+ for wrong_size in (2, 77, 78, 79):
+ self.log.info("Sending a message with incorrect size of {}".format(wrong_size))
+
+ # Unmodified message should submit okay.
+ node.p2p.send_and_ping(msg)
+
+ # A message lying about its data size results in a disconnect when the incorrect
+ # data size is less than the actual size.
+ #
+ # TODO: why does behavior change at 78 bytes?
+ #
+ node.p2p.send_raw_message(self._tweak_msg_data_size(msg, wrong_size))
+
+ # For some reason unknown to me, we sometimes have to push additional data to the
+ # peer in order for it to realize a disconnect.
+ try:
+ node.p2p.send_message(messages.msg_ping(nonce=123123))
+ except IOError:
+ pass
+
+ node.p2p.wait_for_disconnect(timeout=10)
+ node.disconnect_p2ps()
+ node.add_p2p_connection(P2PDataStore())
+
+ #
+ # 3.
+ #
+ # Send a message with a too-long command name.
+ #
+ node.p2p.send_message(msg_nametoolong("foobar"))
+ node.p2p.wait_for_disconnect(timeout=4)
+
+ # Node is still up.
+ conn = node.add_p2p_connection(P2PDataStore())
+ conn.sync_with_ping()
+
+
+ def _tweak_msg_data_size(self, message, wrong_size):
+ """
+ Return a raw message based on another message but with an incorrect data size in
+ the message header.
+ """
+ raw_msg = self.node.p2p.build_message(message)
+
+ bad_size_bytes = struct.pack("<I", wrong_size)
+ num_header_bytes_before_size = 4 + 12
+
+ # Replace the correct data size in the message with an incorrect one.
+ raw_msg_with_wrong_size = (
+ raw_msg[:num_header_bytes_before_size] +
+ bad_size_bytes +
+ raw_msg[(num_header_bytes_before_size + len(bad_size_bytes)):]
+ )
+ assert len(raw_msg) == len(raw_msg_with_wrong_size)
+
+ return raw_msg_with_wrong_size
+
+
+
+if __name__ == '__main__':
+ InvalidMessagesTest().main()
diff --git a/test/functional/p2p_node_network_limited.py b/test/functional/p2p_node_network_limited.py
index ec3d336dc1..359880506e 100755
--- a/test/functional/p2p_node_network_limited.py
+++ b/test/functional/p2p_node_network_limited.py
@@ -43,8 +43,8 @@ class NodeNetworkLimitedTest(BitcoinTestFramework):
disconnect_nodes(self.nodes[1], 2)
def setup_network(self):
- super(NodeNetworkLimitedTest, self).setup_network()
- self.disconnect_all()
+ self.add_nodes(self.num_nodes, self.extra_args)
+ self.start_nodes()
def run_test(self):
node = self.nodes[0].add_p2p_connection(P2PIgnoreInv())
diff --git a/test/functional/rpc_help.py b/test/functional/rpc_help.py
index be096af892..78d6e78aed 100755
--- a/test/functional/rpc_help.py
+++ b/test/functional/rpc_help.py
@@ -7,12 +7,18 @@
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, assert_raises_rpc_error
+import os
+
class HelpRpcTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
def run_test(self):
+ self.test_categories()
+ self.dump_help()
+
+ def test_categories(self):
node = self.nodes[0]
# wrong argument count
@@ -37,6 +43,15 @@ class HelpRpcTest(BitcoinTestFramework):
assert_equal(titles, components)
+ def dump_help(self):
+ dump_dir = os.path.join(self.options.tmpdir, 'rpc_help_dump')
+ os.mkdir(dump_dir)
+ calls = [line.split(' ', 1)[0] for line in self.nodes[0].help().splitlines() if line and not line.startswith('==')]
+ for call in calls:
+ with open(os.path.join(dump_dir, call), 'w', encoding='utf-8') as f:
+ # Make sure the node can generate the help at runtime without crashing
+ f.write(self.nodes[0].help(call))
+
if __name__ == '__main__':
HelpRpcTest().main()
diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py
index 54dc871448..fca910bf64 100755
--- a/test/functional/rpc_psbt.py
+++ b/test/functional/rpc_psbt.py
@@ -6,7 +6,7 @@
"""
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal, assert_raises_rpc_error, find_output
+from test_framework.util import assert_equal, assert_raises_rpc_error, find_output, disconnect_nodes, connect_nodes_bi, sync_blocks
import json
import os
@@ -23,6 +23,45 @@ class PSBTTest(BitcoinTestFramework):
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
+ def test_utxo_conversion(self):
+ mining_node = self.nodes[2]
+ offline_node = self.nodes[0]
+ online_node = self.nodes[1]
+
+ # Disconnect offline node from others
+ disconnect_nodes(offline_node, 1)
+ disconnect_nodes(online_node, 0)
+ disconnect_nodes(offline_node, 2)
+ disconnect_nodes(mining_node, 0)
+
+ # Mine a transaction that credits the offline address
+ offline_addr = offline_node.getnewaddress(address_type="p2sh-segwit")
+ online_addr = online_node.getnewaddress(address_type="p2sh-segwit")
+ online_node.importaddress(offline_addr, "", False)
+ mining_node.sendtoaddress(address=offline_addr, amount=1.0)
+ mining_node.generate(nblocks=1)
+ sync_blocks([mining_node, online_node])
+
+ # Construct an unsigned PSBT on the online node (who doesn't know the output is Segwit, so will include a non-witness UTXO)
+ utxos = online_node.listunspent(addresses=[offline_addr])
+ raw = online_node.createrawtransaction([{"txid":utxos[0]["txid"], "vout":utxos[0]["vout"]}],[{online_addr:0.9999}])
+ psbt = online_node.walletprocesspsbt(online_node.converttopsbt(raw))["psbt"]
+ assert("non_witness_utxo" in mining_node.decodepsbt(psbt)["inputs"][0])
+
+ # Have the offline node sign the PSBT (which will update the UTXO to segwit)
+ signed_psbt = offline_node.walletprocesspsbt(psbt)["psbt"]
+ assert("witness_utxo" in mining_node.decodepsbt(signed_psbt)["inputs"][0])
+
+ # Make sure we can mine the resulting transaction
+ txid = mining_node.sendrawtransaction(mining_node.finalizepsbt(signed_psbt)["hex"])
+ mining_node.generate(1)
+ sync_blocks([mining_node, online_node])
+ assert_equal(online_node.gettxout(txid,0)["confirmations"], 1)
+
+ # Reconnect
+ connect_nodes_bi(self.nodes, 0, 1)
+ connect_nodes_bi(self.nodes, 0, 2)
+
def run_test(self):
# Create and fund a raw tx for sending 10 BTC
psbtx1 = self.nodes[0].walletcreatefundedpsbt([], {self.nodes[2].getnewaddress():10})['psbt']
@@ -224,6 +263,12 @@ class PSBTTest(BitcoinTestFramework):
extracted = self.nodes[2].finalizepsbt(extractor['extract'], True)['hex']
assert_equal(extracted, extractor['result'])
+ # Unload extra wallets
+ for i, signer in enumerate(signers):
+ self.nodes[2].unloadwallet("wallet{}".format(i))
+
+ self.test_utxo_conversion()
+
if __name__ == '__main__':
PSBTTest().main()
diff --git a/test/functional/test_framework/blocktools.py b/test/functional/test_framework/blocktools.py
index 35004fb588..81cce1167b 100644
--- a/test/functional/test_framework/blocktools.py
+++ b/test/functional/test_framework/blocktools.py
@@ -169,7 +169,7 @@ def get_legacy_sigopcount_tx(tx, accurate=True):
return count
def witness_script(use_p2wsh, pubkey):
- """Create a scriptPubKey for a pay-to-wtiness TxOut.
+ """Create a scriptPubKey for a pay-to-witness TxOut.
This is either a P2WPKH output for the given pubkey, or a P2WSH output of a
1-of-1 multisig for the given pubkey. Returns the hex encoding of the
diff --git a/test/functional/test_framework/mininode.py b/test/functional/test_framework/mininode.py
index 91fde136de..1e07c2ff60 100755
--- a/test/functional/test_framework/mininode.py
+++ b/test/functional/test_framework/mininode.py
@@ -207,10 +207,13 @@ class P2PConnection(asyncio.Protocol):
This method takes a P2P payload, builds the P2P header and adds
the message to the send buffer to be sent over the socket."""
+ tmsg = self.build_message(message)
+ self._log_message("send", message)
+ return self.send_raw_message(tmsg)
+
+ def send_raw_message(self, raw_message_bytes):
if not self.is_connected:
raise IOError('Not connected')
- self._log_message("send", message)
- tmsg = self._build_message(message)
def maybe_write():
if not self._transport:
@@ -220,12 +223,12 @@ class P2PConnection(asyncio.Protocol):
# Python 3.4 versions.
if hasattr(self._transport, 'is_closing') and self._transport.is_closing():
return
- self._transport.write(tmsg)
+ self._transport.write(raw_message_bytes)
NetworkThread.network_event_loop.call_soon_threadsafe(maybe_write)
# Class utility methods
- def _build_message(self, message):
+ def build_message(self, message):
"""Build a serialized P2P message"""
command = message.command
data = message.serialize()
@@ -409,9 +412,9 @@ class P2PInterface(P2PConnection):
# Message sending helper functions
- def send_and_ping(self, message):
+ def send_and_ping(self, message, timeout=60):
self.send_message(message)
- self.sync_with_ping()
+ self.sync_with_ping(timeout=timeout)
# Sync up with the node
def sync_with_ping(self, timeout=60):
diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py
index 7e2ec673df..44fc185e6d 100755
--- a/test/functional/test_framework/test_framework.py
+++ b/test/functional/test_framework/test_framework.py
@@ -168,7 +168,6 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
self.skip_test_if_missing_module()
self.setup_chain()
self.setup_network()
- self.import_deterministic_coinbase_privkeys()
self.run_test()
success = TestStatus.PASSED
except JSONRPCException as e:
@@ -261,11 +260,9 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
extra_args = self.extra_args
self.add_nodes(self.num_nodes, extra_args)
self.start_nodes()
+ self.import_deterministic_coinbase_privkeys()
def import_deterministic_coinbase_privkeys(self):
- if self.setup_clean_chain:
- return
-
for n in self.nodes:
try:
n.getwalletinfo()
@@ -273,7 +270,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
assert str(e).startswith('Method not found')
continue
- n.importprivkey(n.get_deterministic_priv_key().key)
+ n.importprivkey(privkey=n.get_deterministic_priv_key().key, label='coinbase')
def run_test(self):
"""Tests must override this method to define test logic"""
diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index cc1bfabbfa..ffff81e070 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -115,6 +115,28 @@ class TestNode():
]
return PRIV_KEYS[self.index]
+ def get_mem_rss(self):
+ """Get the memory usage (RSS) per `ps`.
+
+ If process is stopped or `ps` is unavailable, return None.
+ """
+ if not (self.running and self.process):
+ self.log.warning("Couldn't get memory usage; process isn't running.")
+ return None
+
+ try:
+ return int(subprocess.check_output(
+ "ps h -o rss {}".format(self.process.pid),
+ shell=True, stderr=subprocess.DEVNULL).strip())
+
+ # Catching `Exception` broadly to avoid failing on platforms where ps
+ # isn't installed or doesn't work as expected, e.g. OpenBSD.
+ #
+ # We could later use something like `psutils` to work across platforms.
+ except Exception:
+ self.log.exception("Unable to get memory usage")
+ return None
+
def _node_msg(self, msg: str) -> str:
"""Return a modified msg that identifies this node by its index as a debugging aid."""
return "[node %d] %s" % (self.index, msg)
@@ -199,21 +221,6 @@ class TestNode():
def generate(self, nblocks, maxtries=1000000):
self.log.debug("TestNode.generate() dispatches `generate` call to `generatetoaddress`")
- # Try to import the node's deterministic private key. This is a no-op if the private key
- # has already been imported.
- try:
- self.rpc.importprivkey(privkey=self.get_deterministic_priv_key().key, label='coinbase', rescan=False)
- except JSONRPCException as e:
- # This may fail if:
- # - wallet is disabled ('Method not found')
- # - there are multiple wallets to import to ('Wallet file not specified')
- # - wallet is locked ('Error: Please enter the wallet passphrase with walletpassphrase first')
- # Just ignore those errors. We can make this tidier by importing the privkey during TestFramework.setup_nodes
- # TODO: tidy up deterministic privkey import.
- assert str(e).startswith('Method not found') or \
- str(e).startswith('Wallet file not specified') or \
- str(e).startswith('Error: Please enter the wallet passphrase with walletpassphrase first')
-
return self.generatetoaddress(nblocks=nblocks, address=self.get_deterministic_priv_key().address, maxtries=maxtries)
def get_wallet_rpc(self, wallet_name):
@@ -286,6 +293,29 @@ class TestNode():
if re.search(re.escape(expected_msg), log, flags=re.MULTILINE) is None:
self._raise_assertion_error('Expected message "{}" does not partially match log:\n\n{}\n\n'.format(expected_msg, print_log))
+ @contextlib.contextmanager
+ def assert_memory_usage_stable(self, perc_increase_allowed=0.03):
+ """Context manager that allows the user to assert that a node's memory usage (RSS)
+ hasn't increased beyond some threshold percentage.
+ """
+ before_memory_usage = self.get_mem_rss()
+
+ yield
+
+ after_memory_usage = self.get_mem_rss()
+
+ if not (before_memory_usage and after_memory_usage):
+ self.log.warning("Unable to detect memory usage (RSS) - skipping memory check.")
+ return
+
+ perc_increase_memory_usage = 1 - (float(before_memory_usage) / after_memory_usage)
+
+ if perc_increase_memory_usage > perc_increase_allowed:
+ self._raise_assertion_error(
+ "Memory usage increased over threshold of {:.3f}% from {} to {} ({:.3f}%)".format(
+ perc_increase_allowed * 100, before_memory_usage, after_memory_usage,
+ perc_increase_memory_usage * 100))
+
def assert_start_raises_init_error(self, extra_args=None, expected_msg=None, match=ErrorMatch.FULL_TEXT, *args, **kwargs):
"""Attempt to start the node and expect it to raise an error.
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 3ec46c76ac..b4fd828142 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -68,9 +68,6 @@ if os.name != 'nt' or sys.getwindowsversion() >= (10, 0, 14393):
TEST_EXIT_PASSED = 0
TEST_EXIT_SKIPPED = 77
-# 20 minutes represented in seconds
-TRAVIS_TIMEOUT_DURATION = 20 * 60
-
BASE_SCRIPTS = [
# Scripts that are run by the travis build process.
# Longest test should go first, to favor running tests in parallel
@@ -139,6 +136,7 @@ BASE_SCRIPTS = [
'mining_prioritisetransaction.py',
'p2p_invalid_locator.py',
'p2p_invalid_block.py',
+ 'p2p_invalid_messages.py',
'p2p_invalid_tx.py',
'feature_assumevalid.py',
'example_test.py',
@@ -216,13 +214,14 @@ def main():
formatter_class=argparse.RawTextHelpFormatter)
parser.add_argument('--combinedlogslen', '-c', type=int, default=0, metavar='n', help='On failure, print a log (of length n lines) to the console, combined from the test framework and all test nodes.')
parser.add_argument('--coverage', action='store_true', help='generate a basic coverage report for the RPC interface')
+ parser.add_argument('--ci', action='store_true', help='Run checks and code that are usually only enabled in a continuous integration environment')
parser.add_argument('--exclude', '-x', help='specify a comma-separated-list of scripts to exclude.')
parser.add_argument('--extended', action='store_true', help='run the extended test suite in addition to the basic tests')
parser.add_argument('--force', '-f', action='store_true', help='run tests even on platforms where they are disabled by default (e.g. windows).')
parser.add_argument('--help', '-h', '-?', action='store_true', help='print help text and exit')
parser.add_argument('--jobs', '-j', type=int, default=4, help='how many test scripts to run in parallel. Default=4.')
parser.add_argument('--keepcache', '-k', action='store_true', help='the default behavior is to flush the cache directory on startup. --keepcache retains the cache from the previous testrun.')
- parser.add_argument('--quiet', '-q', action='store_true', help='only print results summary and failure logs')
+ parser.add_argument('--quiet', '-q', action='store_true', help='only print dots, results summary and failure logs')
parser.add_argument('--tmpdirprefix', '-t', default=tempfile.gettempdir(), help="Root directory for datadirs")
parser.add_argument('--failfast', action='store_true', help='stop execution after the first test failure')
args, unknown_args = parser.parse_known_args()
@@ -306,26 +305,26 @@ def main():
subprocess.check_call([sys.executable, os.path.join(config["environment"]["SRCDIR"], 'test', 'functional', test_list[0].split()[0]), '-h'])
sys.exit(0)
- check_script_list(config["environment"]["SRCDIR"])
+ check_script_list(src_dir=config["environment"]["SRCDIR"], fail_on_warn=args.ci)
check_script_prefixes()
if not args.keepcache:
shutil.rmtree("%s/test/cache" % config["environment"]["BUILDDIR"], ignore_errors=True)
run_tests(
- test_list,
- config["environment"]["SRCDIR"],
- config["environment"]["BUILDDIR"],
- tmpdir,
+ test_list=test_list,
+ src_dir=config["environment"]["SRCDIR"],
+ build_dir=config["environment"]["BUILDDIR"],
+ tmpdir=tmpdir,
jobs=args.jobs,
enable_coverage=args.coverage,
args=passon_args,
combined_logs_len=args.combinedlogslen,
failfast=args.failfast,
- level=logging_level
+ runs_ci=args.ci,
)
-def run_tests(test_list, src_dir, build_dir, tmpdir, jobs=1, enable_coverage=False, args=None, combined_logs_len=0, failfast=False, level=logging.DEBUG):
+def run_tests(*, test_list, src_dir, build_dir, tmpdir, jobs=1, enable_coverage=False, args=None, combined_logs_len=0, failfast=False, runs_ci):
args = args or []
# Warn if bitcoind is already running (unix only)
@@ -360,7 +359,14 @@ def run_tests(test_list, src_dir, build_dir, tmpdir, jobs=1, enable_coverage=Fal
raise
#Run Tests
- job_queue = TestHandler(jobs, tests_dir, tmpdir, test_list, flags, level)
+ job_queue = TestHandler(
+ num_tests_parallel=jobs,
+ tests_dir=tests_dir,
+ tmpdir=tmpdir,
+ test_list=test_list,
+ flags=flags,
+ timeout_duration=20 * 60 if runs_ci else float('inf'), # in seconds
+ )
start_time = time.time()
test_results = []
@@ -441,14 +447,14 @@ class TestHandler:
Trigger the test scripts passed in via the list.
"""
- def __init__(self, num_tests_parallel, tests_dir, tmpdir, test_list=None, flags=None, logging_level=logging.DEBUG):
- assert(num_tests_parallel >= 1)
+ def __init__(self, *, num_tests_parallel, tests_dir, tmpdir, test_list, flags, timeout_duration):
+ assert num_tests_parallel >= 1
self.num_jobs = num_tests_parallel
self.tests_dir = tests_dir
self.tmpdir = tmpdir
+ self.timeout_duration = timeout_duration
self.test_list = test_list
self.flags = flags
- self.logging_level = logging_level
self.num_running = 0
self.jobs = []
@@ -481,7 +487,7 @@ class TestHandler:
time.sleep(.5)
for job in self.jobs:
(name, start_time, proc, testdir, log_out, log_err) = job
- if os.getenv('TRAVIS') == 'true' and int(time.time() - start_time) > TRAVIS_TIMEOUT_DURATION:
+ if int(time.time() - start_time) > self.timeout_duration:
# In travis, timeout individual tests (to stop tests hanging and not providing useful output).
proc.send_signal(signal.SIGINT)
if proc.poll() is not None:
@@ -496,14 +502,12 @@ class TestHandler:
status = "Failed"
self.num_running -= 1
self.jobs.remove(job)
- if self.logging_level == logging.DEBUG:
- clearline = '\r' + (' ' * dot_count) + '\r'
- print(clearline, end='', flush=True)
- dot_count = 0
+ clearline = '\r' + (' ' * dot_count) + '\r'
+ print(clearline, end='', flush=True)
+ dot_count = 0
return TestResult(name, status, int(time.time() - start_time)), testdir, stdout, stderr
- if self.logging_level == logging.DEBUG:
- print('.', end='', flush=True)
- dot_count += 1
+ print('.', end='', flush=True)
+ dot_count += 1
def kill_and_join(self):
"""Send SIGKILL to all jobs and block until all have ended."""
@@ -561,7 +565,7 @@ def check_script_prefixes():
raise AssertionError("Some tests are not following naming convention!")
-def check_script_list(src_dir):
+def check_script_list(*, src_dir, fail_on_warn):
"""Check scripts directory.
Check that there are no scripts in the functional tests directory which are
@@ -571,10 +575,11 @@ def check_script_list(src_dir):
missed_tests = list(python_files - set(map(lambda x: x.split()[0], ALL_SCRIPTS + NON_SCRIPTS)))
if len(missed_tests) != 0:
print("%sWARNING!%s The following scripts are not being run: %s. Check the test lists in test_runner.py." % (BOLD[1], BOLD[0], str(missed_tests)))
- if os.getenv('TRAVIS') == 'true':
+ if fail_on_warn:
# On travis this warning is an error to prevent merging incomplete commits into master
sys.exit(1)
+
class RPCCoverage():
"""
Coverage reporting utilities for test_runner.
diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py
index 4de3356d79..c9b40905f0 100755
--- a/test/functional/wallet_basic.py
+++ b/test/functional/wallet_basic.py
@@ -28,10 +28,9 @@ class WalletTest(BitcoinTestFramework):
self.skip_if_no_wallet()
def setup_network(self):
- self.add_nodes(4)
- self.start_node(0)
- self.start_node(1)
- self.start_node(2)
+ self.setup_nodes()
+ # Only need nodes 0-2 running at start of test
+ self.stop_node(3)
connect_nodes_bi(self.nodes, 0, 1)
connect_nodes_bi(self.nodes, 1, 2)
connect_nodes_bi(self.nodes, 0, 2)
@@ -480,7 +479,7 @@ class WalletTest(BitcoinTestFramework):
# Verify nothing new in wallet
assert_equal(total_txs, len(self.nodes[0].listtransactions("*", 99999)))
- # Test getaddressinfo. Note that these addresses are taken from disablewallet.py
+ # Test getaddressinfo on external address. Note that these addresses are taken from disablewallet.py
assert_raises_rpc_error(-5, "Invalid address", self.nodes[0].getaddressinfo, "3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy")
address_info = self.nodes[0].getaddressinfo("mneYUmWYsuk7kySiURxCi3AGxrAqZxLgPZ")
assert_equal(address_info['address'], "mneYUmWYsuk7kySiURxCi3AGxrAqZxLgPZ")
@@ -488,6 +487,22 @@ class WalletTest(BitcoinTestFramework):
assert not address_info["ismine"]
assert not address_info["iswatchonly"]
assert not address_info["isscript"]
+ assert not address_info["ischange"]
+
+ # Test getaddressinfo 'ischange' field on change address.
+ self.nodes[0].generate(1)
+ destination = self.nodes[1].getnewaddress()
+ txid = self.nodes[0].sendtoaddress(destination, 0.123)
+ tx = self.nodes[0].decoderawtransaction(self.nodes[0].getrawtransaction(txid))
+ output_addresses = [vout['scriptPubKey']['addresses'][0] for vout in tx["vout"]]
+ assert len(output_addresses) > 1
+ for address in output_addresses:
+ ischange = self.nodes[0].getaddressinfo(address)['ischange']
+ assert_equal(ischange, address != destination)
+ if ischange:
+ change = address
+ self.nodes[0].setlabel(change, 'foobar')
+ assert_equal(self.nodes[0].getaddressinfo(change)['ischange'], False)
if __name__ == '__main__':
WalletTest().main()
diff --git a/test/functional/wallet_import_rescan.py b/test/functional/wallet_import_rescan.py
index f043a544fc..08809a688a 100755
--- a/test/functional/wallet_import_rescan.py
+++ b/test/functional/wallet_import_rescan.py
@@ -122,16 +122,14 @@ class ImportRescanTest(BitcoinTestFramework):
# Import keys with pruning disabled
self.start_nodes(extra_args=[[]] * self.num_nodes)
- super().import_deterministic_coinbase_privkeys()
+ for n in self.nodes:
+ n.importprivkey(privkey=n.get_deterministic_priv_key().key, label='coinbase')
self.stop_nodes()
self.start_nodes()
for i in range(1, self.num_nodes):
connect_nodes(self.nodes[i], 0)
- def import_deterministic_coinbase_privkeys(self):
- pass
-
def run_test(self):
# Create one transaction on node 0 with a unique amount for
# each possible type of wallet import RPC.
diff --git a/test/functional/wallet_importmulti.py b/test/functional/wallet_importmulti.py
index f78ff19791..9ba6860306 100755
--- a/test/functional/wallet_importmulti.py
+++ b/test/functional/wallet_importmulti.py
@@ -11,8 +11,14 @@ from test_framework.util import (
assert_greater_than,
assert_raises_rpc_error,
bytes_to_hex_str,
+ hex_str_to_bytes
)
-
+from test_framework.script import (
+ CScript,
+ OP_0,
+ hash160
+)
+from test_framework.messages import sha256
class ImportMultiTest(BitcoinTestFramework):
def set_test_params(self):
@@ -90,6 +96,19 @@ class ImportMultiTest(BitcoinTestFramework):
assert_equal(address_assert['ismine'], False)
assert_equal(address_assert['timestamp'], timestamp)
+ # ScriptPubKey + internal + label
+ self.log.info("Should not allow a label to be specified when internal is true")
+ address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
+ result = self.nodes[1].importmulti([{
+ "scriptPubKey": address['scriptPubKey'],
+ "timestamp": "now",
+ "internal": True,
+ "label": "Example label"
+ }])
+ assert_equal(result[0]['success'], False)
+ assert_equal(result[0]['error']['code'], -8)
+ assert_equal(result[0]['error']['message'], 'Internal addresses should not have a label')
+
# Nonstandard scriptPubKey + !internal
self.log.info("Should not import a nonstandard scriptPubKey without internal flag")
nonstandardScriptPubKey = address['scriptPubKey'] + bytes_to_hex_str(script.CScript([script.OP_NOP]))
@@ -198,7 +217,7 @@ class ImportMultiTest(BitcoinTestFramework):
}])
assert_equal(result[0]['success'], False)
assert_equal(result[0]['error']['code'], -8)
- assert_equal(result[0]['error']['message'], 'Incompatibility found between watchonly and keys')
+ assert_equal(result[0]['error']['message'], 'Watch-only addresses should not include private keys')
address_assert = self.nodes[1].getaddressinfo(address['address'])
assert_equal(address_assert['iswatchonly'], False)
assert_equal(address_assert['ismine'], False)
@@ -339,7 +358,7 @@ class ImportMultiTest(BitcoinTestFramework):
}])
assert_equal(result[0]['success'], False)
assert_equal(result[0]['error']['code'], -8)
- assert_equal(result[0]['error']['message'], 'Incompatibility found between watchonly and keys')
+ assert_equal(result[0]['error']['message'], 'Watch-only addresses should not include private keys')
# Address + Public key + !Internal + Wrong pubkey
@@ -355,7 +374,7 @@ class ImportMultiTest(BitcoinTestFramework):
}])
assert_equal(result[0]['success'], False)
assert_equal(result[0]['error']['code'], -5)
- assert_equal(result[0]['error']['message'], 'Consistency check failed')
+ assert_equal(result[0]['error']['message'], 'Key does not match address destination')
address_assert = self.nodes[1].getaddressinfo(address['address'])
assert_equal(address_assert['iswatchonly'], False)
assert_equal(address_assert['ismine'], False)
@@ -375,7 +394,7 @@ class ImportMultiTest(BitcoinTestFramework):
result = self.nodes[1].importmulti(request)
assert_equal(result[0]['success'], False)
assert_equal(result[0]['error']['code'], -5)
- assert_equal(result[0]['error']['message'], 'Consistency check failed')
+ assert_equal(result[0]['error']['message'], 'Key does not match address destination')
address_assert = self.nodes[1].getaddressinfo(address['address'])
assert_equal(address_assert['iswatchonly'], False)
assert_equal(address_assert['ismine'], False)
@@ -395,7 +414,7 @@ class ImportMultiTest(BitcoinTestFramework):
}])
assert_equal(result[0]['success'], False)
assert_equal(result[0]['error']['code'], -5)
- assert_equal(result[0]['error']['message'], 'Consistency check failed')
+ assert_equal(result[0]['error']['message'], 'Key does not match address destination')
address_assert = self.nodes[1].getaddressinfo(address['address'])
assert_equal(address_assert['iswatchonly'], False)
assert_equal(address_assert['ismine'], False)
@@ -414,7 +433,7 @@ class ImportMultiTest(BitcoinTestFramework):
}])
assert_equal(result[0]['success'], False)
assert_equal(result[0]['error']['code'], -5)
- assert_equal(result[0]['error']['message'], 'Consistency check failed')
+ assert_equal(result[0]['error']['message'], 'Key does not match address destination')
address_assert = self.nodes[1].getaddressinfo(address['address'])
assert_equal(address_assert['iswatchonly'], False)
assert_equal(address_assert['ismine'], False)
@@ -458,6 +477,147 @@ class ImportMultiTest(BitcoinTestFramework):
"timestamp": "",
}])
+ # Import P2WPKH address as watch only
+ self.log.info("Should import a P2WPKH address as watch only")
+ address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress(address_type="bech32"))
+ result = self.nodes[1].importmulti([{
+ "scriptPubKey": {
+ "address": address['address']
+ },
+ "timestamp": "now",
+ }])
+ assert_equal(result[0]['success'], True)
+ address_assert = self.nodes[1].getaddressinfo(address['address'])
+ assert_equal(address_assert['iswatchonly'], True)
+ assert_equal(address_assert['solvable'], False)
+
+ # Import P2WPKH address with public key but no private key
+ self.log.info("Should import a P2WPKH address and public key as solvable but not spendable")
+ address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress(address_type="bech32"))
+ result = self.nodes[1].importmulti([{
+ "scriptPubKey": {
+ "address": address['address']
+ },
+ "timestamp": "now",
+ "pubkeys": [ address['pubkey'] ]
+ }])
+ assert_equal(result[0]['success'], True)
+ address_assert = self.nodes[1].getaddressinfo(address['address'])
+ assert_equal(address_assert['ismine'], False)
+ assert_equal(address_assert['solvable'], True)
+
+ # Import P2WPKH address with key and check it is spendable
+ self.log.info("Should import a P2WPKH address with key")
+ address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress(address_type="bech32"))
+ result = self.nodes[1].importmulti([{
+ "scriptPubKey": {
+ "address": address['address']
+ },
+ "timestamp": "now",
+ "keys": [self.nodes[0].dumpprivkey(address['address'])]
+ }])
+ assert_equal(result[0]['success'], True)
+ address_assert = self.nodes[1].getaddressinfo(address['address'])
+ assert_equal(address_assert['iswatchonly'], False)
+ assert_equal(address_assert['ismine'], True)
+
+ # P2WSH multisig address without scripts or keys
+ sig_address_1 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
+ sig_address_2 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
+ multi_sig_script = self.nodes[0].addmultisigaddress(2, [sig_address_1['pubkey'], sig_address_2['pubkey']], "", "bech32")
+ self.log.info("Should import a p2wsh multisig as watch only without respective redeem script and private keys")
+ result = self.nodes[1].importmulti([{
+ "scriptPubKey": {
+ "address": multi_sig_script['address']
+ },
+ "timestamp": "now"
+ }])
+ assert_equal(result[0]['success'], True)
+ address_assert = self.nodes[1].getaddressinfo(multi_sig_script['address'])
+ assert_equal(address_assert['solvable'], False)
+
+ # Same P2WSH multisig address as above, but now with witnessscript + private keys
+ self.log.info("Should import a p2wsh with respective redeem script and private keys")
+ result = self.nodes[1].importmulti([{
+ "scriptPubKey": {
+ "address": multi_sig_script['address']
+ },
+ "timestamp": "now",
+ "witnessscript": multi_sig_script['redeemScript'],
+ "keys": [ self.nodes[0].dumpprivkey(sig_address_1['address']), self.nodes[0].dumpprivkey(sig_address_2['address']) ]
+ }])
+ assert_equal(result[0]['success'], True)
+ address_assert = self.nodes[1].getaddressinfo(multi_sig_script['address'])
+ assert_equal(address_assert['solvable'], True)
+ assert_equal(address_assert['ismine'], True)
+ assert_equal(address_assert['sigsrequired'], 2)
+
+ # P2SH-P2WPKH address with no redeemscript or public or private key
+ sig_address_1 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress(address_type="p2sh-segwit"))
+ pubkeyhash = hash160(hex_str_to_bytes(sig_address_1['pubkey']))
+ pkscript = CScript([OP_0, pubkeyhash])
+ self.log.info("Should import a p2sh-p2wpkh without redeem script or keys")
+ result = self.nodes[1].importmulti([{
+ "scriptPubKey": {
+ "address": sig_address_1['address']
+ },
+ "timestamp": "now"
+ }])
+ assert_equal(result[0]['success'], True)
+ address_assert = self.nodes[1].getaddressinfo(sig_address_1['address'])
+ assert_equal(address_assert['solvable'], False)
+ assert_equal(address_assert['ismine'], False)
+
+ # P2SH-P2WPKH address + redeemscript + public key with no private key
+ self.log.info("Should import a p2sh-p2wpkh with respective redeem script and pubkey as solvable")
+ result = self.nodes[1].importmulti([{
+ "scriptPubKey": {
+ "address": sig_address_1['address']
+ },
+ "timestamp": "now",
+ "redeemscript": bytes_to_hex_str(pkscript),
+ "pubkeys": [ sig_address_1['pubkey'] ]
+ }])
+ assert_equal(result[0]['success'], True)
+ address_assert = self.nodes[1].getaddressinfo(sig_address_1['address'])
+ assert_equal(address_assert['solvable'], True)
+ assert_equal(address_assert['ismine'], False)
+
+ # P2SH-P2WPKH address + redeemscript + private key
+ sig_address_1 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress(address_type="p2sh-segwit"))
+ pubkeyhash = hash160(hex_str_to_bytes(sig_address_1['pubkey']))
+ pkscript = CScript([OP_0, pubkeyhash])
+ self.log.info("Should import a p2sh-p2wpkh with respective redeem script and private keys")
+ result = self.nodes[1].importmulti([{
+ "scriptPubKey": {
+ "address": sig_address_1['address']
+ },
+ "timestamp": "now",
+ "redeemscript": bytes_to_hex_str(pkscript),
+ "keys": [ self.nodes[0].dumpprivkey(sig_address_1['address'])]
+ }])
+ assert_equal(result[0]['success'], True)
+ address_assert = self.nodes[1].getaddressinfo(sig_address_1['address'])
+ assert_equal(address_assert['solvable'], True)
+ assert_equal(address_assert['ismine'], True)
+
+ # P2SH-P2WSH 1-of-1 multisig + redeemscript with no private key
+ sig_address_1 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
+ multi_sig_script = self.nodes[0].addmultisigaddress(1, [sig_address_1['pubkey']], "", "p2sh-segwit")
+ scripthash = sha256(hex_str_to_bytes(multi_sig_script['redeemScript']))
+ redeem_script = CScript([OP_0, scripthash])
+ self.log.info("Should import a p2sh-p2wsh with respective redeem script but no private key")
+ result = self.nodes[1].importmulti([{
+ "scriptPubKey": {
+ "address": multi_sig_script['address']
+ },
+ "timestamp": "now",
+ "redeemscript": bytes_to_hex_str(redeem_script),
+ "witnessscript": multi_sig_script['redeemScript']
+ }])
+ assert_equal(result[0]['success'], True)
+ address_assert = self.nodes[1].getaddressinfo(multi_sig_script['address'])
+ assert_equal(address_assert['solvable'], True)
if __name__ == '__main__':
ImportMultiTest ().main ()
diff --git a/test/functional/wallet_listreceivedby.py b/test/functional/wallet_listreceivedby.py
index 9e8667c600..011975e371 100755
--- a/test/functional/wallet_listreceivedby.py
+++ b/test/functional/wallet_listreceivedby.py
@@ -18,11 +18,6 @@ class ReceivedByTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
- def import_deterministic_coinbase_privkeys(self):
- assert_equal(0, len(self.nodes[1].listreceivedbyaddress(minconf=0, include_empty=True, include_watchonly=True)))
- super().import_deterministic_coinbase_privkeys()
- self.num_cb_reward_addresses = len(self.nodes[1].listreceivedbyaddress(minconf=0, include_empty=True, include_watchonly=True))
-
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
@@ -31,6 +26,9 @@ class ReceivedByTest(BitcoinTestFramework):
self.nodes[0].generate(1)
sync_blocks(self.nodes)
+ # save the number of coinbase reward addresses so far
+ num_cb_reward_addresses = len(self.nodes[1].listreceivedbyaddress(minconf=0, include_empty=True, include_watchonly=True))
+
self.log.info("listreceivedbyaddress Test")
# Send from node 0 to 1
@@ -76,7 +74,7 @@ class ReceivedByTest(BitcoinTestFramework):
assert_raises_rpc_error(-4, "address_filter parameter was invalid", self.nodes[1].listreceivedbyaddress, minconf=0, include_empty=True, include_watchonly=True, address_filter="bamboozling")
# Another address receive money
res = self.nodes[1].listreceivedbyaddress(0, True, True)
- assert_equal(len(res), 2 + self.num_cb_reward_addresses) # Right now 2 entries
+ assert_equal(len(res), 2 + num_cb_reward_addresses) # Right now 2 entries
other_addr = self.nodes[1].getnewaddress()
txid2 = self.nodes[0].sendtoaddress(other_addr, 0.1)
self.nodes[0].generate(1)
@@ -93,7 +91,7 @@ class ReceivedByTest(BitcoinTestFramework):
assert_equal(len(res), 1)
# Should be two entries though without filter
res = self.nodes[1].listreceivedbyaddress(0, True, True)
- assert_equal(len(res), 3 + self.num_cb_reward_addresses) # Became 3 entries
+ assert_equal(len(res), 3 + num_cb_reward_addresses) # Became 3 entries
# Not on random addr
other_addr = self.nodes[0].getnewaddress() # note on node[0]! just a random addr
diff --git a/test/lint/lint-circular-dependencies.sh b/test/lint/lint-circular-dependencies.sh
index 3972baed1d..be67cbed31 100755
--- a/test/lint/lint-circular-dependencies.sh
+++ b/test/lint/lint-circular-dependencies.sh
@@ -9,7 +9,7 @@
export LC_ALL=C
EXPECTED_CIRCULAR_DEPENDENCIES=(
- "chainparamsbase -> util -> chainparamsbase"
+ "chainparamsbase -> util/system -> chainparamsbase"
"checkpoints -> validation -> checkpoints"
"index/txindex -> validation -> index/txindex"
"policy/fees -> txmempool -> policy/fees"
@@ -29,7 +29,6 @@ EXPECTED_CIRCULAR_DEPENDENCIES=(
"validation -> validationinterface -> validation"
"wallet/coincontrol -> wallet/wallet -> wallet/coincontrol"
"wallet/fees -> wallet/wallet -> wallet/fees"
- "wallet/rpcwallet -> wallet/wallet -> wallet/rpcwallet"
"wallet/wallet -> wallet/walletdb -> wallet/wallet"
"policy/fees -> policy/policy -> validation -> policy/fees"
"policy/rbf -> txmempool -> validation -> policy/rbf"
diff --git a/test/lint/lint-format-strings.py b/test/lint/lint-format-strings.py
index 2fb35fd8ca..5caebf3739 100755
--- a/test/lint/lint-format-strings.py
+++ b/test/lint/lint-format-strings.py
@@ -16,8 +16,8 @@ FALSE_POSITIVES = [
("src/dbwrapper.cpp", "vsnprintf(p, limit - p, format, backup_ap)"),
("src/index/base.cpp", "FatalError(const char* fmt, const Args&... args)"),
("src/netbase.cpp", "LogConnectFailure(bool manual_connection, const char* fmt, const Args&... args)"),
- ("src/util.cpp", "strprintf(_(COPYRIGHT_HOLDERS), _(COPYRIGHT_HOLDERS_SUBSTITUTION))"),
- ("src/util.cpp", "strprintf(COPYRIGHT_HOLDERS, COPYRIGHT_HOLDERS_SUBSTITUTION)"),
+ ("src/util/system.cpp", "strprintf(_(COPYRIGHT_HOLDERS), _(COPYRIGHT_HOLDERS_SUBSTITUTION))"),
+ ("src/util/system.cpp", "strprintf(COPYRIGHT_HOLDERS, COPYRIGHT_HOLDERS_SUBSTITUTION)"),
("src/wallet/wallet.h", "WalletLogPrintf(std::string fmt, Params... parameters)"),
("src/wallet/wallet.h", "LogPrintf((\"%s \" + fmt).c_str(), GetDisplayName(), parameters...)"),
("src/logging.h", "LogPrintf(const char* fmt, const Args&... args)"),
diff --git a/test/lint/lint-locale-dependence.sh b/test/lint/lint-locale-dependence.sh
index 216cabbce9..44170a6b5a 100755
--- a/test/lint/lint-locale-dependence.sh
+++ b/test/lint/lint-locale-dependence.sh
@@ -18,16 +18,16 @@ KNOWN_VIOLATIONS=(
"src/torcontrol.cpp:.*atoi"
"src/torcontrol.cpp:.*strtol"
"src/uint256.cpp:.*tolower"
- "src/util.cpp:.*atoi"
- "src/util.cpp:.*fprintf"
- "src/util.cpp:.*tolower"
- "src/utilmoneystr.cpp:.*isdigit"
- "src/utilstrencodings.cpp:.*atoi"
- "src/utilstrencodings.cpp:.*strtol"
- "src/utilstrencodings.cpp:.*strtoll"
- "src/utilstrencodings.cpp:.*strtoul"
- "src/utilstrencodings.cpp:.*strtoull"
- "src/utilstrencodings.h:.*atoi"
+ "src/util/system.cpp:.*atoi"
+ "src/util/system.cpp:.*fprintf"
+ "src/util/system.cpp:.*tolower"
+ "src/util/moneystr.cpp:.*isdigit"
+ "src/util/strencodings.cpp:.*atoi"
+ "src/util/strencodings.cpp:.*strtol"
+ "src/util/strencodings.cpp:.*strtoll"
+ "src/util/strencodings.cpp:.*strtoul"
+ "src/util/strencodings.cpp:.*strtoull"
+ "src/util/strencodings.h:.*atoi"
)
REGEXP_IGNORE_EXTERNAL_DEPENDENCIES="^src/(crypto/ctaes/|leveldb/|secp256k1/|tinyformat.h|univalue/)"