aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rwxr-xr-xtest/functional/combine_logs.py2
-rwxr-xr-xtest/functional/feature_logging.py2
-rwxr-xr-xtest/functional/feature_uacomment.py4
-rwxr-xr-xtest/functional/mempool_package_onemore.py10
-rwxr-xr-xtest/functional/p2p_blocksonly.py10
-rwxr-xr-xtest/functional/rpc_scantxoutset.py7
-rwxr-xr-xtest/functional/rpc_setban.py2
-rwxr-xr-xtest/functional/test_framework/test_node.py36
-rwxr-xr-xtest/functional/test_runner.py1
-rwxr-xr-xtest/functional/wallet_multiwallet.py6
-rwxr-xr-xtest/functional/wallet_reorgsrestore.py105
-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.sh1
16 files changed, 163 insertions, 38 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/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_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/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/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
index 423741fd27..b1d2b6f431 100755
--- a/test/functional/rpc_setban.py
+++ b/test/functional/rpc_setban.py
@@ -26,7 +26,7 @@ class SetBanTests(BitcoinTestFramework):
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):
+ 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")
diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index 9667cf4ea4..55e6d4caa6 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -313,24 +313,24 @@ class TestNode():
with open(debug_log, encoding='utf-8') as dl:
dl.seek(0, 2)
prev_size = dl.tell()
- try:
- yield
- finally:
- 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:
- 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))
+
+ 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:
+ 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_runner.py b/test/functional/test_runner.py
index ad5673e03a..dd61efa757 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',
diff --git a/test/functional/wallet_multiwallet.py b/test/functional/wallet_multiwallet.py
index 2d0a100ac4..68bc45f986 100755
--- a/test/functional/wallet_multiwallet.py
+++ b/test/functional/wallet_multiwallet.py
@@ -102,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')
@@ -148,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)
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/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.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 "!="