summaryrefslogtreecommitdiff
path: root/patches/monero/0006-UR-functions.patch
diff options
context:
space:
mode:
authorcyan <cyjan@mrcyjanek.net>2025-02-20 15:12:58 +0100
committerGitHub <noreply@github.com>2025-02-20 15:12:58 +0100
commit991a0d0c97bce6ae8671dc763dd03876dfa9ea8a (patch)
treedd3ea422437bab03a774676868afb2216e13d5f3 /patches/monero/0006-UR-functions.patch
parent65608c09e9093f1cd42c6afd8e9131016c82574b (diff)
[monero] update to latest v0.18 (#122)v0.18.4.0-RC1
* monero update * update checksum
Diffstat (limited to 'patches/monero/0006-UR-functions.patch')
-rw-r--r--patches/monero/0006-UR-functions.patch1010
1 files changed, 0 insertions, 1010 deletions
diff --git a/patches/monero/0006-UR-functions.patch b/patches/monero/0006-UR-functions.patch
deleted file mode 100644
index c25b2d0..0000000
--- a/patches/monero/0006-UR-functions.patch
+++ /dev/null
@@ -1,1010 +0,0 @@
-From 3138500cb65a6d72726d611d16cbd4b28d01fbe9 Mon Sep 17 00:00:00 2001
-From: tobtoht <tob@featherwallet.org>
-Date: Tue, 12 Mar 2024 10:09:50 +0100
-Subject: [PATCH 06/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 | 4 +
- CMakeLists.txt | 4 +-
- 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 +
- 15 files changed, 519 insertions(+), 57 deletions(-)
- create mode 160000 external/bc-ur
-
-diff --git a/.gitmodules b/.gitmodules
-index ffb73fe..72af74d 100644
---- a/.gitmodules
-+++ b/.gitmodules
-@@ -15,3 +15,7 @@
- path = external/supercop
- url = https://github.com/monero-project/supercop
- branch = monero
-+[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 8fb03ba..63ee825 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)
-
-@@ -364,6 +365,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/external/CMakeLists.txt b/external/CMakeLists.txt
-index 5b7f69a..f9ed6a6 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(randomx 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 5d0afe1..bb5b6f4 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 6095f99..b163212 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 <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 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 <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 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 dfbf4fd..4fb5f75 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 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<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 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<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 debc0c2..dfa3213 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);
-@@ -6986,6 +6996,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;
-@@ -7837,9 +7866,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)
- {
-@@ -11235,7 +11262,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)
- {
-@@ -11285,9 +11312,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({});
-@@ -13923,33 +13956,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
- {
-@@ -14004,53 +14044,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 a050286..1d7e430 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<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);
-@@ -1624,9 +1625,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.39.5 (Apple Git-154)
-