* update wownero to 0.11.3.0 * Updated wownero, cleaned up patches * unshallow zano * Fix ios builds * Update checksum
1037 lines
42 KiB
Diff
1037 lines
42 KiB
Diff
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
|
|
|