aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rwxr-xr-xtest/functional/feature_init.py41
-rwxr-xr-xtest/functional/interface_rest.py53
-rwxr-xr-xtest/functional/test_framework/test_node.py9
-rwxr-xr-xtest/functional/wallet_abandonconflict.py101
-rw-r--r--test/sanitizer_suppressions/ubsan2
5 files changed, 96 insertions, 110 deletions
diff --git a/test/functional/feature_init.py b/test/functional/feature_init.py
index 4b56b0c26b..dbd71a8b2d 100755
--- a/test/functional/feature_init.py
+++ b/test/functional/feature_init.py
@@ -3,8 +3,6 @@
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Stress tests related to node initialization."""
-import random
-import time
import os
from pathlib import Path
@@ -26,7 +24,6 @@ class InitStressTest(BitcoinTestFramework):
def run_test(self):
"""
- test terminating initialization after seeing a certain log line.
- - test terminating init after seeing a random number of log lines.
- test removing certain essential files to test startup error paths.
"""
# TODO: skip Windows for now since it isn't clear how to SIGTERM.
@@ -76,46 +73,14 @@ class InitStressTest(BitcoinTestFramework):
for terminate_line in lines_to_terminate_after:
self.log.info(f"Starting node and will exit after line '{terminate_line}'")
- node.start(extra_args=['-txindex=1'])
-
- num_total_logs = node.wait_for_debug_log([terminate_line], ignore_case=True)
- self.log.debug(f"Terminating node after {num_total_logs} log lines seen")
+ with node.wait_for_debug_log([terminate_line], ignore_case=True):
+ node.start(extra_args=['-txindex=1'])
+ self.log.debug("Terminating node after terminate line was found")
sigterm_node()
check_clean_start()
self.stop_node(0)
- self.log.info(
- f"Terminate at some random point in the init process (max logs: {num_total_logs})")
-
- for _ in range(40):
- num_logs = len(Path(node.debug_log_path).read_text().splitlines())
- additional_lines = random.randint(1, num_total_logs)
- self.log.debug(f"Starting node and will exit after {additional_lines} lines")
- node.start(extra_args=['-txindex=1'])
- logfile = open(node.debug_log_path, 'rb')
-
- MAX_SECS_TO_WAIT = 10
- start = time.time()
- num_lines = 0
-
- while True:
- line = logfile.readline()
- if line:
- num_lines += 1
-
- if num_lines >= (num_logs + additional_lines) or \
- (time.time() - start) > MAX_SECS_TO_WAIT:
- self.log.debug(f"Terminating node after {num_lines} log lines seen")
- sigterm_node()
- break
-
- if node.process.poll() is not None:
- raise AssertionError("node failed to start")
-
- check_clean_start()
- self.stop_node(0)
-
self.log.info("Test startup errors after removing certain essential files")
files_to_disturb = {
diff --git a/test/functional/interface_rest.py b/test/functional/interface_rest.py
index 06aa5608bb..a3d949c6a8 100755
--- a/test/functional/interface_rest.py
+++ b/test/functional/interface_rest.py
@@ -6,21 +6,28 @@
from decimal import Decimal
from enum import Enum
+import http.client
from io import BytesIO
import json
from struct import pack, unpack
-
-import http.client
import urllib.parse
+
+from test_framework.messages import (
+ BLOCK_HEADER_SIZE,
+ COIN,
+)
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
assert_greater_than,
assert_greater_than_or_equal,
)
+from test_framework.wallet import (
+ MiniWallet,
+ getnewdestination,
+)
-from test_framework.messages import BLOCK_HEADER_SIZE
INVALID_PARAM = "abc"
UNKNOWN_PARAM = "0000000000000000000000000000000000000000000000000000000000000000"
@@ -43,14 +50,13 @@ def filter_output_indices_by_value(vouts, value):
class RESTTest (BitcoinTestFramework):
def set_test_params(self):
- self.setup_clean_chain = True
self.num_nodes = 2
self.extra_args = [["-rest", "-blockfilterindex=1"], []]
+ # whitelist peers to speed up tx relay / mempool sync
+ for args in self.extra_args:
+ args.append("-whitelist=noban@127.0.0.1")
self.supports_cli = False
- def skip_test_if_missing_module(self):
- self.skip_if_no_wallet()
-
def test_rest_request(self, uri, http_method='GET', req_type=ReqType.JSON, body='', status=200, ret_type=RetType.JSON):
rest_uri = '/rest' + uri
if req_type == ReqType.JSON:
@@ -79,17 +85,11 @@ class RESTTest (BitcoinTestFramework):
def run_test(self):
self.url = urllib.parse.urlparse(self.nodes[0].url)
- self.log.info("Mine blocks and send Bitcoin to node 1")
+ self.wallet = MiniWallet(self.nodes[0])
+ self.wallet.rescan_utxos()
- # Random address so node1's balance doesn't increase
- not_related_address = "2MxqoHEdNQTyYeX1mHcbrrpzgojbosTpCvJ"
-
- self.generate(self.nodes[0], 1)
- self.generatetoaddress(self.nodes[1], 100, not_related_address)
-
- assert_equal(self.nodes[0].getbalance(), 50)
-
- txid = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 0.1)
+ self.log.info("Broadcast test transaction and sync nodes")
+ txid, _ = self.wallet.send_to(from_node=self.nodes[0], scriptPubKey=getnewdestination()[1], amount=int(0.1 * COIN))
self.sync_all()
self.log.info("Test the /tx URI")
@@ -115,11 +115,9 @@ class RESTTest (BitcoinTestFramework):
self.log.info("Query an unspent TXO using the /getutxos URI")
- self.generatetoaddress(self.nodes[1], 1, not_related_address)
+ self.generate(self.wallet, 1)
bb_hash = self.nodes[0].getbestblockhash()
- assert_equal(self.nodes[1].getbalance(), Decimal("0.1"))
-
# Check chainTip response
json_obj = self.test_rest_request(f"/getutxos/{spending[0]}-{spending[1]}")
assert_equal(json_obj['chaintipHash'], bb_hash)
@@ -161,7 +159,7 @@ class RESTTest (BitcoinTestFramework):
response_hash = output.read(32)[::-1].hex()
assert_equal(bb_hash, response_hash) # check if getutxo's chaintip during calculation was fine
- assert_equal(chain_height, 102) # chain height must be 102
+ assert_equal(chain_height, 201) # chain height must be 201 (pre-mined chain [200] + generated block [1])
self.log.info("Test the /getutxos URI with and without /checkmempool")
# Create a transaction, check that it's found with /checkmempool, but
@@ -169,7 +167,7 @@ class RESTTest (BitcoinTestFramework):
# found with or without /checkmempool.
# do a tx and don't sync
- txid = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 0.1)
+ txid, _ = self.wallet.send_to(from_node=self.nodes[0], scriptPubKey=getnewdestination()[1], amount=int(0.1 * COIN))
json_obj = self.test_rest_request(f"/tx/{txid}")
# get the spent output to later check for utxo (should be spent by then)
spent = (json_obj['vin'][0]['txid'], json_obj['vin'][0]['vout'])
@@ -301,11 +299,13 @@ class RESTTest (BitcoinTestFramework):
self.log.info("Test tx inclusion in the /mempool and /block URIs")
- # Make 3 tx and mine them on node 1
+ # Make 3 chained txs and mine them on node 1
txs = []
- txs.append(self.nodes[0].sendtoaddress(not_related_address, 11))
- txs.append(self.nodes[0].sendtoaddress(not_related_address, 11))
- txs.append(self.nodes[0].sendtoaddress(not_related_address, 11))
+ input_txid = txid
+ for _ in range(3):
+ utxo_to_spend = self.wallet.get_utxo(txid=input_txid)
+ txs.append(self.wallet.send_self_transfer(from_node=self.nodes[0], utxo_to_spend=utxo_to_spend)['txid'])
+ input_txid = txs[-1]
self.sync_all()
# Check that there are exactly 3 transactions in the TX memory pool before generating the block
@@ -351,5 +351,6 @@ class RESTTest (BitcoinTestFramework):
json_obj = self.test_rest_request("/chaininfo")
assert_equal(json_obj['bestblockhash'], bb_hash)
+
if __name__ == '__main__':
RESTTest().main()
diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index 0b9154a030..289e83579b 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -422,7 +422,8 @@ class TestNode():
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))
- def wait_for_debug_log(self, expected_msgs, timeout=10, ignore_case=False) -> int:
+ @contextlib.contextmanager
+ def wait_for_debug_log(self, expected_msgs, timeout=60, ignore_case=False):
"""
Block until we see a particular debug log message fragment or until we exceed the timeout.
Return:
@@ -432,6 +433,8 @@ class TestNode():
prev_size = self.debug_log_bytes()
re_flags = re.MULTILINE | (re.IGNORECASE if ignore_case else 0)
+ yield
+
while True:
found = True
with open(self.debug_log_path, encoding='utf-8') as dl:
@@ -443,8 +446,7 @@ class TestNode():
found = False
if found:
- num_logs = len(log.splitlines())
- return num_logs
+ return
if time.time() >= time_end:
print_log = " - " + "\n - ".join(log.splitlines())
@@ -456,7 +458,6 @@ class TestNode():
self._raise_assertion_error(
'Expected messages "{}" does not partially match log:\n\n{}\n\n'.format(
str(expected_msgs), print_log))
- return -1 # useless return to satisfy linter
@contextlib.contextmanager
def profile_with_perf(self, profile_name: str):
diff --git a/test/functional/wallet_abandonconflict.py b/test/functional/wallet_abandonconflict.py
index 27d9d8da88..36fcdb36d6 100755
--- a/test/functional/wallet_abandonconflict.py
+++ b/test/functional/wallet_abandonconflict.py
@@ -29,20 +29,25 @@ class AbandonConflictTest(BitcoinTestFramework):
self.skip_if_no_wallet()
def run_test(self):
+ # create two wallets to tests conflicts from both sender's and receiver's sides
+ alice = self.nodes[0].get_wallet_rpc(self.default_wallet_name)
+ self.nodes[0].createwallet(wallet_name="bob")
+ bob = self.nodes[0].get_wallet_rpc("bob")
+
self.generate(self.nodes[1], COINBASE_MATURITY)
- balance = self.nodes[0].getbalance()
- txA = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), Decimal("10"))
- txB = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), Decimal("10"))
- txC = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), Decimal("10"))
+ balance = alice.getbalance()
+ txA = alice.sendtoaddress(alice.getnewaddress(), Decimal("10"))
+ txB = alice.sendtoaddress(alice.getnewaddress(), Decimal("10"))
+ txC = alice.sendtoaddress(alice.getnewaddress(), Decimal("10"))
self.sync_mempools()
self.generate(self.nodes[1], 1)
# Can not abandon non-wallet transaction
- assert_raises_rpc_error(-5, 'Invalid or non-wallet transaction id', lambda: self.nodes[0].abandontransaction(txid='ff' * 32))
+ assert_raises_rpc_error(-5, 'Invalid or non-wallet transaction id', lambda: alice.abandontransaction(txid='ff' * 32))
# Can not abandon confirmed transaction
- assert_raises_rpc_error(-5, 'Transaction not eligible for abandonment', lambda: self.nodes[0].abandontransaction(txid=txA))
+ assert_raises_rpc_error(-5, 'Transaction not eligible for abandonment', lambda: alice.abandontransaction(txid=txA))
- newbalance = self.nodes[0].getbalance()
+ newbalance = alice.getbalance()
assert balance - newbalance < Decimal("0.001") #no more than fees lost
balance = newbalance
@@ -50,9 +55,9 @@ class AbandonConflictTest(BitcoinTestFramework):
self.disconnect_nodes(0, 1)
# Identify the 10btc outputs
- nA = next(tx_out["vout"] for tx_out in self.nodes[0].gettransaction(txA)["details"] if tx_out["amount"] == Decimal("10"))
- nB = next(tx_out["vout"] for tx_out in self.nodes[0].gettransaction(txB)["details"] if tx_out["amount"] == Decimal("10"))
- nC = next(tx_out["vout"] for tx_out in self.nodes[0].gettransaction(txC)["details"] if tx_out["amount"] == Decimal("10"))
+ nA = next(tx_out["vout"] for tx_out in alice.gettransaction(txA)["details"] if tx_out["amount"] == Decimal("10"))
+ nB = next(tx_out["vout"] for tx_out in alice.gettransaction(txB)["details"] if tx_out["amount"] == Decimal("10"))
+ nC = next(tx_out["vout"] for tx_out in alice.gettransaction(txC)["details"] if tx_out["amount"] == Decimal("10"))
inputs = []
# spend 10btc outputs from txA and txB
@@ -60,39 +65,40 @@ class AbandonConflictTest(BitcoinTestFramework):
inputs.append({"txid": txB, "vout": nB})
outputs = {}
- outputs[self.nodes[0].getnewaddress()] = Decimal("14.99998")
- outputs[self.nodes[1].getnewaddress()] = Decimal("5")
- signed = self.nodes[0].signrawtransactionwithwallet(self.nodes[0].createrawtransaction(inputs, outputs))
+ outputs[alice.getnewaddress()] = Decimal("14.99998")
+ outputs[bob.getnewaddress()] = Decimal("5")
+ signed = alice.signrawtransactionwithwallet(alice.createrawtransaction(inputs, outputs))
txAB1 = self.nodes[0].sendrawtransaction(signed["hex"])
# Identify the 14.99998btc output
- nAB = next(tx_out["vout"] for tx_out in self.nodes[0].gettransaction(txAB1)["details"] if tx_out["amount"] == Decimal("14.99998"))
+ nAB = next(tx_out["vout"] for tx_out in alice.gettransaction(txAB1)["details"] if tx_out["amount"] == Decimal("14.99998"))
#Create a child tx spending AB1 and C
inputs = []
inputs.append({"txid": txAB1, "vout": nAB})
inputs.append({"txid": txC, "vout": nC})
outputs = {}
- outputs[self.nodes[0].getnewaddress()] = Decimal("24.9996")
- signed2 = self.nodes[0].signrawtransactionwithwallet(self.nodes[0].createrawtransaction(inputs, outputs))
+ outputs[alice.getnewaddress()] = Decimal("24.9996")
+ signed2 = alice.signrawtransactionwithwallet(alice.createrawtransaction(inputs, outputs))
txABC2 = self.nodes[0].sendrawtransaction(signed2["hex"])
# Create a child tx spending ABC2
signed3_change = Decimal("24.999")
inputs = [{"txid": txABC2, "vout": 0}]
- outputs = {self.nodes[0].getnewaddress(): signed3_change}
- signed3 = self.nodes[0].signrawtransactionwithwallet(self.nodes[0].createrawtransaction(inputs, outputs))
+ outputs = {alice.getnewaddress(): signed3_change}
+ signed3 = alice.signrawtransactionwithwallet(alice.createrawtransaction(inputs, outputs))
# note tx is never directly referenced, only abandoned as a child of the above
self.nodes[0].sendrawtransaction(signed3["hex"])
# In mempool txs from self should increase balance from change
- newbalance = self.nodes[0].getbalance()
+ newbalance = alice.getbalance()
assert_equal(newbalance, balance - Decimal("30") + signed3_change)
balance = newbalance
# Restart the node with a higher min relay fee so the parent tx is no longer in mempool
# TODO: redo with eviction
self.restart_node(0, extra_args=["-minrelaytxfee=0.0001"])
+ alice = self.nodes[0].get_wallet_rpc(self.default_wallet_name)
assert self.nodes[0].getmempoolinfo()['loaded']
# Verify txs no longer in either node's mempool
@@ -101,25 +107,25 @@ class AbandonConflictTest(BitcoinTestFramework):
# Not in mempool txs from self should only reduce balance
# inputs are still spent, but change not received
- newbalance = self.nodes[0].getbalance()
+ newbalance = alice.getbalance()
assert_equal(newbalance, balance - signed3_change)
# Unconfirmed received funds that are not in mempool, also shouldn't show
# up in unconfirmed balance
- balances = self.nodes[0].getbalances()['mine']
+ balances = alice.getbalances()['mine']
assert_equal(balances['untrusted_pending'] + balances['trusted'], newbalance)
# Also shouldn't show up in listunspent
- assert not txABC2 in [utxo["txid"] for utxo in self.nodes[0].listunspent(0)]
+ assert not txABC2 in [utxo["txid"] for utxo in alice.listunspent(0)]
balance = newbalance
# Abandon original transaction and verify inputs are available again
# including that the child tx was also abandoned
- self.nodes[0].abandontransaction(txAB1)
- newbalance = self.nodes[0].getbalance()
+ alice.abandontransaction(txAB1)
+ newbalance = alice.getbalance()
assert_equal(newbalance, balance + Decimal("30"))
balance = newbalance
self.log.info("Check abandoned transactions in listsinceblock")
- listsinceblock = self.nodes[0].listsinceblock()
+ listsinceblock = alice.listsinceblock()
txAB1_listsinceblock = [d for d in listsinceblock['transactions'] if d['txid'] == txAB1 and d['category'] == 'send']
for tx in txAB1_listsinceblock:
assert_equal(tx['abandoned'], True)
@@ -128,49 +134,53 @@ class AbandonConflictTest(BitcoinTestFramework):
# Verify that even with a low min relay fee, the tx is not reaccepted from wallet on startup once abandoned
self.restart_node(0, extra_args=["-minrelaytxfee=0.00001"])
+ alice = self.nodes[0].get_wallet_rpc(self.default_wallet_name)
assert self.nodes[0].getmempoolinfo()['loaded']
assert_equal(len(self.nodes[0].getrawmempool()), 0)
- assert_equal(self.nodes[0].getbalance(), balance)
+ assert_equal(alice.getbalance(), balance)
# But if it is received again then it is unabandoned
# And since now in mempool, the change is available
# But its child tx remains abandoned
self.nodes[0].sendrawtransaction(signed["hex"])
- newbalance = self.nodes[0].getbalance()
+ newbalance = alice.getbalance()
assert_equal(newbalance, balance - Decimal("20") + Decimal("14.99998"))
balance = newbalance
# Send child tx again so it is unabandoned
self.nodes[0].sendrawtransaction(signed2["hex"])
- newbalance = self.nodes[0].getbalance()
+ newbalance = alice.getbalance()
assert_equal(newbalance, balance - Decimal("10") - Decimal("14.99998") + Decimal("24.9996"))
balance = newbalance
# Remove using high relay fee again
self.restart_node(0, extra_args=["-minrelaytxfee=0.0001"])
+ alice = self.nodes[0].get_wallet_rpc(self.default_wallet_name)
assert self.nodes[0].getmempoolinfo()['loaded']
assert_equal(len(self.nodes[0].getrawmempool()), 0)
- newbalance = self.nodes[0].getbalance()
+ newbalance = alice.getbalance()
assert_equal(newbalance, balance - Decimal("24.9996"))
balance = newbalance
self.log.info("Test transactions conflicted by a double spend")
+ self.nodes[0].loadwallet("bob")
+ bob = self.nodes[0].get_wallet_rpc("bob")
+
# Create a double spend of AB1 by spending again from only A's 10 output
# Mine double spend from node 1
inputs = []
inputs.append({"txid": txA, "vout": nA})
outputs = {}
- outputs[self.nodes[1].getnewaddress()] = Decimal("9.9999")
- tx = self.nodes[0].createrawtransaction(inputs, outputs)
- signed = self.nodes[0].signrawtransactionwithwallet(tx)
- self.nodes[1].sendrawtransaction(signed["hex"])
- self.generate(self.nodes[1], 1, sync_fun=self.no_op)
-
+ outputs[self.nodes[1].getnewaddress()] = Decimal("3.9999")
+ outputs[bob.getnewaddress()] = Decimal("5.9999")
+ tx = alice.createrawtransaction(inputs, outputs)
+ signed = alice.signrawtransactionwithwallet(tx)
+ double_spend_txid = self.nodes[1].sendrawtransaction(signed["hex"])
self.connect_nodes(0, 1)
- self.sync_blocks()
+ self.generate(self.nodes[1], 1)
- tx_list = self.nodes[0].listtransactions()
+ tx_list = alice.listtransactions()
conflicted = [tx for tx in tx_list if tx["confirmations"] < 0]
assert_equal(4, len(conflicted))
@@ -179,7 +189,7 @@ class AbandonConflictTest(BitcoinTestFramework):
assert_equal(2, len(wallet_conflicts))
double_spends = [tx for tx in tx_list if tx["walletconflicts"] and tx["confirmations"] > 0]
- assert_equal(1, len(double_spends))
+ assert_equal(2, len(double_spends)) # one for each output
double_spend = double_spends[0]
# Test the properties of the conflicted transactions, i.e. with confirmations < 0.
@@ -198,8 +208,19 @@ class AbandonConflictTest(BitcoinTestFramework):
assert_equal(double_spend["walletconflicts"], [tx["txid"]])
assert_equal(tx["walletconflicts"], [double_spend["txid"]])
+ # Test walletconflicts on the receiver's side
+ txinfo = bob.gettransaction(txAB1)
+ assert_equal(txinfo['confirmations'], -1)
+ assert_equal(txinfo['walletconflicts'], [double_spend['txid']])
+
+ double_spends = [tx for tx in bob.listtransactions() if tx["walletconflicts"] and tx["confirmations"] > 0]
+ assert_equal(1, len(double_spends))
+ double_spend = double_spends[0]
+ assert_equal(double_spend_txid, double_spend['txid'])
+ assert_equal(double_spend["walletconflicts"], [txAB1])
+
# Verify that B and C's 10 BTC outputs are available for spending again because AB1 is now conflicted
- newbalance = self.nodes[0].getbalance()
+ newbalance = alice.getbalance()
assert_equal(newbalance, balance + Decimal("20"))
balance = newbalance
@@ -207,7 +228,7 @@ class AbandonConflictTest(BitcoinTestFramework):
# Invalidate the block with the double spend and B's 10 BTC output should no longer be available
# Don't think C's should either
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
- newbalance = self.nodes[0].getbalance()
+ newbalance = alice.getbalance()
#assert_equal(newbalance, balance - Decimal("10"))
self.log.info("If balance has not declined after invalidateblock then out of mempool wallet tx which is no longer")
self.log.info("conflicted has not resumed causing its inputs to be seen as spent. See Issue #7315")
diff --git a/test/sanitizer_suppressions/ubsan b/test/sanitizer_suppressions/ubsan
index 4ebcaf1a15..ec13acb689 100644
--- a/test/sanitizer_suppressions/ubsan
+++ b/test/sanitizer_suppressions/ubsan
@@ -70,9 +70,7 @@ implicit-integer-sign-change:prevector.h
implicit-integer-sign-change:script/bitcoinconsensus.cpp
implicit-integer-sign-change:script/interpreter.cpp
implicit-integer-sign-change:serialize.h
-implicit-integer-sign-change:test/streams_tests.cpp
implicit-integer-sign-change:txmempool.cpp
-implicit-integer-sign-change:zmq/zmqpublishnotifier.cpp
implicit-signed-integer-truncation:addrman.cpp
implicit-signed-integer-truncation:addrman.h
implicit-signed-integer-truncation:crypto/