894 lines
35 KiB
Diff
894 lines
35 KiB
Diff
From f91e92ddaa7c12dcc8f38ac37ff852446bd10722 Mon Sep 17 00:00:00 2001
|
|
From: Czarek Nakamoto <cyjan@mrcyjanek.net>
|
|
Date: Thu, 16 May 2024 17:28:59 +0200
|
|
Subject: [PATCH] WIP: 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 | 3 +
|
|
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 | 286 +++++++++++++++++++++++-
|
|
src/wallet/api/wallet.h | 6 +
|
|
src/wallet/api/wallet2_api.h | 19 +-
|
|
src/wallet/wallet2.cpp | 96 ++++----
|
|
src/wallet/wallet2.h | 2 +
|
|
17 files changed, 452 insertions(+), 53 deletions(-)
|
|
create mode 160000 external/bc-ur
|
|
|
|
diff --git a/.gitmodules b/.gitmodules
|
|
index 7ea87a009..a7e1d2cd0 100644
|
|
--- a/.gitmodules
|
|
+++ b/.gitmodules
|
|
@@ -20,3 +20,6 @@
|
|
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
|
|
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
|
index 63b8c5079..6028c0961 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/contrib/depends/hosts/darwin.mk b/contrib/depends/hosts/darwin.mk
|
|
index cbe795081..b14ee5c5b 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 1b9761d70..0df9f9116 100644
|
|
--- a/external/CMakeLists.txt
|
|
+++ b/external/CMakeLists.txt
|
|
@@ -69,6 +69,7 @@ endif()
|
|
add_subdirectory(db_drivers)
|
|
add_subdirectory(easylogging++)
|
|
add_subdirectory(qrcodegen)
|
|
+add_subdirectory(bc-ur)
|
|
add_subdirectory(randomx EXCLUDE_FROM_ALL)
|
|
add_subdirectory(polyseed EXCLUDE_FROM_ALL)
|
|
add_subdirectory(utf8proc EXCLUDE_FROM_ALL)
|
|
\ No newline at end of file
|
|
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 a4b5f3ef0..90675df11 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 6095f99d5..b163212b7 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 be20b478c..1f714d229 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 {
|
|
@@ -178,6 +180,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 2fbaa83d9..0cc6c58e9 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 90e107137..ac3e6d51e 100644
|
|
--- a/src/wallet/api/wallet.cpp
|
|
+++ b/src/wallet/api/wallet.cpp
|
|
@@ -48,6 +48,7 @@
|
|
|
|
#include <boost/locale.hpp>
|
|
#include <boost/filesystem.hpp>
|
|
+#include "bc-ur/src/bc-ur.hpp"
|
|
|
|
using namespace std;
|
|
using namespace cryptonote;
|
|
@@ -1321,6 +1322,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"))
|
|
@@ -1332,7 +1388,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;
|
|
@@ -1341,6 +1397,56 @@ 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();
|
|
@@ -1373,6 +1479,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"))
|
|
@@ -1398,6 +1537,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"))
|
|
@@ -1430,6 +1625,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"))
|
|
@@ -1464,6 +1693,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 2ad2b62a4..febc93119 100644
|
|
--- a/src/wallet/api/wallet.h
|
|
+++ b/src/wallet/api/wallet.h
|
|
@@ -182,12 +182,18 @@ public:
|
|
const std::set<std::string> &preferred_inputs = {}) 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 1c3a11c39..2bbb32c8b 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;
|
|
@@ -162,7 +163,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;
|
|
};
|
|
|
|
/**
|
|
@@ -938,13 +940,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
|
|
@@ -969,20 +973,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
|
|
@@ -990,6 +996,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 e5773a26b..23b56810e 100644
|
|
--- a/src/wallet/wallet2.cpp
|
|
+++ b/src/wallet/wallet2.cpp
|
|
@@ -14051,33 +14051,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;
|
|
+
|
|
+ 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 save_to_file(filename, magic + ciphertext);
|
|
+ // 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
|
|
{
|
|
@@ -14132,53 +14139,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 0d9a9298f..539d5056c 100644
|
|
--- a/src/wallet/wallet2.h
|
|
+++ b/src/wallet/wallet2.h
|
|
@@ -1650,9 +1650,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.2
|
|
|