aboutsummaryrefslogtreecommitdiff
path: root/tests/functional_tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests/functional_tests')
-rw-r--r--tests/functional_tests/CMakeLists.txt2
-rwxr-xr-xtests/functional_tests/address_book.py2
-rwxr-xr-xtests/functional_tests/bans.py2
-rwxr-xr-xtests/functional_tests/blockchain.py2
-rwxr-xr-xtests/functional_tests/cold_signing.py2
-rw-r--r--tests/functional_tests/cpu_power_test.cpp2
-rwxr-xr-xtests/functional_tests/daemon_info.py2
-rwxr-xr-xtests/functional_tests/get_output_distribution.py2
-rw-r--r--tests/functional_tests/http_digest_auth.py2
-rwxr-xr-xtests/functional_tests/integrated_address.py2
-rwxr-xr-xtests/functional_tests/k_anonymity.py2
-rw-r--r--tests/functional_tests/main.cpp2
-rw-r--r--tests/functional_tests/make_test_signature.cc2
-rwxr-xr-xtests/functional_tests/mining.py2
-rwxr-xr-xtests/functional_tests/multisig.py2
-rwxr-xr-xtests/functional_tests/p2p.py2
-rwxr-xr-xtests/functional_tests/proofs.py2
-rwxr-xr-xtests/functional_tests/rpc_payment.py2
-rwxr-xr-xtests/functional_tests/sign_message.py2
-rwxr-xr-xtests/functional_tests/speed.py2
-rw-r--r--tests/functional_tests/transactions_flow_test.cpp2
-rw-r--r--tests/functional_tests/transactions_flow_test.h2
-rw-r--r--tests/functional_tests/transactions_generation_from_blockchain.cpp2
-rw-r--r--tests/functional_tests/transactions_generation_from_blockchain.h2
-rwxr-xr-xtests/functional_tests/transfer.py402
-rwxr-xr-xtests/functional_tests/txpool.py2
-rwxr-xr-xtests/functional_tests/uri.py2
-rwxr-xr-xtests/functional_tests/util_resources.py27
-rwxr-xr-xtests/functional_tests/validate_address.py2
-rwxr-xr-xtests/functional_tests/wallet.py45
30 files changed, 463 insertions, 65 deletions
diff --git a/tests/functional_tests/CMakeLists.txt b/tests/functional_tests/CMakeLists.txt
index 306eba073..62185a0db 100644
--- a/tests/functional_tests/CMakeLists.txt
+++ b/tests/functional_tests/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
#
# All rights reserved.
#
diff --git a/tests/functional_tests/address_book.py b/tests/functional_tests/address_book.py
index d614fb24c..84365fe8c 100755
--- a/tests/functional_tests/address_book.py
+++ b/tests/functional_tests/address_book.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
#encoding=utf-8
-# Copyright (c) 2019-2023, The Monero Project
+# Copyright (c) 2019-2024, The Monero Project
#
# All rights reserved.
#
diff --git a/tests/functional_tests/bans.py b/tests/functional_tests/bans.py
index 71299b681..fa0a19664 100755
--- a/tests/functional_tests/bans.py
+++ b/tests/functional_tests/bans.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
-# Copyright (c) 2019-2023, The Monero Project
+# Copyright (c) 2019-2024, The Monero Project
#
# All rights reserved.
#
diff --git a/tests/functional_tests/blockchain.py b/tests/functional_tests/blockchain.py
index 65378da4b..7cf3a6420 100755
--- a/tests/functional_tests/blockchain.py
+++ b/tests/functional_tests/blockchain.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
-# Copyright (c) 2018-2023, The Monero Project
+# Copyright (c) 2018-2024, The Monero Project
#
# All rights reserved.
diff --git a/tests/functional_tests/cold_signing.py b/tests/functional_tests/cold_signing.py
index be5710f1d..38e1baa26 100755
--- a/tests/functional_tests/cold_signing.py
+++ b/tests/functional_tests/cold_signing.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
-# Copyright (c) 2019-2023, The Monero Project
+# Copyright (c) 2019-2024, The Monero Project
#
# All rights reserved.
#
diff --git a/tests/functional_tests/cpu_power_test.cpp b/tests/functional_tests/cpu_power_test.cpp
index 0ae48c527..623be90c1 100644
--- a/tests/functional_tests/cpu_power_test.cpp
+++ b/tests/functional_tests/cpu_power_test.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
//
// All rights reserved.
//
diff --git a/tests/functional_tests/daemon_info.py b/tests/functional_tests/daemon_info.py
index 901375bc8..9d645330d 100755
--- a/tests/functional_tests/daemon_info.py
+++ b/tests/functional_tests/daemon_info.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
-# Copyright (c) 2018-2023, The Monero Project
+# Copyright (c) 2018-2024, The Monero Project
#
# All rights reserved.
diff --git a/tests/functional_tests/get_output_distribution.py b/tests/functional_tests/get_output_distribution.py
index 08019121a..54cb63035 100755
--- a/tests/functional_tests/get_output_distribution.py
+++ b/tests/functional_tests/get_output_distribution.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
-# Copyright (c) 2019-2023, The Monero Project
+# Copyright (c) 2019-2024, The Monero Project
#
# All rights reserved.
#
diff --git a/tests/functional_tests/http_digest_auth.py b/tests/functional_tests/http_digest_auth.py
index 7c22f9f30..1311ae62d 100644
--- a/tests/functional_tests/http_digest_auth.py
+++ b/tests/functional_tests/http_digest_auth.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
-# Copyright (c) 2024, The Monero Project
+# Copyright (c) 2024-2024, The Monero Project
#
# All rights reserved.
#
diff --git a/tests/functional_tests/integrated_address.py b/tests/functional_tests/integrated_address.py
index d2e81622f..912bee26a 100755
--- a/tests/functional_tests/integrated_address.py
+++ b/tests/functional_tests/integrated_address.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
-# Copyright (c) 2019-2023, The Monero Project
+# Copyright (c) 2019-2024, The Monero Project
#
# All rights reserved.
#
diff --git a/tests/functional_tests/k_anonymity.py b/tests/functional_tests/k_anonymity.py
index ffa670b4c..1182d29c8 100755
--- a/tests/functional_tests/k_anonymity.py
+++ b/tests/functional_tests/k_anonymity.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
-# Copyright (c) 2023, The Monero Project
+# Copyright (c) 2023-2024, The Monero Project
#
# All rights reserved.
#
diff --git a/tests/functional_tests/main.cpp b/tests/functional_tests/main.cpp
index f19fa2c44..5ec40203c 100644
--- a/tests/functional_tests/main.cpp
+++ b/tests/functional_tests/main.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
//
// All rights reserved.
//
diff --git a/tests/functional_tests/make_test_signature.cc b/tests/functional_tests/make_test_signature.cc
index cba989070..d92986da2 100644
--- a/tests/functional_tests/make_test_signature.cc
+++ b/tests/functional_tests/make_test_signature.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2023, The Monero Project
+// Copyright (c) 2019-2024, The Monero Project
//
// All rights reserved.
//
diff --git a/tests/functional_tests/mining.py b/tests/functional_tests/mining.py
index 242c58dbe..aa7e291b5 100755
--- a/tests/functional_tests/mining.py
+++ b/tests/functional_tests/mining.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
-# Copyright (c) 2018-2023, The Monero Project
+# Copyright (c) 2018-2024, The Monero Project
#
# All rights reserved.
diff --git a/tests/functional_tests/multisig.py b/tests/functional_tests/multisig.py
index 73cc8d643..c29cd1322 100755
--- a/tests/functional_tests/multisig.py
+++ b/tests/functional_tests/multisig.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
-# Copyright (c) 2019-2023, The Monero Project
+# Copyright (c) 2019-2024, The Monero Project
#
# All rights reserved.
#
diff --git a/tests/functional_tests/p2p.py b/tests/functional_tests/p2p.py
index 2c582cc8a..8d92318ce 100755
--- a/tests/functional_tests/p2p.py
+++ b/tests/functional_tests/p2p.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
-# Copyright (c) 2018-2023, The Monero Project
+# Copyright (c) 2018-2024, The Monero Project
#
# All rights reserved.
diff --git a/tests/functional_tests/proofs.py b/tests/functional_tests/proofs.py
index 23cb858be..83869a063 100755
--- a/tests/functional_tests/proofs.py
+++ b/tests/functional_tests/proofs.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
-# Copyright (c) 2019-2023, The Monero Project
+# Copyright (c) 2019-2024, The Monero Project
#
# All rights reserved.
#
diff --git a/tests/functional_tests/rpc_payment.py b/tests/functional_tests/rpc_payment.py
index bfd20bc1a..5b65b5af4 100755
--- a/tests/functional_tests/rpc_payment.py
+++ b/tests/functional_tests/rpc_payment.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
-# Copyright (c) 2019-2023, The Monero Project
+# Copyright (c) 2019-2024, The Monero Project
#
# All rights reserved.
#
diff --git a/tests/functional_tests/sign_message.py b/tests/functional_tests/sign_message.py
index 1bfb6f666..9d6692cf8 100755
--- a/tests/functional_tests/sign_message.py
+++ b/tests/functional_tests/sign_message.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
-# Copyright (c) 2019-2023, The Monero Project
+# Copyright (c) 2019-2024, The Monero Project
#
# All rights reserved.
#
diff --git a/tests/functional_tests/speed.py b/tests/functional_tests/speed.py
index cc971d83d..9f3514a0d 100755
--- a/tests/functional_tests/speed.py
+++ b/tests/functional_tests/speed.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
-# Copyright (c) 2018-2023, The Monero Project
+# Copyright (c) 2018-2024, The Monero Project
#
# All rights reserved.
diff --git a/tests/functional_tests/transactions_flow_test.cpp b/tests/functional_tests/transactions_flow_test.cpp
index a15348bca..fd1a64673 100644
--- a/tests/functional_tests/transactions_flow_test.cpp
+++ b/tests/functional_tests/transactions_flow_test.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
//
// All rights reserved.
//
diff --git a/tests/functional_tests/transactions_flow_test.h b/tests/functional_tests/transactions_flow_test.h
index 568ce4d86..06a401e9b 100644
--- a/tests/functional_tests/transactions_flow_test.h
+++ b/tests/functional_tests/transactions_flow_test.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
//
// All rights reserved.
//
diff --git a/tests/functional_tests/transactions_generation_from_blockchain.cpp b/tests/functional_tests/transactions_generation_from_blockchain.cpp
index 9e33330a6..3abb13909 100644
--- a/tests/functional_tests/transactions_generation_from_blockchain.cpp
+++ b/tests/functional_tests/transactions_generation_from_blockchain.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
//
// All rights reserved.
//
diff --git a/tests/functional_tests/transactions_generation_from_blockchain.h b/tests/functional_tests/transactions_generation_from_blockchain.h
index 4758bd066..12e78579d 100644
--- a/tests/functional_tests/transactions_generation_from_blockchain.h
+++ b/tests/functional_tests/transactions_generation_from_blockchain.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
//
// All rights reserved.
//
diff --git a/tests/functional_tests/transfer.py b/tests/functional_tests/transfer.py
index 56a2514d9..2987ded89 100755
--- a/tests/functional_tests/transfer.py
+++ b/tests/functional_tests/transfer.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
-# Copyright (c) 2019-2023, The Monero Project
+# Copyright (c) 2019-2024, The Monero Project
#
# All rights reserved.
#
@@ -30,6 +30,7 @@
from __future__ import print_function
import json
+import util_resources
import pprint
from deepdiff import DeepDiff
pp = pprint.PrettyPrinter(indent=2)
@@ -46,6 +47,17 @@ seeds = [
'dilute gutter certain antics pamphlet macro enjoy left slid guarded bogeys upload nineteen bomb jubilee enhanced irritate turnip eggs swung jukebox loudly reduce sedan slid',
]
+def diff_transfers(actual_transfers, expected_transfers, ignore_order = True):
+ # The payments containers aren't ordered; re-scanning can lead to diff orders
+ diff = DeepDiff(actual_transfers, expected_transfers, ignore_order = ignore_order)
+ if diff != {}:
+ pp.pprint(diff)
+ assert diff == {}
+
+def diff_incoming_transfers(actual_transfers, expected_transfers):
+ # wallet2 m_transfers container is ordered and order should be the same across rescans
+ diff_transfers(actual_transfers, expected_transfers, ignore_order = False)
+
class TransferTest():
def run_test(self):
self.reset()
@@ -64,6 +76,8 @@ class TransferTest():
self.check_multiple_submissions()
self.check_scan_tx()
self.check_subtract_fee_from_outputs()
+ self.check_background_sync()
+ self.check_background_sync_reorg_recovery()
def reset(self):
print('Resetting blockchain')
@@ -875,12 +889,6 @@ class TransferTest():
print('Testing scan_tx')
- def diff_transfers(actual_transfers, expected_transfers):
- diff = DeepDiff(actual_transfers, expected_transfers)
- if diff != {}:
- pp.pprint(diff)
- assert diff == {}
-
# set up sender_wallet
sender_wallet = self.wallet[0]
try: sender_wallet.close_wallet()
@@ -1162,5 +1170,385 @@ class TransferTest():
except AssertionError:
pass
+ def check_background_sync(self):
+ daemon = Daemon()
+
+ print('Testing background sync')
+
+ # Some helper functions
+ def stop_with_wrong_inputs(wallet, wallet_password, seed = ''):
+ invalid = False
+ try: wallet.stop_background_sync(wallet_password = wallet_password, seed = seed)
+ except: invalid = True
+ assert invalid
+
+ def open_with_wrong_password(wallet, filename, password):
+ invalid_password = False
+ try: wallet.open_wallet(filename, password = password)
+ except: invalid_password = True
+ assert invalid_password
+
+ def restore_wallet(wallet, seed, filename = '', password = ''):
+ wallet.close_wallet()
+ if filename != '':
+ util_resources.remove_wallet_files(filename)
+ wallet.restore_deterministic_wallet(seed = seed, filename = filename, password = password)
+ wallet.auto_refresh(enable = False)
+ assert wallet.get_transfers() == {}
+
+ def assert_correct_transfers(wallet, expected_transfers, expected_inc_transfers, expected_balance):
+ diff_transfers(wallet.get_transfers(), expected_transfers)
+ diff_incoming_transfers(wallet.incoming_transfers(transfer_type = 'all'), expected_inc_transfers)
+ assert wallet.get_balance().balance == expected_balance
+
+ # Set up sender_wallet. Prepare to sweep single output to receiver.
+ # We're testing a sweep because it makes sure background sync can
+ # properly pick up txs which do not have a change output back to sender.
+ sender_wallet = self.wallet[0]
+ try: sender_wallet.close_wallet()
+ except: pass
+ sender_wallet.restore_deterministic_wallet(seed = seeds[0])
+ sender_wallet.auto_refresh(enable = False)
+ sender_wallet.refresh()
+ res = sender_wallet.incoming_transfers(transfer_type = 'available')
+ unlocked = [x for x in res.transfers if x.unlocked and x.amount > 0]
+ assert len(unlocked) > 0
+ ki = unlocked[0].key_image
+ amount = unlocked[0].amount
+ spent_txid = unlocked[0].tx_hash
+ sender_wallet.refresh()
+ res = sender_wallet.get_transfers()
+ out_len = 0 if 'out' not in res else len(res.out)
+ sender_starting_balance = sender_wallet.get_balance().balance
+
+ # Background sync type options
+ reuse_password = sender_wallet.background_sync_options.reuse_password
+ custom_password = sender_wallet.background_sync_options.custom_password
+
+ # set up receiver_wallet
+ receiver_wallet = self.wallet[1]
+ try: receiver_wallet.close_wallet()
+ except: pass
+ receiver_wallet.restore_deterministic_wallet(seed = seeds[1])
+ receiver_wallet.auto_refresh(enable = False)
+ receiver_wallet.refresh()
+ res = receiver_wallet.get_transfers()
+ in_len = 0 if 'in' not in res else len(res['in'])
+ receiver_starting_balance = receiver_wallet.get_balance().balance
+
+ # transfer from sender_wallet to receiver_wallet
+ dst = '44Kbx4sJ7JDRDV5aAhLJzQCjDz2ViLRduE3ijDZu3osWKBjMGkV1XPk4pfDUMqt1Aiezvephdqm6YD19GKFD9ZcXVUTp6BW'
+ res = sender_wallet.sweep_single(dst, key_image = ki)
+ assert len(res.tx_hash) == 32*2
+ txid = res.tx_hash
+ assert res.fee > 0
+ fee = res.fee
+ assert res.amount == amount - fee
+
+ expected_sender_balance = sender_starting_balance - amount
+ expected_receiver_balance = receiver_starting_balance + (amount - fee)
+
+ print('Checking background sync on outgoing wallet')
+ sender_wallet.setup_background_sync(background_sync_type = reuse_password)
+ sender_wallet.start_background_sync()
+ # Mine block to an uninvolved wallet
+ daemon.generateblocks('46r4nYSevkfBUMhuykdK3gQ98XDqDTYW1hNLaXNvjpsJaSbNtdXh1sKMsdVgqkaihChAzEy29zEDPMR3NHQvGoZCLGwTerK', 1)
+ # sender should still be able to scan the transfer normally because we
+ # spent an output that had a known key image
+ sender_wallet.refresh()
+ transfers = sender_wallet.get_transfers()
+ assert 'pending' not in transfers or len(transfers.pending) == 0
+ assert 'pool' not in transfers or len (transfers.pool) == 0
+ assert len(transfers.out) == out_len + 1
+ tx = [x for x in transfers.out if x.txid == txid]
+ assert len(tx) == 1
+ tx = tx[0]
+ assert tx.amount == amount - fee
+ assert tx.fee == fee
+ assert len(tx.destinations) == 1
+ assert tx.destinations[0].amount == amount - fee
+ assert tx.destinations[0].address == dst
+ incoming_transfers = sender_wallet.incoming_transfers(transfer_type = 'all')
+ assert len([x for x in incoming_transfers.transfers if x.tx_hash == spent_txid and x.key_image == ki and x.spent]) == 1
+ assert sender_wallet.get_balance().balance == expected_sender_balance
+
+ # Restore and check background syncing outgoing wallet
+ restore_wallet(sender_wallet, seeds[0])
+ sender_wallet.setup_background_sync(background_sync_type = reuse_password)
+ sender_wallet.start_background_sync()
+ sender_wallet.refresh()
+ for i, out_tx in enumerate(transfers.out):
+ if 'destinations' in out_tx:
+ del transfers.out[i]['destinations'] # destinations are not expected after wallet restore
+ # sender's balance should be higher because can't detect spends while
+ # background sync enabled, only receives
+ background_bal = sender_wallet.get_balance().balance
+ assert background_bal > expected_sender_balance
+ background_transfers = sender_wallet.get_transfers()
+ assert 'out' not in background_transfers or len(background_transfers.out) == 0
+ assert 'in' in background_transfers and len(background_transfers['in']) > 0
+ background_incoming_transfers = sender_wallet.incoming_transfers(transfer_type = 'all')
+ assert len(background_incoming_transfers) == len(incoming_transfers)
+ assert len([x for x in background_incoming_transfers.transfers if x.spent or x.key_image != '']) == 0
+ assert len([x for x in background_incoming_transfers.transfers if x.tx_hash == spent_txid]) == 1
+
+ # Try to stop background sync with the wrong seed
+ stop_with_wrong_inputs(sender_wallet, wallet_password = '', seed = seeds[1])
+
+ # Stop background sync and check transfers update correctly
+ sender_wallet.stop_background_sync(wallet_password = '', seed = seeds[0])
+ assert_correct_transfers(sender_wallet, transfers, incoming_transfers, expected_sender_balance)
+
+ # Check stopping a wallet with wallet files saved to disk
+ for background_sync_type in [reuse_password, custom_password]:
+ restore_wallet(sender_wallet, seeds[0], 'test1', 'test_password')
+ background_cache_password = None if background_sync_type == reuse_password else 'background_password'
+ sender_wallet.setup_background_sync(background_sync_type = background_sync_type, wallet_password = 'test_password', background_cache_password = background_cache_password)
+ sender_wallet.start_background_sync()
+ sender_wallet.refresh()
+ assert_correct_transfers(sender_wallet, background_transfers, background_incoming_transfers, background_bal)
+ stop_with_wrong_inputs(sender_wallet, 'wrong_password')
+ sender_wallet.stop_background_sync(wallet_password = 'test_password')
+ assert_correct_transfers(sender_wallet, transfers, incoming_transfers, expected_sender_balance)
+
+ # Close wallet while background syncing, then reopen
+ for background_sync_type in [reuse_password, custom_password]:
+ restore_wallet(sender_wallet, seeds[0], 'test1', 'test_password')
+ background_cache_password = None if background_sync_type == reuse_password else 'background_password'
+ sender_wallet.setup_background_sync(background_sync_type = background_sync_type, wallet_password = 'test_password', background_cache_password = background_cache_password)
+ sender_wallet.start_background_sync()
+ sender_wallet.refresh()
+ assert_correct_transfers(sender_wallet, background_transfers, background_incoming_transfers, background_bal)
+ sender_wallet.close_wallet()
+ open_with_wrong_password(sender_wallet, 'test1', 'wrong_password')
+ sender_wallet.open_wallet('test1', password = 'test_password')
+ # It should reopen with spend key loaded and correctly scan all transfers
+ assert_correct_transfers(sender_wallet, transfers, incoming_transfers, expected_sender_balance)
+
+ # Close wallet while syncing normally, then reopen
+ for background_sync_type in [reuse_password, custom_password]:
+ restore_wallet(sender_wallet, seeds[0], 'test1', 'test_password')
+ background_cache_password = None if background_sync_type == reuse_password else 'background_password'
+ sender_wallet.setup_background_sync(background_sync_type = background_sync_type, wallet_password = 'test_password', background_cache_password = background_cache_password)
+ sender_wallet.refresh()
+ assert_correct_transfers(sender_wallet, transfers, incoming_transfers, expected_sender_balance)
+ sender_wallet.close_wallet()
+ open_with_wrong_password(sender_wallet, 'test1', 'wrong_password')
+ sender_wallet.open_wallet('test1', password = 'test_password')
+ assert_correct_transfers(sender_wallet, transfers, incoming_transfers, expected_sender_balance)
+
+ # Create background cache using custom password, then use it to sync, then reopen main wallet
+ for background_cache_password in ['background_password', '']:
+ restore_wallet(sender_wallet, seeds[0], 'test1', 'test_password')
+ assert not util_resources.file_exists('test1.background')
+ assert not util_resources.file_exists('test1.background.keys')
+ sender_wallet.setup_background_sync(background_sync_type = custom_password, wallet_password = 'test_password', background_cache_password = background_cache_password)
+ assert util_resources.file_exists('test1.background')
+ assert util_resources.file_exists('test1.background.keys')
+ sender_wallet.close_wallet()
+ open_with_wrong_password(sender_wallet, 'test1.background', 'test_password')
+ sender_wallet.open_wallet('test1.background', password = background_cache_password)
+ sender_wallet.refresh()
+ assert_correct_transfers(sender_wallet, background_transfers, background_incoming_transfers, background_bal)
+ sender_wallet.close_wallet()
+ sender_wallet.open_wallet('test1', password = 'test_password')
+ assert_correct_transfers(sender_wallet, transfers, incoming_transfers, expected_sender_balance)
+
+ # Check that main wallet keeps background cache encrypted with custom password in sync
+ restore_wallet(sender_wallet, seeds[0], 'test1', 'test_password')
+ sender_wallet.setup_background_sync(background_sync_type = background_sync_type, wallet_password = 'test_password', background_cache_password = 'background_password')
+ sender_wallet.refresh()
+ assert_correct_transfers(sender_wallet, transfers, incoming_transfers, expected_sender_balance)
+ sender_wallet.close_wallet()
+ sender_wallet.open_wallet('test1.background', password = 'background_password')
+ assert_correct_transfers(sender_wallet, transfers, incoming_transfers, expected_sender_balance)
+
+ # Try using wallet password as custom background password
+ restore_wallet(sender_wallet, seeds[0], 'test1', 'test_password')
+ assert not util_resources.file_exists('test1.background')
+ assert not util_resources.file_exists('test1.background.keys')
+ same_password = False
+ try: sender_wallet.setup_background_sync(background_sync_type = custom_password, wallet_password = 'test_password', background_cache_password = 'test_password')
+ except: same_password = True
+ assert same_password
+ assert not util_resources.file_exists('test1.background')
+ assert not util_resources.file_exists('test1.background.keys')
+
+ # Turn off background sync
+ for background_sync_type in [reuse_password, custom_password]:
+ restore_wallet(sender_wallet, seeds[0], 'test1', 'test_password')
+ background_cache_password = None if background_sync_type == reuse_password else 'background_password'
+ sender_wallet.setup_background_sync(background_sync_type = background_sync_type, wallet_password = 'test_password', background_cache_password = background_cache_password)
+ if background_sync_type == custom_password:
+ assert util_resources.file_exists('test1.background')
+ assert util_resources.file_exists('test1.background.keys')
+ sender_wallet.close_wallet()
+ assert util_resources.file_exists('test1.background')
+ assert util_resources.file_exists('test1.background.keys')
+ else:
+ assert not util_resources.file_exists('test1.background')
+ assert not util_resources.file_exists('test1.background.keys')
+ sender_wallet.close_wallet()
+ assert not util_resources.file_exists('test1.background')
+ assert not util_resources.file_exists('test1.background.keys')
+ sender_wallet.open_wallet('test1', password = 'test_password')
+ sender_wallet.setup_background_sync(background_sync_type = sender_wallet.background_sync_options.off, wallet_password = 'test_password')
+ assert not util_resources.file_exists('test1.background')
+ assert not util_resources.file_exists('test1.background.keys')
+ sender_wallet.close_wallet()
+ assert not util_resources.file_exists('test1.background')
+ assert not util_resources.file_exists('test1.background.keys')
+ sender_wallet.open_wallet('test1', password = 'test_password')
+
+ # Sanity check against outgoing wallet restored at height 0
+ sender_wallet.close_wallet()
+ sender_wallet.restore_deterministic_wallet(seed = seeds[0], restore_height = 0)
+ sender_wallet.refresh()
+ assert_correct_transfers(sender_wallet, transfers, incoming_transfers, expected_sender_balance)
+
+ print('Checking background sync on incoming wallet')
+ receiver_wallet.setup_background_sync(background_sync_type = reuse_password)
+ receiver_wallet.start_background_sync()
+ receiver_wallet.refresh()
+ transfers = receiver_wallet.get_transfers()
+ assert 'pending' not in transfers or len(transfers.pending) == 0
+ assert 'pool' not in transfers or len (transfers.pool) == 0
+ assert len(transfers['in']) == in_len + 1
+ tx = [x for x in transfers['in'] if x.txid == txid]
+ assert len(tx) == 1
+ tx = tx[0]
+ assert tx.amount == amount - fee
+ assert tx.fee == fee
+ incoming_transfers = receiver_wallet.incoming_transfers(transfer_type = 'all')
+ assert len([x for x in incoming_transfers.transfers if x.tx_hash == txid and x.key_image == '' and not x.spent]) == 1
+ assert receiver_wallet.get_balance().balance == expected_receiver_balance
+
+ # Restore and check background syncing incoming wallet
+ restore_wallet(receiver_wallet, seeds[1])
+ receiver_wallet.setup_background_sync(background_sync_type = reuse_password)
+ receiver_wallet.start_background_sync()
+ receiver_wallet.refresh()
+ if 'out' in transfers:
+ for i, out_tx in enumerate(transfers.out):
+ if 'destinations' in out_tx:
+ del transfers.out[i]['destinations'] # destinations are not expected after wallet restore
+ background_bal = receiver_wallet.get_balance().balance
+ assert background_bal >= expected_receiver_balance
+ background_transfers = receiver_wallet.get_transfers()
+ assert 'out' not in background_transfers or len(background_transfers.out) == 0
+ assert 'in' in background_transfers and len(background_transfers['in']) > 0
+ background_incoming_transfers = receiver_wallet.incoming_transfers(transfer_type = 'all')
+ assert len(background_incoming_transfers) == len(incoming_transfers)
+ assert len([x for x in background_incoming_transfers.transfers if x.spent or x.key_image != '']) == 0
+ assert len([x for x in background_incoming_transfers.transfers if x.tx_hash == txid]) == 1
+
+ # Stop background sync and check transfers update correctly
+ receiver_wallet.stop_background_sync(wallet_password = '', seed = seeds[1])
+ diff_transfers(receiver_wallet.get_transfers(), transfers)
+ incoming_transfers = receiver_wallet.incoming_transfers(transfer_type = 'all')
+ assert len(background_incoming_transfers) == len(incoming_transfers)
+ assert len([x for x in incoming_transfers.transfers if x.tx_hash == txid and x.key_image != '' and not x.spent]) == 1
+ assert receiver_wallet.get_balance().balance == expected_receiver_balance
+
+ # Check a fresh incoming wallet with wallet files saved to disk and encrypted with password
+ restore_wallet(receiver_wallet, seeds[1], 'test2', 'test_password')
+ receiver_wallet.setup_background_sync(background_sync_type = reuse_password, wallet_password = 'test_password')
+ receiver_wallet.start_background_sync()
+ receiver_wallet.refresh()
+ assert_correct_transfers(receiver_wallet, background_transfers, background_incoming_transfers, background_bal)
+ stop_with_wrong_inputs(receiver_wallet, 'wrong_password')
+ receiver_wallet.stop_background_sync(wallet_password = 'test_password')
+ assert_correct_transfers(receiver_wallet, transfers, incoming_transfers, expected_receiver_balance)
+
+ # Close receiver's wallet while background sync is enabled then reopen
+ restore_wallet(receiver_wallet, seeds[1], 'test2', 'test_password')
+ receiver_wallet.setup_background_sync(background_sync_type = reuse_password, wallet_password = 'test_password')
+ receiver_wallet.start_background_sync()
+ receiver_wallet.refresh()
+ diff_transfers(receiver_wallet.get_transfers(), background_transfers)
+ diff_incoming_transfers(receiver_wallet.incoming_transfers(transfer_type = 'all'), background_incoming_transfers)
+ assert receiver_wallet.get_balance().balance == background_bal
+ receiver_wallet.close_wallet()
+ receiver_wallet.open_wallet('test2', password = 'test_password')
+ # It should reopen with spend key loaded and correctly scan all transfers
+ assert_correct_transfers(receiver_wallet, transfers, incoming_transfers, expected_receiver_balance)
+
+ # Sanity check against incoming wallet restored at height 0
+ receiver_wallet.close_wallet()
+ receiver_wallet.restore_deterministic_wallet(seed = seeds[1], restore_height = 0)
+ receiver_wallet.refresh()
+ assert_correct_transfers(receiver_wallet, transfers, incoming_transfers, expected_receiver_balance)
+
+ # Clean up
+ util_resources.remove_wallet_files('test1')
+ util_resources.remove_wallet_files('test2')
+ for i in range(2):
+ self.wallet[i].close_wallet()
+ self.wallet[i].restore_deterministic_wallet(seed = seeds[i])
+
+ def check_background_sync_reorg_recovery(self):
+ daemon = Daemon()
+
+ print('Testing background sync reorg recovery')
+
+ # Disconnect daemon from peers
+ daemon.out_peers(0)
+
+ # Background sync type options
+ sender_wallet = self.wallet[0]
+ reuse_password = sender_wallet.background_sync_options.reuse_password
+ custom_password = sender_wallet.background_sync_options.custom_password
+
+ for background_sync_type in [reuse_password, custom_password]:
+ # Set up wallet saved to disk
+ sender_wallet.close_wallet()
+ util_resources.remove_wallet_files('test1')
+ sender_wallet.restore_deterministic_wallet(seed = seeds[0], filename = 'test1', password = '')
+ sender_wallet.auto_refresh(enable = False)
+ sender_wallet.refresh()
+ sender_starting_balance = sender_wallet.get_balance().balance
+
+ # Send tx and mine a block
+ amount = 1000000000000
+ assert sender_starting_balance > amount
+ dst = {'address': '44Kbx4sJ7JDRDV5aAhLJzQCjDz2ViLRduE3ijDZu3osWKBjMGkV1XPk4pfDUMqt1Aiezvephdqm6YD19GKFD9ZcXVUTp6BW', 'amount': amount}
+ res = sender_wallet.transfer([dst])
+ assert len(res.tx_hash) == 32*2
+ txid = res.tx_hash
+
+ daemon.generateblocks('46r4nYSevkfBUMhuykdK3gQ98XDqDTYW1hNLaXNvjpsJaSbNtdXh1sKMsdVgqkaihChAzEy29zEDPMR3NHQvGoZCLGwTerK', 1)
+
+ # Make sure the wallet can see the tx
+ sender_wallet.refresh()
+ transfers = sender_wallet.get_transfers()
+ assert 'pool' not in transfers or len (transfers.pool) == 0
+ tx = [x for x in transfers.out if x.txid == txid]
+ assert len(tx) == 1
+ tx = tx[0]
+ assert sender_wallet.get_balance().balance < (sender_starting_balance - amount)
+
+ # Pop the block while background syncing
+ background_cache_password = None if background_sync_type == reuse_password else 'background_password'
+ sender_wallet.setup_background_sync(background_sync_type = background_sync_type, wallet_password = '', background_cache_password = background_cache_password)
+ sender_wallet.start_background_sync()
+ daemon.pop_blocks(1)
+ daemon.flush_txpool()
+
+ daemon.generateblocks('46r4nYSevkfBUMhuykdK3gQ98XDqDTYW1hNLaXNvjpsJaSbNtdXh1sKMsdVgqkaihChAzEy29zEDPMR3NHQvGoZCLGwTerK', 1)
+
+ # Make sure the wallet can no longer see the tx
+ sender_wallet.refresh()
+ sender_wallet.stop_background_sync(wallet_password = '', seed = seeds[0])
+ transfers = sender_wallet.get_transfers()
+ no_tx = [x for x in transfers.out if x.txid == txid]
+ assert len(no_tx) == 0
+ assert sender_wallet.get_balance().balance == sender_starting_balance
+
+ # Clean up
+ daemon.out_peers(12)
+ util_resources.remove_wallet_files('test1')
+ self.wallet[0].close_wallet()
+ self.wallet[0].restore_deterministic_wallet(seed = seeds[0])
+
if __name__ == '__main__':
TransferTest().run_test()
diff --git a/tests/functional_tests/txpool.py b/tests/functional_tests/txpool.py
index 273337631..8e4d9c6eb 100755
--- a/tests/functional_tests/txpool.py
+++ b/tests/functional_tests/txpool.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
-# Copyright (c) 2019-2023, The Monero Project
+# Copyright (c) 2019-2024, The Monero Project
#
# All rights reserved.
#
diff --git a/tests/functional_tests/uri.py b/tests/functional_tests/uri.py
index 1cbba14df..7d42c5ef8 100755
--- a/tests/functional_tests/uri.py
+++ b/tests/functional_tests/uri.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
#encoding=utf-8
-# Copyright (c) 2019-2023, The Monero Project
+# Copyright (c) 2019-2024, The Monero Project
#
# All rights reserved.
#
diff --git a/tests/functional_tests/util_resources.py b/tests/functional_tests/util_resources.py
index c12506146..9c879b27c 100755
--- a/tests/functional_tests/util_resources.py
+++ b/tests/functional_tests/util_resources.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
-# Copyright (c) 2021-2023, The Monero Project
+# Copyright (c) 2021-2024, The Monero Project
#
# All rights reserved.
@@ -37,6 +37,8 @@
from __future__ import print_function
import subprocess
import psutil
+import os
+import errno
def available_ram_gb():
ram_bytes = psutil.virtual_memory().available
@@ -51,3 +53,26 @@ def get_time_pi_seconds(cores, app_dir='.'):
miliseconds = int(decoded)
return miliseconds / 1000.0
+
+def remove_file(name):
+ WALLET_DIRECTORY = os.environ['WALLET_DIRECTORY']
+ assert WALLET_DIRECTORY != ''
+ try:
+ os.unlink(WALLET_DIRECTORY + '/' + name)
+ except OSError as e:
+ if e.errno != errno.ENOENT:
+ raise
+
+def get_file_path(name):
+ WALLET_DIRECTORY = os.environ['WALLET_DIRECTORY']
+ assert WALLET_DIRECTORY != ''
+ return WALLET_DIRECTORY + '/' + name
+
+def remove_wallet_files(name):
+ for suffix in ['', '.keys', '.background', '.background.keys', '.address.txt']:
+ remove_file(name + suffix)
+
+def file_exists(name):
+ WALLET_DIRECTORY = os.environ['WALLET_DIRECTORY']
+ assert WALLET_DIRECTORY != ''
+ return os.path.isfile(WALLET_DIRECTORY + '/' + name)
diff --git a/tests/functional_tests/validate_address.py b/tests/functional_tests/validate_address.py
index 915308df5..c237ba0c2 100755
--- a/tests/functional_tests/validate_address.py
+++ b/tests/functional_tests/validate_address.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
-# Copyright (c) 2019-2023, The Monero Project
+# Copyright (c) 2019-2024, The Monero Project
#
# All rights reserved.
#
diff --git a/tests/functional_tests/wallet.py b/tests/functional_tests/wallet.py
index 3bb4459d6..b55a07a70 100755
--- a/tests/functional_tests/wallet.py
+++ b/tests/functional_tests/wallet.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
#encoding=utf-8
-# Copyright (c) 2019-2023, The Monero Project
+# Copyright (c) 2019-2024, The Monero Project
#
# All rights reserved.
#
@@ -34,8 +34,7 @@
from __future__ import print_function
import sys
-import os
-import errno
+import util_resources
from framework.wallet import Wallet
from framework.daemon import Daemon
@@ -54,24 +53,6 @@ class WalletTest():
self.change_password()
self.store()
- def remove_file(self, name):
- WALLET_DIRECTORY = os.environ['WALLET_DIRECTORY']
- assert WALLET_DIRECTORY != ''
- try:
- os.unlink(WALLET_DIRECTORY + '/' + name)
- except OSError as e:
- if e.errno != errno.ENOENT:
- raise
-
- def remove_wallet_files(self, name):
- for suffix in ['', '.keys']:
- self.remove_file(name + suffix)
-
- def file_exists(self, name):
- WALLET_DIRECTORY = os.environ['WALLET_DIRECTORY']
- assert WALLET_DIRECTORY != ''
- return os.path.isfile(WALLET_DIRECTORY + '/' + name)
-
def reset(self):
print('Resetting blockchain')
daemon = Daemon()
@@ -333,7 +314,7 @@ class WalletTest():
try: wallet.close_wallet()
except: pass
- self.remove_wallet_files('test1')
+ util_resources.remove_wallet_files('test1')
seed = 'velvet lymph giddy number token physics poetry unquoted nibs useful sabotage limits benches lifestyle eden nitrogen anvil fewest avoid batch vials washing fences goat unquoted'
res = wallet.restore_deterministic_wallet(seed = seed, filename = 'test1')
@@ -359,7 +340,7 @@ class WalletTest():
wallet.close_wallet()
- self.remove_wallet_files('test1')
+ util_resources.remove_wallet_files('test1')
def store(self):
print('Testing store')
@@ -369,22 +350,26 @@ class WalletTest():
try: wallet.close_wallet()
except: pass
- self.remove_wallet_files('test1')
+ util_resources.remove_wallet_files('test1')
seed = 'velvet lymph giddy number token physics poetry unquoted nibs useful sabotage limits benches lifestyle eden nitrogen anvil fewest avoid batch vials washing fences goat unquoted'
res = wallet.restore_deterministic_wallet(seed = seed, filename = 'test1')
assert res.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
assert res.seed == seed
- self.remove_file('test1')
- assert self.file_exists('test1.keys')
- assert not self.file_exists('test1')
+ util_resources.remove_file('test1')
+ assert util_resources.file_exists('test1.keys')
+ assert not util_resources.file_exists('test1')
wallet.store()
- assert self.file_exists('test1.keys')
- assert self.file_exists('test1')
+ assert util_resources.file_exists('test1.keys')
+ assert util_resources.file_exists('test1')
wallet.close_wallet()
- self.remove_wallet_files('test1')
+
+ wallet.open_wallet(filename = 'test1', password = '')
+ wallet.close_wallet()
+
+ util_resources.remove_wallet_files('test1')
if __name__ == '__main__':