From f3d06bfe0e1f11153533d4e813dff61c920f340d Mon Sep 17 00:00:00 2001 From: Lee Clagett Date: Tue, 1 Apr 2025 21:59:25 -0400 Subject: [PATCH] Added recover from keys, and more bug fixes --- include/lws_frontend.h | 2 +- src/backend.cpp | 21 ++++++++++----- src/backend.h | 6 +++-- src/transaction_info.cpp | 17 ++++++++----- src/wallet.cpp | 55 ++++++++++++++++++++++++++++++++++++++++ src/wallet.h | 7 ++--- src/wallet_manager.cpp | 24 +++++++++++------- 7 files changed, 104 insertions(+), 28 deletions(-) diff --git a/include/lws_frontend.h b/include/lws_frontend.h index bde664a..91f07ca 100644 --- a/include/lws_frontend.h +++ b/include/lws_frontend.h @@ -1375,4 +1375,4 @@ struct WalletManagerFactory static void setLogCategories(const std::string &categories); }; -} +} // lwsf diff --git a/src/backend.cpp b/src/backend.cpp index 604f61e..d22b06b 100644 --- a/src/backend.cpp +++ b/src/backend.cpp @@ -447,8 +447,8 @@ namespace lwsf { namespace internal { namespace backend void merge_subaddrs(account& self, const epee::span subaddrs) { - /* This will purge subaddresses that the server somehow no longer claims - as being valid. The existing info per subaddress is moved/preserved. */ + /* Purge subaccounts that do not have info (label) and are not mentioned + by the server. */ std::map subaccounts; subaccounts.swap(self.subaccounts); @@ -456,6 +456,17 @@ namespace lwsf { namespace internal { namespace backend // Never remove special {0,0} account self.subaccounts[0].minor[0] = std::move(subaccounts[0].minor[0]); + // Keep all subaccounts with a info + for (auto& account : subaccounts) + { + for (auto& subaccount : account.second.minor) + { + if (subaccount.second.preserve()) + self.subaccounts[account.first].minor[subaccount.first] = std::move(subaccount.second); + } + } + + // Keep all subaccounts mentioned by server for (const auto& subaddr : subaddrs) { auto& account = self.subaccounts[subaddr.key]; @@ -463,11 +474,7 @@ namespace lwsf { namespace internal { namespace backend for (const auto& minors : subaddr.value) { for (std::uint32_t minor = std::get<0>(minors); minor < std::uint64_t(std::get<1>(minors)) + 1; ++minor) - { - auto elem = account.minor.try_emplace(minor); - if (elem.second && (subaddr.key || minor)) - elem.first->second = std::move(old.minor[minor]); - } + account.minor.try_emplace(minor); } } } diff --git a/src/backend.h b/src/backend.h index 38c5030..26c78a6 100644 --- a/src/backend.h +++ b/src/backend.h @@ -77,6 +77,8 @@ namespace lwsf { namespace internal { namespace backend struct subaddress { std::string label; + + bool preserve() const noexcept { return !label.empty(); } }; WIRE_DECLARE_OBJECT(subaddress); @@ -128,9 +130,9 @@ namespace lwsf { namespace internal { namespace backend receives(), description(), timestamp(), + height(), amount(0), fee(0), - height(), unlock_time(0), direction(Monero::TransactionInfo::Direction_Out), payment_id(), @@ -154,9 +156,9 @@ namespace lwsf { namespace internal { namespace backend boost::container::flat_map receives; //!< Key is output pub std::string description; std::optional timestamp; + std::optional height; std::uint64_t amount; std::uint64_t fee; - std::optional height; std::uint64_t unlock_time; Monero::TransactionInfo::Direction direction; std::variant payment_id; diff --git a/src/transaction_info.cpp b/src/transaction_info.cpp index 92d2343..4a4faf9 100644 --- a/src/transaction_info.cpp +++ b/src/transaction_info.cpp @@ -76,7 +76,7 @@ namespace lwsf { namespace internal std::string transaction_info::description() const { const boost::lock_guard lock{wallet_->sync}; - return data_->description; + return data_->description; // this can change outside of refresh func } std::set transaction_info::subaddrIndex() const @@ -84,11 +84,16 @@ namespace lwsf { namespace internal std::set out; const std::uint32_t major = subaddrAccount(); - for (const auto& receive : data_->receives) - out.insert(receive.second.recipient.min_i); - - for (const auto& spend : data_->spends) - out.insert(spend.second.sender.min_i); + if (direction() == Direction_In) + { + for (const auto& receive : data_->receives) + out.insert(receive.second.recipient.min_i); + } + else + { + for (const auto& spend : data_->spends) + out.insert(spend.second.sender.min_i); + } return out; } diff --git a/src/wallet.cpp b/src/wallet.cpp index cd213e5..5daed28 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -585,6 +585,61 @@ namespace lwsf { namespace internal load_wallet(data_->primary, recovery, nettype, false); } + + wallet::wallet(from_keys, const Monero::NetworkType nettype, std::string filename, std::string password, const std::uint64_t kdf_rounds, const std::string& address_string, const std::string& view_key, const std::string& spend_key) + : data_(std::make_shared()), + addressbook_(), + history_(), + subaddresses_(), + subaddress_minor_(), + filename_(std::move(filename)), + password_(std::move(password)), + exception_error_(), + status_(), + thread_(), + iterations_(kdf_rounds), + mixin_(config::mixin_default), + refresh_interval_(config::refresh_interval), + refresh_notify_(), + error_sync_(), + refresh_sync_(), + thread_sync_(), + thread_state_(state::stop), + mandatory_refresh_(false) + { + if (sodium_init() < 0) + throw std::runtime_error{"Failed to initialize libsodium"}; + + data_->primary.generated_locally = false; + data_->primary.type = nettype; + + if (!epee::from_hex::to_buffer(epee::as_mut_byte_span(unwrap(unwrap(data_->primary.view.sec))), view_key)) + { + exception_error_ = "view_key contained invalid hex"; + return; + } + if (!crypto::secret_key_to_public_key(data_->primary.view.sec, data_->primary.view.pub)) + { + exception_error_ = "view_pub could not be computed"; + return; + } + + crypto::secret_key spend_sec; + if (!epee::from_hex::to_buffer(epee::as_mut_byte_span(unwrap(unwrap(data_->primary.spend.sec))), spend_key)) + { + exception_error_ = "spend_key contained invalid hex"; + return; + } + if (!crypto::secret_key_to_public_key(data_->primary.spend.sec, data_->primary.spend.pub)) + { + exception_error_ = "spend_pub could not be computed"; + return; + } + + if (data_->get_spend_address({0, 0}) != address_string) + exception_error_ = "view_key, spend_key, and address_string do not match"; + } + wallet::~wallet() { stop_refresh_loop(); } std::string wallet::seed(const std::string& seed_offset) const diff --git a/src/wallet.h b/src/wallet.h index 78ef259..b1ca8ce 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -34,9 +34,10 @@ #include #include #include +#include #include #include -#include "cryptonote_basic/account.h" // monero/src +#include #include "net/http_client.h" // monero/contrib/epee/include #include "wallet/api/wallet2_api.h" // monero/src @@ -89,8 +90,8 @@ namespace internal wallet(create, Monero::NetworkType nettype, std::string filename, std::string password, std::uint64_t kdf_rounds); wallet(open, Monero::NetworkType nettpe, std::string filename, std::string password, std::uint64_t kdf_rounds); - wallet(from_mnemonic, Monero::NetworkType nettype, std::string path, std::string password, std::uint64_t kdf_rounds, const std::string& mnemonic, const std::string& seed_offset); - wallet(from_keys, Monero::NetworkType nettype, std::string path, std::string password, std::uint64_t kdf_rounds, const std::string& mnemonic, const std::string& seed_offset); + wallet(from_mnemonic, Monero::NetworkType nettype, std::string filename, std::string password, std::uint64_t kdf_rounds, const std::string& mnemonic, const std::string& seed_offset); + wallet(from_keys, Monero::NetworkType nettype, std::string filename, std::string password, std::uint64_t kdf_rounds, const std::string& address_string, const std::string& view_key, const std::string& spend_key); wallet(const wallet&) = delete; wallet(wallet&&) = delete; diff --git a/src/wallet_manager.cpp b/src/wallet_manager.cpp index a5c6e63..40b33e8 100644 --- a/src/wallet_manager.cpp +++ b/src/wallet_manager.cpp @@ -28,11 +28,11 @@ #include "lws_frontend.h" -#include #include #include #include #include +#include #include #include "backend.h" #include "common/dns_utils.h" // monero/src @@ -178,7 +178,11 @@ namespace lwsf const std::string &spendKeyString, uint64_t kdf_rounds) override { - //! \TODO Complete + auto out = std::make_unique( + wallet::from_keys{}, nettype, path, password, kdf_rounds, addressString, viewKeyString, spendKeyString + ); + out->setRefreshFromBlockHeight(restoreHeight); + return out.release(); } /*! @@ -201,7 +205,7 @@ namespace lwsf const std::string &viewKeyString, const std::string &spendKeyString) override { - //! \TODO Complete + return createWalletFromKeys(path, "", language, nettype, restoreHeight, addressString, viewKeyString, spendKeyString, 1); } /*! @@ -252,7 +256,7 @@ namespace lwsf */ bool walletExists(const std::string &path) override { - return lwsf::internal::wallet::is_wallet_file(path); + return wallet::is_wallet_file(path); } /*! @@ -269,7 +273,7 @@ namespace lwsf */ bool verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool, uint64_t) const override { - lwsf::internal::wallet::verify_password(keys_file_name, password); + return wallet::verify_password(keys_file_name, password); } /*! @@ -294,7 +298,7 @@ namespace lwsf */ std::vector findWallets(const std::string &path) override { - return lwsf::internal::wallet::find(path); + return wallet::find(path); } std::string errorString() const override { return error_; } @@ -355,21 +359,23 @@ namespace lwsf std::string resolveOpenAlias(const std::string &address, bool &dnssec_valid) const override { - const boost::string_ref prefix{"oa1:xmr "}; + static constexpr const std::string_view prefix{"oa1:xmr "}; std::string real_address = tools::DNSResolver::instance().get_dns_format_from_oa_address(address); bool dnssec_available = false; std::vector records = tools::DNSResolver::instance().get_txt_record(real_address, dnssec_available, dnssec_valid); - for (boost::string_ref record : records) + for (std::string_view record : records) { - if (record.starts_with(prefix)) + if (record.substr(0, prefix.size()) == prefix) { record = record.substr(prefix.size()); return std::string{record.data(), record.size()}; } } + + return {}; } //! checks for an update and returns version, hash and url