From c765eca1fbbfde6165d5ed5e56276878b3496c77 Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Thu, 23 Jul 2026 09:51:20 +0200 Subject: use FCMP branch, bump simplybs, rebase patches --- .gitmodules | 2 +- build_single.sh | 16 +- contrib/depends/Makefile | 8 +- monero | 2 +- monero_libwallet2_api_c/CMakeLists.txt | 3 +- ...issing-___clear_cache-when-targetting-iOS.patch | 34 - patches/monero/0001-store-crash-fix.patch | 335 ++++++ patches/monero/0002-store-crash-fix.patch | 336 ------ .../0002-uint64_t-missing-definition-fix.patch | 25 + .../0003-uint64_t-missing-definition-fix.patch | 25 - ...003-use-proper-error-handling-in-get_seed.patch | 71 ++ ...issing-___clear_cache-when-targetting-iOS.patch | 33 + ...004-use-proper-error-handling-in-get_seed.patch | 71 -- patches/monero/0005-UR-functions.patch | 144 ++- .../monero/0006-add-dummy-device-for-ledger.patch | 60 +- patches/monero/0007-polyseed.patch | 174 ++-- patches/monero/0008-coin-control.patch | 163 ++- ...oding-and-tx-key-getter-for-PendingTransc.patch | 16 +- ...dd-recoverDeterministicWalletFromSpendKey.patch | 24 +- .../monero/0011-add-monero-submodule-support.patch | 32 +- patches/monero/0012-fix-iOS-depends-build.patch | 65 +- ...-include-locale-only-when-targeting-WIN32.patch | 40 +- .../0014-change-earliest-fork-height-message.patch | 10 +- .../0015-remove-trivially_copyable-assert.patch | 30 +- patches/monero/0016-serialize-cache-to-JSON.patch | 141 +-- ...enerate_translations_header.c-requirement.patch | 14 +- ...s-remove-icu4c-monero-project-monero-8880.patch | 21 +- patches/monero/0019-fix-mingw-build-issues.patch | 10 +- patches/monero/0020-fix-remove-flaky-test.patch | 10 +- .../0021-trezor-import-export-sign-functions.patch | 1015 ------------------ ...rezor-import-export-sign-submit-functions.patch | 1099 ++++++++++++++++++++ ...ple-ios-rust-target-when-building-for-iOS.patch | 33 + 32 files changed, 2068 insertions(+), 1994 deletions(-) delete mode 100644 patches/monero/0001-fix-missing-___clear_cache-when-targetting-iOS.patch create mode 100644 patches/monero/0001-store-crash-fix.patch delete mode 100644 patches/monero/0002-store-crash-fix.patch create mode 100644 patches/monero/0002-uint64_t-missing-definition-fix.patch delete mode 100644 patches/monero/0003-uint64_t-missing-definition-fix.patch create mode 100644 patches/monero/0003-use-proper-error-handling-in-get_seed.patch create mode 100644 patches/monero/0004-fix-missing-___clear_cache-when-targetting-iOS.patch delete mode 100644 patches/monero/0004-use-proper-error-handling-in-get_seed.patch delete mode 100644 patches/monero/0021-trezor-import-export-sign-functions.patch create mode 100644 patches/monero/0021-trezor-import-export-sign-submit-functions.patch create mode 100644 patches/monero/0022-fix-use-apple-ios-rust-target-when-building-for-iOS.patch diff --git a/.gitmodules b/.gitmodules index 5a3c641..4c6be37 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "monero"] path = monero - url = https://github.com/monero-project/monero + url = https://github.com/seraphis-migration/monero branch = release-v0.18 shallow = true [submodule "wownero"] diff --git a/build_single.sh b/build_single.sh index ec2d760..f130d97 100755 --- a/build_single.sh +++ b/build_single.sh @@ -63,8 +63,20 @@ pushd contrib/depends env PATH="$PATH" make "$NPROC" HOST="$HOST_ABI" BOOST_VERSION="${sbs_BOOST_VERSION}" popd # source contrib/depends/_native/_source_me -source contrib/depends/$HOST_ABI/_source_me -export PATH="$(pwd)/contrib/depends/_native/bin/:$(pwd)/contrib/depends/$HOST_ABI/native/bin:$PATH" +source ./contrib/depends/_native/_source_me_export@native____ +source ./contrib/depends/_native/_source_me_export@native__ +source ./contrib/depends/$HOST_ABI/_source_me_export@rust-std +export PREFIX="$(pwd)/contrib/depends/$HOST_ABI" +export HOST="$HOST_ABI" +export TARGET="$HOST_ABI" +export HOME="$(pwd)/contrib/depends/_native/home/user" + +export PATH="$(pwd)/contrib/depends/_native/bin/:$(pwd)/contrib/depends/_native/_/bin" + +pushd contrib/depends + env HOST=$HOST_ABI ./gen_toolchain.cmake.sh $HOST_ABI/share/toolchain.cmake +popd +# source contrib/depends/$HOST_ABI/_source_me buildType=Release diff --git a/contrib/depends/Makefile b/contrib/depends/Makefile index 11f0d82..e91cd0f 100644 --- a/contrib/depends/Makefile +++ b/contrib/depends/Makefile @@ -3,8 +3,8 @@ HOST ?= BOOST_VERSION ?= 1_90_0 -PACKAGES = native/protobuf,native/cmake,native/python@3.14,native/git,native/_,native/make,native/cmake-toolchain,icu4c,boost@$(BOOST_VERSION),zeromq,unbound,sodium,openssl,libiconv,zlib,protobuf -SIMPLYBS_HASH = f060b28a0cf32f9ae626a143a029f484bf7b31a6 +PACKAGES = native/_/_,native/libxml2,native/curl,native/libpsl,native/pkgconf,native/rust-toolchain,rust-std,native/protobuf,native/cmake,native/sed,native/python@3.14,native/git,native/_,native/make,native/cmake-toolchain,boost@$(BOOST_VERSION),zeromq,unbound,sodium,openssl,libiconv,zlib,protobuf +SIMPLYBS_HASH = 91f33785859d30ccca13277ae1b6e442abea978b all: simplybs $(if $(HOST),sbs-build-$(HOST),) @if [ -z "$(HOST)" ]; then \ @@ -30,8 +30,8 @@ simplybs: cd simplybs && git reset --hard sbs-build-%: - # cd simplybs && env SIMPLYBS_ENV_NATIVE_DIR=$(PWD)/_native SIMPLYBS_ENV_DIR=$(PWD)/$* go run . -cleanup cd simplybs && env SIMPLYBS_NATIVE_ENV_DIR=$(PWD)/_native SIMPLYBS_ENV_DIR=$(PWD)/$* go run . -host $* -package $(PACKAGES) -build cd simplybs && rm -rf $(PWD)/$* + cd simplybs && rm -rf $(PWD)/_native cd simplybs && env SIMPLYBS_NATIVE_ENV_DIR=$(PWD)/_native SIMPLYBS_ENV_DIR=$(PWD)/$* go run . -host $* -package $(PACKAGES) -extract - env HOST=$* ./gen_toolchain.cmake.sh $*/share/toolchain.cmake + echo ok diff --git a/monero b/monero index dbcc7d2..70600ee 160000 --- a/monero +++ b/monero @@ -1 +1 @@ -Subproject commit dbcc7d212c094bd1a45f7291dbb99a4b4627a96d +Subproject commit 70600ee4d6ef49d7d12de59811370f07792199de diff --git a/monero_libwallet2_api_c/CMakeLists.txt b/monero_libwallet2_api_c/CMakeLists.txt index ab6a3b4..ae5a2fc 100644 --- a/monero_libwallet2_api_c/CMakeLists.txt +++ b/monero_libwallet2_api_c/CMakeLists.txt @@ -4,7 +4,8 @@ message(STATUS HOST_ABI = ${HOST_ABI}) set(MD_LIBRARY "") -set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON) if(${HOST_ABI} STREQUAL "x86_64-w64-mingw32") diff --git a/patches/monero/0001-fix-missing-___clear_cache-when-targetting-iOS.patch b/patches/monero/0001-fix-missing-___clear_cache-when-targetting-iOS.patch deleted file mode 100644 index 888a502..0000000 --- a/patches/monero/0001-fix-missing-___clear_cache-when-targetting-iOS.patch +++ /dev/null @@ -1,34 +0,0 @@ -From 41d9f755a1392dd116241acb32b887091669f090 Mon Sep 17 00:00:00 2001 -From: Czarek Nakamoto -Date: Tue, 2 Apr 2024 16:51:56 +0200 -Subject: [PATCH 01/20] fix missing ___clear_cache when targetting iOS - ---- - .gitmodules | 3 ++- - external/randomx | 2 +- - 2 files changed, 3 insertions(+), 2 deletions(-) - -diff --git a/.gitmodules b/.gitmodules -index 721cce3b4..ffb73fe9a 100644 ---- a/.gitmodules -+++ b/.gitmodules -@@ -9,7 +9,8 @@ - url = https://github.com/trezor/trezor-common.git - [submodule "external/randomx"] - path = external/randomx -- url = https://github.com/tevador/RandomX -+ url = https://github.com/MrCyjaneK/RandomX -+ branch = cyjan-fix-ios - [submodule "external/supercop"] - path = external/supercop - url = https://github.com/monero-project/supercop -diff --git a/external/randomx b/external/randomx -index 102f8acf9..5dfeeb30e 160000 ---- a/external/randomx -+++ b/external/randomx -@@ -1 +1 @@ --Subproject commit 102f8acf90a7649ada410de5499a7ec62e49e1da -+Subproject commit 5dfeeb30ec3446ec9d348153767abc324436c56c --- -2.50.1 (Apple Git-155) - diff --git a/patches/monero/0001-store-crash-fix.patch b/patches/monero/0001-store-crash-fix.patch new file mode 100644 index 0000000..430dd36 --- /dev/null +++ b/patches/monero/0001-store-crash-fix.patch @@ -0,0 +1,335 @@ +From e502b3a2a2c88e9518c96123d44d91e6b6218f29 Mon Sep 17 00:00:00 2001 +From: Czarek Nakamoto +Date: Sat, 11 May 2024 16:25:10 +0200 +Subject: [PATCH 01/22] store crash fix + +Monero wallet crashes (sometimes) when it is syncing, +while the proper solution (that can be seen in feather) +is to not store wallet while it is being synced, this is not +acceptable for mobile wallets where OS can just come +and kill the wallet because it felt like it. + +This patch depends on the background-sync patch, but +to use it as a standalone fix grabbing the definition for the +LOCK_REFRESH macro should be enough. + +tobtoht suggested: +_say you want to store every 15 minutes during background sync. you stop the refresh every 15 minutes. then do something like this in the callback:_ + +``` +// Make sure this doesn't run in the refresh thread +onRefreshed() { + if (hasItBeen15MinutesSinceWeStored()) { + store(); + } + + if (shouldWeContinueRefreshing()) { + startRefresh(); + } +} +``` + +which works for crashes after the wallet is initially synced +but doesn't solve the issue for wallet that are syncing (it +would just wait for it to finish before actually storing). + +Also imo store() functin should store the wallet, no matter +the current state. +--- + src/wallet/api/wallet.cpp | 53 +++++++++++++++++++-------------------- + src/wallet/api/wallet.h | 1 - + src/wallet/wallet2.cpp | 11 +++++++- + src/wallet/wallet2.h | 3 +++ + 4 files changed, 39 insertions(+), 29 deletions(-) + +diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp +index 2a3919577..1da515120 100644 +--- a/src/wallet/api/wallet.cpp ++++ b/src/wallet/api/wallet.cpp +@@ -59,8 +59,8 @@ using namespace cryptonote; + #define MONERO_DEFAULT_LOG_CATEGORY "WalletAPI" + + #define LOCK_REFRESH() \ +- bool refresh_enabled = m_refreshEnabled; \ +- m_refreshEnabled = false; \ ++ bool refresh_enabled = m_wallet->get_refresh_enabled(); \ ++ m_wallet->set_refresh_enabled(false); \ + m_wallet->stop(); \ + m_refreshCV.notify_one(); \ + boost::mutex::scoped_lock lock(m_refreshMutex); \ +@@ -184,7 +184,7 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback + virtual void on_new_block(uint64_t height, const cryptonote::block& block) + { + // Don't flood the GUI with signals. On fast refresh - send signal every 1000th block +- // get_refresh_from_block_height() returns the blockheight from when the wallet was ++ // get_refresh_from_block_height() returns the blockheight from when the wallet was + // created or the restore height specified when wallet was recovered + if(height >= m_wallet->m_wallet->get_refresh_from_block_height() || height % 1000 == 0) { + // LOG_PRINT_L3(__FUNCTION__ << ": new block. height: " << height); +@@ -356,7 +356,7 @@ bool Wallet::keyValid(const std::string &secret_key_string, const std::string &a + error = tr("Failed to parse address"); + return false; + } +- ++ + cryptonote::blobdata key_data; + if(!epee::string_tools::parse_hexstr_to_binbuff(secret_key_string, key_data) || key_data.size() != sizeof(crypto::secret_key)) + { +@@ -381,7 +381,7 @@ bool Wallet::keyValid(const std::string &secret_key_string, const std::string &a + error = tr("key does not match address"); + return false; + } +- ++ + return true; + } + +@@ -443,7 +443,7 @@ WalletImpl::WalletImpl(NetworkType nettype, uint64_t kdf_rounds) + m_wallet2Callback.reset(new Wallet2CallbackImpl(this)); + m_wallet->callback(m_wallet2Callback.get()); + m_refreshThreadDone = false; +- m_refreshEnabled = false; ++ m_wallet->set_refresh_enabled(false); + m_addressBook.reset(new AddressBookImpl(this)); + m_subaddress.reset(new SubaddressImpl(this)); + m_subaddressAccount.reset(new SubaddressAccountImpl(this)); +@@ -464,7 +464,7 @@ WalletImpl::~WalletImpl() + m_wallet->callback(NULL); + // Pause refresh thread - prevents refresh from starting again + WalletImpl::pauseRefresh(); // Call the method directly (not polymorphically) to protect against UB in destructor. +- // Close wallet - stores cache and stops ongoing refresh operation ++ // Close wallet - stores cache and stops ongoing refresh operation + close(false); // do not store wallet as part of the closing activities + // Stop refresh thread + stopRefresh(); +@@ -675,7 +675,7 @@ bool WalletImpl::recoverFromKeysWithPassword(const std::string &path, + setSeedLanguage(language); + LOG_PRINT_L1("Generated deterministic wallet from spend key with seed language: " + language); + } +- ++ + } + catch (const std::exception& e) { + setStatusError(string(tr("failed to generate new wallet: ")) + e.what()); +@@ -939,6 +939,7 @@ void WalletImpl::stop() + bool WalletImpl::store(const std::string &path) + { + clearStatus(); ++ LOCK_REFRESH(); + try { + if (path.empty()) { + m_wallet->store(); +@@ -1047,14 +1048,14 @@ uint64_t WalletImpl::daemonBlockChainTargetHeight() const + } else { + clearStatus(); + } +- // Target height can be 0 when daemon is synced. Use blockchain height instead. ++ // Target height can be 0 when daemon is synced. Use blockchain height instead. + if(result == 0) + result = daemonBlockChainHeight(); + return result; + } + + bool WalletImpl::daemonSynced() const +-{ ++{ + if(connected() == Wallet::ConnectionStatus_Disconnected) + return false; + uint64_t blockChainHeight = daemonBlockChainHeight(); +@@ -1126,14 +1127,14 @@ UnsignedTransaction *WalletImpl::loadUnsignedTx(const std::string &unsigned_file + + return transaction; + } +- ++ + // Check tx data and construct confirmation message + std::string extra_message; + if (!std::get<2>(transaction->m_unsigned_tx_set.transfers).empty()) + extra_message = (boost::format("%u outputs to import. ") % (unsigned)std::get<2>(transaction->m_unsigned_tx_set.transfers).size()).str(); + transaction->checkLoadedTx([&transaction](){return transaction->m_unsigned_tx_set.txes.size();}, [&transaction](size_t n)->const tools::wallet2::tx_construction_data&{return transaction->m_unsigned_tx_set.txes[n];}, extra_message); + setStatus(transaction->status(), transaction->errorString()); +- ++ + return transaction; + } + +@@ -1148,7 +1149,7 @@ bool WalletImpl::submitTransaction(const string &fileName) { + setStatus(Status_Ok, tr("Failed to load transaction from file")); + return false; + } +- ++ + if(!transaction->commit()) { + setStatusError(transaction->m_errorString); + return false; +@@ -1157,7 +1158,7 @@ bool WalletImpl::submitTransaction(const string &fileName) { + return true; + } + +-bool WalletImpl::exportKeyImages(const string &filename, bool all) ++bool WalletImpl::exportKeyImages(const string &filename, bool all) + { + if (m_wallet->watch_only()) + { +@@ -1166,7 +1167,7 @@ bool WalletImpl::exportKeyImages(const string &filename, bool all) + } + if (checkBackgroundSync("cannot export key images")) + return false; +- ++ + try + { + if (!m_wallet->export_key_images(filename, all)) +@@ -1623,7 +1624,7 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vectoradjust_priority(static_cast(priority)); +@@ -2407,10 +2408,10 @@ void WalletImpl::refreshThreadFunc() + } + + LOG_PRINT_L3(__FUNCTION__ << ": refresh lock acquired..."); +- LOG_PRINT_L3(__FUNCTION__ << ": m_refreshEnabled: " << m_refreshEnabled); ++ LOG_PRINT_L3(__FUNCTION__ << ": m_refreshEnabled: " << m_wallet->get_refresh_enabled()); + LOG_PRINT_L3(__FUNCTION__ << ": m_status: " << status()); + LOG_PRINT_L3(__FUNCTION__ << ": m_refreshShouldRescan: " << m_refreshShouldRescan); +- if (m_refreshEnabled) { ++ if (m_wallet->get_refresh_enabled()) { + LOG_PRINT_L3(__FUNCTION__ << ": refreshing..."); + doRefresh(); + } +@@ -2444,7 +2445,7 @@ void WalletImpl::doRefresh() + } catch (const std::exception &e) { + setStatusError(e.what()); + break; +- }while(!rescan && (rescan=m_refreshShouldRescan.exchange(false))); // repeat if not rescanned and rescan was requested ++ }while(m_wallet->get_refresh_enabled() && !rescan && (rescan=m_refreshShouldRescan.exchange(false))); // repeat if not rescanned and rescan was requested + + if (m_wallet2Callback->getListener()) { + m_wallet2Callback->getListener()->refreshed(); +@@ -2454,9 +2455,9 @@ void WalletImpl::doRefresh() + + void WalletImpl::startRefresh() + { +- if (!m_refreshEnabled) { ++ if (!m_wallet->get_refresh_enabled()) { + LOG_PRINT_L2(__FUNCTION__ << ": refresh started/resumed..."); +- m_refreshEnabled = true; ++ m_wallet->set_refresh_enabled(true); + m_refreshCV.notify_one(); + } + } +@@ -2466,7 +2467,7 @@ void WalletImpl::startRefresh() + void WalletImpl::stopRefresh() + { + if (!m_refreshThreadDone) { +- m_refreshEnabled = false; ++ m_wallet->set_refresh_enabled(false); + m_refreshThreadDone = true; + m_refreshCV.notify_one(); + m_refreshThread.join(); +@@ -2477,9 +2478,7 @@ void WalletImpl::pauseRefresh() + { + LOG_PRINT_L2(__FUNCTION__ << ": refresh paused..."); + // TODO synchronize access +- if (!m_refreshThreadDone) { +- m_refreshEnabled = false; +- } ++ m_wallet->set_refresh_enabled(false); + } + + +@@ -2489,7 +2488,7 @@ bool WalletImpl::isNewWallet() const + // it's the same case as if it created from scratch, i.e. we need "fast sync" + // with the daemon (pull hashes instead of pull blocks). + // If wallet cache is rebuilt, creation height stored in .keys is used. +- // Watch only wallet is a copy of an existing wallet. ++ // Watch only wallet is a copy of an existing wallet. + return !(blockChainHeight() > 1 || m_recoveringFromSeed || m_recoveringFromDevice || m_rebuildWalletCache) && !watchOnly(); + } + +@@ -2601,7 +2600,7 @@ void WalletImpl::hardForkInfo(uint8_t &version, uint64_t &earliest_height) const + m_wallet->get_hard_fork_info(version, earliest_height); + } + +-bool WalletImpl::useForkRules(uint8_t version, int64_t early_blocks) const ++bool WalletImpl::useForkRules(uint8_t version, int64_t early_blocks) const + { + return m_wallet->use_fork_rules(version,early_blocks); + } +diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h +index d48d7f130..365025c6e 100644 +--- a/src/wallet/api/wallet.h ++++ b/src/wallet/api/wallet.h +@@ -274,7 +274,6 @@ private: + std::unique_ptr m_subaddressAccount; + + // multi-threaded refresh stuff +- std::atomic m_refreshEnabled; + std::atomic m_refreshThreadDone; + std::atomic m_refreshIntervalMillis; + std::atomic m_refreshShouldRescan; +diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp +index e818afe53..2ca99f63a 100644 +--- a/src/wallet/wallet2.cpp ++++ b/src/wallet/wallet2.cpp +@@ -1222,6 +1222,7 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended, std + m_upper_transaction_weight_limit(0), + m_run(true), + m_callback(0), ++ m_refreshEnabled(false), + m_trusted_daemon(false), + m_nettype(nettype), + m_multisig_rounds_passed(0), +@@ -1432,6 +1433,14 @@ bool wallet2::set_daemon(std::string daemon_address, boost::optionalset_proxy(address); +@@ -4461,7 +4470,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo + + + bool first = true, last = false; +- while(m_run.load(std::memory_order_relaxed) && blocks_fetched < max_blocks) ++ while(m_run.load(std::memory_order_relaxed) && blocks_fetched < max_blocks && m_refreshEnabled) + { + uint64_t next_blocks_start_height; + std::vector next_blocks; +diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h +index c350f1da8..e78abda83 100644 +--- a/src/wallet/wallet2.h ++++ b/src/wallet/wallet2.h +@@ -579,6 +579,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(); } + +@@ -1591,6 +1593,7 @@ private: + + boost::recursive_mutex m_daemon_rpc_mutex; + ++ bool m_refreshEnabled; + bool m_trusted_daemon; + i_wallet2_callback* m_callback; + hw::device::device_type m_key_device_type; +-- +2.54.0 (Apple Git-157) + diff --git a/patches/monero/0002-store-crash-fix.patch b/patches/monero/0002-store-crash-fix.patch deleted file mode 100644 index 8ecd8b1..0000000 --- a/patches/monero/0002-store-crash-fix.patch +++ /dev/null @@ -1,336 +0,0 @@ -From 94cf21261079d6d4ceb848be3863613e98c3bc89 Mon Sep 17 00:00:00 2001 -From: Czarek Nakamoto -Date: Sat, 11 May 2024 16:25:10 +0200 -Subject: [PATCH 02/20] store crash fix - -Monero wallet crashes (sometimes) when it is syncing, -while the proper solution (that can be seen in feather) -is to not store wallet while it is being synced, this is not -acceptable for mobile wallets where OS can just come -and kill the wallet because it felt like it. - -This patch depends on the background-sync patch, but -to use it as a standalone fix grabbing the definition for the -LOCK_REFRESH macro should be enough. - -tobtoht suggested: -_say you want to store every 15 minutes during background sync. you stop the refresh every 15 minutes. then do something like this in the callback:_ - -``` -// Make sure this doesn't run in the refresh thread -onRefreshed() { - if (hasItBeen15MinutesSinceWeStored()) { - store(); - } - - if (shouldWeContinueRefreshing()) { - startRefresh(); - } -} -``` - -which works for crashes after the wallet is initially synced -but doesn't solve the issue for wallet that are syncing (it -would just wait for it to finish before actually storing). - -Also imo store() functin should store the wallet, no matter -the current state. ---- - external/randomx | 2 +- - src/wallet/api/wallet.cpp | 53 +++++++++++++++++++-------------------- - src/wallet/api/wallet.h | 1 - - src/wallet/wallet2.cpp | 11 +++++++- - src/wallet/wallet2.h | 3 +++ - 5 files changed, 40 insertions(+), 30 deletions(-) - -diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp -index 165b21c9f..c2f4176e2 100644 ---- a/src/wallet/api/wallet.cpp -+++ b/src/wallet/api/wallet.cpp -@@ -55,8 +55,8 @@ using namespace cryptonote; - #define MONERO_DEFAULT_LOG_CATEGORY "WalletAPI" - - #define LOCK_REFRESH() \ -- bool refresh_enabled = m_refreshEnabled; \ -- m_refreshEnabled = false; \ -+ bool refresh_enabled = m_wallet->get_refresh_enabled(); \ -+ m_wallet->set_refresh_enabled(false); \ - m_wallet->stop(); \ - m_refreshCV.notify_one(); \ - boost::mutex::scoped_lock lock(m_refreshMutex); \ -@@ -178,7 +178,7 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback - virtual void on_new_block(uint64_t height, const cryptonote::block& block) - { - // Don't flood the GUI with signals. On fast refresh - send signal every 1000th block -- // get_refresh_from_block_height() returns the blockheight from when the wallet was -+ // get_refresh_from_block_height() returns the blockheight from when the wallet was - // created or the restore height specified when wallet was recovered - if(height >= m_wallet->m_wallet->get_refresh_from_block_height() || height % 1000 == 0) { - // LOG_PRINT_L3(__FUNCTION__ << ": new block. height: " << height); -@@ -379,7 +379,7 @@ bool Wallet::keyValid(const std::string &secret_key_string, const std::string &a - error = tr("Failed to parse address"); - return false; - } -- -+ - cryptonote::blobdata key_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(secret_key_string, key_data) || key_data.size() != sizeof(crypto::secret_key)) - { -@@ -404,7 +404,7 @@ bool Wallet::keyValid(const std::string &secret_key_string, const std::string &a - error = tr("key does not match address"); - return false; - } -- -+ - return true; - } - -@@ -466,7 +466,7 @@ WalletImpl::WalletImpl(NetworkType nettype, uint64_t kdf_rounds) - m_wallet2Callback.reset(new Wallet2CallbackImpl(this)); - m_wallet->callback(m_wallet2Callback.get()); - m_refreshThreadDone = false; -- m_refreshEnabled = false; -+ m_wallet->set_refresh_enabled(false); - m_addressBook.reset(new AddressBookImpl(this)); - m_subaddress.reset(new SubaddressImpl(this)); - m_subaddressAccount.reset(new SubaddressAccountImpl(this)); -@@ -487,7 +487,7 @@ WalletImpl::~WalletImpl() - m_wallet->callback(NULL); - // Pause refresh thread - prevents refresh from starting again - WalletImpl::pauseRefresh(); // Call the method directly (not polymorphically) to protect against UB in destructor. -- // Close wallet - stores cache and stops ongoing refresh operation -+ // Close wallet - stores cache and stops ongoing refresh operation - close(false); // do not store wallet as part of the closing activities - // Stop refresh thread - stopRefresh(); -@@ -698,7 +698,7 @@ bool WalletImpl::recoverFromKeysWithPassword(const std::string &path, - setSeedLanguage(language); - LOG_PRINT_L1("Generated deterministic wallet from spend key with seed language: " + language); - } -- -+ - } - catch (const std::exception& e) { - setStatusError(string(tr("failed to generate new wallet: ")) + e.what()); -@@ -962,6 +962,7 @@ void WalletImpl::stop() - bool WalletImpl::store(const std::string &path) - { - clearStatus(); -+ LOCK_REFRESH(); - try { - if (path.empty()) { - m_wallet->store(); -@@ -1110,14 +1111,14 @@ uint64_t WalletImpl::daemonBlockChainTargetHeight() const - } else { - clearStatus(); - } -- // Target height can be 0 when daemon is synced. Use blockchain height instead. -+ // Target height can be 0 when daemon is synced. Use blockchain height instead. - if(result == 0) - result = daemonBlockChainHeight(); - return result; - } - - bool WalletImpl::daemonSynced() const --{ -+{ - if(connected() == Wallet::ConnectionStatus_Disconnected) - return false; - uint64_t blockChainHeight = daemonBlockChainHeight(); -@@ -1189,14 +1190,14 @@ UnsignedTransaction *WalletImpl::loadUnsignedTx(const std::string &unsigned_file - - return transaction; - } -- -+ - // Check tx data and construct confirmation message - std::string extra_message; - if (!std::get<2>(transaction->m_unsigned_tx_set.transfers).empty()) - extra_message = (boost::format("%u outputs to import. ") % (unsigned)std::get<2>(transaction->m_unsigned_tx_set.transfers).size()).str(); - transaction->checkLoadedTx([&transaction](){return transaction->m_unsigned_tx_set.txes.size();}, [&transaction](size_t n)->const tools::wallet2::tx_construction_data&{return transaction->m_unsigned_tx_set.txes[n];}, extra_message); - setStatus(transaction->status(), transaction->errorString()); -- -+ - return transaction; - } - -@@ -1211,7 +1212,7 @@ bool WalletImpl::submitTransaction(const string &fileName) { - setStatus(Status_Ok, tr("Failed to load transaction from file")); - return false; - } -- -+ - if(!transaction->commit()) { - setStatusError(transaction->m_errorString); - return false; -@@ -1220,7 +1221,7 @@ bool WalletImpl::submitTransaction(const string &fileName) { - return true; - } - --bool WalletImpl::exportKeyImages(const string &filename, bool all) -+bool WalletImpl::exportKeyImages(const string &filename, bool all) - { - if (m_wallet->watch_only()) - { -@@ -1229,7 +1230,7 @@ bool WalletImpl::exportKeyImages(const string &filename, bool all) - } - if (checkBackgroundSync("cannot export key images")) - return false; -- -+ - try - { - if (!m_wallet->export_key_images(filename, all)) -@@ -1664,7 +1665,7 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vectoradjust_priority(static_cast(priority)); -@@ -2448,10 +2449,10 @@ void WalletImpl::refreshThreadFunc() - } - - LOG_PRINT_L3(__FUNCTION__ << ": refresh lock acquired..."); -- LOG_PRINT_L3(__FUNCTION__ << ": m_refreshEnabled: " << m_refreshEnabled); -+ LOG_PRINT_L3(__FUNCTION__ << ": m_refreshEnabled: " << m_wallet->get_refresh_enabled()); - LOG_PRINT_L3(__FUNCTION__ << ": m_status: " << status()); - LOG_PRINT_L3(__FUNCTION__ << ": m_refreshShouldRescan: " << m_refreshShouldRescan); -- if (m_refreshEnabled) { -+ if (m_wallet->get_refresh_enabled()) { - LOG_PRINT_L3(__FUNCTION__ << ": refreshing..."); - doRefresh(); - } -@@ -2485,7 +2486,7 @@ void WalletImpl::doRefresh() - } catch (const std::exception &e) { - setStatusError(e.what()); - break; -- }while(!rescan && (rescan=m_refreshShouldRescan.exchange(false))); // repeat if not rescanned and rescan was requested -+ }while(m_wallet->get_refresh_enabled() && !rescan && (rescan=m_refreshShouldRescan.exchange(false))); // repeat if not rescanned and rescan was requested - - if (m_wallet2Callback->getListener()) { - m_wallet2Callback->getListener()->refreshed(); -@@ -2495,9 +2496,9 @@ void WalletImpl::doRefresh() - - void WalletImpl::startRefresh() - { -- if (!m_refreshEnabled) { -+ if (!m_wallet->get_refresh_enabled()) { - LOG_PRINT_L2(__FUNCTION__ << ": refresh started/resumed..."); -- m_refreshEnabled = true; -+ m_wallet->set_refresh_enabled(true); - m_refreshCV.notify_one(); - } - } -@@ -2507,7 +2508,7 @@ void WalletImpl::startRefresh() - void WalletImpl::stopRefresh() - { - if (!m_refreshThreadDone) { -- m_refreshEnabled = false; -+ m_wallet->set_refresh_enabled(false); - m_refreshThreadDone = true; - m_refreshCV.notify_one(); - m_refreshThread.join(); -@@ -2518,9 +2519,7 @@ void WalletImpl::pauseRefresh() - { - LOG_PRINT_L2(__FUNCTION__ << ": refresh paused..."); - // TODO synchronize access -- if (!m_refreshThreadDone) { -- m_refreshEnabled = false; -- } -+ m_wallet->set_refresh_enabled(false); - } - - -@@ -2530,7 +2529,7 @@ bool WalletImpl::isNewWallet() const - // it's the same case as if it created from scratch, i.e. we need "fast sync" - // with the daemon (pull hashes instead of pull blocks). - // If wallet cache is rebuilt, creation height stored in .keys is used. -- // Watch only wallet is a copy of an existing wallet. -+ // Watch only wallet is a copy of an existing wallet. - return !(blockChainHeight() > 1 || m_recoveringFromSeed || m_recoveringFromDevice || m_rebuildWalletCache) && !watchOnly(); - } - -@@ -2642,7 +2641,7 @@ void WalletImpl::hardForkInfo(uint8_t &version, uint64_t &earliest_height) const - m_wallet->get_hard_fork_info(version, earliest_height); - } - --bool WalletImpl::useForkRules(uint8_t version, int64_t early_blocks) const -+bool WalletImpl::useForkRules(uint8_t version, int64_t early_blocks) const - { - return m_wallet->use_fork_rules(version,early_blocks); - } -diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h -index 1f199a72c..ac7ce2f6a 100644 ---- a/src/wallet/api/wallet.h -+++ b/src/wallet/api/wallet.h -@@ -273,7 +273,6 @@ private: - std::unique_ptr m_subaddressAccount; - - // multi-threaded refresh stuff -- std::atomic m_refreshEnabled; - std::atomic m_refreshThreadDone; - std::atomic m_refreshIntervalMillis; - std::atomic m_refreshShouldRescan; -diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp -index af1f03d2c..af876c9f3 100644 ---- a/src/wallet/wallet2.cpp -+++ b/src/wallet/wallet2.cpp -@@ -1195,6 +1195,7 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended, std - m_upper_transaction_weight_limit(0), - m_run(true), - m_callback(0), -+ m_refreshEnabled(false), - m_trusted_daemon(false), - m_nettype(nettype), - m_multisig_rounds_passed(0), -@@ -1415,6 +1416,14 @@ bool wallet2::set_daemon(std::string daemon_address, boost::optionalset_proxy(address); -@@ -4178,7 +4187,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; -- while(m_run.load(std::memory_order_relaxed) && blocks_fetched < max_blocks) -+ while(m_run.load(std::memory_order_relaxed) && blocks_fetched < max_blocks && m_refreshEnabled) - { - uint64_t next_blocks_start_height; - std::vector next_blocks; -diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h -index a765dc475..92f735f96 100644 ---- a/src/wallet/wallet2.h -+++ b/src/wallet/wallet2.h -@@ -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(); } - -@@ -1997,6 +1999,7 @@ private: - - boost::recursive_mutex m_daemon_rpc_mutex; - -+ bool m_refreshEnabled; - bool m_trusted_daemon; - i_wallet2_callback* m_callback; - hw::device::device_type m_key_device_type; --- -2.50.1 (Apple Git-155) - diff --git a/patches/monero/0002-uint64_t-missing-definition-fix.patch b/patches/monero/0002-uint64_t-missing-definition-fix.patch new file mode 100644 index 0000000..7d7654b --- /dev/null +++ b/patches/monero/0002-uint64_t-missing-definition-fix.patch @@ -0,0 +1,25 @@ +From 97bb6fe8c11fe8a036f5808eef1a23c5c9febfb6 Mon Sep 17 00:00:00 2001 +From: Czarek Nakamoto +Date: Mon, 2 Sep 2024 16:40:31 +0200 +Subject: [PATCH 02/22] 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 b53766780..05af46731 100644 +--- a/contrib/epee/include/net/http_base.h ++++ b/contrib/epee/include/net/http_base.h +@@ -28,7 +28,7 @@ + + #pragma once + #include "memwipe.h" +- ++#include + #include + + #include +-- +2.54.0 (Apple Git-157) + diff --git a/patches/monero/0003-uint64_t-missing-definition-fix.patch b/patches/monero/0003-uint64_t-missing-definition-fix.patch deleted file mode 100644 index 87aef72..0000000 --- a/patches/monero/0003-uint64_t-missing-definition-fix.patch +++ /dev/null @@ -1,25 +0,0 @@ -From 3cdb4ef9bdf88936276b4c5286eb7f9d39f556d0 Mon Sep 17 00:00:00 2001 -From: Czarek Nakamoto -Date: Mon, 2 Sep 2024 16:40:31 +0200 -Subject: [PATCH 03/20] 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 f32fdd9ae..a95a7d34f 100644 ---- a/contrib/epee/include/net/http_base.h -+++ b/contrib/epee/include/net/http_base.h -@@ -28,7 +28,7 @@ - - #pragma once - #include "memwipe.h" -- -+#include - #include - - #include --- -2.50.1 (Apple Git-155) - diff --git a/patches/monero/0003-use-proper-error-handling-in-get_seed.patch b/patches/monero/0003-use-proper-error-handling-in-get_seed.patch new file mode 100644 index 0000000..0021f3c --- /dev/null +++ b/patches/monero/0003-use-proper-error-handling-in-get_seed.patch @@ -0,0 +1,71 @@ +From c58a0dbf32dd048875c7d0a0193ddb0ecc1e71ac Mon Sep 17 00:00:00 2001 +From: Czarek Nakamoto +Date: Mon, 24 Jun 2024 10:49:12 +0200 +Subject: [PATCH 03/22] use proper error handling in get_seed + +--- + src/wallet/api/wallet.cpp | 17 ++++++++++++----- + src/wallet/wallet2.cpp | 5 ++++- + 2 files changed, 16 insertions(+), 6 deletions(-) + +diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp +index 1da515120..27d8716c3 100644 +--- a/src/wallet/api/wallet.cpp ++++ b/src/wallet/api/wallet.cpp +@@ -803,12 +803,19 @@ bool WalletImpl::close(bool store) + + std::string WalletImpl::seed(const std::string& seed_offset) const + { +- if (checkBackgroundSync("cannot get seed")) ++ if (checkBackgroundSync("cannot get seed")) { ++ setStatusError("cannot get seed"); + return std::string(); +- epee::wipeable_string seed; +- if (m_wallet) +- m_wallet->get_seed(seed, seed_offset); +- return std::string(seed.data(), seed.size()); // TODO ++ } ++ try { ++ epee::wipeable_string seed; ++ if (m_wallet) ++ m_wallet->get_seed(seed, seed_offset); ++ return std::string(seed.data(), seed.size()); // TODO ++ } catch (const std::exception &e) { ++ setStatusError(e.what()); ++ return std::string(); ++ } + } + + std::string WalletImpl::getSeedLanguage() const +diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp +index 2ca99f63a..ff8950fd4 100644 +--- a/src/wallet/wallet2.cpp ++++ b/src/wallet/wallet2.cpp +@@ -1469,11 +1469,13 @@ bool wallet2::get_seed(epee::wipeable_string& electrum_words, const epee::wipeab + bool keys_deterministic = is_deterministic(); + if (!keys_deterministic) + { ++ THROW_WALLET_EXCEPTION(error::wallet_internal_error, "This is not a deterministic wallet"); + std::cout << "This is not a deterministic wallet" << std::endl; + return false; + } + if (seed_language.empty()) + { ++ THROW_WALLET_EXCEPTION(error::wallet_internal_error, "seed_language not set"); + std::cout << "seed_language not set" << std::endl; + return false; + } +@@ -1483,8 +1485,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)) + { ++ THROW_WALLET_EXCEPTION(error::wallet_internal_error, "Failed to create seed from key for language: "+seed_language+", falling back to English."); + std::cout << "Failed to create seed from key for language: " << seed_language << std::endl; +- return false; ++ crypto::ElectrumWords::bytes_to_words(key, electrum_words, "English"); + } + + return true; +-- +2.54.0 (Apple Git-157) + diff --git a/patches/monero/0004-fix-missing-___clear_cache-when-targetting-iOS.patch b/patches/monero/0004-fix-missing-___clear_cache-when-targetting-iOS.patch new file mode 100644 index 0000000..eb53dff --- /dev/null +++ b/patches/monero/0004-fix-missing-___clear_cache-when-targetting-iOS.patch @@ -0,0 +1,33 @@ +From 19864ece62a8a04299ddc9ffd47f108a1cd4dc3c Mon Sep 17 00:00:00 2001 +From: Czarek Nakamoto +Date: Mon, 27 Jul 2026 13:03:32 +0200 +Subject: [PATCH 04/22] fix missing ___clear_cache when targetting iOS +--- + .gitmodules | 3 ++- + external/randomx | 2 +- + 2 files changed, 3 insertions(+), 2 deletions(-) + +diff --git a/.gitmodules b/.gitmodules +index 691444f97..9dcb71804 100644 +--- a/.gitmodules ++++ b/.gitmodules +@@ -3,7 +3,8 @@ + url = https://github.com/Tencent/rapidjson + [submodule "external/randomx"] + path = external/randomx +- url = https://github.com/tevador/RandomX ++ url = https://github.com/MrCyjaneK/RandomX ++ branch = cyjan-fix-ios + [submodule "external/supercop"] + path = external/supercop + url = https://github.com/monero-project/supercop +diff --git a/external/randomx b/external/randomx +index 6c4340ba4..328bb26fb 160000 +--- a/external/randomx ++++ b/external/randomx +@@ -1 +1 @@ +-Subproject commit 6c4340ba4561aec9a3611c1aedf9931239777fb3 ++Subproject commit 328bb26fb4d3dd2c2d30453719dcc9b9febbd658 +-- +2.54.0 (Apple Git-157) + diff --git a/patches/monero/0004-use-proper-error-handling-in-get_seed.patch b/patches/monero/0004-use-proper-error-handling-in-get_seed.patch deleted file mode 100644 index 154fa90..0000000 --- a/patches/monero/0004-use-proper-error-handling-in-get_seed.patch +++ /dev/null @@ -1,71 +0,0 @@ -From 9174c5ab87a00d16e2930616686de5e57f3e7539 Mon Sep 17 00:00:00 2001 -From: Czarek Nakamoto -Date: Mon, 24 Jun 2024 10:49:12 +0200 -Subject: [PATCH 04/20] use proper error handling in get_seed - ---- - src/wallet/api/wallet.cpp | 17 ++++++++++++----- - src/wallet/wallet2.cpp | 5 ++++- - 2 files changed, 16 insertions(+), 6 deletions(-) - -diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp -index c2f4176e2..6301bd1ef 100644 ---- a/src/wallet/api/wallet.cpp -+++ b/src/wallet/api/wallet.cpp -@@ -826,12 +826,19 @@ bool WalletImpl::close(bool store) - - std::string WalletImpl::seed(const std::string& seed_offset) const - { -- if (checkBackgroundSync("cannot get seed")) -+ if (checkBackgroundSync("cannot get seed")) { -+ setStatusError("cannot get seed"); - return std::string(); -- epee::wipeable_string seed; -- if (m_wallet) -- m_wallet->get_seed(seed, seed_offset); -- return std::string(seed.data(), seed.size()); // TODO -+ } -+ try { -+ epee::wipeable_string seed; -+ if (m_wallet) -+ m_wallet->get_seed(seed, seed_offset); -+ return std::string(seed.data(), seed.size()); // TODO -+ } catch (const std::exception &e) { -+ setStatusError(e.what()); -+ return std::string(); -+ } - } - - std::string WalletImpl::getSeedLanguage() const -diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp -index af876c9f3..ac2a1fec3 100644 ---- a/src/wallet/wallet2.cpp -+++ b/src/wallet/wallet2.cpp -@@ -1452,11 +1452,13 @@ bool wallet2::get_seed(epee::wipeable_string& electrum_words, const epee::wipeab - bool keys_deterministic = is_deterministic(); - if (!keys_deterministic) - { -+ THROW_WALLET_EXCEPTION(error::wallet_internal_error, "This is not a deterministic wallet"); - std::cout << "This is not a deterministic wallet" << std::endl; - return false; - } - if (seed_language.empty()) - { -+ THROW_WALLET_EXCEPTION(error::wallet_internal_error, "seed_language not set"); - std::cout << "seed_language not set" << std::endl; - return false; - } -@@ -1466,8 +1468,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)) - { -+ THROW_WALLET_EXCEPTION(error::wallet_internal_error, "Failed to create seed from key for language: "+seed_language+", falling back to English."); - std::cout << "Failed to create seed from key for language: " << seed_language << std::endl; -- return false; -+ crypto::ElectrumWords::bytes_to_words(key, electrum_words, "English"); - } - - return true; --- -2.50.1 (Apple Git-155) - diff --git a/patches/monero/0005-UR-functions.patch b/patches/monero/0005-UR-functions.patch index 116d050..d86fec2 100644 --- a/patches/monero/0005-UR-functions.patch +++ b/patches/monero/0005-UR-functions.patch @@ -1,7 +1,7 @@ -From ccd02185be7d4a928776ff38111eb3ce9face61f Mon Sep 17 00:00:00 2001 +From 5ffd332c6a2943891d99895ca7ec70d96f23b445 Mon Sep 17 00:00:00 2001 From: tobtoht Date: Tue, 12 Mar 2024 10:09:50 +0100 -Subject: [PATCH 05/20] UR functions +Subject: [PATCH 05/22] UR functions This commit adds UR functions for UR tasks, I believe that the right place to get @@ -20,7 +20,7 @@ Things broken in the commit of this patch) it is not a dealbreaker. --- .gitmodules | 4 + - CMakeLists.txt | 4 +- + CMakeLists.txt | 2 + external/CMakeLists.txt | 1 + external/bc-ur | 1 + src/device/device_ledger.cpp | 5 +- @@ -32,55 +32,54 @@ Things broken in the commit src/wallet/api/wallet.cpp | 307 ++++++++++++++++++++++++ src/wallet/api/wallet.h | 8 + src/wallet/api/wallet2_api.h | 22 +- - src/wallet/wallet2.cpp | 141 +++++++---- + src/wallet/wallet2.cpp | 137 +++++++---- src/wallet/wallet2.h | 3 + - 15 files changed, 518 insertions(+), 56 deletions(-) + 15 files changed, 516 insertions(+), 52 deletions(-) create mode 160000 external/bc-ur diff --git a/.gitmodules b/.gitmodules -index ffb73fe9a..72af74d55 100644 +index 9dcb71804..57fdfbfc8 100644 --- a/.gitmodules +++ b/.gitmodules -@@ -15,3 +15,7 @@ - path = external/supercop - url = https://github.com/monero-project/supercop - branch = monero +@@ -19,3 +19,7 @@ + path = external/mx25519 + url = https://github.com/jeffro256/mx25519 + branch = unclamped +[submodule "external/bc-ur"] + path = external/bc-ur + url = https://github.com/MrCyjaneK/bc-ur + branch = misc diff --git a/CMakeLists.txt b/CMakeLists.txt -index 9b922046e..268339201 100644 +index b829efaf0..4ba3b7557 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt -@@ -96,7 +96,8 @@ enable_language(C ASM) - set(CMAKE_C_STANDARD 11) +@@ -122,6 +122,7 @@ set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_C_EXTENSIONS OFF) --set(CMAKE_CXX_STANDARD 14) -+set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD 17) +add_definitions(-D_LIBCPP_ENABLE_CXX17_REMOVED_FEATURES) # boost: no template named 'unary_function' in namespace 'std'; did you mean '__unary_function'? set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) -@@ -364,6 +365,7 @@ if(NOT MANUAL_SUBMODULES) +@@ -385,6 +386,7 @@ if(NOT MANUAL_SUBMODULES) endfunction () message(STATUS "Checking submodules") +# check_submodule(external/bc-ur) - check_submodule(external/miniupnp) check_submodule(external/rapidjson) - check_submodule(external/trezor-common) + check_submodule(external/randomx) + check_submodule(external/supercop) diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt -index 538e4d215..074e23f16 100644 +index 08e910a0a..900684b6d 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt -@@ -70,4 +70,5 @@ endif() +@@ -31,5 +31,6 @@ add_subdirectory(db_drivers) add_subdirectory(easylogging++) add_subdirectory(qrcodegen) +add_subdirectory(bc-ur) add_subdirectory(randomx EXCLUDE_FROM_ALL) + add_subdirectory(mx25519) diff --git a/external/bc-ur b/external/bc-ur new file mode 160000 index 000000000..d82e7c753 @@ -89,10 +88,10 @@ index 000000000..d82e7c753 @@ -0,0 +1 @@ +Subproject commit d82e7c753e710b8000706dc3383b498438795208 diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp -index 6dde4a564..7e4be6347 100644 +index f8631e42d..aa6e037a1 100644 --- a/src/device/device_ledger.cpp +++ b/src/device/device_ledger.cpp -@@ -313,12 +313,13 @@ namespace hw { +@@ -314,12 +314,13 @@ namespace hw { /* ======================================================================= */ /* LOCKER */ @@ -103,25 +102,25 @@ index 6dde4a564..7e4be6347 100644 + #pragma message ("Warning AUTO_LOCK_CMD is intentionally left broken. This is yet to be fixed.") #define AUTO_LOCK_CMD() \ /* lock both mutexes without deadlock*/ \ -- boost::lock(device_locker, command_locker); \ -+ /* boost::lock(device_locker, command_locker); */ \ +- std::lock(device_locker, command_locker); \ ++ /* std::lock(device_locker, command_locker); */ \ /* make sure both already-locked mutexes are unlocked at the end of scope */ \ - boost::lock_guard lock1(device_locker, boost::adopt_lock); \ - boost::lock_guard lock2(command_locker, boost::adopt_lock) + std::lock_guard lock1(device_locker, std::adopt_lock); \ + std::lock_guard lock2(command_locker, std::adopt_lock) diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt -index 6095f99d5..b163212b7 100644 +index 054a5e876..db7c3c06b 100644 --- a/src/wallet/CMakeLists.txt +++ b/src/wallet/CMakeLists.txt -@@ -50,6 +50,7 @@ monero_add_library(wallet +@@ -52,6 +52,7 @@ monero_add_library(wallet target_link_libraries(wallet PUBLIC rpc_base + bc-ur multisig + carrot_impl common - cryptonote_core diff --git a/src/wallet/api/pending_transaction.cpp b/src/wallet/api/pending_transaction.cpp -index 70a702796..9c3c26ee5 100644 +index 1618b915d..919e210f0 100644 --- a/src/wallet/api/pending_transaction.cpp +++ b/src/wallet/api/pending_transaction.cpp @@ -42,6 +42,8 @@ @@ -172,7 +171,7 @@ index 70a702796..9c3c26ee5 100644 { uint64_t result = 0; diff --git a/src/wallet/api/pending_transaction.h b/src/wallet/api/pending_transaction.h -index 0a9779c07..403bfe281 100644 +index 9d8d754c0..8a70d774d 100644 --- a/src/wallet/api/pending_transaction.h +++ b/src/wallet/api/pending_transaction.h @@ -46,6 +46,7 @@ public: @@ -184,7 +183,7 @@ index 0a9779c07..403bfe281 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 6165a2240..fd03e959d 100644 +index c549539e5..062df7f41 100644 --- a/src/wallet/api/unsigned_transaction.cpp +++ b/src/wallet/api/unsigned_transaction.cpp @@ -40,6 +40,8 @@ @@ -244,7 +243,7 @@ index 6165a2240..fd03e959d 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 30065a7fa..a94b23f75 100644 +index b07d43fb1..76165a230 100644 --- a/src/wallet/api/unsigned_transaction.h +++ b/src/wallet/api/unsigned_transaction.h @@ -53,6 +53,7 @@ public: @@ -256,18 +255,18 @@ index 30065a7fa..a94b23f75 100644 uint64_t minMixinCount() const override; diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp -index 6301bd1ef..d179e502b 100644 +index 27d8716c3..52bc7647a 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp -@@ -47,6 +47,7 @@ +@@ -51,6 +51,7 @@ + #endif - #include #include +#include "bc-ur/src/bc-ur.hpp" using namespace std; using namespace cryptonote; -@@ -1066,6 +1067,24 @@ uint64_t WalletImpl::unlockedBalance(uint32_t accountIndex) const +@@ -1012,6 +1013,24 @@ uint64_t WalletImpl::unlockedBalance(uint32_t accountIndex) const return m_wallet->unlocked_balance(accountIndex, false); } @@ -291,8 +290,8 @@ index 6301bd1ef..d179e502b 100644 + uint64_t WalletImpl::blockChainHeight() const { - if(m_wallet->light_wallet()) { -@@ -1208,6 +1227,61 @@ UnsignedTransaction *WalletImpl::loadUnsignedTx(const std::string &unsigned_file + return m_wallet->get_blockchain_current_height(); +@@ -1145,6 +1164,61 @@ UnsignedTransaction *WalletImpl::loadUnsignedTx(const std::string &unsigned_file return transaction; } @@ -354,7 +353,7 @@ index 6301bd1ef..d179e502b 100644 bool WalletImpl::submitTransaction(const string &fileName) { clearStatus(); if (checkBackgroundSync("cannot submit tx")) -@@ -1228,6 +1302,61 @@ bool WalletImpl::submitTransaction(const string &fileName) { +@@ -1165,6 +1239,61 @@ bool WalletImpl::submitTransaction(const string &fileName) { return true; } @@ -416,7 +415,7 @@ index 6301bd1ef..d179e502b 100644 bool WalletImpl::exportKeyImages(const string &filename, bool all) { if (m_wallet->watch_only()) -@@ -1255,6 +1384,39 @@ bool WalletImpl::exportKeyImages(const string &filename, bool all) +@@ -1192,6 +1321,39 @@ bool WalletImpl::exportKeyImages(const string &filename, bool all) return true; } @@ -456,7 +455,7 @@ index 6301bd1ef..d179e502b 100644 bool WalletImpl::importKeyImages(const string &filename) { if (checkBackgroundSync("cannot import key images")) -@@ -1280,6 +1442,62 @@ bool WalletImpl::importKeyImages(const string &filename) +@@ -1217,6 +1379,62 @@ bool WalletImpl::importKeyImages(const string &filename) return true; } @@ -519,7 +518,7 @@ index 6301bd1ef..d179e502b 100644 bool WalletImpl::exportOutputs(const string &filename, bool all) { if (checkBackgroundSync("cannot export outputs")) -@@ -1312,6 +1530,40 @@ bool WalletImpl::exportOutputs(const string &filename, bool all) +@@ -1249,6 +1467,40 @@ bool WalletImpl::exportOutputs(const string &filename, bool all) return true; } @@ -560,7 +559,7 @@ index 6301bd1ef..d179e502b 100644 bool WalletImpl::importOutputs(const string &filename) { if (checkBackgroundSync("cannot import outputs")) -@@ -1346,6 +1598,61 @@ bool WalletImpl::importOutputs(const string &filename) +@@ -1283,6 +1535,61 @@ bool WalletImpl::importOutputs(const string &filename) return true; } @@ -623,10 +622,10 @@ index 6301bd1ef..d179e502b 100644 { if (checkBackgroundSync("cannot scan transactions")) diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h -index ac7ce2f6a..edf8bb8ce 100644 +index 365025c6e..a50983a69 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h -@@ -112,6 +112,7 @@ public: +@@ -113,6 +113,7 @@ public: bool setProxy(const std::string &address) override; uint64_t balance(uint32_t accountIndex = 0) const override; uint64_t unlockedBalance(uint32_t accountIndex = 0) const override; @@ -634,7 +633,7 @@ index ac7ce2f6a..edf8bb8ce 100644 uint64_t blockChainHeight() const override; uint64_t approximateBlockChainHeight() const override; uint64_t estimateBlockChainHeight() const override; -@@ -164,11 +165,18 @@ public: +@@ -166,11 +167,18 @@ public: std::set subaddr_indices = {}) override; virtual PendingTransaction * createSweepUnmixableTransaction() override; bool submitTransaction(const std::string &fileName) override; @@ -654,10 +653,10 @@ index ac7ce2f6a..edf8bb8ce 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 e349df176..764adbfbf 100644 +index 534679820..a5164260f 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h -@@ -91,6 +91,7 @@ struct PendingTransaction +@@ -83,6 +83,7 @@ struct PendingTransaction virtual std::string errorString() const = 0; // commit transaction or save to file if filename is provided. virtual bool commit(const std::string &filename = "", bool overwrite = false) = 0; @@ -665,7 +664,7 @@ index e349df176..764adbfbf 100644 virtual uint64_t amount() const = 0; virtual uint64_t dust() const = 0; virtual uint64_t fee() const = 0; -@@ -160,7 +161,8 @@ struct UnsignedTransaction +@@ -152,7 +153,8 @@ struct UnsignedTransaction * @param signedFileName * return - true on success */ @@ -675,7 +674,7 @@ index e349df176..764adbfbf 100644 }; /** -@@ -626,6 +628,7 @@ struct Wallet +@@ -619,6 +621,7 @@ struct Wallet result += unlockedBalance(i); return result; } @@ -683,7 +682,7 @@ index e349df176..764adbfbf 100644 /** * @brief watchOnly - checks if wallet is watch only -@@ -884,13 +887,15 @@ struct Wallet +@@ -886,13 +889,15 @@ struct Wallet * after object returned */ virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename) = 0; @@ -702,7 +701,7 @@ index e349df176..764adbfbf 100644 /*! * \brief disposeTransaction - destroys transaction object -@@ -906,6 +911,8 @@ struct Wallet +@@ -908,6 +913,8 @@ struct Wallet virtual uint64_t estimateTransactionFee(const std::vector> &destinations, PendingTransaction::Priority priority) const = 0; @@ -711,7 +710,7 @@ index e349df176..764adbfbf 100644 /*! * \brief exportKeyImages - exports key images to file * \param filename -@@ -913,20 +920,22 @@ struct Wallet +@@ -915,20 +922,22 @@ struct Wallet * \return - true on success */ virtual bool exportKeyImages(const std::string &filename, bool all = false) = 0; @@ -736,7 +735,7 @@ index e349df176..764adbfbf 100644 /*! * \brief importOutputs - imports outputs from file -@@ -934,6 +943,7 @@ struct Wallet +@@ -936,6 +945,7 @@ struct Wallet * \return - true on success */ virtual bool importOutputs(const std::string &filename) = 0; @@ -745,10 +744,10 @@ index e349df176..764adbfbf 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 ac2a1fec3..a8db99c3f 100644 +index ff8950fd4..79cab37a4 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp -@@ -953,6 +953,16 @@ uint32_t get_subaddress_clamped_sum(uint32_t idx, uint32_t extra) +@@ -974,6 +974,16 @@ uint32_t get_subaddress_clamped_sum(uint32_t idx, uint32_t extra) return idx + extra; } @@ -765,7 +764,7 @@ index ac2a1fec3..a8db99c3f 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); -@@ -7065,6 +7075,25 @@ uint64_t wallet2::unlocked_balance(uint32_t index_major, bool strict, uint64_t * +@@ -7326,6 +7336,25 @@ uint64_t wallet2::unlocked_balance(uint32_t index_major, bool strict, uint64_t * return amount; } //---------------------------------------------------------------------------------------------------- @@ -791,18 +790,7 @@ index ac2a1fec3..a8db99c3f 100644 std::map wallet2::balance_per_subaddress(uint32_t index_major, bool strict) const { std::map amount_per_subaddr; -@@ -7916,9 +7945,7 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector additional_derivations; - -- // compute public keys from out secret keys -- crypto::public_key tx_pub_key; -- crypto::secret_key_to_public_key(txs[n].tx_key, tx_pub_key); -+ crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(tx); - std::vector additional_tx_pub_keys; - for (const crypto::secret_key &skey: txs[n].additional_tx_keys) - { -@@ -11261,7 +11288,7 @@ std::vector wallet2::create_transactions_2(std::vector wallet2::create_transactions_2( MDEBUG("Ignoring output " << i << " of amount " << print_money(td.amount()) << " which is below fractional threshold " << print_money(fractional_threshold)); continue; } @@ -811,7 +799,7 @@ index ac2a1fec3..a8db99c3f 100644 { if (td.amount() > m_ignore_outputs_above || td.amount() < m_ignore_outputs_below) { -@@ -11311,9 +11338,15 @@ std::vector wallet2::create_transactions_2(std::vector wallet2::create_transactions_2( LOG_PRINT_L2("Starting with " << num_nondust_outputs << " non-dust outputs and " << num_dust_outputs << " dust outputs"); @@ -829,8 +817,8 @@ index ac2a1fec3..a8db99c3f 100644 // if empty, put dummy entry so that the front can be referenced later in the loop if (unused_dust_indices_per_subaddr.empty()) unused_dust_indices_per_subaddr.push_back({}); -@@ -13949,33 +13982,40 @@ crypto::public_key wallet2::get_tx_pub_key_from_received_outs(const tools::walle - +@@ -13501,33 +13536,40 @@ crypto::public_key wallet2::get_tx_pub_key_from_received_outs(const tools::walle + //---------------------------------------------------------------------------------------------------- bool wallet2::export_key_images(const std::string &filename, bool all) const { - PERF_TIMER(export_key_images); @@ -893,7 +881,7 @@ index ac2a1fec3..a8db99c3f 100644 //---------------------------------------------------------------------------------------------------- std::pair>> wallet2::export_key_images(bool all) const { -@@ -14030,53 +14070,60 @@ std::pair> +@@ -13582,53 +13624,60 @@ std::pair> return std::make_pair(offset, ski); } @@ -973,10 +961,10 @@ index ac2a1fec3..a8db99c3f 100644 ski.push_back(std::make_pair(key_image, signature)); } diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h -index 92f735f96..18e60d89a 100644 +index e78abda83..aa70d5662 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h -@@ -1164,6 +1164,7 @@ private: +@@ -667,6 +667,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); @@ -984,7 +972,7 @@ index 92f735f96..18e60d89a 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); -@@ -1639,9 +1640,11 @@ private: +@@ -1196,9 +1197,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; @@ -997,5 +985,5 @@ index 92f735f96..18e60d89a 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.50.1 (Apple Git-155) +2.54.0 (Apple Git-157) diff --git a/patches/monero/0006-add-dummy-device-for-ledger.patch b/patches/monero/0006-add-dummy-device-for-ledger.patch index 816ad5d..5a8870a 100644 --- a/patches/monero/0006-add-dummy-device-for-ledger.patch +++ b/patches/monero/0006-add-dummy-device-for-ledger.patch @@ -1,7 +1,7 @@ -From eb9ffa912fb31dd2bddf96d7d55d5e5f9d8219b4 Mon Sep 17 00:00:00 2001 +From 8651523017eae94b5527ba599f6391077a6a6dd2 Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Thu, 8 May 2025 13:14:23 +0200 -Subject: [PATCH 06/20] add dummy device for ledger +Subject: [PATCH 06/22] add dummy device for ledger --- CMakeLists.txt | 19 ++-- @@ -21,10 +21,10 @@ Subject: [PATCH 06/20] add dummy device for ledger create mode 100644 src/device/device_io_dummy.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt -index 268339201..eb0d12225 100644 +index 4ba3b7557..8451e7959 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt -@@ -692,16 +692,21 @@ include_directories(${LMDB_INCLUDE}) +@@ -663,16 +663,21 @@ include_directories(${LMDB_INCLUDE}) include_directories(${LIBUNWIND_INCLUDE}) link_directories(${LIBUNWIND_LIBRARY_DIRS}) @@ -54,7 +54,7 @@ index 268339201..eb0d12225 100644 include(CheckTrezor) diff --git a/src/device/CMakeLists.txt b/src/device/CMakeLists.txt -index e4f1159b5..14d398f87 100644 +index e8901cd4c..a0cdcdd1d 100644 --- a/src/device/CMakeLists.txt +++ b/src/device/CMakeLists.txt @@ -29,10 +29,11 @@ @@ -84,7 +84,7 @@ index e4f1159b5..14d398f87 100644 ${device_headers} device_ledger.hpp diff --git a/src/device/device.cpp b/src/device/device.cpp -index e6cd358b6..dd0701e0c 100644 +index e6c39c0b4..8a5d14c41 100644 --- a/src/device/device.cpp +++ b/src/device/device.cpp @@ -29,7 +29,7 @@ @@ -122,7 +122,7 @@ index e6cd358b6..dd0701e0c 100644 return *device->second; } diff --git a/src/device/device.hpp b/src/device/device.hpp -index 392703a24..ffd419779 100644 +index 81caec91b..2e67401d6 100644 --- a/src/device/device.hpp +++ b/src/device/device.hpp @@ -34,17 +34,7 @@ @@ -401,19 +401,19 @@ index 000000000..87a5f109f + +#endif // HAVE_HIDAPI diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp -index 7e4be6347..ee330ba59 100644 +index aa6e037a1..728b06c41 100644 --- a/src/device/device_ledger.cpp +++ b/src/device/device_ledger.cpp -@@ -41,7 +41,7 @@ namespace hw { +@@ -38,7 +38,7 @@ namespace hw { namespace ledger { - #ifdef WITH_DEVICE_LEDGER + #if defined(WITH_DEVICE_LEDGER) || defined(HIDAPI_DUMMY) - #undef MONERO_DEFAULT_LOG_CATEGORY - #define MONERO_DEFAULT_LOG_CATEGORY "device.ledger" -@@ -299,7 +299,7 @@ namespace hw { + namespace { + bool apdu_verbose =true; +@@ -300,7 +300,7 @@ namespace hw { device_ledger::device_ledger(): hw_device(0x0101, 0x05, 64, 2000) { this->id = device_id++; @@ -422,7 +422,7 @@ index 7e4be6347..ee330ba59 100644 this->mode = NONE; this->has_view_key = false; this->tx_in_progress = false; -@@ -534,7 +534,9 @@ namespace hw { +@@ -535,7 +535,9 @@ namespace hw { bool device_ledger::connect(void) { this->disconnect(); @@ -433,7 +433,7 @@ index 7e4be6347..ee330ba59 100644 #ifdef DEBUG_HWDEVICE cryptonote::account_public_address pubkey; diff --git a/src/device/device_ledger.hpp b/src/device/device_ledger.hpp -index 61ac6f9c8..44a1af6c8 100644 +index 2634454a7..da81a399f 100644 --- a/src/device/device_ledger.hpp +++ b/src/device/device_ledger.hpp @@ -35,6 +35,7 @@ @@ -441,10 +441,10 @@ index 61ac6f9c8..44a1af6c8 100644 #include "log.hpp" #include "device_io_hid.hpp" +#include "device_io_dummy.hpp" - #include - #include + #include -@@ -56,7 +57,7 @@ namespace hw { + namespace hw { +@@ -55,7 +56,7 @@ namespace hw { void register_all(std::map> ®istry); @@ -453,8 +453,8 @@ index 61ac6f9c8..44a1af6c8 100644 // Origin: https://github.com/LedgerHQ/ledger-app-monero/blob/master/src/monero_types.h #define SW_OK 0x9000 -@@ -148,7 +149,11 @@ namespace hw { - mutable boost::mutex command_locker; +@@ -143,7 +144,11 @@ namespace hw { + mutable std::mutex command_locker; //IO +#if defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI) @@ -466,11 +466,11 @@ index 61ac6f9c8..44a1af6c8 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 d179e502b..fb71a0521 100644 +index 52bc7647a..98f36bdb6 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp -@@ -48,6 +48,9 @@ - #include +@@ -52,6 +52,9 @@ + #include #include "bc-ur/src/bc-ur.hpp" +#if defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI) @@ -479,7 +479,7 @@ index d179e502b..fb71a0521 100644 using namespace std; using namespace cryptonote; -@@ -3177,4 +3180,101 @@ uint64_t WalletImpl::getBytesSent() +@@ -3136,4 +3139,101 @@ uint64_t WalletImpl::getBytesSent() return m_wallet->get_bytes_sent(); } @@ -582,10 +582,10 @@ index d179e502b..fb71a0521 100644 + } // namespace diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h -index edf8bb8ce..6bfb61cb8 100644 +index a50983a69..194f21ebe 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h -@@ -301,6 +301,20 @@ private: +@@ -302,6 +302,20 @@ private: // cache connection status to avoid unnecessary RPC calls mutable std::atomic m_is_connected; boost::optional m_daemon_login{}; @@ -607,10 +607,10 @@ index edf8bb8ce..6bfb61cb8 100644 diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h -index 764adbfbf..f433b064f 100644 +index a5164260f..7d250bf8b 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h -@@ -1150,6 +1150,19 @@ struct Wallet +@@ -1146,6 +1146,19 @@ struct Wallet //! get bytes sent virtual uint64_t getBytesSent() = 0; @@ -631,10 +631,10 @@ index 764adbfbf..f433b064f 100644 /** diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp -index e81b8f83a..277be6ac9 100644 +index b18333e8f..bd92f7829 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, +@@ -184,10 +184,14 @@ bool WalletManagerImpl::verifyWalletPassword(const std::string &keys_file_name, bool WalletManagerImpl::queryWalletDevice(Wallet::Device& device_type, const std::string &keys_file_name, const std::string &password, uint64_t kdf_rounds) const { @@ -654,5 +654,5 @@ index e81b8f83a..277be6ac9 100644 std::vector WalletManagerImpl::findWallets(const std::string &path) -- -2.50.1 (Apple Git-155) +2.54.0 (Apple Git-157) diff --git a/patches/monero/0007-polyseed.patch b/patches/monero/0007-polyseed.patch index 5caf7c2..cda07eb 100644 --- a/patches/monero/0007-polyseed.patch +++ b/patches/monero/0007-polyseed.patch @@ -1,7 +1,7 @@ -From da0be39a81107060f032c8242c575f04ad61c59f Mon Sep 17 00:00:00 2001 +From 111adbce7b8231974d107c855383611489fbcd8d Mon Sep 17 00:00:00 2001 From: tobtoht Date: Tue, 12 Mar 2024 09:42:37 +0100 -Subject: [PATCH 07/20] polyseed +Subject: [PATCH 07/22] polyseed Co-authored-by: Czarek Nakamoto --- @@ -27,9 +27,9 @@ Co-authored-by: Czarek Nakamoto src/wallet/api/wallet2_api.h | 25 ++++ src/wallet/api/wallet_manager.cpp | 9 ++ src/wallet/api/wallet_manager.h | 10 ++ - src/wallet/wallet2.cpp | 100 ++++++++++++-- - src/wallet/wallet2.h | 30 +++- - 24 files changed, 805 insertions(+), 18 deletions(-) + src/wallet/wallet2.cpp | 101 ++++++++++++-- + src/wallet/wallet2.h | 31 ++++- + 24 files changed, 805 insertions(+), 20 deletions(-) create mode 160000 external/polyseed create mode 160000 external/utf8proc create mode 100644 src/polyseed/CMakeLists.txt @@ -39,10 +39,10 @@ Co-authored-by: Czarek Nakamoto create mode 100644 src/polyseed/polyseed.hpp diff --git a/.gitmodules b/.gitmodules -index 72af74d55..b838e84e0 100644 +index 57fdfbfc8..d768ea541 100644 --- a/.gitmodules +++ b/.gitmodules -@@ -11,6 +11,12 @@ +@@ -5,6 +5,12 @@ path = external/randomx url = https://github.com/MrCyjaneK/RandomX branch = cyjan-fix-ios @@ -56,29 +56,29 @@ index 72af74d55..b838e84e0 100644 path = external/supercop url = https://github.com/monero-project/supercop diff --git a/CMakeLists.txt b/CMakeLists.txt -index eb0d12225..390339523 100644 +index 8451e7959..7b8e9a965 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt -@@ -371,6 +371,8 @@ if(NOT MANUAL_SUBMODULES) - check_submodule(external/trezor-common) +@@ -391,6 +391,8 @@ if(NOT MANUAL_SUBMODULES) check_submodule(external/randomx) check_submodule(external/supercop) + check_submodule(external/mx25519) + check_submodule(external/polyseed) + check_submodule(external/utf8proc) endif() endif() -@@ -460,7 +462,7 @@ endif() - # elseif(CMAKE_SYSTEM_NAME MATCHES ".*BSDI.*") - # set(BSDI TRUE) +@@ -462,7 +464,7 @@ elseif(CMAKE_SYSTEM_NAME MATCHES ".*BSDI.*") + set(BSDI TRUE) + endif() --include_directories(external/rapidjson/include external/easylogging++ src contrib/epee/include external external/supercop/include) -+include_directories(external/rapidjson/include external/easylogging++ src contrib/epee/include external external/supercop/include external/polyseed/include external/utf8proc) +-include_directories(external/rapidjson/include external/easylogging++ src contrib/epee/include external external/supercop/include external/mx25519/include) ++include_directories(external/rapidjson/include external/easylogging++ src contrib/epee/include external external/supercop/include external/mx25519/include external/polyseed/include external/utf8proc) - if(APPLE) - cmake_policy(SET CMP0042 NEW) + if(MINGW) + set(DEFAULT_STATIC true) diff --git a/contrib/epee/include/wipeable_string.h b/contrib/epee/include/wipeable_string.h -index 65977cd97..594e15de4 100644 +index 0a324c159..16c4a8539 100644 --- a/contrib/epee/include/wipeable_string.h +++ b/contrib/epee/include/wipeable_string.h @@ -34,6 +34,7 @@ @@ -103,7 +103,7 @@ index 65977cd97..594e15de4 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 b016f2f48..f2f365b1b 100644 +index 5d4d87d8e..ce1a3baf0 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 b016f2f48..f2f365b1b 100644 + } diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt -index 074e23f16..f7e35f98f 100644 +index 900684b6d..3bb8186ca 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt -@@ -70,5 +70,7 @@ endif() +@@ -31,6 +31,8 @@ add_subdirectory(db_drivers) add_subdirectory(easylogging++) add_subdirectory(qrcodegen) @@ -133,6 +133,7 @@ index 074e23f16..f7e35f98f 100644 +add_subdirectory(utf8proc EXCLUDE_FROM_ALL) add_subdirectory(bc-ur) add_subdirectory(randomx EXCLUDE_FROM_ALL) + add_subdirectory(mx25519) diff --git a/external/polyseed b/external/polyseed new file mode 160000 index 000000000..bd79f5014 @@ -148,10 +149,10 @@ index 000000000..3de4596fb @@ -0,0 +1 @@ +Subproject commit 3de4596fbe28956855df2ecb3c11c0bbc3535838 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt -index 3335d3c21..06b708cf0 100644 +index bf8d1cb51..40b004bb7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt -@@ -95,6 +95,7 @@ add_subdirectory(net) +@@ -96,6 +96,7 @@ add_subdirectory(net) add_subdirectory(hardforks) add_subdirectory(blockchain_db) add_subdirectory(mnemonics) @@ -160,19 +161,19 @@ index 3335d3c21..06b708cf0 100644 if(NOT IOS) add_subdirectory(serialization) diff --git a/src/cryptonote_basic/CMakeLists.txt b/src/cryptonote_basic/CMakeLists.txt -index 1414be1b2..414936a05 100644 +index 0b6bf9790..95d54d599 100644 --- a/src/cryptonote_basic/CMakeLists.txt +++ b/src/cryptonote_basic/CMakeLists.txt -@@ -71,6 +71,7 @@ target_link_libraries(cryptonote_basic - checkpoints +@@ -72,6 +72,7 @@ target_link_libraries(cryptonote_basic cryptonote_format_utils_basic device + ringct_basic + polyseed_wrapper + ${Boost_CHRONO_LIBRARY} ${Boost_DATE_TIME_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY} - ${Boost_SERIALIZATION_LIBRARY} diff --git a/src/cryptonote_basic/account.cpp b/src/cryptonote_basic/account.cpp -index 4e87d4477..2d556f285 100644 +index 3ba5638bf..aaacf08e4 100644 --- a/src/cryptonote_basic/account.cpp +++ b/src/cryptonote_basic/account.cpp @@ -87,12 +87,16 @@ DISABLE_VS_WARNINGS(4244 4345) @@ -225,7 +226,7 @@ index 4e87d4477..2d556f285 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 93d1d28f0..1f76febce 100644 +index de5912032..ce6707b12 100644 --- a/src/cryptonote_basic/account.h +++ b/src/cryptonote_basic/account.h @@ -33,6 +33,7 @@ @@ -263,10 +264,10 @@ index 93d1d28f0..1f76febce 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 82891b9de..26200bf34 100644 +index 476a79e25..556eef259 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h -@@ -211,6 +211,8 @@ +@@ -244,6 +244,8 @@ #define DNS_BLOCKLIST_LIFETIME (86400 * 8) @@ -507,7 +508,7 @@ index 000000000..231a48a94 + } + + result = utf8proc_reencode(buffer, result, options); -+ if (result < 0 || result > POLYSEED_STR_SIZE) { ++ if (result < 0 || result > (POLYSEED_STR_SIZE - 1)) { + throw std::runtime_error("Unicode normalization failed"); + } + @@ -814,10 +815,10 @@ index 000000000..2c8c777a7 +#endif //POLYSEED_HPP \ No newline at end of file diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp -index fb71a0521..17a98c066 100644 +index 98f36bdb6..6eb63882c 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 +@@ -705,6 +705,28 @@ bool WalletImpl::recoverFromDevice(const std::string &path, const std::string &p return true; } @@ -846,7 +847,7 @@ index fb71a0521..17a98c066 100644 Wallet::Device WalletImpl::getDeviceType() const { return static_cast(m_wallet->get_device_type()); -@@ -845,6 +867,54 @@ std::string WalletImpl::seed(const std::string& seed_offset) const +@@ -822,6 +844,54 @@ std::string WalletImpl::seed(const std::string& seed_offset) const } } @@ -902,10 +903,10 @@ index fb71a0521..17a98c066 100644 { return m_wallet->get_seed_language(); diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h -index 6bfb61cb8..e7873dd78 100644 +index 194f21ebe..179897da2 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h -@@ -79,9 +79,19 @@ public: +@@ -80,9 +80,19 @@ public: bool recoverFromDevice(const std::string &path, const std::string &password, const std::string &device_name); @@ -926,10 +927,10 @@ index 6bfb61cb8..e7873dd78 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 f433b064f..80bfdacb2 100644 +index 7d250bf8b..03597da4e 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h -@@ -709,6 +709,10 @@ struct Wallet +@@ -702,6 +702,10 @@ struct Wallet static void warning(const std::string &category, const std::string &str); static void error(const std::string &category, const std::string &str); @@ -940,7 +941,7 @@ index f433b064f..80bfdacb2 100644 /** * @brief StartRefresh - Start/resume refresh thread (refresh every 10 seconds) */ -@@ -1321,6 +1325,27 @@ struct WalletManager +@@ -1317,6 +1321,27 @@ struct WalletManager uint64_t kdf_rounds = 1, WalletListener * listener = nullptr) = 0; @@ -969,10 +970,10 @@ index f433b064f..80bfdacb2 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 277be6ac9..da2056d8a 100644 +index bd92f7829..4a50ec8cc 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, +@@ -152,6 +152,15 @@ Wallet *WalletManagerImpl::createWalletFromDevice(const std::string &path, return wallet; } @@ -989,7 +990,7 @@ index 277be6ac9..da2056d8a 100644 { WalletImpl * wallet_ = dynamic_cast(wallet); diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h -index a223e1df9..28fcd36c9 100644 +index 45a9f010f..793ac492c 100644 --- a/src/wallet/api/wallet_manager.h +++ b/src/wallet/api/wallet_manager.h @@ -75,6 +75,16 @@ public: @@ -1010,28 +1011,28 @@ index a223e1df9..28fcd36c9 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 a8db99c3f..972310343 100644 +index 79cab37a4..a79ce5382 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp -@@ -92,6 +92,7 @@ using namespace epee; - #include "device/device_cold.hpp" - #include "device_trezor/device_trezor.hpp" - #include "net/socks_connect.h" +@@ -106,6 +106,7 @@ using namespace epee; + #include "tx_builder.h" + #include "tx_builder_serialization.h" + #include "hot_cold_serialization.h" //! @TODO: remove line after #52 is merged +#include "polyseed/include/polyseed.h" extern "C" { -@@ -1281,7 +1282,8 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended, std - m_enable_multisig(false), - m_pool_info_query_time(0), +@@ -1299,7 +1300,8 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended, std m_has_ever_refreshed_from_node(false), -- m_allow_mismatched_daemon_version(false) -+ m_allow_mismatched_daemon_version(false), + m_allow_mismatched_daemon_version(false), + m_curve_trees(fcmp_pp::curve_trees::curve_trees_v1()), +- m_tree_cache(fcmp_pp::curve_trees::TreeCacheV1(m_curve_trees, m_max_reorg_depth)) ++ m_tree_cache(fcmp_pp::curve_trees::TreeCacheV1(m_curve_trees, m_max_reorg_depth)), + m_polyseed(false) { - set_rpc_client_secret_key(rct::rct2sk(rct::skGen())); } -@@ -1486,6 +1488,20 @@ bool wallet2::get_seed(epee::wipeable_string& electrum_words, const epee::wipeab + +@@ -1503,6 +1505,20 @@ bool wallet2::get_seed(epee::wipeable_string& electrum_words, const epee::wipeab return true; } //---------------------------------------------------------------------------------------------------- @@ -1051,8 +1052,8 @@ index a8db99c3f..972310343 100644 +//---------------------------------------------------------------------------------------------------- bool wallet2::get_multisig_seed(epee::wipeable_string& seed, const epee::wipeable_string &passphrase) const { - bool ready; -@@ -4877,6 +4893,9 @@ boost::optional wallet2::get_keys_file_data(const crypt + const multisig::multisig_account_status ms_status{get_multisig_status()}; +@@ -5152,6 +5168,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 +1063,7 @@ index a8db99c3f..972310343 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()); -@@ -5116,6 +5135,7 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st +@@ -5385,6 +5404,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 +1071,7 @@ index a8db99c3f..972310343 100644 } else if(json.IsObject()) { -@@ -5356,6 +5376,9 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st +@@ -5611,6 +5631,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 +1081,7 @@ index a8db99c3f..972310343 100644 // Load encryption key used to encrypt background cache crypto::chacha_key custom_background_key; m_custom_background_key = boost::none; -@@ -5675,6 +5698,48 @@ void wallet2::init_type(hw::device::device_type device_type) +@@ -5942,6 +5965,48 @@ void wallet2::init_type(hw::device::device_type device_type) m_key_device_type = device_type; } @@ -1129,7 +1130,7 @@ index a8db99c3f..972310343 100644 /*! * \brief Generates a wallet or restores one. Assumes the multisig setup * has already completed for the provided multisig info. -@@ -5802,7 +5867,7 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wip +@@ -6069,7 +6134,7 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wip return retval; } @@ -1138,7 +1139,7 @@ index a8db99c3f..972310343 100644 { // -1 month for fluctuations in block time and machine date/time setup. // avg seconds per block -@@ -5826,7 +5891,7 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wip +@@ -6093,7 +6158,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,25 +1148,27 @@ index a8db99c3f..972310343 100644 uint64_t target_height = get_daemon_blockchain_target_height(err); if (err.empty()) { if (target_height < height) -@@ -13661,7 +13726,7 @@ uint64_t wallet2::get_daemon_blockchain_target_height(string &err) +@@ -13227,7 +13292,7 @@ uint64_t wallet2::get_daemon_blockchain_target_height(string &err) return target_height; } -uint64_t wallet2::get_approximate_blockchain_height() const +uint64_t wallet2::get_approximate_blockchain_height(uint64_t t) const { - // time of v2 fork - const time_t fork_time = m_nettype == TESTNET ? 1448285909 : m_nettype == STAGENET ? 1520937818 : 1458748658; -@@ -13670,7 +13735,7 @@ uint64_t wallet2::get_approximate_blockchain_height() const + const size_t wallet_num_hard_forks = m_nettype == TESTNET ? num_testnet_hard_forks + : m_nettype == STAGENET ? num_stagenet_hard_forks +@@ -13241,9 +13306,8 @@ uint64_t wallet2::get_approximate_blockchain_height() const + const uint64_t fork_block = wallet_hard_forks[wallet_num_hard_forks-1].height; // avg seconds per block const int seconds_per_block = DIFFICULTY_TARGET_V2; - // Calculated blockchain height -- uint64_t approx_blockchain_height = fork_block + (time(NULL) - fork_time)/seconds_per_block; -+ uint64_t approx_blockchain_height = fork_block + ((t > 0 ? t : time(NULL)) - fork_time)/seconds_per_block; +- // Calculated blockchain height + uint64_t approx_blockchain_height = fork_block; +- const time_t now = time(NULL); ++ const time_t now = t > 0 ? static_cast(t) : time(NULL); + if (now > fork_time) + approx_blockchain_height += (now - fork_time) / seconds_per_block; // 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) -@@ -15796,15 +15861,6 @@ bool wallet2::parse_uri(const std::string &uri, std::string &address, std::strin +@@ -15386,15 +15450,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 +1184,7 @@ index a8db99c3f..972310343 100644 std::tm date = { 0, 0, 0, 0, 0, 0, 0, 0 }; date.tm_year = year - 1900; date.tm_mon = month - 1; -@@ -15813,7 +15869,23 @@ uint64_t wallet2::get_blockchain_height_by_date(uint16_t year, uint8_t month, ui +@@ -15403,7 +15458,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,18 +1209,19 @@ index a8db99c3f..972310343 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 18e60d89a..419272a54 100644 +index aa70d5662..9b0660a1c 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h -@@ -72,6 +72,7 @@ +@@ -72,7 +72,7 @@ #include "message_store.h" - #include "wallet_light_rpc.h" - #include "wallet_rpc_helpers.h" + #include "fee_priority.h" + #include "fee_algorithm.h" +-#include "wallet2_basic/wallet2_types.h" +#include "polyseed/polyseed.hpp" #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "wallet.wallet2" -@@ -921,6 +922,20 @@ private: +@@ -397,6 +397,20 @@ private: void generate(const std::string& wallet_, const epee::wipeable_string& password, const epee::wipeable_string& multisig_data, bool create_address_file = false); @@ -1238,7 +1242,7 @@ index 18e60d89a..419272a54 100644 /*! * \brief Generates a wallet or restores one. * \param wallet_ Name of wallet file -@@ -1095,6 +1110,15 @@ private: +@@ -596,6 +610,15 @@ private: bool is_deterministic() const; bool get_seed(epee::wipeable_string& electrum_words, const epee::wipeable_string &passphrase = epee::wipeable_string()) const; @@ -1252,9 +1256,9 @@ index 18e60d89a..419272a54 100644 + bool get_polyseed(epee::wipeable_string& seed, epee::wipeable_string &passphrase) const; + /*! - * \brief Checks if light wallet. A light wallet sends view key to a server where the blockchain is scanned. - */ -@@ -1570,8 +1594,8 @@ private: + * \brief Gets the seed language + */ +@@ -1127,8 +1150,8 @@ private: /*! * \brief Calculates the approximate blockchain height from current date/time. */ @@ -1265,7 +1269,7 @@ index 18e60d89a..419272a54 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(); -@@ -1665,6 +1689,7 @@ private: +@@ -1222,6 +1245,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 +1277,7 @@ index 18e60d89a..419272a54 100644 bool is_synced(); -@@ -2011,6 +2036,7 @@ private: +@@ -1605,6 +1629,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 +1286,5 @@ index 18e60d89a..419272a54 100644 uint32_t m_multisig_threshold; std::vector m_multisig_signers; -- -2.50.1 (Apple Git-155) +2.54.0 (Apple Git-157) diff --git a/patches/monero/0008-coin-control.patch b/patches/monero/0008-coin-control.patch index 0ed4fb2..7f444b8 100644 --- a/patches/monero/0008-coin-control.patch +++ b/patches/monero/0008-coin-control.patch @@ -1,7 +1,7 @@ -From 6811bdac7b98fd29c0566e758fc2d4353b9c3cec Mon Sep 17 00:00:00 2001 +From 6999bacded86031ec5e517ffaaee5fa29784aa92 Mon Sep 17 00:00:00 2001 From: tobtoht Date: Tue, 12 Mar 2024 11:07:57 +0100 -Subject: [PATCH 08/20] coin control +Subject: [PATCH 08/22] coin control --- src/simplewallet/simplewallet.cpp | 2 +- @@ -13,19 +13,19 @@ Subject: [PATCH 08/20] coin control src/wallet/api/wallet.cpp | 170 +++++++++++++++++++++------ src/wallet/api/wallet.h | 10 +- src/wallet/api/wallet2_api.h | 52 ++++++++- - src/wallet/wallet2.cpp | 46 +++++++- - src/wallet/wallet2.h | 11 +- - 11 files changed, 667 insertions(+), 51 deletions(-) + src/wallet/wallet2.cpp | 43 ++++++- + src/wallet/wallet2.h | 10 +- + 11 files changed, 665 insertions(+), 49 deletions(-) create mode 100644 src/wallet/api/coins.cpp create mode 100644 src/wallet/api/coins.h create mode 100644 src/wallet/api/coins_info.cpp create mode 100644 src/wallet/api/coins_info.h diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp -index 39bf169f3..40e25e1d0 100644 +index c0a47e6de..22f29f813 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp -@@ -6917,7 +6917,7 @@ bool simple_wallet::transfer_main(const std::vector &args_, bool ca +@@ -6638,7 +6638,7 @@ bool simple_wallet::transfer_main(const std::vector &args_, bool ca { // figure out what tx will be necessary auto ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, priority, extra, @@ -35,7 +35,7 @@ index 39bf169f3..40e25e1d0 100644 if (ptx_vector.empty()) { diff --git a/src/wallet/api/CMakeLists.txt b/src/wallet/api/CMakeLists.txt -index af7948d8a..bb740e2ac 100644 +index 06d8db925..aaa465420 100644 --- a/src/wallet/api/CMakeLists.txt +++ b/src/wallet/api/CMakeLists.txt @@ -40,7 +40,9 @@ set(wallet_api_sources @@ -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 17a98c066..1b86404be 100644 +index 6eb63882c..6a27168b5 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -35,6 +35,7 @@ @@ -515,7 +515,7 @@ index 17a98c066..1b86404be 100644 #include "subaddress_account.h" #include "common_defines.h" #include "common/util.h" -@@ -473,6 +474,7 @@ WalletImpl::WalletImpl(NetworkType nettype, uint64_t kdf_rounds) +@@ -450,6 +451,7 @@ WalletImpl::WalletImpl(NetworkType nettype, uint64_t kdf_rounds) m_wallet->set_refresh_enabled(false); m_addressBook.reset(new AddressBookImpl(this)); m_subaddress.reset(new SubaddressImpl(this)); @@ -523,7 +523,7 @@ index 17a98c066..1b86404be 100644 m_subaddressAccount.reset(new SubaddressAccountImpl(this)); -@@ -2046,7 +2048,7 @@ PendingTransaction* WalletImpl::restoreMultisigTransaction(const string& signDat +@@ -2005,7 +2007,7 @@ PendingTransaction* WalletImpl::restoreMultisigTransaction(const string& signDat // - unconfirmed_transfer_details; // - confirmed_transfer_details) @@ -532,7 +532,7 @@ index 17a98c066..1b86404be 100644 { clearStatus(); -@@ -2083,57 +2085,116 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector amount, uint32_t mixin_count, @@ -747,7 +747,7 @@ index 17a98c066..1b86404be 100644 } PendingTransaction *WalletImpl::createSweepUnmixableTransaction() -@@ -2342,6 +2433,11 @@ AddressBook *WalletImpl::addressBook() +@@ -2302,6 +2393,11 @@ AddressBook *WalletImpl::addressBook() return m_addressBook.get(); } @@ -760,10 +760,10 @@ index 17a98c066..1b86404be 100644 { return m_subaddress.get(); diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h -index e7873dd78..bc782dd4a 100644 +index 179897da2..21af6f510 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h -@@ -46,6 +46,7 @@ class PendingTransactionImpl; +@@ -47,6 +47,7 @@ class PendingTransactionImpl; class UnsignedTransactionImpl; class AddressBookImpl; class SubaddressImpl; @@ -771,7 +771,7 @@ index e7873dd78..bc782dd4a 100644 class SubaddressAccountImpl; struct Wallet2CallbackImpl; -@@ -167,12 +168,14 @@ public: +@@ -169,12 +170,14 @@ public: optional> amount, uint32_t mixin_count, PendingTransaction::Priority priority = PendingTransaction::Priority_Low, uint32_t subaddr_account = 0, @@ -788,7 +788,7 @@ index e7873dd78..bc782dd4a 100644 virtual PendingTransaction * createSweepUnmixableTransaction() override; bool submitTransaction(const std::string &fileName) override; bool submitTransactionUR(const std::string &input) override; -@@ -201,6 +204,7 @@ public: +@@ -203,6 +206,7 @@ public: PendingTransaction::Priority priority) const override; virtual TransactionHistory * history() override; virtual AddressBook * addressBook() override; @@ -803,8 +803,8 @@ index e7873dd78..bc782dd4a 100644 + friend class CoinsImpl; friend class SubaddressImpl; friend class SubaddressAccountImpl; - -@@ -288,6 +293,7 @@ private: + friend class ::WalletApiAccessorTest; +@@ -289,6 +294,7 @@ private: std::unique_ptr m_wallet2Callback; std::unique_ptr m_addressBook; std::unique_ptr m_subaddress; @@ -813,10 +813,10 @@ index e7873dd78..bc782dd4a 100644 // multi-threaded refresh stuff diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h -index 80bfdacb2..97dd29bde 100644 +index 03597da4e..768a77a5d 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h -@@ -263,6 +263,51 @@ struct AddressBook +@@ -255,6 +255,51 @@ struct AddressBook virtual int lookupPaymentID(const std::string &payment_id) const = 0; }; @@ -868,7 +868,7 @@ index 80bfdacb2..97dd29bde 100644 struct SubaddressRow { public: SubaddressRow(std::size_t _rowId, const std::string &_address, const std::string &_label): -@@ -856,7 +901,8 @@ struct Wallet +@@ -858,7 +903,8 @@ struct Wallet optional> amount, uint32_t mixin_count, PendingTransaction::Priority = PendingTransaction::Priority_Low, uint32_t subaddr_account = 0, @@ -878,7 +878,7 @@ index 80bfdacb2..97dd29bde 100644 /*! * \brief createTransaction creates transaction. if dst_addr is an integrated address, payment_id is ignored -@@ -875,7 +921,8 @@ struct Wallet +@@ -877,7 +923,8 @@ struct Wallet optional amount, uint32_t mixin_count, PendingTransaction::Priority = PendingTransaction::Priority_Low, uint32_t subaddr_account = 0, @@ -888,7 +888,7 @@ index 80bfdacb2..97dd29bde 100644 /*! * \brief createSweepUnmixableTransaction creates transaction with unmixable outputs. -@@ -994,6 +1041,7 @@ struct Wallet +@@ -996,6 +1043,7 @@ struct Wallet virtual TransactionHistory * history() = 0; virtual AddressBook * addressBook() = 0; @@ -897,10 +897,10 @@ index 80bfdacb2..97dd29bde 100644 virtual SubaddressAccount * subaddressAccount() = 0; virtual void setListener(WalletListener *) = 0; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp -index 972310343..c50a840b6 100644 +index a79ce5382..89009bc77 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp -@@ -2136,12 +2136,21 @@ bool wallet2::frozen(const multisig_tx_set& txs) const +@@ -2363,12 +2363,21 @@ bool wallet2::frozen(const multisig_tx_set& txs) const return false; } @@ -922,7 +922,7 @@ index 972310343..c50a840b6 100644 void wallet2::thaw(const crypto::key_image &ki) { thaw(get_transfer_details(ki)); -@@ -2152,6 +2161,18 @@ bool wallet2::frozen(const crypto::key_image &ki) const +@@ -2379,6 +2388,18 @@ bool wallet2::frozen(const crypto::key_image &ki) const return frozen(get_transfer_details(ki)); } //---------------------------------------------------------------------------------------------------- @@ -941,23 +941,7 @@ index 972310343..c50a840b6 100644 size_t wallet2::get_transfer_details(const crypto::key_image &ki) const { for (size_t idx = 0; idx < m_transfers.size(); ++idx) -@@ -2563,6 +2584,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) - { -+ boost::unique_lock lock(m_transfers_mutex); - m_transfers.push_back(transfer_details{}); - transfer_details& td = m_transfers.back(); - td.m_block_height = height; -@@ -2666,6 +2688,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote - uint64_t extra_amount = amount - burnt; - if (!pool) - { -+ boost::unique_lock lock(m_transfers_mutex); - transfer_details &td = m_transfers[kit->second]; - td.m_block_height = height; - td.m_internal_output_index = o; -@@ -10526,7 +10549,7 @@ void wallet2::transfer_selected_rct(std::vector picks; float current_output_relatdness = 1.0f; -@@ -10537,6 +10560,9 @@ std::vector wallet2::pick_preferred_rct_inputs(uint64_t needed_money, ui +@@ -10584,6 +10605,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 +960,7 @@ index 972310343..c50a840b6 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) -@@ -10557,6 +10583,9 @@ std::vector wallet2::pick_preferred_rct_inputs(uint64_t needed_money, ui +@@ -10604,6 +10628,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 +970,7 @@ index 972310343..c50a840b6 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) -@@ -10568,6 +10597,9 @@ std::vector wallet2::pick_preferred_rct_inputs(uint64_t needed_money, ui +@@ -10615,6 +10642,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,16 +980,15 @@ index 972310343..c50a840b6 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) << "]"); -@@ -11140,7 +11172,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. --std::vector wallet2::create_transactions_2(std::vector dsts, const size_t fake_outs_count, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices, const unique_index_container& subtract_fee_from_outputs) -+std::vector wallet2::create_transactions_2(std::vector dsts, const size_t fake_outs_count, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices, const std::vector& preferred_input_list, const unique_index_container& subtract_fee_from_outputs) +@@ -10724,6 +10754,7 @@ std::vector wallet2::create_transactions_2( + const std::vector& extra, + uint32_t subaddr_account, + std::set subaddr_indices, ++ const std::vector& preferred_input_list, + const unique_index_container& subtract_fee_from_outputs, + const std::size_t max_n_inputs) { - //ensure device is let in NONE mode in any case - hw::device &hwdev = m_account.get_device(); -@@ -11348,6 +11380,9 @@ std::vector wallet2::create_transactions_2(std::vector wallet2::create_transactions_2( for (size_t i = 0; i < m_transfers.size(); ++i) { const transfer_details& td = m_transfers[i]; @@ -1015,7 +998,7 @@ index 972310343..c50a840b6 100644 if (m_ignore_fractional_outputs && td.amount() < fractional_threshold) { MDEBUG("Ignoring output " << i << " of amount " << print_money(td.amount()) << " which is below fractional threshold " << print_money(fractional_threshold)); -@@ -11439,7 +11474,7 @@ std::vector wallet2::create_transactions_2(std::vector wallet2::create_transactions_2( // will get us a known fee. uint64_t estimated_fee = estimate_fee(use_per_byte_fee, use_rct, 2, fake_outs_count, 2, extra.size(), bulletproof, clsag, bulletproof_plus, use_view_tags, base_fee, fee_quantization_mask); total_needed_money = needed_money + (subtract_fee_from_outputs.size() ? 0 : estimated_fee); @@ -1024,16 +1007,16 @@ index 972310343..c50a840b6 100644 if (!preferred_inputs.empty()) { string s; -@@ -11918,7 +11953,7 @@ bool wallet2::sanity_check(const std::vector &ptx_vector, c +@@ -11523,7 +11557,7 @@ bool wallet2::sanity_check(const std::vector &ptx_vector, c return true; } --std::vector wallet2::create_transactions_all(uint64_t below, 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, uint32_t subaddr_account, std::set subaddr_indices) -+std::vector wallet2::create_transactions_all(uint64_t below, 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, uint32_t subaddr_account, std::set subaddr_indices, const std::vector& preferred_input_list) +-std::vector wallet2::create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, fee_priority priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices) ++std::vector wallet2::create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, fee_priority priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices, const std::vector& preferred_input_list) { - std::vector unused_transfers_indices; - std::vector unused_dust_indices; -@@ -11947,6 +11982,9 @@ std::vector wallet2::create_transactions_all(uint64_t below + boost::lock_guard refresh_lock(m_refresh_mutex); + +@@ -11575,6 +11609,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,21 +1027,27 @@ index 972310343..c50a840b6 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 419272a54..d07dc7e8b 100644 +index 9b0660a1c..a46c2e341 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h -@@ -1223,8 +1223,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); -- std::vector create_transactions_2(std::vector dsts, const size_t fake_outs_count, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices, const unique_index_container& subtract_fee_from_outputs = {}); // pass subaddr_indices by value on purpose -- std::vector create_transactions_all(uint64_t below, 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, uint32_t subaddr_account, std::set subaddr_indices); -+ std::vector create_transactions_2(std::vector dsts, const size_t fake_outs_count, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices, const std::vector& preferred_input_list = {}, const unique_index_container& subtract_fee_from_outputs = {}); // pass subaddr_indices by value on purpose -+ std::vector create_transactions_all(uint64_t below, 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, uint32_t subaddr_account, std::set subaddr_indices, const std::vector& preferred_input_list = {}); - 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; -@@ -1576,6 +1576,7 @@ private: +@@ -745,6 +745,7 @@ private: + const std::vector& extra, + uint32_t subaddr_account, + std::set subaddr_indices, // pass subaddr_indices by value on purpose ++ const std::vector& preferred_input_list = {}, + const unique_index_container& subtract_fee_from_outputs = {}, + const std::size_t max_n_inputs = 0); + /** +@@ -762,7 +763,7 @@ private: + * + * Sweep-all-style means that transactions are added until all inputs <= amount `below` are spent. + */ +- std::vector create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, fee_priority priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices); ++ std::vector create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, fee_priority priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices, const std::vector& preferred_input_list = {}); + /** + * brief: create_transactions_single: create "sweep-single" style txs (or tx proposals in hot/cold & multisig wallets) + * param: ki - the key image of the input that is to be spent +@@ -1119,6 +1120,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 +1055,7 @@ index 419272a54..d07dc7e8b 100644 uint8_t get_current_hard_fork(); void get_hard_fork_info(uint8_t version, uint64_t &earliest_height); -@@ -1808,7 +1809,9 @@ private: +@@ -1339,7 +1341,9 @@ private: void freeze(size_t idx); void thaw(size_t idx); bool frozen(size_t idx) const; @@ -1076,16 +1065,16 @@ index 419272a54..d07dc7e8b 100644 void thaw(const crypto::key_image &ki); bool frozen(const crypto::key_image &ki) const; bool frozen(const transfer_details &td) const; -@@ -1849,6 +1852,8 @@ private: - +@@ -1378,6 +1382,8 @@ private: static std::string get_default_daemon_address() { CRITICAL_REGION_LOCAL(default_daemon_address_lock); return default_daemon_address; } -+ boost::shared_mutex m_transfers_mutex; + #ifndef IN_UNIT_TESTS ++ boost::shared_mutex m_transfers_mutex; + private: + #endif /*! - * \brief Stores wallet information to wallet file. -@@ -1920,7 +1925,7 @@ private: +@@ -1514,7 +1520,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 +1084,5 @@ index 419272a54..d07dc7e8b 100644 void set_unspent(size_t idx); bool is_spent(const transfer_details &td, bool strict = true) const; -- -2.50.1 (Apple Git-155) +2.54.0 (Apple Git-157) diff --git a/patches/monero/0009-Add-hex-encoding-and-tx-key-getter-for-PendingTransc.patch b/patches/monero/0009-Add-hex-encoding-and-tx-key-getter-for-PendingTransc.patch index df3b9ad..e237ad5 100644 --- a/patches/monero/0009-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 300a5110cccb1e3caf763b1f9bf5dc0ecc0973c8 Mon Sep 17 00:00:00 2001 +From b3c572ffb401da3aafddc72c9127701abcd70ff1 Mon Sep 17 00:00:00 2001 From: M Date: Fri, 21 Apr 2023 15:43:47 -0400 -Subject: [PATCH 09/20] Add hex encoding and tx key getter for +Subject: [PATCH 09/22] Add hex encoding and tx key getter for PendingTransction in wallet api. --- @@ -11,7 +11,7 @@ Subject: [PATCH 09/20] 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 9c3c26ee5..1f714d229 100644 +index 919e210f0..796e7a50b 100644 --- a/src/wallet/api/pending_transaction.cpp +++ b/src/wallet/api/pending_transaction.cpp @@ -80,6 +80,22 @@ std::vector PendingTransactionImpl::txid() const @@ -30,7 +30,7 @@ index 9c3c26ee5..1f714d229 100644 +{ + std::vector keys; + for (const auto& pt: m_pending_tx) -+ keys.push_back(epee::string_tools::pod_to_hex(pt.tx_key)); ++ keys.push_back(epee::string_tools::pod_to_hex(unwrap(unwrap(pt.tx_key)))); + return keys; +} + @@ -38,7 +38,7 @@ index 9c3c26ee5..1f714d229 100644 { diff --git a/src/wallet/api/pending_transaction.h b/src/wallet/api/pending_transaction.h -index 403bfe281..0cc6c58e9 100644 +index 8a70d774d..eb5c75c5f 100644 --- a/src/wallet/api/pending_transaction.h +++ b/src/wallet/api/pending_transaction.h @@ -59,6 +59,8 @@ public: @@ -51,10 +51,10 @@ index 403bfe281..0cc6c58e9 100644 private: friend class WalletImpl; diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h -index 97dd29bde..b5cccac40 100644 +index 768a77a5d..34df402e4 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h -@@ -127,6 +127,8 @@ struct PendingTransaction +@@ -119,6 +119,8 @@ struct PendingTransaction * @return vector of base58-encoded signers' public keys */ virtual std::vector signersKeys() const = 0; @@ -64,5 +64,5 @@ index 97dd29bde..b5cccac40 100644 /** -- -2.50.1 (Apple Git-155) +2.54.0 (Apple Git-157) diff --git a/patches/monero/0010-Add-recoverDeterministicWalletFromSpendKey.patch b/patches/monero/0010-Add-recoverDeterministicWalletFromSpendKey.patch index e8f7538..de08b57 100644 --- a/patches/monero/0010-Add-recoverDeterministicWalletFromSpendKey.patch +++ b/patches/monero/0010-Add-recoverDeterministicWalletFromSpendKey.patch @@ -1,7 +1,7 @@ -From 3e15968c13d2a7c0098d26e72168a5774d6cdd89 Mon Sep 17 00:00:00 2001 +From f26dc4ee8737fa57da58037134c2d2ed987834a5 Mon Sep 17 00:00:00 2001 From: Konstantin Ullrich Date: Wed, 11 Oct 2023 16:47:59 +0200 -Subject: [PATCH 10/20] Add recoverDeterministicWalletFromSpendKey +Subject: [PATCH 10/22] Add recoverDeterministicWalletFromSpendKey This function is used by Cake Wallet to enable polyseed (dart implementation) support. @@ -19,10 +19,10 @@ 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 1b86404be..00918e357 100644 +index 6a27168b5..a61addbe4 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 +@@ -801,6 +801,35 @@ bool WalletImpl::recover(const std::string &path, const std::string &password, c return status() == Status_Ok; } @@ -59,10 +59,10 @@ index 1b86404be..00918e357 100644 { diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h -index bc782dd4a..bfe81c590 100644 +index 21af6f510..d4b11d96a 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h -@@ -77,6 +77,10 @@ public: +@@ -78,6 +78,10 @@ public: const std::string &address_string, const std::string &viewkey_string, const std::string &spendkey_string = ""); @@ -74,10 +74,10 @@ index bc782dd4a..bfe81c590 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 b5cccac40..fcb8187d4 100644 +index 34df402e4..68efcdaf8 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h -@@ -1324,6 +1324,25 @@ struct WalletManager +@@ -1320,6 +1320,25 @@ struct WalletManager return createWalletFromKeys(path, password, language, testnet ? TESTNET : MAINNET, restoreHeight, addressString, viewKeyString, spendKeyString); } @@ -104,10 +104,10 @@ index b5cccac40..fcb8187d4 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 da2056d8a..c200f52ae 100644 +index 4a50ec8cc..7ee87d04c 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, +@@ -123,6 +123,22 @@ Wallet *WalletManagerImpl::createWalletFromKeys(const std::string &path, return wallet; } @@ -131,7 +131,7 @@ index da2056d8a..c200f52ae 100644 const std::string &password, NetworkType nettype, diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h -index 28fcd36c9..be3ff8184 100644 +index 793ac492c..96373affb 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 28fcd36c9..be3ff8184 100644 const std::string &password, NetworkType nettype, -- -2.50.1 (Apple Git-155) +2.54.0 (Apple Git-157) diff --git a/patches/monero/0011-add-monero-submodule-support.patch b/patches/monero/0011-add-monero-submodule-support.patch index fee1fba..89303c0 100644 --- a/patches/monero/0011-add-monero-submodule-support.patch +++ b/patches/monero/0011-add-monero-submodule-support.patch @@ -1,7 +1,7 @@ -From e06b0e86b6b6204bf20c0caadde64da1f0966da1 Mon Sep 17 00:00:00 2001 +From a3e5604fe761d64c7d55c6c9afe29c05c35883ed Mon Sep 17 00:00:00 2001 From: cyan Date: Thu, 7 Nov 2024 16:46:24 +0000 -Subject: [PATCH 11/20] add monero submodule support +Subject: [PATCH 11/22] add monero submodule support --- CMakeLists.txt | 12 ++++++------ @@ -9,10 +9,10 @@ Subject: [PATCH 11/20] add monero submodule support 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt -index 390339523..d0af390d0 100644 +index 7b8e9a965..2f1aa73b4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt -@@ -75,7 +75,7 @@ if (${CMAKE_VERSION} VERSION_GREATER "3.0.0" AND CMAKE_MAKE_PROGRAM MATCHES "nin +@@ -100,7 +100,7 @@ if (${CMAKE_VERSION} VERSION_GREATER "3.0.0" AND CMAKE_MAKE_PROGRAM MATCHES "nin set(CMAKE_JOB_POOL_LINK link_job_pool) endif () endif () @@ -21,8 +21,8 @@ index 390339523..d0af390d0 100644 option (USE_CLANG_TIDY_C "Lint the code with clang-tidy - variant C" OFF) option (USE_CLANG_TIDY_CXX "Lint the code with clang-tidy - variant C++" OFF) if (USE_CLANG_TIDY_C AND USE_CLANG_TIDY_CXX) -@@ -223,9 +223,9 @@ function(forbid_undefined_symbols) - cmake_minimum_required(VERSION 3.5) +@@ -235,9 +235,9 @@ function(forbid_undefined_symbols) + cmake_minimum_required(VERSION 3.10) project(test) option(EXPECT_SUCCESS "" ON) -file(WRITE "${CMAKE_SOURCE_DIR}/incorrect_source.cpp" "void undefined_symbol(); void symbol() { undefined_symbol(); }") @@ -33,7 +33,7 @@ index 390339523..d0af390d0 100644 endif() add_library(l0 SHARED incorrect_source.cpp) add_library(l1 MODULE incorrect_source.cpp) -@@ -363,7 +363,7 @@ if(NOT MANUAL_SUBMODULES) +@@ -384,7 +384,7 @@ if(NOT MANUAL_SUBMODULES) message(FATAL_ERROR "Submodule '${relative_path}' is not up-to-date. Please update all submodules with\ngit submodule update --init --force\nor run cmake with -DMANUAL_SUBMODULES=1\n") endif() endfunction () @@ -41,17 +41,17 @@ index 390339523..d0af390d0 100644 + message(STATUS "Checking submodules") # check_submodule(external/bc-ur) - check_submodule(external/miniupnp) -@@ -390,7 +390,7 @@ else() + check_submodule(external/rapidjson) +@@ -405,7 +405,7 @@ if(PER_BLOCK_CHECKPOINT) endif() list(INSERT CMAKE_MODULE_PATH 0 - "${CMAKE_SOURCE_DIR}/cmake") + "${CMAKE_CURRENT_SOURCE_DIR}/cmake") - if (NOT DEFINED ENV{DEVELOPER_LOCAL_TOOLS}) - message(STATUS "Could not find DEVELOPER_LOCAL_TOOLS in env (not required)") -@@ -1003,7 +1003,7 @@ else() + option(BOOST_IGNORE_SYSTEM_PATHS "Ignore boost system paths for local boost installation" OFF) + if (BOOST_IGNORE_SYSTEM_PATHS) +@@ -864,7 +864,7 @@ include(CheckTrezor) # random crash on startup when asan is on if pie is enabled if(NOT SANITIZE AND ANDROID AND NOT BUILD_GUI_DEPS STREQUAL "ON" OR IOS) @@ -61,18 +61,18 @@ index 390339523..d0af390d0 100644 set(PIC_FLAG "") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIE") diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp -index 0cf75a1c4..66def08ef 100644 +index 6727345ea..5f666ea61 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp -@@ -1261,7 +1261,7 @@ namespace tools +@@ -1276,7 +1276,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); + const fee_priority priority = m_wallet->adjust_priority(fee_priority_utilities::from_integral(req.priority, fee_algo)); - std::vector ptx_vector = m_wallet->create_transactions_2(dsts, mixin, priority, extra, req.account_index, req.subaddr_indices, req.subtract_fee_from_outputs); + std::vector ptx_vector = m_wallet->create_transactions_2(dsts, mixin, priority, extra, req.account_index, req.subaddr_indices, {}, req.subtract_fee_from_outputs); if (ptx_vector.empty()) { -- -2.50.1 (Apple Git-155) +2.54.0 (Apple Git-157) diff --git a/patches/monero/0012-fix-iOS-depends-build.patch b/patches/monero/0012-fix-iOS-depends-build.patch index 2e4845e..6e8aea0 100644 --- a/patches/monero/0012-fix-iOS-depends-build.patch +++ b/patches/monero/0012-fix-iOS-depends-build.patch @@ -1,49 +1,56 @@ -From 92ca945665cab44adace3331685ae4270a14c07e Mon Sep 17 00:00:00 2001 +From eeab3324de1638a035b28b05d414e2d999c9331a Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Thu, 21 Nov 2024 06:05:03 -0500 -Subject: [PATCH 12/20] fix iOS depends build +Subject: [PATCH 12/22] fix iOS depends build --- - CMakeLists.txt | 4 ---- - src/checkpoints/CMakeLists.txt | 6 +++++- - src/cryptonote_basic/CMakeLists.txt | 6 +++++- - src/cryptonote_basic/miner.cpp | 8 ++++---- - 4 files changed, 14 insertions(+), 10 deletions(-) + CMakeLists.txt | 3 --- + src/checkpoints/CMakeLists.txt | 14 ++++++++++++++ + src/cryptonote_basic/CMakeLists.txt | 6 +++++- + src/cryptonote_basic/miner.cpp | 8 ++++---- + 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt -index d0af390d0..b814c76b7 100644 +index 2f1aa73b4..133997f74 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt -@@ -39,10 +39,6 @@ include(CheckLibraryExists) - include(CheckFunctionExists) - include(FindPythonInterp) +@@ -52,9 +52,6 @@ if (POLICY CMP0179) + cmake_policy(SET CMP0179 NEW) + endif() -if (IOS) - INCLUDE(CmakeLists_IOS.txt) -endif() -- - cmake_minimum_required(VERSION 3.5) - message(STATUS "CMake version ${CMAKE_VERSION}") + + project(monero) diff --git a/src/checkpoints/CMakeLists.txt b/src/checkpoints/CMakeLists.txt -index 665441f62..841df3256 100644 +index 5e680e736..939aa3b90 100644 --- a/src/checkpoints/CMakeLists.txt +++ b/src/checkpoints/CMakeLists.txt -@@ -28,7 +28,11 @@ +@@ -26,6 +26,20 @@ + # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - if(APPLE) - if(DEPENDS) -- list(APPEND EXTRA_LIBRARIES "-framework Foundation -framework ApplicationServices -framework AppKit -framework IOKit") ++if(APPLE) ++ if(DEPENDS) + if(${CMAKE_SYSTEM_NAME} STREQUAL "iOS") + list(APPEND EXTRA_LIBRARIES "-framework Foundation -framework IOKit") + else() + list(APPEND EXTRA_LIBRARIES "-framework Foundation -framework ApplicationServices -framework AppKit -framework IOKit") + endif() - else() - find_library(IOKIT_LIBRARY IOKit) - mark_as_advanced(IOKIT_LIBRARY) ++ else() ++ find_library(IOKIT_LIBRARY IOKit) ++ mark_as_advanced(IOKIT_LIBRARY) ++ list(APPEND EXTRA_LIBRARIES ${IOKIT_LIBRARY}) ++ endif() ++endif() ++ + set(checkpoints_sources + checkpoints.cpp) + diff --git a/src/cryptonote_basic/CMakeLists.txt b/src/cryptonote_basic/CMakeLists.txt -index 414936a05..81c81767f 100644 +index 95d54d599..56fc6d274 100644 --- a/src/cryptonote_basic/CMakeLists.txt +++ b/src/cryptonote_basic/CMakeLists.txt @@ -28,7 +28,11 @@ @@ -60,10 +67,10 @@ index 414936a05..81c81767f 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 71b8f78cc..0f53f024e 100644 +index 9f702a925..346ed6fd6 100644 --- a/src/cryptonote_basic/miner.cpp +++ b/src/cryptonote_basic/miner.cpp -@@ -45,7 +45,7 @@ +@@ -46,7 +46,7 @@ #include "boost/logic/tribool.hpp" #include @@ -72,7 +79,7 @@ index 71b8f78cc..0f53f024e 100644 #include #include #include -@@ -883,7 +883,7 @@ namespace cryptonote +@@ -885,7 +885,7 @@ namespace cryptonote return true; @@ -81,7 +88,7 @@ index 71b8f78cc..0f53f024e 100644 mach_msg_type_number_t count; kern_return_t status; -@@ -949,7 +949,7 @@ namespace cryptonote +@@ -951,7 +951,7 @@ namespace cryptonote return true; } @@ -90,7 +97,7 @@ index 71b8f78cc..0f53f024e 100644 struct tms tms; if ( times(&tms) != (clock_t)-1 ) -@@ -978,7 +978,7 @@ namespace cryptonote +@@ -980,7 +980,7 @@ namespace cryptonote return boost::logic::tribool(power_status.ACLineStatus != 1); } @@ -100,5 +107,5 @@ index 71b8f78cc..0f53f024e 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.50.1 (Apple Git-155) +2.54.0 (Apple Git-157) diff --git a/patches/monero/0013-include-locale-only-when-targeting-WIN32.patch b/patches/monero/0013-include-locale-only-when-targeting-WIN32.patch index 56c8132..63fc7a8 100644 --- a/patches/monero/0013-include-locale-only-when-targeting-WIN32.patch +++ b/patches/monero/0013-include-locale-only-when-targeting-WIN32.patch @@ -1,44 +1,26 @@ -From 60ef2d2a25b07aca8f25ef5016eb4b9b7c253a50 Mon Sep 17 00:00:00 2001 +From 3e83b0212fe9a824ff9e8c399ef86e25c73afa13 Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Mon, 18 Nov 2024 10:57:37 -0500 -Subject: [PATCH 13/20] include locale only when targeting WIN32 +Subject: [PATCH 13/22] include locale only when targeting WIN32 --- - CMakeLists.txt | 6 +++++- - src/wallet/api/wallet.cpp | 2 ++ - 2 files changed, 7 insertions(+), 1 deletion(-) + CMakeLists.txt | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt -index b814c76b7..cef44dd1b 100644 +index 133997f74..95326743c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt -@@ -1090,7 +1090,11 @@ if(NOT Boost_FOUND) - elseif(Boost_FOUND) +@@ -953,7 +953,8 @@ 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) + set(BOOST_COMPONENTS filesystem thread date_time chrono serialization program_options) +- if (WIN32) + + if(WIN32) -+ list(APPEND BOOST_COMPONENTS locale) -+ endif() + 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 00918e357..107f516f3 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.50.1 (Apple Git-155) +2.54.0 (Apple Git-157) diff --git a/patches/monero/0014-change-earliest-fork-height-message.patch b/patches/monero/0014-change-earliest-fork-height-message.patch index 3a4321b..5345e0d 100644 --- a/patches/monero/0014-change-earliest-fork-height-message.patch +++ b/patches/monero/0014-change-earliest-fork-height-message.patch @@ -1,17 +1,17 @@ -From 7c58eb392e94ff5b50bcb15b8d91038476743ba9 Mon Sep 17 00:00:00 2001 +From 513d714b87d2a00e1fcf84d2b0be88acdef32e76 Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Wed, 29 Jan 2025 16:13:28 +0100 -Subject: [PATCH 14/20] change earliest fork height message +Subject: [PATCH 14/22] 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 c50a840b6..a7532d7ec 100644 +index 89009bc77..06a732296 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp -@@ -12380,7 +12380,7 @@ bool wallet2::use_fork_rules(uint8_t version, int64_t early_blocks) +@@ -12053,7 +12053,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); @@ -21,5 +21,5 @@ index c50a840b6..a7532d7ec 100644 bool close_enough = (int64_t)height >= (int64_t)earliest_height - early_blocks && earliest_height != std::numeric_limits::max(); // start using the rules that many blocks beforehand if (close_enough) -- -2.50.1 (Apple Git-155) +2.54.0 (Apple Git-157) diff --git a/patches/monero/0015-remove-trivially_copyable-assert.patch b/patches/monero/0015-remove-trivially_copyable-assert.patch index 8113bdd..d3293fe 100644 --- a/patches/monero/0015-remove-trivially_copyable-assert.patch +++ b/patches/monero/0015-remove-trivially_copyable-assert.patch @@ -1,24 +1,34 @@ -From dc67abfbc3dec2f4aa4c4157378509c9ee07cb0b Mon Sep 17 00:00:00 2001 +From 011bbbadc9388105e44524d59b7468ecf6c863a9 Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Thu, 20 Feb 2025 08:36:28 +0100 -Subject: [PATCH 15/20] remove trivially_copyable assert +Subject: [PATCH 15/22] remove trivially_copyable assert --- - contrib/epee/include/span.h | 1 - - 1 file changed, 1 deletion(-) + contrib/epee/include/span.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/epee/include/span.h b/contrib/epee/include/span.h -index 01dc387d6..2ad733a2f 100644 +index ccc55db53..2774881a1 100644 --- a/contrib/epee/include/span.h +++ b/contrib/epee/include/span.h -@@ -162,7 +162,6 @@ namespace epee +@@ -159,7 +159,7 @@ 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"); + static_assert(std::is_standard_layout_v, "type must have standard layout"); +- static_assert(std::has_unique_object_representations_v, "type must be trivially copyable with no padding"); ++ static_assert(alignof(T) == 1, "type may have padding"); return {reinterpret_cast(std::addressof(src)), sizeof(T)}; } + +@@ -169,7 +169,7 @@ namespace epee + { + static_assert(!std::is_empty(), "empty types will not work -> sizeof == 1"); + static_assert(std::is_standard_layout_v, "type must have standard layout"); +- static_assert(std::has_unique_object_representations_v, "type must be trivially copyable with no padding"); ++ static_assert(alignof(T) == 1, "type may have padding"); + return {reinterpret_cast(std::addressof(src)), sizeof(T)}; + } + -- -2.50.1 (Apple Git-155) +2.54.0 (Apple Git-157) diff --git a/patches/monero/0016-serialize-cache-to-JSON.patch b/patches/monero/0016-serialize-cache-to-JSON.patch index 38aaee4..b2171c2 100644 --- a/patches/monero/0016-serialize-cache-to-JSON.patch +++ b/patches/monero/0016-serialize-cache-to-JSON.patch @@ -1,7 +1,7 @@ -From e6785290c24eb48d6b6aec8e1831b96f65cd3bfd Mon Sep 17 00:00:00 2001 +From 0c287081f916066ba227fb24044d26950a767a59 Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Tue, 12 Aug 2025 07:09:14 -0400 -Subject: [PATCH 16/20] serialize cache to JSON +Subject: [PATCH 16/22] serialize cache to JSON --- src/wallet/CMakeLists.txt | 1 + @@ -9,27 +9,27 @@ Subject: [PATCH 16/20] serialize cache to JSON src/wallet/api/wallet.h | 2 + src/wallet/api/wallet2_api.h | 3 + src/wallet/wallet2.h | 6 + - src/wallet/wallet_cache_to_json.cpp | 368 ++++++++++++++++++++++++++++ - 6 files changed, 385 insertions(+) + src/wallet/wallet_cache_to_json.cpp | 334 ++++++++++++++++++++++++++++ + 6 files changed, 351 insertions(+) create mode 100644 src/wallet/wallet_cache_to_json.cpp diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt -index b163212b7..196ad671f 100644 +index db7c3c06b..4c80e64a4 100644 --- a/src/wallet/CMakeLists.txt +++ b/src/wallet/CMakeLists.txt -@@ -38,6 +38,7 @@ set(wallet_sources - message_store.cpp +@@ -40,6 +40,7 @@ set(wallet_sources message_transporter.cpp - wallet_rpc_payments.cpp + scanning_tools.cpp + tx_builder.cpp + wallet_cache_to_json.cpp ) monero_find_all_headers(wallet_private_headers "${CMAKE_CURRENT_SOURCE_DIR}") diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp -index 107f516f3..c24b4a97d 100644 +index a61addbe4..57f1203c6 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp -@@ -3474,4 +3474,9 @@ void Wallet::setLedgerCallback(void (*sendToLedgerDevice)(unsigned char *command +@@ -3431,4 +3431,9 @@ void Wallet::setLedgerCallback(void (*sendToLedgerDevice)(unsigned char *command #endif } @@ -40,10 +40,10 @@ index 107f516f3..c24b4a97d 100644 + } // namespace diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h -index bfe81c590..98c03b9c1 100644 +index d4b11d96a..8e4846e27 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h -@@ -335,6 +335,8 @@ private: +@@ -336,6 +336,8 @@ private: bool getWaitsForDeviceSend(); bool getWaitsForDeviceReceive(); @@ -53,10 +53,10 @@ index bfe81c590..98c03b9c1 100644 diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h -index fcb8187d4..3d11929f9 100644 +index 68efcdaf8..4552c02d0 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h -@@ -1217,6 +1217,9 @@ struct Wallet +@@ -1213,6 +1213,9 @@ struct Wallet static void setDeviceReceivedData(unsigned char* data, size_t len); static void setDeviceSendData(unsigned char* data, size_t len); static void setLedgerCallback(void (*sendToLedgerDevice)(unsigned char *command, unsigned int cmd_len)); @@ -67,11 +67,11 @@ index fcb8187d4..3d11929f9 100644 /** diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h -index d07dc7e8b..37a2447d2 100644 +index a46c2e341..42e1c02d9 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h -@@ -1436,6 +1436,12 @@ private: - FIELD(m_background_sync_data) +@@ -993,6 +993,12 @@ private: + FIELD(m_tree_cache) END_SERIALIZE() + /*! @@ -85,16 +85,15 @@ index d07dc7e8b..37a2447d2 100644 * \param file_path Wallet file path diff --git a/src/wallet/wallet_cache_to_json.cpp b/src/wallet/wallet_cache_to_json.cpp new file mode 100644 -index 000000000..4743852ca +index 000000000..cbeba044e --- /dev/null +++ b/src/wallet/wallet_cache_to_json.cpp -@@ -0,0 +1,368 @@ +@@ -0,0 +1,334 @@ +#include "wallet2.h" -+#include "serialization/binary_archive.h" ++#include "wallet2_basic/wallet2_serialization.h" +#include "serialization/json_archive.h" +#include "serialization/serialization.h" +#include -+#include + +namespace tools +{ @@ -182,11 +181,10 @@ index 000000000..4743852ca + try + { + std::stringstream oss; -+ json_archive ar(oss, true); // true for pretty printing ++ json_archive ar(oss, true); + + ar.begin_object(); + -+ // MAGIC_FIELD("monero wallet cache") + std::string magic = "monero wallet cache"; + ar.tag("magic"); + ar.serialize_blob((void*)magic.data(), magic.size()); @@ -195,8 +193,7 @@ index 000000000..4743852ca + return json_result.c_str(); + } + -+ // VERSION_FIELD(2) -+ uint32_t version = 2; ++ uint32_t version = 3; + ar.tag("version"); + ar.serialize_varint(version); + if (!ar.good()) { @@ -204,112 +201,96 @@ index 000000000..4743852ca + return json_result.c_str(); + } + -+ // FIELD(m_blockchain) - hashchain type, has serialization support + ar.tag("m_blockchain"); + if (!::serialization::serialize(ar, const_cast(m_blockchain))) { + json_result = "{\"error\":\"Failed to serialize m_blockchain\"}"; + return json_result.c_str(); + } + -+ // FIELD(m_transfers) - transfer_container (std::vector) + ar.tag("m_transfers"); + if (!::serialization::serialize(ar, const_cast(m_transfers))) { + json_result = "{\"error\":\"Failed to serialize m_transfers\"}"; + return json_result.c_str(); + } + -+ // FIELD(m_account_public_address) - cryptonote::account_public_address + ar.tag("m_account_public_address"); + if (!::serialization::serialize(ar, const_cast(m_account_public_address))) { + json_result = "{\"error\":\"Failed to serialize m_account_public_address\"}"; + return json_result.c_str(); + } + -+ // FIELD(m_key_images) - serializable_unordered_map + ar.tag("m_key_images"); -+ if (!::serialization::serialize(ar, const_cast&>(m_key_images))) { ++ if (!::serialization::serialize(ar, const_cast(m_key_images))) { + json_result = "{\"error\":\"Failed to serialize m_key_images\"}"; + return json_result.c_str(); + } + -+ // FIELD(m_unconfirmed_txs) - serializable_unordered_map + ar.tag("m_unconfirmed_txs"); -+ if (!::serialization::serialize(ar, const_cast&>(m_unconfirmed_txs))) { ++ if (!::serialization::serialize(ar, const_cast(m_unconfirmed_txs))) { + json_result = "{\"error\":\"Failed to serialize m_unconfirmed_txs\"}"; + return json_result.c_str(); + } + -+ // FIELD(m_payments) - payment_container (serializable_unordered_multimap) + ar.tag("m_payments"); + if (!::serialization::serialize(ar, const_cast(m_payments))) { + json_result = "{\"error\":\"Failed to serialize m_payments\"}"; + return json_result.c_str(); + } + -+ // FIELD(m_tx_keys) - serializable_unordered_map + ar.tag("m_tx_keys"); -+ if (!::serialization::serialize(ar, const_cast&>(m_tx_keys))) { ++ if (!::serialization::serialize(ar, const_cast(m_tx_keys))) { + json_result = "{\"error\":\"Failed to serialize m_tx_keys\"}"; + return json_result.c_str(); + } + -+ // FIELD(m_confirmed_txs) - serializable_unordered_map + ar.tag("m_confirmed_txs"); -+ if (!::serialization::serialize(ar, const_cast&>(m_confirmed_txs))) { ++ if (!::serialization::serialize(ar, const_cast(m_confirmed_txs))) { + json_result = "{\"error\":\"Failed to serialize m_confirmed_txs\"}"; + return json_result.c_str(); + } + -+ // FIELD(m_tx_notes) - serializable_unordered_map + ar.tag("m_tx_notes"); -+ if (!::serialization::serialize(ar, const_cast&>(m_tx_notes))) { ++ if (!::serialization::serialize(ar, const_cast(m_tx_notes))) { + json_result = "{\"error\":\"Failed to serialize m_tx_notes\"}"; + return json_result.c_str(); + } + -+ // FIELD(m_unconfirmed_payments) - serializable_unordered_multimap + ar.tag("m_unconfirmed_payments"); -+ if (!::serialization::serialize(ar, const_cast&>(m_unconfirmed_payments))) { ++ if (!::serialization::serialize(ar, const_cast(m_unconfirmed_payments))) { + json_result = "{\"error\":\"Failed to serialize m_unconfirmed_payments\"}"; + return json_result.c_str(); + } + -+ // FIELD(m_pub_keys) - serializable_unordered_map + ar.tag("m_pub_keys"); -+ if (!::serialization::serialize(ar, const_cast&>(m_pub_keys))) { ++ if (!::serialization::serialize(ar, const_cast(m_pub_keys))) { + json_result = "{\"error\":\"Failed to serialize m_pub_keys\"}"; + return json_result.c_str(); + } + -+ // FIELD(m_address_book) - std::vector + ar.tag("m_address_book"); -+ if (!::serialization::serialize(ar, const_cast&>(m_address_book))) { ++ if (!::serialization::serialize(ar, const_cast&>(m_address_book))) { + json_result = "{\"error\":\"Failed to serialize m_address_book\"}"; + return json_result.c_str(); + } + -+ // FIELD(m_scanned_pool_txs[0]) - std::unordered_set + ar.tag("m_scanned_pool_txs_0"); + if (!::serialization::serialize(ar, const_cast&>(m_scanned_pool_txs[0]))) { + json_result = "{\"error\":\"Failed to serialize m_scanned_pool_txs[0]\"}"; + return json_result.c_str(); + } + -+ // FIELD(m_scanned_pool_txs[1]) - std::unordered_set + ar.tag("m_scanned_pool_txs_1"); + if (!::serialization::serialize(ar, const_cast&>(m_scanned_pool_txs[1]))) { + json_result = "{\"error\":\"Failed to serialize m_scanned_pool_txs[1]\"}"; + return json_result.c_str(); + } + -+ // FIELD(m_subaddresses) - serializable_unordered_map + ar.tag("m_subaddresses"); -+ if (!::serialization::serialize(ar, const_cast&>(m_subaddresses))) { ++ if (!::serialization::serialize(ar, const_cast(m_subaddresses))) { + json_result = "{\"error\":\"Failed to serialize m_subaddresses\"}"; + return json_result.c_str(); + } + -+ // FIELD(m_subaddress_labels) - std::vector> - manual JSON serialization + oss << ", \n \"m_subaddress_labels\": ["; + for (size_t i = 0; i < m_subaddress_labels.size(); ++i) { + if (i > 0) oss << ", "; @@ -324,14 +305,12 @@ index 000000000..4743852ca + } + oss << "\n ]"; + -+ // FIELD(m_additional_tx_keys) - serializable_unordered_map> + ar.tag("m_additional_tx_keys"); -+ if (!::serialization::serialize(ar, const_cast>&>(m_additional_tx_keys))) { ++ if (!::serialization::serialize(ar, const_cast(m_additional_tx_keys))) { + json_result = "{\"error\":\"Failed to serialize m_additional_tx_keys\"}"; + return json_result.c_str(); + } + -+ // FIELD(m_attributes) - serializable_unordered_map - manual JSON serialization + oss << ", \n \"m_attributes\": {"; + bool first_attr = true; + for (const auto& attr : m_attributes) { @@ -345,7 +324,6 @@ index 000000000..4743852ca + } + oss << "\n }"; + -+ // FIELD(m_account_tags) - std::pair, std::vector> - manual JSON serialization + oss << ", \n \"m_account_tags\": {"; + oss << "\n \"tags_map\": {"; + bool first_tag = true; @@ -369,15 +347,12 @@ index 000000000..4743852ca + oss << "\n ]"; + oss << "\n }"; + -+ // FIELD(m_ring_history_saved) - bool -+ // ar.tag("m_ring_history_saved"); -+ // ar.serialize_blob(&m_ring_history_saved, sizeof(m_ring_history_saved)); -+ // if (!ar.good()) { -+ // json_result = "{\"error\":\"Failed to serialize m_ring_history_saved\"}"; -+ // return json_result.c_str(); -+ // } ++ ar.tag("m_ring_history_saved"); ++ if (!::serialization::serialize(ar, const_cast(m_ring_history_saved))) { ++ json_result = "{\"error\":\"Failed to serialize m_ring_history_saved\"}"; ++ return json_result.c_str(); ++ } + -+ // FIELD(m_last_block_reward) - uint64_t + ar.tag("m_last_block_reward"); + ar.serialize_int(m_last_block_reward); + if (!ar.good()) { @@ -385,14 +360,12 @@ index 000000000..4743852ca + return json_result.c_str(); + } + -+ // FIELD(m_tx_device) - serializable_unordered_map + ar.tag("m_tx_device"); -+ if (!::serialization::serialize(ar, const_cast&>(m_tx_device))) { ++ if (!::serialization::serialize(ar, const_cast(m_tx_device))) { + json_result = "{\"error\":\"Failed to serialize m_tx_device\"}"; + return json_result.c_str(); + } + -+ // FIELD(m_device_last_key_image_sync) - uint64_t + ar.tag("m_device_last_key_image_sync"); + ar.serialize_int(m_device_last_key_image_sync); + if (!ar.good()) { @@ -400,34 +373,21 @@ index 000000000..4743852ca + return json_result.c_str(); + } + -+ // FIELD(m_cold_key_images) - serializable_unordered_map + ar.tag("m_cold_key_images"); -+ if (!::serialization::serialize(ar, const_cast&>(m_cold_key_images))) { ++ if (!::serialization::serialize(ar, const_cast(m_cold_key_images))) { + json_result = "{\"error\":\"Failed to serialize m_cold_key_images\"}"; + return json_result.c_str(); + } + -+ // FIELD(m_rpc_client_secret_key) - crypto::secret_key -+ // ar.tag("m_rpc_client_secret_key"); -+ // ar.serialize_blob(&m_rpc_client_secret_key, sizeof(m_rpc_client_secret_key)); -+ // if (!ar.good()) { -+ // json_result = "{\"error\":\"Failed to serialize m_rpc_client_secret_key\"}"; -+ // return json_result.c_str(); -+ // } -+ -+ // Version-dependent fields + if (version >= 1) { -+ // FIELD(m_has_ever_refreshed_from_node) - bool -+ // ar.tag("m_has_ever_refreshed_from_node"); -+ // ar.serialize_blob(&m_has_ever_refreshed_from_node, sizeof(m_has_ever_refreshed_from_node)); -+ // if (!ar.good()) { -+ // json_result = "{\"error\":\"Failed to serialize m_has_ever_refreshed_from_node\"}"; -+ // return json_result.c_str(); -+ // } ++ ar.tag("m_has_ever_refreshed_from_node"); ++ if (!::serialization::serialize(ar, const_cast(m_has_ever_refreshed_from_node))) { ++ json_result = "{\"error\":\"Failed to serialize m_has_ever_refreshed_from_node\"}"; ++ return json_result.c_str(); ++ } + } + + if (version >= 2) { -+ // FIELD(m_background_sync_data) - background_sync_data_t + ar.tag("m_background_sync_data"); + if (!::serialization::serialize(ar, const_cast(m_background_sync_data))) { + json_result = "{\"error\":\"Failed to serialize m_background_sync_data\"}"; @@ -435,6 +395,14 @@ index 000000000..4743852ca + } + } + ++ if (version >= 3) { ++ ar.tag("m_tree_cache"); ++ if (!::serialization::serialize(ar, const_cast(m_tree_cache))) { ++ json_result = "{\"error\":\"Failed to serialize m_tree_cache\"}"; ++ return json_result.c_str(); ++ } ++ } ++ + ar.end_object(); + + if (!ar.good()) { @@ -443,8 +411,6 @@ index 000000000..4743852ca + } + + json_result = oss.str(); -+ -+ // Post-process to fix malformed JSON + post_process_json(json_result); + + return json_result.c_str(); @@ -457,7 +423,6 @@ index 000000000..4743852ca +} + +} // namespace tools -\ No newline at end of file -- -2.50.1 (Apple Git-155) +2.54.0 (Apple Git-157) diff --git a/patches/monero/0017-drop-generate_translations_header.c-requirement.patch b/patches/monero/0017-drop-generate_translations_header.c-requirement.patch index f6549dc..dff4cdf 100644 --- a/patches/monero/0017-drop-generate_translations_header.c-requirement.patch +++ b/patches/monero/0017-drop-generate_translations_header.c-requirement.patch @@ -1,7 +1,7 @@ -From 6aa368c9613a1a7b7f2f1ce1f025962d40827c67 Mon Sep 17 00:00:00 2001 +From f307b6c8f31a538e8253ac472dc1a5037227f99e Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Fri, 20 Feb 2026 08:03:01 +0100 -Subject: [PATCH 17/20] drop generate_translations_header.c requirement +Subject: [PATCH 17/22] drop generate_translations_header.c requirement --- CMakeLists.txt | 9 +---- @@ -9,10 +9,10 @@ Subject: [PATCH 17/20] drop generate_translations_header.c requirement 2 files changed, 30 insertions(+), 58 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt -index cef44dd1b..ce5e1b557 100644 +index 95326743c..ccb2c34dc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt -@@ -666,16 +666,11 @@ endfunction () +@@ -638,16 +638,11 @@ endfunction () # Generate header for embedded translations # Generate header for embedded translations, use target toolchain if depends, otherwise use the # lrelease and lupdate binaries from the host @@ -32,10 +32,10 @@ index cef44dd1b..ce5e1b557 100644 include_directories(${UNBOUND_INCLUDE_DIR}) diff --git a/translations/CMakeLists.txt b/translations/CMakeLists.txt -index 3b43360f8..d88a78ced 100644 +index 4bc579aaa..ad37b8ba7 100644 --- a/translations/CMakeLists.txt +++ b/translations/CMakeLists.txt -@@ -30,54 +30,31 @@ cmake_minimum_required(VERSION 3.5) +@@ -30,54 +30,31 @@ cmake_minimum_required(VERSION 3.10) project(translations) @@ -119,5 +119,5 @@ index 3b43360f8..d88a78ced 100644 +#endif /* TRANSLATION_FILES_H */ +") -- -2.50.1 (Apple Git-155) +2.54.0 (Apple Git-157) diff --git a/patches/monero/0018-depends-remove-icu4c-monero-project-monero-8880.patch b/patches/monero/0018-depends-remove-icu4c-monero-project-monero-8880.patch index 84b4b99..3baf893 100644 --- a/patches/monero/0018-depends-remove-icu4c-monero-project-monero-8880.patch +++ b/patches/monero/0018-depends-remove-icu4c-monero-project-monero-8880.patch @@ -1,23 +1,24 @@ -From 960a5efe59725eab4c03ce8025de8fc0fbffaacf Mon Sep 17 00:00:00 2001 +From 4fa4cfd3bab65138532ca1a69ecee00baf93de35 Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Tue, 3 Mar 2026 13:55:59 +0100 -Subject: [PATCH 18/20] depends: remove icu4c monero-project/monero#8880 +Subject: [PATCH 18/22] depends: remove icu4c monero-project/monero#8880 --- - CMakeLists.txt | 9 ++------- - 1 file changed, 2 insertions(+), 7 deletions(-) + CMakeLists.txt | 11 ++++------- + 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt -index ce5e1b557..25e034301 100644 +index ccb2c34dc..cbc67f9fd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt -@@ -1117,20 +1117,15 @@ if(MINGW) +@@ -981,19 +981,16 @@ include_directories(SYSTEM ${Boost_INCLUDE_DIRS}) + if(MINGW) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wa,-mbig-obj") set(EXTRA_LIBRARIES mswsock;ws2_32;iphlpapi;crypt32;bcrypt) - if(DEPENDS) -- set(ICU_LIBRARIES icuio icui18n icuuc icudata icutu iconv) +- if(NOT DEPENDS) ++ if(DEPENDS) + set(ICU_LIBRARIES iconv) - else() ++ else() # This is an extremely ugly hack to get around Boost not being built with static ICU. # We reported the issue, we are waiting for upstream to fix this issue: https://github.com/boostorg/boost/issues/1079#issue-3384962885 # This hack links shared ICU libs to avoid linker errors we get in MSYS2 compilation (undefined symbols to ICU). @@ -35,5 +36,5 @@ index ce5e1b557..25e034301 100644 endif() elseif(APPLE OR OPENBSD OR ANDROID) -- -2.50.1 (Apple Git-155) +2.54.0 (Apple Git-157) diff --git a/patches/monero/0019-fix-mingw-build-issues.patch b/patches/monero/0019-fix-mingw-build-issues.patch index a9e7b5c..bfbfaef 100644 --- a/patches/monero/0019-fix-mingw-build-issues.patch +++ b/patches/monero/0019-fix-mingw-build-issues.patch @@ -1,7 +1,7 @@ -From 2d4ed0b13eea96b25a574e8a87644df75c16ffb0 Mon Sep 17 00:00:00 2001 +From 1dd43161a86eb8d887773ce3bcdaaad6c55f08fa Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Wed, 4 Mar 2026 14:52:14 +0100 -Subject: [PATCH 19/20] fix: mingw build issues +Subject: [PATCH 19/22] fix: mingw build issues --- contrib/epee/include/serialization/keyvalue_serialization.h | 1 + @@ -9,7 +9,7 @@ Subject: [PATCH 19/20] fix: mingw build issues 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/contrib/epee/include/serialization/keyvalue_serialization.h b/contrib/epee/include/serialization/keyvalue_serialization.h -index fbbddc7d2..5104f284d 100644 +index 73328919c..71111c1c0 100644 --- a/contrib/epee/include/serialization/keyvalue_serialization.h +++ b/contrib/epee/include/serialization/keyvalue_serialization.h @@ -26,6 +26,7 @@ @@ -21,7 +21,7 @@ index fbbddc7d2..5104f284d 100644 #include #include diff --git a/contrib/epee/src/abstract_http_client.cpp b/contrib/epee/src/abstract_http_client.cpp -index ed4a193d9..2352c7d62 100644 +index bd9cb0dde..340a7cf85 100644 --- a/contrib/epee/src/abstract_http_client.cpp +++ b/contrib/epee/src/abstract_http_client.cpp @@ -3,6 +3,7 @@ @@ -42,5 +42,5 @@ index ed4a193d9..2352c7d62 100644 } -- -2.50.1 (Apple Git-155) +2.54.0 (Apple Git-157) diff --git a/patches/monero/0020-fix-remove-flaky-test.patch b/patches/monero/0020-fix-remove-flaky-test.patch index 1075db5..35473e6 100644 --- a/patches/monero/0020-fix-remove-flaky-test.patch +++ b/patches/monero/0020-fix-remove-flaky-test.patch @@ -1,17 +1,17 @@ -From 81eb38a5add85266cc0d2aa39ecba9d62337f4b1 Mon Sep 17 00:00:00 2001 +From eeb6b98478bf25f794928800fe9cdd8801e964f3 Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Thu, 5 Mar 2026 18:12:53 +0100 -Subject: [PATCH 20/20] fix: remove flaky test +Subject: [PATCH 20/22] fix: remove flaky test --- CMakeLists.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt -index 25e034301..19417f072 100644 +index cbc67f9fd..2377223de 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt -@@ -235,9 +235,7 @@ add_library(l3 OBJECT incorrect_source.cpp) +@@ -248,9 +248,7 @@ add_library(l3 OBJECT incorrect_source.cpp) "-DCMAKE_MODULE_LINKER_FLAGS=${CMAKE_MODULE_LINKER_FLAGS}" "-DEXPECT_SUCCESS=${EXPECT}" ) @@ -23,5 +23,5 @@ index 25e034301..19417f072 100644 endforeach() endfunction() -- -2.50.1 (Apple Git-155) +2.54.0 (Apple Git-157) diff --git a/patches/monero/0021-trezor-import-export-sign-functions.patch b/patches/monero/0021-trezor-import-export-sign-functions.patch deleted file mode 100644 index d98cdd1..0000000 --- a/patches/monero/0021-trezor-import-export-sign-functions.patch +++ /dev/null @@ -1,1015 +0,0 @@ -From 8af15fadb4095f94145cfe5086a1f6796d1cc44a Mon Sep 17 00:00:00 2001 -From: Czarek Nakamoto -Date: Wed, 13 May 2026 11:35:01 -0400 -Subject: [PATCH] trezor import/export/sign/submit functions - -1) This PR fixes protobuf detection, by removing checks as they were -failing on some platforms, essentially removing Protobuf_COMPILE_TEST_PASSED - -2) Wallet::exportTrezorTdis() returns Trezor connect-ish JSON -Exact JSON schema is shown below, path and networkType is ommited - -https://connect.trezor.io/9/methods/monero/moneroKeyImageSync/ - - -```json -{ - tdis: [ - { - out_key: '0a09ab658fca97610a38b8a5c206a0709db435341c31a9d40150df7e52440ac6', - tx_pub_key: 'da13cd8f4cc2c4f769d88b734d71cfdc0e43d01a20eb7bff6553fd67cb2ed37e', - additional_tx_pub_keys: [ - 'aabbccddaabbccddaabbccddaabbccddaabbccddaabbccddaabbccddaabbccdd', - ], - internal_output_index: 1, - sub_addr_major: 0, - sub_addr_minor: 0, - }, - ], -} -``` - -3) Wallet::importTrezorEncryptedKeyImagesJson("{...}") - -Expects Trezor connect MoneroKeyImageSyncResult JSON - -```json -{ - success: true, - payload: { - key_images: [ - { - iv: string, // 12-byte initialization vector/nonce - key_image: string, // Encrypted blob - }, - // ... more key images - ], - signature: string, // 32-byte encryption key - } -} -``` - - -4) PendingTransaction::commitTrezor(0) -New method that returns std::string with Trezor connect compatible JSON, as -shown below and at the link - -https://connect.trezor.io/9/methods/monero/moneroSignTransaction/ -```json -{ - // REQUIRED: Hardened Monero account path (minimum 3 components) - path: "m/44'/128'/0'", - - // REQUIRED: Network type - networkType: 0, // 0=MAINNET, 1=TESTNET, 2=STAGENET, 3=FAKECHAIN - - // REQUIRED: Transaction data with outputs - tsx_data: { - version: 1, - unlock_time: 0, - mixin: 15, - fee: 10000000, - account: 0, - num_inputs: 1, - client_version: 3, - hard_fork: 16, - outputs: [ - { - amount: 1000000000000, // 1.0 XMR in atomic units - addr: { - spend_public_key: - 'abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890', - view_public_key: - 'fedcba0987654321fedcba0987654321fedcba0987654321fedcba0987654321', - }, - is_subaddress: false, - original: 'address_string_here', - is_integrated: false, - }, - ], - rsig_data: { - rsig_type: 1, - bp_version: 4, - grouping: [], - }, - }, - - // REQUIRED: At least one input with ring members - inputs: [ - { - outputs: [ - // Ring member 1 (real output) - { - idx: 12345, - key: { - dest: '1111111111111111111111111111111111111111111111111111111111111111', - commitment: - '2222222222222222222222222222222222222222222222222222222222222222', - }, - }, - // Ring member 2 (decoy) - { - idx: 12346, - key: { - dest: '3333333333333333333333333333333333333333333333333333333333333333', - commitment: - '4444444444444444444444444444444444444444444444444444444444444444', - }, - }, - // ... add more ring members (typically 11 total for mixin=10) - ], - real_output: 0, - real_out_tx_key: 'tx_public_key_64_chars', - real_out_additional_tx_keys: [], - real_output_in_tx_index: 1, - amount: 1010000000, - rct: true, - mask: 'amount_mask_64_chars', - subaddr_minor: 0, - }, - ], -} -``` - -5) Transaction assembly is left as an excercises for the reader, after hex -is assembled you can call Wallet::submitTransactionHex("CAFEBABE") - -That's it. - ---- - cmake/CheckTrezor.cmake | 28 +-- - src/device_trezor/trezor/protocol.cpp | 220 ++++++++++++++++++++++ - src/device_trezor/trezor/protocol.hpp | 8 + - src/wallet/api/pending_transaction.cpp | 60 ++++++ - src/wallet/api/pending_transaction.h | 1 + - src/wallet/api/unsigned_transaction.cpp | 2 + - src/wallet/api/wallet.cpp | 70 ++++++- - src/wallet/api/wallet.h | 9 +- - src/wallet/api/wallet2_api.h | 71 ++++--- - src/wallet/wallet2.cpp | 234 ++++++++++++++++++++++++ - src/wallet/wallet2.h | 7 +- - 11 files changed, 650 insertions(+), 60 deletions(-) - -diff --git a/cmake/CheckTrezor.cmake b/cmake/CheckTrezor.cmake -index 4fae15fad..57e3d4866 100644 ---- a/cmake/CheckTrezor.cmake -+++ b/cmake/CheckTrezor.cmake -@@ -40,9 +40,6 @@ if (USE_DEVICE_TREZOR) - # Protobuf handling the cache variables set in docker. - if(NOT Protobuf_FOUND AND NOT Protobuf_LIBRARY AND NOT Protobuf_PROTOC_EXECUTABLE AND NOT Protobuf_INCLUDE_DIR) - message(STATUS "Could not find Protobuf") -- elseif(NOT Protobuf_LIBRARY OR NOT EXISTS "${Protobuf_LIBRARY}") -- message(STATUS "Protobuf library not found: ${Protobuf_LIBRARY}") -- unset(Protobuf_FOUND) - elseif(NOT Protobuf_PROTOC_EXECUTABLE OR NOT EXISTS "${Protobuf_PROTOC_EXECUTABLE}") - message(STATUS "Protobuf executable not found: ${Protobuf_PROTOC_EXECUTABLE}") - unset(Protobuf_FOUND) -@@ -93,31 +90,8 @@ if(Protobuf_FOUND AND USE_DEVICE_TREZOR) - endif() - endif() - --# Protobuf compilation test --if(Protobuf_FOUND AND USE_DEVICE_TREZOR AND TREZOR_PYTHON) -- execute_process(COMMAND ${Protobuf_PROTOC_EXECUTABLE} -I "${CMAKE_CURRENT_LIST_DIR}" -I "${Protobuf_INCLUDE_DIR}" "${CMAKE_CURRENT_LIST_DIR}/test-protobuf.proto" --cpp_out ${CMAKE_BINARY_DIR} RESULT_VARIABLE RET OUTPUT_VARIABLE OUT ERROR_VARIABLE ERR) -- if(RET) -- message(STATUS "Protobuf test generation failed: ${OUT} ${ERR}") -- endif() -- -- try_compile(Protobuf_COMPILE_TEST_PASSED -- "${CMAKE_BINARY_DIR}" -- SOURCES -- "${CMAKE_BINARY_DIR}/test-protobuf.pb.cc" -- "${CMAKE_CURRENT_LIST_DIR}/test-protobuf.cpp" -- CMAKE_FLAGS -- "-DINCLUDE_DIRECTORIES=${Protobuf_INCLUDE_DIR};${CMAKE_BINARY_DIR}" -- "-DCMAKE_CXX_STANDARD=11" -- LINK_LIBRARIES ${Protobuf_LIBRARY} -- OUTPUT_VARIABLE OUTPUT -- ) -- if(NOT Protobuf_COMPILE_TEST_PASSED) -- message(STATUS "Protobuf Compilation test failed: ${OUTPUT}.") -- endif() --endif() -- - # Try to build protobuf messages --if(Protobuf_FOUND AND USE_DEVICE_TREZOR AND TREZOR_PYTHON AND Protobuf_COMPILE_TEST_PASSED) -+if(Protobuf_FOUND AND USE_DEVICE_TREZOR AND TREZOR_PYTHON) - set(ENV{PROTOBUF_INCLUDE_DIRS} "${Protobuf_INCLUDE_DIR}") - set(ENV{PROTOBUF_PROTOC_EXECUTABLE} "${Protobuf_PROTOC_EXECUTABLE}") - set(TREZOR_PROTOBUF_PARAMS "") -diff --git a/src/device_trezor/trezor/protocol.cpp b/src/device_trezor/trezor/protocol.cpp -index 0e59a16ba..afd1498d2 100644 ---- a/src/device_trezor/trezor/protocol.cpp -+++ b/src/device_trezor/trezor/protocol.cpp -@@ -29,12 +29,17 @@ - - #include "version.h" - #include "protocol.hpp" -+#include "string_tools.h" - #include - #include -+#include - #include - #include - #include - #include -+#include -+#include -+#include - #include - #include - #include -@@ -450,6 +455,10 @@ namespace tx { - } - } - -+ void Signer::export_source_entry(MoneroTransactionSourceEntry *dst, size_t idx, bool need_ring_keys, bool need_ring_indices){ -+ set_tx_input(dst, idx, need_ring_keys, need_ring_indices); -+ } -+ - void Signer::set_tx_input(MoneroTransactionSourceEntry * dst, size_t idx, bool need_ring_keys, bool need_ring_indices){ - const cryptonote::tx_source_entry & src = cur_tx().sources[idx]; - const tools::wallet2::transfer_details & transfer = get_source_transfer(idx); -@@ -1096,6 +1105,217 @@ namespace tx { - memwipe(plaintext.get(), keys_len); - } - -+ namespace { -+ -+ std::string bin_to_hex_lower(const std::string &bin) -+ { -+ return epee::string_tools::buff_to_hex_nodelimer(bin); -+ } -+ -+ rapidjson::Value dest_entry_to_json(const messages::monero::MoneroTransactionDestinationEntry &e, rapidjson::Document::AllocatorType &a) -+ { -+ rapidjson::Value o(rapidjson::kObjectType); -+ if (e.has_amount()) -+ o.AddMember("amount", e.amount(), a); -+ if (e.has_addr()) -+ { -+ rapidjson::Value addr(rapidjson::kObjectType); -+ const auto &ad = e.addr(); -+ if (ad.has_spend_public_key()) -+ { -+ const std::string h = bin_to_hex_lower(ad.spend_public_key()); -+ addr.AddMember("spend_public_key", rapidjson::Value(h.c_str(), static_cast(h.size()), a), a); -+ } -+ if (ad.has_view_public_key()) -+ { -+ const std::string h = bin_to_hex_lower(ad.view_public_key()); -+ addr.AddMember("view_public_key", rapidjson::Value(h.c_str(), static_cast(h.size()), a), a); -+ } -+ o.AddMember("addr", addr, a); -+ } -+ if (e.has_is_subaddress()) -+ o.AddMember("is_subaddress", e.is_subaddress(), a); -+ if (e.has_original()) -+ o.AddMember("original", rapidjson::Value(e.original().c_str(), static_cast(e.original().size()), a), a); -+ if (e.has_is_integrated()) -+ o.AddMember("is_integrated", e.is_integrated(), a); -+ return o; -+ } -+ -+ rapidjson::Value source_entry_to_json(const messages::monero::MoneroTransactionSourceEntry &e, rapidjson::Document::AllocatorType &a) -+ { -+ rapidjson::Value o(rapidjson::kObjectType); -+ rapidjson::Value ring(rapidjson::kArrayType); -+ for (int i = 0; i < e.outputs_size(); ++i) -+ { -+ const auto &out = e.outputs(i); -+ rapidjson::Value ring_m(rapidjson::kObjectType); -+ if (out.has_idx()) -+ ring_m.AddMember("idx", out.idx(), a); -+ if (out.has_key()) -+ { -+ rapidjson::Value key(rapidjson::kObjectType); -+ const auto &k = out.key(); -+ if (k.has_dest()) -+ { -+ const std::string h = bin_to_hex_lower(k.dest()); -+ key.AddMember("dest", rapidjson::Value(h.c_str(), static_cast(h.size()), a), a); -+ } -+ if (k.has_commitment()) -+ { -+ const std::string h = bin_to_hex_lower(k.commitment()); -+ key.AddMember("commitment", rapidjson::Value(h.c_str(), static_cast(h.size()), a), a); -+ } -+ ring_m.AddMember("key", key, a); -+ } -+ ring.PushBack(ring_m, a); -+ } -+ o.AddMember("outputs", ring, a); -+ if (e.has_real_output()) -+ o.AddMember("real_output", e.real_output(), a); -+ if (e.has_real_out_tx_key()) -+ { -+ const std::string h = bin_to_hex_lower(e.real_out_tx_key()); -+ o.AddMember("real_out_tx_key", rapidjson::Value(h.c_str(), static_cast(h.size()), a), a); -+ } -+ rapidjson::Value add_keys(rapidjson::kArrayType); -+ for (int i = 0; i < e.real_out_additional_tx_keys_size(); ++i) -+ { -+ const std::string h = bin_to_hex_lower(e.real_out_additional_tx_keys(i)); -+ add_keys.PushBack(rapidjson::Value(h.c_str(), static_cast(h.size()), a), a); -+ } -+ o.AddMember("real_out_additional_tx_keys", add_keys, a); -+ if (e.has_real_output_in_tx_index()) -+ o.AddMember("real_output_in_tx_index", e.real_output_in_tx_index(), a); -+ if (e.has_amount()) -+ o.AddMember("amount", e.amount(), a); -+ if (e.has_rct()) -+ o.AddMember("rct", e.rct(), a); -+ if (e.has_mask()) -+ { -+ const std::string h = bin_to_hex_lower(e.mask()); -+ o.AddMember("mask", rapidjson::Value(h.c_str(), static_cast(h.size()), a), a); -+ } -+ if (e.has_subaddr_minor()) -+ o.AddMember("subaddr_minor", e.subaddr_minor(), a); -+ return o; -+ } -+ -+ std::string monero_default_bip44_path(uint32_t subaddr_account) -+ { -+ std::ostringstream oss; -+ oss << "m/44'/128'/" << subaddr_account << "'"; -+ return oss.str(); -+ } -+ -+ } // namespace -+ -+ -+ std::string trezor_connect_monero_sign_transaction_to_json( -+ wallet_shim *wallet, -+ const unsigned_tx_set *utx, -+ size_t tx_idx, -+ hw::tx_aux_data *aux_data, -+ cryptonote::network_type network_type) -+ { -+ CHECK_AND_ASSERT_THROW_MES(utx && aux_data, "null argument"); -+ CHECK_AND_ASSERT_THROW_MES(std::get<0>(utx->transfers) == 0, "Unsupported non zero offset"); -+ CHECK_AND_ASSERT_THROW_MES(tx_idx < utx->txes.size(), "Invalid transaction index"); -+ -+ Signer signer(wallet, utx, tx_idx, aux_data); -+ auto init_req = signer.step_init(); -+ const auto &tsx = init_req->tsx_data(); -+ -+ rapidjson::Document doc; -+ doc.SetObject(); -+ auto &alloc = doc.GetAllocator(); -+ -+ const std::string path_str = monero_default_bip44_path(utx->txes[tx_idx].subaddr_account); -+ doc.AddMember("path", rapidjson::Value(path_str.c_str(), static_cast(path_str.size()), alloc), alloc); -+ doc.AddMember("networkType", static_cast(network_type), alloc); -+ -+ rapidjson::Value tsx_data(rapidjson::kObjectType); -+ tsx_data.AddMember("version", tsx.version(), alloc); -+ if (tsx.has_payment_id() && !tsx.payment_id().empty()) -+ { -+ const std::string h = bin_to_hex_lower(tsx.payment_id()); -+ tsx_data.AddMember("payment_id", rapidjson::Value(h.c_str(), static_cast(h.size()), alloc), alloc); -+ } -+ tsx_data.AddMember("unlock_time", static_cast(tsx.unlock_time()), alloc); -+ -+ rapidjson::Value outputs(rapidjson::kArrayType); -+ for (int i = 0; i < tsx.outputs_size(); ++i) -+ outputs.PushBack(dest_entry_to_json(tsx.outputs(i), alloc), alloc); -+ tsx_data.AddMember("outputs", outputs, alloc); -+ -+ if (tsx.has_change_dts()) -+ tsx_data.AddMember("change_dts", dest_entry_to_json(tsx.change_dts(), alloc), alloc); -+ -+ if (tsx.has_num_inputs()) -+ tsx_data.AddMember("num_inputs", tsx.num_inputs(), alloc); -+ if (tsx.has_mixin()) -+ tsx_data.AddMember("mixin", tsx.mixin(), alloc); -+ if (tsx.has_fee()) -+ tsx_data.AddMember("fee", tsx.fee(), alloc); -+ if (tsx.has_account()) -+ tsx_data.AddMember("account", tsx.account(), alloc); -+ -+ if (tsx.minor_indices_size() > 0) -+ { -+ rapidjson::Value minors(rapidjson::kArrayType); -+ for (int i = 0; i < tsx.minor_indices_size(); ++i) -+ minors.PushBack(tsx.minor_indices(i), alloc); -+ tsx_data.AddMember("minor_indices", minors, alloc); -+ } -+ -+ if (tsx.has_rsig_data()) -+ { -+ const auto &rd = tsx.rsig_data(); -+ rapidjson::Value rj(rapidjson::kObjectType); -+ if (rd.has_rsig_type()) -+ rj.AddMember("rsig_type", rd.rsig_type(), alloc); -+ if (rd.has_bp_version()) -+ rj.AddMember("bp_version", rd.bp_version(), alloc); -+ rapidjson::Value grp(rapidjson::kArrayType); -+ for (int i = 0; i < rd.grouping_size(); ++i) -+ grp.PushBack(static_cast(rd.grouping(i)), alloc); -+ rj.AddMember("grouping", grp, alloc); -+ tsx_data.AddMember("rsig_data", rj, alloc); -+ } -+ -+ if (tsx.integrated_indices_size() > 0) -+ { -+ rapidjson::Value ii(rapidjson::kArrayType); -+ for (int i = 0; i < tsx.integrated_indices_size(); ++i) -+ ii.PushBack(tsx.integrated_indices(i), alloc); -+ tsx_data.AddMember("integrated_indices", ii, alloc); -+ } -+ -+ if (tsx.has_client_version()) -+ tsx_data.AddMember("client_version", tsx.client_version(), alloc); -+ if (tsx.has_hard_fork()) -+ tsx_data.AddMember("hard_fork", tsx.hard_fork(), alloc); -+ if (tsx.has_monero_version()) -+ tsx_data.AddMember("monero_version", rapidjson::Value(tsx.monero_version().c_str(), static_cast(tsx.monero_version().size()), alloc), alloc); -+ -+ doc.AddMember("tsx_data", tsx_data, alloc); -+ -+ rapidjson::Value inputs(rapidjson::kArrayType); -+ const size_t n_in = utx->txes[tx_idx].sources.size(); -+ for (size_t i = 0; i < n_in; ++i) -+ { -+ messages::monero::MoneroTransactionSourceEntry src_pb; -+ signer.export_source_entry(&src_pb, i, true, true); -+ inputs.PushBack(source_entry_to_json(src_pb, alloc), alloc); -+ } -+ doc.AddMember("inputs", inputs, alloc); -+ -+ rapidjson::StringBuffer buffer; -+ rapidjson::Writer writer(buffer); -+ doc.Accept(writer); -+ return std::string(buffer.GetString(), buffer.GetSize()); -+ } -+ - } - } - } -diff --git a/src/device_trezor/trezor/protocol.hpp b/src/device_trezor/trezor/protocol.hpp -index 7ffadd9aa..7899e60d0 100644 ---- a/src/device_trezor/trezor/protocol.hpp -+++ b/src/device_trezor/trezor/protocol.hpp -@@ -340,8 +340,16 @@ namespace tx { - const TData & tdata() const { - return m_ct; - } -+ void export_source_entry(MoneroTransactionSourceEntry *dst, size_t idx, bool need_ring_keys, bool need_ring_indices); - }; - -+ std::string trezor_connect_monero_sign_transaction_to_json( -+ wallet_shim *wallet, -+ const unsigned_tx_set *utx, -+ size_t tx_idx, -+ hw::tx_aux_data *aux_data, -+ cryptonote::network_type network_type); -+ - // TX Key decryption - void load_tx_key_data(hw::device_cold::tx_key_data_t & res, const std::string & data); - -diff --git a/src/wallet/api/pending_transaction.cpp b/src/wallet/api/pending_transaction.cpp -index 1f714d229..22c09483d 100644 ---- a/src/wallet/api/pending_transaction.cpp -+++ b/src/wallet/api/pending_transaction.cpp -@@ -44,6 +44,11 @@ - - #include "bc-ur/src/bc-ur.hpp" - -+#if defined(DEVICE_TREZOR_READY) && DEVICE_TREZOR_READY -+#include "device/device_cold.hpp" -+#include "device_trezor/trezor/protocol.hpp" -+#endif -+ - using namespace std; - - namespace Monero { -@@ -210,6 +215,61 @@ std::string PendingTransactionImpl::commitUR(int max_fragment_length) { - } - } - -+std::string PendingTransactionImpl::commitTrezor(uint64_t tx_index) -+{ -+#if !defined(DEVICE_TREZOR_READY) || !DEVICE_TREZOR_READY -+ (void)tx_index; -+ m_errorString = tr("This build was compiled without Trezor support"); -+ m_status = Status_Error; -+ return ""; -+#else -+ if (tx_index >= m_pending_tx.size()) -+ { -+ m_errorString = tr("Invalid transaction index"); -+ m_status = Status_Error; -+ return ""; -+ } -+ try -+ { -+ tools::wallet2 *w = m_wallet.m_wallet.get(); -+ tools::wallet2::unsigned_tx_set utx; -+ w->construct_unsigned_tx_set_for_signing(m_pending_tx, utx); -+ if (std::get<0>(utx.transfers) != 0) -+ { -+ m_errorString = tr("Unsupported unsigned transaction transfer offset"); -+ m_status = Status_Error; -+ return ""; -+ } -+ hw::tx_aux_data aux_data; -+ const int bpv = w->use_fork_rules(HF_VERSION_BULLETPROOF_PLUS, -10) ? 4 -+ : (w->use_fork_rules(HF_VERSION_CLSAG, -10) ? 3 -+ : (w->use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1)); -+ aux_data.bp_version = bpv; -+ aux_data.hard_fork = w->get_current_hard_fork(); -+ aux_data.client_version = static_cast(bpv >= 4 ? 4u : 3u); -+ -+ hw::wallet_shim shim; -+ shim.get_tx_pub_key_from_received_outs = std::bind(&tools::wallet2::get_tx_pub_key_from_received_outs, w, std::placeholders::_1); -+ -+ const std::string json = hw::trezor::protocol::tx::trezor_connect_monero_sign_transaction_to_json( -+ &shim, -+ &utx, -+ static_cast(tx_index), -+ &aux_data, -+ w->nettype()); -+ m_errorString.clear(); -+ m_status = Status_Ok; -+ return json; -+ } -+ catch (const std::exception &e) -+ { -+ m_errorString = e.what(); -+ m_status = Status_Error; -+ return ""; -+ } -+#endif -+} -+ - - uint64_t PendingTransactionImpl::amount() const - { -diff --git a/src/wallet/api/pending_transaction.h b/src/wallet/api/pending_transaction.h -index 0cc6c58e9..9c9097cd1 100644 ---- a/src/wallet/api/pending_transaction.h -+++ b/src/wallet/api/pending_transaction.h -@@ -47,6 +47,7 @@ public: - std::string errorString() const override; - bool commit(const std::string &filename = "", bool overwrite = false) override; - std::string commitUR(int max_fragment_length = 130) override; -+ std::string commitTrezor(uint64_t tx_index = 0) override; - uint64_t amount() const override; - 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 fd03e959d..7232e518f 100644 ---- a/src/wallet/api/unsigned_transaction.cpp -+++ b/src/wallet/api/unsigned_transaction.cpp -@@ -34,11 +34,13 @@ - - #include "cryptonote_basic/cryptonote_format_utils.h" - #include "cryptonote_basic/cryptonote_basic_impl.h" -+#include "cryptonote_config.h" - - #include - #include - #include - #include -+#include - - #include "bc-ur/src/bc-ur.hpp" - -diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp -index c24b4a97d..3a977d6d2 100644 ---- a/src/wallet/api/wallet.cpp -+++ b/src/wallet/api/wallet.cpp -@@ -1457,6 +1457,45 @@ bool WalletImpl::submitTransactionUR(const string &input) { - return true; - } - -+bool WalletImpl::submitTransactionHex(const string &hex) { -+ clearStatus(); -+ if (checkBackgroundSync("cannot submit tx")) -+ return false; -+ -+ pauseRefresh(); -+ try { -+ m_wallet->relay_raw_tx(hex); -+ } catch (const tools::error::daemon_busy&) { -+ setStatusError(tr("daemon is busy. Please try again later.")); -+ startRefresh(); -+ return false; -+ } catch (const tools::error::no_connection_to_daemon&) { -+ setStatusError(tr("no connection to daemon.")); -+ startRefresh(); -+ return false; -+ } catch (const tools::error::tx_rejected& e) { -+ std::ostringstream writer; -+ writer << (boost::format(tr("transaction %s was rejected by daemon with status: ")) % get_transaction_hash(e.tx())) << e.status(); -+ if (!e.reason().empty()) -+ writer << tr(". Reason: ") << e.reason(); -+ setStatusError(writer.str()); -+ startRefresh(); -+ return false; -+ } catch (const std::exception &e) { -+ setStatusError(string(tr("Unknown exception: ")) + e.what()); -+ startRefresh(); -+ return false; -+ } catch (...) { -+ setStatusError(tr("Unhandled exception")); -+ startRefresh(); -+ return false; -+ } -+ startRefresh(); -+ if (m_history) -+ m_history->refresh(); -+ return true; -+} -+ - - bool WalletImpl::hasUnknownKeyImages() const - { -@@ -3479,4 +3518,33 @@ std::string WalletImpl::serializeCacheToJson() const - return std::string(m_wallet->serialize_cache_to_json()); - } - -+std::string WalletImpl::exportTrezorTdis() const -+{ -+ return m_wallet->export_trezor_tdis(); -+} -+ -+bool WalletImpl::importTrezorEncryptedKeyImagesJson(const string &json) -+{ -+ if (checkBackgroundSync("cannot import key images")) -+ return false; -+ if (!trustedDaemon()) { -+ setStatusError(tr("Key images can only be imported with a trusted daemon")); -+ return false; -+ } -+ try -+ { -+ uint64_t spent = 0, unspent = 0; -+ uint64_t height = m_wallet->import_trezor_encrypted_key_images_json(json, spent, unspent); -+ LOG_PRINT_L2("Trezor encrypted key images imported to height " << height << ", " -+ << print_money(spent) << " spent, " << print_money(unspent) << " unspent"); -+ } -+ catch (const std::exception &e) -+ { -+ LOG_ERROR("Error importing Trezor encrypted key images: " << e.what()); -+ setStatusError(string(tr("Failed to import key images: ")) + e.what()); -+ return false; -+ } -+ return true; -+} -+ - } // namespace -diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h -index 98c03b9c1..45a3b6b2f 100644 ---- a/src/wallet/api/wallet.h -+++ b/src/wallet/api/wallet.h -@@ -183,6 +183,7 @@ public: - virtual PendingTransaction * createSweepUnmixableTransaction() override; - bool submitTransaction(const std::string &fileName) override; - bool submitTransactionUR(const std::string &input) override; -+ bool submitTransactionHex(const std::string &hex) override; - virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename) override; - virtual UnsignedTransaction * loadUnsignedTxUR(const std::string &input) override; - bool hasUnknownKeyImages() const override; -@@ -337,6 +338,8 @@ private: - bool getWaitsForDeviceReceive(); - - virtual std::string serializeCacheToJson() const override; -+ virtual std::string exportTrezorTdis() const override; -+ bool importTrezorEncryptedKeyImagesJson(const std::string &json) override; - }; - - -diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h -index 3d11929f9..ac1dd9fb0 100644 ---- a/src/wallet/api/wallet2_api.h -+++ b/src/wallet/api/wallet2_api.h -@@ -92,6 +92,7 @@ struct PendingTransaction - // commit transaction or save to file if filename is provided. - virtual bool commit(const std::string &filename = "", bool overwrite = false) = 0; - virtual std::string commitUR(int max_fragment_length = 130) = 0; -+ virtual std::string commitTrezor(uint64_t tx_index = 0) = 0; - virtual uint64_t amount() const = 0; - virtual uint64_t dust() const = 0; - virtual uint64_t fee() const = 0; -@@ -948,6 +949,7 @@ struct Wallet - */ - virtual bool submitTransaction(const std::string &fileName) = 0; - virtual bool submitTransactionUR(const std::string &input) = 0; -+ virtual bool submitTransactionHex(const std::string &input) = 0; - - - /*! -@@ -1220,6 +1222,19 @@ struct Wallet - - //! serialize wallet cache to JSON - virtual std::string serializeCacheToJson() const = 0; -+ -+ -+ /*! -+ * \brief exportTrezorTdis — export transfer details for Trezor cold key-image sync as JSON -+ * -+ * Returns an object `{ "tdis": [ ... ] }` with hex pubkeys and indices per output. -+ */ -+ virtual std::string exportTrezorTdis() const = 0; -+ -+ /*! -+ * \brief importTrezorEncryptedKeyImagesJson — import key images from Trezor-style encrypted JSON -+ */ -+ virtual bool importTrezorEncryptedKeyImagesJson(const std::string &json) = 0; - }; - - /** -diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp -index a7532d7ec..0c3a628ed 100644 ---- a/src/wallet/wallet2.cpp -+++ b/src/wallet/wallet2.cpp -@@ -98,6 +98,7 @@ extern "C" - { - #include "crypto/keccak.h" - #include "crypto/crypto-ops.h" -+#include - } - using namespace std; - using namespace crypto; -@@ -7799,6 +7800,81 @@ void wallet2::commit_tx(std::vector& ptx_vector) - } - } - //---------------------------------------------------------------------------------------------------- -+void wallet2::relay_raw_tx(const std::string &tx_as_hex) -+{ -+ using namespace cryptonote; -+ -+ cryptonote::blobdata tx_blob; -+ THROW_WALLET_EXCEPTION_IF(!epee::string_tools::parse_hexstr_to_binbuff(tx_as_hex, tx_blob), error::wallet_internal_error, "Failed to parse hex"); -+ -+ transaction tx; -+ THROW_WALLET_EXCEPTION_IF(!parse_and_validate_tx_from_blob(tx_blob, tx), error::wallet_internal_error, "Failed to parse transaction"); -+ -+ if (m_light_wallet) -+ { -+ COMMAND_RPC_SUBMIT_RAW_TX::request oreq; -+ COMMAND_RPC_SUBMIT_RAW_TX::response ores; -+ oreq.address = get_account().get_public_address_str(m_nettype); -+ oreq.view_key = string_tools::pod_to_hex(unwrap(unwrap(get_account().get_keys().m_view_secret_key))); -+ oreq.tx = tx_as_hex; -+ { -+ const boost::lock_guard lock{m_daemon_rpc_mutex}; -+ bool r = epee::net_utils::invoke_http_json("/submit_raw_tx", oreq, ores, *m_http_client, rpc_timeout, "POST"); -+ THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "submit_raw_tx"); -+ THROW_WALLET_EXCEPTION_IF(ores.status != "OK" && ores.status != "success", error::tx_rejected, tx, get_rpc_status(ores.status), ores.error); -+ } -+ } -+ else -+ { -+ COMMAND_RPC_SEND_RAW_TX::request req; -+ req.tx_as_hex = tx_as_hex; -+ req.do_not_relay = false; -+ req.do_sanity_checks = true; -+ COMMAND_RPC_SEND_RAW_TX::response daemon_send_resp; -+ -+ { -+ const boost::lock_guard lock{m_daemon_rpc_mutex}; -+ uint64_t pre_call_credits = m_rpc_payment_state.credits; -+ req.client = get_client_signature(); -+ bool r = epee::net_utils::invoke_http_json("/sendrawtransaction", req, daemon_send_resp, *m_http_client, rpc_timeout); -+ THROW_ON_RPC_RESPONSE_ERROR(r, {}, daemon_send_resp, "sendrawtransaction", error::tx_rejected, tx, get_rpc_status(daemon_send_resp.status), get_text_reason(daemon_send_resp)); -+ check_rpc_cost("/sendrawtransaction", daemon_send_resp.credits, pre_call_credits, COST_PER_TX_RELAY); -+ } -+ } -+ -+ crypto::hash txid = get_transaction_hash(tx); -+ uint64_t tx_money_spent_in_ins = 0; -+ auto subaddr_account = []()->boost::optional { return boost::none; }(); -+ std::set subaddr_indices; -+ for (const auto &in : tx.vin) -+ { -+ if (in.type() != typeid(cryptonote::txin_to_key)) -+ continue; -+ const cryptonote::txin_to_key &in_to_key = boost::get(in); -+ const auto kit = m_key_images.find(in_to_key.k_image); -+ if (kit == m_key_images.end()) -+ continue; -+ const transfer_details &td = m_transfers[kit->second]; -+ const uint64_t amount = in_to_key.amount > 0 ? in_to_key.amount : td.amount(); -+ tx_money_spent_in_ins += amount; -+ subaddr_account = td.m_subaddr_index.major; -+ subaddr_indices.insert(td.m_subaddr_index.minor); -+ set_spent(kit->second, 0); -+ } -+ -+ if (tx_money_spent_in_ins > 0 && store_tx_info() && !m_unconfirmed_txs.count(txid)) -+ { -+ THROW_WALLET_EXCEPTION_IF(!subaddr_account, error::wallet_internal_error, -+ "Relayed tx spends our outputs but subaddress account is unknown"); -+ add_unconfirmed_tx(txid, tx, tx_money_spent_in_ins, {}, crypto::null_hash, 0, *subaddr_account, subaddr_indices); -+ auto utx_it = m_unconfirmed_txs.find(txid); -+ THROW_WALLET_EXCEPTION_IF(utx_it == m_unconfirmed_txs.end(), error::wallet_internal_error, -+ "unconfirmed tx wasn't found: " + string_tools::pod_to_hex(txid)); -+ utx_it->second.m_amount_out = get_outgoing_amount(tx, tx_money_spent_in_ins); -+ LOG_PRINT_L1("Raw transaction relayed. <" << txid << ">"); -+ } -+} -+//---------------------------------------------------------------------------------------------------- - bool wallet2::save_tx(const std::vector& ptx_vector, const std::string &filename) const - { - LOG_PRINT_L0("saving " << ptx_vector.size() << " transactions"); -@@ -7838,6 +7915,20 @@ std::string wallet2::dump_tx_to_str(const std::vector &ptx_vector) c - return std::string(UNSIGNED_TX_PREFIX) + ciphertext; - } - //---------------------------------------------------------------------------------------------------- -+void wallet2::construct_unsigned_tx_set_for_signing(const std::vector& ptx_vector, unsigned_tx_set &utx) const -+{ -+ utx.txes.clear(); -+ utx.txes.reserve(ptx_vector.size()); -+ for (const auto &tx : ptx_vector) -+ utx.txes.push_back(get_construction_data_with_decrypted_short_payment_id(tx, m_account.get_device())); -+ -+ utx.new_transfers = std::make_tuple(static_cast(0), static_cast(0), std::vector()); -+ -+ transfer_container transfers_copy; -+ get_transfers(transfers_copy); -+ utx.transfers = std::make_tuple(static_cast(0), static_cast(transfers_copy.size()), std::move(transfers_copy)); -+} -+//---------------------------------------------------------------------------------------------------- - bool wallet2::load_unsigned_tx(const std::string &unsigned_filename, unsigned_tx_set &exported_txs) const - { - std::string s; -@@ -16384,4 +16475,147 @@ std::pair wallet2::estimate_tx_size_and_weight(bool use_rct, i - return std::make_pair(size, weight); - } - //---------------------------------------------------------------------------------------------------- -+// Returns JSON: { "tdis": [ { "out_key", "tx_pub_key", optional "additional_tx_pub_keys", indices }, ... ] } -+// Hex fields are 64-char lower-case pubkeys; additional_tx_pub_keys matches protocol::ki::key_image_data -+// (subset of txn additional pubkeys for this transfer — currently 0 or 1 entry). -+std::string wallet2::export_trezor_tdis() const -+{ -+ rapidjson::Document doc; -+ doc.SetObject(); -+ auto &alloc = doc.GetAllocator(); -+ -+ rapidjson::Value tdis(rapidjson::kArrayType); -+ tdis.Reserve(static_cast(m_transfers.size()), alloc); -+ -+ for (const auto &td : m_transfers) -+ { -+ const crypto::public_key out_key = td.get_public_key(); -+ const crypto::public_key tx_pub_key = get_tx_pub_key_from_received_outs(td); -+ const std::vector additional_tx_pub_keys = cryptonote::get_additional_tx_pub_keys_from_extra(td.m_tx); -+ -+ std::vector additional_for_tdi; -+ if (!additional_tx_pub_keys.empty() && additional_tx_pub_keys.size() > td.m_internal_output_index) -+ additional_for_tdi.push_back(additional_tx_pub_keys[td.m_internal_output_index]); -+ -+ rapidjson::Value obj(rapidjson::kObjectType); -+ const std::string out_hex = epee::string_tools::pod_to_hex(out_key); -+ const std::string tx_pub_hex = epee::string_tools::pod_to_hex(tx_pub_key); -+ obj.AddMember("out_key", rapidjson::Value(out_hex.c_str(), static_cast(out_hex.size()), alloc), alloc); -+ obj.AddMember("tx_pub_key", rapidjson::Value(tx_pub_hex.c_str(), static_cast(tx_pub_hex.size()), alloc), alloc); -+ if (!additional_for_tdi.empty()) -+ { -+ rapidjson::Value aux(rapidjson::kArrayType); -+ aux.Reserve(static_cast(additional_for_tdi.size()), alloc); -+ for (const auto &apk : additional_for_tdi) -+ { -+ const std::string ah = epee::string_tools::pod_to_hex(apk); -+ aux.PushBack(rapidjson::Value(ah.c_str(), static_cast(ah.size()), alloc), alloc); -+ } -+ obj.AddMember("additional_tx_pub_keys", aux, alloc); -+ } -+ obj.AddMember("internal_output_index", rapidjson::Value(static_cast(td.m_internal_output_index)), alloc); -+ obj.AddMember("sub_addr_major", rapidjson::Value(td.m_subaddr_index.major), alloc); -+ obj.AddMember("sub_addr_minor", rapidjson::Value(td.m_subaddr_index.minor), alloc); -+ tdis.PushBack(obj, alloc); -+ } -+ -+ doc.AddMember("tdis", tdis, alloc); -+ -+ rapidjson::StringBuffer buffer; -+ rapidjson::Writer writer(buffer); -+ doc.Accept(writer); -+ return std::string(buffer.GetString(), buffer.GetSize()); -+} -+//---------------------------------------------------------------------------------------------------- -+namespace -+{ -+ static void decrypt_trezor_exported_ki_blob(const std::string &cipher, const std::string &iv, const std::string &key32, crypto::key_image &ki, crypto::signature &sig) -+ { -+ THROW_WALLET_EXCEPTION_IF(iv.size() != crypto_aead_chacha20poly1305_ietf_NPUBBYTES, -+ error::wallet_internal_error, "Trezor KI JSON: IV must be 12 bytes"); -+ THROW_WALLET_EXCEPTION_IF(key32.size() != 32, -+ error::wallet_internal_error, "Trezor KI JSON: encryption key must be 32 bytes"); -+ THROW_WALLET_EXCEPTION_IF(cipher.size() < crypto_aead_chacha20poly1305_ietf_ABYTES, -+ error::wallet_internal_error, "Trezor KI JSON: key_image ciphertext too short"); -+ char buf[96]; -+ unsigned long long out_len = 0; -+ const int r = crypto_aead_chacha20poly1305_ietf_decrypt( -+ reinterpret_cast(buf), &out_len, nullptr, -+ reinterpret_cast(cipher.data()), cipher.size(), -+ nullptr, 0, -+ reinterpret_cast(iv.data()), -+ reinterpret_cast(key32.data())); -+ THROW_WALLET_EXCEPTION_IF(r != 0, -+ error::wallet_internal_error, "Trezor KI JSON: decryption failed (wrong key or corrupt ciphertext)"); -+ THROW_WALLET_EXCEPTION_IF(out_len != 96, -+ error::wallet_internal_error, "Trezor KI JSON: unexpected plaintext length"); -+ memcpy(ki.data, buf, 32); -+ memcpy(sig.c.data, buf + 32, 32); -+ memcpy(sig.r.data, buf + 64, 32); -+ memwipe(buf, sizeof(buf)); -+ } -+} -+ -+//---------------------------------------------------------------------------------------------------- -+uint64_t wallet2::import_trezor_encrypted_key_images_json(const std::string &json, uint64_t &spent, uint64_t &unspent, bool check_spent) -+{ -+ rapidjson::Document doc; -+ doc.Parse(json.c_str()); -+ THROW_WALLET_EXCEPTION_IF(doc.HasParseError(), error::wallet_internal_error, -+ std::string("Trezor KI JSON: parse error at offset ") + std::to_string(doc.GetErrorOffset())); -+ -+ const rapidjson::Value *proot = &doc; -+ if (doc.IsObject() && doc.HasMember("payload") && doc["payload"].IsObject()) -+ proot = &doc["payload"]; -+ -+ THROW_WALLET_EXCEPTION_IF(!proot->IsObject(), error::wallet_internal_error, "Trezor KI JSON: expected JSON object"); -+ const rapidjson::Value &root = *proot; -+ -+ THROW_WALLET_EXCEPTION_IF(!root.HasMember("key_images") || !root["key_images"].IsArray(), -+ error::wallet_internal_error, "Trezor KI JSON: missing or invalid key_images array"); -+ -+ std::string enc_key_bin; -+ if (root.HasMember("signature") && root["signature"].IsString()) -+ { -+ THROW_WALLET_EXCEPTION_IF(!epee::string_tools::parse_hexstr_to_binbuff(root["signature"].GetString(), enc_key_bin), -+ error::wallet_internal_error, "Trezor KI JSON: invalid hex in signature"); -+ } -+ else if (root.HasMember("enc_key") && root["enc_key"].IsString()) -+ { -+ THROW_WALLET_EXCEPTION_IF(!epee::string_tools::parse_hexstr_to_binbuff(root["enc_key"].GetString(), enc_key_bin), -+ error::wallet_internal_error, "Trezor KI JSON: invalid hex in enc_key"); -+ } -+ else -+ { -+ THROW_WALLET_EXCEPTION(error::wallet_internal_error, "Trezor KI JSON: need signature or enc_key (32-byte hex)"); -+ } -+ THROW_WALLET_EXCEPTION_IF(enc_key_bin.size() != 32, -+ error::wallet_internal_error, "Trezor KI JSON: encryption key must decode to 32 bytes"); -+ -+ const rapidjson::Value &arr = root["key_images"]; -+ std::vector> ski; -+ ski.reserve(arr.Size()); -+ -+ for (rapidjson::SizeType i = 0; i < arr.Size(); ++i) -+ { -+ const rapidjson::Value &el = arr[i]; -+ THROW_WALLET_EXCEPTION_IF(!el.IsObject(), error::wallet_internal_error, "Trezor KI JSON: key_images entry must be object"); -+ THROW_WALLET_EXCEPTION_IF(!el.HasMember("iv") || !el["iv"].IsString(), error::wallet_internal_error, "Trezor KI JSON: missing iv"); -+ THROW_WALLET_EXCEPTION_IF(!el.HasMember("key_image") || !el["key_image"].IsString(), error::wallet_internal_error, "Trezor KI JSON: missing key_image"); -+ -+ std::string iv_bin, blob_bin; -+ THROW_WALLET_EXCEPTION_IF(!epee::string_tools::parse_hexstr_to_binbuff(el["iv"].GetString(), iv_bin), -+ error::wallet_internal_error, "Trezor KI JSON: invalid hex in iv"); -+ THROW_WALLET_EXCEPTION_IF(!epee::string_tools::parse_hexstr_to_binbuff(el["key_image"].GetString(), blob_bin), -+ error::wallet_internal_error, "Trezor KI JSON: invalid hex in key_image"); -+ -+ crypto::key_image ki{}; -+ crypto::signature sig{}; -+ decrypt_trezor_exported_ki_blob(blob_bin, iv_bin, enc_key_bin, ki, sig); -+ ski.emplace_back(std::move(ki), std::move(sig)); -+ } -+ -+ return import_key_images(ski, 0, spent, unspent, check_spent); -+} -+//---------------------------------------------------------------------------------------------------- - } -diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h -index 37a2447d2..2e93f3dbf 100644 ---- a/src/wallet/wallet2.h -+++ b/src/wallet/wallet2.h -@@ -1205,8 +1205,10 @@ private: - - void commit_tx(pending_tx& ptx_vector); - void commit_tx(std::vector& ptx_vector); -+ void relay_raw_tx(const std::string &tx_as_hex); - bool save_tx(const std::vector& ptx_vector, const std::string &filename) const; - std::string dump_tx_to_str(const std::vector &ptx_vector) const; -+ void construct_unsigned_tx_set_for_signing(const std::vector& ptx_vector, unsigned_tx_set &utx) const; - std::string save_multisig_tx(multisig_tx_set txs); - bool save_multisig_tx(const multisig_tx_set &txs, const std::string &filename); - std::string save_multisig_tx(const std::vector& ptx_vector); -@@ -1717,7 +1721,8 @@ private: - bool is_unattended() const { return m_unattended; } - - std::pair estimate_tx_size_and_weight(bool use_rct, int n_inputs, int ring_size, int n_outputs, size_t extra_size); -- -+ std::string export_trezor_tdis() const; -+ uint64_t import_trezor_encrypted_key_images_json(const std::string &json, uint64_t &spent, uint64_t &unspent, bool check_spent = true); - bool get_rpc_payment_info(bool mining, bool &payment_required, uint64_t &credits, uint64_t &diff, uint64_t &credits_per_hash_found, cryptonote::blobdata &hashing_blob, uint64_t &height, uint64_t &seed_height, crypto::hash &seed_hash, crypto::hash &next_seed_hash, uint32_t &cookie); - bool daemon_requires_payment(); - bool make_rpc_payment(uint32_t nonce, uint32_t cookie, uint64_t &credits, uint64_t &balance); --- -2.50.1 (Apple Git-155) - diff --git a/patches/monero/0021-trezor-import-export-sign-submit-functions.patch b/patches/monero/0021-trezor-import-export-sign-submit-functions.patch new file mode 100644 index 0000000..7488d68 --- /dev/null +++ b/patches/monero/0021-trezor-import-export-sign-submit-functions.patch @@ -0,0 +1,1099 @@ +From ece9b55a6c6c34a2aaf87e10e059cc4c38a0f1b8 Mon Sep 17 00:00:00 2001 +From: Czarek Nakamoto +Date: Wed, 13 May 2026 11:35:01 -0400 +Subject: [PATCH 21/22] trezor import/export/sign/submit functions + +1) This PR fixes protobuf detection, by removing checks as they were +failing on some platforms, essentially removing Protobuf_COMPILE_TEST_PASSED + +2) Wallet::exportTrezorTdis() returns Trezor connect-ish JSON +Exact JSON schema is shown below, path and networkType is ommited + +https://connect.trezor.io/9/methods/monero/moneroKeyImageSync/ + +```json +{ + tdis: [ + { + out_key: '0a09ab658fca97610a38b8a5c206a0709db435341c31a9d40150df7e52440ac6', + tx_pub_key: 'da13cd8f4cc2c4f769d88b734d71cfdc0e43d01a20eb7bff6553fd67cb2ed37e', + additional_tx_pub_keys: [ + 'aabbccddaabbccddaabbccddaabbccddaabbccddaabbccddaabbccddaabbccdd', + ], + internal_output_index: 1, + sub_addr_major: 0, + sub_addr_minor: 0, + }, + ], +} +``` + +3) Wallet::importTrezorEncryptedKeyImagesJson("{...}") + +Expects Trezor connect MoneroKeyImageSyncResult JSON + +```json +{ + success: true, + payload: { + key_images: [ + { + iv: string, // 12-byte initialization vector/nonce + key_image: string, // Encrypted blob + }, + // ... more key images + ], + signature: string, // 32-byte encryption key + } +} +``` + +4) PendingTransaction::commitTrezor(0) +New method that returns std::string with Trezor connect compatible JSON, as +shown below and at the link + +https://connect.trezor.io/9/methods/monero/moneroSignTransaction/ +```json +{ + // REQUIRED: Hardened Monero account path (minimum 3 components) + path: "m/44'/128'/0'", + + // REQUIRED: Network type + networkType: 0, // 0=MAINNET, 1=TESTNET, 2=STAGENET, 3=FAKECHAIN + + // REQUIRED: Transaction data with outputs + tsx_data: { + version: 1, + unlock_time: 0, + mixin: 15, + fee: 10000000, + account: 0, + num_inputs: 1, + client_version: 3, + hard_fork: 16, + outputs: [ + { + amount: 1000000000000, // 1.0 XMR in atomic units + addr: { + spend_public_key: + 'abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890', + view_public_key: + 'fedcba0987654321fedcba0987654321fedcba0987654321fedcba0987654321', + }, + is_subaddress: false, + original: 'address_string_here', + is_integrated: false, + }, + ], + rsig_data: { + rsig_type: 1, + bp_version: 4, + grouping: [], + }, + }, + + // REQUIRED: At least one input with ring members + inputs: [ + { + outputs: [ + // Ring member 1 (real output) + { + idx: 12345, + key: { + dest: '1111111111111111111111111111111111111111111111111111111111111111', + commitment: + '2222222222222222222222222222222222222222222222222222222222222222', + }, + }, + // Ring member 2 (decoy) + { + idx: 12346, + key: { + dest: '3333333333333333333333333333333333333333333333333333333333333333', + commitment: + '4444444444444444444444444444444444444444444444444444444444444444', + }, + }, + // ... add more ring members (typically 11 total for mixin=10) + ], + real_output: 0, + real_out_tx_key: 'tx_public_key_64_chars', + real_out_additional_tx_keys: [], + real_output_in_tx_index: 1, + amount: 1010000000, + rct: true, + mask: 'amount_mask_64_chars', + subaddr_minor: 0, + }, + ], +} +``` + +5) Transaction assembly is left as an excercises for the reader, after hex +is assembled you can call Wallet::submitTransactionHex("CAFEBABE") + +That's it. +--- + cmake/CheckTrezor.cmake | 79 +++----- + src/device_trezor/trezor/protocol.cpp | 220 +++++++++++++++++++++ + src/device_trezor/trezor/protocol.hpp | 8 + + src/wallet/api/pending_transaction.cpp | 60 ++++++ + src/wallet/api/pending_transaction.h | 1 + + src/wallet/api/unsigned_transaction.cpp | 2 + + src/wallet/api/wallet.cpp | 68 +++++++ + src/wallet/api/wallet.h | 3 + + src/wallet/api/wallet2_api.h | 15 ++ + src/wallet/wallet2.cpp | 249 ++++++++++++++++++++++++ + src/wallet/wallet2.h | 5 +- + 11 files changed, 661 insertions(+), 49 deletions(-) + +diff --git a/cmake/CheckTrezor.cmake b/cmake/CheckTrezor.cmake +index 718760ace..08eaae58a 100644 +--- a/cmake/CheckTrezor.cmake ++++ b/cmake/CheckTrezor.cmake +@@ -53,6 +53,14 @@ if (USE_DEVICE_TREZOR) + # https://github.com/protocolbuffers/protobuf/issues/14576 + find_program(Protobuf_PROTOC_EXECUTABLE protoc REQUIRED) + set(Protobuf_LIBRARY protobuf::libprotobuf) # Compatibility with FindProtobuf.cmake ++ elseif(NOT Protobuf_LIBRARY AND NOT Protobuf_PROTOC_EXECUTABLE AND NOT Protobuf_INCLUDE_DIR) ++ message(STATUS "Could not find Protobuf") ++ elseif(NOT Protobuf_PROTOC_EXECUTABLE OR NOT EXISTS "${Protobuf_PROTOC_EXECUTABLE}") ++ message(STATUS "Protobuf executable not found: ${Protobuf_PROTOC_EXECUTABLE}") ++ unset(Protobuf_FOUND) ++ elseif(NOT Protobuf_INCLUDE_DIR OR NOT EXISTS "${Protobuf_INCLUDE_DIR}") ++ message(STATUS "Protobuf include dir not found: ${Protobuf_INCLUDE_DIR}") ++ unset(Protobuf_FOUND) + else() + # Look for FindProtobuf.cmake, provided by CMake + find_package(Protobuf) +@@ -80,68 +88,43 @@ else() + message(STATUS "Trezor: support disabled by USE_DEVICE_TREZOR") + endif() + +-# Protobuf compilation test + if(Protobuf_FOUND AND USE_DEVICE_TREZOR) +- execute_process(COMMAND ${Protobuf_PROTOC_EXECUTABLE} -I "${CMAKE_CURRENT_LIST_DIR}" -I "${Protobuf_INCLUDE_DIR}" "${CMAKE_CURRENT_LIST_DIR}/test-protobuf.proto" --cpp_out ${CMAKE_BINARY_DIR} RESULT_VARIABLE RET OUTPUT_VARIABLE OUT ERROR_VARIABLE ERR) +- if(RET) +- trezor_fatal_msg("Trezor: Protobuf test generation failed: ${OUT} ${ERR}") ++ if (NOT "$ENV{TREZOR_PYTHON}" STREQUAL "") ++ set(TREZOR_PYTHON "$ENV{TREZOR_PYTHON}" CACHE INTERNAL "Copied from environment variable TREZOR_PYTHON") ++ else() ++ find_package(Python QUIET COMPONENTS Interpreter) ++ if(Python_Interpreter_FOUND) ++ set(TREZOR_PYTHON "${Python_EXECUTABLE}") ++ endif() + endif() + +- if(ANDROID) +- set(CMAKE_TRY_COMPILE_LINKER_FLAGS "${CMAKE_TRY_COMPILE_LINKER_FLAGS} -llog") +- set(CMAKE_TRY_COMPILE_LINK_LIBRARIES "${CMAKE_TRY_COMPILE_LINK_LIBRARIES} log") ++ if(NOT TREZOR_PYTHON) ++ find_package(PythonInterp) ++ if(PYTHONINTERP_FOUND AND PYTHON_EXECUTABLE) ++ set(TREZOR_PYTHON "${PYTHON_EXECUTABLE}") ++ endif() + endif() + +- if(USE_DEVICE_TREZOR_PROTOBUF_TEST) +- if(PROTOBUF_LDFLAGS) +- set(PROTOBUF_TRYCOMPILE_LINKER "${PROTOBUF_LDFLAGS}") +- else() +- set(PROTOBUF_TRYCOMPILE_LINKER "${Protobuf_LIBRARY}") +- endif() +- +- try_compile(Protobuf_COMPILE_TEST_PASSED +- "${CMAKE_BINARY_DIR}" +- SOURCES +- "${CMAKE_BINARY_DIR}/test-protobuf.pb.cc" +- "${CMAKE_CURRENT_LIST_DIR}/test-protobuf.cpp" +- CMAKE_FLAGS +- CMAKE_EXE_LINKER_FLAGS ${CMAKE_TRY_COMPILE_LINKER_FLAGS} +- "-DINCLUDE_DIRECTORIES=${Protobuf_INCLUDE_DIR};${CMAKE_BINARY_DIR}" +- "-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}" +- LINK_LIBRARIES "${PROTOBUF_TRYCOMPILE_LINKER}" ${CMAKE_TRY_COMPILE_LINK_LIBRARIES} +- OUTPUT_VARIABLE OUTPUT +- ) +- if(NOT Protobuf_COMPILE_TEST_PASSED) +- trezor_fatal_msg("Trezor: Protobuf Compilation test failed: ${OUTPUT}.") +- endif() +- else () +- message(STATUS "Trezor: Protobuf Compilation test skipped, build may fail later") ++ if(NOT TREZOR_PYTHON) ++ message(STATUS "Trezor: Python not found") + endif() + endif() + + # Try to build protobuf messages +-if(Protobuf_FOUND AND USE_DEVICE_TREZOR) +- # .proto files to compile +- set(_proto_files "messages.proto" +- "messages-common.proto" +- "messages-management.proto" +- "messages-monero.proto") +- if (TREZOR_DEBUG) +- list(APPEND _proto_files "messages-debug.proto") +- endif () +- +- set(_proto_include_dir "${CMAKE_CURRENT_LIST_DIR}/../src/device_trezor/trezor/protob") +- set(_proto_files_absolute) +- foreach(file IN LISTS _proto_files) +- list(APPEND _proto_files_absolute "${_proto_include_dir}/${file}") +- endforeach () ++if(Protobuf_FOUND AND USE_DEVICE_TREZOR AND TREZOR_PYTHON) ++ set(ENV{PROTOBUF_INCLUDE_DIRS} "${Protobuf_INCLUDE_DIR}") ++ set(ENV{PROTOBUF_PROTOC_EXECUTABLE} "${Protobuf_PROTOC_EXECUTABLE}") ++ set(TREZOR_PROTOBUF_PARAMS "") ++ if (USE_DEVICE_TREZOR_DEBUG) ++ set(TREZOR_PROTOBUF_PARAMS "--debug") ++ endif() + +- set(_proto_out_dir "${CMAKE_CURRENT_LIST_DIR}/../src/device_trezor/trezor/messages") +- execute_process(COMMAND ${Protobuf_PROTOC_EXECUTABLE} --cpp_out "${_proto_out_dir}" "-I${_proto_include_dir}" ${_proto_files_absolute} RESULT_VARIABLE RET OUTPUT_VARIABLE OUT ERROR_VARIABLE ERR) ++ execute_process(COMMAND ${TREZOR_PYTHON} tools/build_protob.py ${TREZOR_PROTOBUF_PARAMS} WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/../src/device_trezor/trezor RESULT_VARIABLE RET OUTPUT_VARIABLE OUT ERROR_VARIABLE ERR) + if(RET) + trezor_fatal_msg("Trezor: protobuf messages could not be (re)generated (err=${RET}). OUT: ${OUT}, ERR: ${ERR}.") + endif() + ++ set(_proto_out_dir "${CMAKE_CURRENT_LIST_DIR}/../src/device_trezor/trezor/messages") + if(FREEBSD) + # FreeBSD defines `minor` in usr/include/sys/types.h which conflicts with this file + # https://github.com/trezor/trezor-firmware/issues/4460 +diff --git a/src/device_trezor/trezor/protocol.cpp b/src/device_trezor/trezor/protocol.cpp +index 52f02be3b..ec4e9cfe9 100644 +--- a/src/device_trezor/trezor/protocol.cpp ++++ b/src/device_trezor/trezor/protocol.cpp +@@ -29,12 +29,17 @@ + + #include "version.h" + #include "protocol.hpp" ++#include "string_tools.h" + #include + #include ++#include + #include + #include + #include + #include ++#include ++#include ++#include + #include + #include + #include +@@ -439,6 +444,10 @@ namespace tx { + } + } + ++ void Signer::export_source_entry(MoneroTransactionSourceEntry *dst, size_t idx, bool need_ring_keys, bool need_ring_indices){ ++ set_tx_input(dst, idx, need_ring_keys, need_ring_indices); ++ } ++ + void Signer::set_tx_input(MoneroTransactionSourceEntry * dst, size_t idx, bool need_ring_keys, bool need_ring_indices){ + const cryptonote::tx_source_entry & src = cur_tx().sources[idx]; + const wallet2_basic::transfer_details & transfer = get_source_transfer(idx); +@@ -1085,6 +1094,217 @@ namespace tx { + memwipe(plaintext.get(), keys_len); + } + ++ namespace { ++ ++ std::string bin_to_hex_lower(const std::string &bin) ++ { ++ return epee::string_tools::buff_to_hex_nodelimer(bin); ++ } ++ ++ rapidjson::Value dest_entry_to_json(const messages::monero::MoneroTransactionDestinationEntry &e, rapidjson::Document::AllocatorType &a) ++ { ++ rapidjson::Value o(rapidjson::kObjectType); ++ if (e.has_amount()) ++ o.AddMember("amount", e.amount(), a); ++ if (e.has_addr()) ++ { ++ rapidjson::Value addr(rapidjson::kObjectType); ++ const auto &ad = e.addr(); ++ if (ad.has_spend_public_key()) ++ { ++ const std::string h = bin_to_hex_lower(ad.spend_public_key()); ++ addr.AddMember("spend_public_key", rapidjson::Value(h.c_str(), static_cast(h.size()), a), a); ++ } ++ if (ad.has_view_public_key()) ++ { ++ const std::string h = bin_to_hex_lower(ad.view_public_key()); ++ addr.AddMember("view_public_key", rapidjson::Value(h.c_str(), static_cast(h.size()), a), a); ++ } ++ o.AddMember("addr", addr, a); ++ } ++ if (e.has_is_subaddress()) ++ o.AddMember("is_subaddress", e.is_subaddress(), a); ++ if (e.has_original()) ++ o.AddMember("original", rapidjson::Value(e.original().c_str(), static_cast(e.original().size()), a), a); ++ if (e.has_is_integrated()) ++ o.AddMember("is_integrated", e.is_integrated(), a); ++ return o; ++ } ++ ++ rapidjson::Value source_entry_to_json(const messages::monero::MoneroTransactionSourceEntry &e, rapidjson::Document::AllocatorType &a) ++ { ++ rapidjson::Value o(rapidjson::kObjectType); ++ rapidjson::Value ring(rapidjson::kArrayType); ++ for (int i = 0; i < e.outputs_size(); ++i) ++ { ++ const auto &out = e.outputs(i); ++ rapidjson::Value ring_m(rapidjson::kObjectType); ++ if (out.has_idx()) ++ ring_m.AddMember("idx", out.idx(), a); ++ if (out.has_key()) ++ { ++ rapidjson::Value key(rapidjson::kObjectType); ++ const auto &k = out.key(); ++ if (k.has_dest()) ++ { ++ const std::string h = bin_to_hex_lower(k.dest()); ++ key.AddMember("dest", rapidjson::Value(h.c_str(), static_cast(h.size()), a), a); ++ } ++ if (k.has_commitment()) ++ { ++ const std::string h = bin_to_hex_lower(k.commitment()); ++ key.AddMember("commitment", rapidjson::Value(h.c_str(), static_cast(h.size()), a), a); ++ } ++ ring_m.AddMember("key", key, a); ++ } ++ ring.PushBack(ring_m, a); ++ } ++ o.AddMember("outputs", ring, a); ++ if (e.has_real_output()) ++ o.AddMember("real_output", e.real_output(), a); ++ if (e.has_real_out_tx_key()) ++ { ++ const std::string h = bin_to_hex_lower(e.real_out_tx_key()); ++ o.AddMember("real_out_tx_key", rapidjson::Value(h.c_str(), static_cast(h.size()), a), a); ++ } ++ rapidjson::Value add_keys(rapidjson::kArrayType); ++ for (int i = 0; i < e.real_out_additional_tx_keys_size(); ++i) ++ { ++ const std::string h = bin_to_hex_lower(e.real_out_additional_tx_keys(i)); ++ add_keys.PushBack(rapidjson::Value(h.c_str(), static_cast(h.size()), a), a); ++ } ++ o.AddMember("real_out_additional_tx_keys", add_keys, a); ++ if (e.has_real_output_in_tx_index()) ++ o.AddMember("real_output_in_tx_index", e.real_output_in_tx_index(), a); ++ if (e.has_amount()) ++ o.AddMember("amount", e.amount(), a); ++ if (e.has_rct()) ++ o.AddMember("rct", e.rct(), a); ++ if (e.has_mask()) ++ { ++ const std::string h = bin_to_hex_lower(e.mask()); ++ o.AddMember("mask", rapidjson::Value(h.c_str(), static_cast(h.size()), a), a); ++ } ++ if (e.has_subaddr_minor()) ++ o.AddMember("subaddr_minor", e.subaddr_minor(), a); ++ return o; ++ } ++ ++ std::string monero_default_bip44_path(uint32_t subaddr_account) ++ { ++ std::ostringstream oss; ++ oss << "m/44'/128'/" << subaddr_account << "'"; ++ return oss.str(); ++ } ++ ++ } // namespace ++ ++ ++ std::string trezor_connect_monero_sign_transaction_to_json( ++ wallet_shim *wallet, ++ const unsigned_tx_set *utx, ++ size_t tx_idx, ++ hw::tx_aux_data *aux_data, ++ cryptonote::network_type network_type) ++ { ++ CHECK_AND_ASSERT_THROW_MES(utx && aux_data, "null argument"); ++ CHECK_AND_ASSERT_THROW_MES(std::get<0>(utx->transfers) == 0, "Unsupported non zero offset"); ++ CHECK_AND_ASSERT_THROW_MES(tx_idx < utx->txes.size(), "Invalid transaction index"); ++ ++ Signer signer(wallet, utx, tx_idx, aux_data); ++ auto init_req = signer.step_init(); ++ const auto &tsx = init_req->tsx_data(); ++ ++ rapidjson::Document doc; ++ doc.SetObject(); ++ auto &alloc = doc.GetAllocator(); ++ ++ const std::string path_str = monero_default_bip44_path(utx->txes[tx_idx].subaddr_account); ++ doc.AddMember("path", rapidjson::Value(path_str.c_str(), static_cast(path_str.size()), alloc), alloc); ++ doc.AddMember("networkType", static_cast(network_type), alloc); ++ ++ rapidjson::Value tsx_data(rapidjson::kObjectType); ++ tsx_data.AddMember("version", tsx.version(), alloc); ++ if (tsx.has_payment_id() && !tsx.payment_id().empty()) ++ { ++ const std::string h = bin_to_hex_lower(tsx.payment_id()); ++ tsx_data.AddMember("payment_id", rapidjson::Value(h.c_str(), static_cast(h.size()), alloc), alloc); ++ } ++ tsx_data.AddMember("unlock_time", static_cast(tsx.unlock_time()), alloc); ++ ++ rapidjson::Value outputs(rapidjson::kArrayType); ++ for (int i = 0; i < tsx.outputs_size(); ++i) ++ outputs.PushBack(dest_entry_to_json(tsx.outputs(i), alloc), alloc); ++ tsx_data.AddMember("outputs", outputs, alloc); ++ ++ if (tsx.has_change_dts()) ++ tsx_data.AddMember("change_dts", dest_entry_to_json(tsx.change_dts(), alloc), alloc); ++ ++ if (tsx.has_num_inputs()) ++ tsx_data.AddMember("num_inputs", tsx.num_inputs(), alloc); ++ if (tsx.has_mixin()) ++ tsx_data.AddMember("mixin", tsx.mixin(), alloc); ++ if (tsx.has_fee()) ++ tsx_data.AddMember("fee", tsx.fee(), alloc); ++ if (tsx.has_account()) ++ tsx_data.AddMember("account", tsx.account(), alloc); ++ ++ if (tsx.minor_indices_size() > 0) ++ { ++ rapidjson::Value minors(rapidjson::kArrayType); ++ for (int i = 0; i < tsx.minor_indices_size(); ++i) ++ minors.PushBack(tsx.minor_indices(i), alloc); ++ tsx_data.AddMember("minor_indices", minors, alloc); ++ } ++ ++ if (tsx.has_rsig_data()) ++ { ++ const auto &rd = tsx.rsig_data(); ++ rapidjson::Value rj(rapidjson::kObjectType); ++ if (rd.has_rsig_type()) ++ rj.AddMember("rsig_type", rd.rsig_type(), alloc); ++ if (rd.has_bp_version()) ++ rj.AddMember("bp_version", rd.bp_version(), alloc); ++ rapidjson::Value grp(rapidjson::kArrayType); ++ for (int i = 0; i < rd.grouping_size(); ++i) ++ grp.PushBack(static_cast(rd.grouping(i)), alloc); ++ rj.AddMember("grouping", grp, alloc); ++ tsx_data.AddMember("rsig_data", rj, alloc); ++ } ++ ++ if (tsx.integrated_indices_size() > 0) ++ { ++ rapidjson::Value ii(rapidjson::kArrayType); ++ for (int i = 0; i < tsx.integrated_indices_size(); ++i) ++ ii.PushBack(tsx.integrated_indices(i), alloc); ++ tsx_data.AddMember("integrated_indices", ii, alloc); ++ } ++ ++ if (tsx.has_client_version()) ++ tsx_data.AddMember("client_version", tsx.client_version(), alloc); ++ if (tsx.has_hard_fork()) ++ tsx_data.AddMember("hard_fork", tsx.hard_fork(), alloc); ++ if (tsx.has_monero_version()) ++ tsx_data.AddMember("monero_version", rapidjson::Value(tsx.monero_version().c_str(), static_cast(tsx.monero_version().size()), alloc), alloc); ++ ++ doc.AddMember("tsx_data", tsx_data, alloc); ++ ++ rapidjson::Value inputs(rapidjson::kArrayType); ++ const size_t n_in = utx->txes[tx_idx].sources.size(); ++ for (size_t i = 0; i < n_in; ++i) ++ { ++ messages::monero::MoneroTransactionSourceEntry src_pb; ++ signer.export_source_entry(&src_pb, i, true, true); ++ inputs.PushBack(source_entry_to_json(src_pb, alloc), alloc); ++ } ++ doc.AddMember("inputs", inputs, alloc); ++ ++ rapidjson::StringBuffer buffer; ++ rapidjson::Writer writer(buffer); ++ doc.Accept(writer); ++ return std::string(buffer.GetString(), buffer.GetSize()); ++ } ++ + } + } + } +diff --git a/src/device_trezor/trezor/protocol.hpp b/src/device_trezor/trezor/protocol.hpp +index 73bd0b902..2db4e8e16 100644 +--- a/src/device_trezor/trezor/protocol.hpp ++++ b/src/device_trezor/trezor/protocol.hpp +@@ -340,8 +340,16 @@ namespace tx { + const TData & tdata() const { + return m_ct; + } ++ void export_source_entry(MoneroTransactionSourceEntry *dst, size_t idx, bool need_ring_keys, bool need_ring_indices); + }; + ++ std::string trezor_connect_monero_sign_transaction_to_json( ++ wallet_shim *wallet, ++ const unsigned_tx_set *utx, ++ size_t tx_idx, ++ hw::tx_aux_data *aux_data, ++ cryptonote::network_type network_type); ++ + // TX Key decryption + void load_tx_key_data(hw::device_cold::tx_key_data_t & res, const std::string & data); + +diff --git a/src/wallet/api/pending_transaction.cpp b/src/wallet/api/pending_transaction.cpp +index 796e7a50b..9109c8a32 100644 +--- a/src/wallet/api/pending_transaction.cpp ++++ b/src/wallet/api/pending_transaction.cpp +@@ -44,6 +44,11 @@ + + #include "bc-ur/src/bc-ur.hpp" + ++#if defined(DEVICE_TREZOR_READY) && DEVICE_TREZOR_READY ++#include "device/device_cold.hpp" ++#include "device_trezor/trezor/protocol.hpp" ++#endif ++ + using namespace std; + + namespace Monero { +@@ -210,6 +215,61 @@ std::string PendingTransactionImpl::commitUR(int max_fragment_length) { + } + } + ++std::string PendingTransactionImpl::commitTrezor(uint64_t tx_index) ++{ ++#if !defined(DEVICE_TREZOR_READY) || !DEVICE_TREZOR_READY ++ (void)tx_index; ++ m_errorString = tr("This build was compiled without Trezor support"); ++ m_status = Status_Error; ++ return ""; ++#else ++ if (tx_index >= m_pending_tx.size()) ++ { ++ m_errorString = tr("Invalid transaction index"); ++ m_status = Status_Error; ++ return ""; ++ } ++ try ++ { ++ tools::wallet2 *w = m_wallet.m_wallet.get(); ++ tools::wallet2::unsigned_tx_set utx; ++ w->construct_unsigned_tx_set_for_signing(m_pending_tx, utx); ++ if (std::get<0>(utx.transfers) != 0) ++ { ++ m_errorString = tr("Unsupported unsigned transaction transfer offset"); ++ m_status = Status_Error; ++ return ""; ++ } ++ hw::tx_aux_data aux_data; ++ const int bpv = w->use_fork_rules(HF_VERSION_BULLETPROOF_PLUS, -10) ? 4 ++ : (w->use_fork_rules(HF_VERSION_CLSAG, -10) ? 3 ++ : (w->use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1)); ++ aux_data.bp_version = bpv; ++ aux_data.hard_fork = w->get_current_hard_fork(); ++ aux_data.client_version = static_cast(bpv >= 4 ? 4u : 3u); ++ ++ hw::wallet_shim shim; ++ shim.get_tx_pub_key_from_received_outs = std::bind(&tools::wallet2::get_tx_pub_key_from_received_outs, w, std::placeholders::_1); ++ ++ const std::string json = hw::trezor::protocol::tx::trezor_connect_monero_sign_transaction_to_json( ++ &shim, ++ &utx, ++ static_cast(tx_index), ++ &aux_data, ++ w->nettype()); ++ m_errorString.clear(); ++ m_status = Status_Ok; ++ return json; ++ } ++ catch (const std::exception &e) ++ { ++ m_errorString = e.what(); ++ m_status = Status_Error; ++ return ""; ++ } ++#endif ++} ++ + + uint64_t PendingTransactionImpl::amount() const + { +diff --git a/src/wallet/api/pending_transaction.h b/src/wallet/api/pending_transaction.h +index eb5c75c5f..c9c68fb03 100644 +--- a/src/wallet/api/pending_transaction.h ++++ b/src/wallet/api/pending_transaction.h +@@ -47,6 +47,7 @@ public: + std::string errorString() const override; + bool commit(const std::string &filename = "", bool overwrite = false) override; + std::string commitUR(int max_fragment_length = 130) override; ++ std::string commitTrezor(uint64_t tx_index = 0) override; + uint64_t amount() const override; + 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 062df7f41..560890f2c 100644 +--- a/src/wallet/api/unsigned_transaction.cpp ++++ b/src/wallet/api/unsigned_transaction.cpp +@@ -34,11 +34,13 @@ + + #include "cryptonote_basic/cryptonote_format_utils.h" + #include "cryptonote_basic/cryptonote_basic_impl.h" ++#include "cryptonote_config.h" + + #include + #include + #include + #include ++#include + + #include "bc-ur/src/bc-ur.hpp" + +diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp +index 57f1203c6..c65ce8799 100644 +--- a/src/wallet/api/wallet.cpp ++++ b/src/wallet/api/wallet.cpp +@@ -1392,6 +1392,45 @@ bool WalletImpl::submitTransactionUR(const string &input) { + return true; + } + ++bool WalletImpl::submitTransactionHex(const string &hex) { ++ clearStatus(); ++ if (checkBackgroundSync("cannot submit tx")) ++ return false; ++ ++ pauseRefresh(); ++ try { ++ m_wallet->relay_raw_tx(hex); ++ } catch (const tools::error::daemon_busy&) { ++ setStatusError(tr("daemon is busy. Please try again later.")); ++ startRefresh(); ++ return false; ++ } catch (const tools::error::no_connection_to_daemon&) { ++ setStatusError(tr("no connection to daemon.")); ++ startRefresh(); ++ return false; ++ } catch (const tools::error::tx_rejected& e) { ++ std::ostringstream writer; ++ writer << (boost::format(tr("transaction %s was rejected by daemon with status: ")) % get_transaction_hash(e.tx())) << e.status(); ++ if (!e.reason().empty()) ++ writer << tr(". Reason: ") << e.reason(); ++ setStatusError(writer.str()); ++ startRefresh(); ++ return false; ++ } catch (const std::exception &e) { ++ setStatusError(string(tr("Unknown exception: ")) + e.what()); ++ startRefresh(); ++ return false; ++ } catch (...) { ++ setStatusError(tr("Unhandled exception")); ++ startRefresh(); ++ return false; ++ } ++ startRefresh(); ++ if (m_history) ++ m_history->refresh(); ++ return true; ++} ++ + + bool WalletImpl::hasUnknownKeyImages() const + { +@@ -3436,4 +3475,33 @@ std::string WalletImpl::serializeCacheToJson() const + return std::string(m_wallet->serialize_cache_to_json()); + } + ++std::string WalletImpl::exportTrezorTdis() const ++{ ++ return m_wallet->export_trezor_tdis(); ++} ++ ++bool WalletImpl::importTrezorEncryptedKeyImagesJson(const string &json) ++{ ++ if (checkBackgroundSync("cannot import key images")) ++ return false; ++ if (!trustedDaemon()) { ++ setStatusError(tr("Key images can only be imported with a trusted daemon")); ++ return false; ++ } ++ try ++ { ++ uint64_t spent = 0, unspent = 0; ++ uint64_t height = m_wallet->import_trezor_encrypted_key_images_json(json, spent, unspent); ++ LOG_PRINT_L2("Trezor encrypted key images imported to height " << height << ", " ++ << print_money(spent) << " spent, " << print_money(unspent) << " unspent"); ++ } ++ catch (const std::exception &e) ++ { ++ LOG_ERROR("Error importing Trezor encrypted key images: " << e.what()); ++ setStatusError(string(tr("Failed to import key images: ")) + e.what()); ++ return false; ++ } ++ return true; ++} ++ + } // namespace +diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h +index 8e4846e27..ede41b46d 100644 +--- a/src/wallet/api/wallet.h ++++ b/src/wallet/api/wallet.h +@@ -185,6 +185,7 @@ public: + virtual PendingTransaction * createSweepUnmixableTransaction() override; + bool submitTransaction(const std::string &fileName) override; + bool submitTransactionUR(const std::string &input) override; ++ bool submitTransactionHex(const std::string &hex) override; + virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename) override; + virtual UnsignedTransaction * loadUnsignedTxUR(const std::string &input) override; + bool hasUnknownKeyImages() const override; +@@ -338,6 +339,8 @@ private: + bool getWaitsForDeviceReceive(); + + virtual std::string serializeCacheToJson() const override; ++ virtual std::string exportTrezorTdis() const override; ++ bool importTrezorEncryptedKeyImagesJson(const std::string &json) override; + }; + + +diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h +index 4552c02d0..bead275bb 100644 +--- a/src/wallet/api/wallet2_api.h ++++ b/src/wallet/api/wallet2_api.h +@@ -84,6 +84,7 @@ struct PendingTransaction + // commit transaction or save to file if filename is provided. + virtual bool commit(const std::string &filename = "", bool overwrite = false) = 0; + virtual std::string commitUR(int max_fragment_length = 130) = 0; ++ virtual std::string commitTrezor(uint64_t tx_index = 0) = 0; + virtual uint64_t amount() const = 0; + virtual uint64_t dust() const = 0; + virtual uint64_t fee() const = 0; +@@ -950,6 +951,7 @@ struct Wallet + */ + virtual bool submitTransaction(const std::string &fileName) = 0; + virtual bool submitTransactionUR(const std::string &input) = 0; ++ virtual bool submitTransactionHex(const std::string &input) = 0; + + + /*! +@@ -1216,6 +1218,19 @@ struct Wallet + + //! serialize wallet cache to JSON + virtual std::string serializeCacheToJson() const = 0; ++ ++ ++ /*! ++ * \brief exportTrezorTdis — export transfer details for Trezor cold key-image sync as JSON ++ * ++ * Returns an object `{ "tdis": [ ... ] }` with hex pubkeys and indices per output. ++ */ ++ virtual std::string exportTrezorTdis() const = 0; ++ ++ /*! ++ * \brief importTrezorEncryptedKeyImagesJson — import key images from Trezor-style encrypted JSON ++ */ ++ virtual bool importTrezorEncryptedKeyImagesJson(const std::string &json) = 0; + }; + + /** +diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp +index 06a732296..2a71e4751 100644 +--- a/src/wallet/wallet2.cpp ++++ b/src/wallet/wallet2.cpp +@@ -112,6 +112,7 @@ extern "C" + { + #include "crypto/keccak.h" + #include "crypto/crypto-ops.h" ++#include + } + using namespace std; + using namespace crypto; +@@ -967,6 +968,42 @@ static tools::wallet2::tx_construction_data &get_construction_data(tools::wallet + return std::get(ptx.construction_data); + } + ++static bool get_short_payment_id(crypto::hash8 &payment_id8, const tools::wallet2::pending_tx &ptx, hw::device &hwdev) ++{ ++ std::vector tx_extra_fields; ++ parse_tx_extra(ptx.tx.extra, tx_extra_fields); ++ cryptonote::tx_extra_nonce extra_nonce; ++ if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce)) ++ { ++ if (get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8)) ++ { ++ if (ptx.dests.empty()) ++ { ++ MWARNING("Encrypted payment id found, but no destinations public key, cannot decrypt"); ++ return false; ++ } ++ return hwdev.decrypt_payment_id(payment_id8, ptx.dests[0].addr.m_view_public_key, ptx.tx_key); ++ } ++ } ++ return false; ++} ++ ++static tools::wallet2::tx_construction_data get_construction_data_with_decrypted_short_payment_id(const tools::wallet2::pending_tx &ptx, hw::device &hwdev) ++{ ++ tools::wallet2::tx_construction_data construction_data = get_construction_data(ptx); ++ crypto::hash8 payment_id = crypto::null_hash8; ++ if (get_short_payment_id(payment_id, ptx, hwdev)) ++ { ++ remove_field_from_tx_extra(construction_data.extra, typeid(cryptonote::tx_extra_nonce)); ++ std::string extra_nonce; ++ set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, payment_id); ++ THROW_WALLET_EXCEPTION_IF(!add_extra_nonce_to_tx_extra(construction_data.extra, extra_nonce), ++ tools::error::wallet_internal_error, "Failed to add decrypted payment id to tx extra"); ++ MDEBUG("Decrypted payment ID: " << payment_id); ++ } ++ return construction_data; ++} ++ + uint32_t get_subaddress_clamped_sum(uint32_t idx, uint32_t extra) + { + static constexpr uint32_t uint32_max = std::numeric_limits::max(); +@@ -8080,6 +8117,61 @@ void wallet2::commit_tx(std::vector& ptx_vector) + } + } + //---------------------------------------------------------------------------------------------------- ++void wallet2::relay_raw_tx(const std::string &tx_as_hex) ++{ ++ using namespace cryptonote; ++ ++ cryptonote::blobdata tx_blob; ++ THROW_WALLET_EXCEPTION_IF(!epee::string_tools::parse_hexstr_to_binbuff(tx_as_hex, tx_blob), error::wallet_internal_error, "Failed to parse hex"); ++ ++ transaction tx; ++ THROW_WALLET_EXCEPTION_IF(!parse_and_validate_tx_from_blob(tx_blob, tx), error::wallet_internal_error, "Failed to parse transaction"); ++ ++ COMMAND_RPC_SEND_RAW_TX::request req; ++ req.tx_as_hex = tx_as_hex; ++ req.do_not_relay = false; ++ req.do_sanity_checks = true; ++ COMMAND_RPC_SEND_RAW_TX::response daemon_send_resp; ++ ++ { ++ const boost::lock_guard lock{m_daemon_rpc_mutex}; ++ bool r = epee::net_utils::invoke_http_json("/sendrawtransaction", req, daemon_send_resp, *m_http_client, rpc_timeout); ++ THROW_ON_RPC_RESPONSE_ERROR(r, {}, daemon_send_resp, "sendrawtransaction", error::tx_rejected, tx, get_rpc_status(m_trusted_daemon, daemon_send_resp.status), get_text_reason(daemon_send_resp)); ++ } ++ ++ crypto::hash txid = get_transaction_hash(tx); ++ uint64_t tx_money_spent_in_ins = 0; ++ auto subaddr_account = []()->boost::optional { return boost::none; }(); ++ std::set subaddr_indices; ++ for (const auto &in : tx.vin) ++ { ++ if (in.type() != typeid(cryptonote::txin_to_key)) ++ continue; ++ const cryptonote::txin_to_key &in_to_key = boost::get(in); ++ const auto kit = m_key_images.find(in_to_key.k_image); ++ if (kit == m_key_images.end()) ++ continue; ++ const transfer_details &td = m_transfers[kit->second]; ++ const uint64_t amount = in_to_key.amount > 0 ? in_to_key.amount : td.amount(); ++ tx_money_spent_in_ins += amount; ++ subaddr_account = td.m_subaddr_index.major; ++ subaddr_indices.insert(td.m_subaddr_index.minor); ++ set_spent(kit->second, 0); ++ } ++ ++ if (tx_money_spent_in_ins > 0 && store_tx_info() && !m_unconfirmed_txs.count(txid)) ++ { ++ THROW_WALLET_EXCEPTION_IF(!subaddr_account, error::wallet_internal_error, ++ "Relayed tx spends our outputs but subaddress account is unknown"); ++ add_unconfirmed_tx(txid, tx, tx_money_spent_in_ins, {}, crypto::null_hash, 0, *subaddr_account, subaddr_indices); ++ auto utx_it = m_unconfirmed_txs.find(txid); ++ THROW_WALLET_EXCEPTION_IF(utx_it == m_unconfirmed_txs.end(), error::wallet_internal_error, ++ "unconfirmed tx wasn't found: " + string_tools::pod_to_hex(txid)); ++ utx_it->second.m_amount_out = get_outgoing_amount(tx, tx_money_spent_in_ins); ++ LOG_PRINT_L1("Raw transaction relayed. <" << txid << ">"); ++ } ++} ++//---------------------------------------------------------------------------------------------------- + bool wallet2::save_tx(const std::vector& ptx_vector, const std::string &filename) const + { + LOG_PRINT_L0("saving " << ptx_vector.size() << " transactions"); +@@ -8117,6 +8209,20 @@ std::string wallet2::dump_tx_to_str(const std::vector &ptx_vector) c + return std::string(UNSIGNED_TX_PREFIX) + ciphertext; + } + //---------------------------------------------------------------------------------------------------- ++void wallet2::construct_unsigned_tx_set_for_signing(const std::vector& ptx_vector, unsigned_tx_set &utx) const ++{ ++ utx.txes.clear(); ++ utx.txes.reserve(ptx_vector.size()); ++ for (const auto &tx : ptx_vector) ++ utx.txes.push_back(get_construction_data_with_decrypted_short_payment_id(tx, m_account.get_device())); ++ ++ utx.new_transfers = std::make_tuple(static_cast(0), static_cast(0), std::vector()); ++ ++ transfer_container transfers_copy; ++ get_transfers(transfers_copy); ++ utx.transfers = std::make_tuple(static_cast(0), static_cast(transfers_copy.size()), std::move(transfers_copy)); ++} ++//---------------------------------------------------------------------------------------------------- + bool wallet2::load_unsigned_tx(const std::string &unsigned_filename, unsigned_tx_set &exported_txs) const + { + std::string s; +@@ -15946,4 +16052,147 @@ std::pair wallet2::estimate_tx_size_and_weight(bool use_rct, i + return std::make_pair(size, weight); + } + //---------------------------------------------------------------------------------------------------- ++// Returns JSON: { "tdis": [ { "out_key", "tx_pub_key", optional "additional_tx_pub_keys", indices }, ... ] } ++// Hex fields are 64-char lower-case pubkeys; additional_tx_pub_keys matches protocol::ki::key_image_data ++// (subset of txn additional pubkeys for this transfer — currently 0 or 1 entry). ++std::string wallet2::export_trezor_tdis() const ++{ ++ rapidjson::Document doc; ++ doc.SetObject(); ++ auto &alloc = doc.GetAllocator(); ++ ++ rapidjson::Value tdis(rapidjson::kArrayType); ++ tdis.Reserve(static_cast(m_transfers.size()), alloc); ++ ++ for (const auto &td : m_transfers) ++ { ++ const crypto::public_key out_key = td.get_public_key(); ++ const crypto::public_key tx_pub_key = get_tx_pub_key_from_received_outs(td); ++ const std::vector additional_tx_pub_keys = cryptonote::get_additional_tx_pub_keys_from_extra(td.m_tx); ++ ++ std::vector additional_for_tdi; ++ if (!additional_tx_pub_keys.empty() && additional_tx_pub_keys.size() > td.m_internal_output_index) ++ additional_for_tdi.push_back(additional_tx_pub_keys[td.m_internal_output_index]); ++ ++ rapidjson::Value obj(rapidjson::kObjectType); ++ const std::string out_hex = epee::string_tools::pod_to_hex(out_key); ++ const std::string tx_pub_hex = epee::string_tools::pod_to_hex(tx_pub_key); ++ obj.AddMember("out_key", rapidjson::Value(out_hex.c_str(), static_cast(out_hex.size()), alloc), alloc); ++ obj.AddMember("tx_pub_key", rapidjson::Value(tx_pub_hex.c_str(), static_cast(tx_pub_hex.size()), alloc), alloc); ++ if (!additional_for_tdi.empty()) ++ { ++ rapidjson::Value aux(rapidjson::kArrayType); ++ aux.Reserve(static_cast(additional_for_tdi.size()), alloc); ++ for (const auto &apk : additional_for_tdi) ++ { ++ const std::string ah = epee::string_tools::pod_to_hex(apk); ++ aux.PushBack(rapidjson::Value(ah.c_str(), static_cast(ah.size()), alloc), alloc); ++ } ++ obj.AddMember("additional_tx_pub_keys", aux, alloc); ++ } ++ obj.AddMember("internal_output_index", rapidjson::Value(static_cast(td.m_internal_output_index)), alloc); ++ obj.AddMember("sub_addr_major", rapidjson::Value(td.m_subaddr_index.major), alloc); ++ obj.AddMember("sub_addr_minor", rapidjson::Value(td.m_subaddr_index.minor), alloc); ++ tdis.PushBack(obj, alloc); ++ } ++ ++ doc.AddMember("tdis", tdis, alloc); ++ ++ rapidjson::StringBuffer buffer; ++ rapidjson::Writer writer(buffer); ++ doc.Accept(writer); ++ return std::string(buffer.GetString(), buffer.GetSize()); ++} ++//---------------------------------------------------------------------------------------------------- ++namespace ++{ ++ static void decrypt_trezor_exported_ki_blob(const std::string &cipher, const std::string &iv, const std::string &key32, crypto::key_image &ki, crypto::signature &sig) ++ { ++ THROW_WALLET_EXCEPTION_IF(iv.size() != crypto_aead_chacha20poly1305_ietf_NPUBBYTES, ++ error::wallet_internal_error, "Trezor KI JSON: IV must be 12 bytes"); ++ THROW_WALLET_EXCEPTION_IF(key32.size() != 32, ++ error::wallet_internal_error, "Trezor KI JSON: encryption key must be 32 bytes"); ++ THROW_WALLET_EXCEPTION_IF(cipher.size() < crypto_aead_chacha20poly1305_ietf_ABYTES, ++ error::wallet_internal_error, "Trezor KI JSON: key_image ciphertext too short"); ++ char buf[96]; ++ unsigned long long out_len = 0; ++ const int r = crypto_aead_chacha20poly1305_ietf_decrypt( ++ reinterpret_cast(buf), &out_len, nullptr, ++ reinterpret_cast(cipher.data()), cipher.size(), ++ nullptr, 0, ++ reinterpret_cast(iv.data()), ++ reinterpret_cast(key32.data())); ++ THROW_WALLET_EXCEPTION_IF(r != 0, ++ error::wallet_internal_error, "Trezor KI JSON: decryption failed (wrong key or corrupt ciphertext)"); ++ THROW_WALLET_EXCEPTION_IF(out_len != 96, ++ error::wallet_internal_error, "Trezor KI JSON: unexpected plaintext length"); ++ memcpy(ki.data, buf, 32); ++ memcpy(sig.c.data, buf + 32, 32); ++ memcpy(sig.r.data, buf + 64, 32); ++ memwipe(buf, sizeof(buf)); ++ } ++} ++ ++//---------------------------------------------------------------------------------------------------- ++uint64_t wallet2::import_trezor_encrypted_key_images_json(const std::string &json, uint64_t &spent, uint64_t &unspent, bool check_spent) ++{ ++ rapidjson::Document doc; ++ doc.Parse(json.c_str()); ++ THROW_WALLET_EXCEPTION_IF(doc.HasParseError(), error::wallet_internal_error, ++ std::string("Trezor KI JSON: parse error at offset ") + std::to_string(doc.GetErrorOffset())); ++ ++ const rapidjson::Value *proot = &doc; ++ if (doc.IsObject() && doc.HasMember("payload") && doc["payload"].IsObject()) ++ proot = &doc["payload"]; ++ ++ THROW_WALLET_EXCEPTION_IF(!proot->IsObject(), error::wallet_internal_error, "Trezor KI JSON: expected JSON object"); ++ const rapidjson::Value &root = *proot; ++ ++ THROW_WALLET_EXCEPTION_IF(!root.HasMember("key_images") || !root["key_images"].IsArray(), ++ error::wallet_internal_error, "Trezor KI JSON: missing or invalid key_images array"); ++ ++ std::string enc_key_bin; ++ if (root.HasMember("signature") && root["signature"].IsString()) ++ { ++ THROW_WALLET_EXCEPTION_IF(!epee::string_tools::parse_hexstr_to_binbuff(root["signature"].GetString(), enc_key_bin), ++ error::wallet_internal_error, "Trezor KI JSON: invalid hex in signature"); ++ } ++ else if (root.HasMember("enc_key") && root["enc_key"].IsString()) ++ { ++ THROW_WALLET_EXCEPTION_IF(!epee::string_tools::parse_hexstr_to_binbuff(root["enc_key"].GetString(), enc_key_bin), ++ error::wallet_internal_error, "Trezor KI JSON: invalid hex in enc_key"); ++ } ++ else ++ { ++ THROW_WALLET_EXCEPTION(error::wallet_internal_error, "Trezor KI JSON: need signature or enc_key (32-byte hex)"); ++ } ++ THROW_WALLET_EXCEPTION_IF(enc_key_bin.size() != 32, ++ error::wallet_internal_error, "Trezor KI JSON: encryption key must decode to 32 bytes"); ++ ++ const rapidjson::Value &arr = root["key_images"]; ++ std::vector> ski; ++ ski.reserve(arr.Size()); ++ ++ for (rapidjson::SizeType i = 0; i < arr.Size(); ++i) ++ { ++ const rapidjson::Value &el = arr[i]; ++ THROW_WALLET_EXCEPTION_IF(!el.IsObject(), error::wallet_internal_error, "Trezor KI JSON: key_images entry must be object"); ++ THROW_WALLET_EXCEPTION_IF(!el.HasMember("iv") || !el["iv"].IsString(), error::wallet_internal_error, "Trezor KI JSON: missing iv"); ++ THROW_WALLET_EXCEPTION_IF(!el.HasMember("key_image") || !el["key_image"].IsString(), error::wallet_internal_error, "Trezor KI JSON: missing key_image"); ++ ++ std::string iv_bin, blob_bin; ++ THROW_WALLET_EXCEPTION_IF(!epee::string_tools::parse_hexstr_to_binbuff(el["iv"].GetString(), iv_bin), ++ error::wallet_internal_error, "Trezor KI JSON: invalid hex in iv"); ++ THROW_WALLET_EXCEPTION_IF(!epee::string_tools::parse_hexstr_to_binbuff(el["key_image"].GetString(), blob_bin), ++ error::wallet_internal_error, "Trezor KI JSON: invalid hex in key_image"); ++ ++ crypto::key_image ki{}; ++ crypto::signature sig{}; ++ decrypt_trezor_exported_ki_blob(blob_bin, iv_bin, enc_key_bin, ki, sig); ++ ski.emplace_back(std::move(ki), std::move(sig)); ++ } ++ ++ return import_key_images(ski, 0, spent, unspent, check_spent); ++} ++//---------------------------------------------------------------------------------------------------- + } +diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h +index 42e1c02d9..7332459ae 100644 +--- a/src/wallet/wallet2.h ++++ b/src/wallet/wallet2.h +@@ -707,8 +707,10 @@ private: + + void commit_tx(pending_tx& ptx_vector); + void commit_tx(std::vector& ptx_vector); ++ void relay_raw_tx(const std::string &tx_as_hex); + bool save_tx(const std::vector& ptx_vector, const std::string &filename) const; + std::string dump_tx_to_str(const std::vector &ptx_vector) const; ++ void construct_unsigned_tx_set_for_signing(const std::vector& ptx_vector, unsigned_tx_set &utx) const; + std::string save_multisig_tx(multisig_tx_set txs); + bool save_multisig_tx(const multisig_tx_set &txs, const std::string &filename); + std::string save_multisig_tx(const std::vector& ptx_vector); +@@ -1281,7 +1283,8 @@ private: + bool is_unattended() const { return m_unattended; } + + std::pair estimate_tx_size_and_weight(bool use_rct, int n_inputs, int ring_size, int n_outputs, size_t extra_size); +- ++ std::string export_trezor_tdis() const; ++ uint64_t import_trezor_encrypted_key_images_json(const std::string &json, uint64_t &spent, uint64_t &unspent, bool check_spent = true); + + /* + * "attributes" are a mechanism to store an arbitrary number of string values +-- +2.54.0 (Apple Git-157) + diff --git a/patches/monero/0022-fix-use-apple-ios-rust-target-when-building-for-iOS.patch b/patches/monero/0022-fix-use-apple-ios-rust-target-when-building-for-iOS.patch new file mode 100644 index 0000000..4f117eb --- /dev/null +++ b/patches/monero/0022-fix-use-apple-ios-rust-target-when-building-for-iOS.patch @@ -0,0 +1,33 @@ +From cce457ba4807db3f0bb3b06dc512131fe0084b5d Mon Sep 17 00:00:00 2001 +From: Czarek Nakamoto +Date: Mon, 27 Jul 2026 14:15:44 +0200 +Subject: [PATCH 22/22] fix: use correct rust target for iOS device and simulator + +--- + src/fcmp_pp/fcmp_pp_rust/CMakeLists.txt | 14 +++++++++++++- + 1 file changed, 13 insertions(+), 1 deletion(-) + +diff --git a/src/fcmp_pp/fcmp_pp_rust/CMakeLists.txt b/src/fcmp_pp/fcmp_pp_rust/CMakeLists.txt +index 3e2229f07..f157bc12f 100644 +--- a/src/fcmp_pp/fcmp_pp_rust/CMakeLists.txt ++++ b/src/fcmp_pp/fcmp_pp_rust/CMakeLists.txt +@@ -44,7 +44,15 @@ if(MINGW) + set(RUST_PLATFORM "pc-windows") + set(RUST_TOOLCHAIN "-gnu") + elseif(APPLE) +- set(RUST_PLATFORM "apple-darwin") ++ if(CMAKE_SYSTEM_NAME STREQUAL "iOS") ++ if(CMAKE_OSX_SYSROOT MATCHES "i[pP]hone[sS]imulator") ++ set(RUST_PLATFORM "apple-ios-sim") ++ else() ++ set(RUST_PLATFORM "apple-ios") ++ endif() ++ else() ++ set(RUST_PLATFORM "apple-darwin") ++ endif() + set(RUST_TOOLCHAIN "") + elseif(FREEBSD) + set(RUST_PLATFORM "unknown-freebsd") +-- +2.54.0 (Apple Git-157) + -- cgit v1.2.3