diff options
| author | cyan <cyjan@mrcyjanek.net> | 2026-03-09 18:05:16 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-03-09 18:05:16 +0100 |
| commit | 2c11591e02b907e63d8fd4fcb0a6559625934a95 (patch) | |
| tree | dab95d36703f314a8ee9d6277a160df16833c4e5 /patches/wownero/0007-UR-functions.patch | |
| parent | 411e8a1cdb3f4c2812d83f28c335d2a4eb18bd29 (diff) | |
reproducibility (#177)
* reproducibility
* wip: ci fixes, drop generate_translations_header.c
* minor fixes
* fix patch
* fix: toolchain
* bump hash
* fix: minor build issue fixes
* fix: x86_64-w64-mingw32
* wip
* wip
* all updated :o
* fix: reduce git size
* update checksum
remove CI
* chore, more optimal dockerfile
* update monero to v0.18.4.6
* update checksum
* update
* minor patch update
* fix: no command specified
* fix: correct path
* alpine
* stupid.
* AAWASTREYDRFUGTIHYJHGUTYFRDTFYVGUBHINJHGTYFRDSRTXDTCFHBJ
Diffstat (limited to 'patches/wownero/0007-UR-functions.patch')
| -rw-r--r-- | patches/wownero/0007-UR-functions.patch | 1036 |
1 files changed, 0 insertions, 1036 deletions
diff --git a/patches/wownero/0007-UR-functions.patch b/patches/wownero/0007-UR-functions.patch deleted file mode 100644 index 0bdc695..0000000 --- a/patches/wownero/0007-UR-functions.patch +++ /dev/null @@ -1,1036 +0,0 @@ -From 031df7de0d75d93f78be732d5cac702b0ab193f0 Mon Sep 17 00:00:00 2001 -From: tobtoht <tob@featherwallet.org> -Date: Tue, 12 Mar 2024 10:09:50 +0100 -Subject: [PATCH 07/15] 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 b4b8c8089..88335ee9d 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 f118c754e..f26655d68 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 29aed0cc6..dfdffe67c 100644 ---- a/external/CMakeLists.txt -+++ b/external/CMakeLists.txt -@@ -69,4 +69,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<boost::recursive_mutex> lock1(device_locker, boost::adopt_lock); \ - boost::lock_guard<boost::mutex> 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 <boost/format.hpp> - #include <boost/filesystem.hpp> - -+#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 <sstream> - #include <boost/format.hpp> - -+#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<tools::wallet2::pending_tx> 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<size_t()> get_num_txes, const std::function<const tools::wallet2::tx_construction_data&(size_t)> &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 <boost/locale.hpp> - #include <boost/filesystem.hpp> -+#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<std::string> &key_images) const -+{ -+ clearStatus(); -+ -+ std::vector<crypto::key_image> 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<PendingTransactionImpl> 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<std::string> &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<std::string> &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<uint32_t> 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<std::string> &txids) override; - - bool setupBackgroundSync(const BackgroundSyncType background_sync_type, const std::string &wallet_password, const optional<std::string> &background_cache_password = optional<std::string>()) 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<std::string> &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<std::pair<std::string, uint64_t>> &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 48267d647..e14d4d2fc 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<crypto::key_image>& 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); -@@ -6998,6 +7008,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<crypto::key_image>& 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<uint32_t, uint64_t> wallet2::balance_per_subaddress(uint32_t index_major, bool strict) const - { - std::map<uint32_t, uint64_t> amount_per_subaddr; -@@ -7849,9 +7878,7 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector<wallet2::pendin - crypto::key_derivation derivation; - std::vector<crypto::key_derivation> 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<crypto::public_key> additional_tx_pub_keys; - for (const crypto::secret_key &skey: txs[n].additional_tx_keys) - { -@@ -11241,7 +11268,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp - MDEBUG("Ignoring output " << i << " of amount " << print_money(td.amount()) << " which is below fractional threshold " << print_money(fractional_threshold)); - continue; - } -- if (!is_spent(td, false) && !td.m_frozen && !td.m_key_image_partial && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1) -+ if (!is_spent(td, false) && !td.m_frozen && !td.m_key_image_partial && td.m_key_image_known && (use_rct ? true : !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) - { -@@ -11291,9 +11318,15 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp - - LOG_PRINT_L2("Starting with " << num_nondust_outputs << " non-dust outputs and " << num_dust_outputs << " dust outputs"); - -- if (unused_dust_indices_per_subaddr.empty() && unused_transfers_indices_per_subaddr.empty()) -- return std::vector<wallet2::pending_tx>(); -+ // 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<wallet2::pending_tx>(); -+ } - // 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({}); -@@ -13920,33 +13953,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<uint64_t, std::vector<std::pair<crypto::key_image, crypto::signature>>> 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<uint64_t, std::vector<std::pair<crypto::key_image, crypto::signature>>> 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<uint64_t, std::vector<std::pair<crypto::key_image, crypto::signature>>> wallet2::export_key_images(bool all) const - { -@@ -14001,53 +14041,60 @@ std::pair<uint64_t, std::vector<std::pair<crypto::key_image, crypto::signature>> - 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<std::pair<crypto::key_image, crypto::signature>> ski; - ski.reserve(nki); - for (size_t n = 0; n < nki; ++n) - { -- crypto::key_image key_image = *reinterpret_cast<const crypto::key_image*>(&data[headerlen + n * record_size]); -- crypto::signature signature = *reinterpret_cast<const crypto::signature*>(&data[headerlen + n * record_size + sizeof(crypto::key_image)]); -+ crypto::key_image key_image = *reinterpret_cast<const crypto::key_image*>(&data_local[headerlen + n * record_size]); -+ crypto::signature signature = *reinterpret_cast<const crypto::signature*>(&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 022d0696f..746e2aeff 100644 ---- a/src/wallet/wallet2.h -+++ b/src/wallet/wallet2.h -@@ -1157,6 +1157,7 @@ private: - // locked & unlocked balance of given or current subaddress account - uint64_t balance(uint32_t subaddr_index_major, bool strict) const; - uint64_t unlocked_balance(uint32_t subaddr_index_major, bool strict, uint64_t *blocks_to_unlock = NULL, uint64_t *time_to_unlock = NULL); -+ uint64_t view_only_balance(uint32_t index_major, const std::vector<crypto::key_image>& selected_inputs = {}); - // locked & unlocked balance per subaddress of given or current subaddress account - std::map<uint32_t, uint64_t> balance_per_subaddress(uint32_t subaddr_index_major, bool strict) const; - std::map<uint32_t, std::pair<uint64_t, std::pair<uint64_t, uint64_t>>> unlocked_balance_per_subaddress(uint32_t subaddr_index_major, bool strict); -@@ -1631,9 +1632,11 @@ private: - std::tuple<size_t, crypto::hash, std::vector<crypto::hash>> export_blockchain() const; - void import_blockchain(const std::tuple<size_t, crypto::hash, std::vector<crypto::hash>> &bc); - bool export_key_images(const std::string &filename, bool all = false) const; -+ std::string export_key_images_str(bool all) const; - std::pair<uint64_t, std::vector<std::pair<crypto::key_image, crypto::signature>>> export_key_images(bool all = false) const; - uint64_t import_key_images(const std::vector<std::pair<crypto::key_image, crypto::signature>> &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<crypto::key_image> key_images, size_t offset=0, boost::optional<std::unordered_set<size_t>> 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.48.0 - |
