aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/README.md3
-rwxr-xr-xtest/functional/test_runner.py10
-rwxr-xr-xtest/functional/wallet_avoidreuse.py217
-rwxr-xr-xtest/fuzz/test_runner.py4
-rw-r--r--test/sanitizer_suppressions/lsan3
5 files changed, 230 insertions, 7 deletions
diff --git a/test/README.md b/test/README.md
index 9d4351b1de..ecea3213ab 100644
--- a/test/README.md
+++ b/test/README.md
@@ -13,8 +13,7 @@ bitcoin-tx.
- [lint](/test/lint/) which perform various static analysis checks.
The util tests are run as part of `make check` target. The functional
-tests and lint scripts are run by the travis continuous build process whenever a pull
-request is opened. All sets of tests can also be run locally.
+tests and lint scripts can be run as explained in the sections below.
# Running tests locally
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 80dced733e..462547f44f 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -67,14 +67,14 @@ TEST_EXIT_PASSED = 0
TEST_EXIT_SKIPPED = 77
EXTENDED_SCRIPTS = [
- # These tests are not run by the travis build process.
+ # These tests are not run by default.
# Longest test should go first, to favor running tests in parallel
'feature_pruning.py',
'feature_dbcrash.py',
]
BASE_SCRIPTS = [
- # Scripts that are run by the travis build process.
+ # Scripts that are run by default.
# Longest test should go first, to favor running tests in parallel
'feature_fee_estimation.py',
'wallet_hd.py',
@@ -120,6 +120,7 @@ BASE_SCRIPTS = [
'rpc_misc.py',
'interface_rest.py',
'mempool_spend_coinbase.py',
+ 'wallet_avoidreuse.py',
'mempool_reorg.py',
'mempool_persist.py',
'wallet_multiwallet.py',
@@ -494,7 +495,8 @@ class TestHandler:
for job in self.jobs:
(name, start_time, proc, testdir, log_out, log_err) = job
if int(time.time() - start_time) > self.timeout_duration:
- # In travis, timeout individual tests (to stop tests hanging and not providing useful output).
+ # Timeout individual tests if timeout is specified (to stop
+ # tests hanging and not providing useful output).
proc.send_signal(signal.SIGINT)
if proc.poll() is not None:
log_out.seek(0), log_err.seek(0)
@@ -582,7 +584,7 @@ def check_script_list(*, src_dir, fail_on_warn):
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 fail_on_warn:
- # On travis this warning is an error to prevent merging incomplete commits into master
+ # On CI this warning is an error to prevent merging incomplete commits into master
sys.exit(1)
diff --git a/test/functional/wallet_avoidreuse.py b/test/functional/wallet_avoidreuse.py
new file mode 100755
index 0000000000..1dec040f68
--- /dev/null
+++ b/test/functional/wallet_avoidreuse.py
@@ -0,0 +1,217 @@
+#!/usr/bin/env python3
+# Copyright (c) 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 the avoid_reuse and setwalletflag features."""
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal,
+ assert_raises_rpc_error,
+ connect_nodes_bi,
+)
+
+# TODO: Copied from wallet_groups.py -- should perhaps move into util.py
+def assert_approx(v, vexp, vspan=0.00001):
+ if v < vexp - vspan:
+ raise AssertionError("%s < [%s..%s]" % (str(v), str(vexp - vspan), str(vexp + vspan)))
+ if v > vexp + vspan:
+ raise AssertionError("%s > [%s..%s]" % (str(v), str(vexp - vspan), str(vexp + vspan)))
+
+def reset_balance(node, discardaddr):
+ '''Throw away all owned coins by the node so it gets a balance of 0.'''
+ balance = node.getbalance(avoid_reuse=False)
+ if balance > 0.5:
+ node.sendtoaddress(address=discardaddr, amount=balance, subtractfeefromamount=True, avoid_reuse=False)
+
+def count_unspent(node):
+ '''Count the unspent outputs for the given node and return various statistics'''
+ r = {
+ "total": {
+ "count": 0,
+ "sum": 0,
+ },
+ "reused": {
+ "count": 0,
+ "sum": 0,
+ },
+ }
+ supports_reused = True
+ for utxo in node.listunspent(minconf=0):
+ r["total"]["count"] += 1
+ r["total"]["sum"] += utxo["amount"]
+ if supports_reused and "reused" in utxo:
+ if utxo["reused"]:
+ r["reused"]["count"] += 1
+ r["reused"]["sum"] += utxo["amount"]
+ else:
+ supports_reused = False
+ r["reused"]["supported"] = supports_reused
+ return r
+
+def assert_unspent(node, total_count=None, total_sum=None, reused_supported=None, reused_count=None, reused_sum=None):
+ '''Make assertions about a node's unspent output statistics'''
+ stats = count_unspent(node)
+ if total_count is not None:
+ assert_equal(stats["total"]["count"], total_count)
+ if total_sum is not None:
+ assert_approx(stats["total"]["sum"], total_sum, 0.001)
+ if reused_supported is not None:
+ assert_equal(stats["reused"]["supported"], reused_supported)
+ if reused_count is not None:
+ assert_equal(stats["reused"]["count"], reused_count)
+ if reused_sum is not None:
+ assert_approx(stats["reused"]["sum"], reused_sum, 0.001)
+
+class AvoidReuseTest(BitcoinTestFramework):
+
+ def set_test_params(self):
+ self.setup_clean_chain = False
+ self.num_nodes = 2
+
+ def skip_test_if_missing_module(self):
+ self.skip_if_no_wallet()
+
+ def run_test(self):
+ '''Set up initial chain and run tests defined below'''
+
+ self.test_persistence()
+ self.test_immutable()
+
+ self.nodes[0].generate(110)
+ self.sync_all()
+ reset_balance(self.nodes[1], self.nodes[0].getnewaddress())
+ self.test_fund_send_fund_senddirty()
+ reset_balance(self.nodes[1], self.nodes[0].getnewaddress())
+ self.test_fund_send_fund_send()
+
+ def test_persistence(self):
+ '''Test that wallet files persist the avoid_reuse flag.'''
+ # Configure node 1 to use avoid_reuse
+ self.nodes[1].setwalletflag('avoid_reuse')
+
+ # Flags should be node1.avoid_reuse=false, node2.avoid_reuse=true
+ assert_equal(self.nodes[0].getwalletinfo()["avoid_reuse"], False)
+ assert_equal(self.nodes[1].getwalletinfo()["avoid_reuse"], True)
+
+ # Stop and restart node 1
+ self.stop_node(1)
+ self.start_node(1)
+ connect_nodes_bi(self.nodes, 0, 1)
+
+ # Flags should still be node1.avoid_reuse=false, node2.avoid_reuse=true
+ assert_equal(self.nodes[0].getwalletinfo()["avoid_reuse"], False)
+ assert_equal(self.nodes[1].getwalletinfo()["avoid_reuse"], True)
+
+ # Attempting to set flag to its current state should throw
+ assert_raises_rpc_error(-8, "Wallet flag is already set to false", self.nodes[0].setwalletflag, 'avoid_reuse', False)
+ assert_raises_rpc_error(-8, "Wallet flag is already set to true", self.nodes[1].setwalletflag, 'avoid_reuse', True)
+
+ def test_immutable(self):
+ '''Test immutable wallet flags'''
+ # Attempt to set the disable_private_keys flag; this should not work
+ assert_raises_rpc_error(-8, "Wallet flag is immutable", self.nodes[1].setwalletflag, 'disable_private_keys')
+
+ tempwallet = ".wallet_avoidreuse.py_test_immutable_wallet.dat"
+
+ # Create a wallet with disable_private_keys set; this should work
+ self.nodes[1].createwallet(tempwallet, True)
+ w = self.nodes[1].get_wallet_rpc(tempwallet)
+
+ # Attempt to unset the disable_private_keys flag; this should not work
+ assert_raises_rpc_error(-8, "Wallet flag is immutable", w.setwalletflag, 'disable_private_keys', False)
+
+ # Unload temp wallet
+ self.nodes[1].unloadwallet(tempwallet)
+
+ def test_fund_send_fund_senddirty(self):
+ '''
+ Test the same as test_fund_send_fund_send, except send the 10 BTC with
+ the avoid_reuse flag set to false. This means the 10 BTC send should succeed,
+ where it fails in test_fund_send_fund_send.
+ '''
+
+ fundaddr = self.nodes[1].getnewaddress()
+ retaddr = self.nodes[0].getnewaddress()
+
+ self.nodes[0].sendtoaddress(fundaddr, 10)
+ self.nodes[0].generate(1)
+ self.sync_all()
+
+ # listunspent should show 1 single, unused 10 btc output
+ assert_unspent(self.nodes[1], total_count=1, total_sum=10, reused_supported=True, reused_count=0)
+
+ self.nodes[1].sendtoaddress(retaddr, 5)
+ self.nodes[0].generate(1)
+ self.sync_all()
+
+ # listunspent should show 1 single, unused 5 btc output
+ assert_unspent(self.nodes[1], total_count=1, total_sum=5, reused_supported=True, reused_count=0)
+
+ self.nodes[0].sendtoaddress(fundaddr, 10)
+ self.nodes[0].generate(1)
+ self.sync_all()
+
+ # listunspent should show 2 total outputs (5, 10 btc), one unused (5), one reused (10)
+ assert_unspent(self.nodes[1], total_count=2, total_sum=15, reused_count=1, reused_sum=10)
+
+ self.nodes[1].sendtoaddress(address=retaddr, amount=10, avoid_reuse=False)
+
+ # listunspent should show 1 total outputs (5 btc), unused
+ assert_unspent(self.nodes[1], total_count=1, total_sum=5, reused_count=0)
+
+ # node 1 should now have about 5 btc left (for both cases)
+ assert_approx(self.nodes[1].getbalance(), 5, 0.001)
+ assert_approx(self.nodes[1].getbalance(avoid_reuse=False), 5, 0.001)
+
+ def test_fund_send_fund_send(self):
+ '''
+ Test the simple case where [1] generates a new address A, then
+ [0] sends 10 BTC to A.
+ [1] spends 5 BTC from A. (leaving roughly 5 BTC useable)
+ [0] sends 10 BTC to A again.
+ [1] tries to spend 10 BTC (fails; dirty).
+ [1] tries to spend 4 BTC (succeeds; change address sufficient)
+ '''
+
+ fundaddr = self.nodes[1].getnewaddress()
+ retaddr = self.nodes[0].getnewaddress()
+
+ self.nodes[0].sendtoaddress(fundaddr, 10)
+ self.nodes[0].generate(1)
+ self.sync_all()
+
+ # listunspent should show 1 single, unused 10 btc output
+ assert_unspent(self.nodes[1], total_count=1, total_sum=10, reused_supported=True, reused_count=0)
+
+ self.nodes[1].sendtoaddress(retaddr, 5)
+ self.nodes[0].generate(1)
+ self.sync_all()
+
+ # listunspent should show 1 single, unused 5 btc output
+ assert_unspent(self.nodes[1], total_count=1, total_sum=5, reused_supported=True, reused_count=0)
+
+ self.nodes[0].sendtoaddress(fundaddr, 10)
+ self.nodes[0].generate(1)
+ self.sync_all()
+
+ # listunspent should show 2 total outputs (5, 10 btc), one unused (5), one reused (10)
+ assert_unspent(self.nodes[1], total_count=2, total_sum=15, reused_count=1, reused_sum=10)
+
+ # node 1 should now have a balance of 5 (no dirty) or 15 (including dirty)
+ assert_approx(self.nodes[1].getbalance(), 5, 0.001)
+ assert_approx(self.nodes[1].getbalance(avoid_reuse=False), 15, 0.001)
+
+ assert_raises_rpc_error(-6, "Insufficient funds", self.nodes[1].sendtoaddress, retaddr, 10)
+
+ self.nodes[1].sendtoaddress(retaddr, 4)
+
+ # listunspent should show 2 total outputs (1, 10 btc), one unused (1), one reused (10)
+ assert_unspent(self.nodes[1], total_count=2, total_sum=11, reused_count=1, reused_sum=10)
+
+ # node 1 should now have about 1 btc left (no dirty) and 11 (including dirty)
+ assert_approx(self.nodes[1].getbalance(), 1, 0.001)
+ assert_approx(self.nodes[1].getbalance(avoid_reuse=False), 11, 0.001)
+
+if __name__ == '__main__':
+ AvoidReuseTest().main()
diff --git a/test/fuzz/test_runner.py b/test/fuzz/test_runner.py
index 1869f71753..f19cf924d2 100755
--- a/test/fuzz/test_runner.py
+++ b/test/fuzz/test_runner.py
@@ -106,8 +106,10 @@ def run_once(*, corpus, test_list, build_dir, export_coverage):
os.path.join(corpus, t),
]
logging.debug('Run {} with args {}'.format(t, args))
- output = subprocess.run(args, check=True, stderr=subprocess.PIPE, universal_newlines=True).stderr
+ result = subprocess.run(args, stderr=subprocess.PIPE, universal_newlines=True)
+ output = result.stderr
logging.debug('Output: {}'.format(output))
+ result.check_returncode()
if not export_coverage:
continue
for l in output.splitlines():
diff --git a/test/sanitizer_suppressions/lsan b/test/sanitizer_suppressions/lsan
index 6f15c0f1d4..90a92a5115 100644
--- a/test/sanitizer_suppressions/lsan
+++ b/test/sanitizer_suppressions/lsan
@@ -4,3 +4,6 @@ leak:libqminimal
leak:libQt5Core
leak:libQt5Gui
leak:libQt5Widgets
+
+# false-positive due to use of secure_allocator<>
+leak:GetRNGState