diff --git a/impls/monero.dart/lib/src/checksum_monero.dart b/impls/monero.dart/lib/src/checksum_monero.dart index 603eb21..b907f7c 100644 --- a/impls/monero.dart/lib/src/checksum_monero.dart +++ b/impls/monero.dart/lib/src/checksum_monero.dart @@ -1,4 +1,4 @@ // ignore_for_file: constant_identifier_names const String wallet2_api_c_h_sha256 = "9e80c4b59a0509aa02fbf01e8df2881b89f82225d1765bfa7856cbdbaf7af116"; -const String wallet2_api_c_cpp_sha256 = "d229507db508e574bd2badf4819a38dbead8c16a84311ad32c22c887a6003439-b089f9ee69924882c5d14dd1a6991deb05d9d1cd"; +const String wallet2_api_c_cpp_sha256 = "d229507db508e574bd2badf4819a38dbead8c16a84311ad32c22c887a6003439-0232839913b13cf0ab0bb7ad25fff0c05f37d2fe"; const String wallet2_api_c_exp_sha256 = "d0f95f1f3bc49f1f59fe4eb0b61826128d7d3bb75405d5a01a252d02db03097d"; diff --git a/impls/monero.ts/checksum_monero.ts b/impls/monero.ts/checksum_monero.ts index 03bb061..0972f78 100644 --- a/impls/monero.ts/checksum_monero.ts +++ b/impls/monero.ts/checksum_monero.ts @@ -1,5 +1,5 @@ export const moneroChecksum = { wallet2_api_c_h_sha256: "9e80c4b59a0509aa02fbf01e8df2881b89f82225d1765bfa7856cbdbaf7af116", - wallet2_api_c_cpp_sha256: "d229507db508e574bd2badf4819a38dbead8c16a84311ad32c22c887a6003439-b089f9ee69924882c5d14dd1a6991deb05d9d1cd", + wallet2_api_c_cpp_sha256: "d229507db508e574bd2badf4819a38dbead8c16a84311ad32c22c887a6003439-0232839913b13cf0ab0bb7ad25fff0c05f37d2fe", wallet2_api_c_exp_sha256: "d0f95f1f3bc49f1f59fe4eb0b61826128d7d3bb75405d5a01a252d02db03097d", } diff --git a/monero b/monero index b089f9e..0232839 160000 --- a/monero +++ b/monero @@ -1 +1 @@ -Subproject commit b089f9ee69924882c5d14dd1a6991deb05d9d1cd +Subproject commit 0232839913b13cf0ab0bb7ad25fff0c05f37d2fe diff --git a/monero_libwallet2_api_c/src/main/cpp/monero_checksum.h b/monero_libwallet2_api_c/src/main/cpp/monero_checksum.h index 02869fe..4e0fb1c 100644 --- a/monero_libwallet2_api_c/src/main/cpp/monero_checksum.h +++ b/monero_libwallet2_api_c/src/main/cpp/monero_checksum.h @@ -1,6 +1,6 @@ #ifndef MONEROC_CHECKSUMS #define MONEROC_CHECKSUMS const char * MONERO_wallet2_api_c_h_sha256 = "9e80c4b59a0509aa02fbf01e8df2881b89f82225d1765bfa7856cbdbaf7af116"; -const char * MONERO_wallet2_api_c_cpp_sha256 = "d229507db508e574bd2badf4819a38dbead8c16a84311ad32c22c887a6003439-b089f9ee69924882c5d14dd1a6991deb05d9d1cd"; +const char * MONERO_wallet2_api_c_cpp_sha256 = "d229507db508e574bd2badf4819a38dbead8c16a84311ad32c22c887a6003439-0232839913b13cf0ab0bb7ad25fff0c05f37d2fe"; const char * MONERO_wallet2_api_c_exp_sha256 = "d0f95f1f3bc49f1f59fe4eb0b61826128d7d3bb75405d5a01a252d02db03097d"; #endif diff --git a/patches/monero/0002-fix-missing-___clear_cache-when-targetting-iOS.patch b/patches/monero/0001-fix-missing-___clear_cache-when-targetting-iOS.patch similarity index 80% rename from patches/monero/0002-fix-missing-___clear_cache-when-targetting-iOS.patch rename to patches/monero/0001-fix-missing-___clear_cache-when-targetting-iOS.patch index 2b13c05..d8d3dca 100644 --- a/patches/monero/0002-fix-missing-___clear_cache-when-targetting-iOS.patch +++ b/patches/monero/0001-fix-missing-___clear_cache-when-targetting-iOS.patch @@ -1,7 +1,7 @@ -From c7b6e26d36d2428e5747e2b8287ae3719fa443e3 Mon Sep 17 00:00:00 2001 +From 6e284a2ef552f1f47e8ca9edcf8651312c9e37dd Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Tue, 2 Apr 2024 16:51:56 +0200 -Subject: [PATCH 02/14] fix missing ___clear_cache when targetting iOS +Subject: [PATCH 01/14] fix missing ___clear_cache when targetting iOS --- .gitmodules | 3 ++- @@ -9,7 +9,7 @@ Subject: [PATCH 02/14] fix missing ___clear_cache when targetting iOS 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules -index 721cce3..ffb73fe 100644 +index 721cce3b4..ffb73fe9a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -9,7 +9,8 @@ @@ -23,12 +23,12 @@ index 721cce3..ffb73fe 100644 path = external/supercop url = https://github.com/monero-project/supercop diff --git a/external/randomx b/external/randomx -index 102f8ac..ce72c9b 160000 +index 102f8acf9..ce72c9bb9 160000 --- a/external/randomx +++ b/external/randomx @@ -1 +1 @@ -Subproject commit 102f8acf90a7649ada410de5499a7ec62e49e1da +Subproject commit ce72c9bb9cb799e0d9171094b9abb009e04c5bfc -- -2.39.5 (Apple Git-154) +2.48.1 diff --git a/patches/monero/0001-wallet-background-sync-with-just-the-view-key.patch b/patches/monero/0001-wallet-background-sync-with-just-the-view-key.patch deleted file mode 100644 index 849c8be..0000000 --- a/patches/monero/0001-wallet-background-sync-with-just-the-view-key.patch +++ /dev/null @@ -1,4317 +0,0 @@ -From 0efd53a750d0827a500b036dc30ab190689e7e13 Mon Sep 17 00:00:00 2001 -From: j-berman -Date: Thu, 13 Oct 2022 18:33:33 -0700 -Subject: [PATCH 01/14] wallet: background sync with just the view key - -- When background syncing, the wallet wipes the spend key -from memory and processes all new transactions. The wallet saves -all receives, spends, and "plausible" spends of receives the -wallet does not know key images for. -- When background sync disabled, the wallet processes all -background synced txs and then clears the background sync cache. -- Adding "plausible" spends to the background sync cache ensures -that the wallet does not need to query the daemon to see if any -received outputs were spent while background sync was enabled. -This would harm privacy especially for users of 3rd party daemons. -- To enable the feature in the CLI wallet, the user can set -background-sync to reuse-wallet-password or -custom-background-password and the wallet automatically syncs in -the background when the wallet locks, then processes all -background synced txs when the wallet is unlocked. -- The custom-background-password option enables the user to -open a distinct background wallet that only has a view key saved -and can be opened/closed/synced separately from the main wallet. -When the main wallet opens, it processes the background wallet's -cache. -- To enable the feature in the RPC wallet, there is a new -`/setup_background_sync` endpoint. -- HW, multsig and view-only wallets cannot background sync. ---- - src/cryptonote_basic/account.cpp | 11 + - src/cryptonote_basic/account.h | 1 + - src/cryptonote_config.h | 2 + - src/simplewallet/simplewallet.cpp | 204 +++- - src/simplewallet/simplewallet.h | 1 + - src/wallet/api/wallet.cpp | 213 +++- - src/wallet/api/wallet.h | 12 + - src/wallet/api/wallet2_api.h | 42 + - src/wallet/wallet2.cpp | 1030 ++++++++++++++++-- - src/wallet/wallet2.h | 156 ++- - src/wallet/wallet_errors.h | 39 + - src/wallet/wallet_rpc_server.cpp | 162 +++ - src/wallet/wallet_rpc_server.h | 6 + - src/wallet/wallet_rpc_server_commands_defs.h | 64 ++ - src/wallet/wallet_rpc_server_error_codes.h | 2 + - tests/functional_tests/transfer.py | 400 ++++++- - tests/functional_tests/util_resources.py | 25 + - tests/functional_tests/wallet.py | 43 +- - tests/unit_tests/wipeable_string.cpp | 12 + - utils/python-rpc/framework/wallet.py | 42 + - 20 files changed, 2337 insertions(+), 130 deletions(-) - -diff --git a/src/cryptonote_basic/account.cpp b/src/cryptonote_basic/account.cpp -index 2ac455f..4e87d44 100644 ---- a/src/cryptonote_basic/account.cpp -+++ b/src/cryptonote_basic/account.cpp -@@ -152,6 +152,17 @@ DISABLE_VS_WARNINGS(4244 4345) - m_keys.m_multisig_keys.clear(); - } - //----------------------------------------------------------------- -+ void account_base::set_spend_key(const crypto::secret_key& spend_secret_key) -+ { -+ // make sure derived spend public key matches saved public spend key -+ crypto::public_key spend_public_key; -+ crypto::secret_key_to_public_key(spend_secret_key, spend_public_key); -+ CHECK_AND_ASSERT_THROW_MES(m_keys.m_account_address.m_spend_public_key == spend_public_key, -+ "Unexpected derived public spend key"); -+ -+ m_keys.m_spend_secret_key = spend_secret_key; -+ } -+ //----------------------------------------------------------------- - crypto::secret_key account_base::generate(const crypto::secret_key& recovery_key, bool recover, bool two_random) - { - crypto::secret_key first = generate_keys(m_keys.m_account_address.m_spend_public_key, m_keys.m_spend_secret_key, recovery_key, recover); -diff --git a/src/cryptonote_basic/account.h b/src/cryptonote_basic/account.h -index 2ee9545..93d1d28 100644 ---- a/src/cryptonote_basic/account.h -+++ b/src/cryptonote_basic/account.h -@@ -95,6 +95,7 @@ namespace cryptonote - bool store(const std::string& file_path); - - void forget_spend_key(); -+ void set_spend_key(const crypto::secret_key& spend_secret_key); - const std::vector &get_multisig_keys() const { return m_keys.m_multisig_keys; } - - void encrypt_keys(const crypto::chacha_key &key) { m_keys.encrypt(key); } -diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h -index 61146a1..f9e6a6c 100644 ---- a/src/cryptonote_config.h -+++ b/src/cryptonote_config.h -@@ -241,6 +241,8 @@ namespace config - const unsigned char HASH_KEY_ENCRYPTED_PAYMENT_ID = 0x8d; - const unsigned char HASH_KEY_WALLET = 0x8c; - const unsigned char HASH_KEY_WALLET_CACHE = 0x8d; -+ const unsigned char HASH_KEY_BACKGROUND_CACHE = 0x8e; -+ const unsigned char HASH_KEY_BACKGROUND_KEYS_FILE = 0x8f; - const unsigned char HASH_KEY_RPC_PAYMENT_NONCE = 0x58; - const unsigned char HASH_KEY_MEMORY = 'k'; - const unsigned char HASH_KEY_MULTISIG[] = {'M', 'u', 'l', 't' , 'i', 's', 'i', 'g', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; -diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp -index b9e30f9..2c51337 100644 ---- a/src/simplewallet/simplewallet.cpp -+++ b/src/simplewallet/simplewallet.cpp -@@ -155,6 +155,17 @@ typedef cryptonote::simple_wallet sw; - } \ - } while(0) - -+#define CHECK_IF_BACKGROUND_SYNCING(msg) \ -+ do \ -+ { \ -+ if (m_wallet->is_background_wallet() || m_wallet->is_background_syncing()) \ -+ { \ -+ std::string type = m_wallet->is_background_wallet() ? "background wallet" : "background syncing wallet"; \ -+ fail_msg_writer() << boost::format(tr("%s %s")) % type % msg; \ -+ return false; \ -+ } \ -+ } while (0) -+ - static std::string get_human_readable_timespan(std::chrono::seconds seconds); - static std::string get_human_readable_timespan(uint64_t seconds); - -@@ -325,7 +336,7 @@ namespace - auto pwd_container = tools::password_container::prompt(verify, prompt); - if (!pwd_container) - { -- tools::fail_msg_writer() << sw::tr("failed to read wallet password"); -+ tools::fail_msg_writer() << sw::tr("failed to read password"); - } - return pwd_container; - } -@@ -335,6 +346,11 @@ namespace - return password_prompter(verify ? sw::tr("Enter a new password for the wallet") : sw::tr("Wallet password"), verify); - } - -+ boost::optional background_sync_cache_password_prompter(bool verify) -+ { -+ return password_prompter(verify ? sw::tr("Enter a custom password for the background sync cache") : sw::tr("Background sync cache password"), verify); -+ } -+ - inline std::string interpret_rpc_response(bool ok, const std::string& status) - { - std::string err; -@@ -452,6 +468,41 @@ namespace - return "invalid"; - } - -+ const struct -+ { -+ const char *name; -+ tools::wallet2::BackgroundSyncType background_sync_type; -+ } background_sync_type_names[] = -+ { -+ { "off", tools::wallet2::BackgroundSyncOff }, -+ { "reuse-wallet-password", tools::wallet2::BackgroundSyncReusePassword }, -+ { "custom-background-password", tools::wallet2::BackgroundSyncCustomPassword }, -+ }; -+ -+ bool parse_background_sync_type(const std::string &s, tools::wallet2::BackgroundSyncType &background_sync_type) -+ { -+ for (size_t n = 0; n < sizeof(background_sync_type_names) / sizeof(background_sync_type_names[0]); ++n) -+ { -+ if (s == background_sync_type_names[n].name) -+ { -+ background_sync_type = background_sync_type_names[n].background_sync_type; -+ return true; -+ } -+ } -+ fail_msg_writer() << cryptonote::simple_wallet::tr("failed to parse background sync type"); -+ return false; -+ } -+ -+ std::string get_background_sync_type_name(tools::wallet2::BackgroundSyncType type) -+ { -+ for (size_t n = 0; n < sizeof(background_sync_type_names) / sizeof(background_sync_type_names[0]); ++n) -+ { -+ if (type == background_sync_type_names[n].background_sync_type) -+ return background_sync_type_names[n].name; -+ } -+ return "invalid"; -+ } -+ - std::string get_version_string(uint32_t version) - { - return boost::lexical_cast(version >> 16) + "." + boost::lexical_cast(version & 0xffff); -@@ -805,6 +856,7 @@ bool simple_wallet::spendkey(const std::vector &args/* = std::vecto - fail_msg_writer() << tr("wallet is watch-only and has no spend key"); - return true; - } -+ CHECK_IF_BACKGROUND_SYNCING("has no spend key"); - // don't log - PAUSE_READLINE(); - if (m_wallet->key_on_device()) { -@@ -836,6 +888,7 @@ bool simple_wallet::print_seed(bool encrypted) - fail_msg_writer() << tr("wallet is watch-only and has no seed"); - return true; - } -+ CHECK_IF_BACKGROUND_SYNCING("has no seed"); - - multisig = m_wallet->multisig(&ready); - if (multisig) -@@ -913,6 +966,7 @@ bool simple_wallet::seed_set_language(const std::vector &args/* = s - fail_msg_writer() << tr("wallet is watch-only and has no seed"); - return true; - } -+ CHECK_IF_BACKGROUND_SYNCING("has no seed"); - - epee::wipeable_string password; - { -@@ -1059,6 +1113,7 @@ bool simple_wallet::prepare_multisig_main(const std::vector &args, - fail_msg_writer() << tr("wallet is watch-only and cannot be made multisig"); - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING("cannot be made multisig"); - - if(m_wallet->get_num_transfer_details()) - { -@@ -2195,6 +2250,7 @@ bool simple_wallet::save_known_rings(const std::vector &args) - - bool simple_wallet::freeze_thaw(const std::vector &args, bool freeze) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot freeze/thaw"); - if (args.empty()) - { - fail_msg_writer() << boost::format(tr("usage: %s |")) % (freeze ? "freeze" : "thaw"); -@@ -2234,6 +2290,7 @@ bool simple_wallet::thaw(const std::vector &args) - - bool simple_wallet::frozen(const std::vector &args) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot see frozen key images"); - if (args.empty()) - { - size_t ntd = m_wallet->get_num_transfer_details(); -@@ -3005,6 +3062,57 @@ bool simple_wallet::set_track_uses(const std::vector &args/* = std: - return true; - } - -+bool simple_wallet::setup_background_sync(const std::vector &args/* = std::vector()*/) -+{ -+ if (m_wallet->multisig()) -+ { -+ fail_msg_writer() << tr("background sync not implemented for multisig wallet"); -+ return true; -+ } -+ if (m_wallet->watch_only()) -+ { -+ fail_msg_writer() << tr("background sync not implemented for watch only wallet"); -+ return true; -+ } -+ if (m_wallet->key_on_device()) -+ { -+ fail_msg_writer() << tr("command not supported by HW wallet"); -+ return true; -+ } -+ -+ tools::wallet2::BackgroundSyncType background_sync_type; -+ if (!parse_background_sync_type(args[1], background_sync_type)) -+ { -+ fail_msg_writer() << tr("invalid option"); -+ return true; -+ } -+ -+ const auto pwd_container = get_and_verify_password(); -+ if (!pwd_container) -+ return true; -+ -+ try -+ { -+ boost::optional background_cache_password = boost::none; -+ if (background_sync_type == tools::wallet2::BackgroundSyncCustomPassword) -+ { -+ const auto background_pwd_container = background_sync_cache_password_prompter(true); -+ if (!background_pwd_container) -+ return true; -+ background_cache_password = background_pwd_container->password(); -+ } -+ -+ LOCK_IDLE_SCOPE(); -+ m_wallet->setup_background_sync(background_sync_type, pwd_container->password(), background_cache_password); -+ } -+ catch (const std::exception &e) -+ { -+ fail_msg_writer() << tr("Error setting background sync type: ") << e.what(); -+ } -+ -+ return true; -+} -+ - bool simple_wallet::set_show_wallet_name_when_locked(const std::vector &args/* = std::vector()*/) - { - const auto pwd_container = get_and_verify_password(); -@@ -3237,6 +3345,7 @@ bool simple_wallet::apropos(const std::vector &args) - - bool simple_wallet::scan_tx(const std::vector &args) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot scan tx"); - if (args.empty()) - { - PRINT_USAGE(USAGE_SCAN_TX); -@@ -3458,6 +3567,8 @@ simple_wallet::simple_wallet() - " Ignore outputs of amount below this threshold when spending.\n " - "track-uses <1|0>\n " - " Whether to keep track of owned outputs uses.\n " -+ "background-sync \n " -+ " Set this to enable scanning in the background with just the view key while the wallet is locked.\n " - "setup-background-mining <1|0>\n " - " Whether to enable background mining. Set this to support the network and to get a chance to receive new monero.\n " - "device-name \n " -@@ -3876,6 +3987,7 @@ bool simple_wallet::set_variable(const std::vector &args) - success_msg_writer() << "ignore-outputs-above = " << cryptonote::print_money(m_wallet->ignore_outputs_above()); - success_msg_writer() << "ignore-outputs-below = " << cryptonote::print_money(m_wallet->ignore_outputs_below()); - success_msg_writer() << "track-uses = " << m_wallet->track_uses(); -+ success_msg_writer() << "background-sync = " << get_background_sync_type_name(m_wallet->background_sync_type()); - success_msg_writer() << "setup-background-mining = " << setup_background_mining_string; - success_msg_writer() << "device-name = " << m_wallet->device_name(); - success_msg_writer() << "export-format = " << (m_wallet->export_format() == tools::wallet2::ExportFormat::Ascii ? "ascii" : "binary"); -@@ -3894,6 +4006,7 @@ bool simple_wallet::set_variable(const std::vector &args) - } - else - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot change wallet settings"); - - #define CHECK_SIMPLE_VARIABLE(name, f, help) do \ - if (args[0] == name) { \ -@@ -3947,6 +4060,7 @@ bool simple_wallet::set_variable(const std::vector &args) - CHECK_SIMPLE_VARIABLE("ignore-outputs-above", set_ignore_outputs_above, tr("amount")); - CHECK_SIMPLE_VARIABLE("ignore-outputs-below", set_ignore_outputs_below, tr("amount")); - CHECK_SIMPLE_VARIABLE("track-uses", set_track_uses, tr("0 or 1")); -+ CHECK_SIMPLE_VARIABLE("background-sync", setup_background_sync, tr("off (default); reuse-wallet-password (reuse the wallet password to encrypt the background cache); custom-background-password (use a custom background password to encrypt the background cache)")); - CHECK_SIMPLE_VARIABLE("show-wallet-name-when-locked", set_show_wallet_name_when_locked, tr("1 or 0")); - CHECK_SIMPLE_VARIABLE("inactivity-lock-timeout", set_inactivity_lock_timeout, tr("unsigned integer (seconds, 0 to disable)")); - CHECK_SIMPLE_VARIABLE("setup-background-mining", set_setup_background_mining, tr("1/yes or 0/no")); -@@ -4900,7 +5014,10 @@ std::string simple_wallet::get_mnemonic_language() - //---------------------------------------------------------------------------------------------------- - boost::optional simple_wallet::get_and_verify_password() const - { -- auto pwd_container = default_password_prompter(m_wallet_file.empty()); -+ const bool verify = m_wallet_file.empty(); -+ auto pwd_container = (m_wallet->is_background_wallet() && m_wallet->background_sync_type() == tools::wallet2::BackgroundSyncCustomPassword) -+ ? background_sync_cache_password_prompter(verify) -+ : default_password_prompter(verify); - if (!pwd_container) - return boost::none; - -@@ -5203,6 +5320,8 @@ boost::optional simple_wallet::open_wallet(const boost::p - prefix = tr("Opened watch-only wallet"); - else if (m_wallet->multisig(&ready, &threshold, &total)) - prefix = (boost::format(tr("Opened %u/%u multisig wallet%s")) % threshold % total % (ready ? "" : " (not yet finalized)")).str(); -+ else if (m_wallet->is_background_wallet()) -+ prefix = tr("Opened background wallet"); - else - prefix = tr("Opened wallet"); - message_writer(console_color_white, true) << -@@ -5411,6 +5530,10 @@ void simple_wallet::stop_background_mining() - //---------------------------------------------------------------------------------------------------- - void simple_wallet::check_background_mining(const epee::wipeable_string &password) - { -+ // Background mining can be toggled from the main wallet -+ if (m_wallet->is_background_wallet() || m_wallet->is_background_syncing()) -+ return; -+ - tools::wallet2::BackgroundMiningSetupType setup = m_wallet->setup_background_mining(); - if (setup == tools::wallet2::BackgroundMiningNo) - { -@@ -6275,6 +6398,7 @@ bool simple_wallet::show_blockchain_height(const std::vector& args) - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::rescan_spent(const std::vector &args) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot rescan spent"); - if (!m_wallet->is_trusted_daemon()) - { - fail_msg_writer() << tr("this command requires a trusted daemon. Enable with --trusted-daemon"); -@@ -6532,10 +6656,27 @@ void simple_wallet::check_for_inactivity_lock(bool user) - " || ||" << std::endl << - "" << std::endl; - } -+ -+ bool started_background_sync = false; -+ if (!m_wallet->is_background_wallet() && -+ m_wallet->background_sync_type() != tools::wallet2::BackgroundSyncOff) -+ { -+ LOCK_IDLE_SCOPE(); -+ m_wallet->start_background_sync(); -+ started_background_sync = true; -+ } -+ - while (1) - { - const char *inactivity_msg = user ? "" : tr("Locked due to inactivity."); -- tools::msg_writer() << inactivity_msg << (inactivity_msg[0] ? " " : "") << tr("The wallet password is required to unlock the console."); -+ tools::msg_writer() << inactivity_msg << (inactivity_msg[0] ? " " : "") << ( -+ (m_wallet->is_background_wallet() && m_wallet->background_sync_type() == tools::wallet2::BackgroundSyncCustomPassword) -+ ? tr("The background password is required to unlock the console.") -+ : tr("The wallet password is required to unlock the console.") -+ ); -+ -+ if (m_wallet->is_background_syncing()) -+ tools::msg_writer() << tr("\nSyncing in the background while locked...") << std::endl; - - const bool show_wallet_name = m_wallet->show_wallet_name_when_locked(); - if (show_wallet_name) -@@ -6548,8 +6689,16 @@ void simple_wallet::check_for_inactivity_lock(bool user) - } - try - { -- if (get_and_verify_password()) -+ const auto pwd_container = get_and_verify_password(); -+ if (pwd_container) -+ { -+ if (started_background_sync) -+ { -+ LOCK_IDLE_SCOPE(); -+ m_wallet->stop_background_sync(pwd_container->password()); -+ } - break; -+ } - } - catch (...) { /* do nothing, just let the loop loop */ } - } -@@ -6576,6 +6725,7 @@ bool simple_wallet::on_command(bool (simple_wallet::*cmd)(const std::vector &args_, bool called_by_mms) - { - // "transfer [index=[,,...]] [] []
[]" -+ CHECK_IF_BACKGROUND_SYNCING("cannot transfer"); - if (!try_connect_to_daemon()) - return false; - -@@ -7004,6 +7154,7 @@ bool simple_wallet::transfer_main(const std::vector &args_, bool ca - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::transfer(const std::vector &args_) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot transfer"); - if (args_.size() < 1) - { - PRINT_USAGE(USAGE_TRANSFER); -@@ -7016,6 +7167,7 @@ bool simple_wallet::transfer(const std::vector &args_) - - bool simple_wallet::sweep_unmixable(const std::vector &args_) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot sweep"); - if (!try_connect_to_daemon()) - return true; - -@@ -7123,6 +7275,7 @@ bool simple_wallet::sweep_unmixable(const std::vector &args_) - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::sweep_main(uint32_t account, uint64_t below, const std::vector &args_) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot sweep"); - auto print_usage = [this, account, below]() - { - if (below) -@@ -7404,6 +7557,7 @@ bool simple_wallet::sweep_main(uint32_t account, uint64_t below, const std::vect - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::sweep_single(const std::vector &args_) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot sweep"); - if (!try_connect_to_daemon()) - return true; - -@@ -7642,12 +7796,14 @@ bool simple_wallet::sweep_single(const std::vector &args_) - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::sweep_all(const std::vector &args_) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot sweep"); - sweep_main(m_current_subaddress_account, 0, args_); - return true; - } - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::sweep_account(const std::vector &args_) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot sweep"); - auto local_args = args_; - if (local_args.empty()) - { -@@ -7668,6 +7824,7 @@ bool simple_wallet::sweep_account(const std::vector &args_) - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::sweep_below(const std::vector &args_) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot sweep"); - uint64_t below = 0; - if (args_.size() < 1) - { -@@ -7686,6 +7843,7 @@ bool simple_wallet::sweep_below(const std::vector &args_) - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::donate(const std::vector &args_) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot donate"); - std::vector local_args = args_; - if(local_args.empty() || local_args.size() > 5) - { -@@ -7747,6 +7905,7 @@ bool simple_wallet::donate(const std::vector &args_) - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::accept_loaded_tx(const std::function get_num_txes, const std::function &get_tx, const std::string &extra_message) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot load tx"); - // gather info to ask the user - uint64_t amount = 0, amount_to_dests = 0, change = 0; - size_t min_ring_size = ~0; -@@ -7927,6 +8086,7 @@ bool simple_wallet::sign_transfer(const std::vector &args_) - fail_msg_writer() << tr("This is a watch only wallet"); - return true; - } -+ CHECK_IF_BACKGROUND_SYNCING("cannot sign transfer"); - - bool export_raw = false; - std::string unsigned_filename = "unsigned_monero_tx"; -@@ -8034,6 +8194,8 @@ std::string get_tx_key_stream(crypto::secret_key tx_key, std::vector &args_) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot get tx key"); -+ - std::vector local_args = args_; - - if (m_wallet->key_on_device() && m_wallet->get_account().get_device().get_type() != hw::device::TREZOR) -@@ -8074,6 +8236,8 @@ bool simple_wallet::get_tx_key(const std::vector &args_) - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::set_tx_key(const std::vector &args_) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot set tx key"); -+ - std::vector local_args = args_; - - if(local_args.size() != 2 && local_args.size() != 3) { -@@ -8150,6 +8314,8 @@ bool simple_wallet::set_tx_key(const std::vector &args_) - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::get_tx_proof(const std::vector &args) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot get tx proof"); -+ - if (args.size() != 2 && args.size() != 3) - { - PRINT_USAGE(USAGE_GET_TX_PROOF); -@@ -8356,6 +8522,7 @@ bool simple_wallet::check_tx_proof(const std::vector &args) - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::get_spend_proof(const std::vector &args) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot get spend proof"); - if (m_wallet->key_on_device()) - { - fail_msg_writer() << tr("command not supported by HW wallet"); -@@ -8440,6 +8607,7 @@ bool simple_wallet::check_spend_proof(const std::vector &args) - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::get_reserve_proof(const std::vector &args) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot get reserve proof"); - if (m_wallet->key_on_device()) - { - fail_msg_writer() << tr("command not supported by HW wallet"); -@@ -9126,6 +9294,8 @@ bool simple_wallet::unspent_outputs(const std::vector &args_) - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::rescan_blockchain(const std::vector &args_) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot rescan"); -+ - uint64_t start_height = 0; - ResetType reset_type = ResetSoft; - -@@ -9423,6 +9593,7 @@ bool simple_wallet::account(const std::vector &args/* = std::vector - if (command == "new") - { - // create a new account and switch to it -+ CHECK_IF_BACKGROUND_SYNCING("cannot create new account"); - std::string label = boost::join(local_args, " "); - if (label.empty()) - label = tr("(Untitled account)"); -@@ -9453,6 +9624,7 @@ bool simple_wallet::account(const std::vector &args/* = std::vector - else if (command == "label" && local_args.size() >= 1) - { - // set label of the specified account -+ CHECK_IF_BACKGROUND_SYNCING("cannot modify account"); - uint32_t index_major; - if (!epee::string_tools::get_xtype_from_string(index_major, local_args[0])) - { -@@ -9474,6 +9646,7 @@ bool simple_wallet::account(const std::vector &args/* = std::vector - } - else if (command == "tag" && local_args.size() >= 2) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot modify account"); - const std::string tag = local_args[0]; - std::set account_indices; - for (size_t i = 1; i < local_args.size(); ++i) -@@ -9498,6 +9671,7 @@ bool simple_wallet::account(const std::vector &args/* = std::vector - } - else if (command == "untag" && local_args.size() >= 1) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot modify account"); - std::set account_indices; - for (size_t i = 0; i < local_args.size(); ++i) - { -@@ -9521,6 +9695,7 @@ bool simple_wallet::account(const std::vector &args/* = std::vector - } - else if (command == "tag_description" && local_args.size() >= 1) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot modify account"); - const std::string tag = local_args[0]; - std::string description; - if (local_args.size() > 1) -@@ -9638,6 +9813,7 @@ bool simple_wallet::print_address(const std::vector &args/* = std:: - } - else if (local_args[0] == "new") - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot add address"); - local_args.erase(local_args.begin()); - std::string label; - if (local_args.size() > 0) -@@ -9650,6 +9826,7 @@ bool simple_wallet::print_address(const std::vector &args/* = std:: - } - else if (local_args[0] == "mnew") - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot add addresses"); - local_args.erase(local_args.begin()); - if (local_args.size() != 1) - { -@@ -9675,6 +9852,7 @@ bool simple_wallet::print_address(const std::vector &args/* = std:: - } - else if (local_args[0] == "one-off") - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot add address"); - local_args.erase(local_args.begin()); - std::string label; - if (local_args.size() != 2) -@@ -9693,6 +9871,7 @@ bool simple_wallet::print_address(const std::vector &args/* = std:: - } - else if (local_args.size() >= 2 && local_args[0] == "label") - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot modify address"); - if (!epee::string_tools::get_xtype_from_string(index, local_args[1])) - { - fail_msg_writer() << tr("failed to parse index: ") << local_args[1]; -@@ -9839,6 +10018,8 @@ bool simple_wallet::print_integrated_address(const std::vector &arg - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::address_book(const std::vector &args/* = std::vector()*/) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot get address book"); -+ - if (args.size() == 0) - { - } -@@ -9899,6 +10080,8 @@ bool simple_wallet::address_book(const std::vector &args/* = std::v - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::set_tx_note(const std::vector &args) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot set tx note"); -+ - if (args.size() == 0) - { - PRINT_USAGE(USAGE_SET_TX_NOTE); -@@ -9927,6 +10110,8 @@ bool simple_wallet::set_tx_note(const std::vector &args) - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::get_tx_note(const std::vector &args) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot get tx note"); -+ - if (args.size() != 1) - { - PRINT_USAGE(USAGE_GET_TX_NOTE); -@@ -9952,6 +10137,8 @@ bool simple_wallet::get_tx_note(const std::vector &args) - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::set_description(const std::vector &args) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot set description"); -+ - // 0 arguments allowed, for setting the description to empty string - - std::string description = ""; -@@ -9968,6 +10155,8 @@ bool simple_wallet::set_description(const std::vector &args) - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::get_description(const std::vector &args) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot get description"); -+ - if (args.size() != 0) - { - PRINT_USAGE(USAGE_GET_DESCRIPTION); -@@ -10026,6 +10215,8 @@ bool simple_wallet::wallet_info(const std::vector &args) - type = tr("Watch only"); - else if (m_wallet->multisig(&ready, &threshold, &total)) - type = (boost::format(tr("%u/%u multisig%s")) % threshold % total % (ready ? "" : " (not yet finalized)")).str(); -+ else if (m_wallet->is_background_wallet()) -+ type = tr("Background wallet"); - else - type = tr("Normal"); - message_writer() << tr("Type: ") << type; -@@ -10037,6 +10228,7 @@ bool simple_wallet::wallet_info(const std::vector &args) - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::sign(const std::vector &args) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot sign"); - if (m_wallet->key_on_device()) - { - fail_msg_writer() << tr("command not supported by HW wallet"); -@@ -10144,6 +10336,7 @@ bool simple_wallet::export_key_images(const std::vector &args_) - fail_msg_writer() << tr("command not supported by HW wallet"); - return true; - } -+ CHECK_IF_BACKGROUND_SYNCING("cannot export key images"); - auto args = args_; - - if (m_wallet->watch_only()) -@@ -10197,6 +10390,7 @@ bool simple_wallet::import_key_images(const std::vector &args) - fail_msg_writer() << tr("command not supported by HW wallet"); - return true; - } -+ CHECK_IF_BACKGROUND_SYNCING("cannot import key images"); - if (!m_wallet->is_trusted_daemon()) - { - fail_msg_writer() << tr("this command requires a trusted daemon. Enable with --trusted-daemon"); -@@ -10305,6 +10499,7 @@ bool simple_wallet::export_outputs(const std::vector &args_) - fail_msg_writer() << tr("command not supported by HW wallet"); - return true; - } -+ CHECK_IF_BACKGROUND_SYNCING("cannot export outputs"); - auto args = args_; - - bool all = false; -@@ -10354,6 +10549,7 @@ bool simple_wallet::import_outputs(const std::vector &args) - fail_msg_writer() << tr("command not supported by HW wallet"); - return true; - } -+ CHECK_IF_BACKGROUND_SYNCING("cannot import outputs"); - if (args.size() != 1) - { - PRINT_USAGE(USAGE_IMPORT_OUTPUTS); -diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h -index 652708f..159da2c 100644 ---- a/src/simplewallet/simplewallet.h -+++ b/src/simplewallet/simplewallet.h -@@ -147,6 +147,7 @@ namespace cryptonote - bool set_ignore_outputs_above(const std::vector &args = std::vector()); - bool set_ignore_outputs_below(const std::vector &args = std::vector()); - bool set_track_uses(const std::vector &args = std::vector()); -+ bool setup_background_sync(const std::vector &args = std::vector()); - bool set_show_wallet_name_when_locked(const std::vector &args = std::vector()); - bool set_inactivity_lock_timeout(const std::vector &args = std::vector()); - bool set_setup_background_mining(const std::vector &args = std::vector()); -diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp -index fc4f891..e9f76f4 100644 ---- a/src/wallet/api/wallet.cpp -+++ b/src/wallet/api/wallet.cpp -@@ -54,6 +54,40 @@ using namespace cryptonote; - #undef MONERO_DEFAULT_LOG_CATEGORY - #define MONERO_DEFAULT_LOG_CATEGORY "WalletAPI" - -+#define LOCK_REFRESH() \ -+ bool refresh_enabled = m_refreshEnabled; \ -+ m_refreshEnabled = false; \ -+ m_wallet->stop(); \ -+ m_refreshCV.notify_one(); \ -+ boost::mutex::scoped_lock lock(m_refreshMutex); \ -+ boost::mutex::scoped_lock lock2(m_refreshMutex2); \ -+ epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){ \ -+ /* m_refreshMutex's still locked here */ \ -+ if (refresh_enabled) \ -+ startRefresh(); \ -+ }) -+ -+#define PRE_VALIDATE_BACKGROUND_SYNC() \ -+ do \ -+ { \ -+ clearStatus(); \ -+ if (m_wallet->key_on_device()) \ -+ { \ -+ setStatusError(tr("HW wallet cannot use background sync")); \ -+ return false; \ -+ } \ -+ if (m_wallet->watch_only()) \ -+ { \ -+ setStatusError(tr("View only wallet cannot use background sync")); \ -+ return false; \ -+ } \ -+ if (m_wallet->multisig()) \ -+ { \ -+ setStatusError(tr("Multisig wallet cannot use background sync")); \ -+ return false; \ -+ } \ -+ } while (0) -+ - namespace Monero { - - namespace { -@@ -792,6 +826,8 @@ bool WalletImpl::close(bool store) - - std::string WalletImpl::seed(const std::string& seed_offset) const - { -+ if (checkBackgroundSync("cannot get seed")) -+ return std::string(); - epee::wipeable_string seed; - if (m_wallet) - m_wallet->get_seed(seed, seed_offset); -@@ -805,6 +841,8 @@ std::string WalletImpl::getSeedLanguage() const - - void WalletImpl::setSeedLanguage(const std::string &arg) - { -+ if (checkBackgroundSync("cannot set seed language")) -+ return; - m_wallet->set_seed_language(arg); - } - -@@ -828,6 +866,8 @@ void WalletImpl::statusWithErrorString(int& status, std::string& errorString) co - - bool WalletImpl::setPassword(const std::string &password) - { -+ if (checkBackgroundSync("cannot change password")) -+ return false; - clearStatus(); - try { - m_wallet->change_password(m_wallet->get_wallet_file(), m_password, password); -@@ -988,6 +1028,8 @@ bool WalletImpl::lightWalletImportWalletRequest(std::string &payment_id, uint64_ - - void WalletImpl::setRefreshFromBlockHeight(uint64_t refresh_from_block_height) - { -+ if (checkBackgroundSync("cannot change refresh height")) -+ return; - m_wallet->set_refresh_from_block_height(refresh_from_block_height); - } - -@@ -1105,6 +1147,8 @@ void WalletImpl::refreshAsync() - - bool WalletImpl::rescanBlockchain() - { -+ if (checkBackgroundSync("cannot rescan blockchain")) -+ return false; - clearStatus(); - m_refreshShouldRescan = true; - doRefresh(); -@@ -1113,6 +1157,8 @@ bool WalletImpl::rescanBlockchain() - - void WalletImpl::rescanBlockchainAsync() - { -+ if (checkBackgroundSync("cannot rescan blockchain")) -+ return; - m_refreshShouldRescan = true; - refreshAsync(); - } -@@ -1136,7 +1182,7 @@ int WalletImpl::autoRefreshInterval() const - UnsignedTransaction *WalletImpl::loadUnsignedTx(const std::string &unsigned_filename) { - clearStatus(); - UnsignedTransactionImpl * transaction = new UnsignedTransactionImpl(*this); -- if (!m_wallet->load_unsigned_tx(unsigned_filename, transaction->m_unsigned_tx_set)){ -+ if (checkBackgroundSync("cannot load tx") || !m_wallet->load_unsigned_tx(unsigned_filename, transaction->m_unsigned_tx_set)){ - setStatusError(tr("Failed to load unsigned transactions")); - transaction->m_status = UnsignedTransaction::Status::Status_Error; - transaction->m_errorString = errorString(); -@@ -1156,6 +1202,8 @@ UnsignedTransaction *WalletImpl::loadUnsignedTx(const std::string &unsigned_file - - bool WalletImpl::submitTransaction(const string &fileName) { - clearStatus(); -+ if (checkBackgroundSync("cannot submit tx")) -+ return false; - std::unique_ptr transaction(new PendingTransactionImpl(*this)); - - bool r = m_wallet->load_tx(fileName, transaction->m_pending_tx); -@@ -1179,6 +1227,8 @@ bool WalletImpl::exportKeyImages(const string &filename, bool all) - setStatusError(tr("Wallet is view only")); - return false; - } -+ if (checkBackgroundSync("cannot export key images")) -+ return false; - - try - { -@@ -1199,6 +1249,8 @@ bool WalletImpl::exportKeyImages(const string &filename, bool all) - - bool WalletImpl::importKeyImages(const string &filename) - { -+ if (checkBackgroundSync("cannot import key images")) -+ return false; - if (!trustedDaemon()) { - setStatusError(tr("Key images can only be imported with a trusted daemon")); - return false; -@@ -1222,6 +1274,8 @@ bool WalletImpl::importKeyImages(const string &filename) - - bool WalletImpl::exportOutputs(const string &filename, bool all) - { -+ if (checkBackgroundSync("cannot export outputs")) -+ return false; - if (m_wallet->key_on_device()) - { - setStatusError(string(tr("Not supported on HW wallets.")) + filename); -@@ -1252,6 +1306,8 @@ bool WalletImpl::exportOutputs(const string &filename, bool all) - - bool WalletImpl::importOutputs(const string &filename) - { -+ if (checkBackgroundSync("cannot import outputs")) -+ return false; - if (m_wallet->key_on_device()) - { - setStatusError(string(tr("Not supported on HW wallets.")) + filename); -@@ -1284,6 +1340,8 @@ bool WalletImpl::importOutputs(const string &filename) - - bool WalletImpl::scanTransactions(const std::vector &txids) - { -+ if (checkBackgroundSync("cannot scan transactions")) -+ return false; - if (txids.empty()) - { - setStatusError(string(tr("Failed to scan transactions: no transaction ids provided."))); -@@ -1322,8 +1380,86 @@ bool WalletImpl::scanTransactions(const std::vector &txids) - return true; - } - -+bool WalletImpl::setupBackgroundSync(const Wallet::BackgroundSyncType background_sync_type, const std::string &wallet_password, const optional &background_cache_password) -+{ -+ try -+ { -+ PRE_VALIDATE_BACKGROUND_SYNC(); -+ -+ tools::wallet2::BackgroundSyncType bgs_type; -+ switch (background_sync_type) -+ { -+ case Wallet::BackgroundSync_Off: bgs_type = tools::wallet2::BackgroundSyncOff; break; -+ case Wallet::BackgroundSync_ReusePassword: bgs_type = tools::wallet2::BackgroundSyncReusePassword; break; -+ case Wallet::BackgroundSync_CustomPassword: bgs_type = tools::wallet2::BackgroundSyncCustomPassword; break; -+ default: setStatusError(tr("Unknown background sync type")); return false; -+ } -+ -+ boost::optional bgc_password = background_cache_password -+ ? boost::optional(*background_cache_password) -+ : boost::none; -+ -+ LOCK_REFRESH(); -+ m_wallet->setup_background_sync(bgs_type, wallet_password, bgc_password); -+ } -+ catch (const std::exception &e) -+ { -+ LOG_ERROR("Failed to setup background sync: " << e.what()); -+ setStatusError(string(tr("Failed to setup background sync: ")) + e.what()); -+ return false; -+ } -+ return true; -+} -+ -+Wallet::BackgroundSyncType WalletImpl::getBackgroundSyncType() const -+{ -+ switch (m_wallet->background_sync_type()) -+ { -+ case tools::wallet2::BackgroundSyncOff: return Wallet::BackgroundSync_Off; -+ case tools::wallet2::BackgroundSyncReusePassword: return Wallet::BackgroundSync_ReusePassword; -+ case tools::wallet2::BackgroundSyncCustomPassword: return Wallet::BackgroundSync_CustomPassword; -+ default: setStatusError(tr("Unknown background sync type")); return Wallet::BackgroundSync_Off; -+ } -+} -+ -+bool WalletImpl::startBackgroundSync() -+{ -+ try -+ { -+ PRE_VALIDATE_BACKGROUND_SYNC(); -+ LOCK_REFRESH(); -+ m_wallet->start_background_sync(); -+ } -+ catch (const std::exception &e) -+ { -+ LOG_ERROR("Failed to start background sync: " << e.what()); -+ setStatusError(string(tr("Failed to start background sync: ")) + e.what()); -+ return false; -+ } -+ return true; -+} -+ -+bool WalletImpl::stopBackgroundSync(const std::string &wallet_password) -+{ -+ try -+ { -+ PRE_VALIDATE_BACKGROUND_SYNC(); -+ LOCK_REFRESH(); -+ m_wallet->stop_background_sync(epee::wipeable_string(wallet_password)); -+ } -+ catch (const std::exception &e) -+ { -+ LOG_ERROR("Failed to stop background sync: " << e.what()); -+ setStatusError(string(tr("Failed to stop background sync: ")) + e.what()); -+ return false; -+ } -+ return true; -+} -+ - void WalletImpl::addSubaddressAccount(const std::string& label) - { -+ if (checkBackgroundSync("cannot add account")) -+ return; - m_wallet->add_subaddress_account(label); - } - size_t WalletImpl::numSubaddressAccounts() const -@@ -1336,10 +1472,14 @@ size_t WalletImpl::numSubaddresses(uint32_t accountIndex) const - } - void WalletImpl::addSubaddress(uint32_t accountIndex, const std::string& label) - { -+ if (checkBackgroundSync("cannot add subbaddress")) -+ return; - m_wallet->add_subaddress(accountIndex, label); - } - std::string WalletImpl::getSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex) const - { -+ if (checkBackgroundSync("cannot get subbaddress label")) -+ return ""; - try - { - return m_wallet->get_subaddress_label({accountIndex, addressIndex}); -@@ -1353,6 +1493,8 @@ std::string WalletImpl::getSubaddressLabel(uint32_t accountIndex, uint32_t addre - } - void WalletImpl::setSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex, const std::string &label) - { -+ if (checkBackgroundSync("cannot set subbaddress label")) -+ return; - try - { - return m_wallet->set_subaddress_label({accountIndex, addressIndex}, label); -@@ -1366,12 +1508,16 @@ void WalletImpl::setSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex - - MultisigState WalletImpl::multisig() const { - MultisigState state; -+ if (checkBackgroundSync("cannot use multisig")) -+ return state; - state.isMultisig = m_wallet->multisig(&state.isReady, &state.threshold, &state.total); - - return state; - } - - string WalletImpl::getMultisigInfo() const { -+ if (checkBackgroundSync("cannot use multisig")) -+ return string(); - try { - clearStatus(); - return m_wallet->get_multisig_first_kex_msg(); -@@ -1384,6 +1530,8 @@ string WalletImpl::getMultisigInfo() const { - } - - string WalletImpl::makeMultisig(const vector& info, const uint32_t threshold) { -+ if (checkBackgroundSync("cannot make multisig")) -+ return string(); - try { - clearStatus(); - -@@ -1524,6 +1672,9 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector extra; - std::string extra_nonce; - vector dsts; -@@ -1690,6 +1841,9 @@ PendingTransaction *WalletImpl::createSweepUnmixableTransaction() - PendingTransactionImpl * transaction = new PendingTransactionImpl(*this); - - do { -+ if (checkBackgroundSync("cannot sweep")) -+ break; -+ - try { - transaction->m_pending_tx = m_wallet->create_unmixable_sweep_transactions(); - pendingTxPostProcess(transaction); -@@ -1823,11 +1977,15 @@ uint32_t WalletImpl::defaultMixin() const - - void WalletImpl::setDefaultMixin(uint32_t arg) - { -+ if (checkBackgroundSync("cannot set default mixin")) -+ return; - m_wallet->default_mixin(arg); - } - - bool WalletImpl::setCacheAttribute(const std::string &key, const std::string &val) - { -+ if (checkBackgroundSync("cannot set cache attribute")) -+ return false; - m_wallet->set_attribute(key, val); - return true; - } -@@ -1841,6 +1999,8 @@ std::string WalletImpl::getCacheAttribute(const std::string &key) const - - bool WalletImpl::setUserNote(const std::string &txid, const std::string ¬e) - { -+ if (checkBackgroundSync("cannot set user note")) -+ return false; - cryptonote::blobdata txid_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(txid, txid_data) || txid_data.size() != sizeof(crypto::hash)) - return false; -@@ -1852,6 +2012,8 @@ bool WalletImpl::setUserNote(const std::string &txid, const std::string ¬e) - - std::string WalletImpl::getUserNote(const std::string &txid) const - { -+ if (checkBackgroundSync("cannot get user note")) -+ return ""; - cryptonote::blobdata txid_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(txid, txid_data) || txid_data.size() != sizeof(crypto::hash)) - return ""; -@@ -1862,6 +2024,9 @@ std::string WalletImpl::getUserNote(const std::string &txid) const - - std::string WalletImpl::getTxKey(const std::string &txid_str) const - { -+ if (checkBackgroundSync("cannot get tx key")) -+ return ""; -+ - crypto::hash txid; - if(!epee::string_tools::hex_to_pod(txid_str, txid)) - { -@@ -1946,6 +2111,9 @@ bool WalletImpl::checkTxKey(const std::string &txid_str, std::string tx_key_str, - - std::string WalletImpl::getTxProof(const std::string &txid_str, const std::string &address_str, const std::string &message) const - { -+ if (checkBackgroundSync("cannot get tx proof")) -+ return ""; -+ - crypto::hash txid; - if (!epee::string_tools::hex_to_pod(txid_str, txid)) - { -@@ -2002,6 +2170,9 @@ bool WalletImpl::checkTxProof(const std::string &txid_str, const std::string &ad - } - - std::string WalletImpl::getSpendProof(const std::string &txid_str, const std::string &message) const { -+ if (checkBackgroundSync("cannot get spend proof")) -+ return ""; -+ - crypto::hash txid; - if(!epee::string_tools::hex_to_pod(txid_str, txid)) - { -@@ -2044,6 +2215,9 @@ bool WalletImpl::checkSpendProof(const std::string &txid_str, const std::string - } - - std::string WalletImpl::getReserveProof(bool all, uint32_t account_index, uint64_t amount, const std::string &message) const { -+ if (checkBackgroundSync("cannot get reserve proof")) -+ return ""; -+ - try - { - clearStatus(); -@@ -2090,6 +2264,9 @@ bool WalletImpl::checkReserveProof(const std::string &address, const std::string - - std::string WalletImpl::signMessage(const std::string &message, const std::string &address) - { -+ if (checkBackgroundSync("cannot sign message")) -+ return ""; -+ - if (address.empty()) { - return m_wallet->sign(message, tools::wallet2::sign_with_spend_key); - } -@@ -2217,6 +2394,16 @@ bool WalletImpl::isDeterministic() const - return m_wallet->is_deterministic(); - } - -+bool WalletImpl::isBackgroundSyncing() const -+{ -+ return m_wallet->is_background_syncing(); -+} -+ -+bool WalletImpl::isBackgroundWallet() const -+{ -+ return m_wallet->is_background_wallet(); -+} -+ - void WalletImpl::clearStatus() const - { - boost::lock_guard l(m_statusMutex); -@@ -2285,9 +2472,7 @@ void WalletImpl::doRefresh() - if(rescan) - m_wallet->rescan_blockchain(false); - m_wallet->refresh(trustedDaemon()); -- if (!m_synchronized) { -- m_synchronized = true; -- } -+ m_synchronized = m_wallet->is_synced(); - // assuming if we have empty history, it wasn't initialized yet - // for further history changes client need to update history in - // "on_money_received" and "on_money_sent" callbacks -@@ -2391,6 +2576,24 @@ bool WalletImpl::doInit(const string &daemon_address, const std::string &proxy_a - return true; - } - -+bool WalletImpl::checkBackgroundSync(const std::string &message) const -+{ -+ clearStatus(); -+ if (m_wallet->is_background_wallet()) -+ { -+ LOG_ERROR("Background wallets " + message); -+ setStatusError(tr("Background wallets ") + message); -+ return true; -+ } -+ if (m_wallet->is_background_syncing()) -+ { -+ LOG_ERROR(message + " while background syncing"); -+ setStatusError(message + tr(" while background syncing. Stop background syncing first.")); -+ return true; -+ } -+ return false; -+} -+ - bool WalletImpl::parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector &unknown_parameters, std::string &error) - { - return m_wallet->parse_uri(uri, address, payment_id, amount, tx_description, recipient_name, unknown_parameters, error); -@@ -2409,6 +2612,8 @@ std::string WalletImpl::getDefaultDataDir() const - bool WalletImpl::rescanSpent() - { - clearStatus(); -+ if (checkBackgroundSync("cannot rescan spent")) -+ return false; - if (!trustedDaemon()) { - setStatusError(tr("Rescan spent can only be used with a trusted daemon")); - return false; -diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h -index ec2d7e9..1f199a7 100644 ---- a/src/wallet/api/wallet.h -+++ b/src/wallet/api/wallet.h -@@ -171,6 +171,13 @@ public: - bool importOutputs(const std::string &filename) override; - bool scanTransactions(const std::vector &txids) override; - -+ bool setupBackgroundSync(const BackgroundSyncType background_sync_type, const std::string &wallet_password, const optional &background_cache_password = optional()) override; -+ BackgroundSyncType getBackgroundSyncType() const override; -+ bool startBackgroundSync() override; -+ bool stopBackgroundSync(const std::string &wallet_password) override; -+ bool isBackgroundSyncing() const override; -+ bool isBackgroundWallet() const override; -+ - virtual void disposeTransaction(PendingTransaction * t) override; - virtual uint64_t estimateTransactionFee(const std::vector> &destinations, - PendingTransaction::Priority priority) const override; -@@ -239,6 +246,7 @@ private: - bool isNewWallet() const; - void pendingTxPostProcess(PendingTransactionImpl * pending); - bool doInit(const std::string &daemon_address, const std::string &proxy_address, uint64_t upper_transaction_size_limit = 0, bool ssl = false); -+ bool checkBackgroundSync(const std::string &message) const; - - private: - friend class PendingTransactionImpl; -@@ -253,6 +261,10 @@ private: - mutable boost::mutex m_statusMutex; - mutable int m_status; - mutable std::string m_errorString; -+ // TODO: harden password handling in the wallet API, see relevant discussion -+ // https://github.com/monero-project/monero-gui/issues/1537 -+ // https://github.com/feather-wallet/feather/issues/72#issuecomment-1405602142 -+ // https://github.com/monero-project/monero/pull/8619#issuecomment-1632951461 - std::string m_password; - std::unique_ptr m_history; - std::unique_ptr m_wallet2Callback; -diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h -index 71991df..e349df1 100644 ---- a/src/wallet/api/wallet2_api.h -+++ b/src/wallet/api/wallet2_api.h -@@ -445,6 +445,12 @@ struct Wallet - ConnectionStatus_WrongVersion - }; - -+ enum BackgroundSyncType { -+ BackgroundSync_Off = 0, -+ BackgroundSync_ReusePassword = 1, -+ BackgroundSync_CustomPassword = 2 -+ }; -+ - virtual ~Wallet() = 0; - virtual std::string seed(const std::string& seed_offset = "") const = 0; - virtual std::string getSeedLanguage() const = 0; -@@ -936,6 +942,42 @@ struct Wallet - */ - virtual bool scanTransactions(const std::vector &txids) = 0; - -+ /*! -+ * \brief setupBackgroundSync - setup background sync mode with just a view key -+ * \param background_sync_type - the mode the wallet background syncs in -+ * \param wallet_password -+ * \param background_cache_password - custom password to encrypt background cache, only needed for custom password background sync type -+ * \return - true on success -+ */ -+ virtual bool setupBackgroundSync(const BackgroundSyncType background_sync_type, const std::string &wallet_password, const optional &background_cache_password) = 0; -+ -+ /*! -+ * \brief getBackgroundSyncType - get mode the wallet background syncs in -+ * \return - the type, or off if type is unknown -+ */ -+ virtual BackgroundSyncType getBackgroundSyncType() const = 0; -+ -+ /** -+ * @brief startBackgroundSync - sync the chain in the background with just view key -+ */ -+ virtual bool startBackgroundSync() = 0; -+ -+ /** -+ * @brief stopBackgroundSync - bring back spend key and process background synced txs -+ * \param wallet_password -+ */ -+ virtual bool stopBackgroundSync(const std::string &wallet_password) = 0; -+ -+ /** -+ * @brief isBackgroundSyncing - returns true if the wallet is background syncing -+ */ -+ virtual bool isBackgroundSyncing() const = 0; -+ -+ /** -+ * @brief isBackgroundWallet - returns true if the wallet is a background wallet -+ */ -+ virtual bool isBackgroundWallet() const = 0; -+ - virtual TransactionHistory * history() = 0; - virtual AddressBook * addressBook() = 0; - virtual Subaddress * subaddress() = 0; -diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp -index ad8c361..8146014 100644 ---- a/src/wallet/wallet2.cpp -+++ b/src/wallet/wallet2.cpp -@@ -157,6 +157,8 @@ static const std::string MULTISIG_SIGNATURE_MAGIC = "SigMultisigPkV1"; - - static const std::string ASCII_OUTPUT_MAGIC = "MoneroAsciiDataV1"; - -+static const std::string BACKGROUND_WALLET_SUFFIX = ".background"; -+ - boost::mutex tools::wallet2::default_daemon_address_lock; - std::string tools::wallet2::default_daemon_address = ""; - -@@ -1009,14 +1011,14 @@ uint64_t num_priv_multisig_keys_post_setup(uint64_t threshold, uint64_t total) - * @param keys_data_key the chacha key that encrypts wallet keys files - * @return crypto::chacha_key the chacha key that encrypts the wallet cache files - */ --crypto::chacha_key derive_cache_key(const crypto::chacha_key& keys_data_key) -+crypto::chacha_key derive_cache_key(const crypto::chacha_key& keys_data_key, const unsigned char domain_separator) - { - static_assert(HASH_SIZE == sizeof(crypto::chacha_key), "Mismatched sizes of hash and chacha key"); - - crypto::chacha_key cache_key; - epee::mlocked> cache_key_data; - memcpy(cache_key_data.data(), &keys_data_key, HASH_SIZE); -- cache_key_data[HASH_SIZE] = config::HASH_KEY_WALLET_CACHE; -+ cache_key_data[HASH_SIZE] = domain_separator; - cn_fast_hash(cache_key_data.data(), HASH_SIZE+1, (crypto::hash&) cache_key); - - return cache_key; -@@ -1104,7 +1106,7 @@ wallet_keys_unlocker::wallet_keys_unlocker(wallet2 &w, const boost::optional lock(lockers_lock); - if (lockers++ > 0) - locked = false; -- if (!locked || w.is_unattended() || w.ask_password() != tools::wallet2::AskPasswordToDecrypt || w.watch_only()) -+ if (!locked || w.is_unattended() || w.ask_password() != tools::wallet2::AskPasswordToDecrypt || w.watch_only() || w.is_background_syncing()) - { - locked = false; - return; -@@ -1221,6 +1223,11 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended, std - m_ignore_outputs_above(MONEY_SUPPLY), - m_ignore_outputs_below(0), - m_track_uses(false), -+ m_is_background_wallet(false), -+ m_background_sync_type(BackgroundSyncOff), -+ m_background_syncing(false), -+ m_processing_background_cache(false), -+ m_custom_background_key(boost::none), - m_show_wallet_name_when_locked(false), - m_inactivity_lock_timeout(DEFAULT_INACTIVITY_LOCK_TIMEOUT), - m_setup_background_mining(BackgroundMiningMaybe), -@@ -1860,6 +1867,9 @@ bool has_nonrequested_tx_at_height_or_above_requested(uint64_t height, const std - //---------------------------------------------------------------------------------------------------- - void wallet2::scan_tx(const std::unordered_set &txids) - { -+ THROW_WALLET_EXCEPTION_IF(m_background_syncing || m_is_background_wallet, error::wallet_internal_error, -+ "cannot scan tx from background wallet"); -+ - // Get the transactions from daemon in batches sorted lowest height to highest - tx_entry_data txs_to_scan = get_tx_entries(txids); - if (txs_to_scan.tx_entries.empty()) -@@ -2167,11 +2177,11 @@ void wallet2::scan_output(const cryptonote::transaction &tx, bool miner_tx, cons - THROW_WALLET_EXCEPTION_IF(i >= tx.vout.size(), error::wallet_internal_error, "Invalid vout index"); - - // if keys are encrypted, ask for password -- if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only && !m_multisig_rescan_k) -+ if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only && !m_multisig_rescan_k && !m_background_syncing) - { - static critical_section password_lock; - CRITICAL_REGION_LOCAL(password_lock); -- if (!m_encrypt_keys_after_refresh) -+ if (!m_encrypt_keys_after_refresh && !m_processing_background_cache) - { - boost::optional pwd = m_callback->on_get_password(pool ? "output found in pool" : "output received"); - THROW_WALLET_EXCEPTION_IF(!pwd, error::password_needed, tr("Password is needed to compute key image for incoming monero")); -@@ -2183,7 +2193,7 @@ void wallet2::scan_output(const cryptonote::transaction &tx, bool miner_tx, cons - crypto::public_key output_public_key; - THROW_WALLET_EXCEPTION_IF(!get_output_public_key(tx.vout[i], output_public_key), error::wallet_internal_error, "Failed to get output public key"); - -- if (m_multisig) -+ if (m_multisig || m_background_syncing/*no spend key*/) - { - tx_scan_info.in_ephemeral.pub = output_public_key; - tx_scan_info.in_ephemeral.sec = crypto::null_skey; -@@ -2440,6 +2450,22 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote - THROW_WALLET_EXCEPTION_IF(tx.vout.size() != o_indices.size(), error::wallet_internal_error, - "transactions outputs size=" + std::to_string(tx.vout.size()) + - " not match with daemon response size=" + std::to_string(o_indices.size())); -+ -+ // we're going to re-process this receive when background sync is disabled -+ if (m_background_syncing && m_background_sync_data.txs.find(txid) == m_background_sync_data.txs.end()) -+ { -+ size_t bgs_idx = m_background_sync_data.txs.size(); -+ background_synced_tx_t bgs_tx = { -+ .index_in_background_sync_data = bgs_idx, -+ .tx = tx, -+ .output_indices = o_indices, -+ .height = height, -+ .block_timestamp = ts, -+ .double_spend_seen = double_spend_seen -+ }; -+ LOG_PRINT_L2("Adding received tx " << txid << " to background sync data (idx=" << bgs_idx << ")"); -+ m_background_sync_data.txs.insert({txid, std::move(bgs_tx)}); -+ } - } - - for(size_t o: outs) -@@ -2465,7 +2491,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote - td.m_tx = (const cryptonote::transaction_prefix&)tx; - td.m_txid = txid; - td.m_key_image = tx_scan_info[o].ki; -- td.m_key_image_known = !m_watch_only && !m_multisig; -+ td.m_key_image_known = !m_watch_only && !m_multisig && !m_background_syncing; - if (!td.m_key_image_known) - { - // we might have cold signed, and have a mapping to key images -@@ -2655,10 +2681,25 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote - set_spent(it->second, height); - if (!ignore_callbacks && 0 != m_callback) - m_callback->on_money_spent(height, txid, tx, amount, tx, td.m_subaddr_index); -+ -+ if (m_background_syncing && m_background_sync_data.txs.find(txid) == m_background_sync_data.txs.end()) -+ { -+ size_t bgs_idx = m_background_sync_data.txs.size(); -+ background_synced_tx_t bgs_tx = { -+ .index_in_background_sync_data = bgs_idx, -+ .tx = tx, -+ .output_indices = o_indices, -+ .height = height, -+ .block_timestamp = ts, -+ .double_spend_seen = double_spend_seen -+ }; -+ LOG_PRINT_L2("Adding spent tx " << txid << " to background sync data (idx=" << bgs_idx << ")"); -+ m_background_sync_data.txs.insert({txid, std::move(bgs_tx)}); -+ } - } - } - -- if (!pool && m_track_uses) -+ if (!pool && (m_track_uses || (m_background_syncing && it == m_key_images.end()))) - { - PERF_TIMER(track_uses); - const uint64_t amount = in_to_key.amount; -@@ -2672,7 +2713,27 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote - { - size_t idx = i->second; - THROW_WALLET_EXCEPTION_IF(idx >= m_transfers.size(), error::wallet_internal_error, "Output tracker cache index out of range"); -- m_transfers[idx].m_uses.push_back(std::make_pair(height, txid)); -+ -+ if (m_track_uses) -+ m_transfers[idx].m_uses.push_back(std::make_pair(height, txid)); -+ -+ // We'll re-process all txs which *might* be spends when we disable -+ // background sync and retrieve the spend key. We don't know if an -+ // output is a spend in this tx if we don't know its key image. -+ if (m_background_syncing && !m_transfers[idx].m_key_image_known && m_background_sync_data.txs.find(txid) == m_background_sync_data.txs.end()) -+ { -+ size_t bgs_idx = m_background_sync_data.txs.size(); -+ background_synced_tx_t bgs_tx = { -+ .index_in_background_sync_data = bgs_idx, -+ .tx = tx, -+ .output_indices = o_indices, -+ .height = height, -+ .block_timestamp = ts, -+ .double_spend_seen = double_spend_seen -+ }; -+ LOG_PRINT_L2("Adding plausible spent tx " << txid << " to background sync data (idx=" << bgs_idx << ")"); -+ m_background_sync_data.txs.insert({txid, std::move(bgs_tx)}); -+ } - } - } - } -@@ -2682,7 +2743,24 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote - continue; - for (uint64_t offset: offsets) - if (offset == td.m_global_output_index) -- td.m_uses.push_back(std::make_pair(height, txid)); -+ { -+ if (m_track_uses) -+ td.m_uses.push_back(std::make_pair(height, txid)); -+ if (m_background_syncing && !td.m_key_image_known && m_background_sync_data.txs.find(txid) == m_background_sync_data.txs.end()) -+ { -+ size_t bgs_idx = m_background_sync_data.txs.size(); -+ background_synced_tx_t bgs_tx = { -+ .index_in_background_sync_data = bgs_idx, -+ .tx = tx, -+ .output_indices = o_indices, -+ .height = height, -+ .block_timestamp = ts, -+ .double_spend_seen = double_spend_seen -+ }; -+ LOG_PRINT_L2("Adding plausible spent tx " << txid << " to background sync data (idx=" << bgs_idx << ")"); -+ m_background_sync_data.txs.insert({txid, std::move(bgs_tx)}); -+ } -+ } - } - } - } -@@ -3055,8 +3133,8 @@ void wallet2::pull_blocks(bool first, bool try_incremental, uint64_t start_heigh - req.start_height = start_height; - req.no_miner_tx = m_refresh_type == RefreshNoCoinbase; - -- req.requested_info = first ? COMMAND_RPC_GET_BLOCKS_FAST::BLOCKS_AND_POOL : COMMAND_RPC_GET_BLOCKS_FAST::BLOCKS_ONLY; -- if (try_incremental) -+ req.requested_info = (first && !m_background_syncing) ? COMMAND_RPC_GET_BLOCKS_FAST::BLOCKS_AND_POOL : COMMAND_RPC_GET_BLOCKS_FAST::BLOCKS_ONLY; -+ if (try_incremental && !m_background_syncing) - req.pool_info_since = m_pool_info_query_time; - - { -@@ -3083,7 +3161,7 @@ void wallet2::pull_blocks(bool first, bool try_incremental, uint64_t start_heigh - << ", height " << blocks_start_height + blocks.size() << ", node height " << res.current_height - << ", pool info " << static_cast(res.pool_info_extent)); - -- if (first) -+ if (first && !m_background_syncing) - { - if (res.pool_info_extent != COMMAND_RPC_GET_BLOCKS_FAST::NONE) - { -@@ -3595,6 +3673,9 @@ void wallet2::process_unconfirmed_transfer(bool incremental, const crypto::hash - // incremental update anymore, because with that we might miss some txs altogether. - void wallet2::update_pool_state(std::vector> &process_txs, bool refreshed, bool try_incremental) - { -+ process_txs.clear(); -+ if (m_background_syncing) -+ return; - bool updated = false; - if (m_pool_info_query_time != 0 && try_incremental) - { -@@ -4166,6 +4247,8 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo - } - - m_first_refresh_done = true; -+ if (m_background_syncing || m_is_background_wallet) -+ m_background_sync_data.first_refresh_done = true; - - LOG_PRINT_L1("Refresh done, blocks received: " << blocks_fetched << ", balance (all accounts): " << print_money(balance_all(false)) << ", unlocked: " << print_money(unlocked_balance_all(false))); - } -@@ -4251,6 +4334,14 @@ wallet2::detached_blockchain_data wallet2::detach_blockchain(uint64_t height, st - td.m_uses.pop_back(); - } - -+ for (auto it = m_background_sync_data.txs.begin(); it != m_background_sync_data.txs.end(); ) -+ { -+ if(height <= it->second.height) -+ it = m_background_sync_data.txs.erase(it); -+ else -+ ++it; -+ } -+ - if (output_tracker_cache) - output_tracker_cache->clear(); - -@@ -4325,8 +4416,12 @@ void wallet2::handle_reorg(uint64_t height, std::map m_blockchain.offset(), - error::wallet_internal_error, "Daemon claims reorg below last checkpoint"); -+ - detached_blockchain_data dbd = detach_blockchain(height, output_tracker_cache); - -+ if (m_background_syncing && height < m_background_sync_data.start_height) -+ m_background_sync_data.start_height = height; -+ - if (m_callback) - m_callback->on_reorg(height, dbd.detached_blockchain.size(), dbd.detached_tx_hashes.size()); - } -@@ -4336,6 +4431,7 @@ bool wallet2::deinit() - if(m_is_initialized) { - m_is_initialized = false; - unlock_keys_file(); -+ unlock_background_keys_file(); - m_account.deinit(); - } - return true; -@@ -4362,6 +4458,7 @@ bool wallet2::clear() - m_device_last_key_image_sync = 0; - m_pool_info_query_time = 0; - m_skip_to_height = 0; -+ m_background_sync_data = background_sync_data_t{}; - return true; - } - //---------------------------------------------------------------------------------------------------- -@@ -4380,13 +4477,30 @@ void wallet2::clear_soft(bool keep_key_images) - m_scanned_pool_txs[1].clear(); - m_pool_info_query_time = 0; - m_skip_to_height = 0; -+ m_background_sync_data = background_sync_data_t{}; - - cryptonote::block b; - generate_genesis(b); - m_blockchain.push_back(get_block_hash(b)); - m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx); - } -- -+//---------------------------------------------------------------------------------------------------- -+void wallet2::clear_user_data() -+{ -+ for (auto i = m_confirmed_txs.begin(); i != m_confirmed_txs.end(); ++i) -+ i->second.m_dests.clear(); -+ for (auto i = m_unconfirmed_txs.begin(); i != m_unconfirmed_txs.end(); ++i) -+ i->second.m_dests.clear(); -+ for (auto i = m_transfers.begin(); i != m_transfers.end(); ++i) -+ i->m_frozen = false; -+ m_tx_keys.clear(); -+ m_tx_notes.clear(); -+ m_address_book.clear(); -+ m_subaddress_labels.clear(); -+ m_attributes.clear(); -+ m_account_tags = std::pair, std::vector>(); -+} -+//---------------------------------------------------------------------------------------------------- - /*! - * \brief Stores wallet information to wallet file. - * \param keys_file_name Name of wallet file -@@ -4398,16 +4512,35 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable - { - boost::optional keys_file_data = get_keys_file_data(password, watch_only); - CHECK_AND_ASSERT_MES(keys_file_data != boost::none, false, "failed to generate wallet keys data"); -- -+ return store_keys_file_data(keys_file_name, keys_file_data.get()); -+} -+//---------------------------------------------------------------------------------------------------- -+bool wallet2::store_keys(const std::string& keys_file_name, const crypto::chacha_key& key, bool watch_only, bool background_keys_file) -+{ -+ boost::optional keys_file_data = get_keys_file_data(key, watch_only, background_keys_file); -+ CHECK_AND_ASSERT_MES(keys_file_data != boost::none, false, "failed to generate wallet keys data"); -+ return store_keys_file_data(keys_file_name, keys_file_data.get(), background_keys_file); -+} -+//---------------------------------------------------------------------------------------------------- -+bool wallet2::store_keys_file_data(const std::string& keys_file_name, wallet2::keys_file_data &keys_file_data, bool background_keys_file) -+{ - std::string tmp_file_name = keys_file_name + ".new"; - std::string buf; -- bool r = ::serialization::dump_binary(keys_file_data.get(), buf); -+ bool r = ::serialization::dump_binary(keys_file_data, buf); - r = r && save_to_file(tmp_file_name, buf); - CHECK_AND_ASSERT_MES(r, false, "failed to generate wallet keys file " << tmp_file_name); - -- unlock_keys_file(); -+ if (!background_keys_file) -+ unlock_keys_file(); -+ else -+ unlock_background_keys_file(); -+ - std::error_code e = tools::replace_file(tmp_file_name, keys_file_name); -- lock_keys_file(); -+ -+ if (!background_keys_file) -+ lock_keys_file(); -+ else -+ lock_background_keys_file(keys_file_name); - - if (e) { - boost::filesystem::remove(tmp_file_name); -@@ -4419,26 +4552,27 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable - } - //---------------------------------------------------------------------------------------------------- - boost::optional wallet2::get_keys_file_data(const epee::wipeable_string& password, bool watch_only) -+{ -+ crypto::chacha_key key; -+ crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds); -+ verify_password_with_cached_key(key); -+ return get_keys_file_data(key, watch_only); -+} -+//---------------------------------------------------------------------------------------------------- -+boost::optional wallet2::get_keys_file_data(const crypto::chacha_key& key, bool watch_only, bool background_keys_file) - { - epee::byte_slice account_data; - std::string multisig_signers; - std::string multisig_derivations; - cryptonote::account_base account = m_account; - -- crypto::chacha_key key; -- crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds); -- -- // We use m_cache_key as a deterministic test to see if given key corresponds to original password -- const crypto::chacha_key cache_key = derive_cache_key(key); -- THROW_WALLET_EXCEPTION_IF(cache_key != m_cache_key, error::invalid_password); -- - if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only) - { - account.encrypt_viewkey(key); - account.decrypt_keys(key); - } - -- if (watch_only) -+ if (watch_only || background_keys_file) - account.forget_spend_key(); - - account.encrypt_keys(key); -@@ -4573,6 +4707,9 @@ boost::optional wallet2::get_keys_file_data(const epee: - value2.SetInt(m_track_uses ? 1 : 0); - json.AddMember("track_uses", value2, json.GetAllocator()); - -+ value2.SetInt(m_background_sync_type); -+ json.AddMember("background_sync_type", value2, json.GetAllocator()); -+ - value2.SetInt(m_show_wallet_name_when_locked ? 1 : 0); - json.AddMember("show_wallet_name_when_locked", value2, json.GetAllocator()); - -@@ -4630,6 +4767,12 @@ boost::optional wallet2::get_keys_file_data(const epee: - value2.SetInt(m_enable_multisig ? 1 : 0); - json.AddMember("enable_multisig", value2, json.GetAllocator()); - -+ if (m_background_sync_type == BackgroundSyncCustomPassword && !background_keys_file && m_custom_background_key) -+ { -+ value.SetString(reinterpret_cast(m_custom_background_key.get().data()), m_custom_background_key.get().size()); -+ json.AddMember("custom_background_key", value, json.GetAllocator()); -+ } -+ - // Serialize the JSON object - rapidjson::StringBuffer buffer; - rapidjson::Writer writer(buffer); -@@ -4656,13 +4799,81 @@ void wallet2::setup_keys(const epee::wipeable_string &password) - m_account.decrypt_viewkey(key); - } - -- m_cache_key = derive_cache_key(key); -+ m_cache_key = derive_cache_key(key, config::HASH_KEY_WALLET_CACHE); - - get_ringdb_key(); - } - //---------------------------------------------------------------------------------------------------- -+void validate_background_cache_password_usage(const tools::wallet2::BackgroundSyncType background_sync_type, const boost::optional &background_cache_password, const bool multisig, const bool watch_only, const bool key_on_device) -+{ -+ THROW_WALLET_EXCEPTION_IF(multisig || watch_only || key_on_device, error::wallet_internal_error, multisig -+ ? "Background sync not implemented for multisig wallets" : watch_only -+ ? "Background sync not implemented for view only wallets" -+ : "Background sync not implemented for HW wallets"); -+ -+ switch (background_sync_type) -+ { -+ case tools::wallet2::BackgroundSyncOff: -+ { -+ THROW_WALLET_EXCEPTION(error::wallet_internal_error, "background sync is not enabled"); -+ break; -+ } -+ case tools::wallet2::BackgroundSyncReusePassword: -+ { -+ THROW_WALLET_EXCEPTION_IF(background_cache_password, error::wallet_internal_error, -+ "unexpected custom background cache password"); -+ break; -+ } -+ case tools::wallet2::BackgroundSyncCustomPassword: -+ { -+ THROW_WALLET_EXCEPTION_IF(!background_cache_password, error::wallet_internal_error, -+ "expected custom background cache password"); -+ break; -+ } -+ default: THROW_WALLET_EXCEPTION(error::wallet_internal_error, "unknown background sync type"); -+ } -+} -+//---------------------------------------------------------------------------------------------------- -+void get_custom_background_key(const epee::wipeable_string &password, crypto::chacha_key &custom_background_key, const uint64_t kdf_rounds) -+{ -+ crypto::chacha_key key; -+ crypto::generate_chacha_key(password.data(), password.size(), key, kdf_rounds); -+ custom_background_key = derive_cache_key(key, config::HASH_KEY_BACKGROUND_KEYS_FILE); -+} -+//---------------------------------------------------------------------------------------------------- -+const crypto::chacha_key wallet2::get_cache_key() -+{ -+ if (m_background_sync_type == BackgroundSyncCustomPassword && m_background_syncing) -+ { -+ THROW_WALLET_EXCEPTION_IF(!m_custom_background_key, error::wallet_internal_error, "Custom background key not set"); -+ // Domain separate keys used to encrypt background keys file and cache -+ return derive_cache_key(m_custom_background_key.get(), config::HASH_KEY_BACKGROUND_CACHE); -+ } -+ else -+ { -+ return m_cache_key; -+ } -+} -+//---------------------------------------------------------------------------------------------------- -+void wallet2::verify_password_with_cached_key(const epee::wipeable_string &password) -+{ -+ crypto::chacha_key key; -+ crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds); -+ verify_password_with_cached_key(key); -+} -+//---------------------------------------------------------------------------------------------------- -+void wallet2::verify_password_with_cached_key(const crypto::chacha_key &key) -+{ -+ // We use m_cache_key as a deterministic test to see if given key corresponds to original password -+ const crypto::chacha_key cache_key = derive_cache_key(key, config::HASH_KEY_WALLET_CACHE); -+ THROW_WALLET_EXCEPTION_IF(cache_key != m_cache_key, error::invalid_password); -+} -+//---------------------------------------------------------------------------------------------------- - void wallet2::change_password(const std::string &filename, const epee::wipeable_string &original_password, const epee::wipeable_string &new_password) - { -+ THROW_WALLET_EXCEPTION_IF(m_background_syncing || m_is_background_wallet, error::wallet_internal_error, -+ "cannot change password from background wallet"); -+ - if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only) - decrypt_keys(original_password); - setup_keys(new_password); -@@ -4721,8 +4932,24 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st - std::string account_data; - account_data.resize(keys_file_data.account_data.size()); - crypto::chacha20(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); -- if (json.Parse(account_data.c_str()).HasParseError() || !json.IsObject()) -+ const bool try_v0_format = json.Parse(account_data.c_str()).HasParseError() || !json.IsObject(); -+ if (try_v0_format) - crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); -+ -+ // Check if it's a background keys file if both of the above formats fail -+ { -+ m_is_background_wallet = false; -+ m_background_syncing = false; -+ cryptonote::account_base account_data_check; -+ if (try_v0_format && !epee::serialization::load_t_from_binary(account_data_check, account_data)) -+ { -+ get_custom_background_key(password, key, m_kdf_rounds); -+ crypto::chacha20(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); -+ m_is_background_wallet = !json.Parse(account_data.c_str()).HasParseError() && json.IsObject(); -+ m_background_syncing = m_is_background_wallet; // start a background wallet background syncing -+ } -+ } -+ - // The contents should be JSON if the wallet follows the new format. - if (json.Parse(account_data.c_str()).HasParseError()) - { -@@ -4760,6 +4987,7 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st - m_ignore_outputs_above = MONEY_SUPPLY; - m_ignore_outputs_below = 0; - m_track_uses = false; -+ m_background_sync_type = BackgroundSyncOff; - m_show_wallet_name_when_locked = false; - m_inactivity_lock_timeout = DEFAULT_INACTIVITY_LOCK_TIMEOUT; - m_setup_background_mining = BackgroundMiningMaybe; -@@ -4777,6 +5005,7 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st - m_credits_target = 0; - m_enable_multisig = false; - m_allow_mismatched_daemon_version = false; -+ m_custom_background_key = boost::none; - } - else if(json.IsObject()) - { -@@ -5013,6 +5242,39 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st - m_credits_target = field_credits_target; - GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, enable_multisig, int, Int, false, false); - m_enable_multisig = field_enable_multisig; -+ -+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, background_sync_type, BackgroundSyncType, Int, false, BackgroundSyncOff); -+ m_background_sync_type = field_background_sync_type; -+ -+ // Load encryption key used to encrypt background cache -+ crypto::chacha_key custom_background_key; -+ m_custom_background_key = boost::none; -+ if (m_background_sync_type == BackgroundSyncCustomPassword && !m_is_background_wallet) -+ { -+ if (!json.HasMember("custom_background_key")) -+ { -+ LOG_ERROR("Field custom_background_key not found in JSON"); -+ return false; -+ } -+ else if (!json["custom_background_key"].IsString()) -+ { -+ LOG_ERROR("Field custom_background_key found in JSON, but not String"); -+ return false; -+ } -+ else if (json["custom_background_key"].GetStringLength() != sizeof(crypto::chacha_key)) -+ { -+ LOG_ERROR("Field custom_background_key found in JSON, but not correct length"); -+ return false; -+ } -+ const char *field_custom_background_key = json["custom_background_key"].GetString(); -+ memcpy(custom_background_key.data(), field_custom_background_key, sizeof(crypto::chacha_key)); -+ m_custom_background_key = boost::optional(custom_background_key); -+ LOG_PRINT_L1("Loaded custom background key derived from custom password"); -+ } -+ else if (json.HasMember("custom_background_key")) -+ { -+ LOG_ERROR("Unexpected field custom_background_key found in JSON"); -+ } - } - else - { -@@ -5076,12 +5338,17 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st - const cryptonote::account_keys& keys = m_account.get_keys(); - hw::device &hwdev = m_account.get_device(); - r = r && hwdev.verify_keys(keys.m_view_secret_key, keys.m_account_address.m_view_public_key); -- if (!m_watch_only && !m_multisig && hwdev.device_protocol() != hw::device::PROTOCOL_COLD) -+ if (!m_watch_only && !m_multisig && hwdev.device_protocol() != hw::device::PROTOCOL_COLD && !m_is_background_wallet) - r = r && hwdev.verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spend_public_key); - THROW_WALLET_EXCEPTION_IF(!r, error::wallet_files_doesnt_correspond, m_keys_file, m_wallet_file); - - if (r) -- setup_keys(password); -+ { -+ if (!m_is_background_wallet) -+ setup_keys(password); -+ else -+ m_custom_background_key = boost::optional(key); -+ } - - return true; - } -@@ -5096,11 +5363,12 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st - * can be used prior to rewriting wallet keys file, to ensure user has entered the correct password - * - */ --bool wallet2::verify_password(const epee::wipeable_string& password) -+bool wallet2::verify_password(const epee::wipeable_string& password, crypto::secret_key &spend_key_out) - { - // this temporary unlocking is necessary for Windows (otherwise the file couldn't be loaded). - unlock_keys_file(); -- bool r = verify_password(m_keys_file, password, m_account.get_device().device_protocol() == hw::device::PROTOCOL_COLD || m_watch_only || m_multisig, m_account.get_device(), m_kdf_rounds); -+ const bool no_spend_key = m_account.get_device().device_protocol() == hw::device::PROTOCOL_COLD || m_watch_only || m_multisig || m_is_background_wallet; -+ bool r = verify_password(m_keys_file, password, no_spend_key, m_account.get_device(), m_kdf_rounds, spend_key_out); - lock_keys_file(); - return r; - } -@@ -5118,7 +5386,7 @@ bool wallet2::verify_password(const epee::wipeable_string& password) - * can be used prior to rewriting wallet keys file, to ensure user has entered the correct password - * - */ --bool wallet2::verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds) -+bool wallet2::verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds, crypto::secret_key &spend_key_out) - { - rapidjson::Document json; - wallet2::keys_file_data keys_file_data; -@@ -5135,9 +5403,22 @@ bool wallet2::verify_password(const std::string& keys_file_name, const epee::wip - std::string account_data; - account_data.resize(keys_file_data.account_data.size()); - crypto::chacha20(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); -- if (json.Parse(account_data.c_str()).HasParseError() || !json.IsObject()) -+ const bool try_v0_format = json.Parse(account_data.c_str()).HasParseError() || !json.IsObject(); -+ if (try_v0_format) - crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); - -+ // Check if it's a background keys file if both of the above formats fail -+ { -+ cryptonote::account_base account_data_check; -+ if (try_v0_format && !epee::serialization::load_t_from_binary(account_data_check, account_data)) -+ { -+ get_custom_background_key(password, key, kdf_rounds); -+ crypto::chacha20(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); -+ const bool is_background_wallet = json.Parse(account_data.c_str()).HasParseError() && json.IsObject(); -+ no_spend_key = no_spend_key || is_background_wallet; -+ } -+ } -+ - // The contents should be JSON if the wallet follows the new format. - if (json.Parse(account_data.c_str()).HasParseError()) - { -@@ -5162,6 +5443,7 @@ bool wallet2::verify_password(const std::string& keys_file_name, const epee::wip - r = r && hwdev.verify_keys(keys.m_view_secret_key, keys.m_account_address.m_view_public_key); - if(!no_spend_key) - r = r && hwdev.verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spend_public_key); -+ spend_key_out = (!no_spend_key && r) ? keys.m_spend_secret_key : crypto::null_skey; - return r; - } - -@@ -5173,9 +5455,7 @@ void wallet2::encrypt_keys(const crypto::chacha_key &key) - - void wallet2::decrypt_keys(const crypto::chacha_key &key) - { -- // We use m_cache_key as a deterministic test to see if given key corresponds to original password -- const crypto::chacha_key cache_key = derive_cache_key(key); -- THROW_WALLET_EXCEPTION_IF(cache_key != m_cache_key, error::invalid_password); -+ verify_password_with_cached_key(key); - - m_account.encrypt_viewkey(key); - m_account.decrypt_keys(key); -@@ -5851,11 +6131,30 @@ void wallet2::rewrite(const std::string& wallet_name, const epee::wipeable_strin - { - if (wallet_name.empty()) - return; -+ THROW_WALLET_EXCEPTION_IF(m_background_syncing || m_is_background_wallet, error::wallet_internal_error, -+ "cannot change wallet settings from background wallet"); - prepare_file_names(wallet_name); - boost::system::error_code ignored_ec; - THROW_WALLET_EXCEPTION_IF(!boost::filesystem::exists(m_keys_file, ignored_ec), error::file_not_found, m_keys_file); - bool r = store_keys(m_keys_file, password, m_watch_only); - THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file); -+ -+ // Update the background keys file when we rewrite the main wallet keys file -+ if (m_background_sync_type == BackgroundSyncCustomPassword && m_custom_background_key) -+ { -+ const std::string background_keys_filename = make_background_keys_file_name(wallet_name); -+ if (!lock_background_keys_file(background_keys_filename)) -+ { -+ LOG_ERROR("Background keys file " << background_keys_filename << " is opened by another wallet program and cannot be rewritten"); -+ return; // not fatal, background keys file will just have different wallet settings -+ } -+ store_background_keys(m_custom_background_key.get()); -+ store_background_cache(m_custom_background_key.get(), true/*do_reset_background_sync_data*/); -+ } -+ else if (m_background_sync_type == BackgroundSyncReusePassword) -+ { -+ reset_background_sync_data(m_background_sync_data); -+ } - } - /*! - * \brief Writes to a file named based on the normal wallet (doesn't generate key, assumes it's already there) -@@ -5889,6 +6188,16 @@ bool wallet2::wallet_valid_path_format(const std::string& file_path) - return !file_path.empty(); - } - //---------------------------------------------------------------------------------------------------- -+std::string wallet2::make_background_wallet_file_name(const std::string &wallet_file) -+{ -+ return wallet_file + BACKGROUND_WALLET_SUFFIX; -+} -+//---------------------------------------------------------------------------------------------------- -+std::string wallet2::make_background_keys_file_name(const std::string &wallet_file) -+{ -+ return make_background_wallet_file_name(wallet_file) + ".keys"; -+} -+//---------------------------------------------------------------------------------------------------- - bool wallet2::parse_long_payment_id(const std::string& payment_id_str, crypto::hash& payment_id) - { - cryptonote::blobdata payment_id_data; -@@ -6124,10 +6433,81 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass - THROW_WALLET_EXCEPTION_IF(true, error::file_read_error, "failed to load keys from buffer"); - } - -- wallet_keys_unlocker unlocker(*this, m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only, password); -+ wallet_keys_unlocker unlocker(*this, m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only && !m_is_background_wallet, password); - - //keys loaded ok! - //try to load wallet cache. but even if we failed, it is not big problem -+ load_wallet_cache(use_fs, cache_buf); -+ -+ if (!m_persistent_rpc_client_id) -+ set_rpc_client_secret_key(rct::rct2sk(rct::skGen())); -+ -+ // Wallets used to wipe, but not erase, old unused multisig key info, which lead to huge memory leaks. -+ // Here we erase these multisig keys if they're zero'd out to free up space. -+ for (auto &td : m_transfers) -+ { -+ auto mk_it = td.m_multisig_k.begin(); -+ while (mk_it != td.m_multisig_k.end()) -+ { -+ if (*mk_it == rct::zero()) -+ mk_it = td.m_multisig_k.erase(mk_it); -+ else -+ ++mk_it; -+ } -+ } -+ -+ cryptonote::block genesis; -+ generate_genesis(genesis); -+ crypto::hash genesis_hash = get_block_hash(genesis); -+ -+ if (m_blockchain.empty()) -+ { -+ m_blockchain.push_back(genesis_hash); -+ m_last_block_reward = cryptonote::get_outs_money_amount(genesis.miner_tx); -+ } -+ else -+ { -+ check_genesis(genesis_hash); -+ } -+ -+ trim_hashchain(); -+ -+ if (get_num_subaddress_accounts() == 0) -+ add_subaddress_account(tr("Primary account")); -+ -+ try -+ { -+ find_and_save_rings(false); -+ } -+ catch (const std::exception &e) -+ { -+ MERROR("Failed to save rings, will try again next time"); -+ } -+ -+ try -+ { -+ if (use_fs) -+ m_message_store.read_from_file(get_multisig_wallet_state(), m_mms_file, m_load_deprecated_formats); -+ } -+ catch (const std::exception &e) -+ { -+ MERROR("Failed to initialize MMS, it will be unusable"); -+ } -+ -+ try -+ { -+ if (use_fs) -+ process_background_cache_on_open(); -+ } -+ catch (const std::exception &e) -+ { -+ MERROR("Failed to process background cache on open: " << e.what()); -+ } -+} -+//---------------------------------------------------------------------------------------------------- -+void wallet2::load_wallet_cache(const bool use_fs, const std::string& cache_buf) -+{ -+ boost::system::error_code e; - bool cache_missing = use_fs ? (!boost::filesystem::exists(m_wallet_file, e) || e) : cache_buf.empty(); - if (cache_missing) - { -@@ -6141,7 +6521,7 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass - bool r = true; - if (use_fs) - { -- load_from_file(m_wallet_file, cache_file_buf, std::numeric_limits::max()); -+ r = load_from_file(m_wallet_file, cache_file_buf, std::numeric_limits::max()); - THROW_WALLET_EXCEPTION_IF(!r, error::file_read_error, m_wallet_file); - } - -@@ -6154,7 +6534,7 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass - THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "internal error: failed to deserialize \"" + m_wallet_file + '\"'); - std::string cache_data; - cache_data.resize(cache_file_data.cache_data.size()); -- crypto::chacha20(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), m_cache_key, cache_file_data.iv, &cache_data[0]); -+ crypto::chacha20(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), get_cache_key(), cache_file_data.iv, &cache_data[0]); - - try { - bool loaded = false; -@@ -6244,60 +6624,76 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass - m_account_public_address.m_view_public_key != m_account.get_keys().m_account_address.m_view_public_key, - error::wallet_files_doesnt_correspond, m_keys_file, m_wallet_file); - } -+} -+//---------------------------------------------------------------------------------------------------- -+void wallet2::process_background_cache_on_open() -+{ -+ if (m_wallet_file.empty()) -+ return; -+ if (m_background_syncing || m_is_background_wallet) -+ return; -+ if (m_background_sync_type == BackgroundSyncOff) -+ return; - -- if (!m_persistent_rpc_client_id) -- set_rpc_client_secret_key(rct::rct2sk(rct::skGen())); -+ if (m_background_sync_type == BackgroundSyncReusePassword) -+ { -+ const background_sync_data_t background_sync_data = m_background_sync_data; -+ const hashchain blockchain = m_blockchain; -+ process_background_cache(background_sync_data, blockchain, m_last_block_reward); - -- // Wallets used to wipe, but not erase, old unused multisig key info, which lead to huge memory leaks. -- // Here we erase these multisig keys if they're zero'd out to free up space. -- for (auto &td : m_transfers) -+ // Reset the background cache after processing -+ reset_background_sync_data(m_background_sync_data); -+ } -+ else if (m_background_sync_type == BackgroundSyncCustomPassword) - { -- auto mk_it = td.m_multisig_k.begin(); -- while (mk_it != td.m_multisig_k.end()) -+ // If the background wallet files don't exist, recreate them -+ const std::string background_keys_file = make_background_keys_file_name(m_wallet_file); -+ const std::string background_wallet_file = make_background_wallet_file_name(m_wallet_file); -+ const bool background_keys_file_exists = boost::filesystem::exists(background_keys_file); -+ const bool background_wallet_exists = boost::filesystem::exists(background_wallet_file); -+ -+ THROW_WALLET_EXCEPTION_IF(!lock_background_keys_file(background_keys_file), error::background_wallet_already_open, background_wallet_file); -+ THROW_WALLET_EXCEPTION_IF(!m_custom_background_key, error::wallet_internal_error, "Custom background key not set"); -+ -+ if (!background_keys_file_exists) - { -- if (*mk_it == rct::zero()) -- mk_it = td.m_multisig_k.erase(mk_it); -- else -- ++mk_it; -+ MDEBUG("Background keys file not found, restoring"); -+ store_background_keys(m_custom_background_key.get()); - } -- } - -- cryptonote::block genesis; -- generate_genesis(genesis); -- crypto::hash genesis_hash = get_block_hash(genesis); -+ if (!background_wallet_exists) -+ { -+ MDEBUG("Background cache not found, restoring"); -+ store_background_cache(m_custom_background_key.get(), true/*do_reset_background_sync_data*/); -+ return; -+ } - -- if (m_blockchain.empty()) -- { -- m_blockchain.push_back(genesis_hash); -- m_last_block_reward = cryptonote::get_outs_money_amount(genesis.miner_tx); -- } -- else -- { -- check_genesis(genesis_hash); -- } -+ MDEBUG("Loading background cache"); - -- trim_hashchain(); -+ // Set up a minimal background wallet2 instance -+ std::unique_ptr background_w2(new wallet2(m_nettype)); -+ background_w2->m_is_background_wallet = true; -+ background_w2->m_background_syncing = true; -+ background_w2->m_background_sync_type = m_background_sync_type; -+ background_w2->m_custom_background_key = m_custom_background_key; - -- if (get_num_subaddress_accounts() == 0) -- add_subaddress_account(tr("Primary account")); -+ cryptonote::account_base account = m_account; -+ account.forget_spend_key(); -+ background_w2->m_account = account; - -- try -- { -- find_and_save_rings(false); -- } -- catch (const std::exception &e) -- { -- MERROR("Failed to save rings, will try again next time"); -- } -- -- try -- { -- if (use_fs) -- m_message_store.read_from_file(get_multisig_wallet_state(), m_mms_file, m_load_deprecated_formats); -+ // Load background cache from file -+ background_w2->clear(); -+ background_w2->prepare_file_names(background_wallet_file); -+ background_w2->load_wallet_cache(true/*use_fs*/); -+ -+ process_background_cache(background_w2->m_background_sync_data, background_w2->m_blockchain, background_w2->m_last_block_reward); -+ -+ // Reset the background cache after processing -+ store_background_cache(m_custom_background_key.get(), true/*do_reset_background_sync_data*/); - } -- catch (const std::exception &e) -+ else - { -- MERROR("Failed to initialize MMS, it will be unusable"); -+ THROW_WALLET_EXCEPTION(error::wallet_internal_error, "unknown background sync type"); - } - } - //---------------------------------------------------------------------------------------------------- -@@ -6379,6 +6775,8 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas - same_file = canonical_old_path == canonical_new_path; - } - -+ THROW_WALLET_EXCEPTION_IF(m_is_background_wallet && !same_file, error::wallet_internal_error, -+ "Cannot save background wallet files to a different location"); - - if (!same_file) - { -@@ -6395,6 +6793,21 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas - } - } - } -+ else if (m_background_sync_type == BackgroundSyncCustomPassword && m_background_syncing && !m_is_background_wallet) -+ { -+ // We're background syncing, so store the wallet cache as a background cache -+ // keeping the background sync data -+ try -+ { -+ THROW_WALLET_EXCEPTION_IF(!m_custom_background_key, error::wallet_internal_error, "Custom background key not set"); -+ store_background_cache(m_custom_background_key.get(), false/*do_reset_background_sync_data*/); -+ } -+ catch (const std::exception &e) -+ { -+ MERROR("Failed to store background cache while background syncing: " << e.what()); -+ } -+ return; -+ } - - // get wallet cache data - boost::optional cache_file_data = get_cache_file_data(); -@@ -6488,6 +6901,22 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas - // store should only exist if the MMS is really active - m_message_store.write_to_file(get_multisig_wallet_state(), m_mms_file); - } -+ -+ if (m_background_sync_type == BackgroundSyncCustomPassword && !m_background_syncing && !m_is_background_wallet) -+ { -+ // Update the background wallet cache when we store the main wallet cache -+ // Note: if background syncing when this is called, it means the background -+ // wallet is open and was already stored above -+ try -+ { -+ THROW_WALLET_EXCEPTION_IF(!m_custom_background_key, error::wallet_internal_error, "Custom background key not set"); -+ store_background_cache(m_custom_background_key.get(), true/*do_reset_background_sync_data*/); -+ } -+ catch (const std::exception &e) -+ { -+ MERROR("Failed to update background cache: " << e.what()); -+ } -+ } - } - //---------------------------------------------------------------------------------------------------- - boost::optional wallet2::get_cache_file_data() -@@ -6505,7 +6934,7 @@ boost::optional wallet2::get_cache_file_data() - std::string cipher; - cipher.resize(cache_file_data.get().cache_data.size()); - cache_file_data.get().iv = crypto::rand(); -- crypto::chacha20(cache_file_data.get().cache_data.data(), cache_file_data.get().cache_data.size(), m_cache_key, cache_file_data.get().iv, &cipher[0]); -+ crypto::chacha20(cache_file_data.get().cache_data.data(), cache_file_data.get().cache_data.size(), get_cache_key(), cache_file_data.get().iv, &cipher[0]); - cache_file_data.get().cache_data = cipher; - return cache_file_data; - } -@@ -8581,6 +9010,34 @@ bool wallet2::is_keys_file_locked() const - return m_keys_file_locker->locked(); - } - -+bool wallet2::lock_background_keys_file(const std::string &background_keys_file) -+{ -+ if (background_keys_file.empty() || !boost::filesystem::exists(background_keys_file)) -+ return true; -+ if (m_background_keys_file_locker && m_background_keys_file_locker->locked()) -+ return true; -+ m_background_keys_file_locker.reset(new tools::file_locker(background_keys_file)); -+ return m_background_keys_file_locker->locked(); -+} -+ -+bool wallet2::unlock_background_keys_file() -+{ -+ if (!m_background_keys_file_locker) -+ { -+ MDEBUG("background keys file locker is not set"); -+ return false; -+ } -+ m_background_keys_file_locker.reset(); -+ return true; -+} -+ -+bool wallet2::is_background_keys_file_locked() const -+{ -+ if (!m_background_keys_file_locker) -+ return false; -+ return m_background_keys_file_locker->locked(); -+} -+ - bool wallet2::tx_add_fake_output(std::vector> &outs, uint64_t global_index, const crypto::public_key& output_public_key, const rct::key& mask, uint64_t real_index, bool unlocked, std::unordered_set &valid_public_keys_cache) const - { - if (!unlocked) // don't add locked outs -@@ -13913,6 +14370,413 @@ bool wallet2::import_key_images(signed_tx_set & signed_tx, size_t offset, bool o - return import_key_images(signed_tx.key_images, offset, only_selected_transfers ? boost::make_optional(selected_transfers) : boost::none); - } - -+/* -+ In background sync mode, we use just the view key when the wallet is scanning -+ to identify all txs where: -+ -+ 1. We received an output. -+ 2. We spent an output. -+ 3. We *may* have spent a received output but we didn't know for sure because -+ the spend key was not loaded while background sync was enabled. -+ -+ When the user is ready to use the spend key again, we call this function to -+ process all those background synced transactions with the spend key loaded, -+ so that we can properly generate key images for the transactions which we -+ we were not able to do so for while background sync was enabled. This allows -+ us to determine *all* receives and spends the user completed while the wallet -+ had background sync enabled. Once this function completes, we can continue -+ scanning from where the background sync left off. -+ -+ Txs of type 3 (txs which we *may* have spent received output(s)) are txs where -+ 1+ rings contain an output that the user received and the wallet does not know -+ the associated key image for that output. We don't know if the user spent in -+ this type of tx or not. This function will generate key images for all outputs -+ we don't know key images for, and then check if those outputs were spent in -+ the txs of type 3. -+ -+ By storing this type of "plausible spend tx" when scanning in background sync -+ mode, we avoid the need to query the daemon with key images when background -+ sync mode is disabled to see if those key images were spent. This would -+ reveal key images to 3rd party nodes for users who don't run their own. -+ Although this is not a perfect solution to avoid revealing key images to a 3rd -+ party node (since tx submission trivially reveals key images to a node), it's -+ probably better than revealing *unused* key images to a 3rd party node, which -+ would enable the 3rd party to deduce that a tx is spending an output at least -+ X old when the key image is included in the chain. -+*/ -+void wallet2::process_background_cache(const background_sync_data_t &background_sync_data, const hashchain &background_synced_chain, uint64_t last_block_reward) -+{ -+ // We expect the spend key to be in a decrypted state while -+ // m_processing_background_cache is true -+ m_processing_background_cache = true; -+ auto done_processing = epee::misc_utils::create_scope_leave_handler([&, this]() { -+ m_processing_background_cache = false; -+ }); -+ -+ if (m_background_syncing || m_multisig || m_watch_only || key_on_device()) -+ return; -+ -+ if (!background_sync_data.first_refresh_done) -+ { -+ MDEBUG("Skipping processing background cache, background cache has not synced yet"); -+ return; -+ } -+ -+ // Skip processing if wallet cache is synced higher than background cache -+ const uint64_t current_height = m_blockchain.size(); -+ const uint64_t background_height = background_synced_chain.size(); -+ MDEBUG("Background cache height " << background_height << " , wallet height " << current_height); -+ if (current_height > background_height) -+ { -+ MWARNING("Skipping processing background cache, synced height is higher than background cache"); -+ return; -+ } -+ -+ if (m_refresh_from_block_height < background_sync_data.wallet_refresh_from_block_height || -+ m_subaddress_lookahead_major > background_sync_data.subaddress_lookahead_major || -+ m_subaddress_lookahead_minor > background_sync_data.subaddress_lookahead_minor || -+ m_refresh_type < background_sync_data.wallet_refresh_type) -+ { -+ MWARNING("Skipping processing background cache, background wallet sync settings did not match main wallet's"); -+ MDEBUG("Wallet settings: " << -+ ", m_refresh_from_block_height: " << m_refresh_from_block_height << " vs " << background_sync_data.wallet_refresh_from_block_height << -+ ", m_subaddress_lookahead_major: " << m_subaddress_lookahead_major << " vs " << background_sync_data.subaddress_lookahead_major << -+ ", m_subaddress_lookahead_minor: " << m_subaddress_lookahead_minor << " vs " << background_sync_data.subaddress_lookahead_minor << -+ ", m_refresh_type: " << m_refresh_type << " vs " << background_sync_data.wallet_refresh_type); -+ return; -+ } -+ -+ // Sort background synced txs in the order they appeared in the cache so that -+ // we process them in the order they appeared in the chain. Thus if tx2 spends -+ // from tx1, we will know because tx1 is processed before tx2. -+ std::vector> sorted_bgs_cache(background_sync_data.txs.begin(), background_sync_data.txs.end()); -+ std::sort(sorted_bgs_cache.begin(), sorted_bgs_cache.end(), -+ [](const std::pair& l, const std::pair& r) -+ { -+ uint64_t left_index = l.second.index_in_background_sync_data; -+ uint64_t right_index = r.second.index_in_background_sync_data; -+ THROW_WALLET_EXCEPTION_IF( -+ (left_index < right_index && l.second.height > r.second.height) || -+ (left_index > right_index && l.second.height < r.second.height), -+ error::wallet_internal_error, "Unexpected background sync data order"); -+ return left_index < right_index; -+ }); -+ -+ // All txs in the background cache should have height >= sync start height, -+ // but not fatal if not -+ if (!sorted_bgs_cache.empty() && sorted_bgs_cache[0].second.height < background_sync_data.start_height) -+ MWARNING("First tx in background cache has height (" << sorted_bgs_cache[0].second.height << ") lower than sync start height (" << background_sync_data.start_height << ")"); -+ -+ // We want to process all background synced txs in order to make sure -+ // the wallet state updates correctly. First we remove all txs from the wallet -+ // from before the background sync start height, then re-process them in -+ // chronological order. The background cache should contain a superset of -+ // *all* the wallet's txs from after the background sync start height. -+ MDEBUG("Processing " << background_sync_data.txs.size() << " background synced txs starting from height " << background_sync_data.start_height); -+ detached_blockchain_data dbd = detach_blockchain(background_sync_data.start_height); -+ -+ for (const auto &bgs_tx : sorted_bgs_cache) -+ { -+ MDEBUG("Processing background synced tx " << bgs_tx.first); -+ -+ process_new_transaction(bgs_tx.first, bgs_tx.second.tx, bgs_tx.second.output_indices, bgs_tx.second.height, 0, bgs_tx.second.block_timestamp, -+ cryptonote::is_coinbase(bgs_tx.second.tx), false/*pool*/, bgs_tx.second.double_spend_seen, {}, {}, true/*ignore_callbacks*/); -+ -+ // Re-set destination addresses if they were previously set -+ if (m_confirmed_txs.find(bgs_tx.first) != m_confirmed_txs.end() && -+ dbd.detached_confirmed_txs_dests.find(bgs_tx.first) != dbd.detached_confirmed_txs_dests.end()) -+ { -+ m_confirmed_txs[bgs_tx.first].m_dests = std::move(dbd.detached_confirmed_txs_dests[bgs_tx.first]); -+ } -+ } -+ -+ m_blockchain = background_synced_chain; -+ m_last_block_reward = last_block_reward; -+ -+ MDEBUG("Finished processing background sync data"); -+} -+//---------------------------------------------------------------------------------------------------- -+void wallet2::reset_background_sync_data(background_sync_data_t &background_sync_data) -+{ -+ background_sync_data.first_refresh_done = false; -+ background_sync_data.start_height = get_blockchain_current_height(); -+ background_sync_data.txs.clear(); -+ -+ background_sync_data.wallet_refresh_from_block_height = m_refresh_from_block_height; -+ background_sync_data.subaddress_lookahead_major = m_subaddress_lookahead_major; -+ background_sync_data.subaddress_lookahead_minor = m_subaddress_lookahead_minor; -+ background_sync_data.wallet_refresh_type = m_refresh_type; -+} -+//---------------------------------------------------------------------------------------------------- -+void wallet2::store_background_cache(const crypto::chacha_key &custom_background_key, const bool do_reset_background_sync_data) -+{ -+ MDEBUG("Storing background cache (do_reset_background_sync_data=" << do_reset_background_sync_data << ")"); -+ -+ THROW_WALLET_EXCEPTION_IF(m_background_sync_type != BackgroundSyncCustomPassword, error::wallet_internal_error, -+ "Can only write a background cache when using a custom background password"); -+ THROW_WALLET_EXCEPTION_IF(m_wallet_file.empty(), error::wallet_internal_error, -+ "No wallet file known, can't store background cache"); -+ -+ std::unique_ptr background_w2(new wallet2(m_nettype)); -+ background_w2->prepare_file_names(make_background_wallet_file_name(m_wallet_file)); -+ -+ // Make sure background wallet is opened by this wallet -+ THROW_WALLET_EXCEPTION_IF(!lock_background_keys_file(background_w2->m_keys_file), -+ error::background_wallet_already_open, background_w2->m_wallet_file); -+ -+ // Load a background wallet2 instance using this wallet2 instance -+ std::string this_wallet2; -+ bool r = ::serialization::dump_binary(*this, this_wallet2); -+ THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to serialize wallet cache"); -+ -+ background_w2->clear(); -+ r = ::serialization::parse_binary(this_wallet2, *background_w2); -+ THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to deserialize wallet cache"); -+ -+ // Clear sensitive data from background cache not needed to sync -+ background_w2->clear_user_data(); -+ -+ background_w2->m_is_background_wallet = true; -+ if (do_reset_background_sync_data) -+ reset_background_sync_data(background_w2->m_background_sync_data); -+ else -+ background_w2->m_background_sync_data = m_background_sync_data; -+ background_w2->m_background_syncing = true; -+ -+ background_w2->m_custom_background_key = boost::optional(custom_background_key); -+ background_w2->m_background_sync_type = m_background_sync_type; -+ background_w2->store(); -+ -+ MDEBUG("Background cache stored (" << background_w2->m_transfers.size() << " transfers, " << background_w2->m_background_sync_data.txs.size() << " background synced txs)"); -+} -+//---------------------------------------------------------------------------------------------------- -+void wallet2::store_background_keys(const crypto::chacha_key &custom_background_key) -+{ -+ MDEBUG("Storing background keys"); -+ -+ THROW_WALLET_EXCEPTION_IF(m_wallet_file.empty(), error::wallet_internal_error, -+ "No wallet file known, can't store background keys"); -+ -+ const std::string background_keys_file = make_background_keys_file_name(m_wallet_file); -+ bool r = store_keys(background_keys_file, custom_background_key, false/*watch_only*/, true/*background_keys_file*/); -+ THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, background_keys_file); -+ THROW_WALLET_EXCEPTION_IF(!is_background_keys_file_locked(), error::wallet_internal_error, background_keys_file + "\" should be locked"); -+ -+ // GUI uses the address file to differentiate non-mainnet wallets in the UI -+ const std::string background_address_file = make_background_wallet_file_name(m_wallet_file) + ".address.txt"; -+ if (m_nettype != MAINNET && !boost::filesystem::exists(background_address_file)) -+ { -+ r = save_to_file(background_address_file, m_account.get_public_address_str(m_nettype), true); -+ if (!r) MERROR("String with address text not saved"); -+ } -+ -+ MDEBUG("Background keys stored"); -+} -+//---------------------------------------------------------------------------------------------------- -+void wallet2::write_background_sync_wallet(const epee::wipeable_string &wallet_password, const epee::wipeable_string &background_cache_password) -+{ -+ MDEBUG("Storing background sync wallet"); -+ THROW_WALLET_EXCEPTION_IF(m_background_sync_type != BackgroundSyncCustomPassword, error::wallet_internal_error, -+ "Can only write a background sync wallet when using a custom background password"); -+ THROW_WALLET_EXCEPTION_IF(m_background_syncing || m_is_background_wallet, error::wallet_internal_error, -+ "Can't write background sync wallet from an existing background cache"); -+ THROW_WALLET_EXCEPTION_IF(wallet_password == background_cache_password, -+ error::background_custom_password_same_as_wallet_password); -+ -+ // Set the background encryption key -+ crypto::chacha_key custom_background_key; -+ get_custom_background_key(background_cache_password, custom_background_key, m_kdf_rounds); -+ -+ // Keep the background encryption key in memory so the main wallet can update -+ // the background cache when it stores the main wallet cache -+ m_custom_background_key = boost::optional(custom_background_key); -+ -+ if (m_wallet_file.empty() || m_keys_file.empty()) -+ return; -+ -+ // Save background keys file, then background cache, then update main wallet settings -+ store_background_keys(custom_background_key); -+ store_background_cache(custom_background_key, true/*do_reset_background_sync_data*/); -+ bool r = store_keys(m_keys_file, wallet_password, false/*watch_only*/); -+ THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file); -+ -+ MDEBUG("Background sync wallet saved successfully"); -+} -+//---------------------------------------------------------------------------------------------------- -+void wallet2::setup_background_sync(BackgroundSyncType background_sync_type, const epee::wipeable_string &wallet_password, const boost::optional &background_cache_password) -+{ -+ MDEBUG("Setting background sync to type " << background_sync_type); -+ THROW_WALLET_EXCEPTION_IF(m_background_syncing || m_is_background_wallet, error::wallet_internal_error, -+ "Can't set background sync type from an existing background cache"); -+ verify_password_with_cached_key(wallet_password); -+ -+ if (background_sync_type != BackgroundSyncOff) -+ validate_background_cache_password_usage(background_sync_type, background_cache_password, m_multisig, m_watch_only, key_on_device()); -+ -+ THROW_WALLET_EXCEPTION_IF(background_sync_type == BackgroundSyncCustomPassword && wallet_password == background_cache_password, -+ error::background_custom_password_same_as_wallet_password); -+ -+ if (m_background_sync_type == background_sync_type && background_sync_type != BackgroundSyncCustomPassword) -+ return; // No need to make any changes -+ -+ if (!m_wallet_file.empty()) -+ { -+ // Delete existing background files if they already exist -+ const std::string old_background_wallet_file = make_background_wallet_file_name(m_wallet_file); -+ const std::string old_background_keys_file = make_background_keys_file_name(m_wallet_file); -+ const std::string old_background_address_file = old_background_wallet_file + ".address.txt"; -+ -+ // Make sure no other program is using the background wallet -+ THROW_WALLET_EXCEPTION_IF(!lock_background_keys_file(old_background_keys_file), -+ error::background_wallet_already_open, old_background_wallet_file); -+ -+ if (boost::filesystem::exists(old_background_wallet_file)) -+ if (!boost::filesystem::remove(old_background_wallet_file)) -+ LOG_ERROR("Error deleting background wallet file: " << old_background_wallet_file); -+ -+ if (boost::filesystem::exists(old_background_keys_file)) -+ if (!boost::filesystem::remove(old_background_keys_file)) -+ LOG_ERROR("Error deleting background keys file: " << old_background_keys_file); -+ -+ if (boost::filesystem::exists(old_background_address_file)) -+ if (!boost::filesystem::remove(old_background_address_file)) -+ LOG_ERROR("Error deleting background address file: " << old_background_address_file); -+ } -+ -+ m_background_sync_type = background_sync_type; -+ m_custom_background_key = boost::none; -+ -+ // Write the new files -+ switch (background_sync_type) -+ { -+ case BackgroundSyncOff: -+ case BackgroundSyncReusePassword: rewrite(m_wallet_file, wallet_password); break; -+ case BackgroundSyncCustomPassword: write_background_sync_wallet(wallet_password, background_cache_password.get()); break; -+ default: THROW_WALLET_EXCEPTION(error::wallet_internal_error, "unknown background sync type"); -+ } -+ -+ MDEBUG("Done setting background sync type"); -+} -+//---------------------------------------------------------------------------------------------------- -+/* -+ When background syncing, the wallet scans using just the view key, without -+ keeping the spend key in decrypted state. When a user returns to the wallet -+ and decrypts the spend key, the wallet processes the background synced txs, -+ then the wallet picks up scanning normally right where the background sync -+ left off. -+*/ -+void wallet2::start_background_sync() -+{ -+ THROW_WALLET_EXCEPTION_IF(m_background_sync_type == BackgroundSyncOff, error::wallet_internal_error, -+ "must setup background sync first before using background sync"); -+ THROW_WALLET_EXCEPTION_IF(m_is_background_wallet, error::wallet_internal_error, -+ "Can't start background syncing from a background wallet (it is always background syncing)"); -+ -+ MDEBUG("Starting background sync"); -+ -+ if (m_background_syncing) -+ { -+ MDEBUG("Already background syncing"); -+ return; -+ } -+ -+ if (m_background_sync_type == BackgroundSyncCustomPassword && !m_wallet_file.empty()) -+ { -+ // Save the current state of the wallet cache. Only necessary when using a -+ // custom background password which uses distinct background wallet to sync. -+ // When reusing wallet password to sync we reuse the main wallet cache. -+ store(); -+ -+ // Wipe user data from the background wallet cache not needed to sync. -+ // Only wipe user data from background cache if wallet cache is stored -+ // on disk; otherwise we could lose the data. -+ clear_user_data(); -+ -+ // Wipe m_cache_key since it can be used to decrypt main wallet cache -+ m_cache_key.scrub(); -+ } -+ -+ reset_background_sync_data(m_background_sync_data); -+ m_background_syncing = true; -+ -+ // Wipe the spend key from memory -+ m_account.forget_spend_key(); -+ -+ MDEBUG("Background sync started at height " << m_background_sync_data.start_height); -+} -+//---------------------------------------------------------------------------------------------------- -+void wallet2::stop_background_sync(const epee::wipeable_string &wallet_password, const crypto::secret_key &spend_secret_key) -+{ -+ MDEBUG("Stopping background sync"); -+ -+ // Verify provided password and spend secret key. If no spend secret key is -+ // provided, recover it from the wallet keys file -+ crypto::secret_key recovered_spend_key = crypto::null_skey; -+ if (!m_wallet_file.empty()) -+ { -+ THROW_WALLET_EXCEPTION_IF(!verify_password(wallet_password, recovered_spend_key), error::invalid_password); -+ } -+ else -+ { -+ verify_password_with_cached_key(wallet_password); -+ } -+ -+ if (spend_secret_key != crypto::null_skey) -+ { -+ THROW_WALLET_EXCEPTION_IF(!m_wallet_file.empty() && spend_secret_key != recovered_spend_key, -+ error::invalid_spend_key); -+ MDEBUG("Setting spend secret key with the provided key"); -+ recovered_spend_key = spend_secret_key; -+ } -+ -+ // Verify private spend key derives to wallet's public spend key -+ const auto verify_spend_key = [this](crypto::secret_key &recovered_spend_key) -> bool -+ { -+ crypto::public_key spend_public_key; -+ return recovered_spend_key != crypto::null_skey && -+ crypto::secret_key_to_public_key(recovered_spend_key, spend_public_key) && -+ m_account.get_keys().m_account_address.m_spend_public_key == spend_public_key; -+ }; -+ THROW_WALLET_EXCEPTION_IF(!verify_spend_key(recovered_spend_key), error::invalid_spend_key); -+ -+ THROW_WALLET_EXCEPTION_IF(m_background_sync_type == BackgroundSyncOff, error::wallet_internal_error, -+ "must setup background sync first before using background sync"); -+ THROW_WALLET_EXCEPTION_IF(m_is_background_wallet, error::wallet_internal_error, -+ "Can't stop background syncing from a background wallet"); -+ -+ if (!m_background_syncing) -+ return; -+ -+ // Copy background cache, we're about to overwrite it -+ const background_sync_data_t background_sync_data = m_background_sync_data; -+ const hashchain background_synced_chain = m_blockchain; -+ const uint64_t last_block_reward = m_last_block_reward; -+ -+ if (m_background_sync_type == BackgroundSyncCustomPassword && !m_wallet_file.empty()) -+ { -+ // Reload the wallet from disk -+ load(m_wallet_file, wallet_password); -+ THROW_WALLET_EXCEPTION_IF(!verify_spend_key(recovered_spend_key), error::invalid_spend_key); -+ } -+ m_background_syncing = false; -+ -+ // Set the plaintext spend key -+ m_account.set_spend_key(recovered_spend_key); -+ -+ // Encrypt the spend key when done if needed -+ epee::misc_utils::auto_scope_leave_caller keys_reencryptor; -+ if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only) -+ keys_reencryptor = epee::misc_utils::create_scope_leave_handler([&, this]{encrypt_keys(wallet_password);}); -+ -+ // Now we can use the decrypted spend key to process background cache -+ process_background_cache(background_sync_data, background_synced_chain, last_block_reward); -+ -+ // Reset the background cache after processing -+ reset_background_sync_data(m_background_sync_data); -+ -+ MDEBUG("Background sync stopped"); -+} -+//---------------------------------------------------------------------------------------------------- - wallet2::payment_container wallet2::export_payments() const - { - payment_container payments; -diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h -index 24366f6..b1dc4f7 100644 ---- a/src/wallet/wallet2.h -+++ b/src/wallet/wallet2.h -@@ -256,6 +256,20 @@ private: - BackgroundMiningNo = 2, - }; - -+ enum BackgroundSyncType { -+ BackgroundSyncOff = 0, -+ BackgroundSyncReusePassword = 1, -+ BackgroundSyncCustomPassword = 2, -+ }; -+ -+ static BackgroundSyncType background_sync_type_from_str(const std::string &background_sync_type_str) -+ { -+ if (background_sync_type_str == "off") return BackgroundSyncOff; -+ if (background_sync_type_str == "reuse-wallet-password") return BackgroundSyncReusePassword; -+ if (background_sync_type_str == "custom-background-password") return BackgroundSyncCustomPassword; -+ throw std::logic_error("Unknown background sync type"); -+ }; -+ - enum ExportFormat { - Binary = 0, - Ascii, -@@ -282,7 +296,12 @@ private: - //! Just parses variables. - static std::unique_ptr make_dummy(const boost::program_options::variables_map& vm, bool unattended, const std::function(const char *, bool)> &password_prompter); - -- static bool verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds); -+ static bool verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds) -+ { -+ crypto::secret_key spend_key = crypto::null_skey; -+ return verify_password(keys_file_name, password, no_spend_key, hwdev, kdf_rounds, spend_key); -+ }; -+ static bool verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds, crypto::secret_key &spend_key_out); - static bool query_device(hw::device::device_type& device_type, const std::string& keys_file_name, const epee::wipeable_string& password, uint64_t kdf_rounds = 1); - - wallet2(cryptonote::network_type nettype = cryptonote::MAINNET, uint64_t kdf_rounds = 1, bool unattended = false, std::unique_ptr http_client_factory = std::unique_ptr(new net::http::client_factory())); -@@ -792,6 +811,54 @@ private: - END_SERIALIZE() - }; - -+ struct background_synced_tx_t -+ { -+ uint64_t index_in_background_sync_data; -+ cryptonote::transaction tx; -+ std::vector output_indices; -+ uint64_t height; -+ uint64_t block_timestamp; -+ bool double_spend_seen; -+ -+ BEGIN_SERIALIZE_OBJECT() -+ VERSION_FIELD(0) -+ VARINT_FIELD(index_in_background_sync_data) -+ -+ // prune tx; don't need to keep signature data -+ if (!tx.serialize_base(ar)) -+ return false; -+ -+ FIELD(output_indices) -+ VARINT_FIELD(height) -+ VARINT_FIELD(block_timestamp) -+ FIELD(double_spend_seen) -+ END_SERIALIZE() -+ }; -+ -+ struct background_sync_data_t -+ { -+ bool first_refresh_done = false; -+ uint64_t start_height = 0; -+ serializable_unordered_map txs; -+ -+ // Relevant wallet settings -+ uint64_t wallet_refresh_from_block_height; -+ size_t subaddress_lookahead_major; -+ size_t subaddress_lookahead_minor; -+ RefreshType wallet_refresh_type; -+ -+ BEGIN_SERIALIZE_OBJECT() -+ VERSION_FIELD(0) -+ FIELD(first_refresh_done) -+ FIELD(start_height) -+ FIELD(txs) -+ FIELD(wallet_refresh_from_block_height) -+ VARINT_FIELD(subaddress_lookahead_major) -+ VARINT_FIELD(subaddress_lookahead_minor) -+ VARINT_FIELD(wallet_refresh_type) -+ END_SERIALIZE() -+ }; -+ - typedef std::tuple get_outs_entry; - - struct parsed_block -@@ -974,7 +1041,8 @@ private: - /*! - * \brief verifies given password is correct for default wallet keys file - */ -- bool verify_password(const epee::wipeable_string& password); -+ bool verify_password(const epee::wipeable_string& password) {crypto::secret_key key = crypto::null_skey; return verify_password(password, key);}; -+ bool verify_password(const epee::wipeable_string& password, crypto::secret_key &spend_key_out); - cryptonote::account_base& get_account(){return m_account;} - const cryptonote::account_base& get_account()const{return m_account;} - -@@ -1069,6 +1137,7 @@ private: - cryptonote::network_type nettype() const { return m_nettype; } - bool watch_only() const { return m_watch_only; } - bool multisig(bool *ready = NULL, uint32_t *threshold = NULL, uint32_t *total = NULL) const; -+ bool is_background_wallet() const { return m_is_background_wallet; } - bool has_multisig_partial_key_images() const; - bool has_unknown_key_images() const; - bool get_multisig_seed(epee::wipeable_string& seed, const epee::wipeable_string &passphrase = std::string()) const; -@@ -1276,11 +1345,17 @@ private: - return; - } - a & m_has_ever_refreshed_from_node; -+ if(ver < 31) -+ { -+ m_background_sync_data = background_sync_data_t{}; -+ return; -+ } -+ a & m_background_sync_data; - } - - BEGIN_SERIALIZE_OBJECT() - MAGIC_FIELD("monero wallet cache") -- VERSION_FIELD(1) -+ VERSION_FIELD(2) - FIELD(m_blockchain) - FIELD(m_transfers) - FIELD(m_account_public_address) -@@ -1312,6 +1387,12 @@ private: - return true; - } - FIELD(m_has_ever_refreshed_from_node) -+ if (version < 2) -+ { -+ m_background_sync_data = background_sync_data_t{}; -+ return true; -+ } -+ FIELD(m_background_sync_data) - END_SERIALIZE() - - /*! -@@ -1327,6 +1408,8 @@ private: - * \return Whether path is valid format - */ - static bool wallet_valid_path_format(const std::string& file_path); -+ static std::string make_background_wallet_file_name(const std::string &wallet_file); -+ static std::string make_background_keys_file_name(const std::string &wallet_file); - static bool parse_long_payment_id(const std::string& payment_id_str, crypto::hash& payment_id); - static bool parse_short_payment_id(const std::string& payment_id_str, crypto::hash8& payment_id); - static bool parse_payment_id(const std::string& payment_id_str, crypto::hash& payment_id); -@@ -1375,6 +1458,9 @@ private: - void ignore_outputs_below(uint64_t value) { m_ignore_outputs_below = value; } - bool track_uses() const { return m_track_uses; } - void track_uses(bool value) { m_track_uses = value; } -+ BackgroundSyncType background_sync_type() const { return m_background_sync_type; } -+ void setup_background_sync(BackgroundSyncType background_sync_type, const epee::wipeable_string &wallet_password, const boost::optional &background_cache_password); -+ bool is_background_syncing() const { return m_background_syncing; } - bool show_wallet_name_when_locked() const { return m_show_wallet_name_when_locked; } - void show_wallet_name_when_locked(bool value) { m_show_wallet_name_when_locked = value; } - BackgroundMiningSetupType setup_background_mining() const { return m_setup_background_mining; } -@@ -1689,6 +1775,9 @@ private: - uint64_t get_bytes_sent() const; - uint64_t get_bytes_received() const; - -+ void start_background_sync(); -+ void stop_background_sync(const epee::wipeable_string &wallet_password, const crypto::secret_key &spend_secret_key = crypto::null_skey); -+ - // MMS ------------------------------------------------------------------------------------------------- - mms::message_store& get_message_store() { return m_message_store; }; - const mms::message_store& get_message_store() const { return m_message_store; }; -@@ -1724,6 +1813,9 @@ private: - * \return Whether it was successful. - */ - bool store_keys(const std::string& keys_file_name, const epee::wipeable_string& password, bool watch_only = false); -+ bool store_keys(const std::string& keys_file_name, const crypto::chacha_key& key, bool watch_only = false, bool background_keys_file = false); -+ boost::optional get_keys_file_data(const crypto::chacha_key& key, bool watch_only = false, bool background_keys_file = false); -+ bool store_keys_file_data(const std::string& keys_file_name, wallet2::keys_file_data &keys_file_data, bool background_keys_file = false); - /*! - * \brief Load wallet keys information from wallet file. - * \param keys_file_name Name of wallet file -@@ -1737,6 +1829,7 @@ private: - */ - bool load_keys_buf(const std::string& keys_buf, const epee::wipeable_string& password); - bool load_keys_buf(const std::string& keys_buf, const epee::wipeable_string& password, boost::optional& keys_to_encrypt); -+ void load_wallet_cache(const bool use_fs, const std::string& cache_buf = ""); - void process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector &o_indices, uint64_t height, uint8_t block_version, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen, const tx_cache_data &tx_cache_data, std::map, size_t> *output_tracker_cache = NULL, bool ignore_callbacks = false); - bool should_skip_block(const cryptonote::block &b, uint64_t height) const; - void process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const parsed_block &parsed_block, const crypto::hash& bl_id, uint64_t height, const std::vector &tx_cache_data, size_t tx_cache_data_offset, std::map, size_t> *output_tracker_cache = NULL); -@@ -1745,6 +1838,15 @@ private: - void get_short_chain_history(std::list& ids, uint64_t granularity = 1) const; - bool clear(); - void clear_soft(bool keep_key_images=false); -+ /* -+ * clear_user_data clears data created by the user, which is mostly data -+ * that a view key cannot identify on chain. This function was initially -+ * added to ensure that a "background" wallet (a wallet that syncs with just -+ * a view key hot in memory) does not have any sensitive data loaded that it -+ * does not need in order to sync. Future devs should take care to ensure -+ * that this function deletes data that is not useful for background syncing -+ */ -+ void clear_user_data(); - void pull_blocks(bool first, bool try_incremental, uint64_t start_height, uint64_t& blocks_start_height, const std::list &short_chain_history, std::vector &blocks, std::vector &o_indices, uint64_t ¤t_height); - void pull_hashes(uint64_t start_height, uint64_t& blocks_start_height, const std::list &short_chain_history, std::vector &hashes); - void fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list &short_chain_history, bool force = false); -@@ -1796,10 +1898,23 @@ private: - bool get_ring(const crypto::chacha_key &key, const crypto::key_image &key_image, std::vector &outs); - crypto::chacha_key get_ringdb_key(); - void setup_keys(const epee::wipeable_string &password); -+ const crypto::chacha_key get_cache_key(); -+ void verify_password_with_cached_key(const epee::wipeable_string &password); -+ void verify_password_with_cached_key(const crypto::chacha_key &key); - size_t get_transfer_details(const crypto::key_image &ki) const; - tx_entry_data get_tx_entries(const std::unordered_set &txids); - void sort_scan_tx_entries(std::vector &unsorted_tx_entries); - void process_scan_txs(const tx_entry_data &txs_to_scan, const tx_entry_data &txs_to_reprocess, const std::unordered_set &tx_hashes_to_reprocess, detached_blockchain_data &dbd); -+ void write_background_sync_wallet(const epee::wipeable_string &wallet_password, const epee::wipeable_string &background_cache_password); -+ void process_background_cache_on_open(); -+ void process_background_cache(const background_sync_data_t &background_sync_data, const hashchain &background_chain, uint64_t last_block_reward); -+ void reset_background_sync_data(background_sync_data_t &background_sync_data); -+ void store_background_cache(const crypto::chacha_key &custom_background_key, const bool do_reset_background_sync_data = true); -+ void store_background_keys(const crypto::chacha_key &custom_background_key); -+ -+ bool lock_background_keys_file(const std::string &background_keys_file); -+ bool unlock_background_keys_file(); -+ bool is_background_keys_file_locked() const; - - void register_devices(); - hw::device& lookup_device(const std::string & device_descriptor); -@@ -1914,6 +2029,8 @@ private: - uint64_t m_ignore_outputs_above; - uint64_t m_ignore_outputs_below; - bool m_track_uses; -+ bool m_is_background_wallet; -+ BackgroundSyncType m_background_sync_type; - bool m_show_wallet_name_when_locked; - uint32_t m_inactivity_lock_timeout; - BackgroundMiningSetupType m_setup_background_mining; -@@ -1959,6 +2076,7 @@ private: - - uint64_t m_last_block_reward; - std::unique_ptr m_keys_file_locker; -+ std::unique_ptr m_background_keys_file_locker; - - mms::message_store m_message_store; - bool m_original_keys_available; -@@ -1966,6 +2084,7 @@ private: - crypto::secret_key m_original_view_secret_key; - - crypto::chacha_key m_cache_key; -+ boost::optional m_custom_background_key = boost::none; - std::shared_ptr m_encrypt_keys_after_refresh; - - bool m_unattended; -@@ -1981,9 +2100,13 @@ private: - - static boost::mutex default_daemon_address_lock; - static std::string default_daemon_address; -+ -+ bool m_background_syncing; -+ bool m_processing_background_cache; -+ background_sync_data_t m_background_sync_data; - }; - } --BOOST_CLASS_VERSION(tools::wallet2, 30) -+BOOST_CLASS_VERSION(tools::wallet2, 31) - BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 12) - BOOST_CLASS_VERSION(tools::wallet2::multisig_info, 1) - BOOST_CLASS_VERSION(tools::wallet2::multisig_info::LR, 0) -@@ -1999,6 +2122,8 @@ BOOST_CLASS_VERSION(tools::wallet2::signed_tx_set, 1) - BOOST_CLASS_VERSION(tools::wallet2::tx_construction_data, 4) - BOOST_CLASS_VERSION(tools::wallet2::pending_tx, 3) - BOOST_CLASS_VERSION(tools::wallet2::multisig_sig, 1) -+BOOST_CLASS_VERSION(tools::wallet2::background_synced_tx_t, 0) -+BOOST_CLASS_VERSION(tools::wallet2::background_sync_data_t, 0) - - namespace boost - { -@@ -2497,6 +2622,29 @@ namespace boost - return; - a & x.multisig_sigs; - } -+ -+ template -+ inline void serialize(Archive& a, tools::wallet2::background_synced_tx_t &x, const boost::serialization::version_type ver) -+ { -+ a & x.index_in_background_sync_data; -+ a & x.tx; -+ a & x.output_indices; -+ a & x.height; -+ a & x.block_timestamp; -+ a & x.double_spend_seen; -+ } -+ -+ template -+ inline void serialize(Archive& a, tools::wallet2::background_sync_data_t &x, const boost::serialization::version_type ver) -+ { -+ a & x.first_refresh_done; -+ a & x.start_height; -+ a & x.txs.parent(); -+ a & x.wallet_refresh_from_block_height; -+ a & x.subaddress_lookahead_major; -+ a & x.subaddress_lookahead_minor; -+ a & x.wallet_refresh_type; -+ } - } - } - -diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h -index c077313..c54cd34 100644 ---- a/src/wallet/wallet_errors.h -+++ b/src/wallet/wallet_errors.h -@@ -63,6 +63,7 @@ namespace tools - // invalid_password - // invalid_priority - // invalid_multisig_seed -+ // invalid_spend_key - // refresh_error * - // acc_outs_lookup_error - // block_parse_error -@@ -97,6 +98,9 @@ namespace tools - // wallet_files_doesnt_correspond - // scan_tx_error * - // wont_reprocess_recent_txs_via_untrusted_daemon -+ // background_sync_error * -+ // background_wallet_already_open -+ // background_custom_password_same_as_wallet_password - // - // * - class with protected ctor - -@@ -304,6 +308,16 @@ namespace tools - std::string to_string() const { return wallet_logic_error::to_string(); } - }; - -+ struct invalid_spend_key : public wallet_logic_error -+ { -+ explicit invalid_spend_key(std::string&& loc) -+ : wallet_logic_error(std::move(loc), "invalid spend key") -+ { -+ } -+ -+ std::string to_string() const { return wallet_logic_error::to_string(); } -+ }; -+ - //---------------------------------------------------------------------------------------------------- - struct invalid_pregenerated_random : public wallet_logic_error - { -@@ -947,6 +961,31 @@ namespace tools - } - }; - //---------------------------------------------------------------------------------------------------- -+ struct background_sync_error : public wallet_logic_error -+ { -+ protected: -+ explicit background_sync_error(std::string&& loc, const std::string& message) -+ : wallet_logic_error(std::move(loc), message) -+ { -+ } -+ }; -+ //---------------------------------------------------------------------------------------------------- -+ struct background_wallet_already_open : public background_sync_error -+ { -+ explicit background_wallet_already_open(std::string&& loc, const std::string& background_wallet_file) -+ : background_sync_error(std::move(loc), "background wallet " + background_wallet_file + " is already opened by another wallet program") -+ { -+ } -+ }; -+ //---------------------------------------------------------------------------------------------------- -+ struct background_custom_password_same_as_wallet_password : public background_sync_error -+ { -+ explicit background_custom_password_same_as_wallet_password(std::string&& loc) -+ : background_sync_error(std::move(loc), "custom background password must be different than wallet password") -+ { -+ } -+ }; -+ //---------------------------------------------------------------------------------------------------- - - #if !defined(_MSC_VER) - -diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp -index b141994..d24b4c5 100644 ---- a/src/wallet/wallet_rpc_server.cpp -+++ b/src/wallet/wallet_rpc_server.cpp -@@ -73,6 +73,54 @@ using namespace epee; - } \ - } while(0) - -+#define CHECK_IF_BACKGROUND_SYNCING() \ -+ do \ -+ { \ -+ if (!m_wallet) { return not_open(er); } \ -+ if (m_wallet->is_background_wallet()) \ -+ { \ -+ er.code = WALLET_RPC_ERROR_CODE_IS_BACKGROUND_WALLET; \ -+ er.message = "This command is disabled for background wallets."; \ -+ return false; \ -+ } \ -+ if (m_wallet->is_background_syncing()) \ -+ { \ -+ er.code = WALLET_RPC_ERROR_CODE_IS_BACKGROUND_SYNCING; \ -+ er.message = "This command is disabled while background syncing. Stop background syncing to use this command."; \ -+ return false; \ -+ } \ -+ } while(0) -+ -+#define PRE_VALIDATE_BACKGROUND_SYNC() \ -+ do \ -+ { \ -+ if (!m_wallet) { return not_open(er); } \ -+ if (m_restricted) \ -+ { \ -+ er.code = WALLET_RPC_ERROR_CODE_DENIED; \ -+ er.message = "Command unavailable in restricted mode."; \ -+ return false; \ -+ } \ -+ if (m_wallet->key_on_device()) \ -+ { \ -+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; \ -+ er.message = "Command not supported by HW wallet"; \ -+ return false; \ -+ } \ -+ if (m_wallet->multisig()) \ -+ { \ -+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; \ -+ er.message = "Multisig wallet cannot enable background sync"; \ -+ return false; \ -+ } \ -+ if (m_wallet->watch_only()) \ -+ { \ -+ er.code = WALLET_RPC_ERROR_CODE_WATCH_ONLY; \ -+ er.message = "Watch-only wallet cannot enable background sync"; \ -+ return false; \ -+ } \ -+ } while (0) -+ - namespace - { - const command_line::arg_descriptor arg_rpc_bind_port = {"rpc-bind-port", "Sets bind port for server"}; -@@ -291,6 +339,9 @@ namespace tools - { - if (!m_wallet) - return; -+ // Background mining can be toggled from the main wallet -+ if (m_wallet->is_background_wallet() || m_wallet->is_background_syncing()) -+ return; - - tools::wallet2::BackgroundMiningSetupType setup = m_wallet->setup_background_mining(); - if (setup == tools::wallet2::BackgroundMiningNo) -@@ -582,6 +633,7 @@ namespace tools - bool wallet_rpc_server::on_create_address(const wallet_rpc::COMMAND_RPC_CREATE_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_CREATE_ADDRESS::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -+ CHECK_IF_BACKGROUND_SYNCING(); - try - { - if (req.count < 1 || req.count > 64) { -@@ -619,6 +671,7 @@ namespace tools - bool wallet_rpc_server::on_label_address(const wallet_rpc::COMMAND_RPC_LABEL_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_LABEL_ADDRESS::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -+ CHECK_IF_BACKGROUND_SYNCING(); - try - { - m_wallet->set_subaddress_label(req.index, req.label); -@@ -681,6 +734,7 @@ namespace tools - bool wallet_rpc_server::on_create_account(const wallet_rpc::COMMAND_RPC_CREATE_ACCOUNT::request& req, wallet_rpc::COMMAND_RPC_CREATE_ACCOUNT::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -+ CHECK_IF_BACKGROUND_SYNCING(); - try - { - m_wallet->add_subaddress_account(req.label); -@@ -698,6 +752,7 @@ namespace tools - bool wallet_rpc_server::on_label_account(const wallet_rpc::COMMAND_RPC_LABEL_ACCOUNT::request& req, wallet_rpc::COMMAND_RPC_LABEL_ACCOUNT::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -+ CHECK_IF_BACKGROUND_SYNCING(); - try - { - m_wallet->set_subaddress_label({req.account_index, 0}, req.label); -@@ -713,6 +768,7 @@ namespace tools - bool wallet_rpc_server::on_get_account_tags(const wallet_rpc::COMMAND_RPC_GET_ACCOUNT_TAGS::request& req, wallet_rpc::COMMAND_RPC_GET_ACCOUNT_TAGS::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -+ CHECK_IF_BACKGROUND_SYNCING(); - const std::pair, std::vector> account_tags = m_wallet->get_account_tags(); - for (const std::pair& p : account_tags.first) - { -@@ -732,6 +788,7 @@ namespace tools - bool wallet_rpc_server::on_tag_accounts(const wallet_rpc::COMMAND_RPC_TAG_ACCOUNTS::request& req, wallet_rpc::COMMAND_RPC_TAG_ACCOUNTS::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -+ CHECK_IF_BACKGROUND_SYNCING(); - try - { - m_wallet->set_account_tag(req.accounts, req.tag); -@@ -747,6 +804,7 @@ namespace tools - bool wallet_rpc_server::on_untag_accounts(const wallet_rpc::COMMAND_RPC_UNTAG_ACCOUNTS::request& req, wallet_rpc::COMMAND_RPC_UNTAG_ACCOUNTS::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -+ CHECK_IF_BACKGROUND_SYNCING(); - try - { - m_wallet->set_account_tag(req.accounts, ""); -@@ -762,6 +820,7 @@ namespace tools - bool wallet_rpc_server::on_set_account_tag_description(const wallet_rpc::COMMAND_RPC_SET_ACCOUNT_TAG_DESCRIPTION::request& req, wallet_rpc::COMMAND_RPC_SET_ACCOUNT_TAG_DESCRIPTION::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -+ CHECK_IF_BACKGROUND_SYNCING(); - try - { - m_wallet->set_account_tag_description(req.tag, req.description); -@@ -792,6 +851,7 @@ namespace tools - bool wallet_rpc_server::on_freeze(const wallet_rpc::COMMAND_RPC_FREEZE::request& req, wallet_rpc::COMMAND_RPC_FREEZE::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -+ CHECK_IF_BACKGROUND_SYNCING(); - try - { - if (req.key_image.empty()) -@@ -820,6 +880,7 @@ namespace tools - bool wallet_rpc_server::on_thaw(const wallet_rpc::COMMAND_RPC_THAW::request& req, wallet_rpc::COMMAND_RPC_THAW::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -+ CHECK_IF_BACKGROUND_SYNCING(); - try - { - if (req.key_image.empty()) -@@ -848,6 +909,7 @@ namespace tools - bool wallet_rpc_server::on_frozen(const wallet_rpc::COMMAND_RPC_FROZEN::request& req, wallet_rpc::COMMAND_RPC_FROZEN::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -+ CHECK_IF_BACKGROUND_SYNCING(); - try - { - if (req.key_image.empty()) -@@ -875,6 +937,8 @@ namespace tools - //------------------------------------------------------------------------------------------------------------------------------ - bool wallet_rpc_server::validate_transfer(const std::list& destinations, const std::string& payment_id, std::vector& dsts, std::vector& extra, bool at_least_one_destination, epee::json_rpc::error& er) - { -+ CHECK_IF_BACKGROUND_SYNCING(); -+ - crypto::hash8 integrated_payment_id = crypto::null_hash8; - std::string extra_nonce; - for (auto it = destinations.begin(); it != destinations.end(); it++) -@@ -1204,6 +1268,7 @@ namespace tools - } - - CHECK_MULTISIG_ENABLED(); -+ CHECK_IF_BACKGROUND_SYNCING(); - - cryptonote::blobdata blob; - if (!epee::string_tools::parse_hexstr_to_binbuff(req.unsigned_txset, blob)) -@@ -1285,6 +1350,7 @@ namespace tools - er.message = "command not supported by watch-only wallet"; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - if(req.unsigned_txset.empty() && req.multisig_txset.empty()) - { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; -@@ -1554,6 +1620,7 @@ namespace tools - } - - CHECK_MULTISIG_ENABLED(); -+ CHECK_IF_BACKGROUND_SYNCING(); - - try - { -@@ -2115,6 +2182,7 @@ namespace tools - er.message = "The wallet is watch-only. Cannot retrieve seed."; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - if (!m_wallet->is_deterministic()) - { - er.code = WALLET_RPC_ERROR_CODE_NON_DETERMINISTIC; -@@ -2143,6 +2211,7 @@ namespace tools - er.message = "The wallet is watch-only. Cannot retrieve spend key."; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().m_spend_secret_key); - res.key = std::string(key.data(), key.size()); - } -@@ -2164,6 +2233,7 @@ namespace tools - er.message = "Command unavailable in restricted mode."; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - - try - { -@@ -2177,6 +2247,79 @@ namespace tools - return true; - } - //------------------------------------------------------------------------------------------------------------------------------ -+ bool wallet_rpc_server::on_setup_background_sync(const wallet_rpc::COMMAND_RPC_SETUP_BACKGROUND_SYNC::request& req, wallet_rpc::COMMAND_RPC_SETUP_BACKGROUND_SYNC::response& res, epee::json_rpc::error& er, const connection_context *ctx) -+ { -+ try -+ { -+ PRE_VALIDATE_BACKGROUND_SYNC(); -+ const tools::wallet2::BackgroundSyncType background_sync_type = tools::wallet2::background_sync_type_from_str(req.background_sync_type); -+ boost::optional background_cache_password = boost::none; -+ if (background_sync_type == tools::wallet2::BackgroundSyncCustomPassword) -+ background_cache_password = boost::optional(req.background_cache_password); -+ m_wallet->setup_background_sync(background_sync_type, req.wallet_password, background_cache_password); -+ } -+ catch (...) -+ { -+ handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); -+ return false; -+ } -+ return true; -+ } -+ //------------------------------------------------------------------------------------------------------------------------------ -+ bool wallet_rpc_server::on_start_background_sync(const wallet_rpc::COMMAND_RPC_START_BACKGROUND_SYNC::request& req, wallet_rpc::COMMAND_RPC_START_BACKGROUND_SYNC::response& res, epee::json_rpc::error& er, const connection_context *ctx) -+ { -+ try -+ { -+ PRE_VALIDATE_BACKGROUND_SYNC(); -+ m_wallet->start_background_sync(); -+ } -+ catch (...) -+ { -+ handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); -+ return false; -+ } -+ return true; -+ } -+ //------------------------------------------------------------------------------------------------------------------------------ -+ bool wallet_rpc_server::on_stop_background_sync(const wallet_rpc::COMMAND_RPC_STOP_BACKGROUND_SYNC::request& req, wallet_rpc::COMMAND_RPC_STOP_BACKGROUND_SYNC::response& res, epee::json_rpc::error& er, const connection_context *ctx) -+ { -+ try -+ { -+ PRE_VALIDATE_BACKGROUND_SYNC(); -+ crypto::secret_key spend_secret_key = crypto::null_skey; -+ -+ // Load the spend key from seed if seed is provided -+ if (!req.seed.empty()) -+ { -+ crypto::secret_key recovery_key; -+ std::string language; -+ -+ if (!crypto::ElectrumWords::words_to_bytes(req.seed, recovery_key, language)) -+ { -+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; -+ er.message = "Electrum-style word list failed verification"; -+ return false; -+ } -+ -+ if (!req.seed_offset.empty()) -+ recovery_key = cryptonote::decrypt_key(recovery_key, req.seed_offset); -+ -+ // generate spend key -+ cryptonote::account_base account; -+ account.generate(recovery_key, true, false); -+ spend_secret_key = account.get_keys().m_spend_secret_key; -+ } -+ -+ m_wallet->stop_background_sync(req.wallet_password, spend_secret_key); -+ } -+ catch (...) -+ { -+ handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); -+ return false; -+ } -+ return true; -+ } -+ //------------------------------------------------------------------------------------------------------------------------------ - bool wallet_rpc_server::on_sign(const wallet_rpc::COMMAND_RPC_SIGN::request& req, wallet_rpc::COMMAND_RPC_SIGN::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -@@ -2186,6 +2329,7 @@ namespace tools - er.message = "Command unavailable in restricted mode."; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - - tools::wallet2::message_signature_type_t signature_type = tools::wallet2::sign_with_spend_key; - if (req.signature_type == "spend" || req.signature_type == "") -@@ -2278,6 +2422,7 @@ namespace tools - er.message = "Command unavailable in restricted mode."; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - - if (req.txids.size() != req.notes.size()) - { -@@ -2350,6 +2495,7 @@ namespace tools - er.message = "Command unavailable in restricted mode."; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - - m_wallet->set_attribute(req.key, req.value); - -@@ -2377,6 +2523,7 @@ namespace tools - bool wallet_rpc_server::on_get_tx_key(const wallet_rpc::COMMAND_RPC_GET_TX_KEY::request& req, wallet_rpc::COMMAND_RPC_GET_TX_KEY::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -+ CHECK_IF_BACKGROUND_SYNCING(); - - crypto::hash txid; - if (!epee::string_tools::hex_to_pod(req.txid, txid)) -@@ -2468,6 +2615,7 @@ namespace tools - bool wallet_rpc_server::on_get_tx_proof(const wallet_rpc::COMMAND_RPC_GET_TX_PROOF::request& req, wallet_rpc::COMMAND_RPC_GET_TX_PROOF::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -+ CHECK_IF_BACKGROUND_SYNCING(); - - crypto::hash txid; - if (!epee::string_tools::hex_to_pod(req.txid, txid)) -@@ -2584,6 +2732,7 @@ namespace tools - bool wallet_rpc_server::on_get_reserve_proof(const wallet_rpc::COMMAND_RPC_GET_RESERVE_PROOF::request& req, wallet_rpc::COMMAND_RPC_GET_RESERVE_PROOF::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -+ CHECK_IF_BACKGROUND_SYNCING(); - - boost::optional> account_minreserve; - if (!req.all) -@@ -2826,6 +2975,7 @@ namespace tools - er.message = "command not supported by HW wallet"; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - - try - { -@@ -2855,6 +3005,7 @@ namespace tools - er.message = "command not supported by HW wallet"; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - - cryptonote::blobdata blob; - if (!epee::string_tools::parse_hexstr_to_binbuff(req.outputs_data_hex, blob)) -@@ -2880,6 +3031,7 @@ namespace tools - bool wallet_rpc_server::on_export_key_images(const wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES::request& req, wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -+ CHECK_IF_BACKGROUND_SYNCING(); - try - { - std::pair>> ski = m_wallet->export_key_images(req.all); -@@ -2916,6 +3068,7 @@ namespace tools - er.message = "This command requires a trusted daemon."; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - try - { - std::vector> ski; -@@ -2984,6 +3137,7 @@ namespace tools - bool wallet_rpc_server::on_get_address_book(const wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -+ CHECK_IF_BACKGROUND_SYNCING(); - const auto ab = m_wallet->get_address_book(); - if (req.entries.empty()) - { -@@ -3029,6 +3183,7 @@ namespace tools - er.message = "Command unavailable in restricted mode."; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - - cryptonote::address_parse_info info; - er.message = ""; -@@ -3071,6 +3226,7 @@ namespace tools - er.message = "Command unavailable in restricted mode."; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - - const auto ab = m_wallet->get_address_book(); - if (req.index >= ab.size()) -@@ -3133,6 +3289,7 @@ namespace tools - er.message = "Command unavailable in restricted mode."; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - - const auto ab = m_wallet->get_address_book(); - if (req.index >= ab.size()) -@@ -3203,6 +3360,7 @@ namespace tools - er.message = "Command unavailable in restricted mode."; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - - std::unordered_set txids; - std::list::const_iterator i = req.txids.begin(); -@@ -3242,6 +3400,7 @@ namespace tools - er.message = "Command unavailable in restricted mode."; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - try - { - m_wallet->rescan_spent(); -@@ -3506,6 +3665,7 @@ namespace tools - er.message = "Command unavailable in restricted mode."; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - if (m_wallet->verify_password(req.old_password)) - { - try -@@ -4033,6 +4193,7 @@ namespace tools - er.message = "wallet is watch-only and cannot be made multisig"; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - - res.multisig_info = m_wallet->get_multisig_first_kex_msg(); - return true; -@@ -4060,6 +4221,7 @@ namespace tools - er.message = "wallet is watch-only and cannot be made multisig"; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - - try - { -diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h -index 3308d17..c2329aa 100644 ---- a/src/wallet/wallet_rpc_server.h -+++ b/src/wallet/wallet_rpc_server.h -@@ -160,6 +160,9 @@ namespace tools - MAP_JON_RPC_WE("set_log_categories", on_set_log_categories, wallet_rpc::COMMAND_RPC_SET_LOG_CATEGORIES) - MAP_JON_RPC_WE("estimate_tx_size_and_weight", on_estimate_tx_size_and_weight, wallet_rpc::COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT) - MAP_JON_RPC_WE("get_version", on_get_version, wallet_rpc::COMMAND_RPC_GET_VERSION) -+ MAP_JON_RPC_WE("setup_background_sync", on_setup_background_sync, wallet_rpc::COMMAND_RPC_SETUP_BACKGROUND_SYNC) -+ MAP_JON_RPC_WE("start_background_sync", on_start_background_sync, wallet_rpc::COMMAND_RPC_START_BACKGROUND_SYNC) -+ MAP_JON_RPC_WE("stop_background_sync", on_stop_background_sync, wallet_rpc::COMMAND_RPC_STOP_BACKGROUND_SYNC) - END_JSON_RPC_MAP() - END_URI_MAP2() - -@@ -251,6 +254,9 @@ namespace tools - bool on_set_log_categories(const wallet_rpc::COMMAND_RPC_SET_LOG_CATEGORIES::request& req, wallet_rpc::COMMAND_RPC_SET_LOG_CATEGORIES::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); - bool on_estimate_tx_size_and_weight(const wallet_rpc::COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT::request& req, wallet_rpc::COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); - bool on_get_version(const wallet_rpc::COMMAND_RPC_GET_VERSION::request& req, wallet_rpc::COMMAND_RPC_GET_VERSION::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); -+ bool on_setup_background_sync(const wallet_rpc::COMMAND_RPC_SETUP_BACKGROUND_SYNC::request& req, wallet_rpc::COMMAND_RPC_SETUP_BACKGROUND_SYNC::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); -+ bool on_start_background_sync(const wallet_rpc::COMMAND_RPC_START_BACKGROUND_SYNC::request& req, wallet_rpc::COMMAND_RPC_START_BACKGROUND_SYNC::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); -+ bool on_stop_background_sync(const wallet_rpc::COMMAND_RPC_STOP_BACKGROUND_SYNC::request& req, wallet_rpc::COMMAND_RPC_STOP_BACKGROUND_SYNC::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); - - //json rpc v2 - bool on_query_key(const wallet_rpc::COMMAND_RPC_QUERY_KEY::request& req, wallet_rpc::COMMAND_RPC_QUERY_KEY::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); -diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h -index b6098d9..a44b56e 100644 ---- a/src/wallet/wallet_rpc_server_commands_defs.h -+++ b/src/wallet/wallet_rpc_server_commands_defs.h -@@ -2696,5 +2696,69 @@ namespace wallet_rpc - typedef epee::misc_utils::struct_init response; - }; - -+ struct COMMAND_RPC_SETUP_BACKGROUND_SYNC -+ { -+ struct request_t -+ { -+ std::string background_sync_type; -+ std::string wallet_password; -+ std::string background_cache_password; -+ -+ BEGIN_KV_SERIALIZE_MAP() -+ KV_SERIALIZE(background_sync_type) -+ KV_SERIALIZE(wallet_password) -+ KV_SERIALIZE_OPT(background_cache_password, (std::string)"") -+ END_KV_SERIALIZE_MAP() -+ }; -+ typedef epee::misc_utils::struct_init request; -+ -+ struct response_t -+ { -+ BEGIN_KV_SERIALIZE_MAP() -+ END_KV_SERIALIZE_MAP() -+ }; -+ typedef epee::misc_utils::struct_init response; -+ }; -+ -+ struct COMMAND_RPC_START_BACKGROUND_SYNC -+ { -+ struct request_t -+ { -+ BEGIN_KV_SERIALIZE_MAP() -+ END_KV_SERIALIZE_MAP() -+ }; -+ typedef epee::misc_utils::struct_init request; -+ -+ struct response_t -+ { -+ BEGIN_KV_SERIALIZE_MAP() -+ END_KV_SERIALIZE_MAP() -+ }; -+ typedef epee::misc_utils::struct_init response; -+ }; -+ -+ struct COMMAND_RPC_STOP_BACKGROUND_SYNC -+ { -+ struct request_t -+ { -+ std::string wallet_password; -+ std::string seed; -+ std::string seed_offset; -+ -+ BEGIN_KV_SERIALIZE_MAP() -+ KV_SERIALIZE(wallet_password) -+ KV_SERIALIZE_OPT(seed, (std::string)"") -+ KV_SERIALIZE_OPT(seed_offset, (std::string)"") -+ END_KV_SERIALIZE_MAP() -+ }; -+ typedef epee::misc_utils::struct_init request; -+ -+ struct response_t -+ { -+ BEGIN_KV_SERIALIZE_MAP() -+ END_KV_SERIALIZE_MAP() -+ }; -+ typedef epee::misc_utils::struct_init response; -+ }; - } - } -diff --git a/src/wallet/wallet_rpc_server_error_codes.h b/src/wallet/wallet_rpc_server_error_codes.h -index 541d29f..4756c19 100644 ---- a/src/wallet/wallet_rpc_server_error_codes.h -+++ b/src/wallet/wallet_rpc_server_error_codes.h -@@ -81,3 +81,5 @@ - #define WALLET_RPC_ERROR_CODE_DISABLED -48 - #define WALLET_RPC_ERROR_CODE_PROXY_ALREADY_DEFINED -49 - #define WALLET_RPC_ERROR_CODE_NONZERO_UNLOCK_TIME -50 -+#define WALLET_RPC_ERROR_CODE_IS_BACKGROUND_WALLET -51 -+#define WALLET_RPC_ERROR_CODE_IS_BACKGROUND_SYNCING -52 -diff --git a/tests/functional_tests/transfer.py b/tests/functional_tests/transfer.py -index 4063911..60eb09a 100755 ---- a/tests/functional_tests/transfer.py -+++ b/tests/functional_tests/transfer.py -@@ -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() -@@ -63,6 +75,8 @@ class TransferTest(): - self.check_is_key_image_spent() - 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') -@@ -840,12 +854,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() -@@ -1127,5 +1135,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/util_resources.py b/tests/functional_tests/util_resources.py -index e030312..3ca6fdb 100755 ---- a/tests/functional_tests/util_resources.py -+++ b/tests/functional_tests/util_resources.py -@@ -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/wallet.py b/tests/functional_tests/wallet.py -index 1ad05c9..8182cec 100755 ---- a/tests/functional_tests/wallet.py -+++ b/tests/functional_tests/wallet.py -@@ -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__': -diff --git a/tests/unit_tests/wipeable_string.cpp b/tests/unit_tests/wipeable_string.cpp -index ef6964f..25121a0 100644 ---- a/tests/unit_tests/wipeable_string.cpp -+++ b/tests/unit_tests/wipeable_string.cpp -@@ -211,3 +211,15 @@ TEST(wipeable_string, to_hex) - ASSERT_TRUE(epee::to_hex::wipeable_string(epee::span((const uint8_t*)"", 0)) == epee::wipeable_string("")); - ASSERT_TRUE(epee::to_hex::wipeable_string(epee::span((const uint8_t*)"abc", 3)) == epee::wipeable_string("616263")); - } -+ -+TEST(wipeable_string, to_string) -+{ -+ // Converting a wipeable_string to a string defeats the purpose of wipeable_string, -+ // but nice to know this works -+ std::string str; -+ { -+ epee::wipeable_string wipeable_str("foo"); -+ str = std::string(wipeable_str.data(), wipeable_str.size()); -+ } -+ ASSERT_TRUE(str == std::string("foo")); -+} -diff --git a/utils/python-rpc/framework/wallet.py b/utils/python-rpc/framework/wallet.py -index 1e10e1f..bff33a5 100644 ---- a/utils/python-rpc/framework/wallet.py -+++ b/utils/python-rpc/framework/wallet.py -@@ -1138,3 +1138,45 @@ class Wallet(object): - 'id': '0' - } - return self.rpc.send_json_rpc_request(frozen) -+ -+ class BackgroundSyncOptions(object): -+ def __init__(self): -+ self.off = 'off' -+ self.reuse_password = 'reuse-wallet-password' -+ self.custom_password = 'custom-background-password' -+ background_sync_options = BackgroundSyncOptions() -+ -+ def setup_background_sync(self, background_sync_type = background_sync_options.off, wallet_password = '', background_cache_password = ''): -+ setup_background_sync = { -+ 'method': 'setup_background_sync', -+ 'jsonrpc': '2.0', -+ 'params' : { -+ 'background_sync_type': background_sync_type, -+ 'wallet_password': wallet_password, -+ 'background_cache_password': background_cache_password, -+ }, -+ 'id': '0' -+ } -+ return self.rpc.send_json_rpc_request(setup_background_sync) -+ -+ def start_background_sync(self): -+ start_background_sync = { -+ 'method': 'start_background_sync', -+ 'jsonrpc': '2.0', -+ 'params' : {}, -+ 'id': '0' -+ } -+ return self.rpc.send_json_rpc_request(start_background_sync) -+ -+ def stop_background_sync(self, wallet_password = '', seed = '', seed_offset = ''): -+ stop_background_sync = { -+ 'method': 'stop_background_sync', -+ 'jsonrpc': '2.0', -+ 'params' : { -+ 'wallet_password': wallet_password, -+ 'seed': seed, -+ 'seed_offset': seed_offset, -+ }, -+ 'id': '0' -+ } -+ return self.rpc.send_json_rpc_request(stop_background_sync) --- -2.39.5 (Apple Git-154) - diff --git a/patches/monero/0003-store-crash-fix.patch b/patches/monero/0002-store-crash-fix.patch similarity index 92% rename from patches/monero/0003-store-crash-fix.patch rename to patches/monero/0002-store-crash-fix.patch index 225f0ab..5a2c16a 100644 --- a/patches/monero/0003-store-crash-fix.patch +++ b/patches/monero/0002-store-crash-fix.patch @@ -1,7 +1,7 @@ -From b49bc52f3d3d1751dd65d4694e4f74b84b23f583 Mon Sep 17 00:00:00 2001 +From b4f4b38af1ab974872862fc20735e41941b955e9 Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Sat, 11 May 2024 16:25:10 +0200 -Subject: [PATCH 03/14] store crash fix +Subject: [PATCH 02/14] store crash fix Monero wallet crashes (sometimes) when it is syncing, while the proper solution (that can be seen in feather) @@ -43,7 +43,7 @@ the current state. 4 files changed, 25 insertions(+), 15 deletions(-) diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp -index e9f76f4..0d85cf3 100644 +index 8fda0bab7..67b170e3d 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -55,8 +55,8 @@ using namespace cryptonote; @@ -135,7 +135,7 @@ index e9f76f4..0d85cf3 100644 diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h -index 1f199a7..ac7ce2f 100644 +index 1f199a72c..ac7ce2f6a 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -273,7 +273,6 @@ private: @@ -147,7 +147,7 @@ index 1f199a7..ac7ce2f 100644 std::atomic m_refreshIntervalMillis; std::atomic m_refreshShouldRescan; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp -index 8146014..7913d55 100644 +index dfb8b23cb..c9c2dbc82 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1192,6 +1192,7 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended, std @@ -158,7 +158,7 @@ index 8146014..7913d55 100644 m_trusted_daemon(false), m_nettype(nettype), m_multisig_rounds_passed(0), -@@ -1404,6 +1405,14 @@ bool wallet2::set_daemon(std::string daemon_address, boost::optionalset_proxy(address); -@@ -4096,7 +4105,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo +@@ -4107,7 +4116,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo // infer when we get an incoming output bool first = true, last = false; @@ -183,19 +183,19 @@ index 8146014..7913d55 100644 uint64_t next_blocks_start_height; std::vector next_blocks; diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h -index b1dc4f7..a050286 100644 +index 2f4ad52f1..daad1e940 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h -@@ -1071,6 +1071,8 @@ private: - boost::optional daemon_login = boost::none, bool trusted_daemon = true, - epee::net_utils::ssl_options_t ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_autodetect); +@@ -1078,6 +1078,8 @@ private: + epee::net_utils::ssl_options_t ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_autodetect, + const std::string &proxy = ""); bool set_proxy(const std::string &address); + bool get_refresh_enabled(); + void set_refresh_enabled(bool val); void stop() { m_run.store(false, std::memory_order_relaxed); m_message_store.stop(); } -@@ -1981,6 +1983,7 @@ private: +@@ -1989,6 +1991,7 @@ private: boost::recursive_mutex m_daemon_rpc_mutex; @@ -204,5 +204,5 @@ index b1dc4f7..a050286 100644 i_wallet2_callback* m_callback; hw::device::device_type m_key_device_type; -- -2.39.5 (Apple Git-154) +2.48.1 diff --git a/patches/monero/0004-uint64_t-missing-definition-fix.patch b/patches/monero/0003-uint64_t-missing-definition-fix.patch similarity index 74% rename from patches/monero/0004-uint64_t-missing-definition-fix.patch rename to patches/monero/0003-uint64_t-missing-definition-fix.patch index 129886b..1b6e305 100644 --- a/patches/monero/0004-uint64_t-missing-definition-fix.patch +++ b/patches/monero/0003-uint64_t-missing-definition-fix.patch @@ -1,14 +1,14 @@ -From 99e6af859234d8b37866e46d95d77e20687767de Mon Sep 17 00:00:00 2001 +From a74f616e3c1671a883182b0db1c6fc11a1242c01 Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Mon, 2 Sep 2024 16:40:31 +0200 -Subject: [PATCH 04/14] uint64_t missing definition fix +Subject: [PATCH 03/14] uint64_t missing definition fix --- contrib/epee/include/net/http_base.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/epee/include/net/http_base.h b/contrib/epee/include/net/http_base.h -index 4af4da7..ae4c0d0 100644 +index 4af4da790..ae4c0d05e 100644 --- a/contrib/epee/include/net/http_base.h +++ b/contrib/epee/include/net/http_base.h @@ -28,7 +28,7 @@ @@ -21,5 +21,5 @@ index 4af4da7..ae4c0d0 100644 #include -- -2.39.5 (Apple Git-154) +2.48.1 diff --git a/patches/monero/0005-use-proper-error-handling-in-get_seed.patch b/patches/monero/0004-use-proper-error-handling-in-get_seed.patch similarity index 87% rename from patches/monero/0005-use-proper-error-handling-in-get_seed.patch rename to patches/monero/0004-use-proper-error-handling-in-get_seed.patch index 3562b7b..33207f3 100644 --- a/patches/monero/0005-use-proper-error-handling-in-get_seed.patch +++ b/patches/monero/0004-use-proper-error-handling-in-get_seed.patch @@ -1,7 +1,7 @@ -From 6be87b2c75ea663e205f468c986809755839afa3 Mon Sep 17 00:00:00 2001 +From f9b222f1611d7cfbaf0ac52cd6194724c7554def Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Mon, 24 Jun 2024 10:49:12 +0200 -Subject: [PATCH 05/14] use proper error handling in get_seed +Subject: [PATCH 04/14] use proper error handling in get_seed --- src/wallet/api/wallet.cpp | 17 ++++++++++++----- @@ -9,7 +9,7 @@ Subject: [PATCH 05/14] use proper error handling in get_seed 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp -index 0d85cf3..dfbf4fd 100644 +index 67b170e3d..89df5c517 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -826,12 +826,19 @@ bool WalletImpl::close(bool store) @@ -38,10 +38,10 @@ index 0d85cf3..dfbf4fd 100644 std::string WalletImpl::getSeedLanguage() const diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp -index 7913d55..debc0c2 100644 +index c9c2dbc82..b827b826f 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp -@@ -1440,11 +1440,13 @@ bool wallet2::get_seed(epee::wipeable_string& electrum_words, const epee::wipeab +@@ -1449,11 +1449,13 @@ bool wallet2::get_seed(epee::wipeable_string& electrum_words, const epee::wipeab bool keys_deterministic = is_deterministic(); if (!keys_deterministic) { @@ -55,7 +55,7 @@ index 7913d55..debc0c2 100644 std::cout << "seed_language not set" << std::endl; return false; } -@@ -1454,8 +1456,9 @@ bool wallet2::get_seed(epee::wipeable_string& electrum_words, const epee::wipeab +@@ -1463,8 +1465,9 @@ bool wallet2::get_seed(epee::wipeable_string& electrum_words, const epee::wipeab key = cryptonote::encrypt_key(key, passphrase); if (!crypto::ElectrumWords::bytes_to_words(key, electrum_words, seed_language)) { @@ -67,5 +67,5 @@ index 7913d55..debc0c2 100644 return true; -- -2.39.5 (Apple Git-154) +2.48.1 diff --git a/patches/monero/0006-UR-functions.patch b/patches/monero/0005-UR-functions.patch similarity index 97% rename from patches/monero/0006-UR-functions.patch rename to patches/monero/0005-UR-functions.patch index c25b2d0..c3cca26 100644 --- a/patches/monero/0006-UR-functions.patch +++ b/patches/monero/0005-UR-functions.patch @@ -1,7 +1,7 @@ -From 3138500cb65a6d72726d611d16cbd4b28d01fbe9 Mon Sep 17 00:00:00 2001 +From ff70c74c8b331758ace1f14df0ca107731dda49d Mon Sep 17 00:00:00 2001 From: tobtoht Date: Tue, 12 Mar 2024 10:09:50 +0100 -Subject: [PATCH 06/14] UR functions +Subject: [PATCH 05/14] UR functions This commit adds UR functions for UR tasks, I believe that the right place to get @@ -38,7 +38,7 @@ Things broken in the commit create mode 160000 external/bc-ur diff --git a/.gitmodules b/.gitmodules -index ffb73fe..72af74d 100644 +index ffb73fe9a..72af74d55 100644 --- a/.gitmodules +++ b/.gitmodules @@ -15,3 +15,7 @@ @@ -50,7 +50,7 @@ index ffb73fe..72af74d 100644 + url = https://github.com/MrCyjaneK/bc-ur + branch = misc diff --git a/CMakeLists.txt b/CMakeLists.txt -index 8fb03ba..63ee825 100644 +index db69b1b04..c73b813d8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -96,7 +96,8 @@ enable_language(C ASM) @@ -72,10 +72,10 @@ index 8fb03ba..63ee825 100644 check_submodule(external/rapidjson) check_submodule(external/trezor-common) diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt -index 5b7f69a..f9ed6a6 100644 +index 538e4d215..074e23f16 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt -@@ -69,4 +69,5 @@ endif() +@@ -70,4 +70,5 @@ endif() add_subdirectory(db_drivers) add_subdirectory(easylogging++) add_subdirectory(qrcodegen) @@ -83,13 +83,13 @@ index 5b7f69a..f9ed6a6 100644 add_subdirectory(randomx EXCLUDE_FROM_ALL) diff --git a/external/bc-ur b/external/bc-ur new file mode 160000 -index 0000000..d82e7c7 +index 000000000..d82e7c753 --- /dev/null +++ b/external/bc-ur @@ -0,0 +1 @@ +Subproject commit d82e7c753e710b8000706dc3383b498438795208 diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp -index 5d0afe1..bb5b6f4 100644 +index 5d0afe1ee..bb5b6f497 100644 --- a/src/device/device_ledger.cpp +++ b/src/device/device_ledger.cpp @@ -313,12 +313,13 @@ namespace hw { @@ -109,7 +109,7 @@ index 5d0afe1..bb5b6f4 100644 boost::lock_guard lock1(device_locker, boost::adopt_lock); \ boost::lock_guard lock2(command_locker, boost::adopt_lock) diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt -index 6095f99..b163212 100644 +index 6095f99d5..b163212b7 100644 --- a/src/wallet/CMakeLists.txt +++ b/src/wallet/CMakeLists.txt @@ -50,6 +50,7 @@ monero_add_library(wallet @@ -121,7 +121,7 @@ index 6095f99..b163212 100644 common cryptonote_core diff --git a/src/wallet/api/pending_transaction.cpp b/src/wallet/api/pending_transaction.cpp -index 70a7027..9c3c26e 100644 +index 70a702796..9c3c26ee5 100644 --- a/src/wallet/api/pending_transaction.cpp +++ b/src/wallet/api/pending_transaction.cpp @@ -42,6 +42,8 @@ @@ -172,7 +172,7 @@ index 70a7027..9c3c26e 100644 { uint64_t result = 0; diff --git a/src/wallet/api/pending_transaction.h b/src/wallet/api/pending_transaction.h -index 0a9779c..403bfe2 100644 +index 0a9779c07..403bfe281 100644 --- a/src/wallet/api/pending_transaction.h +++ b/src/wallet/api/pending_transaction.h @@ -46,6 +46,7 @@ public: @@ -184,7 +184,7 @@ index 0a9779c..403bfe2 100644 uint64_t dust() const override; uint64_t fee() const override; diff --git a/src/wallet/api/unsigned_transaction.cpp b/src/wallet/api/unsigned_transaction.cpp -index 6165a22..fd03e95 100644 +index 6165a2240..fd03e959d 100644 --- a/src/wallet/api/unsigned_transaction.cpp +++ b/src/wallet/api/unsigned_transaction.cpp @@ -40,6 +40,8 @@ @@ -244,7 +244,7 @@ index 6165a22..fd03e95 100644 bool UnsignedTransactionImpl::checkLoadedTx(const std::function get_num_txes, const std::function &get_tx, const std::string &extra_message) { diff --git a/src/wallet/api/unsigned_transaction.h b/src/wallet/api/unsigned_transaction.h -index 30065a7..a94b23f 100644 +index 30065a7fa..a94b23f75 100644 --- a/src/wallet/api/unsigned_transaction.h +++ b/src/wallet/api/unsigned_transaction.h @@ -53,6 +53,7 @@ public: @@ -256,7 +256,7 @@ index 30065a7..a94b23f 100644 uint64_t minMixinCount() const override; diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp -index dfbf4fd..4fb5f75 100644 +index 89df5c517..3fcd6f332 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -47,6 +47,7 @@ @@ -632,7 +632,7 @@ index dfbf4fd..4fb5f75 100644 { if (checkBackgroundSync("cannot scan transactions")) diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h -index ac7ce2f..edf8bb8 100644 +index ac7ce2f6a..edf8bb8ce 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -112,6 +112,7 @@ public: @@ -663,7 +663,7 @@ index ac7ce2f..edf8bb8 100644 bool setupBackgroundSync(const BackgroundSyncType background_sync_type, const std::string &wallet_password, const optional &background_cache_password = optional()) override; diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h -index e349df1..764adbf 100644 +index e349df176..764adbfbf 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h @@ -91,6 +91,7 @@ struct PendingTransaction @@ -754,7 +754,7 @@ index e349df1..764adbf 100644 /*! * \brief scanTransactions - scan a list of transaction ids, this operation may reveal the txids to the remote node and affect your privacy diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp -index debc0c2..dfa3213 100644 +index b827b826f..765cefb32 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -948,6 +948,16 @@ uint32_t get_subaddress_clamped_sum(uint32_t idx, uint32_t extra) @@ -774,7 +774,7 @@ index debc0c2..dfa3213 100644 static void setup_shim(hw::wallet_shim * shim, tools::wallet2 * wallet) { shim->get_tx_pub_key_from_received_outs = std::bind(&tools::wallet2::get_tx_pub_key_from_received_outs, wallet, std::placeholders::_1); -@@ -6986,6 +6996,25 @@ uint64_t wallet2::unlocked_balance(uint32_t index_major, bool strict, uint64_t * +@@ -6997,6 +7007,25 @@ uint64_t wallet2::unlocked_balance(uint32_t index_major, bool strict, uint64_t * return amount; } //---------------------------------------------------------------------------------------------------- @@ -800,7 +800,7 @@ index debc0c2..dfa3213 100644 std::map wallet2::balance_per_subaddress(uint32_t index_major, bool strict) const { std::map amount_per_subaddr; -@@ -7837,9 +7866,7 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector additional_derivations; @@ -811,7 +811,7 @@ index debc0c2..dfa3213 100644 std::vector additional_tx_pub_keys; for (const crypto::secret_key &skey: txs[n].additional_tx_keys) { -@@ -11235,7 +11262,7 @@ std::vector wallet2::create_transactions_2(std::vector wallet2::create_transactions_2(std::vector m_ignore_outputs_above || td.amount() < m_ignore_outputs_below) { -@@ -11285,9 +11312,15 @@ std::vector wallet2::create_transactions_2(std::vector wallet2::create_transactions_2(std::vector>> wallet2::export_key_images(bool all) const { -@@ -14004,53 +14044,60 @@ std::pair> +@@ -14015,53 +14055,60 @@ std::pair> return std::make_pair(offset, ski); } @@ -982,10 +982,10 @@ index debc0c2..dfa3213 100644 ski.push_back(std::make_pair(key_image, signature)); } diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h -index a050286..1d7e430 100644 +index daad1e940..a752f15b9 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h -@@ -1150,6 +1150,7 @@ private: +@@ -1157,6 +1157,7 @@ private: // locked & unlocked balance of given or current subaddress account uint64_t balance(uint32_t subaddr_index_major, bool strict) const; uint64_t unlocked_balance(uint32_t subaddr_index_major, bool strict, uint64_t *blocks_to_unlock = NULL, uint64_t *time_to_unlock = NULL); @@ -993,7 +993,7 @@ index a050286..1d7e430 100644 // locked & unlocked balance per subaddress of given or current subaddress account std::map balance_per_subaddress(uint32_t subaddr_index_major, bool strict) const; std::map>> unlocked_balance_per_subaddress(uint32_t subaddr_index_major, bool strict); -@@ -1624,9 +1625,11 @@ private: +@@ -1631,9 +1632,11 @@ private: std::tuple> export_blockchain() const; void import_blockchain(const std::tuple> &bc); bool export_key_images(const std::string &filename, bool all = false) const; @@ -1006,5 +1006,5 @@ index a050286..1d7e430 100644 bool import_key_images(signed_tx_set & signed_tx, size_t offset=0, bool only_selected_transfers=false); crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const; -- -2.39.5 (Apple Git-154) +2.48.1 diff --git a/patches/monero/0007-add-dummy-device-for-ledger.patch b/patches/monero/0006-add-dummy-device-for-ledger.patch similarity index 97% rename from patches/monero/0007-add-dummy-device-for-ledger.patch rename to patches/monero/0006-add-dummy-device-for-ledger.patch index 3a91bb1..22ee249 100644 --- a/patches/monero/0007-add-dummy-device-for-ledger.patch +++ b/patches/monero/0006-add-dummy-device-for-ledger.patch @@ -1,7 +1,7 @@ -From bf8c0d01e60dade9fbdd77c4ce825e6b37d3c6c3 Mon Sep 17 00:00:00 2001 +From 904fe95204ba02d1a8c81fc46c1423ba1685c94f Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Wed, 26 Jun 2024 15:04:38 +0200 -Subject: [PATCH 07/14] add dummy device for ledger +Subject: [PATCH 06/14] add dummy device for ledger --- CMakeLists.txt | 6 +- @@ -21,7 +21,7 @@ Subject: [PATCH 07/14] add dummy device for ledger create mode 100644 src/device/device_io_dummy.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt -index 63ee825..43ef6cd 100644 +index c73b813d8..5c0f31cb8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -692,8 +692,12 @@ include_directories(${LMDB_INCLUDE}) @@ -39,7 +39,7 @@ index 63ee825..43ef6cd 100644 add_definitions(-DHAVE_HIDAPI) include_directories(${HIDAPI_INCLUDE_DIR}) diff --git a/src/device/CMakeLists.txt b/src/device/CMakeLists.txt -index e4f1159..14d398f 100644 +index e4f1159b5..14d398f87 100644 --- a/src/device/CMakeLists.txt +++ b/src/device/CMakeLists.txt @@ -29,10 +29,11 @@ @@ -69,7 +69,7 @@ index e4f1159..14d398f 100644 ${device_headers} device_ledger.hpp diff --git a/src/device/device.cpp b/src/device/device.cpp -index e6cd358..777584c 100644 +index e6cd358b6..dd0701e0c 100644 --- a/src/device/device.cpp +++ b/src/device/device.cpp @@ -29,7 +29,7 @@ @@ -107,7 +107,7 @@ index e6cd358..777584c 100644 return *device->second; } diff --git a/src/device/device.hpp b/src/device/device.hpp -index 392703a..ffd4197 100644 +index 392703a24..ffd419779 100644 --- a/src/device/device.hpp +++ b/src/device/device.hpp @@ -34,17 +34,7 @@ @@ -131,7 +131,7 @@ index 392703a..ffd4197 100644 diff --git a/src/device/device_io_dummy.cpp b/src/device/device_io_dummy.cpp new file mode 100644 -index 0000000..edb4bee +index 000000000..edb4beea3 --- /dev/null +++ b/src/device/device_io_dummy.cpp @@ -0,0 +1,133 @@ @@ -271,7 +271,7 @@ index 0000000..edb4bee \ No newline at end of file diff --git a/src/device/device_io_dummy.hpp b/src/device/device_io_dummy.hpp new file mode 100644 -index 0000000..a173361 +index 000000000..a1733616d --- /dev/null +++ b/src/device/device_io_dummy.hpp @@ -0,0 +1,74 @@ @@ -350,7 +350,7 @@ index 0000000..a173361 + +#endif // HAVE_HIDAPI diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp -index bb5b6f4..046201a 100644 +index bb5b6f497..046201a1e 100644 --- a/src/device/device_ledger.cpp +++ b/src/device/device_ledger.cpp @@ -41,7 +41,7 @@ namespace hw { @@ -382,7 +382,7 @@ index bb5b6f4..046201a 100644 #ifdef DEBUG_HWDEVICE cryptonote::account_public_address pubkey; diff --git a/src/device/device_ledger.hpp b/src/device/device_ledger.hpp -index 03058c4..39454ca 100644 +index 03058c4f1..39454ca6d 100644 --- a/src/device/device_ledger.hpp +++ b/src/device/device_ledger.hpp @@ -35,6 +35,7 @@ @@ -415,7 +415,7 @@ index 03058c4..39454ca 100644 unsigned char buffer_send[BUFFER_SEND_SIZE]; unsigned int length_recv; diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp -index 4fb5f75..c73f38e 100644 +index 3fcd6f332..25ade04a7 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -48,6 +48,9 @@ @@ -525,7 +525,7 @@ index 4fb5f75..c73f38e 100644 + } // namespace diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h -index edf8bb8..4e9c21e 100644 +index edf8bb8ce..4e9c21ecb 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -301,6 +301,24 @@ private: @@ -554,7 +554,7 @@ index edf8bb8..4e9c21e 100644 diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h -index 764adbf..53ec4ab 100644 +index 764adbfbf..53ec4abfc 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h @@ -1150,6 +1150,18 @@ struct Wallet @@ -577,7 +577,7 @@ index 764adbf..53ec4ab 100644 /** diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp -index e81b8f8..277be6a 100644 +index e81b8f83a..277be6ac9 100644 --- a/src/wallet/api/wallet_manager.cpp +++ b/src/wallet/api/wallet_manager.cpp @@ -188,10 +188,14 @@ bool WalletManagerImpl::verifyWalletPassword(const std::string &keys_file_name, @@ -600,5 +600,5 @@ index e81b8f8..277be6a 100644 std::vector WalletManagerImpl::findWallets(const std::string &path) -- -2.39.5 (Apple Git-154) +2.48.1 diff --git a/patches/monero/0008-polyseed.patch b/patches/monero/0007-polyseed.patch similarity index 96% rename from patches/monero/0008-polyseed.patch rename to patches/monero/0007-polyseed.patch index 9289fc0..b590a33 100644 --- a/patches/monero/0008-polyseed.patch +++ b/patches/monero/0007-polyseed.patch @@ -1,7 +1,7 @@ -From ed2ce176ea5f1bbe81069a3df4601a141b533b95 Mon Sep 17 00:00:00 2001 +From c54ab3f0984159bd47c63b78074caaaac082727a Mon Sep 17 00:00:00 2001 From: tobtoht Date: Tue, 12 Mar 2024 09:42:37 +0100 -Subject: [PATCH 08/14] polyseed +Subject: [PATCH 07/14] polyseed Co-authored-by: Czarek Nakamoto --- @@ -39,7 +39,7 @@ Co-authored-by: Czarek Nakamoto create mode 100644 src/polyseed/polyseed.hpp diff --git a/.gitmodules b/.gitmodules -index 72af74d..b838e84 100644 +index 72af74d55..b838e84e0 100644 --- a/.gitmodules +++ b/.gitmodules @@ -11,6 +11,12 @@ @@ -56,7 +56,7 @@ index 72af74d..b838e84 100644 path = external/supercop url = https://github.com/monero-project/supercop diff --git a/CMakeLists.txt b/CMakeLists.txt -index 43ef6cd..e7fa90a 100644 +index 5c0f31cb8..f0630ef9b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -371,6 +371,8 @@ if(NOT MANUAL_SUBMODULES) @@ -78,7 +78,7 @@ index 43ef6cd..e7fa90a 100644 if(APPLE) cmake_policy(SET CMP0042 NEW) diff --git a/contrib/epee/include/wipeable_string.h b/contrib/epee/include/wipeable_string.h -index 65977cd..594e15d 100644 +index 65977cd97..594e15de4 100644 --- a/contrib/epee/include/wipeable_string.h +++ b/contrib/epee/include/wipeable_string.h @@ -34,6 +34,7 @@ @@ -103,7 +103,7 @@ index 65977cd..594e15d 100644 private: void grow(size_t sz, size_t reserved = 0); diff --git a/contrib/epee/src/wipeable_string.cpp b/contrib/epee/src/wipeable_string.cpp -index b016f2f..f2f365b 100644 +index b016f2f48..f2f365b1b 100644 --- a/contrib/epee/src/wipeable_string.cpp +++ b/contrib/epee/src/wipeable_string.cpp @@ -261,4 +261,14 @@ wipeable_string &wipeable_string::operator=(const wipeable_string &other) @@ -122,10 +122,10 @@ index b016f2f..f2f365b 100644 + } diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt -index f9ed6a6..8fcf792 100644 +index 074e23f16..f7e35f98f 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt -@@ -69,5 +69,7 @@ endif() +@@ -70,5 +70,7 @@ endif() add_subdirectory(db_drivers) add_subdirectory(easylogging++) add_subdirectory(qrcodegen) @@ -135,20 +135,20 @@ index f9ed6a6..8fcf792 100644 add_subdirectory(randomx EXCLUDE_FROM_ALL) diff --git a/external/polyseed b/external/polyseed new file mode 160000 -index 0000000..bd79f50 +index 000000000..bd79f5014 --- /dev/null +++ b/external/polyseed @@ -0,0 +1 @@ +Subproject commit bd79f5014c331273357277ed8a3d756fb61b9fa1 diff --git a/external/utf8proc b/external/utf8proc new file mode 160000 -index 0000000..3de4596 +index 000000000..3de4596fb --- /dev/null +++ b/external/utf8proc @@ -0,0 +1 @@ +Subproject commit 3de4596fbe28956855df2ecb3c11c0bbc3535838 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt -index 3335d3c..06b708c 100644 +index 3335d3c21..06b708cf0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -95,6 +95,7 @@ add_subdirectory(net) @@ -160,7 +160,7 @@ index 3335d3c..06b708c 100644 if(NOT IOS) add_subdirectory(serialization) diff --git a/src/cryptonote_basic/CMakeLists.txt b/src/cryptonote_basic/CMakeLists.txt -index 1414be1..414936a 100644 +index 1414be1b2..414936a05 100644 --- a/src/cryptonote_basic/CMakeLists.txt +++ b/src/cryptonote_basic/CMakeLists.txt @@ -71,6 +71,7 @@ target_link_libraries(cryptonote_basic @@ -172,7 +172,7 @@ index 1414be1..414936a 100644 ${Boost_PROGRAM_OPTIONS_LIBRARY} ${Boost_SERIALIZATION_LIBRARY} diff --git a/src/cryptonote_basic/account.cpp b/src/cryptonote_basic/account.cpp -index 4e87d44..2d556f2 100644 +index 4e87d4477..2d556f285 100644 --- a/src/cryptonote_basic/account.cpp +++ b/src/cryptonote_basic/account.cpp @@ -87,12 +87,16 @@ DISABLE_VS_WARNINGS(4244 4345) @@ -225,7 +225,7 @@ index 4e87d44..2d556f2 100644 { m_keys.m_account_address.m_spend_public_key = spend_public_key; diff --git a/src/cryptonote_basic/account.h b/src/cryptonote_basic/account.h -index 93d1d28..1f76feb 100644 +index 93d1d28f0..1f76febce 100644 --- a/src/cryptonote_basic/account.h +++ b/src/cryptonote_basic/account.h @@ -33,6 +33,7 @@ @@ -263,10 +263,10 @@ index 93d1d28..1f76feb 100644 const account_keys& get_keys() const; std::string get_public_address_str(network_type nettype) const; diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h -index f9e6a6c..3af3a63 100644 +index 82891b9de..26200bf34 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h -@@ -207,6 +207,8 @@ +@@ -211,6 +211,8 @@ #define DNS_BLOCKLIST_LIFETIME (86400 * 8) @@ -277,7 +277,7 @@ index f9e6a6c..3af3a63 100644 // (1+32) + (1+1+16*32) + (1+16*32) = 1060 diff --git a/src/polyseed/CMakeLists.txt b/src/polyseed/CMakeLists.txt new file mode 100644 -index 0000000..cca4eb7 +index 000000000..cca4eb746 --- /dev/null +++ b/src/polyseed/CMakeLists.txt @@ -0,0 +1,25 @@ @@ -308,7 +308,7 @@ index 0000000..cca4eb7 +) diff --git a/src/polyseed/pbkdf2.c b/src/polyseed/pbkdf2.c new file mode 100644 -index 0000000..1c45f47 +index 000000000..1c45f4708 --- /dev/null +++ b/src/polyseed/pbkdf2.c @@ -0,0 +1,85 @@ @@ -400,7 +400,7 @@ index 0000000..1c45f47 \ No newline at end of file diff --git a/src/polyseed/pbkdf2.h b/src/polyseed/pbkdf2.h new file mode 100644 -index 0000000..f6253b9 +index 000000000..f6253b9d7 --- /dev/null +++ b/src/polyseed/pbkdf2.h @@ -0,0 +1,46 @@ @@ -453,7 +453,7 @@ index 0000000..f6253b9 \ No newline at end of file diff --git a/src/polyseed/polyseed.cpp b/src/polyseed/polyseed.cpp new file mode 100644 -index 0000000..231a48a +index 000000000..231a48a94 --- /dev/null +++ b/src/polyseed/polyseed.cpp @@ -0,0 +1,182 @@ @@ -641,7 +641,7 @@ index 0000000..231a48a +} diff --git a/src/polyseed/polyseed.hpp b/src/polyseed/polyseed.hpp new file mode 100644 -index 0000000..2c8c777 +index 000000000..2c8c777a7 --- /dev/null +++ b/src/polyseed/polyseed.hpp @@ -0,0 +1,167 @@ @@ -814,7 +814,7 @@ index 0000000..2c8c777 +#endif //POLYSEED_HPP \ No newline at end of file diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp -index c73f38e..67ac90a 100644 +index 25ade04a7..51911bf99 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -728,6 +728,28 @@ bool WalletImpl::recoverFromDevice(const std::string &path, const std::string &p @@ -902,7 +902,7 @@ index c73f38e..67ac90a 100644 { return m_wallet->get_seed_language(); diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h -index 4e9c21e..32e1228 100644 +index 4e9c21ecb..32e12284b 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -79,9 +79,19 @@ public: @@ -926,7 +926,7 @@ index 4e9c21e..32e1228 100644 void setSeedLanguage(const std::string &arg) override; // void setListener(Listener *) {} diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h -index 53ec4ab..be1c370 100644 +index 53ec4abfc..be1c3704e 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h @@ -709,6 +709,10 @@ struct Wallet @@ -969,7 +969,7 @@ index 53ec4ab..be1c370 100644 * \brief Closes wallet. In case operation succeeded, wallet object deleted. in case operation failed, wallet object not deleted * \param wallet previously opened / created wallet instance diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp -index 277be6a..da2056d 100644 +index 277be6ac9..da2056d8a 100644 --- a/src/wallet/api/wallet_manager.cpp +++ b/src/wallet/api/wallet_manager.cpp @@ -156,6 +156,15 @@ Wallet *WalletManagerImpl::createWalletFromDevice(const std::string &path, @@ -989,7 +989,7 @@ index 277be6a..da2056d 100644 { WalletImpl * wallet_ = dynamic_cast(wallet); diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h -index a223e1d..28fcd36 100644 +index a223e1df9..28fcd36c9 100644 --- a/src/wallet/api/wallet_manager.h +++ b/src/wallet/api/wallet_manager.h @@ -75,6 +75,16 @@ public: @@ -1010,7 +1010,7 @@ index a223e1d..28fcd36 100644 bool walletExists(const std::string &path) override; bool verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool no_spend_key, uint64_t kdf_rounds = 1) const override; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp -index dfa3213..fa346a9 100644 +index 765cefb32..806de969a 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -92,6 +92,7 @@ using namespace epee; @@ -1031,7 +1031,7 @@ index dfa3213..fa346a9 100644 { set_rpc_client_secret_key(rct::rct2sk(rct::skGen())); } -@@ -1474,6 +1476,20 @@ bool wallet2::get_seed(epee::wipeable_string& electrum_words, const epee::wipeab +@@ -1483,6 +1485,20 @@ bool wallet2::get_seed(epee::wipeable_string& electrum_words, const epee::wipeab return true; } //---------------------------------------------------------------------------------------------------- @@ -1052,7 +1052,7 @@ index dfa3213..fa346a9 100644 bool wallet2::get_multisig_seed(epee::wipeable_string& seed, const epee::wipeable_string &passphrase) const { bool ready; -@@ -4789,6 +4805,9 @@ boost::optional wallet2::get_keys_file_data(const crypt +@@ -4800,6 +4816,9 @@ boost::optional wallet2::get_keys_file_data(const crypt value2.SetInt(m_enable_multisig ? 1 : 0); json.AddMember("enable_multisig", value2, json.GetAllocator()); @@ -1062,7 +1062,7 @@ index dfa3213..fa346a9 100644 if (m_background_sync_type == BackgroundSyncCustomPassword && !background_keys_file && m_custom_background_key) { value.SetString(reinterpret_cast(m_custom_background_key.get().data()), m_custom_background_key.get().size()); -@@ -5028,6 +5047,7 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st +@@ -5039,6 +5058,7 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st m_enable_multisig = false; m_allow_mismatched_daemon_version = false; m_custom_background_key = boost::none; @@ -1070,7 +1070,7 @@ index dfa3213..fa346a9 100644 } else if(json.IsObject()) { -@@ -5268,6 +5288,9 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st +@@ -5279,6 +5299,9 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, background_sync_type, BackgroundSyncType, Int, false, BackgroundSyncOff); m_background_sync_type = field_background_sync_type; @@ -1080,7 +1080,7 @@ index dfa3213..fa346a9 100644 // Load encryption key used to encrypt background cache crypto::chacha_key custom_background_key; m_custom_background_key = boost::none; -@@ -5587,6 +5610,48 @@ void wallet2::init_type(hw::device::device_type device_type) +@@ -5598,6 +5621,48 @@ void wallet2::init_type(hw::device::device_type device_type) m_key_device_type = device_type; } @@ -1129,7 +1129,7 @@ index dfa3213..fa346a9 100644 /*! * \brief Generates a wallet or restores one. Assumes the multisig setup * has already completed for the provided multisig info. -@@ -5714,7 +5779,7 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wip +@@ -5725,7 +5790,7 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wip return retval; } @@ -1138,7 +1138,7 @@ index dfa3213..fa346a9 100644 { // -1 month for fluctuations in block time and machine date/time setup. // avg seconds per block -@@ -5738,7 +5803,7 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wip +@@ -5749,7 +5814,7 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wip // the daemon is currently syncing. // If we use the approximate height we subtract one month as // a safety margin. @@ -1147,7 +1147,7 @@ index dfa3213..fa346a9 100644 uint64_t target_height = get_daemon_blockchain_target_height(err); if (err.empty()) { if (target_height < height) -@@ -13635,7 +13700,7 @@ uint64_t wallet2::get_daemon_blockchain_target_height(string &err) +@@ -13646,7 +13711,7 @@ uint64_t wallet2::get_daemon_blockchain_target_height(string &err) return target_height; } @@ -1156,7 +1156,7 @@ index dfa3213..fa346a9 100644 { // time of v2 fork const time_t fork_time = m_nettype == TESTNET ? 1448285909 : m_nettype == STAGENET ? 1520937818 : 1458748658; -@@ -13644,7 +13709,7 @@ uint64_t wallet2::get_approximate_blockchain_height() const +@@ -13655,7 +13720,7 @@ uint64_t wallet2::get_approximate_blockchain_height() const // avg seconds per block const int seconds_per_block = DIFFICULTY_TARGET_V2; // Calculated blockchain height @@ -1165,7 +1165,7 @@ index dfa3213..fa346a9 100644 // testnet and stagenet got some huge rollbacks, so the estimation is way off static const uint64_t approximate_rolled_back_blocks = m_nettype == TESTNET ? 342100 : m_nettype == STAGENET ? 60000 : 30000; if ((m_nettype == TESTNET || m_nettype == STAGENET) && approx_blockchain_height > approximate_rolled_back_blocks) -@@ -15783,15 +15848,6 @@ bool wallet2::parse_uri(const std::string &uri, std::string &address, std::strin +@@ -15794,15 +15859,6 @@ bool wallet2::parse_uri(const std::string &uri, std::string &address, std::strin //---------------------------------------------------------------------------------------------------- uint64_t wallet2::get_blockchain_height_by_date(uint16_t year, uint8_t month, uint8_t day) { @@ -1181,7 +1181,7 @@ index dfa3213..fa346a9 100644 std::tm date = { 0, 0, 0, 0, 0, 0, 0, 0 }; date.tm_year = year - 1900; date.tm_mon = month - 1; -@@ -15800,7 +15856,23 @@ uint64_t wallet2::get_blockchain_height_by_date(uint16_t year, uint8_t month, ui +@@ -15811,7 +15867,23 @@ uint64_t wallet2::get_blockchain_height_by_date(uint16_t year, uint8_t month, ui { throw std::runtime_error("month or day out of range"); } @@ -1206,7 +1206,7 @@ index dfa3213..fa346a9 100644 uint64_t height_min = 0; uint64_t height_max = get_daemon_blockchain_height(err) - 1; diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h -index 1d7e430..91cf2a3 100644 +index a752f15b9..a619bdd15 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -72,6 +72,7 @@ @@ -1238,7 +1238,7 @@ index 1d7e430..91cf2a3 100644 /*! * \brief Generates a wallet or restores one. * \param wallet_ Name of wallet file -@@ -1088,6 +1103,15 @@ private: +@@ -1095,6 +1110,15 @@ private: bool is_deterministic() const; bool get_seed(epee::wipeable_string& electrum_words, const epee::wipeable_string &passphrase = epee::wipeable_string()) const; @@ -1254,7 +1254,7 @@ index 1d7e430..91cf2a3 100644 /*! * \brief Checks if light wallet. A light wallet sends view key to a server where the blockchain is scanned. */ -@@ -1555,8 +1579,8 @@ private: +@@ -1562,8 +1586,8 @@ private: /*! * \brief Calculates the approximate blockchain height from current date/time. */ @@ -1265,7 +1265,7 @@ index 1d7e430..91cf2a3 100644 std::vector select_available_outputs_from_histogram(uint64_t count, bool atleast, bool unlocked, bool allow_rct); std::vector select_available_outputs(const std::function &f); std::vector select_available_unmixable_outputs(); -@@ -1650,6 +1674,7 @@ private: +@@ -1657,6 +1681,7 @@ private: bool parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector &unknown_parameters, std::string &error); uint64_t get_blockchain_height_by_date(uint16_t year, uint8_t month, uint8_t day); // 1<=month<=12, 1<=day<=31 @@ -1273,7 +1273,7 @@ index 1d7e430..91cf2a3 100644 bool is_synced(); -@@ -1995,6 +2020,7 @@ private: +@@ -2003,6 +2028,7 @@ private: std::string seed_language; /*!< Language of the mnemonics (seed). */ bool is_old_file_format; /*!< Whether the wallet file is of an old file format */ bool m_watch_only; /*!< no spend key */ @@ -1282,5 +1282,5 @@ index 1d7e430..91cf2a3 100644 uint32_t m_multisig_threshold; std::vector m_multisig_signers; -- -2.39.5 (Apple Git-154) +2.48.1 diff --git a/patches/monero/0009-coin-control.patch b/patches/monero/0008-coin-control.patch similarity index 97% rename from patches/monero/0009-coin-control.patch rename to patches/monero/0008-coin-control.patch index 5ce3669..442921d 100644 --- a/patches/monero/0009-coin-control.patch +++ b/patches/monero/0008-coin-control.patch @@ -1,7 +1,7 @@ -From 5b7e0a2085f1e9804b0c4ae97f17d84419a1199b Mon Sep 17 00:00:00 2001 +From 753d7eee48d8e9ab5950e48dc984cc1038c11dd1 Mon Sep 17 00:00:00 2001 From: tobtoht Date: Tue, 12 Mar 2024 11:07:57 +0100 -Subject: [PATCH 10/15] coin control +Subject: [PATCH 08/14] coin control --- src/simplewallet/simplewallet.cpp | 2 +- @@ -22,7 +22,7 @@ Subject: [PATCH 10/15] coin control create mode 100644 src/wallet/api/coins_info.h diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp -index 2c51337ef..645bd37e2 100644 +index 89691e9f7..b26817e52 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -6930,7 +6930,7 @@ bool simple_wallet::transfer_main(const std::vector &args_, bool ca @@ -504,7 +504,7 @@ index 000000000..c43e45abd + +#endif //FEATHER_COINS_INFO_H diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp -index 67ac90a46..06837ab61 100644 +index 51911bf99..933cc2531 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -35,6 +35,7 @@ @@ -897,10 +897,10 @@ index be1c3704e..013b5bcba 100644 virtual SubaddressAccount * subaddressAccount() = 0; virtual void setListener(WalletListener *) = 0; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp -index fa346a96e..d060bf95b 100644 +index 806de969a..8720e18b1 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp -@@ -2094,12 +2094,21 @@ bool wallet2::frozen(const multisig_tx_set& txs) const +@@ -2103,12 +2103,21 @@ bool wallet2::frozen(const multisig_tx_set& txs) const return false; } @@ -922,7 +922,7 @@ index fa346a96e..d060bf95b 100644 void wallet2::thaw(const crypto::key_image &ki) { thaw(get_transfer_details(ki)); -@@ -2110,6 +2119,18 @@ bool wallet2::frozen(const crypto::key_image &ki) const +@@ -2119,6 +2128,18 @@ bool wallet2::frozen(const crypto::key_image &ki) const return frozen(get_transfer_details(ki)); } //---------------------------------------------------------------------------------------------------- @@ -941,7 +941,7 @@ index fa346a96e..d060bf95b 100644 size_t wallet2::get_transfer_details(const crypto::key_image &ki) const { for (size_t idx = 0; idx < m_transfers.size(); ++idx) -@@ -2521,6 +2542,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote +@@ -2530,6 +2551,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote uint64_t amount = tx.vout[o].amount ? tx.vout[o].amount : tx_scan_info[o].amount; if (!pool) { @@ -949,7 +949,7 @@ index fa346a96e..d060bf95b 100644 m_transfers.push_back(transfer_details{}); transfer_details& td = m_transfers.back(); td.m_block_height = height; -@@ -2624,6 +2646,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote +@@ -2633,6 +2655,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote uint64_t extra_amount = amount - burnt; if (!pool) { @@ -957,7 +957,7 @@ index fa346a96e..d060bf95b 100644 transfer_details &td = m_transfers[kit->second]; td.m_block_height = height; td.m_internal_output_index = o; -@@ -10500,7 +10523,7 @@ void wallet2::transfer_selected_rct(std::vector picks; float current_output_relatdness = 1.0f; -@@ -10511,6 +10534,9 @@ std::vector wallet2::pick_preferred_rct_inputs(uint64_t needed_money, ui +@@ -10522,6 +10545,9 @@ std::vector wallet2::pick_preferred_rct_inputs(uint64_t needed_money, ui for (size_t i = 0; i < m_transfers.size(); ++i) { const transfer_details& td = m_transfers[i]; @@ -976,7 +976,7 @@ index fa346a96e..d060bf95b 100644 if (!is_spent(td, false) && !td.m_frozen && td.is_rct() && td.amount() >= needed_money && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1) { if (td.amount() > m_ignore_outputs_above || td.amount() < m_ignore_outputs_below) -@@ -10531,6 +10557,9 @@ std::vector wallet2::pick_preferred_rct_inputs(uint64_t needed_money, ui +@@ -10542,6 +10568,9 @@ std::vector wallet2::pick_preferred_rct_inputs(uint64_t needed_money, ui for (size_t i = 0; i < m_transfers.size(); ++i) { const transfer_details& td = m_transfers[i]; @@ -986,7 +986,7 @@ index fa346a96e..d060bf95b 100644 if (!is_spent(td, false) && !td.m_frozen && !td.m_key_image_partial && td.is_rct() && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1) { if (td.amount() > m_ignore_outputs_above || td.amount() < m_ignore_outputs_below) -@@ -10542,6 +10571,9 @@ std::vector wallet2::pick_preferred_rct_inputs(uint64_t needed_money, ui +@@ -10553,6 +10582,9 @@ std::vector wallet2::pick_preferred_rct_inputs(uint64_t needed_money, ui for (size_t j = i + 1; j < m_transfers.size(); ++j) { const transfer_details& td2 = m_transfers[j]; @@ -996,7 +996,7 @@ index fa346a96e..d060bf95b 100644 if (td2.amount() > m_ignore_outputs_above || td2.amount() < m_ignore_outputs_below) { MDEBUG("Ignoring output " << j << " of amount " << print_money(td2.amount()) << " which is outside prescribed range [" << print_money(m_ignore_outputs_below) << ", " << print_money(m_ignore_outputs_above) << "]"); -@@ -11114,7 +11146,7 @@ bool wallet2::light_wallet_key_image_is_ours(const crypto::key_image& key_image, +@@ -11125,7 +11157,7 @@ bool wallet2::light_wallet_key_image_is_ours(const crypto::key_image& key_image, // This system allows for sending (almost) the entire balance, since it does // not generate spurious change in all txes, thus decreasing the instantaneous // usable balance. @@ -1005,7 +1005,7 @@ index fa346a96e..d060bf95b 100644 { //ensure device is let in NONE mode in any case hw::device &hwdev = m_account.get_device(); -@@ -11322,6 +11354,9 @@ std::vector wallet2::create_transactions_2(std::vector wallet2::create_transactions_2(std::vector wallet2::create_transactions_2(std::vector wallet2::create_transactions_2(std::vector &ptx_vector, c +@@ -11903,7 +11938,7 @@ bool wallet2::sanity_check(const std::vector &ptx_vector, c return true; } @@ -1033,7 +1033,7 @@ index fa346a96e..d060bf95b 100644 { std::vector unused_transfers_indices; std::vector unused_dust_indices; -@@ -11921,6 +11956,9 @@ std::vector wallet2::create_transactions_all(uint64_t below +@@ -11932,6 +11967,9 @@ std::vector wallet2::create_transactions_all(uint64_t below for (size_t i = 0; i < m_transfers.size(); ++i) { const transfer_details& td = m_transfers[i]; @@ -1044,10 +1044,10 @@ index fa346a96e..d060bf95b 100644 { MDEBUG("Ignoring output " << i << " of amount " << print_money(td.amount()) << " which is below threshold " << print_money(fractional_threshold)); diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h -index 91cf2a376..bc16d528c 100644 +index a619bdd15..4f324c238 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h -@@ -1209,8 +1209,8 @@ private: +@@ -1216,8 +1216,8 @@ private: bool parse_unsigned_tx_from_str(const std::string &unsigned_tx_st, unsigned_tx_set &exported_txs) const; bool load_tx(const std::string &signed_filename, std::vector &ptx, std::function accept_func = NULL); bool parse_tx_from_str(const std::string &signed_tx_st, std::vector &ptx, std::function accept_func); @@ -1058,7 +1058,7 @@ index 91cf2a376..bc16d528c 100644 std::vector create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, uint32_t priority, const std::vector& extra); std::vector create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, std::vector unused_transfers_indices, std::vector unused_dust_indices, const size_t fake_outs_count, uint32_t priority, const std::vector& extra); bool sanity_check(const std::vector &ptx_vector, const std::vector& dsts, const unique_index_container& subtract_fee_from_outputs = {}) const; -@@ -1562,6 +1562,7 @@ private: +@@ -1569,6 +1569,7 @@ private: uint64_t get_num_rct_outputs(); size_t get_num_transfer_details() const { return m_transfers.size(); } const transfer_details &get_transfer_details(size_t idx) const; @@ -1066,7 +1066,7 @@ index 91cf2a376..bc16d528c 100644 uint8_t get_current_hard_fork(); void get_hard_fork_info(uint8_t version, uint64_t &earliest_height); -@@ -1793,7 +1794,9 @@ private: +@@ -1800,7 +1801,9 @@ private: void freeze(size_t idx); void thaw(size_t idx); bool frozen(size_t idx) const; @@ -1076,7 +1076,7 @@ index 91cf2a376..bc16d528c 100644 void thaw(const crypto::key_image &ki); bool frozen(const crypto::key_image &ki) const; bool frozen(const transfer_details &td) const; -@@ -1834,6 +1837,8 @@ private: +@@ -1841,6 +1844,8 @@ private: static std::string get_default_daemon_address() { CRITICAL_REGION_LOCAL(default_daemon_address_lock); return default_daemon_address; } @@ -1085,7 +1085,7 @@ index 91cf2a376..bc16d528c 100644 private: /*! * \brief Stores wallet information to wallet file. -@@ -1905,7 +1910,7 @@ private: +@@ -1912,7 +1917,7 @@ private: std::vector get_unspent_amounts_vector(bool strict); uint64_t get_dynamic_base_fee_estimate(); float get_output_relatedness(const transfer_details &td0, const transfer_details &td1) const; @@ -1095,5 +1095,5 @@ index 91cf2a376..bc16d528c 100644 void set_unspent(size_t idx); bool is_spent(const transfer_details &td, bool strict = true) const; -- -2.39.5 (Apple Git-154) +2.48.1 diff --git a/patches/monero/0010-Add-hex-encoding-and-tx-key-getter-for-PendingTransc.patch b/patches/monero/0009-Add-hex-encoding-and-tx-key-getter-for-PendingTransc.patch similarity index 89% rename from patches/monero/0010-Add-hex-encoding-and-tx-key-getter-for-PendingTransc.patch rename to patches/monero/0009-Add-hex-encoding-and-tx-key-getter-for-PendingTransc.patch index 6917daa..9e04787 100644 --- a/patches/monero/0010-Add-hex-encoding-and-tx-key-getter-for-PendingTransc.patch +++ b/patches/monero/0009-Add-hex-encoding-and-tx-key-getter-for-PendingTransc.patch @@ -1,7 +1,7 @@ -From 42aca87b69ecdc9fb24d14dd947f8bbffc7f3500 Mon Sep 17 00:00:00 2001 +From 0ba0339b11d8f7f66f2d5cd27075d438a76351b4 Mon Sep 17 00:00:00 2001 From: M Date: Fri, 21 Apr 2023 15:43:47 -0400 -Subject: [PATCH 10/14] Add hex encoding and tx key getter for +Subject: [PATCH 09/14] Add hex encoding and tx key getter for PendingTransction in wallet api. --- @@ -11,7 +11,7 @@ Subject: [PATCH 10/14] Add hex encoding and tx key getter for 3 files changed, 20 insertions(+) diff --git a/src/wallet/api/pending_transaction.cpp b/src/wallet/api/pending_transaction.cpp -index 9c3c26e..1f714d2 100644 +index 9c3c26ee5..1f714d229 100644 --- a/src/wallet/api/pending_transaction.cpp +++ b/src/wallet/api/pending_transaction.cpp @@ -80,6 +80,22 @@ std::vector PendingTransactionImpl::txid() const @@ -38,7 +38,7 @@ index 9c3c26e..1f714d2 100644 { diff --git a/src/wallet/api/pending_transaction.h b/src/wallet/api/pending_transaction.h -index 403bfe2..0cc6c58 100644 +index 403bfe281..0cc6c58e9 100644 --- a/src/wallet/api/pending_transaction.h +++ b/src/wallet/api/pending_transaction.h @@ -59,6 +59,8 @@ public: @@ -51,7 +51,7 @@ index 403bfe2..0cc6c58 100644 private: friend class WalletImpl; diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h -index 013b5bc..f421fdc 100644 +index 013b5bcba..f421fdc05 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h @@ -127,6 +127,8 @@ struct PendingTransaction @@ -64,5 +64,5 @@ index 013b5bc..f421fdc 100644 /** -- -2.39.5 (Apple Git-154) +2.48.1 diff --git a/patches/monero/0011-Add-recoverDeterministicWalletFromSpendKey.patch b/patches/monero/0010-Add-recoverDeterministicWalletFromSpendKey.patch similarity index 96% rename from patches/monero/0011-Add-recoverDeterministicWalletFromSpendKey.patch rename to patches/monero/0010-Add-recoverDeterministicWalletFromSpendKey.patch index 12d82ff..022604d 100644 --- a/patches/monero/0011-Add-recoverDeterministicWalletFromSpendKey.patch +++ b/patches/monero/0010-Add-recoverDeterministicWalletFromSpendKey.patch @@ -1,7 +1,7 @@ -From ccddf3be96fd08d1eccbb58a7e8f8c98d07b07f2 Mon Sep 17 00:00:00 2001 +From cb02355313d504e6a44f8f70b8eb2be64167ffd4 Mon Sep 17 00:00:00 2001 From: Konstantin Ullrich Date: Wed, 11 Oct 2023 16:47:59 +0200 -Subject: [PATCH 11/14] Add recoverDeterministicWalletFromSpendKey +Subject: [PATCH 10/14] Add recoverDeterministicWalletFromSpendKey This function is used by Cake Wallet to enable polyseed (dart implementation) support. @@ -19,7 +19,7 @@ Co-authored-by: Godwin Asuquo 5 files changed, 75 insertions(+) diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp -index 6bb3a21..5734aff 100644 +index 933cc2531..d8fe108b4 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -824,6 +824,35 @@ bool WalletImpl::recover(const std::string &path, const std::string &password, c @@ -59,7 +59,7 @@ index 6bb3a21..5734aff 100644 { diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h -index a82f270..9e1fbb4 100644 +index a82f270e4..9e1fbb40b 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -77,6 +77,10 @@ public: @@ -74,7 +74,7 @@ index a82f270..9e1fbb4 100644 const std::string &password, const std::string &device_name); diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h -index f421fdc..c8d6bb1 100644 +index f421fdc05..c8d6bb179 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h @@ -1323,6 +1323,25 @@ struct WalletManager @@ -104,7 +104,7 @@ index f421fdc..c8d6bb1 100644 * \deprecated this method creates a wallet WITHOUT a passphrase, use createWalletFromKeys(..., password, ...) instead * \brief recovers existing wallet using keys. Creates a view only wallet if spend key is omitted diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp -index da2056d..c200f52 100644 +index da2056d8a..c200f52ae 100644 --- a/src/wallet/api/wallet_manager.cpp +++ b/src/wallet/api/wallet_manager.cpp @@ -127,6 +127,22 @@ Wallet *WalletManagerImpl::createWalletFromKeys(const std::string &path, @@ -131,7 +131,7 @@ index da2056d..c200f52 100644 const std::string &password, NetworkType nettype, diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h -index 28fcd36..be3ff81 100644 +index 28fcd36c9..be3ff8184 100644 --- a/src/wallet/api/wallet_manager.h +++ b/src/wallet/api/wallet_manager.h @@ -67,6 +67,13 @@ public: @@ -149,5 +149,5 @@ index 28fcd36..be3ff81 100644 const std::string &password, NetworkType nettype, -- -2.39.5 (Apple Git-154) +2.48.1 diff --git a/patches/monero/0012-add-monero-submodule-support.patch b/patches/monero/0011-add-monero-submodule-support.patch similarity index 90% rename from patches/monero/0012-add-monero-submodule-support.patch rename to patches/monero/0011-add-monero-submodule-support.patch index d993a14..cbe6f35 100644 --- a/patches/monero/0012-add-monero-submodule-support.patch +++ b/patches/monero/0011-add-monero-submodule-support.patch @@ -1,7 +1,7 @@ -From d8ab8f6fffd6235e935400f0fe750c1ba29b85e7 Mon Sep 17 00:00:00 2001 +From 846d3f60093add6653d9102d841288066fc08311 Mon Sep 17 00:00:00 2001 From: cyan Date: Thu, 7 Nov 2024 16:46:24 +0000 -Subject: [PATCH 12/14] add monero submodule support +Subject: [PATCH 11/14] add monero submodule support --- CMakeLists.txt | 6 +++--- @@ -10,7 +10,7 @@ Subject: [PATCH 12/14] add monero submodule support 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt -index e7fa90a..b995a68 100644 +index f0630ef9b..9406e57b4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -223,9 +223,9 @@ function(forbid_undefined_symbols) @@ -35,7 +35,7 @@ index e7fa90a..b995a68 100644 if (NOT DEFINED ENV{DEVELOPER_LOCAL_TOOLS}) message(STATUS "Could not find DEVELOPER_LOCAL_TOOLS in env (not required)") diff --git a/cmake/CheckLinkerFlag.cmake b/cmake/CheckLinkerFlag.cmake -index 7ecf5f6..89fb9d1 100644 +index 7ecf5f610..89fb9d167 100644 --- a/cmake/CheckLinkerFlag.cmake +++ b/cmake/CheckLinkerFlag.cmake @@ -6,7 +6,7 @@ macro(CHECK_LINKER_FLAG flag VARIABLE) @@ -48,10 +48,10 @@ index 7ecf5f6..89fb9d1 100644 set(saved_CMAKE_C_FLAGS ${CMAKE_C_FLAGS}) set(CMAKE_C_FLAGS "${flag}") diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp -index d24b4c5..91f6925 100644 +index 3188c88db..9fbdb3c05 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp -@@ -1163,7 +1163,7 @@ namespace tools +@@ -1184,7 +1184,7 @@ namespace tools { uint64_t mixin = m_wallet->adjust_mixin(req.ring_size ? req.ring_size - 1 : 0); uint32_t priority = m_wallet->adjust_priority(req.priority); @@ -61,5 +61,5 @@ index d24b4c5..91f6925 100644 if (ptx_vector.empty()) { -- -2.39.5 (Apple Git-154) +2.48.1 diff --git a/patches/monero/0013-fix-iOS-depends-build.patch b/patches/monero/0012-fix-iOS-depends-build.patch similarity index 93% rename from patches/monero/0013-fix-iOS-depends-build.patch rename to patches/monero/0012-fix-iOS-depends-build.patch index 97c23c6..dcf7c86 100644 --- a/patches/monero/0013-fix-iOS-depends-build.patch +++ b/patches/monero/0012-fix-iOS-depends-build.patch @@ -1,7 +1,7 @@ -From dcb483e5cb97c4e3c500355d8225bd49d3e0ae7f Mon Sep 17 00:00:00 2001 +From 53cc0482e55a39b5dbf2261fea11fcb160778800 Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Thu, 21 Nov 2024 06:05:03 -0500 -Subject: [PATCH 13/14] fix iOS depends build +Subject: [PATCH 12/14] fix iOS depends build --- CMakeLists.txt | 4 ---- @@ -11,7 +11,7 @@ Subject: [PATCH 13/14] fix iOS depends build 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt -index b995a68..4e86328 100644 +index 9406e57b4..1eac121db 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,10 +39,6 @@ include(CheckLibraryExists) @@ -26,7 +26,7 @@ index b995a68..4e86328 100644 message(STATUS "CMake version ${CMAKE_VERSION}") diff --git a/src/checkpoints/CMakeLists.txt b/src/checkpoints/CMakeLists.txt -index 665441f..841df32 100644 +index 665441f62..841df3256 100644 --- a/src/checkpoints/CMakeLists.txt +++ b/src/checkpoints/CMakeLists.txt @@ -28,7 +28,11 @@ @@ -43,7 +43,7 @@ index 665441f..841df32 100644 find_library(IOKIT_LIBRARY IOKit) mark_as_advanced(IOKIT_LIBRARY) diff --git a/src/cryptonote_basic/CMakeLists.txt b/src/cryptonote_basic/CMakeLists.txt -index 414936a..81c8176 100644 +index 414936a05..81c81767f 100644 --- a/src/cryptonote_basic/CMakeLists.txt +++ b/src/cryptonote_basic/CMakeLists.txt @@ -28,7 +28,11 @@ @@ -60,7 +60,7 @@ index 414936a..81c8176 100644 find_library(IOKIT_LIBRARY IOKit) mark_as_advanced(IOKIT_LIBRARY) diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp -index 71b8f78..0f53f02 100644 +index 71b8f78cc..0f53f024e 100644 --- a/src/cryptonote_basic/miner.cpp +++ b/src/cryptonote_basic/miner.cpp @@ -45,7 +45,7 @@ @@ -100,5 +100,5 @@ index 71b8f78..0f53f02 100644 #if TARGET_OS_MAC && (!defined(MAC_OS_X_VERSION_MIN_REQUIRED) || MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_7) return boost::logic::tribool(IOPSGetTimeRemainingEstimate() != kIOPSTimeRemainingUnlimited); -- -2.39.5 (Apple Git-154) +2.48.1 diff --git a/patches/monero/0013-include-locale-only-when-targeting-WIN32.patch b/patches/monero/0013-include-locale-only-when-targeting-WIN32.patch new file mode 100644 index 0000000..64ef6aa --- /dev/null +++ b/patches/monero/0013-include-locale-only-when-targeting-WIN32.patch @@ -0,0 +1,45 @@ +From 1eacd30724559749be5adeb31d763f44c3f221f9 Mon Sep 17 00:00:00 2001 +From: Czarek Nakamoto +Date: Mon, 18 Nov 2024 10:57:37 -0500 +Subject: [PATCH 13/14] include locale only when targeting WIN32 + + +--- + CMakeLists.txt | 6 +++++- + src/wallet/api/wallet.cpp | 2 ++ + 2 files changed, 7 insertions(+), 1 deletion(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 1eac121db..3a4e8f7e1 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -1089,7 +1089,11 @@ if(NOT Boost_FOUND) + elseif(Boost_FOUND) + message(STATUS "Found Boost Version: ${Boost_VERSION_STRING}") + +- set(BOOST_COMPONENTS filesystem thread date_time chrono serialization program_options locale) ++ set(BOOST_COMPONENTS filesystem thread date_time chrono serialization program_options) ++ ++ if(WIN32) ++ list(APPEND BOOST_COMPONENTS locale) ++ endif() + + # Boost System is header-only since 1.69 + if (Boost_VERSION_STRING VERSION_LESS 1.69.0) +diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp +index d8fe108b4..e3e838b13 100644 +--- a/src/wallet/api/wallet.cpp ++++ b/src/wallet/api/wallet.cpp +@@ -46,7 +46,9 @@ + #include + #include + ++#ifdef WIN32 + #include ++#endif + #include + #include "bc-ur/src/bc-ur.hpp" + #if defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI) +-- +2.48.1 + diff --git a/patches/monero/0015-change-earliest-fork-height-message.patch b/patches/monero/0014-change-earliest-fork-height-message.patch similarity index 83% rename from patches/monero/0015-change-earliest-fork-height-message.patch rename to patches/monero/0014-change-earliest-fork-height-message.patch index 0a0bd2c..e73f776 100644 --- a/patches/monero/0015-change-earliest-fork-height-message.patch +++ b/patches/monero/0014-change-earliest-fork-height-message.patch @@ -1,17 +1,17 @@ -From d47284456194c6d99698b28908d753acf1a64010 Mon Sep 17 00:00:00 2001 +From 2d31234e859bff817d30d91b21d9412375668aae Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Wed, 29 Jan 2025 16:13:28 +0100 -Subject: [PATCH] change earliest fork height message +Subject: [PATCH 14/14] change earliest fork height message --- src/wallet/wallet2.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp -index d060bf95b..9c1574c02 100644 +index 8720e18b1..69da11d9c 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp -@@ -12354,7 +12354,7 @@ bool wallet2::use_fork_rules(uint8_t version, int64_t early_blocks) +@@ -12365,7 +12365,7 @@ bool wallet2::use_fork_rules(uint8_t version, int64_t early_blocks) boost::optional result = m_node_rpc_proxy.get_height(height); THROW_WALLET_EXCEPTION_IF(result, error::wallet_internal_error, "Failed to get height"); result = m_node_rpc_proxy.get_earliest_height(version, earliest_height); diff --git a/patches/monero/0014-include-locale-only-when-targeting-WIN32.patch b/patches/monero/0014-include-locale-only-when-targeting-WIN32.patch deleted file mode 100644 index 4cd1d0c..0000000 --- a/patches/monero/0014-include-locale-only-when-targeting-WIN32.patch +++ /dev/null @@ -1,43 +0,0 @@ -From dfe8fde34cd51f5d5ec95fb464737f6e447d41bb Mon Sep 17 00:00:00 2001 -From: Czarek Nakamoto -Date: Mon, 18 Nov 2024 10:57:37 -0500 -Subject: [PATCH 14/14] include locale only when targeting WIN32 - ---- - CMakeLists.txt | 5 ++++- - src/wallet/api/wallet.cpp | 2 ++ - 2 files changed, 6 insertions(+), 1 deletion(-) - -diff --git a/CMakeLists.txt b/CMakeLists.txt -index 4e86328..809bce6 100644 ---- a/CMakeLists.txt -+++ b/CMakeLists.txt -@@ -1079,7 +1079,10 @@ if(STATIC) - set(Boost_USE_STATIC_LIBS ON) - set(Boost_USE_STATIC_RUNTIME ON) - endif() --find_package(Boost 1.58 QUIET REQUIRED COMPONENTS system filesystem thread date_time chrono regex serialization program_options locale) -+if(WIN32) -+ set(BOOST_LOCALE locale) -+endif() -+find_package(Boost 1.58 QUIET REQUIRED COMPONENTS system filesystem thread date_time chrono regex serialization program_options ${BOOST_LOCALE}) - add_definitions(-DBOOST_ASIO_ENABLE_SEQUENTIAL_STRAND_ALLOCATION) - - set(CMAKE_FIND_LIBRARY_SUFFIXES ${OLD_LIB_SUFFIXES}) -diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp -index 5734aff..f4fb093 100644 ---- a/src/wallet/api/wallet.cpp -+++ b/src/wallet/api/wallet.cpp -@@ -46,7 +46,9 @@ - #include - #include - -+#ifdef WIN32 - #include -+#endif - #include - #include "bc-ur/src/bc-ur.hpp" - #if defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI) --- -2.39.5 (Apple Git-154) - diff --git a/patches/monero/0015-remove-trivially_copyable-assert.patch b/patches/monero/0015-remove-trivially_copyable-assert.patch new file mode 100644 index 0000000..d78268f --- /dev/null +++ b/patches/monero/0015-remove-trivially_copyable-assert.patch @@ -0,0 +1,24 @@ +From 71cf45cfbd571ec58e8b2a1d408ff74804bf7e1d Mon Sep 17 00:00:00 2001 +From: Czarek Nakamoto +Date: Thu, 20 Feb 2025 08:36:28 +0100 +Subject: [PATCH] remove trivially_copyable assert + +--- + contrib/epee/include/span.h | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/contrib/epee/include/span.h b/contrib/epee/include/span.h +index 01dc387d6..2ad733a2f 100644 +--- a/contrib/epee/include/span.h ++++ b/contrib/epee/include/span.h +@@ -162,7 +162,6 @@ namespace epee + { + static_assert(!std::is_empty(), "empty types will not work -> sizeof == 1"); + static_assert(std::is_standard_layout(), "type must have standard layout"); +- static_assert(std::is_trivially_copyable(), "type must be trivially copyable"); + static_assert(alignof(T) == 1, "type may have padding"); + return {reinterpret_cast(std::addressof(src)), sizeof(T)}; + } +-- +2.48.1 +