From 019d5d0b70b688822ca1166236b8e9beba912ef7 Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Wed, 23 Oct 2024 15:24:38 +0200 Subject: fix __clear_cache bug on wownero --- ...et-background-sync-with-just-the-view-key.patch | 46 +- .../wownero/0002-fix-is_trivially_copyable.patch | 32 - ...issing-___clear_cache-when-targetting-iOS.patch | 19 + .../wownero/0003-fix-is_trivially_copyable.patch | 32 + patches/wownero/0003-store-crash-fix.patch | 210 --- patches/wownero/0004-Update-android-ndk.patch | 153 -- patches/wownero/0004-store-crash-fix.patch | 210 +++ patches/wownero/0005-Update-android-ndk.patch | 153 ++ .../0005-uint64_t-missing-definition-fix.patch | 25 - .../0006-uint64_t-missing-definition-fix.patch | 25 + ...006-use-proper-error-handling-in-get_seed.patch | 71 - patches/wownero/0007-UR-functions.patch | 1036 -------------- ...007-use-proper-error-handling-in-get_seed.patch | 71 + patches/wownero/0008-UR-functions.patch | 1036 ++++++++++++++ .../wownero/0008-add-dummy-device-for-ledger.patch | 580 -------- .../wownero/0009-add-dummy-device-for-ledger.patch | 580 ++++++++ patches/wownero/0009-polyseed.patch | 1460 ------------------- patches/wownero/0010-coin-control.patch | 979 ------------- patches/wownero/0010-polyseed.patch | 1464 ++++++++++++++++++++ ...oding-and-tx-key-getter-for-PendingTransc.patch | 68 - patches/wownero/0011-coin-control.patch | 979 +++++++++++++ ...oding-and-tx-key-getter-for-PendingTransc.patch | 68 + ...dd-recoverDeterministicWalletFromSpendKey.patch | 153 -- ...dd-recoverDeterministicWalletFromSpendKey.patch | 153 ++ patches/wownero/0013-build-wownero-seed.patch | 614 -------- patches/wownero/0014-build-wownero-seed.patch | 614 ++++++++ 26 files changed, 5427 insertions(+), 5404 deletions(-) delete mode 100644 patches/wownero/0002-fix-is_trivially_copyable.patch create mode 100644 patches/wownero/0002-fix-missing-___clear_cache-when-targetting-iOS.patch create mode 100644 patches/wownero/0003-fix-is_trivially_copyable.patch delete mode 100644 patches/wownero/0003-store-crash-fix.patch delete mode 100644 patches/wownero/0004-Update-android-ndk.patch create mode 100644 patches/wownero/0004-store-crash-fix.patch create mode 100644 patches/wownero/0005-Update-android-ndk.patch delete mode 100644 patches/wownero/0005-uint64_t-missing-definition-fix.patch create mode 100644 patches/wownero/0006-uint64_t-missing-definition-fix.patch delete mode 100644 patches/wownero/0006-use-proper-error-handling-in-get_seed.patch delete mode 100644 patches/wownero/0007-UR-functions.patch create mode 100644 patches/wownero/0007-use-proper-error-handling-in-get_seed.patch create mode 100644 patches/wownero/0008-UR-functions.patch delete mode 100644 patches/wownero/0008-add-dummy-device-for-ledger.patch create mode 100644 patches/wownero/0009-add-dummy-device-for-ledger.patch delete mode 100644 patches/wownero/0009-polyseed.patch delete mode 100644 patches/wownero/0010-coin-control.patch create mode 100644 patches/wownero/0010-polyseed.patch delete mode 100644 patches/wownero/0011-Add-hex-encoding-and-tx-key-getter-for-PendingTransc.patch create mode 100644 patches/wownero/0011-coin-control.patch create mode 100644 patches/wownero/0012-Add-hex-encoding-and-tx-key-getter-for-PendingTransc.patch delete mode 100644 patches/wownero/0012-Add-recoverDeterministicWalletFromSpendKey.patch create mode 100644 patches/wownero/0013-Add-recoverDeterministicWalletFromSpendKey.patch delete mode 100644 patches/wownero/0013-build-wownero-seed.patch create mode 100644 patches/wownero/0014-build-wownero-seed.patch diff --git a/patches/wownero/0001-wallet-background-sync-with-just-the-view-key.patch b/patches/wownero/0001-wallet-background-sync-with-just-the-view-key.patch index dd62d89..0548326 100644 --- a/patches/wownero/0001-wallet-background-sync-with-just-the-view-key.patch +++ b/patches/wownero/0001-wallet-background-sync-with-just-the-view-key.patch @@ -1,7 +1,7 @@ -From e70a2627ce34ce974f26c946801aaf7d9d794bd0 Mon Sep 17 00:00:00 2001 +From 561c17511802188e7de3d37b696adab2cd1c63fe Mon Sep 17 00:00:00 2001 From: j-berman Date: Thu, 13 Oct 2022 18:33:33 -0700 -Subject: [PATCH 01/13] wallet: background sync with just the view key +Subject: [PATCH 01/14] wallet: background sync with just the view key - When background syncing, the wallet wipes the spend key from memory and processes all new transactions. The wallet saves @@ -50,7 +50,7 @@ cache. 20 files changed, 2337 insertions(+), 130 deletions(-) diff --git a/src/cryptonote_basic/account.cpp b/src/cryptonote_basic/account.cpp -index 2ac455fda..4e87d4477 100644 +index 2ac455f..4e87d44 100644 --- a/src/cryptonote_basic/account.cpp +++ b/src/cryptonote_basic/account.cpp @@ -152,6 +152,17 @@ DISABLE_VS_WARNINGS(4244 4345) @@ -72,7 +72,7 @@ index 2ac455fda..4e87d4477 100644 { crypto::secret_key first = generate_keys(m_keys.m_account_address.m_spend_public_key, m_keys.m_spend_secret_key, recovery_key, recover); diff --git a/src/cryptonote_basic/account.h b/src/cryptonote_basic/account.h -index 2ee9545d4..93d1d28f0 100644 +index 2ee9545..93d1d28 100644 --- a/src/cryptonote_basic/account.h +++ b/src/cryptonote_basic/account.h @@ -95,6 +95,7 @@ namespace cryptonote @@ -84,7 +84,7 @@ index 2ee9545d4..93d1d28f0 100644 void encrypt_keys(const crypto::chacha_key &key) { m_keys.encrypt(key); } diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h -index 8c0d3ce20..8b5091a46 100644 +index 8c0d3ce..8b5091a 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -253,6 +253,8 @@ namespace config @@ -97,7 +97,7 @@ index 8c0d3ce20..8b5091a46 100644 const unsigned char HASH_KEY_MEMORY = 'k'; const unsigned char HASH_KEY_MULTISIG[] = {'M', 'u', 'l', 't' , 'i', 's', 'i', 'g', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp -index 462c06f0e..8c5122097 100644 +index 462c06f..8c51220 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -155,6 +155,17 @@ typedef cryptonote::simple_wallet sw; @@ -722,7 +722,7 @@ index 462c06f0e..8c5122097 100644 { PRINT_USAGE(USAGE_IMPORT_OUTPUTS); diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h -index d641dca1b..b98a40713 100644 +index d641dca..b98a407 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -147,6 +147,7 @@ namespace cryptonote @@ -734,7 +734,7 @@ index d641dca1b..b98a40713 100644 bool set_inactivity_lock_timeout(const std::vector &args = std::vector()); bool set_setup_background_mining(const std::vector &args = std::vector()); diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp -index e81265ad3..e868fa039 100644 +index e81265a..e868fa0 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -54,6 +54,40 @@ using namespace cryptonote; @@ -1199,7 +1199,7 @@ index e81265ad3..e868fa039 100644 setStatusError(tr("Rescan spent can only be used with a trusted daemon")); return false; diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h -index ec2d7e9b3..1f199a72c 100644 +index ec2d7e9..1f199a7 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -171,6 +171,13 @@ public: @@ -1236,7 +1236,7 @@ index ec2d7e9b3..1f199a72c 100644 std::unique_ptr m_history; std::unique_ptr m_wallet2Callback; diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h -index 71991df0d..e349df176 100644 +index 71991df..e349df1 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h @@ -445,6 +445,12 @@ struct Wallet @@ -1296,7 +1296,7 @@ index 71991df0d..e349df176 100644 virtual AddressBook * addressBook() = 0; virtual Subaddress * subaddress() = 0; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp -index 30b3502dc..8ce515c57 100644 +index 30b3502..8ce515c 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -157,6 +157,8 @@ static const std::string MULTISIG_SIGNATURE_MAGIC = "SigMultisigPkV1"; @@ -2740,7 +2740,7 @@ index 30b3502dc..8ce515c57 100644 { payment_container payments; diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h -index 75137237b..295976b53 100644 +index 7513723..295976b 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -256,6 +256,20 @@ private: @@ -3050,7 +3050,7 @@ index 75137237b..295976b53 100644 } diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h -index c077313d4..c54cd3499 100644 +index c077313..c54cd34 100644 --- a/src/wallet/wallet_errors.h +++ b/src/wallet/wallet_errors.h @@ -63,6 +63,7 @@ namespace tools @@ -3121,7 +3121,7 @@ index c077313d4..c54cd3499 100644 #if !defined(_MSC_VER) diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp -index 6fc996299..3cc6b278f 100644 +index 6fc9962..3cc6b27 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -73,6 +73,54 @@ using namespace epee; @@ -3567,7 +3567,7 @@ index 6fc996299..3cc6b278f 100644 try { diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h -index 3308d1751..c2329aafe 100644 +index 3308d17..c2329aa 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -160,6 +160,9 @@ namespace tools @@ -3591,7 +3591,7 @@ index 3308d1751..c2329aafe 100644 //json rpc v2 bool on_query_key(const wallet_rpc::COMMAND_RPC_QUERY_KEY::request& req, wallet_rpc::COMMAND_RPC_QUERY_KEY::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h -index b6098d95c..a44b56ed6 100644 +index b6098d9..a44b56e 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -2696,5 +2696,69 @@ namespace wallet_rpc @@ -3665,7 +3665,7 @@ index b6098d95c..a44b56ed6 100644 } } diff --git a/src/wallet/wallet_rpc_server_error_codes.h b/src/wallet/wallet_rpc_server_error_codes.h -index 541d29f86..4756c191c 100644 +index 541d29f..4756c19 100644 --- a/src/wallet/wallet_rpc_server_error_codes.h +++ b/src/wallet/wallet_rpc_server_error_codes.h @@ -81,3 +81,5 @@ @@ -3675,7 +3675,7 @@ index 541d29f86..4756c191c 100644 +#define WALLET_RPC_ERROR_CODE_IS_BACKGROUND_WALLET -51 +#define WALLET_RPC_ERROR_CODE_IS_BACKGROUND_SYNCING -52 diff --git a/tests/functional_tests/transfer.py b/tests/functional_tests/transfer.py -index 4063911f4..60eb09a10 100755 +index 4063911..60eb09a 100755 --- a/tests/functional_tests/transfer.py +++ b/tests/functional_tests/transfer.py @@ -30,6 +30,7 @@ @@ -4113,7 +4113,7 @@ index 4063911f4..60eb09a10 100755 if __name__ == '__main__': TransferTest().run_test() diff --git a/tests/functional_tests/util_resources.py b/tests/functional_tests/util_resources.py -index e030312da..3ca6fdb86 100755 +index e030312..3ca6fdb 100755 --- a/tests/functional_tests/util_resources.py +++ b/tests/functional_tests/util_resources.py @@ -37,6 +37,8 @@ @@ -4153,7 +4153,7 @@ index e030312da..3ca6fdb86 100755 + assert WALLET_DIRECTORY != '' + return os.path.isfile(WALLET_DIRECTORY + '/' + name) diff --git a/tests/functional_tests/wallet.py b/tests/functional_tests/wallet.py -index 1ad05c98f..8182cecb2 100755 +index 1ad05c9..8182cec 100755 --- a/tests/functional_tests/wallet.py +++ b/tests/functional_tests/wallet.py @@ -34,8 +34,7 @@ @@ -4244,7 +4244,7 @@ index 1ad05c98f..8182cecb2 100755 if __name__ == '__main__': diff --git a/tests/unit_tests/wipeable_string.cpp b/tests/unit_tests/wipeable_string.cpp -index ef6964f9e..25121a02e 100644 +index ef6964f..25121a0 100644 --- a/tests/unit_tests/wipeable_string.cpp +++ b/tests/unit_tests/wipeable_string.cpp @@ -211,3 +211,15 @@ TEST(wipeable_string, to_hex) @@ -4264,7 +4264,7 @@ index ef6964f9e..25121a02e 100644 + ASSERT_TRUE(str == std::string("foo")); +} diff --git a/utils/python-rpc/framework/wallet.py b/utils/python-rpc/framework/wallet.py -index 1e10e1f86..bff33a561 100644 +index 1e10e1f..bff33a5 100644 --- a/utils/python-rpc/framework/wallet.py +++ b/utils/python-rpc/framework/wallet.py @@ -1138,3 +1138,45 @@ class Wallet(object): @@ -4314,5 +4314,5 @@ index 1e10e1f86..bff33a561 100644 + } + return self.rpc.send_json_rpc_request(stop_background_sync) -- -2.43.0 +2.39.5 (Apple Git-154) diff --git a/patches/wownero/0002-fix-is_trivially_copyable.patch b/patches/wownero/0002-fix-is_trivially_copyable.patch deleted file mode 100644 index 8cfb64b..0000000 --- a/patches/wownero/0002-fix-is_trivially_copyable.patch +++ /dev/null @@ -1,32 +0,0 @@ -From bce1cdd45be4132599488ea301dd7922f10ed87f Mon Sep 17 00:00:00 2001 -From: cyan -Date: Tue, 22 Oct 2024 10:23:18 +0000 -Subject: [PATCH 02/13] fix is_trivially_copyable - ---- - contrib/epee/include/span.h | 2 -- - 1 file changed, 2 deletions(-) - -diff --git a/contrib/epee/include/span.h b/contrib/epee/include/span.h -index 01dc387d6..5e3af4d11 100644 ---- a/contrib/epee/include/span.h -+++ b/contrib/epee/include/span.h -@@ -151,7 +151,6 @@ namespace epee - using value_type = typename T::value_type; - static_assert(!std::is_empty(), "empty value types will not work -> sizeof == 1"); - static_assert(std::is_standard_layout(), "value type must have standard layout"); -- static_assert(std::is_trivially_copyable(), "value type must be trivially copyable"); - static_assert(alignof(value_type) == 1, "value type may have padding"); - return {reinterpret_cast(src.data()), src.size() * sizeof(value_type)}; - } -@@ -162,7 +161,6 @@ namespace epee - { - static_assert(!std::is_empty(), "empty types will not work -> sizeof == 1"); - static_assert(std::is_standard_layout(), "type must have standard layout"); -- static_assert(std::is_trivially_copyable(), "type must be trivially copyable"); - static_assert(alignof(T) == 1, "type may have padding"); - return {reinterpret_cast(std::addressof(src)), sizeof(T)}; - } --- -2.43.0 - diff --git a/patches/wownero/0002-fix-missing-___clear_cache-when-targetting-iOS.patch b/patches/wownero/0002-fix-missing-___clear_cache-when-targetting-iOS.patch new file mode 100644 index 0000000..8e71764 --- /dev/null +++ b/patches/wownero/0002-fix-missing-___clear_cache-when-targetting-iOS.patch @@ -0,0 +1,19 @@ +From 25c6f31dfea1a4940efee80789f63a7fe41854aa Mon Sep 17 00:00:00 2001 +From: Czarek Nakamoto +Date: Wed, 23 Oct 2024 15:18:21 +0200 +Subject: [PATCH 02/14] fix missing ___clear_cache when targetting iOS + +--- + external/randomwow | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/external/randomwow b/external/randomwow +index 27b099b..057f5f8 160000 +--- a/external/randomwow ++++ b/external/randomwow +@@ -1 +1 @@ +-Subproject commit 27b099b6dd6fef6e17f58c6dfe00009e9c5df587 ++Subproject commit 057f5f800186f821d87844d15930275186d8e736 +-- +2.39.5 (Apple Git-154) + diff --git a/patches/wownero/0003-fix-is_trivially_copyable.patch b/patches/wownero/0003-fix-is_trivially_copyable.patch new file mode 100644 index 0000000..1a86ed6 --- /dev/null +++ b/patches/wownero/0003-fix-is_trivially_copyable.patch @@ -0,0 +1,32 @@ +From 4f88afa6f7fb6fadd1df44148e0a8f2d82d1fe87 Mon Sep 17 00:00:00 2001 +From: cyan +Date: Tue, 22 Oct 2024 10:23:18 +0000 +Subject: [PATCH 03/14] fix is_trivially_copyable + +--- + contrib/epee/include/span.h | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/contrib/epee/include/span.h b/contrib/epee/include/span.h +index 01dc387..5e3af4d 100644 +--- a/contrib/epee/include/span.h ++++ b/contrib/epee/include/span.h +@@ -151,7 +151,6 @@ namespace epee + using value_type = typename T::value_type; + static_assert(!std::is_empty(), "empty value types will not work -> sizeof == 1"); + static_assert(std::is_standard_layout(), "value type must have standard layout"); +- static_assert(std::is_trivially_copyable(), "value type must be trivially copyable"); + static_assert(alignof(value_type) == 1, "value type may have padding"); + return {reinterpret_cast(src.data()), src.size() * sizeof(value_type)}; + } +@@ -162,7 +161,6 @@ namespace epee + { + static_assert(!std::is_empty(), "empty types will not work -> sizeof == 1"); + static_assert(std::is_standard_layout(), "type must have standard layout"); +- static_assert(std::is_trivially_copyable(), "type must be trivially copyable"); + static_assert(alignof(T) == 1, "type may have padding"); + return {reinterpret_cast(std::addressof(src)), sizeof(T)}; + } +-- +2.39.5 (Apple Git-154) + diff --git a/patches/wownero/0003-store-crash-fix.patch b/patches/wownero/0003-store-crash-fix.patch deleted file mode 100644 index 474ec1d..0000000 --- a/patches/wownero/0003-store-crash-fix.patch +++ /dev/null @@ -1,210 +0,0 @@ -From c55999882a406d73ba075c28cd225a4d006b4a0d Mon Sep 17 00:00:00 2001 -From: Czarek Nakamoto -Date: Sat, 11 May 2024 16:25:10 +0200 -Subject: [PATCH 03/13] 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 | 25 ++++++++++++------------- - src/wallet/api/wallet.h | 1 - - src/wallet/wallet2.cpp | 12 +++++++++++- - src/wallet/wallet2.h | 3 +++ - 4 files changed, 26 insertions(+), 15 deletions(-) - -diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp -index e868fa039..899ef044a 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); \ -@@ -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)); -@@ -962,6 +962,7 @@ void WalletImpl::stop() - bool WalletImpl::store(const std::string &path) - { - clearStatus(); -+ LOCK_REFRESH(); - try { - if (path.empty()) { - m_wallet->store(); -@@ -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(); - } -@@ -2481,12 +2482,12 @@ void WalletImpl::doRefresh() - } - m_wallet->find_and_save_rings(false); - } else { -- LOG_PRINT_L3(__FUNCTION__ << ": skipping refresh - daemon is not synced"); -+ LOG_PRINT_L3(__FUNCTION__ << ": skipping refresh - daemon is not synced"); - } - } 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(); -@@ -2496,9 +2497,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(); - } - } -@@ -2508,7 +2509,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(); -@@ -2519,9 +2520,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); - } - - -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 8ce515c57..651161d14 100644 ---- a/src/wallet/wallet2.cpp -+++ b/src/wallet/wallet2.cpp -@@ -1192,6 +1192,7 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended, std - 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), -@@ -1404,6 +1405,14 @@ bool wallet2::set_daemon(std::string daemon_address, boost::optionalset_proxy(address); -@@ -4098,8 +4107,9 @@ 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) - { -+ LOG_ERROR("SYNCING"); - uint64_t next_blocks_start_height; - std::vector next_blocks; - std::vector next_parsed_blocks; -diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h -index 295976b53..c07a47c11 100644 ---- a/src/wallet/wallet2.h -+++ b/src/wallet/wallet2.h -@@ -1071,6 +1071,8 @@ private: - boost::optional daemon_login = boost::none, bool trusted_daemon = true, - epee::net_utils::ssl_options_t ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_autodetect); - bool set_proxy(const std::string &address); -+ bool get_refresh_enabled(); -+ void set_refresh_enabled(bool val); - - void stop() { m_run.store(false, std::memory_order_relaxed); m_message_store.stop(); } - -@@ -1981,6 +1983,7 @@ private: - - 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.43.0 - diff --git a/patches/wownero/0004-Update-android-ndk.patch b/patches/wownero/0004-Update-android-ndk.patch deleted file mode 100644 index 2099c42..0000000 --- a/patches/wownero/0004-Update-android-ndk.patch +++ /dev/null @@ -1,153 +0,0 @@ -From 5bd52d7bf4626ab6bb2b301494465afe0d2e18c9 Mon Sep 17 00:00:00 2001 -From: Czarek Nakamoto -Date: Thu, 23 May 2024 08:02:49 +0200 -Subject: [PATCH 04/13] Update android ndk - -rename arm -> armv7a ---- - contrib/depends/hosts/android.mk | 18 ++++++++++++---- - contrib/depends/packages/android_ndk.mk | 28 ++++++++++++++++++------- - contrib/depends/packages/boost.mk | 1 + - contrib/depends/packages/openssl.mk | 2 +- - contrib/depends/toolchain.cmake.in | 13 ++++++------ - 5 files changed, 44 insertions(+), 18 deletions(-) - -diff --git a/contrib/depends/hosts/android.mk b/contrib/depends/hosts/android.mk -index d6f8b99dd..827103c36 100644 ---- a/contrib/depends/hosts/android.mk -+++ b/contrib/depends/hosts/android.mk -@@ -1,12 +1,22 @@ - ANDROID_API=21 -- --ifeq ($(host_arch),arm) --host_toolchain=arm-linux-androideabi- -+host_toolchain=nonexistent -+ifeq ($(host_arch),armv7a) -+host_toolchain=armv7a-linux-androideabi${ANDROID_API}- -+endif -+ifeq ($(host_arch),x86_64) -+host_toolchain=x86_64-linux-android${ANDROID_API}- -+endif -+ifeq ($(host_arch),i686) -+host_toolchain=i686-linux-android${ANDROID_API}- -+endif -+ifeq ($(host_arch),aarch64) -+host_toolchain=aarch64-linux-android${ANDROID_API}- - endif - - android_CC=$(host_toolchain)clang - android_CXX=$(host_toolchain)clang++ --android_RANLIB=: -+android_RANLIB=llvm-ranlib -+android_AR=llvm-ar - - android_CFLAGS=-pipe - android_CXXFLAGS=$(android_CFLAGS) -diff --git a/contrib/depends/packages/android_ndk.mk b/contrib/depends/packages/android_ndk.mk -index 9b8a5332f..2c2914ec2 100644 ---- a/contrib/depends/packages/android_ndk.mk -+++ b/contrib/depends/packages/android_ndk.mk -@@ -1,12 +1,16 @@ - package=android_ndk --$(package)_version=17b -+$(package)_version=26d - $(package)_download_path=https://dl.google.com/android/repository/ --$(package)_file_name=android-ndk-r$($(package)_version)-linux-x86_64.zip --$(package)_sha256_hash=5dfbbdc2d3ba859fed90d0e978af87c71a91a5be1f6e1c40ba697503d48ccecd -+$(package)_file_name=android-ndk-r$($(package)_version)-linux.zip -+$(package)_sha256_hash=eefeafe7ccf177de7cc57158da585e7af119bb7504a63604ad719e4b2a328b54 -+ -+$(package)_version_apiversion=21 - - define $(package)_set_vars --$(package)_config_opts_arm=--arch arm -+$(package)_config_opts_armv7a=--arch arm - $(package)_config_opts_aarch64=--arch arm64 -+$(package)_config_opts_x86_64=--arch x86_64 -+$(package)_config_opts_i686=--arch x86 - endef - - define $(package)_extract_cmds -@@ -14,9 +18,19 @@ define $(package)_extract_cmds - unzip -q $($(1)_source_dir)/$($(package)_file_name) - endef - -+# arm-linux-androideabi-ar - openssl workaround -+ - define $(package)_stage_cmds -- android-ndk-r$($(package)_version)/build/tools/make_standalone_toolchain.py --api 21 \ -- --install-dir $(build_prefix) --stl=libc++ $($(package)_config_opts) &&\ -- mv $(build_prefix) $($(package)_staging_dir)/$(host_prefix) -+ mkdir -p $(build_prefix) &&\ -+ echo $(build_prefix)/toolchain && \ -+ android-ndk-r$($(package)_version)/build/tools/make_standalone_toolchain.py --api $($(package)_version_apiversion) \ -+ --install-dir $(build_prefix)/toolchain --stl=libc++ $($(package)_config_opts) &&\ -+ mv $(build_prefix)/toolchain $($(package)_staging_dir)/$(host_prefix)/native && \ -+ cp $($(package)_staging_dir)/$(host_prefix)/native/bin/llvm-ar $($(package)_staging_dir)/$(host_prefix)/native/bin/$(host)$($(package)_version_apiversion)-ar &&\ -+ cp $($(package)_staging_dir)/$(host_prefix)/native/bin/llvm-ar $($(package)_staging_dir)/$(host_prefix)/native/bin/arm-linux-androideabi-ar &&\ -+ cp $($(package)_staging_dir)/$(host_prefix)/native/bin/llvm-ranlib $($(package)_staging_dir)/$(host_prefix)/native/bin/$(host)$($(package)_version_apiversion)-ranlib &&\ -+ cp $($(package)_staging_dir)/$(host_prefix)/native/bin/llvm-ranlib $($(package)_staging_dir)/$(host_prefix)/native/bin/arm-linux-androideabi-ranlib &&\ -+ cp $($(package)_staging_dir)/$(host_prefix)/native/bin/llvm-ar $($(package)_staging_dir)/$(host_prefix)/native/bin/$(host)-ar &&\ -+ cp $($(package)_staging_dir)/$(host_prefix)/native/bin/llvm-ranlib $($(package)_staging_dir)/$(host_prefix)/native/bin/$(host)-ranlib - endef - -diff --git a/contrib/depends/packages/boost.mk b/contrib/depends/packages/boost.mk -index fd06c5393..c17e863cc 100644 ---- a/contrib/depends/packages/boost.mk -+++ b/contrib/depends/packages/boost.mk -@@ -25,6 +25,7 @@ $(package)_archiver_darwin=$($(package)_libtool) - $(package)_config_libraries=chrono,filesystem,program_options,system,thread,test,date_time,regex,serialization,locale - $(package)_cxxflags=-std=c++11 - $(package)_cxxflags_linux=-fPIC -+$(package)_cxxflags_android=-fPIC - $(package)_cxxflags_freebsd=-fPIC - endef - -diff --git a/contrib/depends/packages/openssl.mk b/contrib/depends/packages/openssl.mk -index a157762c7..2430f6495 100644 ---- a/contrib/depends/packages/openssl.mk -+++ b/contrib/depends/packages/openssl.mk -@@ -34,7 +34,7 @@ $(package)_config_opts_x86_64_linux=linux-x86_64 - $(package)_config_opts_i686_linux=linux-generic32 - $(package)_config_opts_arm_linux=linux-generic32 - $(package)_config_opts_aarch64_linux=linux-generic64 --$(package)_config_opts_arm_android=--static android-arm -+$(package)_config_opts_armv7a_android=--static android-arm - $(package)_config_opts_aarch64_android=--static android-arm64 - $(package)_config_opts_aarch64_darwin=darwin64-arm64-cc - $(package)_config_opts_riscv64_linux=linux-generic64 -diff --git a/contrib/depends/toolchain.cmake.in b/contrib/depends/toolchain.cmake.in -index f118c754e..cc1d9b5c5 100644 ---- a/contrib/depends/toolchain.cmake.in -+++ b/contrib/depends/toolchain.cmake.in -@@ -100,20 +100,21 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") - SET(LLVM_ENABLE_PIE OFF) - elseif(CMAKE_SYSTEM_NAME STREQUAL "Android") - SET(ANDROID TRUE) -- if(ARCHITECTURE STREQUAL "arm") -+ if(ARCHITECTURE STREQUAL "armv7a") - SET(CMAKE_ANDROID_ARCH_ABI "armeabi-v7a") - SET(CMAKE_SYSTEM_PROCESSOR "armv7-a") - SET(CMAKE_ANDROID_ARM_MODE ON) -- SET(CMAKE_C_COMPILER_TARGET arm-linux-androideabi) -- SET(CMAKE_CXX_COMPILER_TARGET arm-linux-androideabi) -- SET(_CMAKE_TOOLCHAIN_PREFIX arm-linux-androideabi-) -+ SET(CMAKE_C_COMPILER_TARGET armv7a-linux-androideabi21) -+ SET(CMAKE_CXX_COMPILER_TARGET armv7a-linux-androideabi21) -+ SET(_CMAKE_TOOLCHAIN_PREFIX armv7a-linux-androideabi21-) - elseif(ARCHITECTURE STREQUAL "aarch64") - SET(CMAKE_ANDROID_ARCH_ABI "arm64-v8a") - SET(CMAKE_SYSTEM_PROCESSOR "aarch64") - endif() - SET(CMAKE_ANDROID_STANDALONE_TOOLCHAIN @prefix@/native) -- SET(CMAKE_C_COMPILER "${_CMAKE_TOOLCHAIN_PREFIX}clang") -- SET(CMAKE_CXX_COMPILER "${_CMAKE_TOOLCHAIN_PREFIX}clang++") -+ SET(_ANDROID_STANDALONE_TOOLCHAIN_API 21) -+ SET(CMAKE_C_COMPILER @CC@) -+ SET(CMAKE_CXX_COMPILER @CXX@) - else() - SET(CMAKE_C_COMPILER @CC@) - SET(CMAKE_CXX_COMPILER @CXX@) --- -2.43.0 - diff --git a/patches/wownero/0004-store-crash-fix.patch b/patches/wownero/0004-store-crash-fix.patch new file mode 100644 index 0000000..f04c98a --- /dev/null +++ b/patches/wownero/0004-store-crash-fix.patch @@ -0,0 +1,210 @@ +From 149be435f556ef1163abbf82b6b54872dc74025b Mon Sep 17 00:00:00 2001 +From: Czarek Nakamoto +Date: Sat, 11 May 2024 16:25:10 +0200 +Subject: [PATCH 04/14] 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 | 25 ++++++++++++------------- + src/wallet/api/wallet.h | 1 - + src/wallet/wallet2.cpp | 12 +++++++++++- + src/wallet/wallet2.h | 3 +++ + 4 files changed, 26 insertions(+), 15 deletions(-) + +diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp +index e868fa0..899ef04 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); \ +@@ -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)); +@@ -962,6 +962,7 @@ void WalletImpl::stop() + bool WalletImpl::store(const std::string &path) + { + clearStatus(); ++ LOCK_REFRESH(); + try { + if (path.empty()) { + m_wallet->store(); +@@ -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(); + } +@@ -2481,12 +2482,12 @@ void WalletImpl::doRefresh() + } + m_wallet->find_and_save_rings(false); + } else { +- LOG_PRINT_L3(__FUNCTION__ << ": skipping refresh - daemon is not synced"); ++ LOG_PRINT_L3(__FUNCTION__ << ": skipping refresh - daemon is not synced"); + } + } 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(); +@@ -2496,9 +2497,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(); + } + } +@@ -2508,7 +2509,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(); +@@ -2519,9 +2520,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); + } + + +diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h +index 1f199a7..ac7ce2f 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 8ce515c..651161d 100644 +--- a/src/wallet/wallet2.cpp ++++ b/src/wallet/wallet2.cpp +@@ -1192,6 +1192,7 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended, std + 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), +@@ -1404,6 +1405,14 @@ bool wallet2::set_daemon(std::string daemon_address, boost::optionalset_proxy(address); +@@ -4098,8 +4107,9 @@ 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) + { ++ LOG_ERROR("SYNCING"); + uint64_t next_blocks_start_height; + std::vector next_blocks; + std::vector next_parsed_blocks; +diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h +index 295976b..c07a47c 100644 +--- a/src/wallet/wallet2.h ++++ b/src/wallet/wallet2.h +@@ -1071,6 +1071,8 @@ private: + boost::optional daemon_login = boost::none, bool trusted_daemon = true, + epee::net_utils::ssl_options_t ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_autodetect); + bool set_proxy(const std::string &address); ++ bool get_refresh_enabled(); ++ void set_refresh_enabled(bool val); + + void stop() { m_run.store(false, std::memory_order_relaxed); m_message_store.stop(); } + +@@ -1981,6 +1983,7 @@ private: + + 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.39.5 (Apple Git-154) + diff --git a/patches/wownero/0005-Update-android-ndk.patch b/patches/wownero/0005-Update-android-ndk.patch new file mode 100644 index 0000000..57b5ffb --- /dev/null +++ b/patches/wownero/0005-Update-android-ndk.patch @@ -0,0 +1,153 @@ +From 6cc0e6fdccfd48065a03a741f65258f563a9900f Mon Sep 17 00:00:00 2001 +From: Czarek Nakamoto +Date: Thu, 23 May 2024 08:02:49 +0200 +Subject: [PATCH 05/14] Update android ndk + +rename arm -> armv7a +--- + contrib/depends/hosts/android.mk | 18 ++++++++++++---- + contrib/depends/packages/android_ndk.mk | 28 ++++++++++++++++++------- + contrib/depends/packages/boost.mk | 1 + + contrib/depends/packages/openssl.mk | 2 +- + contrib/depends/toolchain.cmake.in | 13 ++++++------ + 5 files changed, 44 insertions(+), 18 deletions(-) + +diff --git a/contrib/depends/hosts/android.mk b/contrib/depends/hosts/android.mk +index d6f8b99..827103c 100644 +--- a/contrib/depends/hosts/android.mk ++++ b/contrib/depends/hosts/android.mk +@@ -1,12 +1,22 @@ + ANDROID_API=21 +- +-ifeq ($(host_arch),arm) +-host_toolchain=arm-linux-androideabi- ++host_toolchain=nonexistent ++ifeq ($(host_arch),armv7a) ++host_toolchain=armv7a-linux-androideabi${ANDROID_API}- ++endif ++ifeq ($(host_arch),x86_64) ++host_toolchain=x86_64-linux-android${ANDROID_API}- ++endif ++ifeq ($(host_arch),i686) ++host_toolchain=i686-linux-android${ANDROID_API}- ++endif ++ifeq ($(host_arch),aarch64) ++host_toolchain=aarch64-linux-android${ANDROID_API}- + endif + + android_CC=$(host_toolchain)clang + android_CXX=$(host_toolchain)clang++ +-android_RANLIB=: ++android_RANLIB=llvm-ranlib ++android_AR=llvm-ar + + android_CFLAGS=-pipe + android_CXXFLAGS=$(android_CFLAGS) +diff --git a/contrib/depends/packages/android_ndk.mk b/contrib/depends/packages/android_ndk.mk +index 9b8a533..2c2914e 100644 +--- a/contrib/depends/packages/android_ndk.mk ++++ b/contrib/depends/packages/android_ndk.mk +@@ -1,12 +1,16 @@ + package=android_ndk +-$(package)_version=17b ++$(package)_version=26d + $(package)_download_path=https://dl.google.com/android/repository/ +-$(package)_file_name=android-ndk-r$($(package)_version)-linux-x86_64.zip +-$(package)_sha256_hash=5dfbbdc2d3ba859fed90d0e978af87c71a91a5be1f6e1c40ba697503d48ccecd ++$(package)_file_name=android-ndk-r$($(package)_version)-linux.zip ++$(package)_sha256_hash=eefeafe7ccf177de7cc57158da585e7af119bb7504a63604ad719e4b2a328b54 ++ ++$(package)_version_apiversion=21 + + define $(package)_set_vars +-$(package)_config_opts_arm=--arch arm ++$(package)_config_opts_armv7a=--arch arm + $(package)_config_opts_aarch64=--arch arm64 ++$(package)_config_opts_x86_64=--arch x86_64 ++$(package)_config_opts_i686=--arch x86 + endef + + define $(package)_extract_cmds +@@ -14,9 +18,19 @@ define $(package)_extract_cmds + unzip -q $($(1)_source_dir)/$($(package)_file_name) + endef + ++# arm-linux-androideabi-ar - openssl workaround ++ + define $(package)_stage_cmds +- android-ndk-r$($(package)_version)/build/tools/make_standalone_toolchain.py --api 21 \ +- --install-dir $(build_prefix) --stl=libc++ $($(package)_config_opts) &&\ +- mv $(build_prefix) $($(package)_staging_dir)/$(host_prefix) ++ mkdir -p $(build_prefix) &&\ ++ echo $(build_prefix)/toolchain && \ ++ android-ndk-r$($(package)_version)/build/tools/make_standalone_toolchain.py --api $($(package)_version_apiversion) \ ++ --install-dir $(build_prefix)/toolchain --stl=libc++ $($(package)_config_opts) &&\ ++ mv $(build_prefix)/toolchain $($(package)_staging_dir)/$(host_prefix)/native && \ ++ cp $($(package)_staging_dir)/$(host_prefix)/native/bin/llvm-ar $($(package)_staging_dir)/$(host_prefix)/native/bin/$(host)$($(package)_version_apiversion)-ar &&\ ++ cp $($(package)_staging_dir)/$(host_prefix)/native/bin/llvm-ar $($(package)_staging_dir)/$(host_prefix)/native/bin/arm-linux-androideabi-ar &&\ ++ cp $($(package)_staging_dir)/$(host_prefix)/native/bin/llvm-ranlib $($(package)_staging_dir)/$(host_prefix)/native/bin/$(host)$($(package)_version_apiversion)-ranlib &&\ ++ cp $($(package)_staging_dir)/$(host_prefix)/native/bin/llvm-ranlib $($(package)_staging_dir)/$(host_prefix)/native/bin/arm-linux-androideabi-ranlib &&\ ++ cp $($(package)_staging_dir)/$(host_prefix)/native/bin/llvm-ar $($(package)_staging_dir)/$(host_prefix)/native/bin/$(host)-ar &&\ ++ cp $($(package)_staging_dir)/$(host_prefix)/native/bin/llvm-ranlib $($(package)_staging_dir)/$(host_prefix)/native/bin/$(host)-ranlib + endef + +diff --git a/contrib/depends/packages/boost.mk b/contrib/depends/packages/boost.mk +index fd06c53..c17e863 100644 +--- a/contrib/depends/packages/boost.mk ++++ b/contrib/depends/packages/boost.mk +@@ -25,6 +25,7 @@ $(package)_archiver_darwin=$($(package)_libtool) + $(package)_config_libraries=chrono,filesystem,program_options,system,thread,test,date_time,regex,serialization,locale + $(package)_cxxflags=-std=c++11 + $(package)_cxxflags_linux=-fPIC ++$(package)_cxxflags_android=-fPIC + $(package)_cxxflags_freebsd=-fPIC + endef + +diff --git a/contrib/depends/packages/openssl.mk b/contrib/depends/packages/openssl.mk +index a157762..2430f64 100644 +--- a/contrib/depends/packages/openssl.mk ++++ b/contrib/depends/packages/openssl.mk +@@ -34,7 +34,7 @@ $(package)_config_opts_x86_64_linux=linux-x86_64 + $(package)_config_opts_i686_linux=linux-generic32 + $(package)_config_opts_arm_linux=linux-generic32 + $(package)_config_opts_aarch64_linux=linux-generic64 +-$(package)_config_opts_arm_android=--static android-arm ++$(package)_config_opts_armv7a_android=--static android-arm + $(package)_config_opts_aarch64_android=--static android-arm64 + $(package)_config_opts_aarch64_darwin=darwin64-arm64-cc + $(package)_config_opts_riscv64_linux=linux-generic64 +diff --git a/contrib/depends/toolchain.cmake.in b/contrib/depends/toolchain.cmake.in +index f118c75..cc1d9b5 100644 +--- a/contrib/depends/toolchain.cmake.in ++++ b/contrib/depends/toolchain.cmake.in +@@ -100,20 +100,21 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + SET(LLVM_ENABLE_PIE OFF) + elseif(CMAKE_SYSTEM_NAME STREQUAL "Android") + SET(ANDROID TRUE) +- if(ARCHITECTURE STREQUAL "arm") ++ if(ARCHITECTURE STREQUAL "armv7a") + SET(CMAKE_ANDROID_ARCH_ABI "armeabi-v7a") + SET(CMAKE_SYSTEM_PROCESSOR "armv7-a") + SET(CMAKE_ANDROID_ARM_MODE ON) +- SET(CMAKE_C_COMPILER_TARGET arm-linux-androideabi) +- SET(CMAKE_CXX_COMPILER_TARGET arm-linux-androideabi) +- SET(_CMAKE_TOOLCHAIN_PREFIX arm-linux-androideabi-) ++ SET(CMAKE_C_COMPILER_TARGET armv7a-linux-androideabi21) ++ SET(CMAKE_CXX_COMPILER_TARGET armv7a-linux-androideabi21) ++ SET(_CMAKE_TOOLCHAIN_PREFIX armv7a-linux-androideabi21-) + elseif(ARCHITECTURE STREQUAL "aarch64") + SET(CMAKE_ANDROID_ARCH_ABI "arm64-v8a") + SET(CMAKE_SYSTEM_PROCESSOR "aarch64") + endif() + SET(CMAKE_ANDROID_STANDALONE_TOOLCHAIN @prefix@/native) +- SET(CMAKE_C_COMPILER "${_CMAKE_TOOLCHAIN_PREFIX}clang") +- SET(CMAKE_CXX_COMPILER "${_CMAKE_TOOLCHAIN_PREFIX}clang++") ++ SET(_ANDROID_STANDALONE_TOOLCHAIN_API 21) ++ SET(CMAKE_C_COMPILER @CC@) ++ SET(CMAKE_CXX_COMPILER @CXX@) + else() + SET(CMAKE_C_COMPILER @CC@) + SET(CMAKE_CXX_COMPILER @CXX@) +-- +2.39.5 (Apple Git-154) + diff --git a/patches/wownero/0005-uint64_t-missing-definition-fix.patch b/patches/wownero/0005-uint64_t-missing-definition-fix.patch deleted file mode 100644 index 43e4c54..0000000 --- a/patches/wownero/0005-uint64_t-missing-definition-fix.patch +++ /dev/null @@ -1,25 +0,0 @@ -From 07b87922ba378d14b45ac78c4a9ca39b1cac636d Mon Sep 17 00:00:00 2001 -From: Czarek Nakamoto -Date: Mon, 2 Sep 2024 16:40:31 +0200 -Subject: [PATCH 05/13] 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 4af4da790..ae4c0d05e 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.43.0 - diff --git a/patches/wownero/0006-uint64_t-missing-definition-fix.patch b/patches/wownero/0006-uint64_t-missing-definition-fix.patch new file mode 100644 index 0000000..e57102e --- /dev/null +++ b/patches/wownero/0006-uint64_t-missing-definition-fix.patch @@ -0,0 +1,25 @@ +From 2db1ea14b07823eed4bdfa872f7b1f40019e4bef Mon Sep 17 00:00:00 2001 +From: Czarek Nakamoto +Date: Mon, 2 Sep 2024 16:40:31 +0200 +Subject: [PATCH 06/14] uint64_t missing definition fix + +--- + contrib/epee/include/net/http_base.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/contrib/epee/include/net/http_base.h b/contrib/epee/include/net/http_base.h +index 4af4da7..ae4c0d0 100644 +--- 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.39.5 (Apple Git-154) + diff --git a/patches/wownero/0006-use-proper-error-handling-in-get_seed.patch b/patches/wownero/0006-use-proper-error-handling-in-get_seed.patch deleted file mode 100644 index 861b4a2..0000000 --- a/patches/wownero/0006-use-proper-error-handling-in-get_seed.patch +++ /dev/null @@ -1,71 +0,0 @@ -From 7fa9815a4fd8782fcf65c1cb85621887aa12c001 Mon Sep 17 00:00:00 2001 -From: Czarek Nakamoto -Date: Mon, 24 Jun 2024 10:49:12 +0200 -Subject: [PATCH 06/13] 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 899ef044a..e16d8f83f 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 651161d14..1e527cf97 100644 ---- a/src/wallet/wallet2.cpp -+++ b/src/wallet/wallet2.cpp -@@ -1440,11 +1440,13 @@ bool wallet2::get_seed(epee::wipeable_string& electrum_words, const epee::wipeab - 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; - } -@@ -1454,8 +1456,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.43.0 - diff --git a/patches/wownero/0007-UR-functions.patch b/patches/wownero/0007-UR-functions.patch deleted file mode 100644 index 6d9a010..0000000 --- a/patches/wownero/0007-UR-functions.patch +++ /dev/null @@ -1,1036 +0,0 @@ -From c902907822edad0b2b681c386919c68ed1d3d268 Mon Sep 17 00:00:00 2001 -From: tobtoht -Date: Tue, 12 Mar 2024 10:09:50 +0100 -Subject: [PATCH 07/13] UR functions - -This commit adds UR functions for UR tasks, -I believe that the right place to get -UR strings is the wallet code itself, -especially because it allows us to -skip the part when we have to store -things to file to encode them later. -Now we are fully in memory - -Things broken in the commit -- ledger support. - AUTO_LOCK_CMD macro causes compile time - issues with this patch. I don't know why - just yet, this is a issue that I'll fix - later. However (considering the purpose - of this patch) it is not a dealbreaker. ---- - .gitmodules | 5 +- - CMakeLists.txt | 4 +- - contrib/depends/hosts/darwin.mk | 2 +- - contrib/depends/toolchain.cmake.in | 2 +- - external/CMakeLists.txt | 1 + - external/bc-ur | 1 + - src/device/device_ledger.cpp | 5 +- - src/wallet/CMakeLists.txt | 1 + - src/wallet/api/pending_transaction.cpp | 33 +++ - src/wallet/api/pending_transaction.h | 1 + - src/wallet/api/unsigned_transaction.cpp | 42 ++++ - src/wallet/api/unsigned_transaction.h | 1 + - src/wallet/api/wallet.cpp | 309 +++++++++++++++++++++++- - src/wallet/api/wallet.h | 8 + - src/wallet/api/wallet2_api.h | 22 +- - src/wallet/wallet2.cpp | 141 +++++++---- - src/wallet/wallet2.h | 3 + - 17 files changed, 521 insertions(+), 60 deletions(-) - create mode 160000 external/bc-ur - -diff --git a/.gitmodules b/.gitmodules -index 991071fbe..b24855d9b 100644 ---- a/.gitmodules -+++ b/.gitmodules -@@ -16,4 +16,7 @@ - path = external/randomwow - url = https://codeberg.org/wownero/RandomWOW - branch = 1.2.1-wow -- -+[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 e387ffb1b..8b81c7ab7 100644 ---- a/CMakeLists.txt -+++ b/CMakeLists.txt -@@ -96,7 +96,8 @@ enable_language(C ASM) - 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) -+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) - -@@ -365,6 +366,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) -diff --git a/contrib/depends/hosts/darwin.mk b/contrib/depends/hosts/darwin.mk -index 79d449054..83d83036b 100644 ---- a/contrib/depends/hosts/darwin.mk -+++ b/contrib/depends/hosts/darwin.mk -@@ -1,4 +1,4 @@ --OSX_MIN_VERSION=10.8 -+OSX_MIN_VERSION=10.14 - LD64_VERSION=609 - ifeq (aarch64, $(host_arch)) - CC_target=arm64-apple-$(host_os) -diff --git a/contrib/depends/toolchain.cmake.in b/contrib/depends/toolchain.cmake.in -index cc1d9b5c5..48a6f947e 100644 ---- a/contrib/depends/toolchain.cmake.in -+++ b/contrib/depends/toolchain.cmake.in -@@ -94,7 +94,7 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") - SET(BREW OFF) - SET(PORT OFF) - SET(CMAKE_OSX_SYSROOT "@prefix@/native/SDK/") -- SET(CMAKE_OSX_DEPLOYMENT_TARGET "10.08") -+ SET(CMAKE_OSX_DEPLOYMENT_TARGET "10.14") - SET(CMAKE_CXX_STANDARD 14) - SET(LLVM_ENABLE_PIC OFF) - SET(LLVM_ENABLE_PIE OFF) -diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt -index 3184ae5a1..88a7bb0b5 100644 ---- a/external/CMakeLists.txt -+++ b/external/CMakeLists.txt -@@ -72,4 +72,5 @@ endif() - add_subdirectory(db_drivers) - add_subdirectory(easylogging++) - add_subdirectory(qrcodegen) -+add_subdirectory(bc-ur) - add_subdirectory(randomwow EXCLUDE_FROM_ALL) -diff --git a/external/bc-ur b/external/bc-ur -new file mode 160000 -index 000000000..d82e7c753 ---- /dev/null -+++ b/external/bc-ur -@@ -0,0 +1 @@ -+Subproject commit d82e7c753e710b8000706dc3383b498438795208 -diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp -index 9961d13e7..8403d76e8 100644 ---- a/src/device/device_ledger.cpp -+++ b/src/device/device_ledger.cpp -@@ -313,12 +313,13 @@ namespace hw { - - /* ======================================================================= */ - /* LOCKER */ -- /* ======================================================================= */ -+ /* ======================================================================= */ - - //automatic lock one more level on device ensuring the current thread is allowed to use it -+ #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); */ \ - /* 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) -diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt -index fdf3f2f5d..66384fe31 100644 ---- a/src/wallet/CMakeLists.txt -+++ b/src/wallet/CMakeLists.txt -@@ -50,6 +50,7 @@ monero_add_library(wallet - target_link_libraries(wallet - PUBLIC - rpc_base -+ bc-ur - multisig - common - cryptonote_core -diff --git a/src/wallet/api/pending_transaction.cpp b/src/wallet/api/pending_transaction.cpp -index 70a702796..9c3c26ee5 100644 ---- a/src/wallet/api/pending_transaction.cpp -+++ b/src/wallet/api/pending_transaction.cpp -@@ -42,6 +42,8 @@ - #include - #include - -+#include "bc-ur/src/bc-ur.hpp" -+ - using namespace std; - - namespace Monero { -@@ -162,6 +164,37 @@ bool PendingTransactionImpl::commit(const std::string &filename, bool overwrite) - return m_status == Status_Ok; - } - -+std::string PendingTransactionImpl::commitUR(int max_fragment_length) { -+ -+ LOG_PRINT_L3("m_pending_tx size: " << m_pending_tx.size()); -+ -+ try { -+ std::string ptx = m_wallet.m_wallet->dump_tx_to_str(m_pending_tx); -+ m_status = Status_Ok; -+ auto urMessage = ur::string_to_bytes(ptx); -+ ur::ByteVector cbor; -+ ur::CborLite::encodeBytes(cbor, urMessage); -+ std::string type; -+ if (m_wallet.watchOnly()) { -+ type = "xmr-txunsigned"; -+ } else { -+ type = "xmr-txsigned"; -+ } -+ ur::UR urData = ur::UR(type, cbor); -+ auto encoder = ur::UREncoder(urData, max_fragment_length); -+ std::string output; -+ for(size_t i = 0; i < encoder.seq_len(); i++) { -+ output.append("\n"+encoder.next_part()); -+ } -+ return output; -+ } catch (const std::exception &e) { -+ m_errorString = string(tr("Unknown exception: ")) + e.what(); -+ m_status = Status_Error; -+ return ""; -+ } -+} -+ -+ - uint64_t PendingTransactionImpl::amount() const - { - uint64_t result = 0; -diff --git a/src/wallet/api/pending_transaction.h b/src/wallet/api/pending_transaction.h -index 0a9779c07..403bfe281 100644 ---- a/src/wallet/api/pending_transaction.h -+++ b/src/wallet/api/pending_transaction.h -@@ -46,6 +46,7 @@ public: - int status() const override; - std::string errorString() const override; - bool commit(const std::string &filename = "", bool overwrite = false) override; -+ std::string commitUR(int max_fragment_length = 130) 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 6165a2240..fd03e959d 100644 ---- a/src/wallet/api/unsigned_transaction.cpp -+++ b/src/wallet/api/unsigned_transaction.cpp -@@ -40,6 +40,8 @@ - #include - #include - -+#include "bc-ur/src/bc-ur.hpp" -+ - using namespace std; - - namespace Monero { -@@ -96,6 +98,46 @@ bool UnsignedTransactionImpl::sign(const std::string &signedFileName) - return true; - } - -+std::string UnsignedTransactionImpl::signUR(int max_fragment_length) -+{ -+ if(m_wallet.watchOnly()) -+ { -+ m_errorString = tr("This is a watch only wallet"); -+ m_status = Status_Error; -+ return ""; -+ } -+ std::vector ptx; -+ try -+ { -+ tools::wallet2::signed_tx_set signed_txes; -+ std::string signedTx = m_wallet.m_wallet->sign_tx_dump_to_str(m_unsigned_tx_set, ptx, signed_txes); -+ if (signedTx.empty()) -+ { -+ m_errorString = tr("Failed to sign transaction"); -+ m_status = Status_Error; -+ return ""; -+ } -+ auto urMessage = ur::string_to_bytes(signedTx); -+ ur::ByteVector cbor; -+ ur::CborLite::encodeBytes(cbor, urMessage); -+ std::string type = "xmr-txsigned"; -+ ur::UR urData = ur::UR(type, cbor); -+ auto encoder = ur::UREncoder(urData, max_fragment_length); -+ std::string output; -+ for(size_t i = 0; i < encoder.seq_len(); i++) { -+ output.append("\n"+encoder.next_part()); -+ } -+ return output; -+ } -+ catch (const std::exception &e) -+ { -+ m_errorString = string(tr("Failed to sign transaction")) + e.what(); -+ m_status = Status_Error; -+ return ""; -+ } -+ return ""; -+} -+ - //---------------------------------------------------------------------------------------------------- - 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 ---- a/src/wallet/api/unsigned_transaction.h -+++ b/src/wallet/api/unsigned_transaction.h -@@ -53,6 +53,7 @@ public: - uint64_t txCount() const override; - // sign txs and save to file - bool sign(const std::string &signedFileName) override; -+ std::string signUR(int max_fragment_length = 130) override; - std::string confirmationMessage() const override {return m_confirmationMessage;} - uint64_t minMixinCount() const override; - -diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp -index e16d8f83f..ee000e7ab 100644 ---- a/src/wallet/api/wallet.cpp -+++ b/src/wallet/api/wallet.cpp -@@ -47,6 +47,7 @@ - - #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 - return m_wallet->unlocked_balance(accountIndex, false); - } - -+uint64_t WalletImpl::viewOnlyBalance(uint32_t accountIndex, const std::vector &key_images) const -+{ -+ clearStatus(); -+ -+ std::vector kis; -+ for (const auto &key_image : key_images) { -+ crypto::key_image ki; -+ if (!epee::string_tools::hex_to_pod(key_image, ki)) -+ { -+ setStatusError(tr("failed to parse key image")); -+ return 0; -+ } -+ kis.push_back(ki); -+ } -+ -+ return m_wallet->view_only_balance(accountIndex, kis); -+} -+ - uint64_t WalletImpl::blockChainHeight() const - { - if(m_wallet->light_wallet()) { -@@ -1208,6 +1227,61 @@ UnsignedTransaction *WalletImpl::loadUnsignedTx(const std::string &unsigned_file - return transaction; - } - -+ -+UnsignedTransaction *WalletImpl::loadUnsignedTxUR(const std::string &input) { -+ clearStatus(); -+ UnsignedTransactionImpl * transaction = new UnsignedTransactionImpl(*this); -+ auto decoder = ur::URDecoder(); -+ -+ std::string delimiter = "\n"; -+ std::string inp = input; -+ size_t pos = 0; -+ std::string token; -+ while ((pos = inp.find(delimiter)) != std::string::npos) { -+ token = inp.substr(0, pos); -+ decoder.receive_part(token); -+ inp.erase(0, pos + delimiter.length()); -+ } -+ decoder.receive_part(inp); -+ -+ if (decoder.is_failure()) { -+ setStatusError(decoder.result_error().what()); -+ transaction->m_status = UnsignedTransaction::Status::Status_Error; -+ transaction->m_errorString = errorString(); -+ return transaction; -+ } -+ -+ if (!decoder.is_complete()) { -+ setStatusError("file ended but ur didn't complete"); -+ transaction->m_status = UnsignedTransaction::Status::Status_Error; -+ transaction->m_errorString = errorString(); -+ return transaction; -+ } -+ -+ std::string data; -+ auto cbor = decoder.result_ur().cbor(); -+ auto i = cbor.begin(); -+ auto end = cbor.end(); -+ ur::CborLite::decodeBytes(i, end, data); -+ -+ if (checkBackgroundSync("cannot load tx") || !m_wallet->parse_unsigned_tx_from_str(data, transaction->m_unsigned_tx_set)){ -+ setStatusError(tr("Failed to load unsigned transactions")); -+ transaction->m_status = UnsignedTransaction::Status::Status_Error; -+ transaction->m_errorString = errorString(); -+ -+ 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; -+} -+ - bool WalletImpl::submitTransaction(const string &fileName) { - clearStatus(); - if (checkBackgroundSync("cannot submit tx")) -@@ -1219,7 +1293,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; -@@ -1228,6 +1302,61 @@ bool WalletImpl::submitTransaction(const string &fileName) { - return true; - } - -+ -+bool WalletImpl::submitTransactionUR(const string &input) { -+ clearStatus(); -+ auto decoder = ur::URDecoder(); -+ -+ std::string delimiter = "\n"; -+ std::string inp = input; -+ size_t pos = 0; -+ std::string token; -+ while ((pos = inp.find(delimiter)) != std::string::npos) { -+ token = inp.substr(0, pos); -+ decoder.receive_part(token); -+ inp.erase(0, pos + delimiter.length()); -+ } -+ decoder.receive_part(inp); -+ -+ if (decoder.is_failure()) { -+ setStatusError(decoder.result_error().what()); -+ return false; -+ } -+ -+ if (!decoder.is_complete()) { -+ setStatusError("file ended but ur didn't complete"); -+ return false; -+ } -+ -+ std::string data; -+ auto cbor = decoder.result_ur().cbor(); -+ auto i = cbor.begin(); -+ auto end = cbor.end(); -+ ur::CborLite::decodeBytes(i, end, data); -+ if (checkBackgroundSync("cannot submit tx")) -+ return false; -+ std::unique_ptr transaction(new PendingTransactionImpl(*this)); -+ -+ bool r = m_wallet->parse_tx_from_str(data, transaction->m_pending_tx, NULL); -+ if (!r) { -+ setStatus(Status_Ok, tr("Failed to load transaction from file")); -+ return false; -+ } -+ -+ if(!transaction->commit()) { -+ setStatusError(transaction->m_errorString); -+ return false; -+ } -+ -+ return true; -+} -+ -+ -+bool WalletImpl::hasUnknownKeyImages() const -+{ -+ return m_wallet->has_unknown_key_images(); -+} -+ - 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) - return true; - } - -+std::string WalletImpl::exportKeyImagesUR(size_t max_fragment_length, bool all) -+{ -+ if (m_wallet->watch_only()) -+ { -+ setStatusError(tr("Wallet is view only")); -+ return ""; -+ } -+ if (checkBackgroundSync("cannot export key images")) -+ return ""; -+ -+ try -+ { -+ std::string keyImages = m_wallet->export_key_images_str(all); -+ auto urMessage = ur::string_to_bytes(keyImages); -+ ur::ByteVector cbor; -+ ur::CborLite::encodeBytes(cbor, urMessage); -+ ur::UR urData = ur::UR("xmr-keyimage", cbor); -+ auto encoder = ur::UREncoder(urData, max_fragment_length); -+ std::string output; -+ for(size_t i = 0; i < encoder.seq_len(); i++) { -+ output.append("\n"+encoder.next_part()); -+ } -+ return output; -+ } -+ catch (const std::exception &e) -+ { -+ LOG_ERROR("Error exporting key images: " << e.what()); -+ setStatusError(e.what()); -+ return ""; -+ } -+ return ""; -+} -+ - bool WalletImpl::importKeyImages(const string &filename) - { - if (checkBackgroundSync("cannot import key images")) -@@ -1280,6 +1442,62 @@ bool WalletImpl::importKeyImages(const string &filename) - return true; - } - -+ -+bool WalletImpl::importKeyImagesUR(const string &input) -+{ -+ 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 -+ { -+ auto decoder = ur::URDecoder(); -+ std::string delimiter = "\n"; -+ std::string inp = input; -+ size_t pos = 0; -+ std::string token; -+ while ((pos = inp.find(delimiter)) != std::string::npos) { -+ token = inp.substr(0, pos); -+ decoder.receive_part(token); -+ inp.erase(0, pos + delimiter.length()); -+ } -+ decoder.receive_part(inp); -+ -+ if (decoder.is_failure()) { -+ setStatusError(decoder.result_error().what()); -+ return false; -+ } -+ -+ if (!decoder.is_complete()) { -+ setStatusError("file ended but ur didn't complete"); -+ return false; -+ } -+ -+ std::string data; -+ auto cbor = decoder.result_ur().cbor(); -+ auto i = cbor.begin(); -+ auto end = cbor.end(); -+ ur::CborLite::decodeBytes(i, end, data); -+ -+ uint64_t spent = 0, unspent = 0; -+ -+ uint64_t height = m_wallet->import_key_images_str(data, spent, unspent); -+ LOG_PRINT_L2("Signed key images imported to height " << height << ", " -+ << print_money(spent) << " spent, " << print_money(unspent) << " unspent"); -+ } -+ catch (const std::exception &e) -+ { -+ LOG_ERROR("Error exporting key images: " << e.what()); -+ setStatusError(string(tr("Failed to import key images: ")) + e.what()); -+ return false; -+ } -+ -+ return true; -+} -+ -+ - 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) - return true; - } - -+std::string WalletImpl::exportOutputsUR(size_t max_fragment_length, bool all) -+{ -+ -+ if (checkBackgroundSync("cannot export outputs")) -+ return ""; -+ if (m_wallet->key_on_device()) -+ { -+ setStatusError(string(tr("Not supported on HW wallets."))); -+ return ""; -+ } -+ -+ try -+ { -+ std::string data = m_wallet->export_outputs_to_str(all); -+ auto urMessage = ur::string_to_bytes(data); -+ ur::ByteVector cbor; -+ ur::CborLite::encodeBytes(cbor, urMessage); -+ ur::UR urData = ur::UR("xmr-output", cbor); -+ auto encoder = ur::UREncoder(urData, max_fragment_length); -+ std::string output; -+ for(size_t i = 0; i < encoder.seq_len(); i++) { -+ output.append("\n"+encoder.next_part()); -+ } -+ return output; -+ } -+ catch (const std::exception &e) -+ { -+ LOG_ERROR("Error exporting outputs: " << e.what()); -+ setStatusError(string(tr("Error exporting outputs: ")) + e.what()); -+ return ""; -+ } -+} -+ -+ - bool WalletImpl::importOutputs(const string &filename) - { - if (checkBackgroundSync("cannot import outputs")) -@@ -1346,6 +1598,61 @@ bool WalletImpl::importOutputs(const string &filename) - return true; - } - -+ -+bool WalletImpl::importOutputsUR(const string &input) -+{ -+ if (checkBackgroundSync("cannot import outputs")) -+ return false; -+ if (m_wallet->key_on_device()) -+ { -+ setStatusError(string(tr("Not supported on HW wallets."))); -+ return false; -+ } -+ -+ try -+ { -+ auto decoder = ur::URDecoder(); -+ -+ std::string delimiter = "\n"; -+ std::string inp = input; -+ size_t pos = 0; -+ std::string token; -+ while ((pos = inp.find(delimiter)) != std::string::npos) { -+ token = inp.substr(0, pos); -+ decoder.receive_part(token); -+ inp.erase(0, pos + delimiter.length()); -+ } -+ decoder.receive_part(inp); -+ -+ if (decoder.is_failure()) { -+ setStatusError(decoder.result_error().what()); -+ return false; -+ } -+ -+ if (!decoder.is_complete()) { -+ setStatusError("file ended but ur didn't complete"); -+ return false; -+ } -+ -+ std::string data; -+ auto cbor = decoder.result_ur().cbor(); -+ auto i = cbor.begin(); -+ auto end = cbor.end(); -+ ur::CborLite::decodeBytes(i, end, data); -+ size_t n_outputs = m_wallet->import_outputs_from_str(std::string(data)); -+ LOG_PRINT_L2(std::to_string(n_outputs) << " outputs imported"); -+ } -+ catch (const std::exception &e) -+ { -+ LOG_ERROR("Failed to import outputs: " << e.what()); -+ setStatusError(string(tr("Failed to import outputs: ")) + e.what()); -+ return false; -+ } -+ -+ return true; -+} -+ -+ - bool WalletImpl::scanTransactions(const std::vector &txids) - { - if (checkBackgroundSync("cannot scan transactions")) -diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h -index ac7ce2f6a..edf8bb8ce 100644 ---- a/src/wallet/api/wallet.h -+++ b/src/wallet/api/wallet.h -@@ -112,6 +112,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; -+ uint64_t viewOnlyBalance(uint32_t accountIndex, const std::vector &key_images) const override; - uint64_t blockChainHeight() const override; - uint64_t approximateBlockChainHeight() const override; - uint64_t estimateBlockChainHeight() const override; -@@ -164,11 +165,18 @@ public: - std::set subaddr_indices = {}) override; - virtual PendingTransaction * createSweepUnmixableTransaction() override; - bool submitTransaction(const std::string &fileName) override; -+ bool submitTransactionUR(const std::string &input) override; - virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename) override; -+ virtual UnsignedTransaction * loadUnsignedTxUR(const std::string &input) override; -+ bool hasUnknownKeyImages() const override; - bool exportKeyImages(const std::string &filename, bool all = false) override; -+ std::string exportKeyImagesUR(size_t max_fragment_length, bool all = false) override; - bool importKeyImages(const std::string &filename) override; -+ bool importKeyImagesUR(const std::string &input) override; - bool exportOutputs(const std::string &filename, bool all = false) override; -+ std::string exportOutputsUR(size_t max_fragment_length, bool all) override; - bool importOutputs(const std::string &filename) override; -+ bool importOutputsUR(const std::string &filename) override; - bool scanTransactions(const std::vector &txids) override; - - bool setupBackgroundSync(const BackgroundSyncType background_sync_type, const std::string &wallet_password, const optional &background_cache_password = optional()) override; -diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h -index e349df176..764adbfbf 100644 ---- a/src/wallet/api/wallet2_api.h -+++ b/src/wallet/api/wallet2_api.h -@@ -91,6 +91,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; -+ virtual std::string commitUR(int max_fragment_length = 130) = 0; - virtual uint64_t amount() const = 0; - virtual uint64_t dust() const = 0; - virtual uint64_t fee() const = 0; -@@ -160,7 +161,8 @@ struct UnsignedTransaction - * @param signedFileName - * return - true on success - */ -- virtual bool sign(const std::string &signedFileName) = 0; -+ virtual bool sign(const std::string &signedFileName) = 0; -+ virtual std::string signUR(int max_fragment_length = 130) = 0; - }; - - /** -@@ -626,6 +628,7 @@ struct Wallet - result += unlockedBalance(i); - return result; - } -+ virtual uint64_t viewOnlyBalance(uint32_t accountIndex, const std::vector &key_images = {}) const = 0; - - /** - * @brief watchOnly - checks if wallet is watch only -@@ -884,13 +887,15 @@ struct Wallet - * after object returned - */ - virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename) = 0; -- -- /*! -+ virtual UnsignedTransaction * loadUnsignedTxUR(const std::string &input) = 0; -+ -+ /*! - * \brief submitTransaction - submits transaction in signed tx file - * \return - true on success - */ - virtual bool submitTransaction(const std::string &fileName) = 0; -- -+ virtual bool submitTransactionUR(const std::string &input) = 0; -+ - - /*! - * \brief disposeTransaction - destroys transaction object -@@ -906,6 +911,8 @@ struct Wallet - virtual uint64_t estimateTransactionFee(const std::vector> &destinations, - PendingTransaction::Priority priority) const = 0; - -+ virtual bool hasUnknownKeyImages() const = 0; -+ - /*! - * \brief exportKeyImages - exports key images to file - * \param filename -@@ -913,20 +920,22 @@ struct Wallet - * \return - true on success - */ - virtual bool exportKeyImages(const std::string &filename, bool all = false) = 0; -- -+ virtual std::string exportKeyImagesUR(size_t max_fragment_length, bool all = false) = 0; - /*! - * \brief importKeyImages - imports key images from file - * \param filename - * \return - true on success - */ - virtual bool importKeyImages(const std::string &filename) = 0; -+ virtual bool importKeyImagesUR(const std::string &input) = 0; - - /*! -- * \brief importOutputs - exports outputs to file -+ * \brief exportOutputs - exports outputs to file - * \param filename - * \return - true on success - */ - virtual bool exportOutputs(const std::string &filename, bool all = false) = 0; -+ virtual std::string exportOutputsUR(size_t max_fragment_length, bool all = false) = 0; - - /*! - * \brief importOutputs - imports outputs from file -@@ -934,6 +943,7 @@ struct Wallet - * \return - true on success - */ - virtual bool importOutputs(const std::string &filename) = 0; -+ virtual bool importOutputsUR(const std::string &filename) = 0; - - /*! - * \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 1e527cf97..671fa5298 100644 ---- a/src/wallet/wallet2.cpp -+++ b/src/wallet/wallet2.cpp -@@ -948,6 +948,16 @@ uint32_t get_subaddress_clamped_sum(uint32_t idx, uint32_t extra) - return idx + extra; - } - -+bool is_preferred_input(const std::vector& preferred_input_list, const crypto::key_image& input) { -+ if (!preferred_input_list.empty()) { -+ auto it = std::find(preferred_input_list.begin(), preferred_input_list.end(), input); -+ if (it == preferred_input_list.end()) { -+ return false; -+ } -+ } -+ return true; -+} -+ - 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); -@@ -6989,6 +6999,25 @@ uint64_t wallet2::unlocked_balance(uint32_t index_major, bool strict, uint64_t * - return amount; - } - //---------------------------------------------------------------------------------------------------- -+uint64_t wallet2::view_only_balance(uint32_t index_major, const std::vector& selected_inputs) -+{ -+ uint64_t amount = 0; -+ for (const auto &td : m_transfers) { -+ if (is_preferred_input(selected_inputs, td.m_key_image) && -+ !is_spent(td, false) && -+ !td.m_frozen && -+ !td.m_key_image_partial && -+ td.m_key_image_known && -+ td.is_rct() && -+ is_transfer_unlocked(td) && -+ td.m_subaddr_index.major == index_major) -+ { -+ amount += td.m_amount; -+ } -+ } -+ return amount; -+} -+//---------------------------------------------------------------------------------------------------- - std::map wallet2::balance_per_subaddress(uint32_t index_major, bool strict) const - { - std::map amount_per_subaddr; -@@ -7840,9 +7869,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) - { -@@ -11232,7 +11259,7 @@ std::vector wallet2::create_transactions_2(std::vector m_ignore_outputs_above || td.amount() < m_ignore_outputs_below) - { -@@ -11282,9 +11309,15 @@ std::vector wallet2::create_transactions_2(std::vector(); -+ // use tobotoht's code path on view-only wallet, otherwise default to upstream -+ bool throwOnNoEnotes = m_account.get_device().device_protocol() == hw::device::PROTOCOL_COLD || m_watch_only || m_multisig || m_is_background_wallet; - -+ if (throwOnNoEnotes) { -+ THROW_WALLET_EXCEPTION_IF(unused_dust_indices_per_subaddr.empty() && unused_transfers_indices_per_subaddr.empty(), error::wallet_internal_error, "No enotes available to spend") -+ } else { -+ if (unused_dust_indices_per_subaddr.empty() && unused_transfers_indices_per_subaddr.empty()) -+ return std::vector(); -+ } - // 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({}); -@@ -13911,33 +13944,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); -- std::pair>> ski = export_key_images(all); -- std::string magic(KEY_IMAGE_EXPORT_FILE_MAGIC, strlen(KEY_IMAGE_EXPORT_FILE_MAGIC)); -- const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address; -- const uint32_t offset = ski.first; -+ std::string data = export_key_images_str(all); -+ return save_to_file(filename, data); -+} - -- std::string data; -- data.reserve(4 + ski.second.size() * (sizeof(crypto::key_image) + sizeof(crypto::signature)) + 2 * sizeof(crypto::public_key)); -- data.resize(4); -- data[0] = offset & 0xff; -- data[1] = (offset >> 8) & 0xff; -- data[2] = (offset >> 16) & 0xff; -- data[3] = (offset >> 24) & 0xff; -- data += std::string((const char *)&keys.m_spend_public_key, sizeof(crypto::public_key)); -- data += std::string((const char *)&keys.m_view_public_key, sizeof(crypto::public_key)); -- for (const auto &i: ski.second) -- { -- data += std::string((const char *)&i.first, sizeof(crypto::key_image)); -- data += std::string((const char *)&i.second, sizeof(crypto::signature)); -- } -+std::string wallet2::export_key_images_str(bool all) const -+{ -+ PERF_TIMER(export_key_images); -+ std::pair>> ski = export_key_images(all); -+ std::string magic(KEY_IMAGE_EXPORT_FILE_MAGIC, strlen(KEY_IMAGE_EXPORT_FILE_MAGIC)); -+ const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address; -+ const uint32_t offset = ski.first; - -- // encrypt data, keep magic plaintext -- PERF_TIMER(export_key_images_encrypt); -- std::string ciphertext = encrypt_with_view_secret_key(data); -- return save_to_file(filename, magic + ciphertext); -+ std::string data; -+ data.reserve(4 + ski.second.size() * (sizeof(crypto::key_image) + sizeof(crypto::signature)) + 2 * sizeof(crypto::public_key)); -+ data.resize(4); -+ data[0] = offset & 0xff; -+ data[1] = (offset >> 8) & 0xff; -+ data[2] = (offset >> 16) & 0xff; -+ data[3] = (offset >> 24) & 0xff; -+ data += std::string((const char *)&keys.m_spend_public_key, sizeof(crypto::public_key)); -+ data += std::string((const char *)&keys.m_view_public_key, sizeof(crypto::public_key)); -+ for (const auto &i: ski.second) -+ { -+ data += std::string((const char *)&i.first, sizeof(crypto::key_image)); -+ data += std::string((const char *)&i.second, sizeof(crypto::signature)); -+ } -+ -+ // encrypt data, keep magic plaintext -+ PERF_TIMER(export_key_images_encrypt); -+ std::string ciphertext = encrypt_with_view_secret_key(data); -+ return magic + ciphertext; - } - -+ - //---------------------------------------------------------------------------------------------------- - std::pair>> wallet2::export_key_images(bool all) const - { -@@ -13992,53 +14032,60 @@ std::pair> - return std::make_pair(offset, ski); - } - --uint64_t wallet2::import_key_images(const std::string &filename, uint64_t &spent, uint64_t &unspent) -+uint64_t wallet2::import_key_images(const std::string &filename, uint64_t &spent, uint64_t &unspent) { -+ std::string data; -+ -+ bool r = load_from_file(filename, data); -+ -+ THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, std::string(tr("failed to read file ")) + filename); -+ -+ return import_key_images_str(data, spent, unspent); -+} -+ -+uint64_t wallet2::import_key_images_str(const std::string &data, uint64_t &spent, uint64_t &unspent) - { - PERF_TIMER(import_key_images_fsu); -- std::string data; -- bool r = load_from_file(filename, data); -- -- THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, std::string(tr("failed to read file ")) + filename); -+ std::string data_local = data; - - const size_t magiclen = strlen(KEY_IMAGE_EXPORT_FILE_MAGIC); - if (data.size() < magiclen || memcmp(data.data(), KEY_IMAGE_EXPORT_FILE_MAGIC, magiclen)) - { -- THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Bad key image export file magic in ") + filename); -+ THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Bad key image export file magic")); - } - - try - { - PERF_TIMER(import_key_images_decrypt); -- data = decrypt_with_view_secret_key(std::string(data, magiclen)); -+ data_local = decrypt_with_view_secret_key(std::string(data, magiclen)); - } - catch (const std::exception &e) - { -- THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Failed to decrypt ") + filename + ": " + e.what()); -+ THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Failed to decrypt ") + ": " + e.what()); - } - - const size_t headerlen = 4 + 2 * sizeof(crypto::public_key); -- THROW_WALLET_EXCEPTION_IF(data.size() < headerlen, error::wallet_internal_error, std::string("Bad data size from file ") + filename); -- const uint32_t offset = (uint8_t)data[0] | (((uint8_t)data[1]) << 8) | (((uint8_t)data[2]) << 16) | (((uint8_t)data[3]) << 24); -- const crypto::public_key &public_spend_key = *(const crypto::public_key*)&data[4]; -- const crypto::public_key &public_view_key = *(const crypto::public_key*)&data[4 + sizeof(crypto::public_key)]; -+ THROW_WALLET_EXCEPTION_IF(data_local.size() < headerlen, error::wallet_internal_error, std::string("Bad data size from file ")); -+ const uint32_t offset = (uint8_t)data_local[0] | (((uint8_t)data_local[1]) << 8) | (((uint8_t)data_local[2]) << 16) | (((uint8_t)data_local[3]) << 24); -+ const crypto::public_key &public_spend_key = *(const crypto::public_key*)&data_local[4]; -+ const crypto::public_key &public_view_key = *(const crypto::public_key*)&data_local[4 + sizeof(crypto::public_key)]; - const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address; - if (public_spend_key != keys.m_spend_public_key || public_view_key != keys.m_view_public_key) - { -- THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string( "Key images from ") + filename + " are for a different account"); -+ THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string( "Key images from ") + " are for a different account"); - } - THROW_WALLET_EXCEPTION_IF(offset > m_transfers.size(), error::wallet_internal_error, "Offset larger than known outputs"); - - const size_t record_size = sizeof(crypto::key_image) + sizeof(crypto::signature); -- THROW_WALLET_EXCEPTION_IF((data.size() - headerlen) % record_size, -- error::wallet_internal_error, std::string("Bad data size from file ") + filename); -- size_t nki = (data.size() - headerlen) / record_size; -+ THROW_WALLET_EXCEPTION_IF((data_local.size() - headerlen) % record_size, -+ error::wallet_internal_error, std::string("Bad data size from file ")); -+ size_t nki = (data_local.size() - headerlen) / record_size; - - std::vector> ski; - ski.reserve(nki); - for (size_t n = 0; n < nki; ++n) - { -- crypto::key_image key_image = *reinterpret_cast(&data[headerlen + n * record_size]); -- crypto::signature signature = *reinterpret_cast(&data[headerlen + n * record_size + sizeof(crypto::key_image)]); -+ crypto::key_image key_image = *reinterpret_cast(&data_local[headerlen + n * record_size]); -+ crypto::signature signature = *reinterpret_cast(&data_local[headerlen + n * record_size + sizeof(crypto::key_image)]); - - ski.push_back(std::make_pair(key_image, signature)); - } -diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h -index c07a47c11..80ff0698d 100644 ---- a/src/wallet/wallet2.h -+++ b/src/wallet/wallet2.h -@@ -1150,6 +1150,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); -+ uint64_t view_only_balance(uint32_t index_major, const std::vector& selected_inputs = {}); - // locked & unlocked balance per subaddress of given or current subaddress account - std::map balance_per_subaddress(uint32_t subaddr_index_major, bool strict) const; - std::map>> unlocked_balance_per_subaddress(uint32_t subaddr_index_major, bool strict); -@@ -1624,9 +1625,11 @@ private: - std::tuple> export_blockchain() const; - void import_blockchain(const std::tuple> &bc); - bool export_key_images(const std::string &filename, bool all = false) const; -+ std::string export_key_images_str(bool all) const; - std::pair>> export_key_images(bool all = false) const; - uint64_t import_key_images(const std::vector> &signed_key_images, size_t offset, uint64_t &spent, uint64_t &unspent, bool check_spent = true); - uint64_t import_key_images(const std::string &filename, uint64_t &spent, uint64_t &unspent); -+ uint64_t import_key_images_str(const std::string &data, uint64_t &spent, uint64_t &unspent); - bool import_key_images(std::vector key_images, size_t offset=0, boost::optional> selected_transfers=boost::none); - 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.43.0 - diff --git a/patches/wownero/0007-use-proper-error-handling-in-get_seed.patch b/patches/wownero/0007-use-proper-error-handling-in-get_seed.patch new file mode 100644 index 0000000..d0d5aac --- /dev/null +++ b/patches/wownero/0007-use-proper-error-handling-in-get_seed.patch @@ -0,0 +1,71 @@ +From 9f186308301738bdd5deb53a77471f2f5ce40766 Mon Sep 17 00:00:00 2001 +From: Czarek Nakamoto +Date: Mon, 24 Jun 2024 10:49:12 +0200 +Subject: [PATCH 07/14] 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 899ef04..e16d8f8 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 651161d..1e527cf 100644 +--- a/src/wallet/wallet2.cpp ++++ b/src/wallet/wallet2.cpp +@@ -1440,11 +1440,13 @@ bool wallet2::get_seed(epee::wipeable_string& electrum_words, const epee::wipeab + 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; + } +@@ -1454,8 +1456,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.39.5 (Apple Git-154) + diff --git a/patches/wownero/0008-UR-functions.patch b/patches/wownero/0008-UR-functions.patch new file mode 100644 index 0000000..ce2867e --- /dev/null +++ b/patches/wownero/0008-UR-functions.patch @@ -0,0 +1,1036 @@ +From 9c4df0e534ec8c04d95620ab7032771edb6f981e Mon Sep 17 00:00:00 2001 +From: tobtoht +Date: Tue, 12 Mar 2024 10:09:50 +0100 +Subject: [PATCH 08/14] UR functions + +This commit adds UR functions for UR tasks, +I believe that the right place to get +UR strings is the wallet code itself, +especially because it allows us to +skip the part when we have to store +things to file to encode them later. +Now we are fully in memory + +Things broken in the commit +- ledger support. + AUTO_LOCK_CMD macro causes compile time + issues with this patch. I don't know why + just yet, this is a issue that I'll fix + later. However (considering the purpose + of this patch) it is not a dealbreaker. +--- + .gitmodules | 5 +- + CMakeLists.txt | 4 +- + contrib/depends/hosts/darwin.mk | 2 +- + contrib/depends/toolchain.cmake.in | 2 +- + external/CMakeLists.txt | 1 + + external/bc-ur | 1 + + src/device/device_ledger.cpp | 5 +- + src/wallet/CMakeLists.txt | 1 + + src/wallet/api/pending_transaction.cpp | 33 +++ + src/wallet/api/pending_transaction.h | 1 + + src/wallet/api/unsigned_transaction.cpp | 42 ++++ + src/wallet/api/unsigned_transaction.h | 1 + + src/wallet/api/wallet.cpp | 309 +++++++++++++++++++++++- + src/wallet/api/wallet.h | 8 + + src/wallet/api/wallet2_api.h | 22 +- + src/wallet/wallet2.cpp | 141 +++++++---- + src/wallet/wallet2.h | 3 + + 17 files changed, 521 insertions(+), 60 deletions(-) + create mode 160000 external/bc-ur + +diff --git a/.gitmodules b/.gitmodules +index 991071f..b24855d 100644 +--- a/.gitmodules ++++ b/.gitmodules +@@ -16,4 +16,7 @@ + path = external/randomwow + url = https://codeberg.org/wownero/RandomWOW + branch = 1.2.1-wow +- ++[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 e387ffb..8b81c7a 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -96,7 +96,8 @@ enable_language(C ASM) + 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) ++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) + +@@ -365,6 +366,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) +diff --git a/contrib/depends/hosts/darwin.mk b/contrib/depends/hosts/darwin.mk +index 79d4490..83d8303 100644 +--- a/contrib/depends/hosts/darwin.mk ++++ b/contrib/depends/hosts/darwin.mk +@@ -1,4 +1,4 @@ +-OSX_MIN_VERSION=10.8 ++OSX_MIN_VERSION=10.14 + LD64_VERSION=609 + ifeq (aarch64, $(host_arch)) + CC_target=arm64-apple-$(host_os) +diff --git a/contrib/depends/toolchain.cmake.in b/contrib/depends/toolchain.cmake.in +index cc1d9b5..48a6f94 100644 +--- a/contrib/depends/toolchain.cmake.in ++++ b/contrib/depends/toolchain.cmake.in +@@ -94,7 +94,7 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + SET(BREW OFF) + SET(PORT OFF) + SET(CMAKE_OSX_SYSROOT "@prefix@/native/SDK/") +- SET(CMAKE_OSX_DEPLOYMENT_TARGET "10.08") ++ SET(CMAKE_OSX_DEPLOYMENT_TARGET "10.14") + SET(CMAKE_CXX_STANDARD 14) + SET(LLVM_ENABLE_PIC OFF) + SET(LLVM_ENABLE_PIE OFF) +diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt +index 3184ae5..88a7bb0 100644 +--- a/external/CMakeLists.txt ++++ b/external/CMakeLists.txt +@@ -72,4 +72,5 @@ endif() + add_subdirectory(db_drivers) + add_subdirectory(easylogging++) + add_subdirectory(qrcodegen) ++add_subdirectory(bc-ur) + add_subdirectory(randomwow EXCLUDE_FROM_ALL) +diff --git a/external/bc-ur b/external/bc-ur +new file mode 160000 +index 0000000..d82e7c7 +--- /dev/null ++++ b/external/bc-ur +@@ -0,0 +1 @@ ++Subproject commit d82e7c753e710b8000706dc3383b498438795208 +diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp +index 9961d13..8403d76 100644 +--- a/src/device/device_ledger.cpp ++++ b/src/device/device_ledger.cpp +@@ -313,12 +313,13 @@ namespace hw { + + /* ======================================================================= */ + /* LOCKER */ +- /* ======================================================================= */ ++ /* ======================================================================= */ + + //automatic lock one more level on device ensuring the current thread is allowed to use it ++ #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); */ \ + /* 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) +diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt +index fdf3f2f..66384fe 100644 +--- a/src/wallet/CMakeLists.txt ++++ b/src/wallet/CMakeLists.txt +@@ -50,6 +50,7 @@ monero_add_library(wallet + target_link_libraries(wallet + PUBLIC + rpc_base ++ bc-ur + multisig + common + cryptonote_core +diff --git a/src/wallet/api/pending_transaction.cpp b/src/wallet/api/pending_transaction.cpp +index 70a7027..9c3c26e 100644 +--- a/src/wallet/api/pending_transaction.cpp ++++ b/src/wallet/api/pending_transaction.cpp +@@ -42,6 +42,8 @@ + #include + #include + ++#include "bc-ur/src/bc-ur.hpp" ++ + using namespace std; + + namespace Monero { +@@ -162,6 +164,37 @@ bool PendingTransactionImpl::commit(const std::string &filename, bool overwrite) + return m_status == Status_Ok; + } + ++std::string PendingTransactionImpl::commitUR(int max_fragment_length) { ++ ++ LOG_PRINT_L3("m_pending_tx size: " << m_pending_tx.size()); ++ ++ try { ++ std::string ptx = m_wallet.m_wallet->dump_tx_to_str(m_pending_tx); ++ m_status = Status_Ok; ++ auto urMessage = ur::string_to_bytes(ptx); ++ ur::ByteVector cbor; ++ ur::CborLite::encodeBytes(cbor, urMessage); ++ std::string type; ++ if (m_wallet.watchOnly()) { ++ type = "xmr-txunsigned"; ++ } else { ++ type = "xmr-txsigned"; ++ } ++ ur::UR urData = ur::UR(type, cbor); ++ auto encoder = ur::UREncoder(urData, max_fragment_length); ++ std::string output; ++ for(size_t i = 0; i < encoder.seq_len(); i++) { ++ output.append("\n"+encoder.next_part()); ++ } ++ return output; ++ } catch (const std::exception &e) { ++ m_errorString = string(tr("Unknown exception: ")) + e.what(); ++ m_status = Status_Error; ++ return ""; ++ } ++} ++ ++ + uint64_t PendingTransactionImpl::amount() const + { + uint64_t result = 0; +diff --git a/src/wallet/api/pending_transaction.h b/src/wallet/api/pending_transaction.h +index 0a9779c..403bfe2 100644 +--- a/src/wallet/api/pending_transaction.h ++++ b/src/wallet/api/pending_transaction.h +@@ -46,6 +46,7 @@ public: + int status() const override; + std::string errorString() const override; + bool commit(const std::string &filename = "", bool overwrite = false) override; ++ std::string commitUR(int max_fragment_length = 130) 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 6165a22..fd03e95 100644 +--- a/src/wallet/api/unsigned_transaction.cpp ++++ b/src/wallet/api/unsigned_transaction.cpp +@@ -40,6 +40,8 @@ + #include + #include + ++#include "bc-ur/src/bc-ur.hpp" ++ + using namespace std; + + namespace Monero { +@@ -96,6 +98,46 @@ bool UnsignedTransactionImpl::sign(const std::string &signedFileName) + return true; + } + ++std::string UnsignedTransactionImpl::signUR(int max_fragment_length) ++{ ++ if(m_wallet.watchOnly()) ++ { ++ m_errorString = tr("This is a watch only wallet"); ++ m_status = Status_Error; ++ return ""; ++ } ++ std::vector ptx; ++ try ++ { ++ tools::wallet2::signed_tx_set signed_txes; ++ std::string signedTx = m_wallet.m_wallet->sign_tx_dump_to_str(m_unsigned_tx_set, ptx, signed_txes); ++ if (signedTx.empty()) ++ { ++ m_errorString = tr("Failed to sign transaction"); ++ m_status = Status_Error; ++ return ""; ++ } ++ auto urMessage = ur::string_to_bytes(signedTx); ++ ur::ByteVector cbor; ++ ur::CborLite::encodeBytes(cbor, urMessage); ++ std::string type = "xmr-txsigned"; ++ ur::UR urData = ur::UR(type, cbor); ++ auto encoder = ur::UREncoder(urData, max_fragment_length); ++ std::string output; ++ for(size_t i = 0; i < encoder.seq_len(); i++) { ++ output.append("\n"+encoder.next_part()); ++ } ++ return output; ++ } ++ catch (const std::exception &e) ++ { ++ m_errorString = string(tr("Failed to sign transaction")) + e.what(); ++ m_status = Status_Error; ++ return ""; ++ } ++ return ""; ++} ++ + //---------------------------------------------------------------------------------------------------- + bool UnsignedTransactionImpl::checkLoadedTx(const std::function get_num_txes, const std::function &get_tx, const std::string &extra_message) + { +diff --git a/src/wallet/api/unsigned_transaction.h b/src/wallet/api/unsigned_transaction.h +index 30065a7..a94b23f 100644 +--- a/src/wallet/api/unsigned_transaction.h ++++ b/src/wallet/api/unsigned_transaction.h +@@ -53,6 +53,7 @@ public: + uint64_t txCount() const override; + // sign txs and save to file + bool sign(const std::string &signedFileName) override; ++ std::string signUR(int max_fragment_length = 130) override; + std::string confirmationMessage() const override {return m_confirmationMessage;} + uint64_t minMixinCount() const override; + +diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp +index e16d8f8..ee000e7 100644 +--- a/src/wallet/api/wallet.cpp ++++ b/src/wallet/api/wallet.cpp +@@ -47,6 +47,7 @@ + + #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 + return m_wallet->unlocked_balance(accountIndex, false); + } + ++uint64_t WalletImpl::viewOnlyBalance(uint32_t accountIndex, const std::vector &key_images) const ++{ ++ clearStatus(); ++ ++ std::vector kis; ++ for (const auto &key_image : key_images) { ++ crypto::key_image ki; ++ if (!epee::string_tools::hex_to_pod(key_image, ki)) ++ { ++ setStatusError(tr("failed to parse key image")); ++ return 0; ++ } ++ kis.push_back(ki); ++ } ++ ++ return m_wallet->view_only_balance(accountIndex, kis); ++} ++ + uint64_t WalletImpl::blockChainHeight() const + { + if(m_wallet->light_wallet()) { +@@ -1208,6 +1227,61 @@ UnsignedTransaction *WalletImpl::loadUnsignedTx(const std::string &unsigned_file + return transaction; + } + ++ ++UnsignedTransaction *WalletImpl::loadUnsignedTxUR(const std::string &input) { ++ clearStatus(); ++ UnsignedTransactionImpl * transaction = new UnsignedTransactionImpl(*this); ++ auto decoder = ur::URDecoder(); ++ ++ std::string delimiter = "\n"; ++ std::string inp = input; ++ size_t pos = 0; ++ std::string token; ++ while ((pos = inp.find(delimiter)) != std::string::npos) { ++ token = inp.substr(0, pos); ++ decoder.receive_part(token); ++ inp.erase(0, pos + delimiter.length()); ++ } ++ decoder.receive_part(inp); ++ ++ if (decoder.is_failure()) { ++ setStatusError(decoder.result_error().what()); ++ transaction->m_status = UnsignedTransaction::Status::Status_Error; ++ transaction->m_errorString = errorString(); ++ return transaction; ++ } ++ ++ if (!decoder.is_complete()) { ++ setStatusError("file ended but ur didn't complete"); ++ transaction->m_status = UnsignedTransaction::Status::Status_Error; ++ transaction->m_errorString = errorString(); ++ return transaction; ++ } ++ ++ std::string data; ++ auto cbor = decoder.result_ur().cbor(); ++ auto i = cbor.begin(); ++ auto end = cbor.end(); ++ ur::CborLite::decodeBytes(i, end, data); ++ ++ if (checkBackgroundSync("cannot load tx") || !m_wallet->parse_unsigned_tx_from_str(data, transaction->m_unsigned_tx_set)){ ++ setStatusError(tr("Failed to load unsigned transactions")); ++ transaction->m_status = UnsignedTransaction::Status::Status_Error; ++ transaction->m_errorString = errorString(); ++ ++ 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; ++} ++ + bool WalletImpl::submitTransaction(const string &fileName) { + clearStatus(); + if (checkBackgroundSync("cannot submit tx")) +@@ -1219,7 +1293,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; +@@ -1228,6 +1302,61 @@ bool WalletImpl::submitTransaction(const string &fileName) { + return true; + } + ++ ++bool WalletImpl::submitTransactionUR(const string &input) { ++ clearStatus(); ++ auto decoder = ur::URDecoder(); ++ ++ std::string delimiter = "\n"; ++ std::string inp = input; ++ size_t pos = 0; ++ std::string token; ++ while ((pos = inp.find(delimiter)) != std::string::npos) { ++ token = inp.substr(0, pos); ++ decoder.receive_part(token); ++ inp.erase(0, pos + delimiter.length()); ++ } ++ decoder.receive_part(inp); ++ ++ if (decoder.is_failure()) { ++ setStatusError(decoder.result_error().what()); ++ return false; ++ } ++ ++ if (!decoder.is_complete()) { ++ setStatusError("file ended but ur didn't complete"); ++ return false; ++ } ++ ++ std::string data; ++ auto cbor = decoder.result_ur().cbor(); ++ auto i = cbor.begin(); ++ auto end = cbor.end(); ++ ur::CborLite::decodeBytes(i, end, data); ++ if (checkBackgroundSync("cannot submit tx")) ++ return false; ++ std::unique_ptr transaction(new PendingTransactionImpl(*this)); ++ ++ bool r = m_wallet->parse_tx_from_str(data, transaction->m_pending_tx, NULL); ++ if (!r) { ++ setStatus(Status_Ok, tr("Failed to load transaction from file")); ++ return false; ++ } ++ ++ if(!transaction->commit()) { ++ setStatusError(transaction->m_errorString); ++ return false; ++ } ++ ++ return true; ++} ++ ++ ++bool WalletImpl::hasUnknownKeyImages() const ++{ ++ return m_wallet->has_unknown_key_images(); ++} ++ + 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) + return true; + } + ++std::string WalletImpl::exportKeyImagesUR(size_t max_fragment_length, bool all) ++{ ++ if (m_wallet->watch_only()) ++ { ++ setStatusError(tr("Wallet is view only")); ++ return ""; ++ } ++ if (checkBackgroundSync("cannot export key images")) ++ return ""; ++ ++ try ++ { ++ std::string keyImages = m_wallet->export_key_images_str(all); ++ auto urMessage = ur::string_to_bytes(keyImages); ++ ur::ByteVector cbor; ++ ur::CborLite::encodeBytes(cbor, urMessage); ++ ur::UR urData = ur::UR("xmr-keyimage", cbor); ++ auto encoder = ur::UREncoder(urData, max_fragment_length); ++ std::string output; ++ for(size_t i = 0; i < encoder.seq_len(); i++) { ++ output.append("\n"+encoder.next_part()); ++ } ++ return output; ++ } ++ catch (const std::exception &e) ++ { ++ LOG_ERROR("Error exporting key images: " << e.what()); ++ setStatusError(e.what()); ++ return ""; ++ } ++ return ""; ++} ++ + bool WalletImpl::importKeyImages(const string &filename) + { + if (checkBackgroundSync("cannot import key images")) +@@ -1280,6 +1442,62 @@ bool WalletImpl::importKeyImages(const string &filename) + return true; + } + ++ ++bool WalletImpl::importKeyImagesUR(const string &input) ++{ ++ 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 ++ { ++ auto decoder = ur::URDecoder(); ++ std::string delimiter = "\n"; ++ std::string inp = input; ++ size_t pos = 0; ++ std::string token; ++ while ((pos = inp.find(delimiter)) != std::string::npos) { ++ token = inp.substr(0, pos); ++ decoder.receive_part(token); ++ inp.erase(0, pos + delimiter.length()); ++ } ++ decoder.receive_part(inp); ++ ++ if (decoder.is_failure()) { ++ setStatusError(decoder.result_error().what()); ++ return false; ++ } ++ ++ if (!decoder.is_complete()) { ++ setStatusError("file ended but ur didn't complete"); ++ return false; ++ } ++ ++ std::string data; ++ auto cbor = decoder.result_ur().cbor(); ++ auto i = cbor.begin(); ++ auto end = cbor.end(); ++ ur::CborLite::decodeBytes(i, end, data); ++ ++ uint64_t spent = 0, unspent = 0; ++ ++ uint64_t height = m_wallet->import_key_images_str(data, spent, unspent); ++ LOG_PRINT_L2("Signed key images imported to height " << height << ", " ++ << print_money(spent) << " spent, " << print_money(unspent) << " unspent"); ++ } ++ catch (const std::exception &e) ++ { ++ LOG_ERROR("Error exporting key images: " << e.what()); ++ setStatusError(string(tr("Failed to import key images: ")) + e.what()); ++ return false; ++ } ++ ++ return true; ++} ++ ++ + 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) + return true; + } + ++std::string WalletImpl::exportOutputsUR(size_t max_fragment_length, bool all) ++{ ++ ++ if (checkBackgroundSync("cannot export outputs")) ++ return ""; ++ if (m_wallet->key_on_device()) ++ { ++ setStatusError(string(tr("Not supported on HW wallets."))); ++ return ""; ++ } ++ ++ try ++ { ++ std::string data = m_wallet->export_outputs_to_str(all); ++ auto urMessage = ur::string_to_bytes(data); ++ ur::ByteVector cbor; ++ ur::CborLite::encodeBytes(cbor, urMessage); ++ ur::UR urData = ur::UR("xmr-output", cbor); ++ auto encoder = ur::UREncoder(urData, max_fragment_length); ++ std::string output; ++ for(size_t i = 0; i < encoder.seq_len(); i++) { ++ output.append("\n"+encoder.next_part()); ++ } ++ return output; ++ } ++ catch (const std::exception &e) ++ { ++ LOG_ERROR("Error exporting outputs: " << e.what()); ++ setStatusError(string(tr("Error exporting outputs: ")) + e.what()); ++ return ""; ++ } ++} ++ ++ + bool WalletImpl::importOutputs(const string &filename) + { + if (checkBackgroundSync("cannot import outputs")) +@@ -1346,6 +1598,61 @@ bool WalletImpl::importOutputs(const string &filename) + return true; + } + ++ ++bool WalletImpl::importOutputsUR(const string &input) ++{ ++ if (checkBackgroundSync("cannot import outputs")) ++ return false; ++ if (m_wallet->key_on_device()) ++ { ++ setStatusError(string(tr("Not supported on HW wallets."))); ++ return false; ++ } ++ ++ try ++ { ++ auto decoder = ur::URDecoder(); ++ ++ std::string delimiter = "\n"; ++ std::string inp = input; ++ size_t pos = 0; ++ std::string token; ++ while ((pos = inp.find(delimiter)) != std::string::npos) { ++ token = inp.substr(0, pos); ++ decoder.receive_part(token); ++ inp.erase(0, pos + delimiter.length()); ++ } ++ decoder.receive_part(inp); ++ ++ if (decoder.is_failure()) { ++ setStatusError(decoder.result_error().what()); ++ return false; ++ } ++ ++ if (!decoder.is_complete()) { ++ setStatusError("file ended but ur didn't complete"); ++ return false; ++ } ++ ++ std::string data; ++ auto cbor = decoder.result_ur().cbor(); ++ auto i = cbor.begin(); ++ auto end = cbor.end(); ++ ur::CborLite::decodeBytes(i, end, data); ++ size_t n_outputs = m_wallet->import_outputs_from_str(std::string(data)); ++ LOG_PRINT_L2(std::to_string(n_outputs) << " outputs imported"); ++ } ++ catch (const std::exception &e) ++ { ++ LOG_ERROR("Failed to import outputs: " << e.what()); ++ setStatusError(string(tr("Failed to import outputs: ")) + e.what()); ++ return false; ++ } ++ ++ return true; ++} ++ ++ + bool WalletImpl::scanTransactions(const std::vector &txids) + { + if (checkBackgroundSync("cannot scan transactions")) +diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h +index ac7ce2f..edf8bb8 100644 +--- a/src/wallet/api/wallet.h ++++ b/src/wallet/api/wallet.h +@@ -112,6 +112,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; ++ uint64_t viewOnlyBalance(uint32_t accountIndex, const std::vector &key_images) const override; + uint64_t blockChainHeight() const override; + uint64_t approximateBlockChainHeight() const override; + uint64_t estimateBlockChainHeight() const override; +@@ -164,11 +165,18 @@ public: + std::set subaddr_indices = {}) override; + virtual PendingTransaction * createSweepUnmixableTransaction() override; + bool submitTransaction(const std::string &fileName) override; ++ bool submitTransactionUR(const std::string &input) override; + virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename) override; ++ virtual UnsignedTransaction * loadUnsignedTxUR(const std::string &input) override; ++ bool hasUnknownKeyImages() const override; + bool exportKeyImages(const std::string &filename, bool all = false) override; ++ std::string exportKeyImagesUR(size_t max_fragment_length, bool all = false) override; + bool importKeyImages(const std::string &filename) override; ++ bool importKeyImagesUR(const std::string &input) override; + bool exportOutputs(const std::string &filename, bool all = false) override; ++ std::string exportOutputsUR(size_t max_fragment_length, bool all) override; + bool importOutputs(const std::string &filename) override; ++ bool importOutputsUR(const std::string &filename) override; + bool scanTransactions(const std::vector &txids) override; + + bool setupBackgroundSync(const BackgroundSyncType background_sync_type, const std::string &wallet_password, const optional &background_cache_password = optional()) override; +diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h +index e349df1..764adbf 100644 +--- a/src/wallet/api/wallet2_api.h ++++ b/src/wallet/api/wallet2_api.h +@@ -91,6 +91,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; ++ virtual std::string commitUR(int max_fragment_length = 130) = 0; + virtual uint64_t amount() const = 0; + virtual uint64_t dust() const = 0; + virtual uint64_t fee() const = 0; +@@ -160,7 +161,8 @@ struct UnsignedTransaction + * @param signedFileName + * return - true on success + */ +- virtual bool sign(const std::string &signedFileName) = 0; ++ virtual bool sign(const std::string &signedFileName) = 0; ++ virtual std::string signUR(int max_fragment_length = 130) = 0; + }; + + /** +@@ -626,6 +628,7 @@ struct Wallet + result += unlockedBalance(i); + return result; + } ++ virtual uint64_t viewOnlyBalance(uint32_t accountIndex, const std::vector &key_images = {}) const = 0; + + /** + * @brief watchOnly - checks if wallet is watch only +@@ -884,13 +887,15 @@ struct Wallet + * after object returned + */ + virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename) = 0; +- +- /*! ++ virtual UnsignedTransaction * loadUnsignedTxUR(const std::string &input) = 0; ++ ++ /*! + * \brief submitTransaction - submits transaction in signed tx file + * \return - true on success + */ + virtual bool submitTransaction(const std::string &fileName) = 0; +- ++ virtual bool submitTransactionUR(const std::string &input) = 0; ++ + + /*! + * \brief disposeTransaction - destroys transaction object +@@ -906,6 +911,8 @@ struct Wallet + virtual uint64_t estimateTransactionFee(const std::vector> &destinations, + PendingTransaction::Priority priority) const = 0; + ++ virtual bool hasUnknownKeyImages() const = 0; ++ + /*! + * \brief exportKeyImages - exports key images to file + * \param filename +@@ -913,20 +920,22 @@ struct Wallet + * \return - true on success + */ + virtual bool exportKeyImages(const std::string &filename, bool all = false) = 0; +- ++ virtual std::string exportKeyImagesUR(size_t max_fragment_length, bool all = false) = 0; + /*! + * \brief importKeyImages - imports key images from file + * \param filename + * \return - true on success + */ + virtual bool importKeyImages(const std::string &filename) = 0; ++ virtual bool importKeyImagesUR(const std::string &input) = 0; + + /*! +- * \brief importOutputs - exports outputs to file ++ * \brief exportOutputs - exports outputs to file + * \param filename + * \return - true on success + */ + virtual bool exportOutputs(const std::string &filename, bool all = false) = 0; ++ virtual std::string exportOutputsUR(size_t max_fragment_length, bool all = false) = 0; + + /*! + * \brief importOutputs - imports outputs from file +@@ -934,6 +943,7 @@ struct Wallet + * \return - true on success + */ + virtual bool importOutputs(const std::string &filename) = 0; ++ virtual bool importOutputsUR(const std::string &filename) = 0; + + /*! + * \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 1e527cf..671fa52 100644 +--- a/src/wallet/wallet2.cpp ++++ b/src/wallet/wallet2.cpp +@@ -948,6 +948,16 @@ uint32_t get_subaddress_clamped_sum(uint32_t idx, uint32_t extra) + return idx + extra; + } + ++bool is_preferred_input(const std::vector& preferred_input_list, const crypto::key_image& input) { ++ if (!preferred_input_list.empty()) { ++ auto it = std::find(preferred_input_list.begin(), preferred_input_list.end(), input); ++ if (it == preferred_input_list.end()) { ++ return false; ++ } ++ } ++ return true; ++} ++ + 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); +@@ -6989,6 +6999,25 @@ uint64_t wallet2::unlocked_balance(uint32_t index_major, bool strict, uint64_t * + return amount; + } + //---------------------------------------------------------------------------------------------------- ++uint64_t wallet2::view_only_balance(uint32_t index_major, const std::vector& selected_inputs) ++{ ++ uint64_t amount = 0; ++ for (const auto &td : m_transfers) { ++ if (is_preferred_input(selected_inputs, td.m_key_image) && ++ !is_spent(td, false) && ++ !td.m_frozen && ++ !td.m_key_image_partial && ++ td.m_key_image_known && ++ td.is_rct() && ++ is_transfer_unlocked(td) && ++ td.m_subaddr_index.major == index_major) ++ { ++ amount += td.m_amount; ++ } ++ } ++ return amount; ++} ++//---------------------------------------------------------------------------------------------------- + std::map wallet2::balance_per_subaddress(uint32_t index_major, bool strict) const + { + std::map amount_per_subaddr; +@@ -7840,9 +7869,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) + { +@@ -11232,7 +11259,7 @@ std::vector wallet2::create_transactions_2(std::vector m_ignore_outputs_above || td.amount() < m_ignore_outputs_below) + { +@@ -11282,9 +11309,15 @@ std::vector wallet2::create_transactions_2(std::vector(); ++ // use tobotoht's code path on view-only wallet, otherwise default to upstream ++ bool throwOnNoEnotes = m_account.get_device().device_protocol() == hw::device::PROTOCOL_COLD || m_watch_only || m_multisig || m_is_background_wallet; + ++ if (throwOnNoEnotes) { ++ THROW_WALLET_EXCEPTION_IF(unused_dust_indices_per_subaddr.empty() && unused_transfers_indices_per_subaddr.empty(), error::wallet_internal_error, "No enotes available to spend") ++ } else { ++ if (unused_dust_indices_per_subaddr.empty() && unused_transfers_indices_per_subaddr.empty()) ++ return std::vector(); ++ } + // 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({}); +@@ -13911,33 +13944,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); +- std::pair>> ski = export_key_images(all); +- std::string magic(KEY_IMAGE_EXPORT_FILE_MAGIC, strlen(KEY_IMAGE_EXPORT_FILE_MAGIC)); +- const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address; +- const uint32_t offset = ski.first; ++ std::string data = export_key_images_str(all); ++ return save_to_file(filename, data); ++} + +- std::string data; +- data.reserve(4 + ski.second.size() * (sizeof(crypto::key_image) + sizeof(crypto::signature)) + 2 * sizeof(crypto::public_key)); +- data.resize(4); +- data[0] = offset & 0xff; +- data[1] = (offset >> 8) & 0xff; +- data[2] = (offset >> 16) & 0xff; +- data[3] = (offset >> 24) & 0xff; +- data += std::string((const char *)&keys.m_spend_public_key, sizeof(crypto::public_key)); +- data += std::string((const char *)&keys.m_view_public_key, sizeof(crypto::public_key)); +- for (const auto &i: ski.second) +- { +- data += std::string((const char *)&i.first, sizeof(crypto::key_image)); +- data += std::string((const char *)&i.second, sizeof(crypto::signature)); +- } ++std::string wallet2::export_key_images_str(bool all) const ++{ ++ PERF_TIMER(export_key_images); ++ std::pair>> ski = export_key_images(all); ++ std::string magic(KEY_IMAGE_EXPORT_FILE_MAGIC, strlen(KEY_IMAGE_EXPORT_FILE_MAGIC)); ++ const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address; ++ const uint32_t offset = ski.first; + +- // encrypt data, keep magic plaintext +- PERF_TIMER(export_key_images_encrypt); +- std::string ciphertext = encrypt_with_view_secret_key(data); +- return save_to_file(filename, magic + ciphertext); ++ std::string data; ++ data.reserve(4 + ski.second.size() * (sizeof(crypto::key_image) + sizeof(crypto::signature)) + 2 * sizeof(crypto::public_key)); ++ data.resize(4); ++ data[0] = offset & 0xff; ++ data[1] = (offset >> 8) & 0xff; ++ data[2] = (offset >> 16) & 0xff; ++ data[3] = (offset >> 24) & 0xff; ++ data += std::string((const char *)&keys.m_spend_public_key, sizeof(crypto::public_key)); ++ data += std::string((const char *)&keys.m_view_public_key, sizeof(crypto::public_key)); ++ for (const auto &i: ski.second) ++ { ++ data += std::string((const char *)&i.first, sizeof(crypto::key_image)); ++ data += std::string((const char *)&i.second, sizeof(crypto::signature)); ++ } ++ ++ // encrypt data, keep magic plaintext ++ PERF_TIMER(export_key_images_encrypt); ++ std::string ciphertext = encrypt_with_view_secret_key(data); ++ return magic + ciphertext; + } + ++ + //---------------------------------------------------------------------------------------------------- + std::pair>> wallet2::export_key_images(bool all) const + { +@@ -13992,53 +14032,60 @@ std::pair> + return std::make_pair(offset, ski); + } + +-uint64_t wallet2::import_key_images(const std::string &filename, uint64_t &spent, uint64_t &unspent) ++uint64_t wallet2::import_key_images(const std::string &filename, uint64_t &spent, uint64_t &unspent) { ++ std::string data; ++ ++ bool r = load_from_file(filename, data); ++ ++ THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, std::string(tr("failed to read file ")) + filename); ++ ++ return import_key_images_str(data, spent, unspent); ++} ++ ++uint64_t wallet2::import_key_images_str(const std::string &data, uint64_t &spent, uint64_t &unspent) + { + PERF_TIMER(import_key_images_fsu); +- std::string data; +- bool r = load_from_file(filename, data); +- +- THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, std::string(tr("failed to read file ")) + filename); ++ std::string data_local = data; + + const size_t magiclen = strlen(KEY_IMAGE_EXPORT_FILE_MAGIC); + if (data.size() < magiclen || memcmp(data.data(), KEY_IMAGE_EXPORT_FILE_MAGIC, magiclen)) + { +- THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Bad key image export file magic in ") + filename); ++ THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Bad key image export file magic")); + } + + try + { + PERF_TIMER(import_key_images_decrypt); +- data = decrypt_with_view_secret_key(std::string(data, magiclen)); ++ data_local = decrypt_with_view_secret_key(std::string(data, magiclen)); + } + catch (const std::exception &e) + { +- THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Failed to decrypt ") + filename + ": " + e.what()); ++ THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Failed to decrypt ") + ": " + e.what()); + } + + const size_t headerlen = 4 + 2 * sizeof(crypto::public_key); +- THROW_WALLET_EXCEPTION_IF(data.size() < headerlen, error::wallet_internal_error, std::string("Bad data size from file ") + filename); +- const uint32_t offset = (uint8_t)data[0] | (((uint8_t)data[1]) << 8) | (((uint8_t)data[2]) << 16) | (((uint8_t)data[3]) << 24); +- const crypto::public_key &public_spend_key = *(const crypto::public_key*)&data[4]; +- const crypto::public_key &public_view_key = *(const crypto::public_key*)&data[4 + sizeof(crypto::public_key)]; ++ THROW_WALLET_EXCEPTION_IF(data_local.size() < headerlen, error::wallet_internal_error, std::string("Bad data size from file ")); ++ const uint32_t offset = (uint8_t)data_local[0] | (((uint8_t)data_local[1]) << 8) | (((uint8_t)data_local[2]) << 16) | (((uint8_t)data_local[3]) << 24); ++ const crypto::public_key &public_spend_key = *(const crypto::public_key*)&data_local[4]; ++ const crypto::public_key &public_view_key = *(const crypto::public_key*)&data_local[4 + sizeof(crypto::public_key)]; + const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address; + if (public_spend_key != keys.m_spend_public_key || public_view_key != keys.m_view_public_key) + { +- THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string( "Key images from ") + filename + " are for a different account"); ++ THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string( "Key images from ") + " are for a different account"); + } + THROW_WALLET_EXCEPTION_IF(offset > m_transfers.size(), error::wallet_internal_error, "Offset larger than known outputs"); + + const size_t record_size = sizeof(crypto::key_image) + sizeof(crypto::signature); +- THROW_WALLET_EXCEPTION_IF((data.size() - headerlen) % record_size, +- error::wallet_internal_error, std::string("Bad data size from file ") + filename); +- size_t nki = (data.size() - headerlen) / record_size; ++ THROW_WALLET_EXCEPTION_IF((data_local.size() - headerlen) % record_size, ++ error::wallet_internal_error, std::string("Bad data size from file ")); ++ size_t nki = (data_local.size() - headerlen) / record_size; + + std::vector> ski; + ski.reserve(nki); + for (size_t n = 0; n < nki; ++n) + { +- crypto::key_image key_image = *reinterpret_cast(&data[headerlen + n * record_size]); +- crypto::signature signature = *reinterpret_cast(&data[headerlen + n * record_size + sizeof(crypto::key_image)]); ++ crypto::key_image key_image = *reinterpret_cast(&data_local[headerlen + n * record_size]); ++ crypto::signature signature = *reinterpret_cast(&data_local[headerlen + n * record_size + sizeof(crypto::key_image)]); + + ski.push_back(std::make_pair(key_image, signature)); + } +diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h +index c07a47c..80ff069 100644 +--- a/src/wallet/wallet2.h ++++ b/src/wallet/wallet2.h +@@ -1150,6 +1150,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); ++ uint64_t view_only_balance(uint32_t index_major, const std::vector& selected_inputs = {}); + // locked & unlocked balance per subaddress of given or current subaddress account + std::map balance_per_subaddress(uint32_t subaddr_index_major, bool strict) const; + std::map>> unlocked_balance_per_subaddress(uint32_t subaddr_index_major, bool strict); +@@ -1624,9 +1625,11 @@ private: + std::tuple> export_blockchain() const; + void import_blockchain(const std::tuple> &bc); + bool export_key_images(const std::string &filename, bool all = false) const; ++ std::string export_key_images_str(bool all) const; + std::pair>> export_key_images(bool all = false) const; + uint64_t import_key_images(const std::vector> &signed_key_images, size_t offset, uint64_t &spent, uint64_t &unspent, bool check_spent = true); + uint64_t import_key_images(const std::string &filename, uint64_t &spent, uint64_t &unspent); ++ uint64_t import_key_images_str(const std::string &data, uint64_t &spent, uint64_t &unspent); + bool import_key_images(std::vector key_images, size_t offset=0, boost::optional> selected_transfers=boost::none); + bool import_key_images(signed_tx_set & signed_tx, size_t offset=0, bool only_selected_transfers=false); + crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const; +-- +2.39.5 (Apple Git-154) + diff --git a/patches/wownero/0008-add-dummy-device-for-ledger.patch b/patches/wownero/0008-add-dummy-device-for-ledger.patch deleted file mode 100644 index 22c9e18..0000000 --- a/patches/wownero/0008-add-dummy-device-for-ledger.patch +++ /dev/null @@ -1,580 +0,0 @@ -From 339d7485c0b8bb1e922d959c1e2cc0016c1efa65 Mon Sep 17 00:00:00 2001 -From: Czarek Nakamoto -Date: Wed, 26 Jun 2024 15:04:38 +0200 -Subject: [PATCH 08/13] add dummy device for ledger - ---- - CMakeLists.txt | 6 +- - src/device/CMakeLists.txt | 6 +- - src/device/device.cpp | 10 ++- - src/device/device.hpp | 12 +-- - src/device/device_io_dummy.cpp | 133 +++++++++++++++++++++++++++++++++ - src/device/device_io_dummy.hpp | 74 ++++++++++++++++++ - src/device/device_ledger.cpp | 6 +- - src/device/device_ledger.hpp | 7 +- - src/wallet/api/wallet.cpp | 94 +++++++++++++++++++++++ - src/wallet/api/wallet.h | 18 +++++ - src/wallet/api/wallet2_api.h | 12 +++ - 11 files changed, 357 insertions(+), 21 deletions(-) - create mode 100644 src/device/device_io_dummy.cpp - create mode 100644 src/device/device_io_dummy.hpp - -diff --git a/CMakeLists.txt b/CMakeLists.txt -index 8b81c7ab7..abe44eca5 100644 ---- a/CMakeLists.txt -+++ b/CMakeLists.txt -@@ -709,8 +709,12 @@ include_directories(${LMDB_INCLUDE}) - include_directories(${LIBUNWIND_INCLUDE}) - link_directories(${LIBUNWIND_LIBRARY_DIRS}) - -+if (HIDAPI_DUMMY) -+ add_definitions(-DHIDAPI_DUMMY) -+endif() -+ - # Final setup for hid --if (HIDAPI_FOUND) -+if (HIDAPI_FOUND) - message(STATUS "Using HIDAPI include dir at ${HIDAPI_INCLUDE_DIR}") - add_definitions(-DHAVE_HIDAPI) - include_directories(${HIDAPI_INCLUDE_DIR}) -diff --git a/src/device/CMakeLists.txt b/src/device/CMakeLists.txt -index e4f1159b5..14d398f87 100644 ---- a/src/device/CMakeLists.txt -+++ b/src/device/CMakeLists.txt -@@ -29,10 +29,11 @@ - set(device_sources - device.cpp - device_default.cpp -+ device_io_dummy.cpp - log.cpp - ) - --if(HIDAPI_FOUND) -+if(HIDAPI_FOUND OR HIDAPI_DUMMY) - set(device_sources - ${device_sources} - device_ledger.cpp -@@ -45,10 +46,11 @@ set(device_headers - device_io.hpp - device_default.hpp - device_cold.hpp -+ device_io_dummy.hpp - log.hpp - ) - --if(HIDAPI_FOUND) -+if(HIDAPI_FOUND OR HIDAPI_DUMMY) - set(device_headers - ${device_headers} - device_ledger.hpp -diff --git a/src/device/device.cpp b/src/device/device.cpp -index e6cd358b6..636929feb 100644 ---- a/src/device/device.cpp -+++ b/src/device/device.cpp -@@ -29,7 +29,7 @@ - - #include "device.hpp" - #include "device_default.hpp" --#ifdef WITH_DEVICE_LEDGER -+#if defined(WITH_DEVICE_LEDGER) || defined(HIDAPI_DUMMY) - #include "device_ledger.hpp" - #endif - #include "misc_log_ex.h" -@@ -57,7 +57,7 @@ namespace hw { - - device_registry::device_registry(){ - hw::core::register_all(registry); -- #ifdef WITH_DEVICE_LEDGER -+ #if defined(WITH_DEVICE_LEDGER) || defined(HIDAPI_DUMMY) - hw::ledger::register_all(registry); - #endif - atexit(clear_device_registry); -@@ -83,11 +83,13 @@ namespace hw { - - auto device = registry.find(device_descriptor_lookup); - if (device == registry.end()) { -- MERROR("Device not found in registry: '" << device_descriptor << "'. Known devices: "); -+ std::stringstream ss("Device not found in registry: '" + device_descriptor + "'. Known devices: "); -+ MERROR("Device not found in registry: '" << device_descriptor << "'. Known devices: \n"); - for( const auto& sm_pair : registry ) { -+ ss << "\n- " + sm_pair.first; - MERROR(" - " << sm_pair.first); - } -- throw std::runtime_error("device not found: " + device_descriptor); -+ throw std::runtime_error("device not found: " + device_descriptor + "\nlalala\n" + ss.str()); - } - return *device->second; - } -diff --git a/src/device/device.hpp b/src/device/device.hpp -index 392703a24..ffd419779 100644 ---- a/src/device/device.hpp -+++ b/src/device/device.hpp -@@ -34,17 +34,7 @@ - #include "ringct/rctTypes.h" - #include "cryptonote_config.h" - -- --#ifndef USE_DEVICE_LEDGER --#define USE_DEVICE_LEDGER 1 --#endif -- --#if !defined(HAVE_HIDAPI) --#undef USE_DEVICE_LEDGER --#define USE_DEVICE_LEDGER 0 --#endif -- --#if USE_DEVICE_LEDGER -+#if defined(HAVE_HIDAPI) || defined(HIDAPI_DUMMY) - #define WITH_DEVICE_LEDGER - #endif - -diff --git a/src/device/device_io_dummy.cpp b/src/device/device_io_dummy.cpp -new file mode 100644 -index 000000000..fb082694e ---- /dev/null -+++ b/src/device/device_io_dummy.cpp -@@ -0,0 +1,133 @@ -+// Copyright (c) 2017-2022, The Monero Project -+// -+// All rights reserved. -+// -+// Redistribution and use in source and binary forms, with or without modification, are -+// permitted provided that the following conditions are met: -+// -+// 1. Redistributions of source code must retain the above copyright notice, this list of -+// conditions and the following disclaimer. -+// -+// 2. Redistributions in binary form must reproduce the above copyright notice, this list -+// of conditions and the following disclaimer in the documentation and/or other -+// materials provided with the distribution. -+// -+// 3. Neither the name of the copyright holder nor the names of its contributors may be -+// used to endorse or promote products derived from this software without specific -+// prior written permission. -+// -+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY -+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL -+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -+// 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. -+// -+ -+// device_io_dummy -+// Main goal of device_io_dummy is to emulate a hw::io::device_io without the need to actually -+// connect a device. -+// Many operating systems do not support giving raw USB access to a process (android), or don't -+// support that at all (hi iOS), therefore other means of connection can be used, either USB -+// abstraction provided by the OS (monerujo), or BLE (also monerujo). -+// Monerujo implementation is written in Java, which makes it a nice fit for iOS, but makes the -+// code extremely unportable, so for this reason the code in here is written in CPP. -+// Data transport is made available in wallet2_api.h, so wallet developers can easily plug their -+// own USB/BLE/other transport layer. -+ -+#ifdef HIDAPI_DUMMY -+#include -+#include "log.hpp" -+#include "device_io_dummy.hpp" -+#include "device_ledger.hpp" -+ -+ -+bool hw::io::device_io_dummy::stateIsConnected = false; -+unsigned char* hw::io::device_io_dummy::sendToDevice = {}; -+size_t hw::io::device_io_dummy::sendToDeviceLength = 0; -+unsigned char* hw::io::device_io_dummy::receivedFromDevice = {}; -+size_t hw::io::device_io_dummy::receivedFromDeviceLength = 0; -+bool hw::io::device_io_dummy::waitsForDeviceSend = false; -+bool hw::io::device_io_dummy::waitsForDeviceReceive = false; -+ -+namespace hw { -+ namespace io { -+ -+#undef MONERO_DEFAULT_LOG_CATEGORY -+#define MONERO_DEFAULT_LOG_CATEGORY "device.io_dummy" -+ device_io_dummy::device_io_dummy(int a, int b, int c, int d) { -+ MDEBUG("device_io_dummy(a: " << a << ", b: " << b << ", c: " << c << ", d: " << d <<")"); -+ } -+ -+ void device_io_dummy::init() { -+ MDEBUG("init()"); -+ } -+ -+ void device_io_dummy::connect(void *params) { -+ MDEBUG("connect(" << params << ")"); -+ stateIsConnected = true; -+ } -+ -+ void device_io_dummy::connect(const std::vector& known_devices) { -+ MDEBUG("connect(["); -+ for (const auto &item: known_devices) { -+ MDEBUG("{ interface_number: " << item.interface_number); -+ MDEBUG(" pid : " << item.pid); -+ MDEBUG(" usage_page : " << item.usage_page); -+ MDEBUG(" vid : " << item.vid << " },"); -+ } -+ MDEBUG("])"); -+ stateIsConnected = true; -+ } -+ -+ bool device_io_dummy::connected() const { -+ MDEBUG("connected()"); -+ return stateIsConnected; -+ } -+ -+ int device_io_dummy::exchange(unsigned char *command, unsigned int cmd_len, unsigned char *response, unsigned int max_resp_len, bool user_input) { -+ MDEBUG("exchange(): locking mutex"); -+ boost::unique_lock lock(mutex); -+ sendToDevice = command; -+ sendToDeviceLength = cmd_len; -+ waitsForDeviceSend = true; -+ waitsForDeviceReceive = true; -+ MDEBUG("exchange(): waitsForDeviceSend"); -+ // NOTE: waitsForDeviceSend should be changed by external code -+ while (waitsForDeviceSend) { -+ sleep(1); -+ MDEBUG("exchange(): waitsForDeviceSend (still)"); -+ } -+ -+ MDEBUG("exchange(): waitsForDeviceReceive"); -+ while (waitsForDeviceReceive) { -+ sleep(1); -+ MDEBUG("exchange(): waitsForDeviceReceive (still)"); -+ } -+ -+ if (receivedFromDeviceLength > max_resp_len) { -+ MDEBUG("exchange(): receivedFromDeviceLength ("<& known_devices); -+ void disconnect(); -+ bool connected() const; -+ -+ int exchange(unsigned char *command, unsigned int cmd_len, unsigned char *response, unsigned int max_resp_len, bool user_input); -+ }; -+ }; -+}; -+ -+#endif // HAVE_HIDAPI -diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp -index 8403d76e8..db40a2eb7 100644 ---- a/src/device/device_ledger.cpp -+++ b/src/device/device_ledger.cpp -@@ -41,7 +41,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 { - - device_ledger::device_ledger(): hw_device(0x0101, 0x05, 64, 2000) { - this->id = device_id++; -- this->reset_buffer(); -+ this->reset_buffer(); - this->mode = NONE; - this->has_view_key = false; - this->tx_in_progress = false; -@@ -533,7 +533,9 @@ namespace hw { - - bool device_ledger::connect(void) { - this->disconnect(); -+ #ifndef HIDAPI_DUMMY - hw_device.connect(known_devices); -+ #endif - this->reset(); - #ifdef DEBUG_HWDEVICE - cryptonote::account_public_address pubkey; -diff --git a/src/device/device_ledger.hpp b/src/device/device_ledger.hpp -index 03058c4f1..506f27c4a 100644 ---- a/src/device/device_ledger.hpp -+++ b/src/device/device_ledger.hpp -@@ -35,6 +35,7 @@ - #include "device.hpp" - #include "log.hpp" - #include "device_io_hid.hpp" -+#include "device_io_dummy.hpp" - #include - #include - -@@ -56,7 +57,7 @@ namespace hw { - - void register_all(std::map> ®istry); - -- #ifdef WITH_DEVICE_LEDGER -+ #if defined(WITH_DEVICE_LEDGER) || defined(HIDAPI_DUMMY) - - // 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; - - //IO -+#ifdef HIDAPI_DUMMY -+ hw::io::device_io_dummy hw_device; -+#else - hw::io::device_io_hid hw_device; -+#endif - unsigned int length_send; - 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 ee000e7ab..375edb4f1 100644 ---- a/src/wallet/api/wallet.cpp -+++ b/src/wallet/api/wallet.cpp -@@ -48,6 +48,9 @@ - #include - #include - #include "bc-ur/src/bc-ur.hpp" -+#ifdef HIDAPI_DUMMY -+#include "device/device_io_dummy.hpp" -+#endif - - using namespace std; - using namespace cryptonote; -@@ -3178,4 +3181,95 @@ uint64_t WalletImpl::getBytesSent() - return m_wallet->get_bytes_sent(); - } - -+ -+// HIDAPI_DUMMY -+bool WalletImpl::getStateIsConnected() { -+ #ifndef HIDAPI_DUMMY -+ setStatusError("MONERO compiled with -DHIDAPI_DUMMY"); -+ return false; -+ #else -+ return hw::io::device_io_dummy::stateIsConnected; -+ #endif -+} -+ -+unsigned char* WalletImpl::getSendToDevice() { -+ #ifndef HIDAPI_DUMMY -+ setStatusError("MONERO compiled with -DHIDAPI_DUMMY"); -+ return {}; -+ #else -+ return hw::io::device_io_dummy::sendToDevice; -+ #endif -+} -+ -+size_t WalletImpl::getSendToDeviceLength() { -+ #ifndef HIDAPI_DUMMY -+ setStatusError("MONERO compiled with -DHIDAPI_DUMMY"); -+ return -1; -+ #else -+ return hw::io::device_io_dummy::sendToDeviceLength; -+ #endif -+} -+ -+unsigned char* WalletImpl::getReceivedFromDevice() { -+ #ifndef HIDAPI_DUMMY -+ setStatusError("MONERO compiled with -DHIDAPI_DUMMY"); -+ return {}; -+ #else -+ return hw::io::device_io_dummy::receivedFromDevice; -+ #endif -+} -+ -+size_t WalletImpl::getReceivedFromDeviceLength() { -+ #ifndef HIDAPI_DUMMY -+ setStatusError("MONERO compiled with -DHIDAPI_DUMMY"); -+ return -1; -+ #else -+ return hw::io::device_io_dummy::receivedFromDeviceLength; -+ #endif -+} -+ -+bool WalletImpl::getWaitsForDeviceSend() { -+ #ifndef HIDAPI_DUMMY -+ setStatusError("MONERO compiled with -DHIDAPI_DUMMY"); -+ return false; -+ #else -+ return hw::io::device_io_dummy::receivedFromDeviceLength; -+ #endif -+} -+ -+bool WalletImpl::getWaitsForDeviceReceive() { -+ #ifndef HIDAPI_DUMMY -+ setStatusError("MONERO compiled with -DHIDAPI_DUMMY"); -+ return false; -+ #else -+ return hw::io::device_io_dummy::waitsForDeviceReceive; -+ #endif -+} -+ -+void WalletImpl::setDeviceReceivedData(unsigned char* data, size_t len) { -+ #ifndef HIDAPI_DUMMY -+ setStatusError("MONERO compiled with -DHIDAPI_DUMMY"); -+ return; -+ #else -+ hw::io::device_io_dummy::receivedFromDevice = static_cast(malloc(len)); -+ hw::io::device_io_dummy::receivedFromDeviceLength = len; -+ memset(hw::io::device_io_dummy::receivedFromDevice, 0, len); -+ memcpy(hw::io::device_io_dummy::receivedFromDevice, data, len); -+ hw::io::device_io_dummy::waitsForDeviceReceive = false; -+ #endif -+} -+ -+void WalletImpl::setDeviceSendData(unsigned char* data, size_t len) { -+ #ifndef HIDAPI_DUMMY -+ setStatusError("MONERO compiled with -DHIDAPI_DUMMY"); -+ return; -+ #else -+ hw::io::device_io_dummy::sendToDevice = static_cast(malloc(len)); -+ hw::io::device_io_dummy::sendToDeviceLength = len; -+ memset(hw::io::device_io_dummy::sendToDevice, 0, len); -+ memcpy(hw::io::device_io_dummy::sendToDevice, data, len); -+ hw::io::device_io_dummy::waitsForDeviceSend = false; -+ #endif -+} -+ - } // namespace -diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h -index edf8bb8ce..4e9c21ecb 100644 ---- a/src/wallet/api/wallet.h -+++ b/src/wallet/api/wallet.h -@@ -301,6 +301,24 @@ private: - // cache connection status to avoid unnecessary RPC calls - mutable std::atomic m_is_connected; - boost::optional m_daemon_login{}; -+ -+ bool getStateIsConnected(); -+ -+ unsigned char *getSendToDevice(); -+ -+ size_t getSendToDeviceLength(); -+ -+ unsigned char *getReceivedFromDevice(); -+ -+ size_t getReceivedFromDeviceLength(); -+ -+ bool getWaitsForDeviceSend(); -+ -+ bool getWaitsForDeviceReceive(); -+ -+ void setDeviceReceivedData(unsigned char *data, size_t len); -+ -+ void setDeviceSendData(unsigned char *data, size_t len); - }; - - -diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h -index 764adbfbf..53ec4abfc 100644 ---- a/src/wallet/api/wallet2_api.h -+++ b/src/wallet/api/wallet2_api.h -@@ -1150,6 +1150,18 @@ struct Wallet - - //! get bytes sent - virtual uint64_t getBytesSent() = 0; -+ -+ // HIDAPI_DUMMY -+ virtual bool getStateIsConnected() = 0; -+ virtual unsigned char* getSendToDevice() = 0; -+ virtual size_t getSendToDeviceLength() = 0; -+ virtual unsigned char* getReceivedFromDevice() = 0; -+ virtual size_t getReceivedFromDeviceLength() = 0; -+ virtual bool getWaitsForDeviceSend() = 0; -+ virtual bool getWaitsForDeviceReceive() = 0; -+ -+ virtual void setDeviceReceivedData(unsigned char* data, size_t len) = 0; -+ virtual void setDeviceSendData(unsigned char* data, size_t len) = 0; - }; - - /** --- -2.43.0 - diff --git a/patches/wownero/0009-add-dummy-device-for-ledger.patch b/patches/wownero/0009-add-dummy-device-for-ledger.patch new file mode 100644 index 0000000..cb2a82e --- /dev/null +++ b/patches/wownero/0009-add-dummy-device-for-ledger.patch @@ -0,0 +1,580 @@ +From 45b79176318ccf64154e85bd41db10708999023c Mon Sep 17 00:00:00 2001 +From: Czarek Nakamoto +Date: Wed, 26 Jun 2024 15:04:38 +0200 +Subject: [PATCH 09/14] add dummy device for ledger + +--- + CMakeLists.txt | 6 +- + src/device/CMakeLists.txt | 6 +- + src/device/device.cpp | 10 ++- + src/device/device.hpp | 12 +-- + src/device/device_io_dummy.cpp | 133 +++++++++++++++++++++++++++++++++ + src/device/device_io_dummy.hpp | 74 ++++++++++++++++++ + src/device/device_ledger.cpp | 6 +- + src/device/device_ledger.hpp | 7 +- + src/wallet/api/wallet.cpp | 94 +++++++++++++++++++++++ + src/wallet/api/wallet.h | 18 +++++ + src/wallet/api/wallet2_api.h | 12 +++ + 11 files changed, 357 insertions(+), 21 deletions(-) + create mode 100644 src/device/device_io_dummy.cpp + create mode 100644 src/device/device_io_dummy.hpp + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 8b81c7a..abe44ec 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -709,8 +709,12 @@ include_directories(${LMDB_INCLUDE}) + include_directories(${LIBUNWIND_INCLUDE}) + link_directories(${LIBUNWIND_LIBRARY_DIRS}) + ++if (HIDAPI_DUMMY) ++ add_definitions(-DHIDAPI_DUMMY) ++endif() ++ + # Final setup for hid +-if (HIDAPI_FOUND) ++if (HIDAPI_FOUND) + message(STATUS "Using HIDAPI include dir at ${HIDAPI_INCLUDE_DIR}") + add_definitions(-DHAVE_HIDAPI) + include_directories(${HIDAPI_INCLUDE_DIR}) +diff --git a/src/device/CMakeLists.txt b/src/device/CMakeLists.txt +index e4f1159..14d398f 100644 +--- a/src/device/CMakeLists.txt ++++ b/src/device/CMakeLists.txt +@@ -29,10 +29,11 @@ + set(device_sources + device.cpp + device_default.cpp ++ device_io_dummy.cpp + log.cpp + ) + +-if(HIDAPI_FOUND) ++if(HIDAPI_FOUND OR HIDAPI_DUMMY) + set(device_sources + ${device_sources} + device_ledger.cpp +@@ -45,10 +46,11 @@ set(device_headers + device_io.hpp + device_default.hpp + device_cold.hpp ++ device_io_dummy.hpp + log.hpp + ) + +-if(HIDAPI_FOUND) ++if(HIDAPI_FOUND OR HIDAPI_DUMMY) + set(device_headers + ${device_headers} + device_ledger.hpp +diff --git a/src/device/device.cpp b/src/device/device.cpp +index e6cd358..636929f 100644 +--- a/src/device/device.cpp ++++ b/src/device/device.cpp +@@ -29,7 +29,7 @@ + + #include "device.hpp" + #include "device_default.hpp" +-#ifdef WITH_DEVICE_LEDGER ++#if defined(WITH_DEVICE_LEDGER) || defined(HIDAPI_DUMMY) + #include "device_ledger.hpp" + #endif + #include "misc_log_ex.h" +@@ -57,7 +57,7 @@ namespace hw { + + device_registry::device_registry(){ + hw::core::register_all(registry); +- #ifdef WITH_DEVICE_LEDGER ++ #if defined(WITH_DEVICE_LEDGER) || defined(HIDAPI_DUMMY) + hw::ledger::register_all(registry); + #endif + atexit(clear_device_registry); +@@ -83,11 +83,13 @@ namespace hw { + + auto device = registry.find(device_descriptor_lookup); + if (device == registry.end()) { +- MERROR("Device not found in registry: '" << device_descriptor << "'. Known devices: "); ++ std::stringstream ss("Device not found in registry: '" + device_descriptor + "'. Known devices: "); ++ MERROR("Device not found in registry: '" << device_descriptor << "'. Known devices: \n"); + for( const auto& sm_pair : registry ) { ++ ss << "\n- " + sm_pair.first; + MERROR(" - " << sm_pair.first); + } +- throw std::runtime_error("device not found: " + device_descriptor); ++ throw std::runtime_error("device not found: " + device_descriptor + "\nlalala\n" + ss.str()); + } + return *device->second; + } +diff --git a/src/device/device.hpp b/src/device/device.hpp +index 392703a..ffd4197 100644 +--- a/src/device/device.hpp ++++ b/src/device/device.hpp +@@ -34,17 +34,7 @@ + #include "ringct/rctTypes.h" + #include "cryptonote_config.h" + +- +-#ifndef USE_DEVICE_LEDGER +-#define USE_DEVICE_LEDGER 1 +-#endif +- +-#if !defined(HAVE_HIDAPI) +-#undef USE_DEVICE_LEDGER +-#define USE_DEVICE_LEDGER 0 +-#endif +- +-#if USE_DEVICE_LEDGER ++#if defined(HAVE_HIDAPI) || defined(HIDAPI_DUMMY) + #define WITH_DEVICE_LEDGER + #endif + +diff --git a/src/device/device_io_dummy.cpp b/src/device/device_io_dummy.cpp +new file mode 100644 +index 0000000..fb08269 +--- /dev/null ++++ b/src/device/device_io_dummy.cpp +@@ -0,0 +1,133 @@ ++// Copyright (c) 2017-2022, The Monero Project ++// ++// All rights reserved. ++// ++// Redistribution and use in source and binary forms, with or without modification, are ++// permitted provided that the following conditions are met: ++// ++// 1. Redistributions of source code must retain the above copyright notice, this list of ++// conditions and the following disclaimer. ++// ++// 2. Redistributions in binary form must reproduce the above copyright notice, this list ++// of conditions and the following disclaimer in the documentation and/or other ++// materials provided with the distribution. ++// ++// 3. Neither the name of the copyright holder nor the names of its contributors may be ++// used to endorse or promote products derived from this software without specific ++// prior written permission. ++// ++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY ++// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF ++// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ++// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, ++// 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. ++// ++ ++// device_io_dummy ++// Main goal of device_io_dummy is to emulate a hw::io::device_io without the need to actually ++// connect a device. ++// Many operating systems do not support giving raw USB access to a process (android), or don't ++// support that at all (hi iOS), therefore other means of connection can be used, either USB ++// abstraction provided by the OS (monerujo), or BLE (also monerujo). ++// Monerujo implementation is written in Java, which makes it a nice fit for iOS, but makes the ++// code extremely unportable, so for this reason the code in here is written in CPP. ++// Data transport is made available in wallet2_api.h, so wallet developers can easily plug their ++// own USB/BLE/other transport layer. ++ ++#ifdef HIDAPI_DUMMY ++#include ++#include "log.hpp" ++#include "device_io_dummy.hpp" ++#include "device_ledger.hpp" ++ ++ ++bool hw::io::device_io_dummy::stateIsConnected = false; ++unsigned char* hw::io::device_io_dummy::sendToDevice = {}; ++size_t hw::io::device_io_dummy::sendToDeviceLength = 0; ++unsigned char* hw::io::device_io_dummy::receivedFromDevice = {}; ++size_t hw::io::device_io_dummy::receivedFromDeviceLength = 0; ++bool hw::io::device_io_dummy::waitsForDeviceSend = false; ++bool hw::io::device_io_dummy::waitsForDeviceReceive = false; ++ ++namespace hw { ++ namespace io { ++ ++#undef MONERO_DEFAULT_LOG_CATEGORY ++#define MONERO_DEFAULT_LOG_CATEGORY "device.io_dummy" ++ device_io_dummy::device_io_dummy(int a, int b, int c, int d) { ++ MDEBUG("device_io_dummy(a: " << a << ", b: " << b << ", c: " << c << ", d: " << d <<")"); ++ } ++ ++ void device_io_dummy::init() { ++ MDEBUG("init()"); ++ } ++ ++ void device_io_dummy::connect(void *params) { ++ MDEBUG("connect(" << params << ")"); ++ stateIsConnected = true; ++ } ++ ++ void device_io_dummy::connect(const std::vector& known_devices) { ++ MDEBUG("connect(["); ++ for (const auto &item: known_devices) { ++ MDEBUG("{ interface_number: " << item.interface_number); ++ MDEBUG(" pid : " << item.pid); ++ MDEBUG(" usage_page : " << item.usage_page); ++ MDEBUG(" vid : " << item.vid << " },"); ++ } ++ MDEBUG("])"); ++ stateIsConnected = true; ++ } ++ ++ bool device_io_dummy::connected() const { ++ MDEBUG("connected()"); ++ return stateIsConnected; ++ } ++ ++ int device_io_dummy::exchange(unsigned char *command, unsigned int cmd_len, unsigned char *response, unsigned int max_resp_len, bool user_input) { ++ MDEBUG("exchange(): locking mutex"); ++ boost::unique_lock lock(mutex); ++ sendToDevice = command; ++ sendToDeviceLength = cmd_len; ++ waitsForDeviceSend = true; ++ waitsForDeviceReceive = true; ++ MDEBUG("exchange(): waitsForDeviceSend"); ++ // NOTE: waitsForDeviceSend should be changed by external code ++ while (waitsForDeviceSend) { ++ sleep(1); ++ MDEBUG("exchange(): waitsForDeviceSend (still)"); ++ } ++ ++ MDEBUG("exchange(): waitsForDeviceReceive"); ++ while (waitsForDeviceReceive) { ++ sleep(1); ++ MDEBUG("exchange(): waitsForDeviceReceive (still)"); ++ } ++ ++ if (receivedFromDeviceLength > max_resp_len) { ++ MDEBUG("exchange(): receivedFromDeviceLength ("<& known_devices); ++ void disconnect(); ++ bool connected() const; ++ ++ int exchange(unsigned char *command, unsigned int cmd_len, unsigned char *response, unsigned int max_resp_len, bool user_input); ++ }; ++ }; ++}; ++ ++#endif // HAVE_HIDAPI +diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp +index 8403d76..db40a2e 100644 +--- a/src/device/device_ledger.cpp ++++ b/src/device/device_ledger.cpp +@@ -41,7 +41,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 { + + device_ledger::device_ledger(): hw_device(0x0101, 0x05, 64, 2000) { + this->id = device_id++; +- this->reset_buffer(); ++ this->reset_buffer(); + this->mode = NONE; + this->has_view_key = false; + this->tx_in_progress = false; +@@ -533,7 +533,9 @@ namespace hw { + + bool device_ledger::connect(void) { + this->disconnect(); ++ #ifndef HIDAPI_DUMMY + hw_device.connect(known_devices); ++ #endif + this->reset(); + #ifdef DEBUG_HWDEVICE + cryptonote::account_public_address pubkey; +diff --git a/src/device/device_ledger.hpp b/src/device/device_ledger.hpp +index 03058c4..506f27c 100644 +--- a/src/device/device_ledger.hpp ++++ b/src/device/device_ledger.hpp +@@ -35,6 +35,7 @@ + #include "device.hpp" + #include "log.hpp" + #include "device_io_hid.hpp" ++#include "device_io_dummy.hpp" + #include + #include + +@@ -56,7 +57,7 @@ namespace hw { + + void register_all(std::map> ®istry); + +- #ifdef WITH_DEVICE_LEDGER ++ #if defined(WITH_DEVICE_LEDGER) || defined(HIDAPI_DUMMY) + + // 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; + + //IO ++#ifdef HIDAPI_DUMMY ++ hw::io::device_io_dummy hw_device; ++#else + hw::io::device_io_hid hw_device; ++#endif + unsigned int length_send; + 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 ee000e7..375edb4 100644 +--- a/src/wallet/api/wallet.cpp ++++ b/src/wallet/api/wallet.cpp +@@ -48,6 +48,9 @@ + #include + #include + #include "bc-ur/src/bc-ur.hpp" ++#ifdef HIDAPI_DUMMY ++#include "device/device_io_dummy.hpp" ++#endif + + using namespace std; + using namespace cryptonote; +@@ -3178,4 +3181,95 @@ uint64_t WalletImpl::getBytesSent() + return m_wallet->get_bytes_sent(); + } + ++ ++// HIDAPI_DUMMY ++bool WalletImpl::getStateIsConnected() { ++ #ifndef HIDAPI_DUMMY ++ setStatusError("MONERO compiled with -DHIDAPI_DUMMY"); ++ return false; ++ #else ++ return hw::io::device_io_dummy::stateIsConnected; ++ #endif ++} ++ ++unsigned char* WalletImpl::getSendToDevice() { ++ #ifndef HIDAPI_DUMMY ++ setStatusError("MONERO compiled with -DHIDAPI_DUMMY"); ++ return {}; ++ #else ++ return hw::io::device_io_dummy::sendToDevice; ++ #endif ++} ++ ++size_t WalletImpl::getSendToDeviceLength() { ++ #ifndef HIDAPI_DUMMY ++ setStatusError("MONERO compiled with -DHIDAPI_DUMMY"); ++ return -1; ++ #else ++ return hw::io::device_io_dummy::sendToDeviceLength; ++ #endif ++} ++ ++unsigned char* WalletImpl::getReceivedFromDevice() { ++ #ifndef HIDAPI_DUMMY ++ setStatusError("MONERO compiled with -DHIDAPI_DUMMY"); ++ return {}; ++ #else ++ return hw::io::device_io_dummy::receivedFromDevice; ++ #endif ++} ++ ++size_t WalletImpl::getReceivedFromDeviceLength() { ++ #ifndef HIDAPI_DUMMY ++ setStatusError("MONERO compiled with -DHIDAPI_DUMMY"); ++ return -1; ++ #else ++ return hw::io::device_io_dummy::receivedFromDeviceLength; ++ #endif ++} ++ ++bool WalletImpl::getWaitsForDeviceSend() { ++ #ifndef HIDAPI_DUMMY ++ setStatusError("MONERO compiled with -DHIDAPI_DUMMY"); ++ return false; ++ #else ++ return hw::io::device_io_dummy::receivedFromDeviceLength; ++ #endif ++} ++ ++bool WalletImpl::getWaitsForDeviceReceive() { ++ #ifndef HIDAPI_DUMMY ++ setStatusError("MONERO compiled with -DHIDAPI_DUMMY"); ++ return false; ++ #else ++ return hw::io::device_io_dummy::waitsForDeviceReceive; ++ #endif ++} ++ ++void WalletImpl::setDeviceReceivedData(unsigned char* data, size_t len) { ++ #ifndef HIDAPI_DUMMY ++ setStatusError("MONERO compiled with -DHIDAPI_DUMMY"); ++ return; ++ #else ++ hw::io::device_io_dummy::receivedFromDevice = static_cast(malloc(len)); ++ hw::io::device_io_dummy::receivedFromDeviceLength = len; ++ memset(hw::io::device_io_dummy::receivedFromDevice, 0, len); ++ memcpy(hw::io::device_io_dummy::receivedFromDevice, data, len); ++ hw::io::device_io_dummy::waitsForDeviceReceive = false; ++ #endif ++} ++ ++void WalletImpl::setDeviceSendData(unsigned char* data, size_t len) { ++ #ifndef HIDAPI_DUMMY ++ setStatusError("MONERO compiled with -DHIDAPI_DUMMY"); ++ return; ++ #else ++ hw::io::device_io_dummy::sendToDevice = static_cast(malloc(len)); ++ hw::io::device_io_dummy::sendToDeviceLength = len; ++ memset(hw::io::device_io_dummy::sendToDevice, 0, len); ++ memcpy(hw::io::device_io_dummy::sendToDevice, data, len); ++ hw::io::device_io_dummy::waitsForDeviceSend = false; ++ #endif ++} ++ + } // namespace +diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h +index edf8bb8..4e9c21e 100644 +--- a/src/wallet/api/wallet.h ++++ b/src/wallet/api/wallet.h +@@ -301,6 +301,24 @@ private: + // cache connection status to avoid unnecessary RPC calls + mutable std::atomic m_is_connected; + boost::optional m_daemon_login{}; ++ ++ bool getStateIsConnected(); ++ ++ unsigned char *getSendToDevice(); ++ ++ size_t getSendToDeviceLength(); ++ ++ unsigned char *getReceivedFromDevice(); ++ ++ size_t getReceivedFromDeviceLength(); ++ ++ bool getWaitsForDeviceSend(); ++ ++ bool getWaitsForDeviceReceive(); ++ ++ void setDeviceReceivedData(unsigned char *data, size_t len); ++ ++ void setDeviceSendData(unsigned char *data, size_t len); + }; + + +diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h +index 764adbf..53ec4ab 100644 +--- a/src/wallet/api/wallet2_api.h ++++ b/src/wallet/api/wallet2_api.h +@@ -1150,6 +1150,18 @@ struct Wallet + + //! get bytes sent + virtual uint64_t getBytesSent() = 0; ++ ++ // HIDAPI_DUMMY ++ virtual bool getStateIsConnected() = 0; ++ virtual unsigned char* getSendToDevice() = 0; ++ virtual size_t getSendToDeviceLength() = 0; ++ virtual unsigned char* getReceivedFromDevice() = 0; ++ virtual size_t getReceivedFromDeviceLength() = 0; ++ virtual bool getWaitsForDeviceSend() = 0; ++ virtual bool getWaitsForDeviceReceive() = 0; ++ ++ virtual void setDeviceReceivedData(unsigned char* data, size_t len) = 0; ++ virtual void setDeviceSendData(unsigned char* data, size_t len) = 0; + }; + + /** +-- +2.39.5 (Apple Git-154) + diff --git a/patches/wownero/0009-polyseed.patch b/patches/wownero/0009-polyseed.patch deleted file mode 100644 index adedb2d..0000000 --- a/patches/wownero/0009-polyseed.patch +++ /dev/null @@ -1,1460 +0,0 @@ -From 047c1c7a0d26bb1972381813847729ac06e9efca Mon Sep 17 00:00:00 2001 -From: tobtoht -Date: Tue, 12 Mar 2024 09:42:37 +0100 -Subject: [PATCH 09/13] polyseed - -Co-authored-by: Czarek Nakamoto ---- - .gitmodules | 6 + - CMakeLists.txt | 4 +- - contrib/depends/hosts/darwin.mk | 2 + - contrib/depends/hosts/linux.mk | 8 +- - contrib/depends/packages/packages.mk | 2 +- - contrib/depends/packages/polyseed.mk | 28 +++ - contrib/depends/packages/sodium.mk | 2 +- - .../polyseed/0001-disable-soname.patch | 48 +++++ - .../patches/polyseed/force-static-mingw.patch | 23 +++ - contrib/epee/include/wipeable_string.h | 7 + - contrib/epee/src/wipeable_string.cpp | 10 + - external/CMakeLists.txt | 2 + - src/CMakeLists.txt | 1 + - src/cryptonote_basic/CMakeLists.txt | 1 + - src/cryptonote_basic/account.cpp | 23 ++- - src/cryptonote_basic/account.h | 6 + - src/cryptonote_config.h | 2 + - src/polyseed/CMakeLists.txt | 25 +++ - src/polyseed/pbkdf2.c | 85 ++++++++ - src/polyseed/pbkdf2.h | 46 +++++ - src/polyseed/polyseed.cpp | 182 ++++++++++++++++++ - src/polyseed/polyseed.hpp | 167 ++++++++++++++++ - src/wallet/api/wallet.cpp | 70 +++++++ - src/wallet/api/wallet.h | 10 + - 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 | 99 ++++++++-- - src/wallet/wallet2.h | 30 ++- - 29 files changed, 910 insertions(+), 23 deletions(-) - create mode 100644 contrib/depends/packages/polyseed.mk - create mode 100644 contrib/depends/patches/polyseed/0001-disable-soname.patch - create mode 100644 contrib/depends/patches/polyseed/force-static-mingw.patch - create mode 100644 src/polyseed/CMakeLists.txt - create mode 100644 src/polyseed/pbkdf2.c - create mode 100644 src/polyseed/pbkdf2.h - create mode 100644 src/polyseed/polyseed.cpp - create mode 100644 src/polyseed/polyseed.hpp - -diff --git a/.gitmodules b/.gitmodules -index b24855d9b..589676649 100644 ---- a/.gitmodules -+++ b/.gitmodules -@@ -20,3 +20,9 @@ - path = external/bc-ur - url = https://github.com/MrCyjaneK/bc-ur - branch = misc -+[submodule "external/utf8proc"] -+ path = external/utf8proc -+ url = https://github.com/JuliaStrings/utf8proc.git -+[submodule "external/polyseed"] -+ path = external/polyseed -+ url = https://github.com/tevador/polyseed.git -\ No newline at end of file -diff --git a/CMakeLists.txt b/CMakeLists.txt -index abe44eca5..85a62ef7b 100644 ---- a/CMakeLists.txt -+++ b/CMakeLists.txt -@@ -372,6 +372,8 @@ if(NOT MANUAL_SUBMODULES) - #check_submodule(external/trezor-common) - check_submodule(external/randomwow) - check_submodule(external/supercop) -+ check_submodule(external/polyseed) -+ check_submodule(external/utf8proc) - endif() - endif() - -@@ -461,7 +463,7 @@ endif() - # elseif(CMAKE_SYSTEM_NAME MATCHES ".*BSDI.*") - # set(BSDI TRUE) - --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) - - if(APPLE) - cmake_policy(SET CMP0042 NEW) -diff --git a/contrib/depends/hosts/darwin.mk b/contrib/depends/hosts/darwin.mk -index 83d83036b..b14ee5c5b 100644 ---- a/contrib/depends/hosts/darwin.mk -+++ b/contrib/depends/hosts/darwin.mk -@@ -8,6 +8,8 @@ endif - darwin_CC=clang -target $(CC_target) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(host_prefix)/native/SDK/ -mlinker-version=$(LD64_VERSION) -B$(host_prefix)/native/bin/$(host)- - darwin_CXX=clang++ -target $(CC_target) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(host_prefix)/native/SDK/ -mlinker-version=$(LD64_VERSION) -stdlib=libc++ -B$(host_prefix)/native/bin/$(host)- - -+darwin_RANLIB=$(host_prefix)/native/bin/$(host)-ranlib -+ - darwin_CFLAGS=-pipe - darwin_CXXFLAGS=$(darwin_CFLAGS) - darwin_ARFLAGS=cr -diff --git a/contrib/depends/hosts/linux.mk b/contrib/depends/hosts/linux.mk -index 912fdb03c..b79799f30 100644 ---- a/contrib/depends/hosts/linux.mk -+++ b/contrib/depends/hosts/linux.mk -@@ -11,15 +11,15 @@ linux_debug_CXXFLAGS=$(linux_debug_CFLAGS) - linux_debug_CPPFLAGS=-D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC - - ifeq (86,$(findstring 86,$(build_arch))) --i686_linux_CC=gcc -m32 --i686_linux_CXX=g++ -m32 -+i686_linux_CC=i686-linux-gnu-gcc -+i686_linux_CXX=i686-linux-gnu-g++ - i686_linux_AR=ar - i686_linux_RANLIB=ranlib - i686_linux_NM=nm - i686_linux_STRIP=strip - --x86_64_linux_CC=gcc -m64 --x86_64_linux_CXX=g++ -m64 -+x86_64_linux_CC=x86_64-linux-gnu-gcc -+x86_64_linux_CXX=x86_64-linux-gnu-g++ - x86_64_linux_AR=ar - x86_64_linux_RANLIB=ranlib - x86_64_linux_NM=nm -diff --git a/contrib/depends/packages/packages.mk b/contrib/depends/packages/packages.mk -index d2d1eca85..8783d4955 100644 ---- a/contrib/depends/packages/packages.mk -+++ b/contrib/depends/packages/packages.mk -@@ -1,4 +1,4 @@ --packages:=boost openssl zeromq libiconv expat unbound -+packages:=boost openssl zeromq libiconv expat unbound polyseed - - # ccache is useless in gitian builds - ifneq ($(GITIAN),1) -diff --git a/contrib/depends/packages/polyseed.mk b/contrib/depends/packages/polyseed.mk -new file mode 100644 -index 000000000..0071b20f3 ---- /dev/null -+++ b/contrib/depends/packages/polyseed.mk -@@ -0,0 +1,28 @@ -+package=polyseed -+$(package)_version=2.0.0 -+$(package)_download_path=https://github.com/tevador/$(package)/archive/refs/tags/ -+$(package)_download_file=v$($(package)_version).tar.gz -+$(package)_file_name=$(package)-$($(package)_version).tar.gz -+$(package)_sha256_hash=f36282fcbcd68d32461b8230c89e1a40661bd46b91109681cec637433004135a -+$(package)_patches=force-static-mingw.patch 0001-disable-soname.patch -+ -+define $(package)_preprocess_cmds -+ patch -p1 < $($(package)_patch_dir)/force-static-mingw.patch &&\ -+ patch -p1 < $($(package)_patch_dir)/0001-disable-soname.patch -+endef -+ -+define $(package)_config_cmds -+ CC="$($(package)_cc)" cmake -DCMAKE_INSTALL_PREFIX="$(host_prefix)" . -+endef -+ -+define $(package)_set_vars -+ $(package)_build_opts=CC="$($(package)_cc)" -+endef -+ -+define $(package)_build_cmds -+ CC="$($(package)_cc)" $(MAKE) -+endef -+ -+define $(package)_stage_cmds -+ $(MAKE) DESTDIR=$($(package)_staging_dir) install -+endef -diff --git a/contrib/depends/packages/sodium.mk b/contrib/depends/packages/sodium.mk -index 87b34599e..68a5b48ba 100644 ---- a/contrib/depends/packages/sodium.mk -+++ b/contrib/depends/packages/sodium.mk -@@ -6,7 +6,7 @@ $(package)_sha256_hash=6f504490b342a4f8a4c4a02fc9b866cbef8622d5df4e5452b46be121e - $(package)_patches=disable-glibc-getrandom-getentropy.patch fix-whitespace.patch - - define $(package)_set_vars --$(package)_config_opts=--enable-static --disable-shared -+$(package)_config_opts=--enable-static --disable-shared --with-pic - $(package)_config_opts+=--prefix=$(host_prefix) - endef - -diff --git a/contrib/depends/patches/polyseed/0001-disable-soname.patch b/contrib/depends/patches/polyseed/0001-disable-soname.patch -new file mode 100644 -index 000000000..bd97dd394 ---- /dev/null -+++ b/contrib/depends/patches/polyseed/0001-disable-soname.patch -@@ -0,0 +1,48 @@ -+From aabafcfc0572651436d024a635483c49042fad7f Mon Sep 17 00:00:00 2001 -+From: Czarek Nakamoto -+Date: Thu, 28 Mar 2024 00:32:51 +0100 -+Subject: [PATCH] disable soname -+ -+--- -+ CMakeLists.txt | 16 +++++++++------- -+ 1 file changed, 9 insertions(+), 7 deletions(-) -+ -+diff --git a/CMakeLists.txt b/CMakeLists.txt -+index 8a8e7c2..5301353 100644 -+--- a/CMakeLists.txt -++++ b/CMakeLists.txt -+@@ -36,6 +36,7 @@ include_directories(polyseed -+ target_compile_definitions(polyseed PRIVATE POLYSEED_SHARED) -+ set_target_properties(polyseed PROPERTIES VERSION 2.0.0 -+ SOVERSION 2 -++ NO_SONAME 1 -+ C_STANDARD 11 -+ C_STANDARD_REQUIRED ON) -+ -+@@ -45,16 +46,17 @@ include_directories(polyseed_static -+ include/) -+ target_compile_definitions(polyseed_static PRIVATE POLYSEED_STATIC) -+ set_target_properties(polyseed_static PROPERTIES OUTPUT_NAME polyseed -++ NO_SONAME 1 -+ C_STANDARD 11 -+ C_STANDARD_REQUIRED ON) -+ -+-add_executable(polyseed-tests -+- tests/tests.c) -+-include_directories(polyseed-tests -+- include/) -+-target_compile_definitions(polyseed-tests PRIVATE POLYSEED_STATIC) -+-target_link_libraries(polyseed-tests -+- PRIVATE polyseed_static) -++# add_executable(polyseed-tests -++# tests/tests.c) -++# include_directories(polyseed-tests -++# include/) -++# target_compile_definitions(polyseed-tests PRIVATE POLYSEED_STATIC) -++# target_link_libraries(polyseed-tests -++# PRIVATE polyseed_static) -+ -+ include(GNUInstallDirs) -+ install(TARGETS polyseed polyseed_static -+-- -+2.39.2 -diff --git a/contrib/depends/patches/polyseed/force-static-mingw.patch b/contrib/depends/patches/polyseed/force-static-mingw.patch -new file mode 100644 -index 000000000..f05cb2b6a ---- /dev/null -+++ b/contrib/depends/patches/polyseed/force-static-mingw.patch -@@ -0,0 +1,23 @@ -+--- a/include/polyseed.h -++++ b/include/polyseed.h -+@@ -93,13 +93,13 @@ Shared/static library definitions -+ - define POLYSEED_STATIC when linking to the static library -+ */ -+ #if defined(_WIN32) || defined(__CYGWIN__) -+- #ifdef POLYSEED_SHARED -+- #define POLYSEED_API __declspec(dllexport) -+- #elif !defined(POLYSEED_STATIC) -+- #define POLYSEED_API __declspec(dllimport) -+- #else -+- #define POLYSEED_API -+- #endif -++// #ifdef POLYSEED_SHARED -++// #define POLYSEED_API __declspec(dllexport) -++// #elif !defined(POLYSEED_STATIC) -++// #define POLYSEED_API __declspec(dllimport) -++// #else -++ #define POLYSEED_API -++// #endif -+ #define POLYSEED_PRIVATE -+ #else -+ #ifdef POLYSEED_SHARED -diff --git a/contrib/epee/include/wipeable_string.h b/contrib/epee/include/wipeable_string.h -index 65977cd97..594e15de4 100644 ---- a/contrib/epee/include/wipeable_string.h -+++ b/contrib/epee/include/wipeable_string.h -@@ -34,6 +34,7 @@ - #include - #include "memwipe.h" - #include "fnv1.h" -+#include "serialization/keyvalue_serialization.h" - - namespace epee - { -@@ -75,6 +76,12 @@ namespace epee - bool operator!=(const wipeable_string &other) const noexcept { return buffer != other.buffer; } - wipeable_string &operator=(wipeable_string &&other); - wipeable_string &operator=(const wipeable_string &other); -+ char& operator[](size_t idx); -+ const char& operator[](size_t idx) const; -+ -+ BEGIN_KV_SERIALIZE_MAP() -+ KV_SERIALIZE_CONTAINER_POD_AS_BLOB(buffer) -+ END_KV_SERIALIZE_MAP() - - 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 ---- 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) - return *this; - } - -+char& wipeable_string::operator[](size_t idx) { -+ CHECK_AND_ASSERT_THROW_MES(idx < buffer.size(), "Index out of bounds"); -+ return buffer[idx]; -+} -+ -+const char& wipeable_string::operator[](size_t idx) const { -+ CHECK_AND_ASSERT_THROW_MES(idx < buffer.size(), "Index out of bounds"); -+ return buffer[idx]; -+} -+ - } -diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt -index 88a7bb0b5..f8f4b3c0f 100644 ---- a/external/CMakeLists.txt -+++ b/external/CMakeLists.txt -@@ -73,4 +73,6 @@ add_subdirectory(db_drivers) - add_subdirectory(easylogging++) - add_subdirectory(qrcodegen) - add_subdirectory(bc-ur) -+add_subdirectory(polyseed EXCLUDE_FROM_ALL) -+add_subdirectory(utf8proc EXCLUDE_FROM_ALL) - add_subdirectory(randomwow EXCLUDE_FROM_ALL) -diff --git a/external/polyseed b/external/polyseed -new file mode 160000 -index 000000000..dfb05d8ed ---- /dev/null -+++ b/external/polyseed -@@ -0,0 +1 @@ -+Subproject commit dfb05d8edb682b0e8f743b1b70c9131712ff4157 -diff --git a/external/utf8proc b/external/utf8proc -new file mode 160000 -index 000000000..3de4596fb ---- /dev/null -+++ b/external/utf8proc -@@ -0,0 +1 @@ -+Subproject commit 3de4596fbe28956855df2ecb3c11c0bbc3535838 -diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt -index 9216bcaa5..c043ba150 100644 ---- a/src/CMakeLists.txt -+++ b/src/CMakeLists.txt -@@ -95,6 +95,7 @@ add_subdirectory(net) - add_subdirectory(hardforks) - add_subdirectory(blockchain_db) - add_subdirectory(mnemonics) -+add_subdirectory(polyseed) - add_subdirectory(rpc) - if(NOT IOS) - add_subdirectory(serialization) -diff --git a/src/cryptonote_basic/CMakeLists.txt b/src/cryptonote_basic/CMakeLists.txt -index 1414be1b2..414936a05 100644 ---- a/src/cryptonote_basic/CMakeLists.txt -+++ b/src/cryptonote_basic/CMakeLists.txt -@@ -71,6 +71,7 @@ target_link_libraries(cryptonote_basic - checkpoints - cryptonote_format_utils_basic - device -+ polyseed_wrapper - ${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 ---- a/src/cryptonote_basic/account.cpp -+++ b/src/cryptonote_basic/account.cpp -@@ -87,12 +87,16 @@ DISABLE_VS_WARNINGS(4244 4345) - void account_keys::xor_with_key_stream(const crypto::chacha_key &key) - { - // encrypt a large enough byte stream with chacha20 -- epee::wipeable_string key_stream = get_key_stream(key, m_encryption_iv, sizeof(crypto::secret_key) * (2 + m_multisig_keys.size())); -+ epee::wipeable_string key_stream = get_key_stream(key, m_encryption_iv, sizeof(crypto::secret_key) * (3 + m_multisig_keys.size()) + m_passphrase.size()); - const char *ptr = key_stream.data(); - for (size_t i = 0; i < sizeof(crypto::secret_key); ++i) - m_spend_secret_key.data[i] ^= *ptr++; - for (size_t i = 0; i < sizeof(crypto::secret_key); ++i) - m_view_secret_key.data[i] ^= *ptr++; -+ for (size_t i = 0; i < sizeof(crypto::secret_key); ++i) -+ m_polyseed.data[i] ^= *ptr++; -+ for (size_t i = 0; i < m_passphrase.size(); ++i) -+ m_passphrase.data()[i] ^= *ptr++; - for (crypto::secret_key &k: m_multisig_keys) - { - for (size_t i = 0; i < sizeof(crypto::secret_key); ++i) -@@ -150,6 +154,8 @@ DISABLE_VS_WARNINGS(4244 4345) - { - m_keys.m_spend_secret_key = crypto::secret_key(); - m_keys.m_multisig_keys.clear(); -+ m_keys.m_polyseed = crypto::secret_key(); -+ m_keys.m_passphrase.wipe(); - } - //----------------------------------------------------------------- - void account_base::set_spend_key(const crypto::secret_key& spend_secret_key) -@@ -255,6 +261,21 @@ DISABLE_VS_WARNINGS(4244 4345) - create_from_keys(address, fake, viewkey); - } - //----------------------------------------------------------------- -+ void account_base::create_from_polyseed(const polyseed::data& seed, const epee::wipeable_string &passphrase) -+ { -+ crypto::secret_key secret_key; -+ seed.keygen(&secret_key, sizeof(secret_key)); -+ -+ if (!passphrase.empty()) { -+ secret_key = cryptonote::decrypt_key(secret_key, passphrase); -+ } -+ -+ generate(secret_key, true, false); -+ -+ seed.save(m_keys.m_polyseed.data); -+ m_keys.m_passphrase = passphrase; -+ } -+ //----------------------------------------------------------------- - bool account_base::make_multisig(const crypto::secret_key &view_secret_key, const crypto::secret_key &spend_secret_key, const crypto::public_key &spend_public_key, const std::vector &multisig_keys) - { - 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 ---- a/src/cryptonote_basic/account.h -+++ b/src/cryptonote_basic/account.h -@@ -33,6 +33,7 @@ - #include "cryptonote_basic.h" - #include "crypto/crypto.h" - #include "serialization/keyvalue_serialization.h" -+#include "polyseed/polyseed.hpp" - - namespace cryptonote - { -@@ -45,6 +46,8 @@ namespace cryptonote - std::vector m_multisig_keys; - hw::device *m_device = &hw::get_device("default"); - crypto::chacha_iv m_encryption_iv; -+ crypto::secret_key m_polyseed; -+ epee::wipeable_string m_passphrase; // Only used with polyseed - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(m_account_address) -@@ -53,6 +56,8 @@ namespace cryptonote - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(m_multisig_keys) - const crypto::chacha_iv default_iv{{0, 0, 0, 0, 0, 0, 0, 0}}; - KV_SERIALIZE_VAL_POD_AS_BLOB_OPT(m_encryption_iv, default_iv) -+ KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(m_polyseed) -+ KV_SERIALIZE(m_passphrase) - END_KV_SERIALIZE_MAP() - - void encrypt(const crypto::chacha_key &key); -@@ -79,6 +84,7 @@ namespace cryptonote - void create_from_device(hw::device &hwdev); - void create_from_keys(const cryptonote::account_public_address& address, const crypto::secret_key& spendkey, const crypto::secret_key& viewkey); - void create_from_viewkey(const cryptonote::account_public_address& address, const crypto::secret_key& viewkey); -+ void create_from_polyseed(const polyseed::data &polyseed, const epee::wipeable_string &passphrase); - bool make_multisig(const crypto::secret_key &view_secret_key, const crypto::secret_key &spend_secret_key, const crypto::public_key &spend_public_key, const std::vector &multisig_keys); - 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 8b5091a46..a192bd64d 100644 ---- a/src/cryptonote_config.h -+++ b/src/cryptonote_config.h -@@ -219,6 +219,8 @@ - - #define DNS_BLOCKLIST_LIFETIME (86400 * 8) - -+#define POLYSEED_COIN POLYSEED_WOWNERO -+ - //The limit is enough for the mandatory transaction content with 16 outputs (547 bytes), - //a custom tag (1 byte) and up to 32 bytes of custom data for each recipient. - // (1+32) + (1+1+16*32) + (1+16*32) = 1060 -diff --git a/src/polyseed/CMakeLists.txt b/src/polyseed/CMakeLists.txt -new file mode 100644 -index 000000000..cca4eb746 ---- /dev/null -+++ b/src/polyseed/CMakeLists.txt -@@ -0,0 +1,25 @@ -+set(polyseed_sources -+ pbkdf2.c -+ polyseed.cpp -+) -+ -+monero_find_all_headers(polyseed_private_headers "${CMAKE_CURRENT_SOURCE_DIR}") -+ -+monero_private_headers(polyseed_wrapper -+ ${polyseed_private_headers} -+) -+ -+monero_add_library(polyseed_wrapper -+ ${polyseed_sources} -+ ${polyseed_headers} -+ ${polyseed_private_headers} -+) -+ -+target_link_libraries(polyseed_wrapper -+PUBLIC -+ polyseed -+ utf8proc -+ ${SODIUM_LIBRARY} -+ PRIVATE -+ ${EXTRA_LIBRARIES} -+) -diff --git a/src/polyseed/pbkdf2.c b/src/polyseed/pbkdf2.c -new file mode 100644 -index 000000000..1c45f4708 ---- /dev/null -+++ b/src/polyseed/pbkdf2.c -@@ -0,0 +1,85 @@ -+// Copyright (c) 2023, The Monero Project -+// Copyright (c) 2021, tevador -+// Copyright (c) 2005,2007,2009 Colin Percival -+// -+// All rights reserved. -+// -+// Redistribution and use in source and binary forms, with or without -+// modification, are permitted provided that the following conditions -+// are met: -+// 1. Redistributions of source code must retain the above copyright -+// notice, this list of conditions and the following disclaimer. -+// 2. Redistributions in binary form must reproduce the above copyright -+// notice, this list of conditions and the following disclaimer in the -+// documentation and/or other materials provided with the distribution. -+// -+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -+// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE -+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -+// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -+// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 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. -+ -+#include -+ -+#include -+#include -+ -+static inline void -+store32_be(uint8_t dst[4], uint32_t w) -+{ -+ dst[3] = (uint8_t) w; w >>= 8; -+ dst[2] = (uint8_t) w; w >>= 8; -+ dst[1] = (uint8_t) w; w >>= 8; -+ dst[0] = (uint8_t) w; -+} -+ -+void -+crypto_pbkdf2_sha256(const uint8_t* passwd, size_t passwdlen, -+ const uint8_t* salt, size_t saltlen, uint64_t c, -+ uint8_t* buf, size_t dkLen) -+{ -+ crypto_auth_hmacsha256_state Phctx, PShctx, hctx; -+ size_t i; -+ uint8_t ivec[4]; -+ uint8_t U[32]; -+ uint8_t T[32]; -+ uint64_t j; -+ int k; -+ size_t clen; -+ -+ crypto_auth_hmacsha256_init(&Phctx, passwd, passwdlen); -+ PShctx = Phctx; -+ crypto_auth_hmacsha256_update(&PShctx, salt, saltlen); -+ -+ for (i = 0; i * 32 < dkLen; i++) { -+ store32_be(ivec, (uint32_t)(i + 1)); -+ hctx = PShctx; -+ crypto_auth_hmacsha256_update(&hctx, ivec, 4); -+ crypto_auth_hmacsha256_final(&hctx, U); -+ -+ memcpy(T, U, 32); -+ for (j = 2; j <= c; j++) { -+ hctx = Phctx; -+ crypto_auth_hmacsha256_update(&hctx, U, 32); -+ crypto_auth_hmacsha256_final(&hctx, U); -+ -+ for (k = 0; k < 32; k++) { -+ T[k] ^= U[k]; -+ } -+ } -+ -+ clen = dkLen - i * 32; -+ if (clen > 32) { -+ clen = 32; -+ } -+ memcpy(&buf[i * 32], T, clen); -+ } -+ sodium_memzero((void*)&Phctx, sizeof Phctx); -+ sodium_memzero((void*)&PShctx, sizeof PShctx); -+} -\ No newline at end of file -diff --git a/src/polyseed/pbkdf2.h b/src/polyseed/pbkdf2.h -new file mode 100644 -index 000000000..f6253b9d7 ---- /dev/null -+++ b/src/polyseed/pbkdf2.h -@@ -0,0 +1,46 @@ -+// Copyright (c) 2023, The Monero Project -+// Copyright (c) 2021, tevador -+// -+// All rights reserved. -+// -+// Redistribution and use in source and binary forms, with or without -+// modification, are permitted provided that the following conditions -+// are met: -+// 1. Redistributions of source code must retain the above copyright -+// notice, this list of conditions and the following disclaimer. -+// 2. Redistributions in binary form must reproduce the above copyright -+// notice, this list of conditions and the following disclaimer in the -+// documentation and/or other materials provided with the distribution. -+// -+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -+// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE -+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -+// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -+// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 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. -+ -+#ifndef PBKDF2_H -+#define PBKDF2_H -+ -+#include -+#include -+ -+#ifdef __cplusplus -+extern "C" { -+#endif -+ -+void -+crypto_pbkdf2_sha256(const uint8_t* passwd, size_t passwdlen, -+ const uint8_t* salt, size_t saltlen, uint64_t c, -+ uint8_t* buf, size_t dkLen); -+ -+#ifdef __cplusplus -+} -+#endif -+ -+#endif -\ No newline at end of file -diff --git a/src/polyseed/polyseed.cpp b/src/polyseed/polyseed.cpp -new file mode 100644 -index 000000000..231a48a94 ---- /dev/null -+++ b/src/polyseed/polyseed.cpp -@@ -0,0 +1,182 @@ -+// Copyright (c) 2023, The Monero Project -+// Copyright (c) 2021, tevador -+// -+// All rights reserved. -+// -+// Redistribution and use in source and binary forms, with or without -+// modification, are permitted provided that the following conditions -+// are met: -+// 1. Redistributions of source code must retain the above copyright -+// notice, this list of conditions and the following disclaimer. -+// 2. Redistributions in binary form must reproduce the above copyright -+// notice, this list of conditions and the following disclaimer in the -+// documentation and/or other materials provided with the distribution. -+// -+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -+// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE -+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -+// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -+// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 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. -+ -+#include "polyseed.hpp" -+#include "pbkdf2.h" -+ -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+ -+namespace polyseed { -+ -+ inline size_t utf8_norm(const char* str, polyseed_str norm, utf8proc_option_t options) { -+ utf8proc_int32_t buffer[POLYSEED_STR_SIZE]; -+ utf8proc_ssize_t result; -+ -+ result = utf8proc_decompose(reinterpret_cast(str), 0, buffer, POLYSEED_STR_SIZE, options); -+ if (result < 0 || result > (POLYSEED_STR_SIZE - 1)) { -+ throw std::runtime_error("Unicode normalization failed"); -+ } -+ -+ result = utf8proc_reencode(buffer, result, options); -+ if (result < 0 || result > POLYSEED_STR_SIZE) { -+ throw std::runtime_error("Unicode normalization failed"); -+ } -+ -+ strcpy(norm, reinterpret_cast(buffer)); -+ sodium_memzero(buffer, POLYSEED_STR_SIZE); -+ return result; -+ } -+ -+ static size_t utf8_nfc(const char* str, polyseed_str norm) { -+ // Note: UTF8PROC_LUMP is used here to replace the ideographic space with a regular space for Japanese phrases -+ // to allow wallets to split on ' '. -+ return utf8_norm(str, norm, (utf8proc_option_t)(UTF8PROC_NULLTERM | UTF8PROC_STABLE | UTF8PROC_COMPOSE | UTF8PROC_STRIPNA)); -+ } -+ -+ static size_t utf8_nfkd(const char* str, polyseed_str norm) { -+ return utf8_norm(str, norm, (utf8proc_option_t)(UTF8PROC_NULLTERM | UTF8PROC_STABLE | UTF8PROC_DECOMPOSE | UTF8PROC_COMPAT | UTF8PROC_STRIPNA)); -+ } -+ -+ struct dependency { -+ dependency(); -+ std::vector languages; -+ }; -+ -+ static dependency deps; -+ -+ dependency::dependency() { -+ if (sodium_init() == -1) { -+ throw std::runtime_error("sodium_init failed"); -+ } -+ -+ polyseed_dependency pd; -+ pd.randbytes = &randombytes_buf; -+ pd.pbkdf2_sha256 = &crypto_pbkdf2_sha256; -+ pd.memzero = &sodium_memzero; -+ pd.u8_nfc = &utf8_nfc; -+ pd.u8_nfkd = &utf8_nfkd; -+ pd.time = nullptr; -+ pd.alloc = nullptr; -+ pd.free = nullptr; -+ -+ polyseed_inject(&pd); -+ -+ for (int i = 0; i < polyseed_get_num_langs(); ++i) { -+ languages.push_back(language(polyseed_get_lang(i))); -+ } -+ } -+ -+ static language invalid_lang; -+ -+ const std::vector& get_langs() { -+ return deps.languages; -+ } -+ -+ const language& get_lang_by_name(const std::string& name) { -+ for (auto& lang : deps.languages) { -+ if (name == lang.name_en()) { -+ return lang; -+ } -+ if (name == lang.name()) { -+ return lang; -+ } -+ } -+ return invalid_lang; -+ } -+ -+ inline void data::check_init() const { -+ if (valid()) { -+ throw std::runtime_error("already initialized"); -+ } -+ } -+ -+ static std::array error_desc = { -+ "Success", -+ "Wrong number of words in the phrase", -+ "Unknown language or unsupported words", -+ "Checksum mismatch", -+ "Unsupported seed features", -+ "Invalid seed format", -+ "Memory allocation failure", -+ "Unicode normalization failed" -+ }; -+ -+ static error get_error(polyseed_status status) { -+ if (status > 0 && status < sizeof(error_desc) / sizeof(const char*)) { -+ return error(error_desc[(int)status], status); -+ } -+ return error("Unknown error", status); -+ } -+ -+ void data::create(feature_type features) { -+ check_init(); -+ auto status = polyseed_create(features, &m_data); -+ if (status != POLYSEED_OK) { -+ throw get_error(status); -+ } -+ } -+ -+ void data::split(const language& lang, polyseed_phrase& words) { -+ check_init(); -+ if (!lang.valid()) { -+ throw std::runtime_error("invalid language"); -+ } -+ } -+ -+ void data::load(polyseed_storage storage) { -+ check_init(); -+ auto status = polyseed_load(storage, &m_data); -+ if (status != POLYSEED_OK) { -+ throw get_error(status); -+ } -+ } -+ -+ void data::load(const crypto::secret_key &key) { -+ polyseed_storage d; -+ memcpy(&d, &key.data, 32); -+ auto status = polyseed_load(d, &m_data); -+ if (status != POLYSEED_OK) { -+ throw get_error(status); -+ } -+ } -+ -+ language data::decode(const char* phrase) { -+ check_init(); -+ const polyseed_lang* lang; -+ auto status = polyseed_decode(phrase, m_coin, &lang, &m_data); -+ if (status != POLYSEED_OK) { -+ throw get_error(status); -+ } -+ return language(lang); -+ } -+} -diff --git a/src/polyseed/polyseed.hpp b/src/polyseed/polyseed.hpp -new file mode 100644 -index 000000000..2c8c777a7 ---- /dev/null -+++ b/src/polyseed/polyseed.hpp -@@ -0,0 +1,167 @@ -+// Copyright (c) 2023, The Monero Project -+// Copyright (c) 2021, tevador -+// -+// All rights reserved. -+// -+// Redistribution and use in source and binary forms, with or without -+// modification, are permitted provided that the following conditions -+// are met: -+// 1. Redistributions of source code must retain the above copyright -+// notice, this list of conditions and the following disclaimer. -+// 2. Redistributions in binary form must reproduce the above copyright -+// notice, this list of conditions and the following disclaimer in the -+// documentation and/or other materials provided with the distribution. -+// -+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -+// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE -+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -+// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -+// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 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. -+ -+#ifndef POLYSEED_HPP -+#define POLYSEED_HPP -+ -+#include -+#include -+#include -+#include -+#include -+#include "crypto/crypto.h" -+ -+namespace polyseed { -+ -+ class data; -+ -+ class language { -+ public: -+ language() : m_lang(nullptr) {} -+ language(const language&) = default; -+ language(const polyseed_lang* lang) : m_lang(lang) {} -+ const char* name() const { -+ return polyseed_get_lang_name(m_lang); -+ } -+ const char* name_en() const { -+ return polyseed_get_lang_name_en(m_lang); -+ } -+ const char* separator() const { -+ return m_lang->separator; -+ } -+ bool valid() const { -+ return m_lang != nullptr; -+ } -+ -+ const polyseed_lang* m_lang; -+ private: -+ -+ friend class data; -+ }; -+ -+ const std::vector& get_langs(); -+ const language& get_lang_by_name(const std::string& name); -+ -+ class error : public std::runtime_error { -+ public: -+ error(const char* msg, polyseed_status status) -+ : std::runtime_error(msg), m_status(status) -+ { -+ } -+ polyseed_status status() const { -+ return m_status; -+ } -+ private: -+ polyseed_status m_status; -+ }; -+ -+ using feature_type = unsigned int; -+ -+ inline int enable_features(feature_type features) { -+ return polyseed_enable_features(features); -+ } -+ -+ class data { -+ public: -+ data(const data&) = delete; -+ data(polyseed_coin coin) : m_data(nullptr), m_coin(coin) {} -+ ~data() { -+ polyseed_free(m_data); -+ } -+ -+ void create(feature_type features); -+ -+ void load(polyseed_storage storage); -+ -+ void load(const crypto::secret_key &key); -+ -+ language decode(const char* phrase); -+ -+ template -+ void encode(const language& lang, str_type& str) const { -+ check_valid(); -+ if (!lang.valid()) { -+ throw std::runtime_error("invalid language"); -+ } -+ str.resize(POLYSEED_STR_SIZE); -+ auto size = polyseed_encode(m_data, lang.m_lang, m_coin, &str[0]); -+ str.resize(size); -+ } -+ -+ void split(const language& lang, polyseed_phrase& words); -+ -+ void save(polyseed_storage storage) const { -+ check_valid(); -+ polyseed_store(m_data, storage); -+ } -+ -+ void save(void *storage) const { -+ check_valid(); -+ polyseed_store(m_data, (uint8_t*)storage); -+ } -+ -+ void crypt(const char* password) { -+ check_valid(); -+ polyseed_crypt(m_data, password); -+ } -+ -+ void keygen(void* ptr, size_t key_size) const { -+ check_valid(); -+ polyseed_keygen(m_data, m_coin, key_size, (uint8_t*)ptr); -+ } -+ -+ bool valid() const { -+ return m_data != nullptr; -+ } -+ -+ bool encrypted() const { -+ check_valid(); -+ return polyseed_is_encrypted(m_data); -+ } -+ -+ uint64_t birthday() const { -+ check_valid(); -+ return polyseed_get_birthday(m_data); -+ } -+ -+ bool has_feature(feature_type feature) const { -+ check_valid(); -+ return polyseed_get_feature(m_data, feature) != 0; -+ } -+ private: -+ void check_valid() const { -+ if (m_data == nullptr) { -+ throw std::runtime_error("invalid object"); -+ } -+ } -+ void check_init() const; -+ -+ polyseed_data* m_data; -+ polyseed_coin m_coin; -+ }; -+} -+ -+#endif //POLYSEED_HPP -\ No newline at end of file -diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp -index 375edb4f1..0d785360a 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 - return true; - } - -+bool WalletImpl::createFromPolyseed(const std::string &path, const std::string &password, const std::string &seed, -+ const std::string &passphrase, bool newWallet, uint64_t restoreHeight) -+{ -+ clearStatus(); -+ m_recoveringFromSeed = !newWallet; -+ m_recoveringFromDevice = false; -+ -+ polyseed::data polyseed(POLYSEED_COIN); -+ -+ try { -+ auto lang = polyseed.decode(seed.data()); -+ m_wallet->set_seed_language(lang.name()); -+ m_wallet->generate(path, password, polyseed, passphrase, !newWallet); -+ } -+ catch (const std::exception &e) { -+ setStatusError(e.what()); -+ return false; -+ } -+ -+ return true; -+} -+ - 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 - } - } - -+bool WalletImpl::getPolyseed(std::string &seed_words, std::string &passphrase) const -+{ -+ epee::wipeable_string seed_words_epee(seed_words.c_str(), seed_words.size()); -+ epee::wipeable_string passphrase_epee(passphrase.c_str(), passphrase.size()); -+ clearStatus(); -+ -+ if (!m_wallet) { -+ return false; -+ } -+ -+ bool result = m_wallet->get_polyseed(seed_words_epee, passphrase_epee); -+ -+ seed_words.assign(seed_words_epee.data(), seed_words_epee.size()); -+ passphrase.assign(passphrase_epee.data(), passphrase_epee.size()); -+ -+ return result; -+} -+ -+std::vector> Wallet::getPolyseedLanguages() -+ { -+ std::vector> languages; -+ -+ auto langs = polyseed::get_langs(); -+ for (const auto &lang : langs) { -+ languages.emplace_back(std::pair(lang.name_en(), lang.name())); -+ } -+ -+ return languages; -+} -+ -+bool Wallet::createPolyseed(std::string &seed_words, std::string &err, const std::string &language) -+{ -+ epee::wipeable_string seed_words_epee(seed_words.c_str(), seed_words.size()); -+ -+ try { -+ polyseed::data polyseed(POLYSEED_COIN); -+ polyseed.create(0); -+ polyseed.encode(polyseed::get_lang_by_name(language), seed_words_epee); -+ -+ seed_words.assign(seed_words_epee.data(), seed_words_epee.size()); -+ } -+ catch (const std::exception &e) { -+ err = e.what(); -+ return false; -+ } -+ -+ return true; -+} - std::string WalletImpl::getSeedLanguage() const - { - return m_wallet->get_seed_language(); -diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h -index 4e9c21ecb..32e12284b 100644 ---- a/src/wallet/api/wallet.h -+++ b/src/wallet/api/wallet.h -@@ -79,9 +79,19 @@ public: - bool recoverFromDevice(const std::string &path, - const std::string &password, - const std::string &device_name); -+ -+ bool createFromPolyseed(const std::string &path, -+ const std::string &password, -+ const std::string &seed, -+ const std::string &passphrase = "", -+ bool newWallet = true, -+ uint64_t restoreHeight = 0); -+ - Device getDeviceType() const override; - bool close(bool store = true); - std::string seed(const std::string& seed_offset = "") const override; -+ bool getPolyseed(std::string &seed_words, std::string &passphrase) const override; -+ - std::string getSeedLanguage() const override; - 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 53ec4abfc..be1c3704e 100644 ---- a/src/wallet/api/wallet2_api.h -+++ b/src/wallet/api/wallet2_api.h -@@ -709,6 +709,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); - -+ virtual bool getPolyseed(std::string &seed, std::string &passphrase) const = 0; -+ static bool createPolyseed(std::string &seed_words, std::string &err, const std::string &language = "English"); -+ static std::vector> getPolyseedLanguages(); -+ - /** - * @brief StartRefresh - Start/resume refresh thread (refresh every 10 seconds) - */ -@@ -1320,6 +1324,27 @@ struct WalletManager - uint64_t kdf_rounds = 1, - WalletListener * listener = nullptr) = 0; - -+ /*! -+ * \brief creates a wallet from a polyseed mnemonic phrase -+ * \param path Name of the wallet file to be created -+ * \param password Password of wallet file -+ * \param nettype Network type -+ * \param mnemonic Polyseed mnemonic -+ * \param passphrase Optional seed offset passphrase -+ * \param newWallet Whether it is a new wallet -+ * \param restoreHeight Override the embedded restore height -+ * \param kdf_rounds Number of rounds for key derivation function -+ * @return -+ */ -+ virtual Wallet * createWalletFromPolyseed(const std::string &path, -+ const std::string &password, -+ NetworkType nettype, -+ const std::string &mnemonic, -+ const std::string &passphrase = "", -+ bool newWallet = true, -+ uint64_t restore_height = 0, -+ uint64_t kdf_rounds = 1) = 0; -+ - /*! - * \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 e81b8f83a..c79fe25d6 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, - return wallet; - } - -+Wallet *WalletManagerImpl::createWalletFromPolyseed(const std::string &path, const std::string &password, NetworkType nettype, -+ const std::string &mnemonic, const std::string &passphrase, -+ bool newWallet, uint64_t restoreHeight, uint64_t kdf_rounds) -+{ -+ WalletImpl * wallet = new WalletImpl(nettype, kdf_rounds); -+ wallet->createFromPolyseed(path, password, mnemonic, passphrase, newWallet, restoreHeight); -+ return wallet; -+} -+ - bool WalletManagerImpl::closeWallet(Wallet *wallet, bool store) - { - 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 ---- a/src/wallet/api/wallet_manager.h -+++ b/src/wallet/api/wallet_manager.h -@@ -75,6 +75,16 @@ public: - const std::string &subaddressLookahead = "", - uint64_t kdf_rounds = 1, - WalletListener * listener = nullptr) override; -+ -+ virtual Wallet * createWalletFromPolyseed(const std::string &path, -+ const std::string &password, -+ NetworkType nettype, -+ const std::string &mnemonic, -+ const std::string &passphrase, -+ bool newWallet = true, -+ uint64_t restore_height = 0, -+ uint64_t kdf_rounds = 1) override; -+ - virtual bool closeWallet(Wallet *wallet, bool store = true) override; - 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 671fa5298..3e49c21f8 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" -+#include "polyseed/include/polyseed.h" - - extern "C" - { -@@ -1278,7 +1279,8 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended, std - m_enable_multisig(false), - m_pool_info_query_time(0), - m_has_ever_refreshed_from_node(false), -- m_allow_mismatched_daemon_version(true) -+ m_allow_mismatched_daemon_version(true), -+ m_polyseed(false) - { - set_rpc_client_secret_key(rct::rct2sk(rct::skGen())); - } -@@ -1474,6 +1476,20 @@ bool wallet2::get_seed(epee::wipeable_string& electrum_words, const epee::wipeab - return true; - } - //---------------------------------------------------------------------------------------------------- -+ -+bool wallet2::get_polyseed(epee::wipeable_string& polyseed, epee::wipeable_string& passphrase) const -+{ -+ if (!m_polyseed) { -+ return false; -+ } -+ -+ polyseed::data data(POLYSEED_COIN); -+ data.load(get_account().get_keys().m_polyseed); -+ data.encode(polyseed::get_lang_by_name(seed_language), polyseed); -+ passphrase = get_account().get_keys().m_passphrase; -+ return true; -+} -+//---------------------------------------------------------------------------------------------------- - bool wallet2::get_multisig_seed(epee::wipeable_string& seed, const epee::wipeable_string &passphrase) const - { - bool ready; -@@ -4792,6 +4808,9 @@ boost::optional wallet2::get_keys_file_data(const crypt - value2.SetInt(m_enable_multisig ? 1 : 0); - json.AddMember("enable_multisig", value2, json.GetAllocator()); - -+ value2.SetInt(m_polyseed ? 1 : 0); -+ json.AddMember("polyseed", value2, json.GetAllocator()); -+ - if (m_background_sync_type == BackgroundSyncCustomPassword && !background_keys_file && m_custom_background_key) - { - value.SetString(reinterpret_cast(m_custom_background_key.get().data()), m_custom_background_key.get().size()); -@@ -5031,6 +5050,7 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st - m_enable_multisig = false; - m_allow_mismatched_daemon_version = true; - m_custom_background_key = boost::none; -+ m_polyseed = false; - } - else if(json.IsObject()) - { -@@ -5271,6 +5291,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; - -+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, polyseed, int, Int, false, false); -+ m_polyseed = field_polyseed; -+ - // Load encryption key used to encrypt background cache - crypto::chacha_key custom_background_key; - m_custom_background_key = boost::none; -@@ -5590,6 +5613,48 @@ void wallet2::init_type(hw::device::device_type device_type) - m_key_device_type = device_type; - } - -+/*! -+ * \brief Generates a polyseed wallet or restores one. -+ * \param wallet_ Name of wallet file -+ * \param password Password of wallet file -+ * \param passphrase Seed offset passphrase -+ * \param recover Whether it is a restore -+ * \param seed_words If it is a restore, the polyseed -+ * \param create_address_file Whether to create an address file -+ * \return The secret key of the generated wallet -+ */ -+void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& password, -+ const polyseed::data &seed, const epee::wipeable_string& passphrase, bool recover, uint64_t restoreHeight, bool create_address_file) -+{ -+ clear(); -+ prepare_file_names(wallet_); -+ -+ if (!wallet_.empty()) { -+ boost::system::error_code ignored_ec; -+ THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_wallet_file, ignored_ec), error::file_exists, m_wallet_file); -+ THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_keys_file, ignored_ec), error::file_exists, m_keys_file); -+ } -+ -+ m_account.create_from_polyseed(seed, passphrase); -+ -+ init_type(hw::device::device_type::SOFTWARE); -+ m_polyseed = true; -+ setup_keys(password); -+ -+ if (recover) { -+ m_refresh_from_block_height = estimate_blockchain_height(restoreHeight > 0 ? restoreHeight : seed.birthday()); -+ } else { -+ m_refresh_from_block_height = estimate_blockchain_height(); -+ } -+ -+ create_keys_file(wallet_, false, password, m_nettype != MAINNET || create_address_file); -+ -+ setup_new_blockchain(); -+ -+ if (!wallet_.empty()) -+ store(); -+} -+ - /*! - * \brief Generates a wallet or restores one. Assumes the multisig setup - * has already completed for the provided multisig info. -@@ -5717,7 +5782,7 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wip - return retval; - } - -- uint64_t wallet2::estimate_blockchain_height() -+ uint64_t wallet2::estimate_blockchain_height(uint64_t time) - { - // -1 month for fluctuations in block time and machine date/time setup. - // avg seconds per block -@@ -5741,7 +5806,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. -- height = get_approximate_blockchain_height(); -+ height = get_approximate_blockchain_height(time); - uint64_t target_height = get_daemon_blockchain_target_height(err); - if (err.empty()) { - if (target_height < height) -@@ -13634,9 +13699,10 @@ 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 - { - uint64_t approx_blockchain_height = m_nettype == TESTNET ? 0 : (time(NULL) - 1522624244)/307; -+ // uint64_t approx_blockchain_height = fork_block + ((t > 0 ? t : time(NULL)) - fork_time)/seconds_per_block; - LOG_PRINT_L2("Calculated blockchain height: " << approx_blockchain_height); - return approx_blockchain_height; - } -@@ -15771,15 +15837,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) - { -- uint32_t version; -- if (!check_connection(&version)) -- { -- throw std::runtime_error("failed to connect to daemon: " + get_daemon_address()); -- } -- if (version < MAKE_CORE_RPC_VERSION(1, 6)) -- { -- throw std::runtime_error("this function requires RPC version 1.6 or higher"); -- } - std::tm date = { 0, 0, 0, 0, 0, 0, 0, 0 }; - date.tm_year = year - 1900; - date.tm_mon = month - 1; -@@ -15788,7 +15845,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"); - } -+ - uint64_t timestamp_target = std::mktime(&date); -+ -+ return get_blockchain_height_by_timestamp(timestamp_target); -+} -+ -+uint64_t wallet2::get_blockchain_height_by_timestamp(uint64_t timestamp_target) { -+ uint32_t version; -+ if (!check_connection(&version)) -+ { -+ throw std::runtime_error("failed to connect to daemon: " + get_daemon_address()); -+ } -+ if (version < MAKE_CORE_RPC_VERSION(1, 6)) -+ { -+ throw std::runtime_error("this function requires RPC version 1.6 or higher"); -+ } -+ - std::string err; - 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 80ff0698d..c26349ce3 100644 ---- a/src/wallet/wallet2.h -+++ b/src/wallet/wallet2.h -@@ -72,6 +72,7 @@ - #include "message_store.h" - #include "wallet_light_rpc.h" - #include "wallet_rpc_helpers.h" -+#include "polyseed/polyseed.hpp" - - #undef MONERO_DEFAULT_LOG_CATEGORY - #define MONERO_DEFAULT_LOG_CATEGORY "wallet.wallet2" -@@ -921,6 +922,20 @@ private: - void generate(const std::string& wallet_, const epee::wipeable_string& password, - const epee::wipeable_string& multisig_data, bool create_address_file = false); - -+ /*! -+ * \brief Generates a wallet from a polyseed. -+ * @param wallet_ Name of wallet file -+ * @param password Password of wallet file -+ * @param seed Polyseed data -+ * @param passphrase Optional seed offset passphrase -+ * @param recover Whether it is a restore -+ * @param restoreHeight Override the embedded restore height -+ * @param create_address_file Whether to create an address file -+ */ -+ void generate(const std::string& wallet_, const epee::wipeable_string& password, -+ const polyseed::data &seed, const epee::wipeable_string& passphrase = "", -+ bool recover = false, uint64_t restoreHeight = 0, bool create_address_file = false); -+ - /*! - * \brief Generates a wallet or restores one. - * \param wallet_ Name of wallet file -@@ -1088,6 +1103,15 @@ private: - bool is_deterministic() const; - bool get_seed(epee::wipeable_string& electrum_words, const epee::wipeable_string &passphrase = epee::wipeable_string()) const; - -+ /*! -+ * \brief get_polyseed Gets the polyseed (if available) and passphrase (if set) needed to recover the wallet. -+ * @param seed Polyseed mnemonic phrase -+ * @param passphrase Seed offset passphrase that was used to restore the wallet -+ * @return Returns true if the wallet has a polyseed. -+ * Note: both the mnemonic phrase and the passphrase are needed to recover the wallet -+ */ -+ 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. - */ -@@ -1555,8 +1579,8 @@ private: - /*! - * \brief Calculates the approximate blockchain height from current date/time. - */ -- uint64_t get_approximate_blockchain_height() const; -- uint64_t estimate_blockchain_height(); -+ uint64_t get_approximate_blockchain_height(uint64_t time = 0) const; -+ uint64_t estimate_blockchain_height(uint64_t time = 0); - std::vector select_available_outputs_from_histogram(uint64_t count, bool atleast, bool unlocked, bool allow_rct); - std::vector select_available_outputs(const std::function &f); - std::vector select_available_unmixable_outputs(); -@@ -1650,6 +1674,7 @@ private: - 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 -+ uint64_t get_blockchain_height_by_timestamp(uint64_t timestamp); - - bool is_synced(); - -@@ -1995,6 +2020,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 */ -+ bool m_polyseed; - bool m_multisig; /*!< if > 1 spend secret key will not match spend public key */ - uint32_t m_multisig_threshold; - std::vector m_multisig_signers; --- -2.43.0 - diff --git a/patches/wownero/0010-coin-control.patch b/patches/wownero/0010-coin-control.patch deleted file mode 100644 index f2a44fc..0000000 --- a/patches/wownero/0010-coin-control.patch +++ /dev/null @@ -1,979 +0,0 @@ -From edff8414d752d5cf23804d423281c3973b2b5376 Mon Sep 17 00:00:00 2001 -From: tobtoht -Date: Tue, 12 Mar 2024 11:07:57 +0100 -Subject: [PATCH 10/13] coin control - ---- - src/simplewallet/simplewallet.cpp | 2 +- - src/wallet/api/CMakeLists.txt | 8 +- - src/wallet/api/coins.cpp | 186 ++++++++++++++++++++++++++++++ - src/wallet/api/coins.h | 40 +++++++ - src/wallet/api/coins_info.cpp | 122 ++++++++++++++++++++ - src/wallet/api/coins_info.h | 71 ++++++++++++ - src/wallet/api/wallet.cpp | 64 +++++++++- - 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, 593 insertions(+), 19 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 8c5122097..cfdb8935f 100644 ---- a/src/simplewallet/simplewallet.cpp -+++ b/src/simplewallet/simplewallet.cpp -@@ -6981,7 +6981,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, -- m_current_subaddress_account, subaddr_indices, subtract_fee_from_outputs); -+ m_current_subaddress_account, subaddr_indices, {}, subtract_fee_from_outputs); - - if (ptx_vector.empty()) - { -diff --git a/src/wallet/api/CMakeLists.txt b/src/wallet/api/CMakeLists.txt -index af7948d8a..bb740e2ac 100644 ---- a/src/wallet/api/CMakeLists.txt -+++ b/src/wallet/api/CMakeLists.txt -@@ -40,7 +40,9 @@ set(wallet_api_sources - address_book.cpp - subaddress.cpp - subaddress_account.cpp -- unsigned_transaction.cpp) -+ unsigned_transaction.cpp -+ coins.cpp -+ coins_info.cpp) - - set(wallet_api_headers - wallet2_api.h) -@@ -55,7 +57,9 @@ set(wallet_api_private_headers - address_book.h - subaddress.h - subaddress_account.h -- unsigned_transaction.h) -+ unsigned_transaction.h -+ coins.h -+ coins_info.h) - - monero_private_headers(wallet_api - ${wallet_api_private_headers}) -diff --git a/src/wallet/api/coins.cpp b/src/wallet/api/coins.cpp -new file mode 100644 -index 000000000..ef12141cf ---- /dev/null -+++ b/src/wallet/api/coins.cpp -@@ -0,0 +1,186 @@ -+#include "coins.h" -+#include "coins_info.h" -+#include "wallet.h" -+#include "crypto/hash.h" -+#include "wallet/wallet2.h" -+#include "common_defines.h" -+ -+#include -+#include -+ -+using namespace epee; -+ -+namespace Monero { -+ -+Coins::~Coins() = default; -+ -+CoinsImpl::CoinsImpl(WalletImpl *wallet) -+ : m_wallet(wallet) {} -+ -+CoinsImpl::~CoinsImpl() -+{ -+ for (auto t : m_rows) -+ delete t; -+} -+ -+int CoinsImpl::count() const -+{ -+ boost::shared_lock lock(m_rowsMutex); -+ int result = m_rows.size(); -+ return result; -+} -+ -+CoinsInfo *CoinsImpl::coin(int index) const -+{ -+ boost::shared_lock lock(m_rowsMutex); -+ // sanity check -+ if (index < 0) -+ return nullptr; -+ auto index_ = static_cast(index); -+ return index_ < m_rows.size() ? m_rows[index_] : nullptr; -+} -+ -+std::vector CoinsImpl::getAll() const -+{ -+ boost::shared_lock lock(m_rowsMutex); -+ return m_rows; -+} -+ -+ -+void CoinsImpl::refresh() -+{ -+ LOG_PRINT_L2("Refreshing coins"); -+ -+ boost::unique_lock lock(m_rowsMutex); -+ boost::shared_lock transfers_lock(m_wallet->m_wallet->m_transfers_mutex); -+ -+ // delete old outputs; -+ for (auto t : m_rows) -+ delete t; -+ m_rows.clear(); -+ -+ for (size_t i = 0; i < m_wallet->m_wallet->get_num_transfer_details(); ++i) -+ { -+ const tools::wallet2::transfer_details &td = m_wallet->m_wallet->get_transfer_details(i); -+ -+ auto ci = new CoinsInfoImpl(); -+ ci->m_blockHeight = td.m_block_height; -+ ci->m_hash = string_tools::pod_to_hex(td.m_txid); -+ ci->m_internalOutputIndex = td.m_internal_output_index; -+ ci->m_globalOutputIndex = td.m_global_output_index; -+ ci->m_spent = td.m_spent; -+ ci->m_frozen = td.m_frozen; -+ ci->m_spentHeight = td.m_spent_height; -+ ci->m_amount = td.m_amount; -+ ci->m_rct = td.m_rct; -+ ci->m_keyImageKnown = td.m_key_image_known; -+ ci->m_pkIndex = td.m_pk_index; -+ ci->m_subaddrIndex = td.m_subaddr_index.minor; -+ ci->m_subaddrAccount = td.m_subaddr_index.major; -+ ci->m_address = m_wallet->m_wallet->get_subaddress_as_str(td.m_subaddr_index); // todo: this is expensive, cache maybe? -+ ci->m_addressLabel = m_wallet->m_wallet->get_subaddress_label(td.m_subaddr_index); -+ ci->m_keyImage = string_tools::pod_to_hex(td.m_key_image); -+ ci->m_unlockTime = td.m_tx.unlock_time; -+ ci->m_unlocked = m_wallet->m_wallet->is_transfer_unlocked(td); -+ ci->m_pubKey = string_tools::pod_to_hex(td.get_public_key()); -+ ci->m_coinbase = td.m_tx.vin.size() == 1 && td.m_tx.vin[0].type() == typeid(cryptonote::txin_gen); -+ ci->m_description = m_wallet->m_wallet->get_tx_note(td.m_txid); -+ -+ m_rows.push_back(ci); -+ } -+} -+ -+void CoinsImpl::setFrozen(std::string public_key) -+{ -+ crypto::public_key pk; -+ if (!epee::string_tools::hex_to_pod(public_key, pk)) -+ { -+ LOG_ERROR("Invalid public key: " << public_key); -+ return; -+ } -+ -+ try -+ { -+ m_wallet->m_wallet->freeze(pk); -+ refresh(); -+ } -+ catch (const std::exception& e) -+ { -+ LOG_ERROR("setFrozen: " << e.what()); -+ } -+} -+ -+void CoinsImpl::setFrozen(int index) -+{ -+ try -+ { -+ LOG_ERROR("Freezing coin: " << index); -+ m_wallet->m_wallet->freeze(index); -+ refresh(); -+ } -+ catch (const std::exception& e) -+ { -+ LOG_ERROR("setLabel: " << e.what()); -+ } -+} -+ -+void CoinsImpl::thaw(std::string public_key) -+{ -+ crypto::public_key pk; -+ if (!epee::string_tools::hex_to_pod(public_key, pk)) -+ { -+ LOG_ERROR("Invalid public key: " << public_key); -+ return; -+ } -+ -+ try -+ { -+ m_wallet->m_wallet->thaw(pk); -+ refresh(); -+ } -+ catch (const std::exception& e) -+ { -+ LOG_ERROR("thaw: " << e.what()); -+ } -+} -+ -+void CoinsImpl::thaw(int index) -+{ -+ try -+ { -+ m_wallet->m_wallet->thaw(index); -+ refresh(); -+ } -+ catch (const std::exception& e) -+ { -+ LOG_ERROR("thaw: " << e.what()); -+ } -+} -+ -+bool CoinsImpl::isTransferUnlocked(uint64_t unlockTime, uint64_t blockHeight) { -+ return m_wallet->m_wallet->is_transfer_unlocked(unlockTime, blockHeight); -+} -+ -+void CoinsImpl::setDescription(const std::string &public_key, const std::string &description) -+{ -+ crypto::public_key pk; -+ if (!epee::string_tools::hex_to_pod(public_key, pk)) -+ { -+ LOG_ERROR("Invalid public key: " << public_key); -+ return; -+ } -+ -+ try -+ { -+ const size_t index = m_wallet->m_wallet->get_transfer_details(pk); -+ const tools::wallet2::transfer_details &td = m_wallet->m_wallet->get_transfer_details(index); -+ m_wallet->m_wallet->set_tx_note(td.m_txid, description); -+ refresh(); -+ } -+ catch (const std::exception& e) -+ { -+ LOG_ERROR("setDescription: " << e.what()); -+ } -+} -+ -+} // namespace -diff --git a/src/wallet/api/coins.h b/src/wallet/api/coins.h -new file mode 100644 -index 000000000..b7a0a8642 ---- /dev/null -+++ b/src/wallet/api/coins.h -@@ -0,0 +1,40 @@ -+#ifndef FEATHER_COINS_H -+#define FEATHER_COINS_H -+ -+#include "wallet/api/wallet2_api.h" -+#include "wallet/wallet2.h" -+ -+namespace Monero { -+ -+class WalletImpl; -+ -+class CoinsImpl : public Coins -+{ -+public: -+ explicit CoinsImpl(WalletImpl * wallet); -+ ~CoinsImpl() override; -+ int count() const override; -+ CoinsInfo * coin(int index) const override; -+ std::vector getAll() const override; -+ void refresh() override; -+ -+ void setFrozen(std::string public_key) override; -+ void setFrozen(int index) override; -+ void thaw(std::string public_key) override; -+ void thaw(int index) override; -+ -+ bool isTransferUnlocked(uint64_t unlockTime, uint64_t blockHeight) override; -+ -+ void setDescription(const std::string &public_key, const std::string &description) override; -+ -+private: -+ WalletImpl *m_wallet; -+ std::vector m_rows; -+ mutable boost::shared_mutex m_rowsMutex; -+}; -+ -+} -+ -+namespace Bitmonero = Monero; -+ -+#endif //FEATHER_COINS_H -diff --git a/src/wallet/api/coins_info.cpp b/src/wallet/api/coins_info.cpp -new file mode 100644 -index 000000000..5f2c4e1e4 ---- /dev/null -+++ b/src/wallet/api/coins_info.cpp -@@ -0,0 +1,122 @@ -+#include "coins_info.h" -+ -+using namespace std; -+ -+namespace Monero { -+ -+CoinsInfo::~CoinsInfo() = default; -+ -+CoinsInfoImpl::CoinsInfoImpl() -+ : m_blockHeight(0) -+ , m_internalOutputIndex(0) -+ , m_globalOutputIndex(0) -+ , m_spent(false) -+ , m_frozen(false) -+ , m_spentHeight(0) -+ , m_amount(0) -+ , m_rct(false) -+ , m_keyImageKnown(false) -+ , m_pkIndex(0) -+ , m_subaddrAccount(0) -+ , m_subaddrIndex(0) -+ , m_unlockTime(0) -+ , m_unlocked(false) -+{ -+ -+} -+ -+CoinsInfoImpl::~CoinsInfoImpl() = default; -+ -+uint64_t CoinsInfoImpl::blockHeight() const -+{ -+ return m_blockHeight; -+} -+ -+string CoinsInfoImpl::hash() const -+{ -+ return m_hash; -+} -+ -+size_t CoinsInfoImpl::internalOutputIndex() const { -+ return m_internalOutputIndex; -+} -+ -+uint64_t CoinsInfoImpl::globalOutputIndex() const -+{ -+ return m_globalOutputIndex; -+} -+ -+bool CoinsInfoImpl::spent() const -+{ -+ return m_spent; -+} -+ -+bool CoinsInfoImpl::frozen() const -+{ -+ return m_frozen; -+} -+ -+uint64_t CoinsInfoImpl::spentHeight() const -+{ -+ return m_spentHeight; -+} -+ -+uint64_t CoinsInfoImpl::amount() const -+{ -+ return m_amount; -+} -+ -+bool CoinsInfoImpl::rct() const { -+ return m_rct; -+} -+ -+bool CoinsInfoImpl::keyImageKnown() const { -+ return m_keyImageKnown; -+} -+ -+size_t CoinsInfoImpl::pkIndex() const { -+ return m_pkIndex; -+} -+ -+uint32_t CoinsInfoImpl::subaddrIndex() const { -+ return m_subaddrIndex; -+} -+ -+uint32_t CoinsInfoImpl::subaddrAccount() const { -+ return m_subaddrAccount; -+} -+ -+string CoinsInfoImpl::address() const { -+ return m_address; -+} -+ -+string CoinsInfoImpl::addressLabel() const { -+ return m_addressLabel; -+} -+ -+string CoinsInfoImpl::keyImage() const { -+ return m_keyImage; -+} -+ -+uint64_t CoinsInfoImpl::unlockTime() const { -+ return m_unlockTime; -+} -+ -+bool CoinsInfoImpl::unlocked() const { -+ return m_unlocked; -+} -+ -+string CoinsInfoImpl::pubKey() const { -+ return m_pubKey; -+} -+ -+bool CoinsInfoImpl::coinbase() const { -+ return m_coinbase; -+} -+ -+string CoinsInfoImpl::description() const { -+ return m_description; -+} -+} // namespace -+ -+namespace Bitmonero = Monero; -diff --git a/src/wallet/api/coins_info.h b/src/wallet/api/coins_info.h -new file mode 100644 -index 000000000..c43e45abd ---- /dev/null -+++ b/src/wallet/api/coins_info.h -@@ -0,0 +1,71 @@ -+#ifndef FEATHER_COINS_INFO_H -+#define FEATHER_COINS_INFO_H -+ -+#include "wallet/api/wallet2_api.h" -+#include -+#include -+ -+namespace Monero { -+ -+class CoinsImpl; -+ -+class CoinsInfoImpl : public CoinsInfo -+{ -+public: -+ CoinsInfoImpl(); -+ ~CoinsInfoImpl(); -+ -+ virtual uint64_t blockHeight() const override; -+ virtual std::string hash() const override; -+ virtual size_t internalOutputIndex() const override; -+ virtual uint64_t globalOutputIndex() const override; -+ virtual bool spent() const override; -+ virtual bool frozen() const override; -+ virtual uint64_t spentHeight() const override; -+ virtual uint64_t amount() const override; -+ virtual bool rct() const override; -+ virtual bool keyImageKnown() const override; -+ virtual size_t pkIndex() const override; -+ virtual uint32_t subaddrIndex() const override; -+ virtual uint32_t subaddrAccount() const override; -+ virtual std::string address() const override; -+ virtual std::string addressLabel() const override; -+ virtual std::string keyImage() const override; -+ virtual uint64_t unlockTime() const override; -+ virtual bool unlocked() const override; -+ virtual std::string pubKey() const override; -+ virtual bool coinbase() const override; -+ virtual std::string description() const override; -+ -+private: -+ uint64_t m_blockHeight; -+ std::string m_hash; -+ size_t m_internalOutputIndex; -+ uint64_t m_globalOutputIndex; -+ bool m_spent; -+ bool m_frozen; -+ uint64_t m_spentHeight; -+ uint64_t m_amount; -+ bool m_rct; -+ bool m_keyImageKnown; -+ size_t m_pkIndex; -+ uint32_t m_subaddrIndex; -+ uint32_t m_subaddrAccount; -+ std::string m_address; -+ std::string m_addressLabel; -+ std::string m_keyImage; -+ uint64_t m_unlockTime; -+ bool m_unlocked; -+ std::string m_pubKey; -+ bool m_coinbase; -+ std::string m_description; -+ -+ friend class CoinsImpl; -+ -+}; -+ -+} // namespace -+ -+namespace Bitmonero = Monero; -+ -+#endif //FEATHER_COINS_INFO_H -diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp -index 0d785360a..ff1baac5c 100644 ---- a/src/wallet/api/wallet.cpp -+++ b/src/wallet/api/wallet.cpp -@@ -35,6 +35,7 @@ - #include "transaction_history.h" - #include "address_book.h" - #include "subaddress.h" -+#include "coins.h" - #include "subaddress_account.h" - #include "common_defines.h" - #include "common/util.h" -@@ -473,6 +474,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)); -+ m_coins.reset(new CoinsImpl(this)); - m_subaddressAccount.reset(new SubaddressAccountImpl(this)); - - -@@ -2046,7 +2048,7 @@ PendingTransaction* WalletImpl::restoreMultisigTransaction(const string& signDat - // - unconfirmed_transfer_details; - // - confirmed_transfer_details) - --PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector &dst_addr, const string &payment_id, optional> amount, uint32_t mixin_count, PendingTransaction::Priority priority, uint32_t subaddr_account, std::set subaddr_indices) -+PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector &dst_addr, const string &payment_id, optional> amount, uint32_t mixin_count, PendingTransaction::Priority priority, uint32_t subaddr_account, std::set subaddr_indices, const std::set &preferred_inputs) - - { - clearStatus(); -@@ -2084,6 +2086,7 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vectornettype(), dst_addr[i])) { - // TODO: copy-paste 'if treating as an address fails, try as url' from simplewallet.cpp:1982 -@@ -2105,6 +2108,7 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vectorunlocked_balance(subaddr_account, true); -+ // if (maxAllowedSpend < amountSum) { -+ // error = true; -+ // setStatusError(tr("Amount you are trying to spend is larger than unlocked amount")); -+ // break; -+ // } -+ std::vector preferred_input_list; -+ if (!preferred_inputs.empty()) { -+ LOG_ERROR("empty"); -+ -+ for (const auto &public_key : preferred_inputs) { -+ crypto::key_image keyImage; -+ bool r = epee::string_tools::hex_to_pod(public_key, keyImage); -+ if (!r) { -+ error = true; -+ setStatusError(tr("failed to parse key image")); -+ break; -+ } -+ if (m_wallet->frozen(keyImage)) { -+ error = true; -+ setStatusError(tr("refusing to spend frozen coin")); -+ break; -+ } -+ -+ preferred_input_list.push_back(keyImage); -+ } -+ } else { -+ LOG_ERROR("not empty"); -+ -+ boost::shared_lock transfers_lock(m_wallet->m_transfers_mutex); -+ for (size_t i = 0; i < m_wallet->get_num_transfer_details(); ++i) { -+ const tools::wallet2::transfer_details &td = m_wallet->get_transfer_details(i); -+ LOG_ERROR("COIN: " << i << ": " << td.amount() << "; "<frozen(td)); -+ if (td.m_spent) continue; -+ LOG_ERROR("is frozen"); -+ if (!td.m_frozen) { -+ LOG_ERROR("isn't:"); -+ LOG_ERROR("hash: " << td.m_key_image << "; " << td.amount()); -+ preferred_input_list.push_back(td.m_key_image); -+ } -+ } -+ } -+ for (const auto &de : preferred_input_list) { -+ LOG_ERROR("preferred input: " << de); -+ } - if (error) { - break; - } -@@ -2129,11 +2178,11 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vectorm_pending_tx = m_wallet->create_transactions_2(dsts, fake_outs_count, - adjusted_priority, -- extra, subaddr_account, subaddr_indices); -+ extra, subaddr_account, subaddr_indices, preferred_input_list); - } else { - transaction->m_pending_tx = m_wallet->create_transactions_all(0, info.address, info.is_subaddress, 1, fake_outs_count, - adjusted_priority, -- extra, subaddr_account, subaddr_indices); -+ extra, subaddr_account, subaddr_indices, preferred_input_list); - } - pendingTxPostProcess(transaction); - -@@ -2214,10 +2263,10 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector amount, uint32_t mixin_count, -- PendingTransaction::Priority priority, uint32_t subaddr_account, std::set subaddr_indices) -+ PendingTransaction::Priority priority, uint32_t subaddr_account, std::set subaddr_indices, const std::set &preferred_inputs) - - { -- return createTransactionMultDest(std::vector {dst_addr}, payment_id, amount ? (std::vector {*amount}) : (optional>()), mixin_count, priority, subaddr_account, subaddr_indices); -+ return createTransactionMultDest(std::vector {dst_addr}, payment_id, amount ? (std::vector {*amount}) : (optional>()), mixin_count, priority, subaddr_account, subaddr_indices, preferred_inputs); - } - - PendingTransaction *WalletImpl::createSweepUnmixableTransaction() -@@ -2342,6 +2391,11 @@ AddressBook *WalletImpl::addressBook() - return m_addressBook.get(); - } - -+Coins *WalletImpl::coins() -+{ -+ return m_coins.get(); -+} -+ - Subaddress *WalletImpl::subaddress() - { - return m_subaddress.get(); -diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h -index 32e12284b..a82f270e4 100644 ---- a/src/wallet/api/wallet.h -+++ b/src/wallet/api/wallet.h -@@ -46,6 +46,7 @@ class PendingTransactionImpl; - class UnsignedTransactionImpl; - class AddressBookImpl; - class SubaddressImpl; -+class CoinsImpl; - class SubaddressAccountImpl; - struct Wallet2CallbackImpl; - -@@ -167,12 +168,14 @@ public: - optional> amount, uint32_t mixin_count, - PendingTransaction::Priority priority = PendingTransaction::Priority_Low, - uint32_t subaddr_account = 0, -- std::set subaddr_indices = {}) override; -+ std::set subaddr_indices = {}, -+ const std::set &preferred_inputs = {}) override; - PendingTransaction * createTransaction(const std::string &dst_addr, const std::string &payment_id, - optional amount, uint32_t mixin_count, - PendingTransaction::Priority priority = PendingTransaction::Priority_Low, - uint32_t subaddr_account = 0, -- std::set subaddr_indices = {}) override; -+ std::set subaddr_indices = {}, -+ const std::set &preferred_inputs = {}) override; - virtual PendingTransaction * createSweepUnmixableTransaction() override; - bool submitTransaction(const std::string &fileName) override; - bool submitTransactionUR(const std::string &input) override; -@@ -201,6 +204,7 @@ public: - PendingTransaction::Priority priority) const override; - virtual TransactionHistory * history() override; - virtual AddressBook * addressBook() override; -+ virtual Coins * coins() override; - virtual Subaddress * subaddress() override; - virtual SubaddressAccount * subaddressAccount() override; - virtual void setListener(WalletListener * l) override; -@@ -272,6 +276,7 @@ private: - friend class TransactionHistoryImpl; - friend struct Wallet2CallbackImpl; - friend class AddressBookImpl; -+ friend class CoinsImpl; - friend class SubaddressImpl; - friend class SubaddressAccountImpl; - -@@ -288,6 +293,7 @@ private: - std::unique_ptr m_wallet2Callback; - std::unique_ptr m_addressBook; - std::unique_ptr m_subaddress; -+ std::unique_ptr m_coins; - std::unique_ptr m_subaddressAccount; - - // multi-threaded refresh stuff -diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h -index be1c3704e..013b5bcba 100644 ---- a/src/wallet/api/wallet2_api.h -+++ b/src/wallet/api/wallet2_api.h -@@ -263,6 +263,51 @@ struct AddressBook - virtual int lookupPaymentID(const std::string &payment_id) const = 0; - }; - -+/** -+ * @brief The CoinsInfo - interface for displaying coins information -+ */ -+struct CoinsInfo -+{ -+ virtual ~CoinsInfo() = 0; -+ -+ virtual uint64_t blockHeight() const = 0; -+ virtual std::string hash() const = 0; -+ virtual size_t internalOutputIndex() const = 0; -+ virtual uint64_t globalOutputIndex() const = 0; -+ virtual bool spent() const = 0; -+ virtual bool frozen() const = 0; -+ virtual uint64_t spentHeight() const = 0; -+ virtual uint64_t amount() const = 0; -+ virtual bool rct() const = 0; -+ virtual bool keyImageKnown() const = 0; -+ virtual size_t pkIndex() const = 0; -+ virtual uint32_t subaddrIndex() const = 0; -+ virtual uint32_t subaddrAccount() const = 0; -+ virtual std::string address() const = 0; -+ virtual std::string addressLabel() const = 0; -+ virtual std::string keyImage() const = 0; -+ virtual uint64_t unlockTime() const = 0; -+ virtual bool unlocked() const = 0; -+ virtual std::string pubKey() const = 0; -+ virtual bool coinbase() const = 0; -+ virtual std::string description() const = 0; -+}; -+ -+struct Coins -+{ -+ virtual ~Coins() = 0; -+ virtual int count() const = 0; -+ virtual CoinsInfo * coin(int index) const = 0; -+ virtual std::vector getAll() const = 0; -+ virtual void refresh() = 0; -+ virtual void setFrozen(std::string public_key) = 0; -+ virtual void setFrozen(int index) = 0; -+ virtual void thaw(std::string public_key) = 0; -+ virtual void thaw(int index) = 0; -+ virtual bool isTransferUnlocked(uint64_t unlockTime, uint64_t blockHeight) = 0; -+ virtual void setDescription(const std::string &public_key, const std::string &description) = 0; -+}; -+ - struct SubaddressRow { - public: - SubaddressRow(std::size_t _rowId, const std::string &_address, const std::string &_label): -@@ -856,7 +901,8 @@ struct Wallet - optional> amount, uint32_t mixin_count, - PendingTransaction::Priority = PendingTransaction::Priority_Low, - uint32_t subaddr_account = 0, -- std::set subaddr_indices = {}) = 0; -+ std::set subaddr_indices = {}, -+ const std::set &preferred_inputs = {}) = 0; - - /*! - * \brief createTransaction creates transaction. if dst_addr is an integrated address, payment_id is ignored -@@ -875,7 +921,8 @@ struct Wallet - optional amount, uint32_t mixin_count, - PendingTransaction::Priority = PendingTransaction::Priority_Low, - uint32_t subaddr_account = 0, -- std::set subaddr_indices = {}) = 0; -+ std::set subaddr_indices = {}, -+ const std::set &preferred_inputs = {}) = 0; - - /*! - * \brief createSweepUnmixableTransaction creates transaction with unmixable outputs. -@@ -994,6 +1041,7 @@ struct Wallet - - virtual TransactionHistory * history() = 0; - virtual AddressBook * addressBook() = 0; -+ virtual Coins * coins() = 0; - virtual Subaddress * subaddress() = 0; - virtual SubaddressAccount * subaddressAccount() = 0; - virtual void setListener(WalletListener *) = 0; -diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp -index 3e49c21f8..4d756567f 100644 ---- a/src/wallet/wallet2.cpp -+++ b/src/wallet/wallet2.cpp -@@ -2094,12 +2094,21 @@ bool wallet2::frozen(const multisig_tx_set& txs) const - - return false; - } -+void wallet2::freeze(const crypto::public_key &pk) -+{ -+ freeze(get_transfer_details(pk)); -+} - //---------------------------------------------------------------------------------------------------- - void wallet2::freeze(const crypto::key_image &ki) - { - freeze(get_transfer_details(ki)); - } - //---------------------------------------------------------------------------------------------------- -+void wallet2::thaw(const crypto::public_key &pk) -+{ -+ thaw(get_transfer_details(pk)); -+} -+//---------------------------------------------------------------------------------------------------- - void wallet2::thaw(const crypto::key_image &ki) - { - thaw(get_transfer_details(ki)); -@@ -2110,6 +2119,18 @@ bool wallet2::frozen(const crypto::key_image &ki) const - return frozen(get_transfer_details(ki)); - } - //---------------------------------------------------------------------------------------------------- -+size_t wallet2::get_transfer_details(const crypto::public_key &pk) const -+{ -+ for (size_t idx = 0; idx < m_transfers.size(); ++idx) -+ { -+ const transfer_details &td = m_transfers[idx]; -+ if (td.get_public_key() == pk) { -+ return idx; -+ } -+ } -+ CHECK_AND_ASSERT_THROW_MES(false, "Public key not found"); -+} -+//---------------------------------------------------------------------------------------------------- - size_t wallet2::get_transfer_details(const crypto::key_image &ki) const - { - for (size_t idx = 0; idx < m_transfers.size(); ++idx) -@@ -2523,6 +2544,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; -@@ -2626,6 +2648,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; -@@ -10497,7 +10520,7 @@ void wallet2::transfer_selected_rct(std::vector wallet2::pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set &subaddr_indices) -+std::vector wallet2::pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set &subaddr_indices, const std::vector& preferred_input_list) - { - std::vector picks; - float current_output_relatdness = 1.0f; -@@ -10508,6 +10531,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]; -+ if (!is_preferred_input(preferred_input_list, td.m_key_image)) { -+ continue; -+ } - 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) -@@ -10528,6 +10554,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]; -+ if (!is_preferred_input(preferred_input_list, td.m_key_image)) { -+ continue; -+ } - 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) -@@ -10539,6 +10568,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]; -+ if (!is_preferred_input(preferred_input_list, td2.m_key_image)) { -+ continue; -+ } - 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) << "]"); -@@ -11111,7 +11143,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) - { - //ensure device is let in NONE mode in any case - hw::device &hwdev = m_account.get_device(); -@@ -11319,6 +11351,9 @@ std::vector wallet2::create_transactions_2(std::vector wallet2::create_transactions_2(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 unused_transfers_indices; - std::vector unused_dust_indices; -@@ -11918,6 +11953,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]; -+ if (!is_preferred_input(preferred_input_list, td.m_key_image)) { -+ continue; -+ } - if (m_ignore_fractional_outputs && td.amount() < fractional_threshold) - { - 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 c26349ce3..a48ba33b6 100644 ---- a/src/wallet/wallet2.h -+++ b/src/wallet/wallet2.h -@@ -1209,8 +1209,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; -@@ -1562,6 +1562,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; -+ size_t get_transfer_details(const crypto::public_key &pk) const; - - uint8_t get_current_hard_fork(); - void get_hard_fork_info(uint8_t version, uint64_t &earliest_height); -@@ -1793,7 +1794,9 @@ private: - void freeze(size_t idx); - void thaw(size_t idx); - bool frozen(size_t idx) const; -+ void freeze(const crypto::public_key &pk); - void freeze(const crypto::key_image &ki); -+ void thaw(const crypto::public_key &pk); - void thaw(const crypto::key_image &ki); - bool frozen(const crypto::key_image &ki) const; - bool frozen(const transfer_details &td) const; -@@ -1834,6 +1837,8 @@ private: - - static std::string get_default_daemon_address() { CRITICAL_REGION_LOCAL(default_daemon_address_lock); return default_daemon_address; } - -+ boost::shared_mutex m_transfers_mutex; -+ - private: - /*! - * \brief Stores wallet information to wallet file. -@@ -1905,7 +1910,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; -- std::vector pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set &subaddr_indices); -+ std::vector pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set &subaddr_indices, const std::vector& preferred_input_list); - void set_spent(size_t idx, uint64_t height); - void set_unspent(size_t idx); - bool is_spent(const transfer_details &td, bool strict = true) const; --- -2.43.0 - diff --git a/patches/wownero/0010-polyseed.patch b/patches/wownero/0010-polyseed.patch new file mode 100644 index 0000000..2df7488 --- /dev/null +++ b/patches/wownero/0010-polyseed.patch @@ -0,0 +1,1464 @@ +From 43eeedec72e5e2912118a51af5457ca7d489cdbb Mon Sep 17 00:00:00 2001 +From: tobtoht +Date: Tue, 12 Mar 2024 09:42:37 +0100 +Subject: [PATCH 10/14] polyseed + +Co-authored-by: Czarek Nakamoto +--- + .gitmodules | 6 + + CMakeLists.txt | 4 +- + contrib/depends/hosts/darwin.mk | 2 + + contrib/depends/hosts/linux.mk | 8 +- + contrib/depends/packages/packages.mk | 2 +- + contrib/depends/packages/polyseed.mk | 28 +++ + contrib/depends/packages/sodium.mk | 2 +- + .../polyseed/0001-disable-soname.patch | 48 +++++ + .../patches/polyseed/force-static-mingw.patch | 23 +++ + contrib/epee/include/wipeable_string.h | 7 + + contrib/epee/src/wipeable_string.cpp | 10 + + external/CMakeLists.txt | 2 + + external/polyseed | 1 + + external/utf8proc | 1 + + src/CMakeLists.txt | 1 + + src/cryptonote_basic/CMakeLists.txt | 1 + + src/cryptonote_basic/account.cpp | 23 ++- + src/cryptonote_basic/account.h | 6 + + src/cryptonote_config.h | 2 + + src/polyseed/CMakeLists.txt | 25 +++ + src/polyseed/pbkdf2.c | 85 ++++++++ + src/polyseed/pbkdf2.h | 46 +++++ + src/polyseed/polyseed.cpp | 182 ++++++++++++++++++ + src/polyseed/polyseed.hpp | 167 ++++++++++++++++ + src/wallet/api/wallet.cpp | 70 +++++++ + src/wallet/api/wallet.h | 10 + + 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 | 99 ++++++++-- + src/wallet/wallet2.h | 30 ++- + 31 files changed, 912 insertions(+), 23 deletions(-) + create mode 100644 contrib/depends/packages/polyseed.mk + create mode 100644 contrib/depends/patches/polyseed/0001-disable-soname.patch + create mode 100644 contrib/depends/patches/polyseed/force-static-mingw.patch + create mode 160000 external/polyseed + create mode 160000 external/utf8proc + create mode 100644 src/polyseed/CMakeLists.txt + create mode 100644 src/polyseed/pbkdf2.c + create mode 100644 src/polyseed/pbkdf2.h + create mode 100644 src/polyseed/polyseed.cpp + create mode 100644 src/polyseed/polyseed.hpp + +diff --git a/.gitmodules b/.gitmodules +index b24855d..5896766 100644 +--- a/.gitmodules ++++ b/.gitmodules +@@ -20,3 +20,9 @@ + path = external/bc-ur + url = https://github.com/MrCyjaneK/bc-ur + branch = misc ++[submodule "external/utf8proc"] ++ path = external/utf8proc ++ url = https://github.com/JuliaStrings/utf8proc.git ++[submodule "external/polyseed"] ++ path = external/polyseed ++ url = https://github.com/tevador/polyseed.git +\ No newline at end of file +diff --git a/CMakeLists.txt b/CMakeLists.txt +index abe44ec..85a62ef 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -372,6 +372,8 @@ if(NOT MANUAL_SUBMODULES) + #check_submodule(external/trezor-common) + check_submodule(external/randomwow) + check_submodule(external/supercop) ++ check_submodule(external/polyseed) ++ check_submodule(external/utf8proc) + endif() + endif() + +@@ -461,7 +463,7 @@ endif() + # elseif(CMAKE_SYSTEM_NAME MATCHES ".*BSDI.*") + # set(BSDI TRUE) + +-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) + + if(APPLE) + cmake_policy(SET CMP0042 NEW) +diff --git a/contrib/depends/hosts/darwin.mk b/contrib/depends/hosts/darwin.mk +index 83d8303..b14ee5c 100644 +--- a/contrib/depends/hosts/darwin.mk ++++ b/contrib/depends/hosts/darwin.mk +@@ -8,6 +8,8 @@ endif + darwin_CC=clang -target $(CC_target) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(host_prefix)/native/SDK/ -mlinker-version=$(LD64_VERSION) -B$(host_prefix)/native/bin/$(host)- + darwin_CXX=clang++ -target $(CC_target) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(host_prefix)/native/SDK/ -mlinker-version=$(LD64_VERSION) -stdlib=libc++ -B$(host_prefix)/native/bin/$(host)- + ++darwin_RANLIB=$(host_prefix)/native/bin/$(host)-ranlib ++ + darwin_CFLAGS=-pipe + darwin_CXXFLAGS=$(darwin_CFLAGS) + darwin_ARFLAGS=cr +diff --git a/contrib/depends/hosts/linux.mk b/contrib/depends/hosts/linux.mk +index 912fdb0..b79799f 100644 +--- a/contrib/depends/hosts/linux.mk ++++ b/contrib/depends/hosts/linux.mk +@@ -11,15 +11,15 @@ linux_debug_CXXFLAGS=$(linux_debug_CFLAGS) + linux_debug_CPPFLAGS=-D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC + + ifeq (86,$(findstring 86,$(build_arch))) +-i686_linux_CC=gcc -m32 +-i686_linux_CXX=g++ -m32 ++i686_linux_CC=i686-linux-gnu-gcc ++i686_linux_CXX=i686-linux-gnu-g++ + i686_linux_AR=ar + i686_linux_RANLIB=ranlib + i686_linux_NM=nm + i686_linux_STRIP=strip + +-x86_64_linux_CC=gcc -m64 +-x86_64_linux_CXX=g++ -m64 ++x86_64_linux_CC=x86_64-linux-gnu-gcc ++x86_64_linux_CXX=x86_64-linux-gnu-g++ + x86_64_linux_AR=ar + x86_64_linux_RANLIB=ranlib + x86_64_linux_NM=nm +diff --git a/contrib/depends/packages/packages.mk b/contrib/depends/packages/packages.mk +index d2d1eca..8783d49 100644 +--- a/contrib/depends/packages/packages.mk ++++ b/contrib/depends/packages/packages.mk +@@ -1,4 +1,4 @@ +-packages:=boost openssl zeromq libiconv expat unbound ++packages:=boost openssl zeromq libiconv expat unbound polyseed + + # ccache is useless in gitian builds + ifneq ($(GITIAN),1) +diff --git a/contrib/depends/packages/polyseed.mk b/contrib/depends/packages/polyseed.mk +new file mode 100644 +index 0000000..0071b20 +--- /dev/null ++++ b/contrib/depends/packages/polyseed.mk +@@ -0,0 +1,28 @@ ++package=polyseed ++$(package)_version=2.0.0 ++$(package)_download_path=https://github.com/tevador/$(package)/archive/refs/tags/ ++$(package)_download_file=v$($(package)_version).tar.gz ++$(package)_file_name=$(package)-$($(package)_version).tar.gz ++$(package)_sha256_hash=f36282fcbcd68d32461b8230c89e1a40661bd46b91109681cec637433004135a ++$(package)_patches=force-static-mingw.patch 0001-disable-soname.patch ++ ++define $(package)_preprocess_cmds ++ patch -p1 < $($(package)_patch_dir)/force-static-mingw.patch &&\ ++ patch -p1 < $($(package)_patch_dir)/0001-disable-soname.patch ++endef ++ ++define $(package)_config_cmds ++ CC="$($(package)_cc)" cmake -DCMAKE_INSTALL_PREFIX="$(host_prefix)" . ++endef ++ ++define $(package)_set_vars ++ $(package)_build_opts=CC="$($(package)_cc)" ++endef ++ ++define $(package)_build_cmds ++ CC="$($(package)_cc)" $(MAKE) ++endef ++ ++define $(package)_stage_cmds ++ $(MAKE) DESTDIR=$($(package)_staging_dir) install ++endef +diff --git a/contrib/depends/packages/sodium.mk b/contrib/depends/packages/sodium.mk +index 87b3459..68a5b48 100644 +--- a/contrib/depends/packages/sodium.mk ++++ b/contrib/depends/packages/sodium.mk +@@ -6,7 +6,7 @@ $(package)_sha256_hash=6f504490b342a4f8a4c4a02fc9b866cbef8622d5df4e5452b46be121e + $(package)_patches=disable-glibc-getrandom-getentropy.patch fix-whitespace.patch + + define $(package)_set_vars +-$(package)_config_opts=--enable-static --disable-shared ++$(package)_config_opts=--enable-static --disable-shared --with-pic + $(package)_config_opts+=--prefix=$(host_prefix) + endef + +diff --git a/contrib/depends/patches/polyseed/0001-disable-soname.patch b/contrib/depends/patches/polyseed/0001-disable-soname.patch +new file mode 100644 +index 0000000..bd97dd3 +--- /dev/null ++++ b/contrib/depends/patches/polyseed/0001-disable-soname.patch +@@ -0,0 +1,48 @@ ++From aabafcfc0572651436d024a635483c49042fad7f Mon Sep 17 00:00:00 2001 ++From: Czarek Nakamoto ++Date: Thu, 28 Mar 2024 00:32:51 +0100 ++Subject: [PATCH] disable soname ++ ++--- ++ CMakeLists.txt | 16 +++++++++------- ++ 1 file changed, 9 insertions(+), 7 deletions(-) ++ ++diff --git a/CMakeLists.txt b/CMakeLists.txt ++index 8a8e7c2..5301353 100644 ++--- a/CMakeLists.txt +++++ b/CMakeLists.txt ++@@ -36,6 +36,7 @@ include_directories(polyseed ++ target_compile_definitions(polyseed PRIVATE POLYSEED_SHARED) ++ set_target_properties(polyseed PROPERTIES VERSION 2.0.0 ++ SOVERSION 2 +++ NO_SONAME 1 ++ C_STANDARD 11 ++ C_STANDARD_REQUIRED ON) ++ ++@@ -45,16 +46,17 @@ include_directories(polyseed_static ++ include/) ++ target_compile_definitions(polyseed_static PRIVATE POLYSEED_STATIC) ++ set_target_properties(polyseed_static PROPERTIES OUTPUT_NAME polyseed +++ NO_SONAME 1 ++ C_STANDARD 11 ++ C_STANDARD_REQUIRED ON) ++ ++-add_executable(polyseed-tests ++- tests/tests.c) ++-include_directories(polyseed-tests ++- include/) ++-target_compile_definitions(polyseed-tests PRIVATE POLYSEED_STATIC) ++-target_link_libraries(polyseed-tests ++- PRIVATE polyseed_static) +++# add_executable(polyseed-tests +++# tests/tests.c) +++# include_directories(polyseed-tests +++# include/) +++# target_compile_definitions(polyseed-tests PRIVATE POLYSEED_STATIC) +++# target_link_libraries(polyseed-tests +++# PRIVATE polyseed_static) ++ ++ include(GNUInstallDirs) ++ install(TARGETS polyseed polyseed_static ++-- ++2.39.2 +diff --git a/contrib/depends/patches/polyseed/force-static-mingw.patch b/contrib/depends/patches/polyseed/force-static-mingw.patch +new file mode 100644 +index 0000000..f05cb2b +--- /dev/null ++++ b/contrib/depends/patches/polyseed/force-static-mingw.patch +@@ -0,0 +1,23 @@ ++--- a/include/polyseed.h +++++ b/include/polyseed.h ++@@ -93,13 +93,13 @@ Shared/static library definitions ++ - define POLYSEED_STATIC when linking to the static library ++ */ ++ #if defined(_WIN32) || defined(__CYGWIN__) ++- #ifdef POLYSEED_SHARED ++- #define POLYSEED_API __declspec(dllexport) ++- #elif !defined(POLYSEED_STATIC) ++- #define POLYSEED_API __declspec(dllimport) ++- #else ++- #define POLYSEED_API ++- #endif +++// #ifdef POLYSEED_SHARED +++// #define POLYSEED_API __declspec(dllexport) +++// #elif !defined(POLYSEED_STATIC) +++// #define POLYSEED_API __declspec(dllimport) +++// #else +++ #define POLYSEED_API +++// #endif ++ #define POLYSEED_PRIVATE ++ #else ++ #ifdef POLYSEED_SHARED +diff --git a/contrib/epee/include/wipeable_string.h b/contrib/epee/include/wipeable_string.h +index 65977cd..594e15d 100644 +--- a/contrib/epee/include/wipeable_string.h ++++ b/contrib/epee/include/wipeable_string.h +@@ -34,6 +34,7 @@ + #include + #include "memwipe.h" + #include "fnv1.h" ++#include "serialization/keyvalue_serialization.h" + + namespace epee + { +@@ -75,6 +76,12 @@ namespace epee + bool operator!=(const wipeable_string &other) const noexcept { return buffer != other.buffer; } + wipeable_string &operator=(wipeable_string &&other); + wipeable_string &operator=(const wipeable_string &other); ++ char& operator[](size_t idx); ++ const char& operator[](size_t idx) const; ++ ++ BEGIN_KV_SERIALIZE_MAP() ++ KV_SERIALIZE_CONTAINER_POD_AS_BLOB(buffer) ++ END_KV_SERIALIZE_MAP() + + private: + void grow(size_t sz, size_t reserved = 0); +diff --git a/contrib/epee/src/wipeable_string.cpp b/contrib/epee/src/wipeable_string.cpp +index b016f2f..f2f365b 100644 +--- 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) + return *this; + } + ++char& wipeable_string::operator[](size_t idx) { ++ CHECK_AND_ASSERT_THROW_MES(idx < buffer.size(), "Index out of bounds"); ++ return buffer[idx]; ++} ++ ++const char& wipeable_string::operator[](size_t idx) const { ++ CHECK_AND_ASSERT_THROW_MES(idx < buffer.size(), "Index out of bounds"); ++ return buffer[idx]; ++} ++ + } +diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt +index 88a7bb0..95be500 100644 +--- a/external/CMakeLists.txt ++++ b/external/CMakeLists.txt +@@ -73,4 +73,6 @@ add_subdirectory(db_drivers) + add_subdirectory(easylogging++) + add_subdirectory(qrcodegen) + add_subdirectory(bc-ur) ++add_subdirectory(polyseed EXCLUDE_FROM_ALL) ++add_subdirectory(utf8proc EXCLUDE_FROM_ALL) + add_subdirectory(randomwow EXCLUDE_FROM_ALL) +diff --git a/external/polyseed b/external/polyseed +new file mode 160000 +index 0000000..dfb05d8 +--- /dev/null ++++ b/external/polyseed +@@ -0,0 +1 @@ ++Subproject commit dfb05d8edb682b0e8f743b1b70c9131712ff4157 +diff --git a/external/utf8proc b/external/utf8proc +new file mode 160000 +index 0000000..3de4596 +--- /dev/null ++++ b/external/utf8proc +@@ -0,0 +1 @@ ++Subproject commit 3de4596fbe28956855df2ecb3c11c0bbc3535838 +diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt +index 9216bca..c043ba1 100644 +--- a/src/CMakeLists.txt ++++ b/src/CMakeLists.txt +@@ -95,6 +95,7 @@ add_subdirectory(net) + add_subdirectory(hardforks) + add_subdirectory(blockchain_db) + add_subdirectory(mnemonics) ++add_subdirectory(polyseed) + add_subdirectory(rpc) + if(NOT IOS) + add_subdirectory(serialization) +diff --git a/src/cryptonote_basic/CMakeLists.txt b/src/cryptonote_basic/CMakeLists.txt +index 1414be1..414936a 100644 +--- a/src/cryptonote_basic/CMakeLists.txt ++++ b/src/cryptonote_basic/CMakeLists.txt +@@ -71,6 +71,7 @@ target_link_libraries(cryptonote_basic + checkpoints + cryptonote_format_utils_basic + device ++ polyseed_wrapper + ${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 4e87d44..2d556f2 100644 +--- a/src/cryptonote_basic/account.cpp ++++ b/src/cryptonote_basic/account.cpp +@@ -87,12 +87,16 @@ DISABLE_VS_WARNINGS(4244 4345) + void account_keys::xor_with_key_stream(const crypto::chacha_key &key) + { + // encrypt a large enough byte stream with chacha20 +- epee::wipeable_string key_stream = get_key_stream(key, m_encryption_iv, sizeof(crypto::secret_key) * (2 + m_multisig_keys.size())); ++ epee::wipeable_string key_stream = get_key_stream(key, m_encryption_iv, sizeof(crypto::secret_key) * (3 + m_multisig_keys.size()) + m_passphrase.size()); + const char *ptr = key_stream.data(); + for (size_t i = 0; i < sizeof(crypto::secret_key); ++i) + m_spend_secret_key.data[i] ^= *ptr++; + for (size_t i = 0; i < sizeof(crypto::secret_key); ++i) + m_view_secret_key.data[i] ^= *ptr++; ++ for (size_t i = 0; i < sizeof(crypto::secret_key); ++i) ++ m_polyseed.data[i] ^= *ptr++; ++ for (size_t i = 0; i < m_passphrase.size(); ++i) ++ m_passphrase.data()[i] ^= *ptr++; + for (crypto::secret_key &k: m_multisig_keys) + { + for (size_t i = 0; i < sizeof(crypto::secret_key); ++i) +@@ -150,6 +154,8 @@ DISABLE_VS_WARNINGS(4244 4345) + { + m_keys.m_spend_secret_key = crypto::secret_key(); + m_keys.m_multisig_keys.clear(); ++ m_keys.m_polyseed = crypto::secret_key(); ++ m_keys.m_passphrase.wipe(); + } + //----------------------------------------------------------------- + void account_base::set_spend_key(const crypto::secret_key& spend_secret_key) +@@ -255,6 +261,21 @@ DISABLE_VS_WARNINGS(4244 4345) + create_from_keys(address, fake, viewkey); + } + //----------------------------------------------------------------- ++ void account_base::create_from_polyseed(const polyseed::data& seed, const epee::wipeable_string &passphrase) ++ { ++ crypto::secret_key secret_key; ++ seed.keygen(&secret_key, sizeof(secret_key)); ++ ++ if (!passphrase.empty()) { ++ secret_key = cryptonote::decrypt_key(secret_key, passphrase); ++ } ++ ++ generate(secret_key, true, false); ++ ++ seed.save(m_keys.m_polyseed.data); ++ m_keys.m_passphrase = passphrase; ++ } ++ //----------------------------------------------------------------- + bool account_base::make_multisig(const crypto::secret_key &view_secret_key, const crypto::secret_key &spend_secret_key, const crypto::public_key &spend_public_key, const std::vector &multisig_keys) + { + m_keys.m_account_address.m_spend_public_key = spend_public_key; +diff --git a/src/cryptonote_basic/account.h b/src/cryptonote_basic/account.h +index 93d1d28..1f76feb 100644 +--- a/src/cryptonote_basic/account.h ++++ b/src/cryptonote_basic/account.h +@@ -33,6 +33,7 @@ + #include "cryptonote_basic.h" + #include "crypto/crypto.h" + #include "serialization/keyvalue_serialization.h" ++#include "polyseed/polyseed.hpp" + + namespace cryptonote + { +@@ -45,6 +46,8 @@ namespace cryptonote + std::vector m_multisig_keys; + hw::device *m_device = &hw::get_device("default"); + crypto::chacha_iv m_encryption_iv; ++ crypto::secret_key m_polyseed; ++ epee::wipeable_string m_passphrase; // Only used with polyseed + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(m_account_address) +@@ -53,6 +56,8 @@ namespace cryptonote + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(m_multisig_keys) + const crypto::chacha_iv default_iv{{0, 0, 0, 0, 0, 0, 0, 0}}; + KV_SERIALIZE_VAL_POD_AS_BLOB_OPT(m_encryption_iv, default_iv) ++ KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(m_polyseed) ++ KV_SERIALIZE(m_passphrase) + END_KV_SERIALIZE_MAP() + + void encrypt(const crypto::chacha_key &key); +@@ -79,6 +84,7 @@ namespace cryptonote + void create_from_device(hw::device &hwdev); + void create_from_keys(const cryptonote::account_public_address& address, const crypto::secret_key& spendkey, const crypto::secret_key& viewkey); + void create_from_viewkey(const cryptonote::account_public_address& address, const crypto::secret_key& viewkey); ++ void create_from_polyseed(const polyseed::data &polyseed, const epee::wipeable_string &passphrase); + bool make_multisig(const crypto::secret_key &view_secret_key, const crypto::secret_key &spend_secret_key, const crypto::public_key &spend_public_key, const std::vector &multisig_keys); + 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 8b5091a..d9151e8 100644 +--- a/src/cryptonote_config.h ++++ b/src/cryptonote_config.h +@@ -219,6 +219,8 @@ + + #define DNS_BLOCKLIST_LIFETIME (86400 * 8) + ++#define POLYSEED_COIN POLYSEED_WOWNERO ++ + //The limit is enough for the mandatory transaction content with 16 outputs (547 bytes), + //a custom tag (1 byte) and up to 32 bytes of custom data for each recipient. + // (1+32) + (1+1+16*32) + (1+16*32) = 1060 +diff --git a/src/polyseed/CMakeLists.txt b/src/polyseed/CMakeLists.txt +new file mode 100644 +index 0000000..cca4eb7 +--- /dev/null ++++ b/src/polyseed/CMakeLists.txt +@@ -0,0 +1,25 @@ ++set(polyseed_sources ++ pbkdf2.c ++ polyseed.cpp ++) ++ ++monero_find_all_headers(polyseed_private_headers "${CMAKE_CURRENT_SOURCE_DIR}") ++ ++monero_private_headers(polyseed_wrapper ++ ${polyseed_private_headers} ++) ++ ++monero_add_library(polyseed_wrapper ++ ${polyseed_sources} ++ ${polyseed_headers} ++ ${polyseed_private_headers} ++) ++ ++target_link_libraries(polyseed_wrapper ++PUBLIC ++ polyseed ++ utf8proc ++ ${SODIUM_LIBRARY} ++ PRIVATE ++ ${EXTRA_LIBRARIES} ++) +diff --git a/src/polyseed/pbkdf2.c b/src/polyseed/pbkdf2.c +new file mode 100644 +index 0000000..1c45f47 +--- /dev/null ++++ b/src/polyseed/pbkdf2.c +@@ -0,0 +1,85 @@ ++// Copyright (c) 2023, The Monero Project ++// Copyright (c) 2021, tevador ++// Copyright (c) 2005,2007,2009 Colin Percival ++// ++// All rights reserved. ++// ++// Redistribution and use in source and binary forms, with or without ++// modification, are permitted provided that the following conditions ++// are met: ++// 1. Redistributions of source code must retain the above copyright ++// notice, this list of conditions and the following disclaimer. ++// 2. Redistributions in binary form must reproduce the above copyright ++// notice, this list of conditions and the following disclaimer in the ++// documentation and/or other materials provided with the distribution. ++// ++// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ++// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ++// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ++// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ++// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 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. ++ ++#include ++ ++#include ++#include ++ ++static inline void ++store32_be(uint8_t dst[4], uint32_t w) ++{ ++ dst[3] = (uint8_t) w; w >>= 8; ++ dst[2] = (uint8_t) w; w >>= 8; ++ dst[1] = (uint8_t) w; w >>= 8; ++ dst[0] = (uint8_t) w; ++} ++ ++void ++crypto_pbkdf2_sha256(const uint8_t* passwd, size_t passwdlen, ++ const uint8_t* salt, size_t saltlen, uint64_t c, ++ uint8_t* buf, size_t dkLen) ++{ ++ crypto_auth_hmacsha256_state Phctx, PShctx, hctx; ++ size_t i; ++ uint8_t ivec[4]; ++ uint8_t U[32]; ++ uint8_t T[32]; ++ uint64_t j; ++ int k; ++ size_t clen; ++ ++ crypto_auth_hmacsha256_init(&Phctx, passwd, passwdlen); ++ PShctx = Phctx; ++ crypto_auth_hmacsha256_update(&PShctx, salt, saltlen); ++ ++ for (i = 0; i * 32 < dkLen; i++) { ++ store32_be(ivec, (uint32_t)(i + 1)); ++ hctx = PShctx; ++ crypto_auth_hmacsha256_update(&hctx, ivec, 4); ++ crypto_auth_hmacsha256_final(&hctx, U); ++ ++ memcpy(T, U, 32); ++ for (j = 2; j <= c; j++) { ++ hctx = Phctx; ++ crypto_auth_hmacsha256_update(&hctx, U, 32); ++ crypto_auth_hmacsha256_final(&hctx, U); ++ ++ for (k = 0; k < 32; k++) { ++ T[k] ^= U[k]; ++ } ++ } ++ ++ clen = dkLen - i * 32; ++ if (clen > 32) { ++ clen = 32; ++ } ++ memcpy(&buf[i * 32], T, clen); ++ } ++ sodium_memzero((void*)&Phctx, sizeof Phctx); ++ sodium_memzero((void*)&PShctx, sizeof PShctx); ++} +\ No newline at end of file +diff --git a/src/polyseed/pbkdf2.h b/src/polyseed/pbkdf2.h +new file mode 100644 +index 0000000..f6253b9 +--- /dev/null ++++ b/src/polyseed/pbkdf2.h +@@ -0,0 +1,46 @@ ++// Copyright (c) 2023, The Monero Project ++// Copyright (c) 2021, tevador ++// ++// All rights reserved. ++// ++// Redistribution and use in source and binary forms, with or without ++// modification, are permitted provided that the following conditions ++// are met: ++// 1. Redistributions of source code must retain the above copyright ++// notice, this list of conditions and the following disclaimer. ++// 2. Redistributions in binary form must reproduce the above copyright ++// notice, this list of conditions and the following disclaimer in the ++// documentation and/or other materials provided with the distribution. ++// ++// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ++// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ++// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ++// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ++// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 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. ++ ++#ifndef PBKDF2_H ++#define PBKDF2_H ++ ++#include ++#include ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++void ++crypto_pbkdf2_sha256(const uint8_t* passwd, size_t passwdlen, ++ const uint8_t* salt, size_t saltlen, uint64_t c, ++ uint8_t* buf, size_t dkLen); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif +\ No newline at end of file +diff --git a/src/polyseed/polyseed.cpp b/src/polyseed/polyseed.cpp +new file mode 100644 +index 0000000..231a48a +--- /dev/null ++++ b/src/polyseed/polyseed.cpp +@@ -0,0 +1,182 @@ ++// Copyright (c) 2023, The Monero Project ++// Copyright (c) 2021, tevador ++// ++// All rights reserved. ++// ++// Redistribution and use in source and binary forms, with or without ++// modification, are permitted provided that the following conditions ++// are met: ++// 1. Redistributions of source code must retain the above copyright ++// notice, this list of conditions and the following disclaimer. ++// 2. Redistributions in binary form must reproduce the above copyright ++// notice, this list of conditions and the following disclaimer in the ++// documentation and/or other materials provided with the distribution. ++// ++// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ++// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ++// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ++// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ++// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 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. ++ ++#include "polyseed.hpp" ++#include "pbkdf2.h" ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++namespace polyseed { ++ ++ inline size_t utf8_norm(const char* str, polyseed_str norm, utf8proc_option_t options) { ++ utf8proc_int32_t buffer[POLYSEED_STR_SIZE]; ++ utf8proc_ssize_t result; ++ ++ result = utf8proc_decompose(reinterpret_cast(str), 0, buffer, POLYSEED_STR_SIZE, options); ++ if (result < 0 || result > (POLYSEED_STR_SIZE - 1)) { ++ throw std::runtime_error("Unicode normalization failed"); ++ } ++ ++ result = utf8proc_reencode(buffer, result, options); ++ if (result < 0 || result > POLYSEED_STR_SIZE) { ++ throw std::runtime_error("Unicode normalization failed"); ++ } ++ ++ strcpy(norm, reinterpret_cast(buffer)); ++ sodium_memzero(buffer, POLYSEED_STR_SIZE); ++ return result; ++ } ++ ++ static size_t utf8_nfc(const char* str, polyseed_str norm) { ++ // Note: UTF8PROC_LUMP is used here to replace the ideographic space with a regular space for Japanese phrases ++ // to allow wallets to split on ' '. ++ return utf8_norm(str, norm, (utf8proc_option_t)(UTF8PROC_NULLTERM | UTF8PROC_STABLE | UTF8PROC_COMPOSE | UTF8PROC_STRIPNA)); ++ } ++ ++ static size_t utf8_nfkd(const char* str, polyseed_str norm) { ++ return utf8_norm(str, norm, (utf8proc_option_t)(UTF8PROC_NULLTERM | UTF8PROC_STABLE | UTF8PROC_DECOMPOSE | UTF8PROC_COMPAT | UTF8PROC_STRIPNA)); ++ } ++ ++ struct dependency { ++ dependency(); ++ std::vector languages; ++ }; ++ ++ static dependency deps; ++ ++ dependency::dependency() { ++ if (sodium_init() == -1) { ++ throw std::runtime_error("sodium_init failed"); ++ } ++ ++ polyseed_dependency pd; ++ pd.randbytes = &randombytes_buf; ++ pd.pbkdf2_sha256 = &crypto_pbkdf2_sha256; ++ pd.memzero = &sodium_memzero; ++ pd.u8_nfc = &utf8_nfc; ++ pd.u8_nfkd = &utf8_nfkd; ++ pd.time = nullptr; ++ pd.alloc = nullptr; ++ pd.free = nullptr; ++ ++ polyseed_inject(&pd); ++ ++ for (int i = 0; i < polyseed_get_num_langs(); ++i) { ++ languages.push_back(language(polyseed_get_lang(i))); ++ } ++ } ++ ++ static language invalid_lang; ++ ++ const std::vector& get_langs() { ++ return deps.languages; ++ } ++ ++ const language& get_lang_by_name(const std::string& name) { ++ for (auto& lang : deps.languages) { ++ if (name == lang.name_en()) { ++ return lang; ++ } ++ if (name == lang.name()) { ++ return lang; ++ } ++ } ++ return invalid_lang; ++ } ++ ++ inline void data::check_init() const { ++ if (valid()) { ++ throw std::runtime_error("already initialized"); ++ } ++ } ++ ++ static std::array error_desc = { ++ "Success", ++ "Wrong number of words in the phrase", ++ "Unknown language or unsupported words", ++ "Checksum mismatch", ++ "Unsupported seed features", ++ "Invalid seed format", ++ "Memory allocation failure", ++ "Unicode normalization failed" ++ }; ++ ++ static error get_error(polyseed_status status) { ++ if (status > 0 && status < sizeof(error_desc) / sizeof(const char*)) { ++ return error(error_desc[(int)status], status); ++ } ++ return error("Unknown error", status); ++ } ++ ++ void data::create(feature_type features) { ++ check_init(); ++ auto status = polyseed_create(features, &m_data); ++ if (status != POLYSEED_OK) { ++ throw get_error(status); ++ } ++ } ++ ++ void data::split(const language& lang, polyseed_phrase& words) { ++ check_init(); ++ if (!lang.valid()) { ++ throw std::runtime_error("invalid language"); ++ } ++ } ++ ++ void data::load(polyseed_storage storage) { ++ check_init(); ++ auto status = polyseed_load(storage, &m_data); ++ if (status != POLYSEED_OK) { ++ throw get_error(status); ++ } ++ } ++ ++ void data::load(const crypto::secret_key &key) { ++ polyseed_storage d; ++ memcpy(&d, &key.data, 32); ++ auto status = polyseed_load(d, &m_data); ++ if (status != POLYSEED_OK) { ++ throw get_error(status); ++ } ++ } ++ ++ language data::decode(const char* phrase) { ++ check_init(); ++ const polyseed_lang* lang; ++ auto status = polyseed_decode(phrase, m_coin, &lang, &m_data); ++ if (status != POLYSEED_OK) { ++ throw get_error(status); ++ } ++ return language(lang); ++ } ++} +diff --git a/src/polyseed/polyseed.hpp b/src/polyseed/polyseed.hpp +new file mode 100644 +index 0000000..2c8c777 +--- /dev/null ++++ b/src/polyseed/polyseed.hpp +@@ -0,0 +1,167 @@ ++// Copyright (c) 2023, The Monero Project ++// Copyright (c) 2021, tevador ++// ++// All rights reserved. ++// ++// Redistribution and use in source and binary forms, with or without ++// modification, are permitted provided that the following conditions ++// are met: ++// 1. Redistributions of source code must retain the above copyright ++// notice, this list of conditions and the following disclaimer. ++// 2. Redistributions in binary form must reproduce the above copyright ++// notice, this list of conditions and the following disclaimer in the ++// documentation and/or other materials provided with the distribution. ++// ++// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ++// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ++// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ++// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ++// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 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. ++ ++#ifndef POLYSEED_HPP ++#define POLYSEED_HPP ++ ++#include ++#include ++#include ++#include ++#include ++#include "crypto/crypto.h" ++ ++namespace polyseed { ++ ++ class data; ++ ++ class language { ++ public: ++ language() : m_lang(nullptr) {} ++ language(const language&) = default; ++ language(const polyseed_lang* lang) : m_lang(lang) {} ++ const char* name() const { ++ return polyseed_get_lang_name(m_lang); ++ } ++ const char* name_en() const { ++ return polyseed_get_lang_name_en(m_lang); ++ } ++ const char* separator() const { ++ return m_lang->separator; ++ } ++ bool valid() const { ++ return m_lang != nullptr; ++ } ++ ++ const polyseed_lang* m_lang; ++ private: ++ ++ friend class data; ++ }; ++ ++ const std::vector& get_langs(); ++ const language& get_lang_by_name(const std::string& name); ++ ++ class error : public std::runtime_error { ++ public: ++ error(const char* msg, polyseed_status status) ++ : std::runtime_error(msg), m_status(status) ++ { ++ } ++ polyseed_status status() const { ++ return m_status; ++ } ++ private: ++ polyseed_status m_status; ++ }; ++ ++ using feature_type = unsigned int; ++ ++ inline int enable_features(feature_type features) { ++ return polyseed_enable_features(features); ++ } ++ ++ class data { ++ public: ++ data(const data&) = delete; ++ data(polyseed_coin coin) : m_data(nullptr), m_coin(coin) {} ++ ~data() { ++ polyseed_free(m_data); ++ } ++ ++ void create(feature_type features); ++ ++ void load(polyseed_storage storage); ++ ++ void load(const crypto::secret_key &key); ++ ++ language decode(const char* phrase); ++ ++ template ++ void encode(const language& lang, str_type& str) const { ++ check_valid(); ++ if (!lang.valid()) { ++ throw std::runtime_error("invalid language"); ++ } ++ str.resize(POLYSEED_STR_SIZE); ++ auto size = polyseed_encode(m_data, lang.m_lang, m_coin, &str[0]); ++ str.resize(size); ++ } ++ ++ void split(const language& lang, polyseed_phrase& words); ++ ++ void save(polyseed_storage storage) const { ++ check_valid(); ++ polyseed_store(m_data, storage); ++ } ++ ++ void save(void *storage) const { ++ check_valid(); ++ polyseed_store(m_data, (uint8_t*)storage); ++ } ++ ++ void crypt(const char* password) { ++ check_valid(); ++ polyseed_crypt(m_data, password); ++ } ++ ++ void keygen(void* ptr, size_t key_size) const { ++ check_valid(); ++ polyseed_keygen(m_data, m_coin, key_size, (uint8_t*)ptr); ++ } ++ ++ bool valid() const { ++ return m_data != nullptr; ++ } ++ ++ bool encrypted() const { ++ check_valid(); ++ return polyseed_is_encrypted(m_data); ++ } ++ ++ uint64_t birthday() const { ++ check_valid(); ++ return polyseed_get_birthday(m_data); ++ } ++ ++ bool has_feature(feature_type feature) const { ++ check_valid(); ++ return polyseed_get_feature(m_data, feature) != 0; ++ } ++ private: ++ void check_valid() const { ++ if (m_data == nullptr) { ++ throw std::runtime_error("invalid object"); ++ } ++ } ++ void check_init() const; ++ ++ polyseed_data* m_data; ++ polyseed_coin m_coin; ++ }; ++} ++ ++#endif //POLYSEED_HPP +\ No newline at end of file +diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp +index 375edb4..0d78536 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 + return true; + } + ++bool WalletImpl::createFromPolyseed(const std::string &path, const std::string &password, const std::string &seed, ++ const std::string &passphrase, bool newWallet, uint64_t restoreHeight) ++{ ++ clearStatus(); ++ m_recoveringFromSeed = !newWallet; ++ m_recoveringFromDevice = false; ++ ++ polyseed::data polyseed(POLYSEED_COIN); ++ ++ try { ++ auto lang = polyseed.decode(seed.data()); ++ m_wallet->set_seed_language(lang.name()); ++ m_wallet->generate(path, password, polyseed, passphrase, !newWallet); ++ } ++ catch (const std::exception &e) { ++ setStatusError(e.what()); ++ return false; ++ } ++ ++ return true; ++} ++ + 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 + } + } + ++bool WalletImpl::getPolyseed(std::string &seed_words, std::string &passphrase) const ++{ ++ epee::wipeable_string seed_words_epee(seed_words.c_str(), seed_words.size()); ++ epee::wipeable_string passphrase_epee(passphrase.c_str(), passphrase.size()); ++ clearStatus(); ++ ++ if (!m_wallet) { ++ return false; ++ } ++ ++ bool result = m_wallet->get_polyseed(seed_words_epee, passphrase_epee); ++ ++ seed_words.assign(seed_words_epee.data(), seed_words_epee.size()); ++ passphrase.assign(passphrase_epee.data(), passphrase_epee.size()); ++ ++ return result; ++} ++ ++std::vector> Wallet::getPolyseedLanguages() ++ { ++ std::vector> languages; ++ ++ auto langs = polyseed::get_langs(); ++ for (const auto &lang : langs) { ++ languages.emplace_back(std::pair(lang.name_en(), lang.name())); ++ } ++ ++ return languages; ++} ++ ++bool Wallet::createPolyseed(std::string &seed_words, std::string &err, const std::string &language) ++{ ++ epee::wipeable_string seed_words_epee(seed_words.c_str(), seed_words.size()); ++ ++ try { ++ polyseed::data polyseed(POLYSEED_COIN); ++ polyseed.create(0); ++ polyseed.encode(polyseed::get_lang_by_name(language), seed_words_epee); ++ ++ seed_words.assign(seed_words_epee.data(), seed_words_epee.size()); ++ } ++ catch (const std::exception &e) { ++ err = e.what(); ++ return false; ++ } ++ ++ return true; ++} + std::string WalletImpl::getSeedLanguage() const + { + return m_wallet->get_seed_language(); +diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h +index 4e9c21e..32e1228 100644 +--- a/src/wallet/api/wallet.h ++++ b/src/wallet/api/wallet.h +@@ -79,9 +79,19 @@ public: + bool recoverFromDevice(const std::string &path, + const std::string &password, + const std::string &device_name); ++ ++ bool createFromPolyseed(const std::string &path, ++ const std::string &password, ++ const std::string &seed, ++ const std::string &passphrase = "", ++ bool newWallet = true, ++ uint64_t restoreHeight = 0); ++ + Device getDeviceType() const override; + bool close(bool store = true); + std::string seed(const std::string& seed_offset = "") const override; ++ bool getPolyseed(std::string &seed_words, std::string &passphrase) const override; ++ + std::string getSeedLanguage() const override; + void setSeedLanguage(const std::string &arg) override; + // void setListener(Listener *) {} +diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h +index 53ec4ab..be1c370 100644 +--- a/src/wallet/api/wallet2_api.h ++++ b/src/wallet/api/wallet2_api.h +@@ -709,6 +709,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); + ++ virtual bool getPolyseed(std::string &seed, std::string &passphrase) const = 0; ++ static bool createPolyseed(std::string &seed_words, std::string &err, const std::string &language = "English"); ++ static std::vector> getPolyseedLanguages(); ++ + /** + * @brief StartRefresh - Start/resume refresh thread (refresh every 10 seconds) + */ +@@ -1320,6 +1324,27 @@ struct WalletManager + uint64_t kdf_rounds = 1, + WalletListener * listener = nullptr) = 0; + ++ /*! ++ * \brief creates a wallet from a polyseed mnemonic phrase ++ * \param path Name of the wallet file to be created ++ * \param password Password of wallet file ++ * \param nettype Network type ++ * \param mnemonic Polyseed mnemonic ++ * \param passphrase Optional seed offset passphrase ++ * \param newWallet Whether it is a new wallet ++ * \param restoreHeight Override the embedded restore height ++ * \param kdf_rounds Number of rounds for key derivation function ++ * @return ++ */ ++ virtual Wallet * createWalletFromPolyseed(const std::string &path, ++ const std::string &password, ++ NetworkType nettype, ++ const std::string &mnemonic, ++ const std::string &passphrase = "", ++ bool newWallet = true, ++ uint64_t restore_height = 0, ++ uint64_t kdf_rounds = 1) = 0; ++ + /*! + * \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 e81b8f8..c79fe25 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, + return wallet; + } + ++Wallet *WalletManagerImpl::createWalletFromPolyseed(const std::string &path, const std::string &password, NetworkType nettype, ++ const std::string &mnemonic, const std::string &passphrase, ++ bool newWallet, uint64_t restoreHeight, uint64_t kdf_rounds) ++{ ++ WalletImpl * wallet = new WalletImpl(nettype, kdf_rounds); ++ wallet->createFromPolyseed(path, password, mnemonic, passphrase, newWallet, restoreHeight); ++ return wallet; ++} ++ + bool WalletManagerImpl::closeWallet(Wallet *wallet, bool store) + { + WalletImpl * wallet_ = dynamic_cast(wallet); +diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h +index a223e1d..28fcd36 100644 +--- a/src/wallet/api/wallet_manager.h ++++ b/src/wallet/api/wallet_manager.h +@@ -75,6 +75,16 @@ public: + const std::string &subaddressLookahead = "", + uint64_t kdf_rounds = 1, + WalletListener * listener = nullptr) override; ++ ++ virtual Wallet * createWalletFromPolyseed(const std::string &path, ++ const std::string &password, ++ NetworkType nettype, ++ const std::string &mnemonic, ++ const std::string &passphrase, ++ bool newWallet = true, ++ uint64_t restore_height = 0, ++ uint64_t kdf_rounds = 1) override; ++ + virtual bool closeWallet(Wallet *wallet, bool store = true) override; + 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 671fa52..3e49c21 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" ++#include "polyseed/include/polyseed.h" + + extern "C" + { +@@ -1278,7 +1279,8 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended, std + m_enable_multisig(false), + m_pool_info_query_time(0), + m_has_ever_refreshed_from_node(false), +- m_allow_mismatched_daemon_version(true) ++ m_allow_mismatched_daemon_version(true), ++ m_polyseed(false) + { + set_rpc_client_secret_key(rct::rct2sk(rct::skGen())); + } +@@ -1474,6 +1476,20 @@ bool wallet2::get_seed(epee::wipeable_string& electrum_words, const epee::wipeab + return true; + } + //---------------------------------------------------------------------------------------------------- ++ ++bool wallet2::get_polyseed(epee::wipeable_string& polyseed, epee::wipeable_string& passphrase) const ++{ ++ if (!m_polyseed) { ++ return false; ++ } ++ ++ polyseed::data data(POLYSEED_COIN); ++ data.load(get_account().get_keys().m_polyseed); ++ data.encode(polyseed::get_lang_by_name(seed_language), polyseed); ++ passphrase = get_account().get_keys().m_passphrase; ++ return true; ++} ++//---------------------------------------------------------------------------------------------------- + bool wallet2::get_multisig_seed(epee::wipeable_string& seed, const epee::wipeable_string &passphrase) const + { + bool ready; +@@ -4792,6 +4808,9 @@ boost::optional wallet2::get_keys_file_data(const crypt + value2.SetInt(m_enable_multisig ? 1 : 0); + json.AddMember("enable_multisig", value2, json.GetAllocator()); + ++ value2.SetInt(m_polyseed ? 1 : 0); ++ json.AddMember("polyseed", value2, json.GetAllocator()); ++ + if (m_background_sync_type == BackgroundSyncCustomPassword && !background_keys_file && m_custom_background_key) + { + value.SetString(reinterpret_cast(m_custom_background_key.get().data()), m_custom_background_key.get().size()); +@@ -5031,6 +5050,7 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st + m_enable_multisig = false; + m_allow_mismatched_daemon_version = true; + m_custom_background_key = boost::none; ++ m_polyseed = false; + } + else if(json.IsObject()) + { +@@ -5271,6 +5291,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; + ++ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, polyseed, int, Int, false, false); ++ m_polyseed = field_polyseed; ++ + // Load encryption key used to encrypt background cache + crypto::chacha_key custom_background_key; + m_custom_background_key = boost::none; +@@ -5590,6 +5613,48 @@ void wallet2::init_type(hw::device::device_type device_type) + m_key_device_type = device_type; + } + ++/*! ++ * \brief Generates a polyseed wallet or restores one. ++ * \param wallet_ Name of wallet file ++ * \param password Password of wallet file ++ * \param passphrase Seed offset passphrase ++ * \param recover Whether it is a restore ++ * \param seed_words If it is a restore, the polyseed ++ * \param create_address_file Whether to create an address file ++ * \return The secret key of the generated wallet ++ */ ++void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& password, ++ const polyseed::data &seed, const epee::wipeable_string& passphrase, bool recover, uint64_t restoreHeight, bool create_address_file) ++{ ++ clear(); ++ prepare_file_names(wallet_); ++ ++ if (!wallet_.empty()) { ++ boost::system::error_code ignored_ec; ++ THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_wallet_file, ignored_ec), error::file_exists, m_wallet_file); ++ THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_keys_file, ignored_ec), error::file_exists, m_keys_file); ++ } ++ ++ m_account.create_from_polyseed(seed, passphrase); ++ ++ init_type(hw::device::device_type::SOFTWARE); ++ m_polyseed = true; ++ setup_keys(password); ++ ++ if (recover) { ++ m_refresh_from_block_height = estimate_blockchain_height(restoreHeight > 0 ? restoreHeight : seed.birthday()); ++ } else { ++ m_refresh_from_block_height = estimate_blockchain_height(); ++ } ++ ++ create_keys_file(wallet_, false, password, m_nettype != MAINNET || create_address_file); ++ ++ setup_new_blockchain(); ++ ++ if (!wallet_.empty()) ++ store(); ++} ++ + /*! + * \brief Generates a wallet or restores one. Assumes the multisig setup + * has already completed for the provided multisig info. +@@ -5717,7 +5782,7 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wip + return retval; + } + +- uint64_t wallet2::estimate_blockchain_height() ++ uint64_t wallet2::estimate_blockchain_height(uint64_t time) + { + // -1 month for fluctuations in block time and machine date/time setup. + // avg seconds per block +@@ -5741,7 +5806,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. +- height = get_approximate_blockchain_height(); ++ height = get_approximate_blockchain_height(time); + uint64_t target_height = get_daemon_blockchain_target_height(err); + if (err.empty()) { + if (target_height < height) +@@ -13634,9 +13699,10 @@ 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 + { + uint64_t approx_blockchain_height = m_nettype == TESTNET ? 0 : (time(NULL) - 1522624244)/307; ++ // uint64_t approx_blockchain_height = fork_block + ((t > 0 ? t : time(NULL)) - fork_time)/seconds_per_block; + LOG_PRINT_L2("Calculated blockchain height: " << approx_blockchain_height); + return approx_blockchain_height; + } +@@ -15771,15 +15837,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) + { +- uint32_t version; +- if (!check_connection(&version)) +- { +- throw std::runtime_error("failed to connect to daemon: " + get_daemon_address()); +- } +- if (version < MAKE_CORE_RPC_VERSION(1, 6)) +- { +- throw std::runtime_error("this function requires RPC version 1.6 or higher"); +- } + std::tm date = { 0, 0, 0, 0, 0, 0, 0, 0 }; + date.tm_year = year - 1900; + date.tm_mon = month - 1; +@@ -15788,7 +15845,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"); + } ++ + uint64_t timestamp_target = std::mktime(&date); ++ ++ return get_blockchain_height_by_timestamp(timestamp_target); ++} ++ ++uint64_t wallet2::get_blockchain_height_by_timestamp(uint64_t timestamp_target) { ++ uint32_t version; ++ if (!check_connection(&version)) ++ { ++ throw std::runtime_error("failed to connect to daemon: " + get_daemon_address()); ++ } ++ if (version < MAKE_CORE_RPC_VERSION(1, 6)) ++ { ++ throw std::runtime_error("this function requires RPC version 1.6 or higher"); ++ } ++ + std::string err; + 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 80ff069..c26349c 100644 +--- a/src/wallet/wallet2.h ++++ b/src/wallet/wallet2.h +@@ -72,6 +72,7 @@ + #include "message_store.h" + #include "wallet_light_rpc.h" + #include "wallet_rpc_helpers.h" ++#include "polyseed/polyseed.hpp" + + #undef MONERO_DEFAULT_LOG_CATEGORY + #define MONERO_DEFAULT_LOG_CATEGORY "wallet.wallet2" +@@ -921,6 +922,20 @@ private: + void generate(const std::string& wallet_, const epee::wipeable_string& password, + const epee::wipeable_string& multisig_data, bool create_address_file = false); + ++ /*! ++ * \brief Generates a wallet from a polyseed. ++ * @param wallet_ Name of wallet file ++ * @param password Password of wallet file ++ * @param seed Polyseed data ++ * @param passphrase Optional seed offset passphrase ++ * @param recover Whether it is a restore ++ * @param restoreHeight Override the embedded restore height ++ * @param create_address_file Whether to create an address file ++ */ ++ void generate(const std::string& wallet_, const epee::wipeable_string& password, ++ const polyseed::data &seed, const epee::wipeable_string& passphrase = "", ++ bool recover = false, uint64_t restoreHeight = 0, bool create_address_file = false); ++ + /*! + * \brief Generates a wallet or restores one. + * \param wallet_ Name of wallet file +@@ -1088,6 +1103,15 @@ private: + bool is_deterministic() const; + bool get_seed(epee::wipeable_string& electrum_words, const epee::wipeable_string &passphrase = epee::wipeable_string()) const; + ++ /*! ++ * \brief get_polyseed Gets the polyseed (if available) and passphrase (if set) needed to recover the wallet. ++ * @param seed Polyseed mnemonic phrase ++ * @param passphrase Seed offset passphrase that was used to restore the wallet ++ * @return Returns true if the wallet has a polyseed. ++ * Note: both the mnemonic phrase and the passphrase are needed to recover the wallet ++ */ ++ 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. + */ +@@ -1555,8 +1579,8 @@ private: + /*! + * \brief Calculates the approximate blockchain height from current date/time. + */ +- uint64_t get_approximate_blockchain_height() const; +- uint64_t estimate_blockchain_height(); ++ uint64_t get_approximate_blockchain_height(uint64_t time = 0) const; ++ uint64_t estimate_blockchain_height(uint64_t time = 0); + std::vector select_available_outputs_from_histogram(uint64_t count, bool atleast, bool unlocked, bool allow_rct); + std::vector select_available_outputs(const std::function &f); + std::vector select_available_unmixable_outputs(); +@@ -1650,6 +1674,7 @@ private: + 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 ++ uint64_t get_blockchain_height_by_timestamp(uint64_t timestamp); + + bool is_synced(); + +@@ -1995,6 +2020,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 */ ++ bool m_polyseed; + bool m_multisig; /*!< if > 1 spend secret key will not match spend public key */ + uint32_t m_multisig_threshold; + std::vector m_multisig_signers; +-- +2.39.5 (Apple Git-154) + diff --git a/patches/wownero/0011-Add-hex-encoding-and-tx-key-getter-for-PendingTransc.patch b/patches/wownero/0011-Add-hex-encoding-and-tx-key-getter-for-PendingTransc.patch deleted file mode 100644 index d5a7cb3..0000000 --- a/patches/wownero/0011-Add-hex-encoding-and-tx-key-getter-for-PendingTransc.patch +++ /dev/null @@ -1,68 +0,0 @@ -From 8fe8ce5b9d688e3f97d2dd7fa7ff688ec6308cb6 Mon Sep 17 00:00:00 2001 -From: M -Date: Fri, 21 Apr 2023 15:43:47 -0400 -Subject: [PATCH 11/13] Add hex encoding and tx key getter for - PendingTransction in wallet api. - ---- - src/wallet/api/pending_transaction.cpp | 16 ++++++++++++++++ - src/wallet/api/pending_transaction.h | 2 ++ - src/wallet/api/wallet2_api.h | 2 ++ - 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 ---- a/src/wallet/api/pending_transaction.cpp -+++ b/src/wallet/api/pending_transaction.cpp -@@ -80,6 +80,22 @@ std::vector PendingTransactionImpl::txid() const - return txid; - } - -+std::vector PendingTransactionImpl::hex() const -+{ -+ std::vector hexs; -+ for (const auto &pt: m_pending_tx) -+ hexs.push_back(epee::string_tools::buff_to_hex_nodelimer(cryptonote::tx_to_blob(pt.tx))); -+ return hexs; -+} -+ -+std::vector PendingTransactionImpl::txKey() const -+{ -+ std::vector keys; -+ for (const auto& pt: m_pending_tx) -+ keys.push_back(epee::string_tools::pod_to_hex(pt.tx_key)); -+ return keys; -+} -+ - bool PendingTransactionImpl::commit(const std::string &filename, bool overwrite) - { - -diff --git a/src/wallet/api/pending_transaction.h b/src/wallet/api/pending_transaction.h -index 403bfe281..0cc6c58e9 100644 ---- a/src/wallet/api/pending_transaction.h -+++ b/src/wallet/api/pending_transaction.h -@@ -59,6 +59,8 @@ public: - std::string multisigSignData() override; - void signMultisigTx() override; - std::vector signersKeys() const override; -+ std::vector hex() const override; -+ std::vector txKey() const override; - - private: - friend class WalletImpl; -diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h -index 013b5bcba..f421fdc05 100644 ---- a/src/wallet/api/wallet2_api.h -+++ b/src/wallet/api/wallet2_api.h -@@ -127,6 +127,8 @@ struct PendingTransaction - * @return vector of base58-encoded signers' public keys - */ - virtual std::vector signersKeys() const = 0; -+ virtual std::vector hex() const = 0; -+ virtual std::vector txKey() const = 0; - }; - - /** --- -2.43.0 - diff --git a/patches/wownero/0011-coin-control.patch b/patches/wownero/0011-coin-control.patch new file mode 100644 index 0000000..342151e --- /dev/null +++ b/patches/wownero/0011-coin-control.patch @@ -0,0 +1,979 @@ +From 4292758a3d7c0186ee3fb665d789e078ff3e9ec6 Mon Sep 17 00:00:00 2001 +From: tobtoht +Date: Tue, 12 Mar 2024 11:07:57 +0100 +Subject: [PATCH 11/14] coin control + +--- + src/simplewallet/simplewallet.cpp | 2 +- + src/wallet/api/CMakeLists.txt | 8 +- + src/wallet/api/coins.cpp | 186 ++++++++++++++++++++++++++++++ + src/wallet/api/coins.h | 40 +++++++ + src/wallet/api/coins_info.cpp | 122 ++++++++++++++++++++ + src/wallet/api/coins_info.h | 71 ++++++++++++ + src/wallet/api/wallet.cpp | 64 +++++++++- + 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, 593 insertions(+), 19 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 8c51220..cfdb893 100644 +--- a/src/simplewallet/simplewallet.cpp ++++ b/src/simplewallet/simplewallet.cpp +@@ -6981,7 +6981,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, +- m_current_subaddress_account, subaddr_indices, subtract_fee_from_outputs); ++ m_current_subaddress_account, subaddr_indices, {}, subtract_fee_from_outputs); + + if (ptx_vector.empty()) + { +diff --git a/src/wallet/api/CMakeLists.txt b/src/wallet/api/CMakeLists.txt +index af7948d..bb740e2 100644 +--- a/src/wallet/api/CMakeLists.txt ++++ b/src/wallet/api/CMakeLists.txt +@@ -40,7 +40,9 @@ set(wallet_api_sources + address_book.cpp + subaddress.cpp + subaddress_account.cpp +- unsigned_transaction.cpp) ++ unsigned_transaction.cpp ++ coins.cpp ++ coins_info.cpp) + + set(wallet_api_headers + wallet2_api.h) +@@ -55,7 +57,9 @@ set(wallet_api_private_headers + address_book.h + subaddress.h + subaddress_account.h +- unsigned_transaction.h) ++ unsigned_transaction.h ++ coins.h ++ coins_info.h) + + monero_private_headers(wallet_api + ${wallet_api_private_headers}) +diff --git a/src/wallet/api/coins.cpp b/src/wallet/api/coins.cpp +new file mode 100644 +index 0000000..ef12141 +--- /dev/null ++++ b/src/wallet/api/coins.cpp +@@ -0,0 +1,186 @@ ++#include "coins.h" ++#include "coins_info.h" ++#include "wallet.h" ++#include "crypto/hash.h" ++#include "wallet/wallet2.h" ++#include "common_defines.h" ++ ++#include ++#include ++ ++using namespace epee; ++ ++namespace Monero { ++ ++Coins::~Coins() = default; ++ ++CoinsImpl::CoinsImpl(WalletImpl *wallet) ++ : m_wallet(wallet) {} ++ ++CoinsImpl::~CoinsImpl() ++{ ++ for (auto t : m_rows) ++ delete t; ++} ++ ++int CoinsImpl::count() const ++{ ++ boost::shared_lock lock(m_rowsMutex); ++ int result = m_rows.size(); ++ return result; ++} ++ ++CoinsInfo *CoinsImpl::coin(int index) const ++{ ++ boost::shared_lock lock(m_rowsMutex); ++ // sanity check ++ if (index < 0) ++ return nullptr; ++ auto index_ = static_cast(index); ++ return index_ < m_rows.size() ? m_rows[index_] : nullptr; ++} ++ ++std::vector CoinsImpl::getAll() const ++{ ++ boost::shared_lock lock(m_rowsMutex); ++ return m_rows; ++} ++ ++ ++void CoinsImpl::refresh() ++{ ++ LOG_PRINT_L2("Refreshing coins"); ++ ++ boost::unique_lock lock(m_rowsMutex); ++ boost::shared_lock transfers_lock(m_wallet->m_wallet->m_transfers_mutex); ++ ++ // delete old outputs; ++ for (auto t : m_rows) ++ delete t; ++ m_rows.clear(); ++ ++ for (size_t i = 0; i < m_wallet->m_wallet->get_num_transfer_details(); ++i) ++ { ++ const tools::wallet2::transfer_details &td = m_wallet->m_wallet->get_transfer_details(i); ++ ++ auto ci = new CoinsInfoImpl(); ++ ci->m_blockHeight = td.m_block_height; ++ ci->m_hash = string_tools::pod_to_hex(td.m_txid); ++ ci->m_internalOutputIndex = td.m_internal_output_index; ++ ci->m_globalOutputIndex = td.m_global_output_index; ++ ci->m_spent = td.m_spent; ++ ci->m_frozen = td.m_frozen; ++ ci->m_spentHeight = td.m_spent_height; ++ ci->m_amount = td.m_amount; ++ ci->m_rct = td.m_rct; ++ ci->m_keyImageKnown = td.m_key_image_known; ++ ci->m_pkIndex = td.m_pk_index; ++ ci->m_subaddrIndex = td.m_subaddr_index.minor; ++ ci->m_subaddrAccount = td.m_subaddr_index.major; ++ ci->m_address = m_wallet->m_wallet->get_subaddress_as_str(td.m_subaddr_index); // todo: this is expensive, cache maybe? ++ ci->m_addressLabel = m_wallet->m_wallet->get_subaddress_label(td.m_subaddr_index); ++ ci->m_keyImage = string_tools::pod_to_hex(td.m_key_image); ++ ci->m_unlockTime = td.m_tx.unlock_time; ++ ci->m_unlocked = m_wallet->m_wallet->is_transfer_unlocked(td); ++ ci->m_pubKey = string_tools::pod_to_hex(td.get_public_key()); ++ ci->m_coinbase = td.m_tx.vin.size() == 1 && td.m_tx.vin[0].type() == typeid(cryptonote::txin_gen); ++ ci->m_description = m_wallet->m_wallet->get_tx_note(td.m_txid); ++ ++ m_rows.push_back(ci); ++ } ++} ++ ++void CoinsImpl::setFrozen(std::string public_key) ++{ ++ crypto::public_key pk; ++ if (!epee::string_tools::hex_to_pod(public_key, pk)) ++ { ++ LOG_ERROR("Invalid public key: " << public_key); ++ return; ++ } ++ ++ try ++ { ++ m_wallet->m_wallet->freeze(pk); ++ refresh(); ++ } ++ catch (const std::exception& e) ++ { ++ LOG_ERROR("setFrozen: " << e.what()); ++ } ++} ++ ++void CoinsImpl::setFrozen(int index) ++{ ++ try ++ { ++ LOG_ERROR("Freezing coin: " << index); ++ m_wallet->m_wallet->freeze(index); ++ refresh(); ++ } ++ catch (const std::exception& e) ++ { ++ LOG_ERROR("setLabel: " << e.what()); ++ } ++} ++ ++void CoinsImpl::thaw(std::string public_key) ++{ ++ crypto::public_key pk; ++ if (!epee::string_tools::hex_to_pod(public_key, pk)) ++ { ++ LOG_ERROR("Invalid public key: " << public_key); ++ return; ++ } ++ ++ try ++ { ++ m_wallet->m_wallet->thaw(pk); ++ refresh(); ++ } ++ catch (const std::exception& e) ++ { ++ LOG_ERROR("thaw: " << e.what()); ++ } ++} ++ ++void CoinsImpl::thaw(int index) ++{ ++ try ++ { ++ m_wallet->m_wallet->thaw(index); ++ refresh(); ++ } ++ catch (const std::exception& e) ++ { ++ LOG_ERROR("thaw: " << e.what()); ++ } ++} ++ ++bool CoinsImpl::isTransferUnlocked(uint64_t unlockTime, uint64_t blockHeight) { ++ return m_wallet->m_wallet->is_transfer_unlocked(unlockTime, blockHeight); ++} ++ ++void CoinsImpl::setDescription(const std::string &public_key, const std::string &description) ++{ ++ crypto::public_key pk; ++ if (!epee::string_tools::hex_to_pod(public_key, pk)) ++ { ++ LOG_ERROR("Invalid public key: " << public_key); ++ return; ++ } ++ ++ try ++ { ++ const size_t index = m_wallet->m_wallet->get_transfer_details(pk); ++ const tools::wallet2::transfer_details &td = m_wallet->m_wallet->get_transfer_details(index); ++ m_wallet->m_wallet->set_tx_note(td.m_txid, description); ++ refresh(); ++ } ++ catch (const std::exception& e) ++ { ++ LOG_ERROR("setDescription: " << e.what()); ++ } ++} ++ ++} // namespace +diff --git a/src/wallet/api/coins.h b/src/wallet/api/coins.h +new file mode 100644 +index 0000000..b7a0a86 +--- /dev/null ++++ b/src/wallet/api/coins.h +@@ -0,0 +1,40 @@ ++#ifndef FEATHER_COINS_H ++#define FEATHER_COINS_H ++ ++#include "wallet/api/wallet2_api.h" ++#include "wallet/wallet2.h" ++ ++namespace Monero { ++ ++class WalletImpl; ++ ++class CoinsImpl : public Coins ++{ ++public: ++ explicit CoinsImpl(WalletImpl * wallet); ++ ~CoinsImpl() override; ++ int count() const override; ++ CoinsInfo * coin(int index) const override; ++ std::vector getAll() const override; ++ void refresh() override; ++ ++ void setFrozen(std::string public_key) override; ++ void setFrozen(int index) override; ++ void thaw(std::string public_key) override; ++ void thaw(int index) override; ++ ++ bool isTransferUnlocked(uint64_t unlockTime, uint64_t blockHeight) override; ++ ++ void setDescription(const std::string &public_key, const std::string &description) override; ++ ++private: ++ WalletImpl *m_wallet; ++ std::vector m_rows; ++ mutable boost::shared_mutex m_rowsMutex; ++}; ++ ++} ++ ++namespace Bitmonero = Monero; ++ ++#endif //FEATHER_COINS_H +diff --git a/src/wallet/api/coins_info.cpp b/src/wallet/api/coins_info.cpp +new file mode 100644 +index 0000000..5f2c4e1 +--- /dev/null ++++ b/src/wallet/api/coins_info.cpp +@@ -0,0 +1,122 @@ ++#include "coins_info.h" ++ ++using namespace std; ++ ++namespace Monero { ++ ++CoinsInfo::~CoinsInfo() = default; ++ ++CoinsInfoImpl::CoinsInfoImpl() ++ : m_blockHeight(0) ++ , m_internalOutputIndex(0) ++ , m_globalOutputIndex(0) ++ , m_spent(false) ++ , m_frozen(false) ++ , m_spentHeight(0) ++ , m_amount(0) ++ , m_rct(false) ++ , m_keyImageKnown(false) ++ , m_pkIndex(0) ++ , m_subaddrAccount(0) ++ , m_subaddrIndex(0) ++ , m_unlockTime(0) ++ , m_unlocked(false) ++{ ++ ++} ++ ++CoinsInfoImpl::~CoinsInfoImpl() = default; ++ ++uint64_t CoinsInfoImpl::blockHeight() const ++{ ++ return m_blockHeight; ++} ++ ++string CoinsInfoImpl::hash() const ++{ ++ return m_hash; ++} ++ ++size_t CoinsInfoImpl::internalOutputIndex() const { ++ return m_internalOutputIndex; ++} ++ ++uint64_t CoinsInfoImpl::globalOutputIndex() const ++{ ++ return m_globalOutputIndex; ++} ++ ++bool CoinsInfoImpl::spent() const ++{ ++ return m_spent; ++} ++ ++bool CoinsInfoImpl::frozen() const ++{ ++ return m_frozen; ++} ++ ++uint64_t CoinsInfoImpl::spentHeight() const ++{ ++ return m_spentHeight; ++} ++ ++uint64_t CoinsInfoImpl::amount() const ++{ ++ return m_amount; ++} ++ ++bool CoinsInfoImpl::rct() const { ++ return m_rct; ++} ++ ++bool CoinsInfoImpl::keyImageKnown() const { ++ return m_keyImageKnown; ++} ++ ++size_t CoinsInfoImpl::pkIndex() const { ++ return m_pkIndex; ++} ++ ++uint32_t CoinsInfoImpl::subaddrIndex() const { ++ return m_subaddrIndex; ++} ++ ++uint32_t CoinsInfoImpl::subaddrAccount() const { ++ return m_subaddrAccount; ++} ++ ++string CoinsInfoImpl::address() const { ++ return m_address; ++} ++ ++string CoinsInfoImpl::addressLabel() const { ++ return m_addressLabel; ++} ++ ++string CoinsInfoImpl::keyImage() const { ++ return m_keyImage; ++} ++ ++uint64_t CoinsInfoImpl::unlockTime() const { ++ return m_unlockTime; ++} ++ ++bool CoinsInfoImpl::unlocked() const { ++ return m_unlocked; ++} ++ ++string CoinsInfoImpl::pubKey() const { ++ return m_pubKey; ++} ++ ++bool CoinsInfoImpl::coinbase() const { ++ return m_coinbase; ++} ++ ++string CoinsInfoImpl::description() const { ++ return m_description; ++} ++} // namespace ++ ++namespace Bitmonero = Monero; +diff --git a/src/wallet/api/coins_info.h b/src/wallet/api/coins_info.h +new file mode 100644 +index 0000000..c43e45a +--- /dev/null ++++ b/src/wallet/api/coins_info.h +@@ -0,0 +1,71 @@ ++#ifndef FEATHER_COINS_INFO_H ++#define FEATHER_COINS_INFO_H ++ ++#include "wallet/api/wallet2_api.h" ++#include ++#include ++ ++namespace Monero { ++ ++class CoinsImpl; ++ ++class CoinsInfoImpl : public CoinsInfo ++{ ++public: ++ CoinsInfoImpl(); ++ ~CoinsInfoImpl(); ++ ++ virtual uint64_t blockHeight() const override; ++ virtual std::string hash() const override; ++ virtual size_t internalOutputIndex() const override; ++ virtual uint64_t globalOutputIndex() const override; ++ virtual bool spent() const override; ++ virtual bool frozen() const override; ++ virtual uint64_t spentHeight() const override; ++ virtual uint64_t amount() const override; ++ virtual bool rct() const override; ++ virtual bool keyImageKnown() const override; ++ virtual size_t pkIndex() const override; ++ virtual uint32_t subaddrIndex() const override; ++ virtual uint32_t subaddrAccount() const override; ++ virtual std::string address() const override; ++ virtual std::string addressLabel() const override; ++ virtual std::string keyImage() const override; ++ virtual uint64_t unlockTime() const override; ++ virtual bool unlocked() const override; ++ virtual std::string pubKey() const override; ++ virtual bool coinbase() const override; ++ virtual std::string description() const override; ++ ++private: ++ uint64_t m_blockHeight; ++ std::string m_hash; ++ size_t m_internalOutputIndex; ++ uint64_t m_globalOutputIndex; ++ bool m_spent; ++ bool m_frozen; ++ uint64_t m_spentHeight; ++ uint64_t m_amount; ++ bool m_rct; ++ bool m_keyImageKnown; ++ size_t m_pkIndex; ++ uint32_t m_subaddrIndex; ++ uint32_t m_subaddrAccount; ++ std::string m_address; ++ std::string m_addressLabel; ++ std::string m_keyImage; ++ uint64_t m_unlockTime; ++ bool m_unlocked; ++ std::string m_pubKey; ++ bool m_coinbase; ++ std::string m_description; ++ ++ friend class CoinsImpl; ++ ++}; ++ ++} // namespace ++ ++namespace Bitmonero = Monero; ++ ++#endif //FEATHER_COINS_INFO_H +diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp +index 0d78536..ff1baac 100644 +--- a/src/wallet/api/wallet.cpp ++++ b/src/wallet/api/wallet.cpp +@@ -35,6 +35,7 @@ + #include "transaction_history.h" + #include "address_book.h" + #include "subaddress.h" ++#include "coins.h" + #include "subaddress_account.h" + #include "common_defines.h" + #include "common/util.h" +@@ -473,6 +474,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)); ++ m_coins.reset(new CoinsImpl(this)); + m_subaddressAccount.reset(new SubaddressAccountImpl(this)); + + +@@ -2046,7 +2048,7 @@ PendingTransaction* WalletImpl::restoreMultisigTransaction(const string& signDat + // - unconfirmed_transfer_details; + // - confirmed_transfer_details) + +-PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector &dst_addr, const string &payment_id, optional> amount, uint32_t mixin_count, PendingTransaction::Priority priority, uint32_t subaddr_account, std::set subaddr_indices) ++PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector &dst_addr, const string &payment_id, optional> amount, uint32_t mixin_count, PendingTransaction::Priority priority, uint32_t subaddr_account, std::set subaddr_indices, const std::set &preferred_inputs) + + { + clearStatus(); +@@ -2084,6 +2086,7 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vectornettype(), dst_addr[i])) { + // TODO: copy-paste 'if treating as an address fails, try as url' from simplewallet.cpp:1982 +@@ -2105,6 +2108,7 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vectorunlocked_balance(subaddr_account, true); ++ // if (maxAllowedSpend < amountSum) { ++ // error = true; ++ // setStatusError(tr("Amount you are trying to spend is larger than unlocked amount")); ++ // break; ++ // } ++ std::vector preferred_input_list; ++ if (!preferred_inputs.empty()) { ++ LOG_ERROR("empty"); ++ ++ for (const auto &public_key : preferred_inputs) { ++ crypto::key_image keyImage; ++ bool r = epee::string_tools::hex_to_pod(public_key, keyImage); ++ if (!r) { ++ error = true; ++ setStatusError(tr("failed to parse key image")); ++ break; ++ } ++ if (m_wallet->frozen(keyImage)) { ++ error = true; ++ setStatusError(tr("refusing to spend frozen coin")); ++ break; ++ } ++ ++ preferred_input_list.push_back(keyImage); ++ } ++ } else { ++ LOG_ERROR("not empty"); ++ ++ boost::shared_lock transfers_lock(m_wallet->m_transfers_mutex); ++ for (size_t i = 0; i < m_wallet->get_num_transfer_details(); ++i) { ++ const tools::wallet2::transfer_details &td = m_wallet->get_transfer_details(i); ++ LOG_ERROR("COIN: " << i << ": " << td.amount() << "; "<frozen(td)); ++ if (td.m_spent) continue; ++ LOG_ERROR("is frozen"); ++ if (!td.m_frozen) { ++ LOG_ERROR("isn't:"); ++ LOG_ERROR("hash: " << td.m_key_image << "; " << td.amount()); ++ preferred_input_list.push_back(td.m_key_image); ++ } ++ } ++ } ++ for (const auto &de : preferred_input_list) { ++ LOG_ERROR("preferred input: " << de); ++ } + if (error) { + break; + } +@@ -2129,11 +2178,11 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vectorm_pending_tx = m_wallet->create_transactions_2(dsts, fake_outs_count, + adjusted_priority, +- extra, subaddr_account, subaddr_indices); ++ extra, subaddr_account, subaddr_indices, preferred_input_list); + } else { + transaction->m_pending_tx = m_wallet->create_transactions_all(0, info.address, info.is_subaddress, 1, fake_outs_count, + adjusted_priority, +- extra, subaddr_account, subaddr_indices); ++ extra, subaddr_account, subaddr_indices, preferred_input_list); + } + pendingTxPostProcess(transaction); + +@@ -2214,10 +2263,10 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector amount, uint32_t mixin_count, +- PendingTransaction::Priority priority, uint32_t subaddr_account, std::set subaddr_indices) ++ PendingTransaction::Priority priority, uint32_t subaddr_account, std::set subaddr_indices, const std::set &preferred_inputs) + + { +- return createTransactionMultDest(std::vector {dst_addr}, payment_id, amount ? (std::vector {*amount}) : (optional>()), mixin_count, priority, subaddr_account, subaddr_indices); ++ return createTransactionMultDest(std::vector {dst_addr}, payment_id, amount ? (std::vector {*amount}) : (optional>()), mixin_count, priority, subaddr_account, subaddr_indices, preferred_inputs); + } + + PendingTransaction *WalletImpl::createSweepUnmixableTransaction() +@@ -2342,6 +2391,11 @@ AddressBook *WalletImpl::addressBook() + return m_addressBook.get(); + } + ++Coins *WalletImpl::coins() ++{ ++ return m_coins.get(); ++} ++ + Subaddress *WalletImpl::subaddress() + { + return m_subaddress.get(); +diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h +index 32e1228..a82f270 100644 +--- a/src/wallet/api/wallet.h ++++ b/src/wallet/api/wallet.h +@@ -46,6 +46,7 @@ class PendingTransactionImpl; + class UnsignedTransactionImpl; + class AddressBookImpl; + class SubaddressImpl; ++class CoinsImpl; + class SubaddressAccountImpl; + struct Wallet2CallbackImpl; + +@@ -167,12 +168,14 @@ public: + optional> amount, uint32_t mixin_count, + PendingTransaction::Priority priority = PendingTransaction::Priority_Low, + uint32_t subaddr_account = 0, +- std::set subaddr_indices = {}) override; ++ std::set subaddr_indices = {}, ++ const std::set &preferred_inputs = {}) override; + PendingTransaction * createTransaction(const std::string &dst_addr, const std::string &payment_id, + optional amount, uint32_t mixin_count, + PendingTransaction::Priority priority = PendingTransaction::Priority_Low, + uint32_t subaddr_account = 0, +- std::set subaddr_indices = {}) override; ++ std::set subaddr_indices = {}, ++ const std::set &preferred_inputs = {}) override; + virtual PendingTransaction * createSweepUnmixableTransaction() override; + bool submitTransaction(const std::string &fileName) override; + bool submitTransactionUR(const std::string &input) override; +@@ -201,6 +204,7 @@ public: + PendingTransaction::Priority priority) const override; + virtual TransactionHistory * history() override; + virtual AddressBook * addressBook() override; ++ virtual Coins * coins() override; + virtual Subaddress * subaddress() override; + virtual SubaddressAccount * subaddressAccount() override; + virtual void setListener(WalletListener * l) override; +@@ -272,6 +276,7 @@ private: + friend class TransactionHistoryImpl; + friend struct Wallet2CallbackImpl; + friend class AddressBookImpl; ++ friend class CoinsImpl; + friend class SubaddressImpl; + friend class SubaddressAccountImpl; + +@@ -288,6 +293,7 @@ private: + std::unique_ptr m_wallet2Callback; + std::unique_ptr m_addressBook; + std::unique_ptr m_subaddress; ++ std::unique_ptr m_coins; + std::unique_ptr m_subaddressAccount; + + // multi-threaded refresh stuff +diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h +index be1c370..013b5bc 100644 +--- a/src/wallet/api/wallet2_api.h ++++ b/src/wallet/api/wallet2_api.h +@@ -263,6 +263,51 @@ struct AddressBook + virtual int lookupPaymentID(const std::string &payment_id) const = 0; + }; + ++/** ++ * @brief The CoinsInfo - interface for displaying coins information ++ */ ++struct CoinsInfo ++{ ++ virtual ~CoinsInfo() = 0; ++ ++ virtual uint64_t blockHeight() const = 0; ++ virtual std::string hash() const = 0; ++ virtual size_t internalOutputIndex() const = 0; ++ virtual uint64_t globalOutputIndex() const = 0; ++ virtual bool spent() const = 0; ++ virtual bool frozen() const = 0; ++ virtual uint64_t spentHeight() const = 0; ++ virtual uint64_t amount() const = 0; ++ virtual bool rct() const = 0; ++ virtual bool keyImageKnown() const = 0; ++ virtual size_t pkIndex() const = 0; ++ virtual uint32_t subaddrIndex() const = 0; ++ virtual uint32_t subaddrAccount() const = 0; ++ virtual std::string address() const = 0; ++ virtual std::string addressLabel() const = 0; ++ virtual std::string keyImage() const = 0; ++ virtual uint64_t unlockTime() const = 0; ++ virtual bool unlocked() const = 0; ++ virtual std::string pubKey() const = 0; ++ virtual bool coinbase() const = 0; ++ virtual std::string description() const = 0; ++}; ++ ++struct Coins ++{ ++ virtual ~Coins() = 0; ++ virtual int count() const = 0; ++ virtual CoinsInfo * coin(int index) const = 0; ++ virtual std::vector getAll() const = 0; ++ virtual void refresh() = 0; ++ virtual void setFrozen(std::string public_key) = 0; ++ virtual void setFrozen(int index) = 0; ++ virtual void thaw(std::string public_key) = 0; ++ virtual void thaw(int index) = 0; ++ virtual bool isTransferUnlocked(uint64_t unlockTime, uint64_t blockHeight) = 0; ++ virtual void setDescription(const std::string &public_key, const std::string &description) = 0; ++}; ++ + struct SubaddressRow { + public: + SubaddressRow(std::size_t _rowId, const std::string &_address, const std::string &_label): +@@ -856,7 +901,8 @@ struct Wallet + optional> amount, uint32_t mixin_count, + PendingTransaction::Priority = PendingTransaction::Priority_Low, + uint32_t subaddr_account = 0, +- std::set subaddr_indices = {}) = 0; ++ std::set subaddr_indices = {}, ++ const std::set &preferred_inputs = {}) = 0; + + /*! + * \brief createTransaction creates transaction. if dst_addr is an integrated address, payment_id is ignored +@@ -875,7 +921,8 @@ struct Wallet + optional amount, uint32_t mixin_count, + PendingTransaction::Priority = PendingTransaction::Priority_Low, + uint32_t subaddr_account = 0, +- std::set subaddr_indices = {}) = 0; ++ std::set subaddr_indices = {}, ++ const std::set &preferred_inputs = {}) = 0; + + /*! + * \brief createSweepUnmixableTransaction creates transaction with unmixable outputs. +@@ -994,6 +1041,7 @@ struct Wallet + + virtual TransactionHistory * history() = 0; + virtual AddressBook * addressBook() = 0; ++ virtual Coins * coins() = 0; + virtual Subaddress * subaddress() = 0; + virtual SubaddressAccount * subaddressAccount() = 0; + virtual void setListener(WalletListener *) = 0; +diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp +index 3e49c21..4d75656 100644 +--- a/src/wallet/wallet2.cpp ++++ b/src/wallet/wallet2.cpp +@@ -2094,12 +2094,21 @@ bool wallet2::frozen(const multisig_tx_set& txs) const + + return false; + } ++void wallet2::freeze(const crypto::public_key &pk) ++{ ++ freeze(get_transfer_details(pk)); ++} + //---------------------------------------------------------------------------------------------------- + void wallet2::freeze(const crypto::key_image &ki) + { + freeze(get_transfer_details(ki)); + } + //---------------------------------------------------------------------------------------------------- ++void wallet2::thaw(const crypto::public_key &pk) ++{ ++ thaw(get_transfer_details(pk)); ++} ++//---------------------------------------------------------------------------------------------------- + void wallet2::thaw(const crypto::key_image &ki) + { + thaw(get_transfer_details(ki)); +@@ -2110,6 +2119,18 @@ bool wallet2::frozen(const crypto::key_image &ki) const + return frozen(get_transfer_details(ki)); + } + //---------------------------------------------------------------------------------------------------- ++size_t wallet2::get_transfer_details(const crypto::public_key &pk) const ++{ ++ for (size_t idx = 0; idx < m_transfers.size(); ++idx) ++ { ++ const transfer_details &td = m_transfers[idx]; ++ if (td.get_public_key() == pk) { ++ return idx; ++ } ++ } ++ CHECK_AND_ASSERT_THROW_MES(false, "Public key not found"); ++} ++//---------------------------------------------------------------------------------------------------- + size_t wallet2::get_transfer_details(const crypto::key_image &ki) const + { + for (size_t idx = 0; idx < m_transfers.size(); ++idx) +@@ -2523,6 +2544,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; +@@ -2626,6 +2648,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; +@@ -10497,7 +10520,7 @@ void wallet2::transfer_selected_rct(std::vector wallet2::pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set &subaddr_indices) ++std::vector wallet2::pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set &subaddr_indices, const std::vector& preferred_input_list) + { + std::vector picks; + float current_output_relatdness = 1.0f; +@@ -10508,6 +10531,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]; ++ if (!is_preferred_input(preferred_input_list, td.m_key_image)) { ++ continue; ++ } + 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) +@@ -10528,6 +10554,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]; ++ if (!is_preferred_input(preferred_input_list, td.m_key_image)) { ++ continue; ++ } + 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) +@@ -10539,6 +10568,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]; ++ if (!is_preferred_input(preferred_input_list, td2.m_key_image)) { ++ continue; ++ } + 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) << "]"); +@@ -11111,7 +11143,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) + { + //ensure device is let in NONE mode in any case + hw::device &hwdev = m_account.get_device(); +@@ -11319,6 +11351,9 @@ std::vector wallet2::create_transactions_2(std::vector wallet2::create_transactions_2(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 unused_transfers_indices; + std::vector unused_dust_indices; +@@ -11918,6 +11953,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]; ++ if (!is_preferred_input(preferred_input_list, td.m_key_image)) { ++ continue; ++ } + if (m_ignore_fractional_outputs && td.amount() < fractional_threshold) + { + 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 c26349c..a48ba33 100644 +--- a/src/wallet/wallet2.h ++++ b/src/wallet/wallet2.h +@@ -1209,8 +1209,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; +@@ -1562,6 +1562,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; ++ size_t get_transfer_details(const crypto::public_key &pk) const; + + uint8_t get_current_hard_fork(); + void get_hard_fork_info(uint8_t version, uint64_t &earliest_height); +@@ -1793,7 +1794,9 @@ private: + void freeze(size_t idx); + void thaw(size_t idx); + bool frozen(size_t idx) const; ++ void freeze(const crypto::public_key &pk); + void freeze(const crypto::key_image &ki); ++ void thaw(const crypto::public_key &pk); + void thaw(const crypto::key_image &ki); + bool frozen(const crypto::key_image &ki) const; + bool frozen(const transfer_details &td) const; +@@ -1834,6 +1837,8 @@ private: + + static std::string get_default_daemon_address() { CRITICAL_REGION_LOCAL(default_daemon_address_lock); return default_daemon_address; } + ++ boost::shared_mutex m_transfers_mutex; ++ + private: + /*! + * \brief Stores wallet information to wallet file. +@@ -1905,7 +1910,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; +- std::vector pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set &subaddr_indices); ++ std::vector pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set &subaddr_indices, const std::vector& preferred_input_list); + void set_spent(size_t idx, uint64_t height); + void set_unspent(size_t idx); + bool is_spent(const transfer_details &td, bool strict = true) const; +-- +2.39.5 (Apple Git-154) + diff --git a/patches/wownero/0012-Add-hex-encoding-and-tx-key-getter-for-PendingTransc.patch b/patches/wownero/0012-Add-hex-encoding-and-tx-key-getter-for-PendingTransc.patch new file mode 100644 index 0000000..8834241 --- /dev/null +++ b/patches/wownero/0012-Add-hex-encoding-and-tx-key-getter-for-PendingTransc.patch @@ -0,0 +1,68 @@ +From 88736bd375a752998aab2f663e2c180f89f52937 Mon Sep 17 00:00:00 2001 +From: M +Date: Fri, 21 Apr 2023 15:43:47 -0400 +Subject: [PATCH 12/14] Add hex encoding and tx key getter for + PendingTransction in wallet api. + +--- + src/wallet/api/pending_transaction.cpp | 16 ++++++++++++++++ + src/wallet/api/pending_transaction.h | 2 ++ + src/wallet/api/wallet2_api.h | 2 ++ + 3 files changed, 20 insertions(+) + +diff --git a/src/wallet/api/pending_transaction.cpp b/src/wallet/api/pending_transaction.cpp +index 9c3c26e..1f714d2 100644 +--- a/src/wallet/api/pending_transaction.cpp ++++ b/src/wallet/api/pending_transaction.cpp +@@ -80,6 +80,22 @@ std::vector PendingTransactionImpl::txid() const + return txid; + } + ++std::vector PendingTransactionImpl::hex() const ++{ ++ std::vector hexs; ++ for (const auto &pt: m_pending_tx) ++ hexs.push_back(epee::string_tools::buff_to_hex_nodelimer(cryptonote::tx_to_blob(pt.tx))); ++ return hexs; ++} ++ ++std::vector PendingTransactionImpl::txKey() const ++{ ++ std::vector keys; ++ for (const auto& pt: m_pending_tx) ++ keys.push_back(epee::string_tools::pod_to_hex(pt.tx_key)); ++ return keys; ++} ++ + bool PendingTransactionImpl::commit(const std::string &filename, bool overwrite) + { + +diff --git a/src/wallet/api/pending_transaction.h b/src/wallet/api/pending_transaction.h +index 403bfe2..0cc6c58 100644 +--- a/src/wallet/api/pending_transaction.h ++++ b/src/wallet/api/pending_transaction.h +@@ -59,6 +59,8 @@ public: + std::string multisigSignData() override; + void signMultisigTx() override; + std::vector signersKeys() const override; ++ std::vector hex() const override; ++ std::vector txKey() const override; + + private: + friend class WalletImpl; +diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h +index 013b5bc..f421fdc 100644 +--- a/src/wallet/api/wallet2_api.h ++++ b/src/wallet/api/wallet2_api.h +@@ -127,6 +127,8 @@ struct PendingTransaction + * @return vector of base58-encoded signers' public keys + */ + virtual std::vector signersKeys() const = 0; ++ virtual std::vector hex() const = 0; ++ virtual std::vector txKey() const = 0; + }; + + /** +-- +2.39.5 (Apple Git-154) + diff --git a/patches/wownero/0012-Add-recoverDeterministicWalletFromSpendKey.patch b/patches/wownero/0012-Add-recoverDeterministicWalletFromSpendKey.patch deleted file mode 100644 index 37fdbaa..0000000 --- a/patches/wownero/0012-Add-recoverDeterministicWalletFromSpendKey.patch +++ /dev/null @@ -1,153 +0,0 @@ -From 42ec0a77b464682f35ea859ab1be7733126eef4d Mon Sep 17 00:00:00 2001 -From: Konstantin Ullrich -Date: Wed, 11 Oct 2023 16:47:59 +0200 -Subject: [PATCH 12/13] Add recoverDeterministicWalletFromSpendKey - -This function is used by Cake Wallet to enable polyseed (dart implementation) -support. - -Sourced from the following commit: -https://github.com/cake-tech/monero/commit/cb6fb5ab218878702ed151c0e3d5d68eb2732788 - -Co-authored-by: Godwin Asuquo ---- - src/wallet/api/wallet.cpp | 29 +++++++++++++++++++++++++++++ - src/wallet/api/wallet.h | 4 ++++ - src/wallet/api/wallet2_api.h | 19 +++++++++++++++++++ - src/wallet/api/wallet_manager.cpp | 16 ++++++++++++++++ - src/wallet/api/wallet_manager.h | 7 +++++++ - 5 files changed, 75 insertions(+) - -diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp -index ff1baac5c..685432597 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 - return status() == Status_Ok; - } - -+bool WalletImpl::recoverDeterministicWalletFromSpendKey(const std::string &path, const std::string &password, const std::string &language, const std::string &spendkey_string) -+{ -+ clearStatus(); -+ m_errorString.clear(); -+ -+ m_recoveringFromSeed = true; -+ m_recoveringFromDevice = false; -+ -+ // parse spend key -+ crypto::secret_key spendkey; -+ if (!spendkey_string.empty()) { -+ cryptonote::blobdata spendkey_data; -+ if(!epee::string_tools::parse_hexstr_to_binbuff(spendkey_string, spendkey_data) || spendkey_data.size() != sizeof(crypto::secret_key)) -+ { -+ setStatusError(tr("failed to parse secret spend key")); -+ return false; -+ } -+ spendkey = *reinterpret_cast(spendkey_data.data()); -+ } -+ -+ try { -+ m_wallet->generate(path, password, spendkey, true, false); -+ setSeedLanguage(language); -+ } catch (const std::exception &e) { -+ setStatusCritical(e.what()); -+ } -+ return status() == Status_Ok; -+} -+ - bool WalletImpl::close(bool store) - { - -diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h -index a82f270e4..9e1fbb40b 100644 ---- a/src/wallet/api/wallet.h -+++ b/src/wallet/api/wallet.h -@@ -77,6 +77,10 @@ public: - const std::string &address_string, - const std::string &viewkey_string, - const std::string &spendkey_string = ""); -+ bool recoverDeterministicWalletFromSpendKey(const std::string &path, -+ const std::string &password, -+ const std::string &language, -+ const std::string &spendkey_string); - bool recoverFromDevice(const std::string &path, - 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 f421fdc05..c8d6bb179 100644 ---- a/src/wallet/api/wallet2_api.h -+++ b/src/wallet/api/wallet2_api.h -@@ -1323,6 +1323,25 @@ struct WalletManager - return createWalletFromKeys(path, password, language, testnet ? TESTNET : MAINNET, restoreHeight, addressString, viewKeyString, spendKeyString); - } - -+ /*! -+ * \brief recover deterministic wallet from spend key. -+ * \param path Name of wallet file to be created -+ * \param password Password of wallet file -+ * \param language language -+ * \param nettype Network type -+ * \param restoreHeight restore from start height -+ * \param spendKeyString spend key -+ * \param kdf_rounds Number of rounds for key derivation function -+ * \return Wallet instance (Wallet::status() needs to be called to check if recovered successfully) -+ */ -+ virtual Wallet * createDeterministicWalletFromSpendKey(const std::string &path, -+ const std::string &password, -+ const std::string &language, -+ NetworkType nettype, -+ uint64_t restoreHeight, -+ const std::string &spendKeyString, -+ uint64_t kdf_rounds = 1) = 0; -+ - /*! - * \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 c79fe25d6..f88bd9e64 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, - return wallet; - } - -+Wallet *WalletManagerImpl::createDeterministicWalletFromSpendKey(const std::string &path, -+ const std::string &password, -+ const std::string &language, -+ NetworkType nettype, -+ uint64_t restoreHeight, -+ const std::string &spendkey_string, -+ uint64_t kdf_rounds) -+{ -+ WalletImpl * wallet = new WalletImpl(nettype, kdf_rounds); -+ if(restoreHeight > 0){ -+ wallet->setRefreshFromBlockHeight(restoreHeight); -+ } -+ wallet->recoverDeterministicWalletFromSpendKey(path, password, language, spendkey_string); -+ return wallet; -+} -+ - Wallet *WalletManagerImpl::createWalletFromDevice(const std::string &path, - 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 ---- a/src/wallet/api/wallet_manager.h -+++ b/src/wallet/api/wallet_manager.h -@@ -67,6 +67,13 @@ public: - const std::string &addressString, - const std::string &viewKeyString, - const std::string &spendKeyString = "") override; -+ virtual Wallet * createDeterministicWalletFromSpendKey(const std::string &path, -+ const std::string &password, -+ const std::string &language, -+ NetworkType nettype, -+ uint64_t restoreHeight, -+ const std::string &spendkey_string, -+ uint64_t kdf_rounds) override; - virtual Wallet * createWalletFromDevice(const std::string &path, - const std::string &password, - NetworkType nettype, --- -2.43.0 - diff --git a/patches/wownero/0013-Add-recoverDeterministicWalletFromSpendKey.patch b/patches/wownero/0013-Add-recoverDeterministicWalletFromSpendKey.patch new file mode 100644 index 0000000..8487192 --- /dev/null +++ b/patches/wownero/0013-Add-recoverDeterministicWalletFromSpendKey.patch @@ -0,0 +1,153 @@ +From 35fbb3f808e1884e496082dc5280b7e2636f0eb0 Mon Sep 17 00:00:00 2001 +From: Konstantin Ullrich +Date: Wed, 11 Oct 2023 16:47:59 +0200 +Subject: [PATCH 13/14] Add recoverDeterministicWalletFromSpendKey + +This function is used by Cake Wallet to enable polyseed (dart implementation) +support. + +Sourced from the following commit: +https://github.com/cake-tech/monero/commit/cb6fb5ab218878702ed151c0e3d5d68eb2732788 + +Co-authored-by: Godwin Asuquo +--- + src/wallet/api/wallet.cpp | 29 +++++++++++++++++++++++++++++ + src/wallet/api/wallet.h | 4 ++++ + src/wallet/api/wallet2_api.h | 19 +++++++++++++++++++ + src/wallet/api/wallet_manager.cpp | 16 ++++++++++++++++ + src/wallet/api/wallet_manager.h | 7 +++++++ + 5 files changed, 75 insertions(+) + +diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp +index ff1baac..6854325 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 + return status() == Status_Ok; + } + ++bool WalletImpl::recoverDeterministicWalletFromSpendKey(const std::string &path, const std::string &password, const std::string &language, const std::string &spendkey_string) ++{ ++ clearStatus(); ++ m_errorString.clear(); ++ ++ m_recoveringFromSeed = true; ++ m_recoveringFromDevice = false; ++ ++ // parse spend key ++ crypto::secret_key spendkey; ++ if (!spendkey_string.empty()) { ++ cryptonote::blobdata spendkey_data; ++ if(!epee::string_tools::parse_hexstr_to_binbuff(spendkey_string, spendkey_data) || spendkey_data.size() != sizeof(crypto::secret_key)) ++ { ++ setStatusError(tr("failed to parse secret spend key")); ++ return false; ++ } ++ spendkey = *reinterpret_cast(spendkey_data.data()); ++ } ++ ++ try { ++ m_wallet->generate(path, password, spendkey, true, false); ++ setSeedLanguage(language); ++ } catch (const std::exception &e) { ++ setStatusCritical(e.what()); ++ } ++ return status() == Status_Ok; ++} ++ + bool WalletImpl::close(bool store) + { + +diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h +index a82f270..9e1fbb4 100644 +--- a/src/wallet/api/wallet.h ++++ b/src/wallet/api/wallet.h +@@ -77,6 +77,10 @@ public: + const std::string &address_string, + const std::string &viewkey_string, + const std::string &spendkey_string = ""); ++ bool recoverDeterministicWalletFromSpendKey(const std::string &path, ++ const std::string &password, ++ const std::string &language, ++ const std::string &spendkey_string); + bool recoverFromDevice(const std::string &path, + const std::string &password, + const std::string &device_name); +diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h +index f421fdc..c8d6bb1 100644 +--- a/src/wallet/api/wallet2_api.h ++++ b/src/wallet/api/wallet2_api.h +@@ -1323,6 +1323,25 @@ struct WalletManager + return createWalletFromKeys(path, password, language, testnet ? TESTNET : MAINNET, restoreHeight, addressString, viewKeyString, spendKeyString); + } + ++ /*! ++ * \brief recover deterministic wallet from spend key. ++ * \param path Name of wallet file to be created ++ * \param password Password of wallet file ++ * \param language language ++ * \param nettype Network type ++ * \param restoreHeight restore from start height ++ * \param spendKeyString spend key ++ * \param kdf_rounds Number of rounds for key derivation function ++ * \return Wallet instance (Wallet::status() needs to be called to check if recovered successfully) ++ */ ++ virtual Wallet * createDeterministicWalletFromSpendKey(const std::string &path, ++ const std::string &password, ++ const std::string &language, ++ NetworkType nettype, ++ uint64_t restoreHeight, ++ const std::string &spendKeyString, ++ uint64_t kdf_rounds = 1) = 0; ++ + /*! + * \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 c79fe25..f88bd9e 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, + return wallet; + } + ++Wallet *WalletManagerImpl::createDeterministicWalletFromSpendKey(const std::string &path, ++ const std::string &password, ++ const std::string &language, ++ NetworkType nettype, ++ uint64_t restoreHeight, ++ const std::string &spendkey_string, ++ uint64_t kdf_rounds) ++{ ++ WalletImpl * wallet = new WalletImpl(nettype, kdf_rounds); ++ if(restoreHeight > 0){ ++ wallet->setRefreshFromBlockHeight(restoreHeight); ++ } ++ wallet->recoverDeterministicWalletFromSpendKey(path, password, language, spendkey_string); ++ return wallet; ++} ++ + Wallet *WalletManagerImpl::createWalletFromDevice(const std::string &path, + const std::string &password, + NetworkType nettype, +diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h +index 28fcd36..be3ff81 100644 +--- a/src/wallet/api/wallet_manager.h ++++ b/src/wallet/api/wallet_manager.h +@@ -67,6 +67,13 @@ public: + const std::string &addressString, + const std::string &viewKeyString, + const std::string &spendKeyString = "") override; ++ virtual Wallet * createDeterministicWalletFromSpendKey(const std::string &path, ++ const std::string &password, ++ const std::string &language, ++ NetworkType nettype, ++ uint64_t restoreHeight, ++ const std::string &spendkey_string, ++ uint64_t kdf_rounds) override; + virtual Wallet * createWalletFromDevice(const std::string &path, + const std::string &password, + NetworkType nettype, +-- +2.39.5 (Apple Git-154) + diff --git a/patches/wownero/0013-build-wownero-seed.patch b/patches/wownero/0013-build-wownero-seed.patch deleted file mode 100644 index c0a10b8..0000000 --- a/patches/wownero/0013-build-wownero-seed.patch +++ /dev/null @@ -1,614 +0,0 @@ -From f5cba5478e7a83cdd207881eb643f7a0f81c065b Mon Sep 17 00:00:00 2001 -From: Czarek Nakamoto -Date: Thu, 25 Apr 2024 10:06:04 +0200 -Subject: [PATCH 13/13] build wownero-seed - ---- - contrib/depends/hosts/android.mk | 4 +- - contrib/depends/hosts/darwin.mk | 1 + - contrib/depends/hosts/mingw32.mk | 3 + - contrib/depends/packages/packages.mk | 2 +- - contrib/depends/packages/wownero_seed.mk | 35 ++ - .../0001-fix-duplicate-symbol-error.patch | 497 ++++++++++++++++++ - 6 files changed, 539 insertions(+), 3 deletions(-) - create mode 100644 contrib/depends/packages/wownero_seed.mk - create mode 100644 contrib/depends/patches/wownero_seed/0001-fix-duplicate-symbol-error.patch - -diff --git a/contrib/depends/hosts/android.mk b/contrib/depends/hosts/android.mk -index 827103c36..8aad7ec65 100644 ---- a/contrib/depends/hosts/android.mk -+++ b/contrib/depends/hosts/android.mk -@@ -15,8 +15,8 @@ endif - - android_CC=$(host_toolchain)clang - android_CXX=$(host_toolchain)clang++ --android_RANLIB=llvm-ranlib --android_AR=llvm-ar -+android_RANLIB=$(host_toolchain)ranlib -+android_AR=$(host_toolchain)ar - - android_CFLAGS=-pipe - android_CXXFLAGS=$(android_CFLAGS) -diff --git a/contrib/depends/hosts/darwin.mk b/contrib/depends/hosts/darwin.mk -index b14ee5c5b..2168702aa 100644 ---- a/contrib/depends/hosts/darwin.mk -+++ b/contrib/depends/hosts/darwin.mk -@@ -9,6 +9,7 @@ darwin_CC=clang -target $(CC_target) -mmacosx-version-min=$(OSX_MIN_VERSION) --s - darwin_CXX=clang++ -target $(CC_target) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(host_prefix)/native/SDK/ -mlinker-version=$(LD64_VERSION) -stdlib=libc++ -B$(host_prefix)/native/bin/$(host)- - - darwin_RANLIB=$(host_prefix)/native/bin/$(host)-ranlib -+darwin_AR=$(host_prefix)/native/bin/$(host)-ar - - darwin_CFLAGS=-pipe - darwin_CXXFLAGS=$(darwin_CFLAGS) -diff --git a/contrib/depends/hosts/mingw32.mk b/contrib/depends/hosts/mingw32.mk -index ccc4c5082..4677694a6 100644 ---- a/contrib/depends/hosts/mingw32.mk -+++ b/contrib/depends/hosts/mingw32.mk -@@ -2,6 +2,9 @@ mingw32_CFLAGS=-pipe - mingw32_CXXFLAGS=$(mingw32_CFLAGS) - mingw32_ARFLAGS=cr - -+mingw32_RANLIB=$(shell which $(host)-ranlib) -+mingw32_AR=$(shell which $(host)-ar) -+ - mingw32_release_CFLAGS=-O2 - mingw32_release_CXXFLAGS=$(mingw32_release_CFLAGS) - -diff --git a/contrib/depends/packages/packages.mk b/contrib/depends/packages/packages.mk -index 8783d4955..3d513c5a2 100644 ---- a/contrib/depends/packages/packages.mk -+++ b/contrib/depends/packages/packages.mk -@@ -1,4 +1,4 @@ --packages:=boost openssl zeromq libiconv expat unbound polyseed -+packages:=boost openssl zeromq libiconv expat unbound polyseed wownero_seed - - # ccache is useless in gitian builds - ifneq ($(GITIAN),1) -diff --git a/contrib/depends/packages/wownero_seed.mk b/contrib/depends/packages/wownero_seed.mk -new file mode 100644 -index 000000000..b376f80c5 ---- /dev/null -+++ b/contrib/depends/packages/wownero_seed.mk -@@ -0,0 +1,35 @@ -+package=wownero_seed -+$(package)_version=0.3.0 -+$(package)_download_path=https://github.com/MrCyjaneK/wownero-seed/archive/ -+$(package)_download_file=d3f68be347facfeebbd8f68fd74982c705cb917b.tar.gz -+$(package)_file_name=$(package)-$($(package)_version).tar.gz -+$(package)_sha256_hash=3b59ccde08e0fee204680240af4b270a18a677aa0e6036a3504570193d232406 -+$(package)_patches=0001-fix-duplicate-symbol-error.patch -+ -+define $(package)_preprocess_cmds -+ patch -p1 < $($(package)_patch_dir)/0001-fix-duplicate-symbol-error.patch -+endef -+ -+ -+ifeq ($(host_os),darwin) -+ define $(package)_config_cmds -+ CC="$($(package)_cc)" CXX="$($(package)_cxx)" cmake -DCMAKE_RANLIB="$($(package)_ranlib)" -DCMAKE_AR="$($(package)_ar)" -DCMAKE_INSTALL_PREFIX="$(host_prefix)" -DCMAKE_POSITION_INDEPENDENT_CODE=ON . -+ endef -+else -+ define $(package)_config_cmds -+ CC="$($(package)_cc)" CXX="$($(package)_cxx)" cmake -DCMAKE_INSTALL_PREFIX="$(host_prefix)" -DCMAKE_POSITION_INDEPENDENT_CODE=ON . -+ endef -+endif -+ -+define $(package)_set_vars -+ $(package)_build_opts=CC="$($(package)_cc)" CXX="$($(package)_cxx)" -+endef -+ -+ -+define $(package)_build_cmds -+ CC="$($(package)_cc)" CXX="$($(package)_cxx)" $(MAKE) VERBOSE=1 -+endef -+ -+define $(package)_stage_cmds -+ CC="$($(package)_cc)" CXX="$($(package)_cxx)" $(MAKE) DESTDIR=$($(package)_staging_dir) install -+endef -diff --git a/contrib/depends/patches/wownero_seed/0001-fix-duplicate-symbol-error.patch b/contrib/depends/patches/wownero_seed/0001-fix-duplicate-symbol-error.patch -new file mode 100644 -index 000000000..a8f8fe059 ---- /dev/null -+++ b/contrib/depends/patches/wownero_seed/0001-fix-duplicate-symbol-error.patch -@@ -0,0 +1,497 @@ -+From 4be93209afb80b11834a0849391ee6eeb68aec4a Mon Sep 17 00:00:00 2001 -+From: Czarek Nakamoto -+Date: Thu, 25 Apr 2024 09:37:37 +0200 -+Subject: [PATCH] fix duplicate symbol error -+ -+--- -+ src/argon2/argon2.c | 8 +++--- -+ src/argon2/argon2.h | 4 +-- -+ src/argon2/blake2/blake2.h | 12 ++++----- -+ src/argon2/blake2/blake2b.c | 46 ++++++++++++++++---------------- -+ src/argon2/core.c | 52 ++++++++++++++++++------------------- -+ src/argon2/core.h | 17 ++++++------ -+ src/argon2/ref.c | 2 +- -+ 7 files changed, 70 insertions(+), 71 deletions(-) -+ -+diff --git a/src/argon2/argon2.c b/src/argon2/argon2.c -+index e9882b7..470dc26 100644 -+--- a/src/argon2/argon2.c -++++ b/src/argon2/argon2.c -+@@ -37,7 +37,7 @@ const char *argon2_type2string(argon2_type type, int uppercase) { -+ -+ int argon2_ctx(argon2_context *context, argon2_type type) { -+ /* 1. Validate all inputs */ -+- int result = validate_inputs(context); -++ int result = validate_inputs_wowseed(context); -+ uint32_t memory_blocks, segment_length; -+ argon2_instance_t instance; -+ -+@@ -78,20 +78,20 @@ int argon2_ctx(argon2_context *context, argon2_type type) { -+ /* 3. Initialization: Hashing inputs, allocating memory, filling first -+ * blocks -+ */ -+- result = initialize(&instance, context); -++ result = initialize_wowseed(&instance, context); -+ -+ if (ARGON2_OK != result) { -+ return result; -+ } -+ -+ /* 4. Filling memory */ -+- result = fill_memory_blocks(&instance); -++ result = _fill_memory_blocks_wowseed(&instance); -+ -+ if (ARGON2_OK != result) { -+ return result; -+ } -+ /* 5. Finalization */ -+- finalize(context, &instance); -++ finalize_wowseed(context, &instance); -+ -+ return ARGON2_OK; -+ } -+diff --git a/src/argon2/argon2.h b/src/argon2/argon2.h -+index 1b471f6..f60e269 100644 -+--- a/src/argon2/argon2.h -++++ b/src/argon2/argon2.h -+@@ -185,7 +185,7 @@ typedef void (*deallocate_fptr)(uint8_t *memory, size_t bytes_to_allocate); -+ * 4 parallel lanes. -+ * You want to erase the password, but you're OK with last pass not being -+ * erased. You want to use the default memory allocator. -+- * Then you initialize: -++ * Then you initialize_wowseed: -+ Argon2_Context(out,8,pwd,32,salt,16,NULL,0,NULL,0,5,1<<20,4,4,NULL,NULL,true,false,false,false) -+ */ -+ typedef struct Argon2_Context { -+@@ -329,7 +329,7 @@ ARGON2_PUBLIC int argon2_hash(const uint32_t t_cost, const uint32_t m_cost, -+ -+ /** -+ * Verifies a password against an encoded string -+- * Encoded string is restricted as in validate_inputs() -++ * Encoded string is restricted as in validate_inputs_wowseed() -+ * @param encoded String encoding parameters, salt, hash -+ * @param pwd Pointer to password -+ * @pre Returns ARGON2_OK if successful -+diff --git a/src/argon2/blake2/blake2.h b/src/argon2/blake2/blake2.h -+index 9f97e1c..25b445d 100644 -+--- a/src/argon2/blake2/blake2.h -++++ b/src/argon2/blake2/blake2.h -+@@ -67,15 +67,15 @@ enum { -+ }; -+ -+ /* Streaming API */ -+-ARGON2_LOCAL int blake2b_init(blake2b_state *S, size_t outlen); -+-ARGON2_LOCAL int blake2b_init_key(blake2b_state *S, size_t outlen, const void *key, -++ARGON2_LOCAL int blake2b_init_wowseed(blake2b_state *S, size_t outlen); -++ARGON2_LOCAL int blake2b_init_key_wowseed(blake2b_state *S, size_t outlen, const void *key, -+ size_t keylen); -+-ARGON2_LOCAL int blake2b_init_param(blake2b_state *S, const blake2b_param *P); -+-ARGON2_LOCAL int blake2b_update(blake2b_state *S, const void *in, size_t inlen); -+-ARGON2_LOCAL int blake2b_final(blake2b_state *S, void *out, size_t outlen); -++ARGON2_LOCAL int blake2b_init_param_wowseed(blake2b_state *S, const blake2b_param *P); -++ARGON2_LOCAL int blake2b_update_wowseed(blake2b_state *S, const void *in, size_t inlen); -++ARGON2_LOCAL int blake2b_final_wowseed(blake2b_state *S, void *out, size_t outlen); -+ -+ /* Simple API */ -+-ARGON2_LOCAL int blake2b(void *out, size_t outlen, const void *in, size_t inlen, -++ARGON2_LOCAL int blake2b_wowseed(void *out, size_t outlen, const void *in, size_t inlen, -+ const void *key, size_t keylen); -+ -+ /* Argon2 Team - Begin Code */ -+diff --git a/src/argon2/blake2/blake2b.c b/src/argon2/blake2/blake2b.c -+index ca05df5..8138b86 100644 -+--- a/src/argon2/blake2/blake2b.c -++++ b/src/argon2/blake2/blake2b.c -+@@ -70,7 +70,7 @@ static BLAKE2_INLINE void blake2b_init0(blake2b_state *S) { -+ memcpy(S->h, blake2b_IV, sizeof(S->h)); -+ } -+ -+-int blake2b_init_param(blake2b_state *S, const blake2b_param *P) { -++int blake2b_init_param_wowseed(blake2b_state *S, const blake2b_param *P) { -+ const unsigned char *p = (const unsigned char *)P; -+ unsigned int i; -+ -+@@ -88,7 +88,7 @@ int blake2b_init_param(blake2b_state *S, const blake2b_param *P) { -+ } -+ -+ /* Sequential blake2b initialization */ -+-int blake2b_init(blake2b_state *S, size_t outlen) { -++int blake2b_init_wowseed(blake2b_state *S, size_t outlen) { -+ blake2b_param P; -+ -+ if (S == NULL) { -+@@ -113,10 +113,10 @@ int blake2b_init(blake2b_state *S, size_t outlen) { -+ memset(P.salt, 0, sizeof(P.salt)); -+ memset(P.personal, 0, sizeof(P.personal)); -+ -+- return blake2b_init_param(S, &P); -++ return blake2b_init_param_wowseed(S, &P); -+ } -+ -+-int blake2b_init_key(blake2b_state *S, size_t outlen, const void *key, -++int blake2b_init_key_wowseed(blake2b_state *S, size_t outlen, const void *key, -+ size_t keylen) { -+ blake2b_param P; -+ -+@@ -147,7 +147,7 @@ int blake2b_init_key(blake2b_state *S, size_t outlen, const void *key, -+ memset(P.salt, 0, sizeof(P.salt)); -+ memset(P.personal, 0, sizeof(P.personal)); -+ -+- if (blake2b_init_param(S, &P) < 0) { -++ if (blake2b_init_param_wowseed(S, &P) < 0) { -+ blake2b_invalidate_state(S); -+ return -1; -+ } -+@@ -156,7 +156,7 @@ int blake2b_init_key(blake2b_state *S, size_t outlen, const void *key, -+ uint8_t block[BLAKE2B_BLOCKBYTES]; -+ memset(block, 0, BLAKE2B_BLOCKBYTES); -+ memcpy(block, key, keylen); -+- blake2b_update(S, block, BLAKE2B_BLOCKBYTES); -++ blake2b_update_wowseed(S, block, BLAKE2B_BLOCKBYTES); -+ /* Burn the key from stack */ -+ clear_internal_memory(block, BLAKE2B_BLOCKBYTES); -+ } -+@@ -221,7 +221,7 @@ static void blake2b_compress(blake2b_state *S, const uint8_t *block) { -+ #undef ROUND -+ } -+ -+-int blake2b_update(blake2b_state *S, const void *in, size_t inlen) { -++int blake2b_update_wowseed(blake2b_state *S, const void *in, size_t inlen) { -+ const uint8_t *pin = (const uint8_t *)in; -+ -+ if (inlen == 0) { -+@@ -261,7 +261,7 @@ int blake2b_update(blake2b_state *S, const void *in, size_t inlen) { -+ return 0; -+ } -+ -+-int blake2b_final(blake2b_state *S, void *out, size_t outlen) { -++int blake2b_final_wowseed(blake2b_state *S, void *out, size_t outlen) { -+ uint8_t buffer[BLAKE2B_OUTBYTES] = {0}; -+ unsigned int i; -+ -+@@ -291,7 +291,7 @@ int blake2b_final(blake2b_state *S, void *out, size_t outlen) { -+ return 0; -+ } -+ -+-int blake2b(void *out, size_t outlen, const void *in, size_t inlen, -++int blake2b_wowseed(void *out, size_t outlen, const void *in, size_t inlen, -+ const void *key, size_t keylen) { -+ blake2b_state S; -+ int ret = -1; -+@@ -310,19 +310,19 @@ int blake2b(void *out, size_t outlen, const void *in, size_t inlen, -+ } -+ -+ if (keylen > 0) { -+- if (blake2b_init_key(&S, outlen, key, keylen) < 0) { -++ if (blake2b_init_key_wowseed(&S, outlen, key, keylen) < 0) { -+ goto fail; -+ } -+ } else { -+- if (blake2b_init(&S, outlen) < 0) { -++ if (blake2b_init_wowseed(&S, outlen) < 0) { -+ goto fail; -+ } -+ } -+ -+- if (blake2b_update(&S, in, inlen) < 0) { -++ if (blake2b_update_wowseed(&S, in, inlen) < 0) { -+ goto fail; -+ } -+- ret = blake2b_final(&S, out, outlen); -++ ret = blake2b_final_wowseed(&S, out, outlen); -+ -+ fail: -+ clear_internal_memory(&S, sizeof(S)); -+@@ -352,25 +352,25 @@ int blake2b_long(void *pout, size_t outlen, const void *in, size_t inlen) { -+ } while ((void)0, 0) -+ -+ if (outlen <= BLAKE2B_OUTBYTES) { -+- TRY(blake2b_init(&blake_state, outlen)); -+- TRY(blake2b_update(&blake_state, outlen_bytes, sizeof(outlen_bytes))); -+- TRY(blake2b_update(&blake_state, in, inlen)); -+- TRY(blake2b_final(&blake_state, out, outlen)); -++ TRY(blake2b_init_wowseed(&blake_state, outlen)); -++ TRY(blake2b_update_wowseed(&blake_state, outlen_bytes, sizeof(outlen_bytes))); -++ TRY(blake2b_update_wowseed(&blake_state, in, inlen)); -++ TRY(blake2b_final_wowseed(&blake_state, out, outlen)); -+ } else { -+ uint32_t toproduce; -+ uint8_t out_buffer[BLAKE2B_OUTBYTES]; -+ uint8_t in_buffer[BLAKE2B_OUTBYTES]; -+- TRY(blake2b_init(&blake_state, BLAKE2B_OUTBYTES)); -+- TRY(blake2b_update(&blake_state, outlen_bytes, sizeof(outlen_bytes))); -+- TRY(blake2b_update(&blake_state, in, inlen)); -+- TRY(blake2b_final(&blake_state, out_buffer, BLAKE2B_OUTBYTES)); -++ TRY(blake2b_init_wowseed(&blake_state, BLAKE2B_OUTBYTES)); -++ TRY(blake2b_update_wowseed(&blake_state, outlen_bytes, sizeof(outlen_bytes))); -++ TRY(blake2b_update_wowseed(&blake_state, in, inlen)); -++ TRY(blake2b_final_wowseed(&blake_state, out_buffer, BLAKE2B_OUTBYTES)); -+ memcpy(out, out_buffer, BLAKE2B_OUTBYTES / 2); -+ out += BLAKE2B_OUTBYTES / 2; -+ toproduce = (uint32_t)outlen - BLAKE2B_OUTBYTES / 2; -+ -+ while (toproduce > BLAKE2B_OUTBYTES) { -+ memcpy(in_buffer, out_buffer, BLAKE2B_OUTBYTES); -+- TRY(blake2b(out_buffer, BLAKE2B_OUTBYTES, in_buffer, -++ TRY(blake2b_wowseed(out_buffer, BLAKE2B_OUTBYTES, in_buffer, -+ BLAKE2B_OUTBYTES, NULL, 0)); -+ memcpy(out, out_buffer, BLAKE2B_OUTBYTES / 2); -+ out += BLAKE2B_OUTBYTES / 2; -+@@ -378,7 +378,7 @@ int blake2b_long(void *pout, size_t outlen, const void *in, size_t inlen) { -+ } -+ -+ memcpy(in_buffer, out_buffer, BLAKE2B_OUTBYTES); -+- TRY(blake2b(out_buffer, toproduce, in_buffer, BLAKE2B_OUTBYTES, NULL, -++ TRY(blake2b_wowseed(out_buffer, toproduce, in_buffer, BLAKE2B_OUTBYTES, NULL, -+ 0)); -+ memcpy(out, out_buffer, toproduce); -+ } -+diff --git a/src/argon2/core.c b/src/argon2/core.c -+index 5eafe08..c25eb53 100644 -+--- a/src/argon2/core.c -++++ b/src/argon2/core.c -+@@ -151,7 +151,7 @@ void clear_internal_memory(void *v, size_t n) { -+ } -+ } -+ -+-void finalize(const argon2_context *context, argon2_instance_t *instance) { -++void finalize_wowseed(const argon2_context *context, argon2_instance_t *instance) { -+ if (context != NULL && instance != NULL) { -+ block blockhash; -+ uint32_t l; -+@@ -256,7 +256,7 @@ uint32_t index_alpha(const argon2_instance_t *instance, -+ } -+ -+ /* Single-threaded version for p=1 case */ -+-static int fill_memory_blocks_st(argon2_instance_t *instance) { -++static int _fill_memory_blocks_wowseed_st(argon2_instance_t *instance) { -+ uint32_t r, s, l; -+ -+ for (r = 0; r < instance->passes; ++r) { -+@@ -273,14 +273,14 @@ static int fill_memory_blocks_st(argon2_instance_t *instance) { -+ return ARGON2_OK; -+ } -+ -+-int fill_memory_blocks(argon2_instance_t *instance) { -++int _fill_memory_blocks_wowseed(argon2_instance_t *instance) { -+ if (instance == NULL || instance->lanes == 0) { -+ return ARGON2_INCORRECT_PARAMETER; -+ } -+- return fill_memory_blocks_st(instance); -++ return _fill_memory_blocks_wowseed_st(instance); -+ } -+ -+-int validate_inputs(const argon2_context *context) { -++int validate_inputs_wowseed(const argon2_context *context) { -+ if (NULL == context) { -+ return ARGON2_INCORRECT_PARAMETER; -+ } -+@@ -407,7 +407,7 @@ int validate_inputs(const argon2_context *context) { -+ return ARGON2_OK; -+ } -+ -+-void fill_first_blocks(uint8_t *blockhash, const argon2_instance_t *instance) { -++void fill_first_blocks_wowseed(uint8_t *blockhash, const argon2_instance_t *instance) { -+ uint32_t l; -+ /* Make the first and second block in each lane as G(H0||0||i) or -+ G(H0||1||i) */ -+@@ -430,7 +430,7 @@ void fill_first_blocks(uint8_t *blockhash, const argon2_instance_t *instance) { -+ clear_internal_memory(blockhash_bytes, ARGON2_BLOCK_SIZE); -+ } -+ -+-void initial_hash(uint8_t *blockhash, argon2_context *context, -++void initial_hash_wowseed(uint8_t *blockhash, argon2_context *context, -+ argon2_type type) { -+ blake2b_state BlakeHash; -+ uint8_t value[sizeof(uint32_t)]; -+@@ -439,31 +439,31 @@ void initial_hash(uint8_t *blockhash, argon2_context *context, -+ return; -+ } -+ -+- blake2b_init(&BlakeHash, ARGON2_PREHASH_DIGEST_LENGTH); -++ blake2b_init_wowseed(&BlakeHash, ARGON2_PREHASH_DIGEST_LENGTH); -+ -+ store32(&value, context->lanes); -+- blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); -++ blake2b_update_wowseed(&BlakeHash, (const uint8_t *)&value, sizeof(value)); -+ -+ store32(&value, context->outlen); -+- blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); -++ blake2b_update_wowseed(&BlakeHash, (const uint8_t *)&value, sizeof(value)); -+ -+ store32(&value, context->m_cost); -+- blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); -++ blake2b_update_wowseed(&BlakeHash, (const uint8_t *)&value, sizeof(value)); -+ -+ store32(&value, context->t_cost); -+- blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); -++ blake2b_update_wowseed(&BlakeHash, (const uint8_t *)&value, sizeof(value)); -+ -+ store32(&value, context->version); -+- blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); -++ blake2b_update_wowseed(&BlakeHash, (const uint8_t *)&value, sizeof(value)); -+ -+ store32(&value, (uint32_t)type); -+- blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); -++ blake2b_update_wowseed(&BlakeHash, (const uint8_t *)&value, sizeof(value)); -+ -+ store32(&value, context->pwdlen); -+- blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); -++ blake2b_update_wowseed(&BlakeHash, (const uint8_t *)&value, sizeof(value)); -+ -+ if (context->pwd != NULL) { -+- blake2b_update(&BlakeHash, (const uint8_t *)context->pwd, -++ blake2b_update_wowseed(&BlakeHash, (const uint8_t *)context->pwd, -+ context->pwdlen); -+ -+ if (context->flags & ARGON2_FLAG_CLEAR_PASSWORD) { -+@@ -473,18 +473,18 @@ void initial_hash(uint8_t *blockhash, argon2_context *context, -+ } -+ -+ store32(&value, context->saltlen); -+- blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); -++ blake2b_update_wowseed(&BlakeHash, (const uint8_t *)&value, sizeof(value)); -+ -+ if (context->salt != NULL) { -+- blake2b_update(&BlakeHash, (const uint8_t *)context->salt, -++ blake2b_update_wowseed(&BlakeHash, (const uint8_t *)context->salt, -+ context->saltlen); -+ } -+ -+ store32(&value, context->secretlen); -+- blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); -++ blake2b_update_wowseed(&BlakeHash, (const uint8_t *)&value, sizeof(value)); -+ -+ if (context->secret != NULL) { -+- blake2b_update(&BlakeHash, (const uint8_t *)context->secret, -++ blake2b_update_wowseed(&BlakeHash, (const uint8_t *)context->secret, -+ context->secretlen); -+ -+ if (context->flags & ARGON2_FLAG_CLEAR_SECRET) { -+@@ -494,17 +494,17 @@ void initial_hash(uint8_t *blockhash, argon2_context *context, -+ } -+ -+ store32(&value, context->adlen); -+- blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); -++ blake2b_update_wowseed(&BlakeHash, (const uint8_t *)&value, sizeof(value)); -+ -+ if (context->ad != NULL) { -+- blake2b_update(&BlakeHash, (const uint8_t *)context->ad, -++ blake2b_update_wowseed(&BlakeHash, (const uint8_t *)context->ad, -+ context->adlen); -+ } -+ -+- blake2b_final(&BlakeHash, blockhash, ARGON2_PREHASH_DIGEST_LENGTH); -++ blake2b_final_wowseed(&BlakeHash, blockhash, ARGON2_PREHASH_DIGEST_LENGTH); -+ } -+ -+-int initialize(argon2_instance_t *instance, argon2_context *context) { -++int initialize_wowseed(argon2_instance_t *instance, argon2_context *context) { -+ uint8_t blockhash[ARGON2_PREHASH_SEED_LENGTH]; -+ int result = ARGON2_OK; -+ -+@@ -523,7 +523,7 @@ int initialize(argon2_instance_t *instance, argon2_context *context) { -+ /* H_0 + 8 extra bytes to produce the first blocks */ -+ /* uint8_t blockhash[ARGON2_PREHASH_SEED_LENGTH]; */ -+ /* Hashing all inputs */ -+- initial_hash(blockhash, context, instance->type); -++ initial_hash_wowseed(blockhash, context, instance->type); -+ /* Zeroing 8 extra bytes */ -+ clear_internal_memory(blockhash + ARGON2_PREHASH_DIGEST_LENGTH, -+ ARGON2_PREHASH_SEED_LENGTH - -+@@ -535,7 +535,7 @@ int initialize(argon2_instance_t *instance, argon2_context *context) { -+ -+ /* 3. Creating first blocks, we always have at least two blocks in a slice -+ */ -+- fill_first_blocks(blockhash, instance); -++ fill_first_blocks_wowseed(blockhash, instance); -+ /* Clearing the hash */ -+ clear_internal_memory(blockhash, ARGON2_PREHASH_SEED_LENGTH); -+ -+diff --git a/src/argon2/core.h b/src/argon2/core.h -+index 78000ba..6b0154c 100644 -+--- a/src/argon2/core.h -++++ b/src/argon2/core.h -+@@ -53,7 +53,7 @@ typedef struct block_ { uint64_t v[ARGON2_QWORDS_IN_BLOCK]; } block; -+ -+ /*****************Functions that work with the block******************/ -+ -+-/* Initialize each byte of the block with @in */ -++/* initialize_wowseed each byte of the block with @in */ -+ void init_block_value(block *b, uint8_t in); -+ -+ /* Copy block @src to block @dst */ -+@@ -158,7 +158,7 @@ uint32_t index_alpha(const argon2_instance_t *instance, -+ * @return ARGON2_OK if everything is all right, otherwise one of error codes -+ * (all defined in -+ */ -+-int validate_inputs(const argon2_context *context); -++int validate_inputs_wowseed(const argon2_context *context); -+ -+ /* -+ * Hashes all the inputs into @a blockhash[PREHASH_DIGEST_LENGTH], clears -+@@ -170,28 +170,27 @@ int validate_inputs(const argon2_context *context); -+ * @pre @a blockhash must have at least @a PREHASH_DIGEST_LENGTH bytes -+ * allocated -+ */ -+-void initial_hash(uint8_t *blockhash, argon2_context *context, -++void initial_hash_wowseed(uint8_t *blockhash, argon2_context *context, -+ argon2_type type); -+- -+ /* -+ * Function creates first 2 blocks per lane -+ * @param instance Pointer to the current instance -+ * @param blockhash Pointer to the pre-hashing digest -+ * @pre blockhash must point to @a PREHASH_SEED_LENGTH allocated values -+ */ -+-void fill_first_blocks(uint8_t *blockhash, const argon2_instance_t *instance); -++void fill_first_blocks_wowseed(uint8_t *blockhash, const argon2_instance_t *instance); -+ -+ /* -+ * Function allocates memory, hashes the inputs with Blake, and creates first -+ * two blocks. Returns the pointer to the main memory with 2 blocks per lane -+- * initialized -++ * initialize_wowseedd -+ * @param context Pointer to the Argon2 internal structure containing memory -+ * pointer, and parameters for time and space requirements. -+ * @param instance Current Argon2 instance -+ * @return Zero if successful, -1 if memory failed to allocate. @context->state -+ * will be modified if successful. -+ */ -+-int initialize(argon2_instance_t *instance, argon2_context *context); -++int initialize_wowseed(argon2_instance_t *instance, argon2_context *context); -+ -+ /* -+ * XORing the last block of each lane, hashing it, making the tag. Deallocates -+@@ -204,7 +203,7 @@ int initialize(argon2_instance_t *instance, argon2_context *context); -+ * @pre if context->free_cbk is not NULL, it should point to a function that -+ * deallocates memory -+ */ -+-void finalize(const argon2_context *context, argon2_instance_t *instance); -++void finalize_wowseed(const argon2_context *context, argon2_instance_t *instance); -+ -+ /* -+ * Function that fills the segment using previous segments also from other -+@@ -223,6 +222,6 @@ void fill_segment(const argon2_instance_t *instance, -+ * @param instance Pointer to the current instance -+ * @return ARGON2_OK if successful, @context->state -+ */ -+-int fill_memory_blocks(argon2_instance_t *instance); -++int _fill_memory_blocks_wowseed(argon2_instance_t *instance); -+ -+ #endif -+diff --git a/src/argon2/ref.c b/src/argon2/ref.c -+index ad1cf46..7edbd6e 100644 -+--- a/src/argon2/ref.c -++++ b/src/argon2/ref.c -+@@ -29,7 +29,7 @@ -+ -+ /* -+ * Function fills a new memory block and optionally XORs the old block over the new one. -+- * @next_block must be initialized. -++ * @next_block must be initialize_wowseedd. -+ * @param prev_block Pointer to the previous block -+ * @param ref_block Pointer to the reference block -+ * @param next_block Pointer to the block to be constructed -+-- -+2.39.2 --- -2.43.0 - diff --git a/patches/wownero/0014-build-wownero-seed.patch b/patches/wownero/0014-build-wownero-seed.patch new file mode 100644 index 0000000..649127e --- /dev/null +++ b/patches/wownero/0014-build-wownero-seed.patch @@ -0,0 +1,614 @@ +From 942d3f9053779f7673e327d1d22faa3f52a936a6 Mon Sep 17 00:00:00 2001 +From: Czarek Nakamoto +Date: Thu, 25 Apr 2024 10:06:04 +0200 +Subject: [PATCH 14/14] build wownero-seed + +--- + contrib/depends/hosts/android.mk | 4 +- + contrib/depends/hosts/darwin.mk | 1 + + contrib/depends/hosts/mingw32.mk | 3 + + contrib/depends/packages/packages.mk | 2 +- + contrib/depends/packages/wownero_seed.mk | 35 ++ + .../0001-fix-duplicate-symbol-error.patch | 497 ++++++++++++++++++ + 6 files changed, 539 insertions(+), 3 deletions(-) + create mode 100644 contrib/depends/packages/wownero_seed.mk + create mode 100644 contrib/depends/patches/wownero_seed/0001-fix-duplicate-symbol-error.patch + +diff --git a/contrib/depends/hosts/android.mk b/contrib/depends/hosts/android.mk +index 827103c..8aad7ec 100644 +--- a/contrib/depends/hosts/android.mk ++++ b/contrib/depends/hosts/android.mk +@@ -15,8 +15,8 @@ endif + + android_CC=$(host_toolchain)clang + android_CXX=$(host_toolchain)clang++ +-android_RANLIB=llvm-ranlib +-android_AR=llvm-ar ++android_RANLIB=$(host_toolchain)ranlib ++android_AR=$(host_toolchain)ar + + android_CFLAGS=-pipe + android_CXXFLAGS=$(android_CFLAGS) +diff --git a/contrib/depends/hosts/darwin.mk b/contrib/depends/hosts/darwin.mk +index b14ee5c..2168702 100644 +--- a/contrib/depends/hosts/darwin.mk ++++ b/contrib/depends/hosts/darwin.mk +@@ -9,6 +9,7 @@ darwin_CC=clang -target $(CC_target) -mmacosx-version-min=$(OSX_MIN_VERSION) --s + darwin_CXX=clang++ -target $(CC_target) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(host_prefix)/native/SDK/ -mlinker-version=$(LD64_VERSION) -stdlib=libc++ -B$(host_prefix)/native/bin/$(host)- + + darwin_RANLIB=$(host_prefix)/native/bin/$(host)-ranlib ++darwin_AR=$(host_prefix)/native/bin/$(host)-ar + + darwin_CFLAGS=-pipe + darwin_CXXFLAGS=$(darwin_CFLAGS) +diff --git a/contrib/depends/hosts/mingw32.mk b/contrib/depends/hosts/mingw32.mk +index ccc4c50..4677694 100644 +--- a/contrib/depends/hosts/mingw32.mk ++++ b/contrib/depends/hosts/mingw32.mk +@@ -2,6 +2,9 @@ mingw32_CFLAGS=-pipe + mingw32_CXXFLAGS=$(mingw32_CFLAGS) + mingw32_ARFLAGS=cr + ++mingw32_RANLIB=$(shell which $(host)-ranlib) ++mingw32_AR=$(shell which $(host)-ar) ++ + mingw32_release_CFLAGS=-O2 + mingw32_release_CXXFLAGS=$(mingw32_release_CFLAGS) + +diff --git a/contrib/depends/packages/packages.mk b/contrib/depends/packages/packages.mk +index 8783d49..3d513c5 100644 +--- a/contrib/depends/packages/packages.mk ++++ b/contrib/depends/packages/packages.mk +@@ -1,4 +1,4 @@ +-packages:=boost openssl zeromq libiconv expat unbound polyseed ++packages:=boost openssl zeromq libiconv expat unbound polyseed wownero_seed + + # ccache is useless in gitian builds + ifneq ($(GITIAN),1) +diff --git a/contrib/depends/packages/wownero_seed.mk b/contrib/depends/packages/wownero_seed.mk +new file mode 100644 +index 0000000..b376f80 +--- /dev/null ++++ b/contrib/depends/packages/wownero_seed.mk +@@ -0,0 +1,35 @@ ++package=wownero_seed ++$(package)_version=0.3.0 ++$(package)_download_path=https://github.com/MrCyjaneK/wownero-seed/archive/ ++$(package)_download_file=d3f68be347facfeebbd8f68fd74982c705cb917b.tar.gz ++$(package)_file_name=$(package)-$($(package)_version).tar.gz ++$(package)_sha256_hash=3b59ccde08e0fee204680240af4b270a18a677aa0e6036a3504570193d232406 ++$(package)_patches=0001-fix-duplicate-symbol-error.patch ++ ++define $(package)_preprocess_cmds ++ patch -p1 < $($(package)_patch_dir)/0001-fix-duplicate-symbol-error.patch ++endef ++ ++ ++ifeq ($(host_os),darwin) ++ define $(package)_config_cmds ++ CC="$($(package)_cc)" CXX="$($(package)_cxx)" cmake -DCMAKE_RANLIB="$($(package)_ranlib)" -DCMAKE_AR="$($(package)_ar)" -DCMAKE_INSTALL_PREFIX="$(host_prefix)" -DCMAKE_POSITION_INDEPENDENT_CODE=ON . ++ endef ++else ++ define $(package)_config_cmds ++ CC="$($(package)_cc)" CXX="$($(package)_cxx)" cmake -DCMAKE_INSTALL_PREFIX="$(host_prefix)" -DCMAKE_POSITION_INDEPENDENT_CODE=ON . ++ endef ++endif ++ ++define $(package)_set_vars ++ $(package)_build_opts=CC="$($(package)_cc)" CXX="$($(package)_cxx)" ++endef ++ ++ ++define $(package)_build_cmds ++ CC="$($(package)_cc)" CXX="$($(package)_cxx)" $(MAKE) VERBOSE=1 ++endef ++ ++define $(package)_stage_cmds ++ CC="$($(package)_cc)" CXX="$($(package)_cxx)" $(MAKE) DESTDIR=$($(package)_staging_dir) install ++endef +diff --git a/contrib/depends/patches/wownero_seed/0001-fix-duplicate-symbol-error.patch b/contrib/depends/patches/wownero_seed/0001-fix-duplicate-symbol-error.patch +new file mode 100644 +index 0000000..a8f8fe0 +--- /dev/null ++++ b/contrib/depends/patches/wownero_seed/0001-fix-duplicate-symbol-error.patch +@@ -0,0 +1,497 @@ ++From 4be93209afb80b11834a0849391ee6eeb68aec4a Mon Sep 17 00:00:00 2001 ++From: Czarek Nakamoto ++Date: Thu, 25 Apr 2024 09:37:37 +0200 ++Subject: [PATCH] fix duplicate symbol error ++ ++--- ++ src/argon2/argon2.c | 8 +++--- ++ src/argon2/argon2.h | 4 +-- ++ src/argon2/blake2/blake2.h | 12 ++++----- ++ src/argon2/blake2/blake2b.c | 46 ++++++++++++++++---------------- ++ src/argon2/core.c | 52 ++++++++++++++++++------------------- ++ src/argon2/core.h | 17 ++++++------ ++ src/argon2/ref.c | 2 +- ++ 7 files changed, 70 insertions(+), 71 deletions(-) ++ ++diff --git a/src/argon2/argon2.c b/src/argon2/argon2.c ++index e9882b7..470dc26 100644 ++--- a/src/argon2/argon2.c +++++ b/src/argon2/argon2.c ++@@ -37,7 +37,7 @@ const char *argon2_type2string(argon2_type type, int uppercase) { ++ ++ int argon2_ctx(argon2_context *context, argon2_type type) { ++ /* 1. Validate all inputs */ ++- int result = validate_inputs(context); +++ int result = validate_inputs_wowseed(context); ++ uint32_t memory_blocks, segment_length; ++ argon2_instance_t instance; ++ ++@@ -78,20 +78,20 @@ int argon2_ctx(argon2_context *context, argon2_type type) { ++ /* 3. Initialization: Hashing inputs, allocating memory, filling first ++ * blocks ++ */ ++- result = initialize(&instance, context); +++ result = initialize_wowseed(&instance, context); ++ ++ if (ARGON2_OK != result) { ++ return result; ++ } ++ ++ /* 4. Filling memory */ ++- result = fill_memory_blocks(&instance); +++ result = _fill_memory_blocks_wowseed(&instance); ++ ++ if (ARGON2_OK != result) { ++ return result; ++ } ++ /* 5. Finalization */ ++- finalize(context, &instance); +++ finalize_wowseed(context, &instance); ++ ++ return ARGON2_OK; ++ } ++diff --git a/src/argon2/argon2.h b/src/argon2/argon2.h ++index 1b471f6..f60e269 100644 ++--- a/src/argon2/argon2.h +++++ b/src/argon2/argon2.h ++@@ -185,7 +185,7 @@ typedef void (*deallocate_fptr)(uint8_t *memory, size_t bytes_to_allocate); ++ * 4 parallel lanes. ++ * You want to erase the password, but you're OK with last pass not being ++ * erased. You want to use the default memory allocator. ++- * Then you initialize: +++ * Then you initialize_wowseed: ++ Argon2_Context(out,8,pwd,32,salt,16,NULL,0,NULL,0,5,1<<20,4,4,NULL,NULL,true,false,false,false) ++ */ ++ typedef struct Argon2_Context { ++@@ -329,7 +329,7 @@ ARGON2_PUBLIC int argon2_hash(const uint32_t t_cost, const uint32_t m_cost, ++ ++ /** ++ * Verifies a password against an encoded string ++- * Encoded string is restricted as in validate_inputs() +++ * Encoded string is restricted as in validate_inputs_wowseed() ++ * @param encoded String encoding parameters, salt, hash ++ * @param pwd Pointer to password ++ * @pre Returns ARGON2_OK if successful ++diff --git a/src/argon2/blake2/blake2.h b/src/argon2/blake2/blake2.h ++index 9f97e1c..25b445d 100644 ++--- a/src/argon2/blake2/blake2.h +++++ b/src/argon2/blake2/blake2.h ++@@ -67,15 +67,15 @@ enum { ++ }; ++ ++ /* Streaming API */ ++-ARGON2_LOCAL int blake2b_init(blake2b_state *S, size_t outlen); ++-ARGON2_LOCAL int blake2b_init_key(blake2b_state *S, size_t outlen, const void *key, +++ARGON2_LOCAL int blake2b_init_wowseed(blake2b_state *S, size_t outlen); +++ARGON2_LOCAL int blake2b_init_key_wowseed(blake2b_state *S, size_t outlen, const void *key, ++ size_t keylen); ++-ARGON2_LOCAL int blake2b_init_param(blake2b_state *S, const blake2b_param *P); ++-ARGON2_LOCAL int blake2b_update(blake2b_state *S, const void *in, size_t inlen); ++-ARGON2_LOCAL int blake2b_final(blake2b_state *S, void *out, size_t outlen); +++ARGON2_LOCAL int blake2b_init_param_wowseed(blake2b_state *S, const blake2b_param *P); +++ARGON2_LOCAL int blake2b_update_wowseed(blake2b_state *S, const void *in, size_t inlen); +++ARGON2_LOCAL int blake2b_final_wowseed(blake2b_state *S, void *out, size_t outlen); ++ ++ /* Simple API */ ++-ARGON2_LOCAL int blake2b(void *out, size_t outlen, const void *in, size_t inlen, +++ARGON2_LOCAL int blake2b_wowseed(void *out, size_t outlen, const void *in, size_t inlen, ++ const void *key, size_t keylen); ++ ++ /* Argon2 Team - Begin Code */ ++diff --git a/src/argon2/blake2/blake2b.c b/src/argon2/blake2/blake2b.c ++index ca05df5..8138b86 100644 ++--- a/src/argon2/blake2/blake2b.c +++++ b/src/argon2/blake2/blake2b.c ++@@ -70,7 +70,7 @@ static BLAKE2_INLINE void blake2b_init0(blake2b_state *S) { ++ memcpy(S->h, blake2b_IV, sizeof(S->h)); ++ } ++ ++-int blake2b_init_param(blake2b_state *S, const blake2b_param *P) { +++int blake2b_init_param_wowseed(blake2b_state *S, const blake2b_param *P) { ++ const unsigned char *p = (const unsigned char *)P; ++ unsigned int i; ++ ++@@ -88,7 +88,7 @@ int blake2b_init_param(blake2b_state *S, const blake2b_param *P) { ++ } ++ ++ /* Sequential blake2b initialization */ ++-int blake2b_init(blake2b_state *S, size_t outlen) { +++int blake2b_init_wowseed(blake2b_state *S, size_t outlen) { ++ blake2b_param P; ++ ++ if (S == NULL) { ++@@ -113,10 +113,10 @@ int blake2b_init(blake2b_state *S, size_t outlen) { ++ memset(P.salt, 0, sizeof(P.salt)); ++ memset(P.personal, 0, sizeof(P.personal)); ++ ++- return blake2b_init_param(S, &P); +++ return blake2b_init_param_wowseed(S, &P); ++ } ++ ++-int blake2b_init_key(blake2b_state *S, size_t outlen, const void *key, +++int blake2b_init_key_wowseed(blake2b_state *S, size_t outlen, const void *key, ++ size_t keylen) { ++ blake2b_param P; ++ ++@@ -147,7 +147,7 @@ int blake2b_init_key(blake2b_state *S, size_t outlen, const void *key, ++ memset(P.salt, 0, sizeof(P.salt)); ++ memset(P.personal, 0, sizeof(P.personal)); ++ ++- if (blake2b_init_param(S, &P) < 0) { +++ if (blake2b_init_param_wowseed(S, &P) < 0) { ++ blake2b_invalidate_state(S); ++ return -1; ++ } ++@@ -156,7 +156,7 @@ int blake2b_init_key(blake2b_state *S, size_t outlen, const void *key, ++ uint8_t block[BLAKE2B_BLOCKBYTES]; ++ memset(block, 0, BLAKE2B_BLOCKBYTES); ++ memcpy(block, key, keylen); ++- blake2b_update(S, block, BLAKE2B_BLOCKBYTES); +++ blake2b_update_wowseed(S, block, BLAKE2B_BLOCKBYTES); ++ /* Burn the key from stack */ ++ clear_internal_memory(block, BLAKE2B_BLOCKBYTES); ++ } ++@@ -221,7 +221,7 @@ static void blake2b_compress(blake2b_state *S, const uint8_t *block) { ++ #undef ROUND ++ } ++ ++-int blake2b_update(blake2b_state *S, const void *in, size_t inlen) { +++int blake2b_update_wowseed(blake2b_state *S, const void *in, size_t inlen) { ++ const uint8_t *pin = (const uint8_t *)in; ++ ++ if (inlen == 0) { ++@@ -261,7 +261,7 @@ int blake2b_update(blake2b_state *S, const void *in, size_t inlen) { ++ return 0; ++ } ++ ++-int blake2b_final(blake2b_state *S, void *out, size_t outlen) { +++int blake2b_final_wowseed(blake2b_state *S, void *out, size_t outlen) { ++ uint8_t buffer[BLAKE2B_OUTBYTES] = {0}; ++ unsigned int i; ++ ++@@ -291,7 +291,7 @@ int blake2b_final(blake2b_state *S, void *out, size_t outlen) { ++ return 0; ++ } ++ ++-int blake2b(void *out, size_t outlen, const void *in, size_t inlen, +++int blake2b_wowseed(void *out, size_t outlen, const void *in, size_t inlen, ++ const void *key, size_t keylen) { ++ blake2b_state S; ++ int ret = -1; ++@@ -310,19 +310,19 @@ int blake2b(void *out, size_t outlen, const void *in, size_t inlen, ++ } ++ ++ if (keylen > 0) { ++- if (blake2b_init_key(&S, outlen, key, keylen) < 0) { +++ if (blake2b_init_key_wowseed(&S, outlen, key, keylen) < 0) { ++ goto fail; ++ } ++ } else { ++- if (blake2b_init(&S, outlen) < 0) { +++ if (blake2b_init_wowseed(&S, outlen) < 0) { ++ goto fail; ++ } ++ } ++ ++- if (blake2b_update(&S, in, inlen) < 0) { +++ if (blake2b_update_wowseed(&S, in, inlen) < 0) { ++ goto fail; ++ } ++- ret = blake2b_final(&S, out, outlen); +++ ret = blake2b_final_wowseed(&S, out, outlen); ++ ++ fail: ++ clear_internal_memory(&S, sizeof(S)); ++@@ -352,25 +352,25 @@ int blake2b_long(void *pout, size_t outlen, const void *in, size_t inlen) { ++ } while ((void)0, 0) ++ ++ if (outlen <= BLAKE2B_OUTBYTES) { ++- TRY(blake2b_init(&blake_state, outlen)); ++- TRY(blake2b_update(&blake_state, outlen_bytes, sizeof(outlen_bytes))); ++- TRY(blake2b_update(&blake_state, in, inlen)); ++- TRY(blake2b_final(&blake_state, out, outlen)); +++ TRY(blake2b_init_wowseed(&blake_state, outlen)); +++ TRY(blake2b_update_wowseed(&blake_state, outlen_bytes, sizeof(outlen_bytes))); +++ TRY(blake2b_update_wowseed(&blake_state, in, inlen)); +++ TRY(blake2b_final_wowseed(&blake_state, out, outlen)); ++ } else { ++ uint32_t toproduce; ++ uint8_t out_buffer[BLAKE2B_OUTBYTES]; ++ uint8_t in_buffer[BLAKE2B_OUTBYTES]; ++- TRY(blake2b_init(&blake_state, BLAKE2B_OUTBYTES)); ++- TRY(blake2b_update(&blake_state, outlen_bytes, sizeof(outlen_bytes))); ++- TRY(blake2b_update(&blake_state, in, inlen)); ++- TRY(blake2b_final(&blake_state, out_buffer, BLAKE2B_OUTBYTES)); +++ TRY(blake2b_init_wowseed(&blake_state, BLAKE2B_OUTBYTES)); +++ TRY(blake2b_update_wowseed(&blake_state, outlen_bytes, sizeof(outlen_bytes))); +++ TRY(blake2b_update_wowseed(&blake_state, in, inlen)); +++ TRY(blake2b_final_wowseed(&blake_state, out_buffer, BLAKE2B_OUTBYTES)); ++ memcpy(out, out_buffer, BLAKE2B_OUTBYTES / 2); ++ out += BLAKE2B_OUTBYTES / 2; ++ toproduce = (uint32_t)outlen - BLAKE2B_OUTBYTES / 2; ++ ++ while (toproduce > BLAKE2B_OUTBYTES) { ++ memcpy(in_buffer, out_buffer, BLAKE2B_OUTBYTES); ++- TRY(blake2b(out_buffer, BLAKE2B_OUTBYTES, in_buffer, +++ TRY(blake2b_wowseed(out_buffer, BLAKE2B_OUTBYTES, in_buffer, ++ BLAKE2B_OUTBYTES, NULL, 0)); ++ memcpy(out, out_buffer, BLAKE2B_OUTBYTES / 2); ++ out += BLAKE2B_OUTBYTES / 2; ++@@ -378,7 +378,7 @@ int blake2b_long(void *pout, size_t outlen, const void *in, size_t inlen) { ++ } ++ ++ memcpy(in_buffer, out_buffer, BLAKE2B_OUTBYTES); ++- TRY(blake2b(out_buffer, toproduce, in_buffer, BLAKE2B_OUTBYTES, NULL, +++ TRY(blake2b_wowseed(out_buffer, toproduce, in_buffer, BLAKE2B_OUTBYTES, NULL, ++ 0)); ++ memcpy(out, out_buffer, toproduce); ++ } ++diff --git a/src/argon2/core.c b/src/argon2/core.c ++index 5eafe08..c25eb53 100644 ++--- a/src/argon2/core.c +++++ b/src/argon2/core.c ++@@ -151,7 +151,7 @@ void clear_internal_memory(void *v, size_t n) { ++ } ++ } ++ ++-void finalize(const argon2_context *context, argon2_instance_t *instance) { +++void finalize_wowseed(const argon2_context *context, argon2_instance_t *instance) { ++ if (context != NULL && instance != NULL) { ++ block blockhash; ++ uint32_t l; ++@@ -256,7 +256,7 @@ uint32_t index_alpha(const argon2_instance_t *instance, ++ } ++ ++ /* Single-threaded version for p=1 case */ ++-static int fill_memory_blocks_st(argon2_instance_t *instance) { +++static int _fill_memory_blocks_wowseed_st(argon2_instance_t *instance) { ++ uint32_t r, s, l; ++ ++ for (r = 0; r < instance->passes; ++r) { ++@@ -273,14 +273,14 @@ static int fill_memory_blocks_st(argon2_instance_t *instance) { ++ return ARGON2_OK; ++ } ++ ++-int fill_memory_blocks(argon2_instance_t *instance) { +++int _fill_memory_blocks_wowseed(argon2_instance_t *instance) { ++ if (instance == NULL || instance->lanes == 0) { ++ return ARGON2_INCORRECT_PARAMETER; ++ } ++- return fill_memory_blocks_st(instance); +++ return _fill_memory_blocks_wowseed_st(instance); ++ } ++ ++-int validate_inputs(const argon2_context *context) { +++int validate_inputs_wowseed(const argon2_context *context) { ++ if (NULL == context) { ++ return ARGON2_INCORRECT_PARAMETER; ++ } ++@@ -407,7 +407,7 @@ int validate_inputs(const argon2_context *context) { ++ return ARGON2_OK; ++ } ++ ++-void fill_first_blocks(uint8_t *blockhash, const argon2_instance_t *instance) { +++void fill_first_blocks_wowseed(uint8_t *blockhash, const argon2_instance_t *instance) { ++ uint32_t l; ++ /* Make the first and second block in each lane as G(H0||0||i) or ++ G(H0||1||i) */ ++@@ -430,7 +430,7 @@ void fill_first_blocks(uint8_t *blockhash, const argon2_instance_t *instance) { ++ clear_internal_memory(blockhash_bytes, ARGON2_BLOCK_SIZE); ++ } ++ ++-void initial_hash(uint8_t *blockhash, argon2_context *context, +++void initial_hash_wowseed(uint8_t *blockhash, argon2_context *context, ++ argon2_type type) { ++ blake2b_state BlakeHash; ++ uint8_t value[sizeof(uint32_t)]; ++@@ -439,31 +439,31 @@ void initial_hash(uint8_t *blockhash, argon2_context *context, ++ return; ++ } ++ ++- blake2b_init(&BlakeHash, ARGON2_PREHASH_DIGEST_LENGTH); +++ blake2b_init_wowseed(&BlakeHash, ARGON2_PREHASH_DIGEST_LENGTH); ++ ++ store32(&value, context->lanes); ++- blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); +++ blake2b_update_wowseed(&BlakeHash, (const uint8_t *)&value, sizeof(value)); ++ ++ store32(&value, context->outlen); ++- blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); +++ blake2b_update_wowseed(&BlakeHash, (const uint8_t *)&value, sizeof(value)); ++ ++ store32(&value, context->m_cost); ++- blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); +++ blake2b_update_wowseed(&BlakeHash, (const uint8_t *)&value, sizeof(value)); ++ ++ store32(&value, context->t_cost); ++- blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); +++ blake2b_update_wowseed(&BlakeHash, (const uint8_t *)&value, sizeof(value)); ++ ++ store32(&value, context->version); ++- blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); +++ blake2b_update_wowseed(&BlakeHash, (const uint8_t *)&value, sizeof(value)); ++ ++ store32(&value, (uint32_t)type); ++- blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); +++ blake2b_update_wowseed(&BlakeHash, (const uint8_t *)&value, sizeof(value)); ++ ++ store32(&value, context->pwdlen); ++- blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); +++ blake2b_update_wowseed(&BlakeHash, (const uint8_t *)&value, sizeof(value)); ++ ++ if (context->pwd != NULL) { ++- blake2b_update(&BlakeHash, (const uint8_t *)context->pwd, +++ blake2b_update_wowseed(&BlakeHash, (const uint8_t *)context->pwd, ++ context->pwdlen); ++ ++ if (context->flags & ARGON2_FLAG_CLEAR_PASSWORD) { ++@@ -473,18 +473,18 @@ void initial_hash(uint8_t *blockhash, argon2_context *context, ++ } ++ ++ store32(&value, context->saltlen); ++- blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); +++ blake2b_update_wowseed(&BlakeHash, (const uint8_t *)&value, sizeof(value)); ++ ++ if (context->salt != NULL) { ++- blake2b_update(&BlakeHash, (const uint8_t *)context->salt, +++ blake2b_update_wowseed(&BlakeHash, (const uint8_t *)context->salt, ++ context->saltlen); ++ } ++ ++ store32(&value, context->secretlen); ++- blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); +++ blake2b_update_wowseed(&BlakeHash, (const uint8_t *)&value, sizeof(value)); ++ ++ if (context->secret != NULL) { ++- blake2b_update(&BlakeHash, (const uint8_t *)context->secret, +++ blake2b_update_wowseed(&BlakeHash, (const uint8_t *)context->secret, ++ context->secretlen); ++ ++ if (context->flags & ARGON2_FLAG_CLEAR_SECRET) { ++@@ -494,17 +494,17 @@ void initial_hash(uint8_t *blockhash, argon2_context *context, ++ } ++ ++ store32(&value, context->adlen); ++- blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); +++ blake2b_update_wowseed(&BlakeHash, (const uint8_t *)&value, sizeof(value)); ++ ++ if (context->ad != NULL) { ++- blake2b_update(&BlakeHash, (const uint8_t *)context->ad, +++ blake2b_update_wowseed(&BlakeHash, (const uint8_t *)context->ad, ++ context->adlen); ++ } ++ ++- blake2b_final(&BlakeHash, blockhash, ARGON2_PREHASH_DIGEST_LENGTH); +++ blake2b_final_wowseed(&BlakeHash, blockhash, ARGON2_PREHASH_DIGEST_LENGTH); ++ } ++ ++-int initialize(argon2_instance_t *instance, argon2_context *context) { +++int initialize_wowseed(argon2_instance_t *instance, argon2_context *context) { ++ uint8_t blockhash[ARGON2_PREHASH_SEED_LENGTH]; ++ int result = ARGON2_OK; ++ ++@@ -523,7 +523,7 @@ int initialize(argon2_instance_t *instance, argon2_context *context) { ++ /* H_0 + 8 extra bytes to produce the first blocks */ ++ /* uint8_t blockhash[ARGON2_PREHASH_SEED_LENGTH]; */ ++ /* Hashing all inputs */ ++- initial_hash(blockhash, context, instance->type); +++ initial_hash_wowseed(blockhash, context, instance->type); ++ /* Zeroing 8 extra bytes */ ++ clear_internal_memory(blockhash + ARGON2_PREHASH_DIGEST_LENGTH, ++ ARGON2_PREHASH_SEED_LENGTH - ++@@ -535,7 +535,7 @@ int initialize(argon2_instance_t *instance, argon2_context *context) { ++ ++ /* 3. Creating first blocks, we always have at least two blocks in a slice ++ */ ++- fill_first_blocks(blockhash, instance); +++ fill_first_blocks_wowseed(blockhash, instance); ++ /* Clearing the hash */ ++ clear_internal_memory(blockhash, ARGON2_PREHASH_SEED_LENGTH); ++ ++diff --git a/src/argon2/core.h b/src/argon2/core.h ++index 78000ba..6b0154c 100644 ++--- a/src/argon2/core.h +++++ b/src/argon2/core.h ++@@ -53,7 +53,7 @@ typedef struct block_ { uint64_t v[ARGON2_QWORDS_IN_BLOCK]; } block; ++ ++ /*****************Functions that work with the block******************/ ++ ++-/* Initialize each byte of the block with @in */ +++/* initialize_wowseed each byte of the block with @in */ ++ void init_block_value(block *b, uint8_t in); ++ ++ /* Copy block @src to block @dst */ ++@@ -158,7 +158,7 @@ uint32_t index_alpha(const argon2_instance_t *instance, ++ * @return ARGON2_OK if everything is all right, otherwise one of error codes ++ * (all defined in ++ */ ++-int validate_inputs(const argon2_context *context); +++int validate_inputs_wowseed(const argon2_context *context); ++ ++ /* ++ * Hashes all the inputs into @a blockhash[PREHASH_DIGEST_LENGTH], clears ++@@ -170,28 +170,27 @@ int validate_inputs(const argon2_context *context); ++ * @pre @a blockhash must have at least @a PREHASH_DIGEST_LENGTH bytes ++ * allocated ++ */ ++-void initial_hash(uint8_t *blockhash, argon2_context *context, +++void initial_hash_wowseed(uint8_t *blockhash, argon2_context *context, ++ argon2_type type); ++- ++ /* ++ * Function creates first 2 blocks per lane ++ * @param instance Pointer to the current instance ++ * @param blockhash Pointer to the pre-hashing digest ++ * @pre blockhash must point to @a PREHASH_SEED_LENGTH allocated values ++ */ ++-void fill_first_blocks(uint8_t *blockhash, const argon2_instance_t *instance); +++void fill_first_blocks_wowseed(uint8_t *blockhash, const argon2_instance_t *instance); ++ ++ /* ++ * Function allocates memory, hashes the inputs with Blake, and creates first ++ * two blocks. Returns the pointer to the main memory with 2 blocks per lane ++- * initialized +++ * initialize_wowseedd ++ * @param context Pointer to the Argon2 internal structure containing memory ++ * pointer, and parameters for time and space requirements. ++ * @param instance Current Argon2 instance ++ * @return Zero if successful, -1 if memory failed to allocate. @context->state ++ * will be modified if successful. ++ */ ++-int initialize(argon2_instance_t *instance, argon2_context *context); +++int initialize_wowseed(argon2_instance_t *instance, argon2_context *context); ++ ++ /* ++ * XORing the last block of each lane, hashing it, making the tag. Deallocates ++@@ -204,7 +203,7 @@ int initialize(argon2_instance_t *instance, argon2_context *context); ++ * @pre if context->free_cbk is not NULL, it should point to a function that ++ * deallocates memory ++ */ ++-void finalize(const argon2_context *context, argon2_instance_t *instance); +++void finalize_wowseed(const argon2_context *context, argon2_instance_t *instance); ++ ++ /* ++ * Function that fills the segment using previous segments also from other ++@@ -223,6 +222,6 @@ void fill_segment(const argon2_instance_t *instance, ++ * @param instance Pointer to the current instance ++ * @return ARGON2_OK if successful, @context->state ++ */ ++-int fill_memory_blocks(argon2_instance_t *instance); +++int _fill_memory_blocks_wowseed(argon2_instance_t *instance); ++ ++ #endif ++diff --git a/src/argon2/ref.c b/src/argon2/ref.c ++index ad1cf46..7edbd6e 100644 ++--- a/src/argon2/ref.c +++++ b/src/argon2/ref.c ++@@ -29,7 +29,7 @@ ++ ++ /* ++ * Function fills a new memory block and optionally XORs the old block over the new one. ++- * @next_block must be initialized. +++ * @next_block must be initialize_wowseedd. ++ * @param prev_block Pointer to the previous block ++ * @param ref_block Pointer to the reference block ++ * @param next_block Pointer to the block to be constructed ++-- ++2.39.2 +-- +2.39.5 (Apple Git-154) + -- cgit v1.2.3