aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rwxr-xr-xtest/functional/combine_logs.py2
-rw-r--r--test/functional/data/wallets/high_minversion/.walletlock0
-rw-r--r--test/functional/data/wallets/high_minversion/db.log0
-rw-r--r--test/functional/data/wallets/high_minversion/wallet.datbin0 -> 16384 bytes
-rwxr-xr-xtest/functional/feature_config_args.py14
-rwxr-xr-xtest/functional/feature_logging.py2
-rwxr-xr-xtest/functional/feature_segwit.py23
-rwxr-xr-xtest/functional/feature_uacomment.py4
-rwxr-xr-xtest/functional/interface_zmq.py99
-rwxr-xr-xtest/functional/mempool_package_onemore.py10
-rwxr-xr-xtest/functional/p2p_blocksonly.py10
-rwxr-xr-x[-rw-r--r--]test/functional/p2p_permissions.py66
-rwxr-xr-xtest/functional/rpc_blockchain.py2
-rwxr-xr-xtest/functional/rpc_createmultisig.py21
-rwxr-xr-xtest/functional/rpc_deriveaddresses.py4
-rwxr-xr-xtest/functional/rpc_net.py39
-rwxr-xr-xtest/functional/rpc_scantxoutset.py7
-rwxr-xr-xtest/functional/rpc_setban.py47
-rwxr-xr-xtest/functional/test_framework/messages.py8
-rwxr-xr-xtest/functional/test_framework/test_node.py19
-rw-r--r--test/functional/test_framework/util.py9
-rwxr-xr-xtest/functional/test_runner.py2
-rwxr-xr-xtest/functional/wallet_address_types.py4
-rwxr-xr-xtest/functional/wallet_basic.py5
-rwxr-xr-xtest/functional/wallet_importmulti.py6
-rwxr-xr-xtest/functional/wallet_multiwallet.py31
-rwxr-xr-xtest/functional/wallet_reorgsrestore.py105
-rwxr-xr-x[-rw-r--r--]test/functional/wallet_watchonly.py0
-rwxr-xr-xtest/lint/check-doc.py4
-rwxr-xr-xtest/lint/check-rpc-mappings.py6
-rwxr-xr-xtest/lint/lint-format-strings.py2
-rwxr-xr-xtest/lint/lint-includes.sh3
-rwxr-xr-xtest/lint/lint-python-mutable-default-parameters.sh52
-rwxr-xr-xtest/lint/lint-python.sh1
-rw-r--r--test/lint/lint-spelling.ignore-words.txt1
-rwxr-xr-xtest/lint/lint-spelling.sh5
-rw-r--r--test/util/data/txcreateoutpubkey1.json6
37 files changed, 501 insertions, 118 deletions
diff --git a/test/functional/combine_logs.py b/test/functional/combine_logs.py
index 5364ac4b8c..367d0f6916 100755
--- a/test/functional/combine_logs.py
+++ b/test/functional/combine_logs.py
@@ -81,7 +81,7 @@ def read_logs(tmp_dir):
chain = glob.glob("{}/node0/*/debug.log".format(tmp_dir))
if chain:
chain = chain[0] # pick the first one if more than one chain was found (should never happen)
- chain = re.search('node0/(.+?)/debug\.log$', chain).group(1) # extract the chain name
+ chain = re.search(r'node0/(.+?)/debug\.log$', chain).group(1) # extract the chain name
else:
chain = 'regtest' # fallback to regtest (should only happen when none exists)
diff --git a/test/functional/data/wallets/high_minversion/.walletlock b/test/functional/data/wallets/high_minversion/.walletlock
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/functional/data/wallets/high_minversion/.walletlock
diff --git a/test/functional/data/wallets/high_minversion/db.log b/test/functional/data/wallets/high_minversion/db.log
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/functional/data/wallets/high_minversion/db.log
diff --git a/test/functional/data/wallets/high_minversion/wallet.dat b/test/functional/data/wallets/high_minversion/wallet.dat
new file mode 100644
index 0000000000..99ab809263
--- /dev/null
+++ b/test/functional/data/wallets/high_minversion/wallet.dat
Binary files differ
diff --git a/test/functional/feature_config_args.py b/test/functional/feature_config_args.py
index 70a824b863..b997c76025 100755
--- a/test/functional/feature_config_args.py
+++ b/test/functional/feature_config_args.py
@@ -109,17 +109,15 @@ class ConfArgsTest(BitcoinTestFramework):
f.write("datadir=" + new_data_dir + "\n")
f.write(conf_file_contents)
- # Temporarily disabled, because this test would access the user's home dir (~/.bitcoin)
- #self.nodes[0].assert_start_raises_init_error(['-conf=' + conf_file], 'Error reading configuration file: specified data directory "' + new_data_dir + '" does not exist.')
+ self.nodes[0].assert_start_raises_init_error(['-conf=' + conf_file], 'Error: Error reading configuration file: specified data directory "' + new_data_dir + '" does not exist.')
# Create the directory and ensure the config file now works
os.mkdir(new_data_dir)
- # Temporarily disabled, because this test would access the user's home dir (~/.bitcoin)
- #self.start_node(0, ['-conf='+conf_file, '-wallet=w1'])
- #self.stop_node(0)
- #assert os.path.exists(os.path.join(new_data_dir, 'regtest', 'blocks'))
- #if self.is_wallet_compiled():
- #assert os.path.exists(os.path.join(new_data_dir, 'regtest', 'wallets', 'w1'))
+ self.start_node(0, ['-conf='+conf_file, '-wallet=w1'])
+ self.stop_node(0)
+ assert os.path.exists(os.path.join(new_data_dir, 'regtest', 'blocks'))
+ if self.is_wallet_compiled():
+ assert os.path.exists(os.path.join(new_data_dir, 'regtest', 'wallets', 'w1'))
# Ensure command line argument overrides datadir in conf
os.mkdir(new_data_dir_2)
diff --git a/test/functional/feature_logging.py b/test/functional/feature_logging.py
index 8bb7e02695..e6ff21ee9c 100755
--- a/test/functional/feature_logging.py
+++ b/test/functional/feature_logging.py
@@ -36,7 +36,7 @@ class LoggingTest(BitcoinTestFramework):
invdir = self.relative_log_path("foo")
invalidname = os.path.join("foo", "foo.log")
self.stop_node(0)
- exp_stderr = "Error: Could not open debug log file \S+$"
+ exp_stderr = r"Error: Could not open debug log file \S+$"
self.nodes[0].assert_start_raises_init_error(["-debuglogfile=%s" % (invalidname)], exp_stderr, match=ErrorMatch.FULL_REGEX)
assert not os.path.isfile(os.path.join(invdir, "foo.log"))
diff --git a/test/functional/feature_segwit.py b/test/functional/feature_segwit.py
index d47065d1cb..b9db618575 100755
--- a/test/functional/feature_segwit.py
+++ b/test/functional/feature_segwit.py
@@ -226,6 +226,16 @@ class SegWitTest(BitcoinTestFramework):
assert tx.wit.is_null() # This should not be a segwit input
assert txid1 in self.nodes[0].getrawmempool()
+ tx1_hex = self.nodes[0].gettransaction(txid1)['hex']
+ tx1 = FromHex(CTransaction(), tx1_hex)
+
+ # Check that wtxid is properly reported in mempool entry (txid1)
+ assert_equal(int(self.nodes[0].getmempoolentry(txid1)["wtxid"], 16), tx1.calc_sha256(True))
+
+ # Check that weight and vsize are properly reported in mempool entry (txid1)
+ assert_equal(self.nodes[0].getmempoolentry(txid1)["vsize"], (self.nodes[0].getmempoolentry(txid1)["weight"] + 3) // 4)
+ assert_equal(self.nodes[0].getmempoolentry(txid1)["weight"], len(tx1.serialize_without_witness())*3 + len(tx1.serialize_with_witness()))
+
# Now create tx2, which will spend from txid1.
tx = CTransaction()
tx.vin.append(CTxIn(COutPoint(int(txid1, 16), 0), b''))
@@ -235,6 +245,13 @@ class SegWitTest(BitcoinTestFramework):
tx = FromHex(CTransaction(), tx2_hex)
assert not tx.wit.is_null()
+ # Check that wtxid is properly reported in mempool entry (txid2)
+ assert_equal(int(self.nodes[0].getmempoolentry(txid2)["wtxid"], 16), tx.calc_sha256(True))
+
+ # Check that weight and vsize are properly reported in mempool entry (txid2)
+ assert_equal(self.nodes[0].getmempoolentry(txid2)["vsize"], (self.nodes[0].getmempoolentry(txid2)["weight"] + 3) // 4)
+ assert_equal(self.nodes[0].getmempoolentry(txid2)["weight"], len(tx.serialize_without_witness())*3 + len(tx.serialize_with_witness()))
+
# Now create tx3, which will spend from txid2
tx = CTransaction()
tx.vin.append(CTxIn(COutPoint(int(txid2, 16), 0), b""))
@@ -251,9 +268,13 @@ class SegWitTest(BitcoinTestFramework):
assert txid2 in template_txids
assert txid3 in template_txids
- # Check that wtxid is properly reported in mempool entry
+ # Check that wtxid is properly reported in mempool entry (txid3)
assert_equal(int(self.nodes[0].getmempoolentry(txid3)["wtxid"], 16), tx.calc_sha256(True))
+ # Check that weight and vsize are properly reported in mempool entry (txid3)
+ assert_equal(self.nodes[0].getmempoolentry(txid3)["vsize"], (self.nodes[0].getmempoolentry(txid3)["weight"] + 3) // 4)
+ assert_equal(self.nodes[0].getmempoolentry(txid3)["weight"], len(tx.serialize_without_witness())*3 + len(tx.serialize_with_witness()))
+
# Mine a block to clear the gbt cache again.
self.nodes[0].generate(1)
diff --git a/test/functional/feature_uacomment.py b/test/functional/feature_uacomment.py
index fb4ad21359..85c250173f 100755
--- a/test/functional/feature_uacomment.py
+++ b/test/functional/feature_uacomment.py
@@ -27,12 +27,12 @@ class UacommentTest(BitcoinTestFramework):
self.log.info("test -uacomment max length")
self.stop_node(0)
- expected = "Error: Total length of network version string \([0-9]+\) exceeds maximum length \(256\). Reduce the number or size of uacomments."
+ expected = r"Error: Total length of network version string \([0-9]+\) exceeds maximum length \(256\). Reduce the number or size of uacomments."
self.nodes[0].assert_start_raises_init_error(["-uacomment=" + 'a' * 256], expected, match=ErrorMatch.FULL_REGEX)
self.log.info("test -uacomment unsafe characters")
for unsafe_char in ['/', ':', '(', ')', '₿', '🏃']:
- expected = "Error: User Agent comment \(" + re.escape(unsafe_char) + "\) contains unsafe characters."
+ expected = r"Error: User Agent comment \(" + re.escape(unsafe_char) + r"\) contains unsafe characters."
self.nodes[0].assert_start_raises_init_error(["-uacomment=" + unsafe_char], expected, match=ErrorMatch.FULL_REGEX)
diff --git a/test/functional/interface_zmq.py b/test/functional/interface_zmq.py
index 1ba781c539..5aea10fbce 100755
--- a/test/functional/interface_zmq.py
+++ b/test/functional/interface_zmq.py
@@ -8,10 +8,9 @@ import struct
from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE
from test_framework.test_framework import BitcoinTestFramework
from test_framework.messages import CTransaction, hash256
-from test_framework.util import assert_equal
+from test_framework.util import assert_equal, connect_nodes
from io import BytesIO
-
-ADDRESS = "tcp://127.0.0.1:28332"
+from time import sleep
def hash256_reversed(byte_str):
return hash256(byte_str)[::-1]
@@ -43,66 +42,64 @@ class ZMQTest (BitcoinTestFramework):
self.skip_if_no_py3_zmq()
self.skip_if_no_bitcoind_zmq()
- def setup_nodes(self):
+ def run_test(self):
import zmq
+ self.ctx = zmq.Context()
+ try:
+ self.test_basic()
+ self.test_reorg()
+ finally:
+ # Destroy the ZMQ context.
+ self.log.debug("Destroying ZMQ context")
+ self.ctx.destroy(linger=None)
- # Initialize ZMQ context and socket.
+ def test_basic(self):
# All messages are received in the same socket which means
# that this test fails if the publishing order changes.
# Note that the publishing order is not defined in the documentation and
# is subject to change.
- self.zmq_context = zmq.Context()
- socket = self.zmq_context.socket(zmq.SUB)
+ import zmq
+ address = 'tcp://127.0.0.1:28332'
+ socket = self.ctx.socket(zmq.SUB)
socket.set(zmq.RCVTIMEO, 60000)
- socket.connect(ADDRESS)
# Subscribe to all available topics.
- self.hashblock = ZMQSubscriber(socket, b"hashblock")
- self.hashtx = ZMQSubscriber(socket, b"hashtx")
- self.rawblock = ZMQSubscriber(socket, b"rawblock")
- self.rawtx = ZMQSubscriber(socket, b"rawtx")
-
- self.extra_args = [
- ["-zmqpub%s=%s" % (sub.topic.decode(), ADDRESS) for sub in [self.hashblock, self.hashtx, self.rawblock, self.rawtx]],
- [],
- ]
- self.add_nodes(self.num_nodes, self.extra_args)
- self.start_nodes()
- self.import_deterministic_coinbase_privkeys()
+ hashblock = ZMQSubscriber(socket, b"hashblock")
+ hashtx = ZMQSubscriber(socket, b"hashtx")
+ rawblock = ZMQSubscriber(socket, b"rawblock")
+ rawtx = ZMQSubscriber(socket, b"rawtx")
- def run_test(self):
- try:
- self._zmq_test()
- finally:
- # Destroy the ZMQ context.
- self.log.debug("Destroying ZMQ context")
- self.zmq_context.destroy(linger=None)
+ self.restart_node(0, ["-zmqpub%s=%s" % (sub.topic.decode(), address) for sub in [hashblock, hashtx, rawblock, rawtx]])
+ connect_nodes(self.nodes[0], 1)
+ socket.connect(address)
+ # Relax so that the subscriber is ready before publishing zmq messages
+ sleep(0.2)
- def _zmq_test(self):
num_blocks = 5
self.log.info("Generate %(n)d blocks (and %(n)d coinbase txes)" % {"n": num_blocks})
genhashes = self.nodes[0].generatetoaddress(num_blocks, ADDRESS_BCRT1_UNSPENDABLE)
+
self.sync_all()
for x in range(num_blocks):
# Should receive the coinbase txid.
- txid = self.hashtx.receive()
+ txid = hashtx.receive()
# Should receive the coinbase raw transaction.
- hex = self.rawtx.receive()
+ hex = rawtx.receive()
tx = CTransaction()
tx.deserialize(BytesIO(hex))
tx.calc_sha256()
assert_equal(tx.hash, txid.hex())
# Should receive the generated block hash.
- hash = self.hashblock.receive().hex()
+ hash = hashblock.receive().hex()
assert_equal(genhashes[x], hash)
# The block should only have the coinbase txid.
assert_equal([txid.hex()], self.nodes[1].getblock(hash)["tx"])
# Should receive the generated raw block.
- block = self.rawblock.receive()
+ block = rawblock.receive()
assert_equal(genhashes[x], hash256_reversed(block[:80]).hex())
if self.is_wallet_compiled():
@@ -111,23 +108,49 @@ class ZMQTest (BitcoinTestFramework):
self.sync_all()
# Should receive the broadcasted txid.
- txid = self.hashtx.receive()
+ txid = hashtx.receive()
assert_equal(payment_txid, txid.hex())
# Should receive the broadcasted raw transaction.
- hex = self.rawtx.receive()
+ hex = rawtx.receive()
assert_equal(payment_txid, hash256_reversed(hex).hex())
self.log.info("Test the getzmqnotifications RPC")
assert_equal(self.nodes[0].getzmqnotifications(), [
- {"type": "pubhashblock", "address": ADDRESS, "hwm": 1000},
- {"type": "pubhashtx", "address": ADDRESS, "hwm": 1000},
- {"type": "pubrawblock", "address": ADDRESS, "hwm": 1000},
- {"type": "pubrawtx", "address": ADDRESS, "hwm": 1000},
+ {"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(), [])
+ def test_reorg(self):
+ import zmq
+ address = 'tcp://127.0.0.1:28333'
+ socket = self.ctx.socket(zmq.SUB)
+ socket.set(zmq.RCVTIMEO, 60000)
+ hashblock = ZMQSubscriber(socket, b'hashblock')
+
+ # Should only notify the tip if a reorg occurs
+ self.restart_node(0, ['-zmqpub%s=%s' % (hashblock.topic.decode(), address)])
+ socket.connect(address)
+ # Relax so that the subscriber is ready before publishing zmq messages
+ sleep(0.2)
+
+ # Generate 1 block in nodes[0] and receive all notifications
+ self.nodes[0].generatetoaddress(1, ADDRESS_BCRT1_UNSPENDABLE)
+ assert_equal(self.nodes[0].getbestblockhash(), hashblock.receive().hex())
+
+ # Generate 2 blocks in nodes[1]
+ self.nodes[1].generatetoaddress(2, ADDRESS_BCRT1_UNSPENDABLE)
+
+ # nodes[0] will reorg chain after connecting back nodes[1]
+ connect_nodes(self.nodes[0], 1)
+
+ # Should receive nodes[1] tip
+ assert_equal(self.nodes[1].getbestblockhash(), hashblock.receive().hex())
+
if __name__ == '__main__':
ZMQTest().main()
diff --git a/test/functional/mempool_package_onemore.py b/test/functional/mempool_package_onemore.py
index 30f851fb8e..0739d7e29b 100755
--- a/test/functional/mempool_package_onemore.py
+++ b/test/functional/mempool_package_onemore.py
@@ -33,7 +33,7 @@ class MempoolPackagesTest(BitcoinTestFramework):
outputs = {}
for i in range(num_outputs):
outputs[node.getnewaddress()] = send_value
- rawtx = node.createrawtransaction(inputs, outputs)
+ rawtx = node.createrawtransaction(inputs, outputs, 0, True)
signedtx = node.signrawtransactionwithwallet(rawtx)
txid = node.sendrawtransaction(signedtx['hex'])
fulltx = node.getrawtransaction(txid, 1)
@@ -75,10 +75,16 @@ class MempoolPackagesTest(BitcoinTestFramework):
# ...especially if its > 40k weight
assert_raises_rpc_error(-26, "too-long-mempool-chain, too many descendants", self.chain_transaction, self.nodes[0], [chain[0][0]], [1], chain[0][1], fee, 350)
# But not if it chains directly off the first transaction
- self.chain_transaction(self.nodes[0], [chain[0][0]], [1], chain[0][1], fee, 1)
+ (replacable_txid, replacable_orig_value) = self.chain_transaction(self.nodes[0], [chain[0][0]], [1], chain[0][1], fee, 1)
# and the second chain should work just fine
self.chain_transaction(self.nodes[0], [second_chain], [0], second_chain_value, fee, 1)
+ # Make sure we can RBF the chain which used our carve-out rule
+ second_tx_outputs = {self.nodes[0].getrawtransaction(replacable_txid, True)["vout"][0]['scriptPubKey']['addresses'][0]: replacable_orig_value - (Decimal(1) / Decimal(100))}
+ second_tx = self.nodes[0].createrawtransaction([{'txid': chain[0][0], 'vout': 1}], second_tx_outputs)
+ signed_second_tx = self.nodes[0].signrawtransactionwithwallet(second_tx)
+ self.nodes[0].sendrawtransaction(signed_second_tx['hex'])
+
# Finally, check that we added two transactions
assert_equal(len(self.nodes[0].getrawmempool(True)), MAX_ANCESTORS + 3)
diff --git a/test/functional/p2p_blocksonly.py b/test/functional/p2p_blocksonly.py
index 12cb06a407..3258a38e3c 100755
--- a/test/functional/p2p_blocksonly.py
+++ b/test/functional/p2p_blocksonly.py
@@ -19,7 +19,7 @@ class P2PBlocksOnly(BitcoinTestFramework):
def run_test(self):
self.nodes[0].add_p2p_connection(P2PInterface())
- self.log.info('Check that txs from p2p are rejected')
+ self.log.info('Check that txs from p2p are rejected and result in disconnect')
prevtx = self.nodes[0].getblock(self.nodes[0].getblockhash(1), 2)['tx'][0]
rawtx = self.nodes[0].createrawtransaction(
inputs=[{
@@ -42,13 +42,17 @@ class P2PBlocksOnly(BitcoinTestFramework):
assert_equal(self.nodes[0].getnetworkinfo()['localrelay'], False)
with self.nodes[0].assert_debug_log(['transaction sent in violation of protocol peer=0']):
self.nodes[0].p2p.send_message(msg_tx(FromHex(CTransaction(), sigtx)))
- self.nodes[0].p2p.sync_with_ping()
+ self.nodes[0].p2p.wait_for_disconnect()
assert_equal(self.nodes[0].getmempoolinfo()['size'], 0)
+ # Remove the disconnected peer and add a new one.
+ del self.nodes[0].p2ps[0]
+ self.nodes[0].add_p2p_connection(P2PInterface())
+
self.log.info('Check that txs from rpc are not rejected and relayed to other peers')
assert_equal(self.nodes[0].getpeerinfo()[0]['relaytxes'], True)
txid = self.nodes[0].testmempoolaccept([sigtx])[0]['txid']
- with self.nodes[0].assert_debug_log(['received getdata for: tx {} peer=0'.format(txid)]):
+ with self.nodes[0].assert_debug_log(['received getdata for: tx {} peer=1'.format(txid)]):
self.nodes[0].sendrawtransaction(sigtx)
self.nodes[0].p2p.wait_for_tx(txid)
assert_equal(self.nodes[0].getmempoolinfo()['size'], 1)
diff --git a/test/functional/p2p_permissions.py b/test/functional/p2p_permissions.py
index 1013055420..40b28d7533 100644..100755
--- a/test/functional/p2p_permissions.py
+++ b/test/functional/p2p_permissions.py
@@ -23,18 +23,24 @@ class P2PPermissionsTests(BitcoinTestFramework):
def run_test(self):
self.checkpermission(
- # relay permission added
- ["-whitelist=127.0.0.1", "-whitelistrelay"],
- ["relay", "noban", "mempool"],
- True)
+ # default permissions (no specific permissions)
+ ["-whitelist=127.0.0.1"],
+ ["relay", "noban", "mempool"],
+ True)
self.checkpermission(
- # forcerelay and relay permission added
- # Legacy parameter interaction which set whitelistrelay to true
- # if whitelistforcerelay is true
- ["-whitelist=127.0.0.1", "-whitelistforcerelay"],
- ["forcerelay", "relay", "noban", "mempool"],
- True)
+ # relay permission removed (no specific permissions)
+ ["-whitelist=127.0.0.1", "-whitelistrelay=0"],
+ ["noban", "mempool"],
+ True)
+
+ self.checkpermission(
+ # forcerelay and relay permission added
+ # Legacy parameter interaction which set whitelistrelay to true
+ # if whitelistforcerelay is true
+ ["-whitelist=127.0.0.1", "-whitelistforcerelay"],
+ ["forcerelay", "relay", "noban", "mempool"],
+ True)
# Let's make sure permissions are merged correctly
# For this, we need to use whitebind instead of bind
@@ -42,35 +48,35 @@ class P2PPermissionsTests(BitcoinTestFramework):
ip_port = "127.0.0.1:{}".format(p2p_port(1))
self.replaceinconfig(1, "bind=127.0.0.1", "whitebind=bloomfilter,forcerelay@" + ip_port)
self.checkpermission(
- ["-whitelist=noban@127.0.0.1" ],
- # Check parameter interaction forcerelay should activate relay
- ["noban", "bloomfilter", "forcerelay", "relay" ],
- False)
+ ["-whitelist=noban@127.0.0.1" ],
+ # Check parameter interaction forcerelay should activate relay
+ ["noban", "bloomfilter", "forcerelay", "relay" ],
+ False)
self.replaceinconfig(1, "whitebind=bloomfilter,forcerelay@" + ip_port, "bind=127.0.0.1")
self.checkpermission(
- # legacy whitelistrelay should be ignored
- ["-whitelist=noban,mempool@127.0.0.1", "-whitelistrelay"],
- ["noban", "mempool"],
- False)
+ # legacy whitelistrelay should be ignored
+ ["-whitelist=noban,mempool@127.0.0.1", "-whitelistrelay"],
+ ["noban", "mempool"],
+ False)
self.checkpermission(
- # legacy whitelistforcerelay should be ignored
- ["-whitelist=noban,mempool@127.0.0.1", "-whitelistforcerelay"],
- ["noban", "mempool"],
- False)
+ # legacy whitelistforcerelay should be ignored
+ ["-whitelist=noban,mempool@127.0.0.1", "-whitelistforcerelay"],
+ ["noban", "mempool"],
+ False)
self.checkpermission(
- # missing mempool permission to be considered legacy whitelisted
- ["-whitelist=noban@127.0.0.1"],
- ["noban"],
- False)
+ # missing mempool permission to be considered legacy whitelisted
+ ["-whitelist=noban@127.0.0.1"],
+ ["noban"],
+ False)
self.checkpermission(
- # all permission added
- ["-whitelist=all@127.0.0.1"],
- ["forcerelay", "noban", "mempool", "bloomfilter", "relay"],
- False)
+ # all permission added
+ ["-whitelist=all@127.0.0.1"],
+ ["forcerelay", "noban", "mempool", "bloomfilter", "relay"],
+ False)
self.stop_node(1)
self.nodes[1].assert_start_raises_init_error(["-whitelist=oopsie@127.0.0.1"], "Invalid P2P permission", match=ErrorMatch.PARTIAL_REGEX)
diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py
index 6c30e05084..266a0d6cd2 100755
--- a/test/functional/rpc_blockchain.py
+++ b/test/functional/rpc_blockchain.py
@@ -186,6 +186,7 @@ class BlockchainTest(BitcoinTestFramework):
assert_equal(chaintxstats['time'], b200['time'])
assert_equal(chaintxstats['txcount'], 201)
assert_equal(chaintxstats['window_final_block_hash'], b200_hash)
+ assert_equal(chaintxstats['window_final_block_height'], 200)
assert_equal(chaintxstats['window_block_count'], 199)
assert_equal(chaintxstats['window_tx_count'], 199)
assert_equal(chaintxstats['window_interval'], time_diff)
@@ -195,6 +196,7 @@ class BlockchainTest(BitcoinTestFramework):
assert_equal(chaintxstats['time'], b1['time'])
assert_equal(chaintxstats['txcount'], 2)
assert_equal(chaintxstats['window_final_block_hash'], b1_hash)
+ assert_equal(chaintxstats['window_final_block_height'], 1)
assert_equal(chaintxstats['window_block_count'], 0)
assert 'window_tx_count' not in chaintxstats
assert 'window_interval' not in chaintxstats
diff --git a/test/functional/rpc_createmultisig.py b/test/functional/rpc_createmultisig.py
index 62f3843756..056e193d55 100755
--- a/test/functional/rpc_createmultisig.py
+++ b/test/functional/rpc_createmultisig.py
@@ -134,6 +134,27 @@ class RpcCreateMultiSigTest(BitcoinTestFramework):
assert_raises_rpc_error(-8, "Missing redeemScript/witnessScript", node2.signrawtransactionwithkey, rawtx, self.priv[0:self.nsigs-1], [prevtx_err])
+ # if witnessScript specified, all ok
+ prevtx_err["witnessScript"] = prevtxs[0]["redeemScript"]
+ node2.signrawtransactionwithkey(rawtx, self.priv[0:self.nsigs-1], [prevtx_err])
+
+ # both specified, also ok
+ prevtx_err["redeemScript"] = prevtxs[0]["redeemScript"]
+ node2.signrawtransactionwithkey(rawtx, self.priv[0:self.nsigs-1], [prevtx_err])
+
+ # redeemScript mismatch to witnessScript
+ prevtx_err["redeemScript"] = "6a" # OP_RETURN
+ assert_raises_rpc_error(-8, "redeemScript does not correspond to witnessScript", node2.signrawtransactionwithkey, rawtx, self.priv[0:self.nsigs-1], [prevtx_err])
+
+ # redeemScript does not match scriptPubKey
+ del prevtx_err["witnessScript"]
+ assert_raises_rpc_error(-8, "redeemScript/witnessScript does not match scriptPubKey", node2.signrawtransactionwithkey, rawtx, self.priv[0:self.nsigs-1], [prevtx_err])
+
+ # witnessScript does not match scriptPubKey
+ prevtx_err["witnessScript"] = prevtx_err["redeemScript"]
+ del prevtx_err["redeemScript"]
+ assert_raises_rpc_error(-8, "redeemScript/witnessScript does not match scriptPubKey", node2.signrawtransactionwithkey, rawtx, self.priv[0:self.nsigs-1], [prevtx_err])
+
rawtx2 = node2.signrawtransactionwithkey(rawtx, self.priv[0:self.nsigs - 1], prevtxs)
rawtx3 = node2.signrawtransactionwithkey(rawtx2["hex"], [self.priv[-1]], prevtxs)
diff --git a/test/functional/rpc_deriveaddresses.py b/test/functional/rpc_deriveaddresses.py
index 1984694692..42128d5767 100755
--- a/test/functional/rpc_deriveaddresses.py
+++ b/test/functional/rpc_deriveaddresses.py
@@ -13,14 +13,14 @@ class DeriveaddressesTest(BitcoinTestFramework):
self.supports_cli = 1
def run_test(self):
- assert_raises_rpc_error(-5, "Invalid descriptor", self.nodes[0].deriveaddresses, "a")
+ assert_raises_rpc_error(-5, "Missing checksum", self.nodes[0].deriveaddresses, "a")
descriptor = "wpkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/0)#t6wfjs64"
address = "bcrt1qjqmxmkpmxt80xz4y3746zgt0q3u3ferr34acd5"
assert_equal(self.nodes[0].deriveaddresses(descriptor), [address])
descriptor = descriptor[:-9]
- assert_raises_rpc_error(-5, "Invalid descriptor", self.nodes[0].deriveaddresses, descriptor)
+ assert_raises_rpc_error(-5, "Missing checksum", self.nodes[0].deriveaddresses, descriptor)
descriptor_pubkey = "wpkh(tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/1/0)#s9ga3alw"
address = "bcrt1qjqmxmkpmxt80xz4y3746zgt0q3u3ferr34acd5"
diff --git a/test/functional/rpc_net.py b/test/functional/rpc_net.py
index b12eb1d9ec..104c66aaa7 100755
--- a/test/functional/rpc_net.py
+++ b/test/functional/rpc_net.py
@@ -20,7 +20,32 @@ from test_framework.util import (
wait_until,
)
from test_framework.mininode import P2PInterface
-from test_framework.messages import CAddress, msg_addr, NODE_NETWORK, NODE_WITNESS
+from test_framework.messages import (
+ CAddress,
+ msg_addr,
+ NODE_NETWORK,
+ NODE_WITNESS,
+ NODE_GETUTXO,NODE_BLOOM,
+ NODE_NETWORK_LIMITED,
+)
+
+def assert_net_servicesnames(servicesflag, servicenames):
+ """Utility that checks if all flags are correctly decoded in
+ `getpeerinfo` and `getnetworkinfo`.
+
+ :param servicesflag: The services as an integer.
+ :param servicesnames: The list of decoded services names, as strings.
+ """
+ if servicesflag & NODE_NETWORK:
+ assert "NETWORK" in servicenames
+ if servicesflag & NODE_GETUTXO:
+ assert "GETUTXO" in servicenames
+ if servicesflag & NODE_BLOOM:
+ assert "BLOOM" in servicenames
+ if servicesflag & NODE_WITNESS:
+ assert "WITNESS" in servicenames
+ if servicesflag & NODE_NETWORK_LIMITED:
+ assert "NETWORK_LIMITED" in servicenames
class NetTest(BitcoinTestFramework):
def set_test_params(self):
@@ -31,7 +56,7 @@ class NetTest(BitcoinTestFramework):
def run_test(self):
self._test_connection_count()
self._test_getnettotals()
- self._test_getnetworkinginfo()
+ self._test_getnetworkinfo()
self._test_getaddednodeinfo()
self._test_getpeerinfo()
self._test_getnodeaddresses()
@@ -70,7 +95,7 @@ class NetTest(BitcoinTestFramework):
assert_greater_than_or_equal(after['bytesrecv_per_msg'].get('pong', 0), before['bytesrecv_per_msg'].get('pong', 0) + 32)
assert_greater_than_or_equal(after['bytessent_per_msg'].get('ping', 0), before['bytessent_per_msg'].get('ping', 0) + 32)
- def _test_getnetworkinginfo(self):
+ def _test_getnetworkinfo(self):
assert_equal(self.nodes[0].getnetworkinfo()['networkactive'], True)
assert_equal(self.nodes[0].getnetworkinfo()['connections'], 2)
@@ -84,6 +109,11 @@ class NetTest(BitcoinTestFramework):
assert_equal(self.nodes[0].getnetworkinfo()['networkactive'], True)
assert_equal(self.nodes[0].getnetworkinfo()['connections'], 2)
+ # check the `servicesnames` field
+ network_info = [node.getnetworkinfo() for node in self.nodes]
+ for info in network_info:
+ assert_net_servicesnames(int(info["localservices"]), info["localservicesnames"])
+
def _test_getaddednodeinfo(self):
assert_equal(self.nodes[0].getaddednodeinfo(), [])
# add a node (node2) to node0
@@ -104,6 +134,9 @@ class NetTest(BitcoinTestFramework):
assert_equal(peer_info[1][0]['addrbind'], peer_info[0][0]['addr'])
assert_equal(peer_info[0][0]['minfeefilter'], Decimal("0.00000500"))
assert_equal(peer_info[1][0]['minfeefilter'], Decimal("0.00001000"))
+ # check the `servicesnames` field
+ for info in peer_info:
+ assert_net_servicesnames(int(info[0]["services"]), info[0]["servicesnames"])
def _test_getnodeaddresses(self):
self.nodes[0].add_p2p_connection(P2PInterface())
diff --git a/test/functional/rpc_scantxoutset.py b/test/functional/rpc_scantxoutset.py
index a1cd33ad54..9f94d11a93 100755
--- a/test/functional/rpc_scantxoutset.py
+++ b/test/functional/rpc_scantxoutset.py
@@ -58,6 +58,13 @@ class ScantxoutsetTest(BitcoinTestFramework):
self.start_node(0)
self.nodes[0].generate(110)
+ scan = self.nodes[0].scantxoutset("start", [])
+ info = self.nodes[0].gettxoutsetinfo()
+ assert_equal(scan['success'], True)
+ assert_equal(scan['height'], info['height'])
+ assert_equal(scan['txouts'], info['txouts'])
+ assert_equal(scan['bestblock'], info['bestblock'])
+
self.restart_node(0, ['-nowallet'])
self.log.info("Test if we have found the non HD unspent outputs.")
assert_equal(self.nodes[0].scantxoutset("start", [ "pkh(" + pubk1 + ")", "pkh(" + pubk2 + ")", "pkh(" + pubk3 + ")"])['total_amount'], Decimal("0.002"))
diff --git a/test/functional/rpc_setban.py b/test/functional/rpc_setban.py
new file mode 100755
index 0000000000..b1d2b6f431
--- /dev/null
+++ b/test/functional/rpc_setban.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python3
+# Copyright (c) 2015-2019 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Test the setban rpc call."""
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ connect_nodes,
+ p2p_port
+)
+
+class SetBanTests(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 2
+ self.setup_clean_chain = True
+ self.extra_args = [[],[]]
+
+ def run_test(self):
+ # Node 0 connects to Node 1, check that the noban permission is not granted
+ connect_nodes(self.nodes[0], 1)
+ peerinfo = self.nodes[1].getpeerinfo()[0]
+ assert(not 'noban' in peerinfo['permissions'])
+
+ # Node 0 get banned by Node 1
+ self.nodes[1].setban("127.0.0.1", "add")
+
+ # Node 0 should not be able to reconnect
+ with self.nodes[1].assert_debug_log(expected_msgs=['dropped (banned)\n'], timeout=5):
+ self.restart_node(1, [])
+ self.nodes[0].addnode("127.0.0.1:" + str(p2p_port(1)), "onetry")
+
+ # However, node 0 should be able to reconnect if it has noban permission
+ self.restart_node(1, ['-whitelist=127.0.0.1'])
+ connect_nodes(self.nodes[0], 1)
+ peerinfo = self.nodes[1].getpeerinfo()[0]
+ assert('noban' in peerinfo['permissions'])
+
+ # If we remove the ban, Node 0 should be able to reconnect even without noban permission
+ self.nodes[1].setban("127.0.0.1", "remove")
+ self.restart_node(1, [])
+ connect_nodes(self.nodes[0], 1)
+ peerinfo = self.nodes[1].getpeerinfo()[0]
+ assert(not 'noban' in peerinfo['permissions'])
+
+if __name__ == '__main__':
+ SetBanTests().main()
diff --git a/test/functional/test_framework/messages.py b/test/functional/test_framework/messages.py
index 89a5a65e64..3cb5f56bee 100755
--- a/test/functional/test_framework/messages.py
+++ b/test/functional/test_framework/messages.py
@@ -43,8 +43,8 @@ COIN = 100000000 # 1 btc in satoshis
BIP125_SEQUENCE_NUMBER = 0xfffffffd # Sequence number that is BIP 125 opt-in and BIP 68-opt-out
NODE_NETWORK = (1 << 0)
-# NODE_GETUTXO = (1 << 1)
-# NODE_BLOOM = (1 << 2)
+NODE_GETUTXO = (1 << 1)
+NODE_BLOOM = (1 << 2)
NODE_WITNESS = (1 << 3)
NODE_NETWORK_LIMITED = (1 << 10)
@@ -803,7 +803,9 @@ class HeaderAndShortIDs:
return [ key0, key1 ]
# Version 2 compact blocks use wtxid in shortids (rather than txid)
- def initialize_from_block(self, block, nonce=0, prefill_list = [0], use_witness = False):
+ def initialize_from_block(self, block, nonce=0, prefill_list=None, use_witness=False):
+ if prefill_list is None:
+ prefill_list = [0]
self.header = CBlockHeader(block)
self.nonce = nonce
self.prefilled_txn = [ PrefilledTransaction(i, block.vtx[i]) for i in prefill_list ]
diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index 16553367a7..d8bfdfd040 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -307,21 +307,30 @@ class TestNode():
wait_until(self.is_node_stopped, timeout=timeout)
@contextlib.contextmanager
- def assert_debug_log(self, expected_msgs):
+ def assert_debug_log(self, expected_msgs, timeout=2):
+ time_end = time.time() + timeout
debug_log = os.path.join(self.datadir, self.chain, 'debug.log')
with open(debug_log, encoding='utf-8') as dl:
dl.seek(0, 2)
prev_size = dl.tell()
- try:
- yield
- finally:
+
+ yield
+
+ while True:
+ found = True
with open(debug_log, encoding='utf-8') as dl:
dl.seek(prev_size)
log = dl.read()
print_log = " - " + "\n - ".join(log.splitlines())
for expected_msg in expected_msgs:
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))
+ found = False
+ if found:
+ return
+ if time.time() >= time_end:
+ break
+ time.sleep(0.05)
+ self._raise_assertion_error('Expected messages "{}" does not partially match log:\n\n{}\n\n'.format(str(expected_msgs), print_log))
@contextlib.contextmanager
def assert_memory_usage_stable(self, *, increase_allowed=0.03):
diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py
index 7e8020cceb..f9f5fe553e 100644
--- a/test/functional/test_framework/util.py
+++ b/test/functional/test_framework/util.py
@@ -56,7 +56,9 @@ def assert_raises_message(exc, message, fun, *args, **kwds):
raise AssertionError("Use assert_raises_rpc_error() to test RPC failures")
except exc as e:
if message is not None and message not in e.error['message']:
- raise AssertionError("Expected substring not found:" + e.error['message'])
+ raise AssertionError(
+ "Expected substring not found in error message:\nsubstring: '{}'\nerror message: '{}'.".format(
+ message, e.error['message']))
except Exception as e:
raise AssertionError("Unexpected exception raised: " + type(e).__name__)
else:
@@ -116,7 +118,9 @@ def try_rpc(code, message, fun, *args, **kwds):
if (code is not None) and (code != e.error["code"]):
raise AssertionError("Unexpected JSONRPC error code %i" % e.error["code"])
if (message is not None) and (message not in e.error['message']):
- raise AssertionError("Expected substring not found:" + e.error['message'])
+ raise AssertionError(
+ "Expected substring not found in error message:\nsubstring: '{}'\nerror message: '{}'.".format(
+ message, e.error['message']))
return True
except Exception as e:
raise AssertionError("Unexpected exception raised: " + type(e).__name__)
@@ -301,6 +305,7 @@ def initialize_datadir(dirname, n, chain):
f.write("dnsseed=0\n")
f.write("listenonion=0\n")
f.write("printtoconsole=0\n")
+ f.write("upnp=0\n")
os.makedirs(os.path.join(datadir, 'stderr'), exist_ok=True)
os.makedirs(os.path.join(datadir, 'stdout'), exist_ok=True)
return datadir
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 0cbcc528ed..9c92091f1d 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -131,6 +131,7 @@ BASE_SCRIPTS = [
'wallet_createwallet.py --usecli',
'wallet_watchonly.py',
'wallet_watchonly.py --usecli',
+ 'wallet_reorgsrestore.py',
'interface_http.py',
'interface_rpc.py',
'rpc_psbt.py',
@@ -146,6 +147,7 @@ BASE_SCRIPTS = [
'rpc_net.py',
'wallet_keypool.py',
'p2p_mempool.py',
+ 'rpc_setban.py',
'p2p_blocksonly.py',
'mining_prioritisetransaction.py',
'p2p_invalid_locator.py',
diff --git a/test/functional/wallet_address_types.py b/test/functional/wallet_address_types.py
index a40613dfc7..4e4ed8f26b 100755
--- a/test/functional/wallet_address_types.py
+++ b/test/functional/wallet_address_types.py
@@ -175,6 +175,10 @@ class AddressTypeTest(BitcoinTestFramework):
assert info['desc'] == descsum_create(info['desc'][:-9])
# Verify that stripping the checksum and feeding it to getdescriptorinfo roundtrips
assert info['desc'] == self.nodes[0].getdescriptorinfo(info['desc'][:-9])['descriptor']
+ assert_equal(info['desc'][-8:], self.nodes[0].getdescriptorinfo(info['desc'][:-9])['checksum'])
+ # Verify that keeping the checksum and feeding it to getdescriptorinfo roundtrips
+ assert info['desc'] == self.nodes[0].getdescriptorinfo(info['desc'])['descriptor']
+ assert_equal(info['desc'][-8:], self.nodes[0].getdescriptorinfo(info['desc'])['checksum'])
if not multisig and typ == 'legacy':
# P2PKH
diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py
index 34e84fcf55..74350649c7 100755
--- a/test/functional/wallet_basic.py
+++ b/test/functional/wallet_basic.py
@@ -499,6 +499,11 @@ class WalletTest(BitcoinTestFramework):
self.nodes[0].setlabel(change, 'foobar')
assert_equal(self.nodes[0].getaddressinfo(change)['ischange'], False)
+ # Test "decoded" field value in gettransaction response
+ self.log.info("Testing gettransaction decoding...")
+ tx = self.nodes[0].gettransaction(txid=txid, decode=True)
+ assert_equal(tx["decoded"], self.nodes[0].decoderawtransaction(tx["hex"]))
+
if __name__ == '__main__':
WalletTest().main()
diff --git a/test/functional/wallet_importmulti.py b/test/functional/wallet_importmulti.py
index e19c7919a9..23748e5dd7 100755
--- a/test/functional/wallet_importmulti.py
+++ b/test/functional/wallet_importmulti.py
@@ -44,8 +44,10 @@ class ImportMultiTest(BitcoinTestFramework):
def setup_network(self):
self.setup_nodes()
- def test_importmulti(self, req, success, error_code=None, error_message=None, warnings=[]):
+ def test_importmulti(self, req, success, error_code=None, error_message=None, warnings=None):
"""Run importmulti and assert success"""
+ if warnings is None:
+ warnings = []
result = self.nodes[1].importmulti([req])
observed_warnings = []
if 'warnings' in result[0]:
@@ -552,7 +554,7 @@ class ImportMultiTest(BitcoinTestFramework):
"keys": [key.privkey]},
success=False,
error_code=-5,
- error_message="Descriptor is invalid")
+ error_message="Missing checksum")
# Test importing of a P2SH-P2WPKH address via descriptor + private key
key = get_key(self.nodes[0])
diff --git a/test/functional/wallet_multiwallet.py b/test/functional/wallet_multiwallet.py
index 984ffab5a4..68bc45f986 100755
--- a/test/functional/wallet_multiwallet.py
+++ b/test/functional/wallet_multiwallet.py
@@ -17,6 +17,8 @@ from test_framework.util import (
assert_raises_rpc_error,
)
+FEATURE_LATEST = 169900
+
class MultiWalletTest(BitcoinTestFramework):
def set_test_params(self):
@@ -27,6 +29,13 @@ class MultiWalletTest(BitcoinTestFramework):
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
+ def add_options(self, parser):
+ parser.add_argument(
+ '--data_wallets_dir',
+ default=os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data/wallets/'),
+ help='Test data with wallet directories (default: %(default)s)',
+ )
+
def run_test(self):
node = self.nodes[0]
@@ -93,12 +102,12 @@ class MultiWalletTest(BitcoinTestFramework):
# should not initialize if one wallet is a copy of another
shutil.copyfile(wallet_dir('w8'), wallet_dir('w8_copy'))
- exp_stderr = "BerkeleyBatch: Can't open database w8_copy \(duplicates fileid \w+ from w8\)"
+ exp_stderr = r"BerkeleyBatch: Can't open database w8_copy \(duplicates fileid \w+ from w8\)"
self.nodes[0].assert_start_raises_init_error(['-wallet=w8', '-wallet=w8_copy'], exp_stderr, match=ErrorMatch.PARTIAL_REGEX)
# should not initialize if wallet file is a symlink
os.symlink('w8', wallet_dir('w8_symlink'))
- self.nodes[0].assert_start_raises_init_error(['-wallet=w8_symlink'], 'Error: Invalid -wallet path \'w8_symlink\'\. .*', match=ErrorMatch.FULL_REGEX)
+ self.nodes[0].assert_start_raises_init_error(['-wallet=w8_symlink'], r'Error: Invalid -wallet path \'w8_symlink\'\. .*', match=ErrorMatch.FULL_REGEX)
# should not initialize if the specified walletdir does not exist
self.nodes[0].assert_start_raises_init_error(['-walletdir=bad'], 'Error: Specified -walletdir "bad" does not exist')
@@ -139,7 +148,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])
- exp_stderr = "Error: Error initializing wallet database environment \"\S+competing_walletdir\"!"
+ 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)
self.restart_node(0, extra_args)
@@ -323,6 +332,22 @@ class MultiWalletTest(BitcoinTestFramework):
self.nodes[0].unloadwallet(wallet)
self.nodes[1].loadwallet(wallet)
+ # Fail to load if wallet is downgraded
+ shutil.copytree(os.path.join(self.options.data_wallets_dir, 'high_minversion'), wallet_dir('high_minversion'))
+ self.restart_node(0, extra_args=['-upgradewallet={}'.format(FEATURE_LATEST)])
+ assert {'name': 'high_minversion'} in self.nodes[0].listwalletdir()['wallets']
+ self.log.info("Fail -upgradewallet that results in downgrade")
+ assert_raises_rpc_error(
+ -4,
+ "Wallet loading failed.",
+ lambda: self.nodes[0].loadwallet(filename='high_minversion'),
+ )
+ self.stop_node(
+ i=0,
+ expected_stderr='Error: Error loading {}: Wallet requires newer version of Bitcoin Core'.format(
+ wallet_dir('high_minversion', 'wallet.dat')),
+ )
+
if __name__ == '__main__':
MultiWalletTest().main()
diff --git a/test/functional/wallet_reorgsrestore.py b/test/functional/wallet_reorgsrestore.py
new file mode 100755
index 0000000000..f48018e9fb
--- /dev/null
+++ b/test/functional/wallet_reorgsrestore.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python3
+# Copyright (c) 2019 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+"""Test tx status in case of reorgs while wallet being shutdown.
+
+Wallet txn status rely on block connection/disconnection for its
+accuracy. In case of reorgs happening while wallet being shutdown
+block updates are not going to be received. At wallet loading, we
+check against chain if confirmed txn are still in chain and change
+their status if block in which they have been included has been
+disconnected.
+"""
+
+from decimal import Decimal
+import os
+import shutil
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal,
+ connect_nodes,
+ disconnect_nodes,
+)
+
+class ReorgsRestoreTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 3
+
+ def skip_test_if_missing_module(self):
+ self.skip_if_no_wallet()
+
+ def run_test(self):
+ # Send a tx from which to conflict outputs later
+ txid_conflict_from = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), Decimal("10"))
+ self.nodes[0].generate(1)
+ self.sync_blocks()
+
+ # Disconnect node1 from others to reorg its chain later
+ disconnect_nodes(self.nodes[0], 1)
+ disconnect_nodes(self.nodes[1], 2)
+ connect_nodes(self.nodes[0], 2)
+
+ # Send a tx to be unconfirmed later
+ txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), Decimal("10"))
+ tx = self.nodes[0].gettransaction(txid)
+ self.nodes[0].generate(4)
+ tx_before_reorg = self.nodes[0].gettransaction(txid)
+ assert_equal(tx_before_reorg["confirmations"], 4)
+
+ # Disconnect node0 from node2 to broadcast a conflict on their respective chains
+ disconnect_nodes(self.nodes[0], 2)
+ nA = next(tx_out["vout"] for tx_out in self.nodes[0].gettransaction(txid_conflict_from)["details"] if tx_out["amount"] == Decimal("10"))
+ inputs = []
+ inputs.append({"txid": txid_conflict_from, "vout": nA})
+ outputs_1 = {}
+ outputs_2 = {}
+
+ # Create a conflicted tx broadcast on node0 chain and conflicting tx broadcast on node1 chain. Both spend from txid_conflict_from
+ outputs_1[self.nodes[0].getnewaddress()] = Decimal("9.99998")
+ outputs_2[self.nodes[0].getnewaddress()] = Decimal("9.99998")
+ conflicted = self.nodes[0].signrawtransactionwithwallet(self.nodes[0].createrawtransaction(inputs, outputs_1))
+ conflicting = self.nodes[0].signrawtransactionwithwallet(self.nodes[0].createrawtransaction(inputs, outputs_2))
+
+ conflicted_txid = self.nodes[0].sendrawtransaction(conflicted["hex"])
+ self.nodes[0].generate(1)
+ conflicting_txid = self.nodes[2].sendrawtransaction(conflicting["hex"])
+ self.nodes[2].generate(9)
+
+ # Reconnect node0 and node2 and check that conflicted_txid is effectively conflicted
+ connect_nodes(self.nodes[0], 2)
+ self.sync_blocks([self.nodes[0], self.nodes[2]])
+ conflicted = self.nodes[0].gettransaction(conflicted_txid)
+ conflicting = self.nodes[0].gettransaction(conflicting_txid)
+ assert_equal(conflicted["confirmations"], -9)
+ assert_equal(conflicted["walletconflicts"][0], conflicting["txid"])
+
+ # Node0 wallet is shutdown
+ self.stop_node(0)
+ self.start_node(0)
+
+ # The block chain re-orgs and the tx is included in a different block
+ self.nodes[1].generate(9)
+ self.nodes[1].sendrawtransaction(tx["hex"])
+ self.nodes[1].generate(1)
+ self.nodes[1].sendrawtransaction(conflicted["hex"])
+ self.nodes[1].generate(1)
+
+ # Node0 wallet file is loaded on longest sync'ed node1
+ self.stop_node(1)
+ self.nodes[0].backupwallet(os.path.join(self.nodes[0].datadir, 'wallet.bak'))
+ shutil.copyfile(os.path.join(self.nodes[0].datadir, 'wallet.bak'), os.path.join(self.nodes[1].datadir, 'regtest', 'wallet.dat'))
+ self.start_node(1)
+ tx_after_reorg = self.nodes[1].gettransaction(txid)
+ # Check that normal confirmed tx is confirmed again but with different blockhash
+ assert_equal(tx_after_reorg["confirmations"], 2)
+ assert(tx_before_reorg["blockhash"] != tx_after_reorg["blockhash"])
+ conflicted_after_reorg = self.nodes[1].gettransaction(conflicted_txid)
+ # Check that conflicted tx is confirmed again with blockhash different than previously conflicting tx
+ assert_equal(conflicted_after_reorg["confirmations"], 1)
+ assert(conflicting["blockhash"] != conflicted_after_reorg["blockhash"])
+
+if __name__ == '__main__':
+ ReorgsRestoreTest().main()
diff --git a/test/functional/wallet_watchonly.py b/test/functional/wallet_watchonly.py
index be8d7714fb..be8d7714fb 100644..100755
--- a/test/functional/wallet_watchonly.py
+++ b/test/functional/wallet_watchonly.py
diff --git a/test/lint/check-doc.py b/test/lint/check-doc.py
index 1d6122a13d..bd947d194c 100755
--- a/test/lint/check-doc.py
+++ b/test/lint/check-doc.py
@@ -15,8 +15,8 @@ import re
FOLDER_GREP = 'src'
FOLDER_TEST = 'src/test/'
-REGEX_ARG = '(?:ForceSet|SoftSet|Get|Is)(?:Bool)?Args?(?:Set)?\("(-[^"]+)"'
-REGEX_DOC = 'AddArg\("(-[^"=]+?)(?:=|")'
+REGEX_ARG = r'(?:ForceSet|SoftSet|Get|Is)(?:Bool)?Args?(?:Set)?\("(-[^"]+)"'
+REGEX_DOC = r'AddArg\("(-[^"=]+?)(?:=|")'
CMD_ROOT_DIR = '$(git rev-parse --show-toplevel)/{}'.format(FOLDER_GREP)
CMD_GREP_ARGS = r"git grep --perl-regexp '{}' -- {} ':(exclude){}'".format(REGEX_ARG, CMD_ROOT_DIR, FOLDER_TEST)
CMD_GREP_WALLET_ARGS = r"git grep --function-context 'void WalletInit::AddWalletOptions' -- {} | grep AddArg".format(CMD_ROOT_DIR)
diff --git a/test/lint/check-rpc-mappings.py b/test/lint/check-rpc-mappings.py
index 137cc82b5d..a33ab17f3f 100755
--- a/test/lint/check-rpc-mappings.py
+++ b/test/lint/check-rpc-mappings.py
@@ -48,13 +48,13 @@ def process_commands(fname):
for line in f:
line = line.rstrip()
if not in_rpcs:
- if re.match("static const CRPCCommand .*\[\] =", line):
+ if re.match(r"static const CRPCCommand .*\[\] =", line):
in_rpcs = True
else:
if line.startswith('};'):
in_rpcs = False
elif '{' in line and '"' in line:
- m = re.search('{ *("[^"]*"), *("[^"]*"), *&([^,]*), *{([^}]*)} *},', line)
+ m = re.search(r'{ *("[^"]*"), *("[^"]*"), *&([^,]*), *{([^}]*)} *},', line)
assert m, 'No match to table expression: %s' % line
name = parse_string(m.group(2))
args_str = m.group(4).strip()
@@ -80,7 +80,7 @@ def process_mapping(fname):
if line.startswith('};'):
in_rpcs = False
elif '{' in line and '"' in line:
- m = re.search('{ *("[^"]*"), *([0-9]+) *, *("[^"]*") *},', line)
+ m = re.search(r'{ *("[^"]*"), *([0-9]+) *, *("[^"]*") *},', line)
assert m, 'No match to table expression: %s' % line
name = parse_string(m.group(1))
idx = int(m.group(2))
diff --git a/test/lint/lint-format-strings.py b/test/lint/lint-format-strings.py
index 47ad896589..9f34d0f4dd 100755
--- a/test/lint/lint-format-strings.py
+++ b/test/lint/lint-format-strings.py
@@ -55,7 +55,7 @@ def normalize(s):
assert type(s) is str
s = s.replace("\n", " ")
s = s.replace("\t", " ")
- s = re.sub("/\*.*?\*/", " ", s)
+ s = re.sub(r"/\*.*?\*/", " ", s)
s = re.sub(" {2,}", " ", s)
return s.strip()
diff --git a/test/lint/lint-includes.sh b/test/lint/lint-includes.sh
index 4b9e2615b6..d27e45a23f 100755
--- a/test/lint/lint-includes.sh
+++ b/test/lint/lint-includes.sh
@@ -11,6 +11,9 @@
export LC_ALL=C
IGNORE_REGEXP="/(leveldb|secp256k1|univalue)/"
+# cd to root folder of git repo for git ls-files to work properly
+cd "$(dirname $0)/../.." || exit 1
+
filter_suffix() {
git ls-files | grep -E "^src/.*\.${1}"'$' | grep -Ev "${IGNORE_REGEXP}"
}
diff --git a/test/lint/lint-python-mutable-default-parameters.sh b/test/lint/lint-python-mutable-default-parameters.sh
new file mode 100755
index 0000000000..1f9f035d30
--- /dev/null
+++ b/test/lint/lint-python-mutable-default-parameters.sh
@@ -0,0 +1,52 @@
+#!/usr/bin/env bash
+#
+# Copyright (c) 2019 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#
+# Detect when a mutable list or dict is used as a default parameter value in a Python function.
+
+export LC_ALL=C
+EXIT_CODE=0
+OUTPUT=$(git grep -E '^\s*def [a-zA-Z0-9_]+\(.*=\s*(\[|\{)' -- "*.py")
+if [[ ${OUTPUT} != "" ]]; then
+ echo "A mutable list or dict seems to be used as default parameter value:"
+ echo
+ echo "${OUTPUT}"
+ echo
+ cat << EXAMPLE
+This is how mutable list and dict default parameter values behave:
+
+>>> def f(i, j=[], k={}):
+... j.append(i)
+... k[i] = True
+... return j, k
+...
+>>> f(1)
+([1], {1: True})
+>>> f(1)
+([1, 1], {1: True})
+>>> f(2)
+([1, 1, 2], {1: True, 2: True})
+
+The intended behaviour was likely:
+
+>>> def f(i, j=None, k=None):
+... if j is None:
+... j = []
+... if k is None:
+... k = {}
+... j.append(i)
+... k[i] = True
+... return j, k
+...
+>>> f(1)
+([1], {1: True})
+>>> f(1)
+([1], {1: True})
+>>> f(2)
+([2], {2: True})
+EXAMPLE
+ EXIT_CODE=1
+fi
+exit ${EXIT_CODE}
diff --git a/test/lint/lint-python.sh b/test/lint/lint-python.sh
index a76806003f..3c82ec19e3 100755
--- a/test/lint/lint-python.sh
+++ b/test/lint/lint-python.sh
@@ -73,7 +73,6 @@ enabled=(
W291 # trailing whitespace
W292 # no newline at end of file
W293 # blank line contains whitespace
- W504 # line break after binary operator
W601 # .has_key() is deprecated, use "in"
W602 # deprecated form of raising exception
W603 # "<>" is deprecated, use "!="
diff --git a/test/lint/lint-spelling.ignore-words.txt b/test/lint/lint-spelling.ignore-words.txt
index a25de2435b..b08837c1d4 100644
--- a/test/lint/lint-spelling.ignore-words.txt
+++ b/test/lint/lint-spelling.ignore-words.txt
@@ -12,3 +12,4 @@ cachable
errorstring
keyserver
homogenous
+setban
diff --git a/test/lint/lint-spelling.sh b/test/lint/lint-spelling.sh
index 5d672698a7..e70b73e1cc 100755
--- a/test/lint/lint-spelling.sh
+++ b/test/lint/lint-spelling.sh
@@ -9,6 +9,11 @@
export LC_ALL=C
+if ! command -v codespell > /dev/null; then
+ echo "Skipping spell check linting since codespell is not installed."
+ exit 0
+fi
+
IGNORE_WORDS_FILE=test/lint/lint-spelling.ignore-words.txt
if ! codespell --check-filenames --disable-colors --quiet-level=7 --ignore-words=${IGNORE_WORDS_FILE} $(git ls-files -- ":(exclude)build-aux/m4/" ":(exclude)contrib/seeds/*.txt" ":(exclude)depends/" ":(exclude)doc/release-notes/" ":(exclude)src/leveldb/" ":(exclude)src/qt/locale/" ":(exclude)src/secp256k1/" ":(exclude)src/univalue/"); then
echo "^ Warning: codespell identified likely spelling errors. Any false positives? Add them to the list of ignored words in ${IGNORE_WORDS_FILE}"
diff --git a/test/util/data/txcreateoutpubkey1.json b/test/util/data/txcreateoutpubkey1.json
index 32097b3ebe..42b519bb21 100644
--- a/test/util/data/txcreateoutpubkey1.json
+++ b/test/util/data/txcreateoutpubkey1.json
@@ -15,11 +15,7 @@
"scriptPubKey": {
"asm": "02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397 OP_CHECKSIG",
"hex": "2102a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397ac",
- "reqSigs": 1,
- "type": "pubkey",
- "addresses": [
- "1FoG2386FG2tAJS9acMuiDsKy67aGg9MKz"
- ]
+ "type": "pubkey"
}
}
],