From b6492b546aab109fbaace07d5808ea9ae1eabb14 Mon Sep 17 00:00:00 2001 From: SNeedlewoods Date: Fri, 7 Jun 2024 20:08:31 +0200 Subject: [PATCH 1/4] organize functions --- src/wallet/api/address_book.cpp | 39 +- src/wallet/api/address_book.h | 5 +- src/wallet/api/pending_transaction.cpp | 16 +- src/wallet/api/subaddress.cpp | 20 +- src/wallet/api/subaddress.h | 3 +- src/wallet/api/subaddress_account.cpp | 20 +- src/wallet/api/subaddress_account.h | 3 +- src/wallet/api/transaction_history.cpp | 22 +- src/wallet/api/transaction_info.cpp | 19 +- src/wallet/api/transaction_info.h | 5 +- src/wallet/api/unsigned_transaction.cpp | 201 +-- src/wallet/api/unsigned_transaction.h | 5 +- src/wallet/api/wallet.cpp | 2005 +++++++++++------------ src/wallet/api/wallet.h | 120 +- src/wallet/api/wallet2_api.h | 243 +-- src/wallet/api/wallet_manager.cpp | 11 +- src/wallet/api/wallet_manager.h | 4 +- 17 files changed, 1365 insertions(+), 1376 deletions(-) diff --git a/src/wallet/api/address_book.cpp b/src/wallet/api/address_book.cpp index aeffe921e8..91c27a3f76 100644 --- a/src/wallet/api/address_book.cpp +++ b/src/wallet/api/address_book.cpp @@ -44,6 +44,16 @@ AddressBook::~AddressBook() {} AddressBookImpl::AddressBookImpl(WalletImpl *wallet) : m_wallet(wallet), m_errorCode(Status_Ok) {} +AddressBookImpl::~AddressBookImpl() +{ + clearRows(); +} + +std::vector AddressBookImpl::getAll() const +{ + return m_rows; +} + bool AddressBookImpl::addRow(const std::string &dst_addr , const std::string &payment_id_str, const std::string &description) { clearStatus(); @@ -70,6 +80,15 @@ bool AddressBookImpl::addRow(const std::string &dst_addr , const std::string &pa return r; } +bool AddressBookImpl::deleteRow(std::size_t rowId) +{ + LOG_PRINT_L2("Deleting address book row " << rowId); + bool r = m_wallet->m_wallet->delete_address_book_row(rowId); + if (r) + refresh(); + return r; +} + bool AddressBookImpl::setDescription(std::size_t index, const std::string &description) { clearStatus(); @@ -111,15 +130,6 @@ void AddressBookImpl::refresh() } -bool AddressBookImpl::deleteRow(std::size_t rowId) -{ - LOG_PRINT_L2("Deleting address book row " << rowId); - bool r = m_wallet->m_wallet->delete_address_book_row(rowId); - if (r) - refresh(); - return r; -} - int AddressBookImpl::lookupPaymentID(const std::string &payment_id) const { // turn short ones into long ones for comparison @@ -154,15 +164,4 @@ void AddressBookImpl::clearStatus(){ m_errorCode = 0; } -std::vector AddressBookImpl::getAll() const -{ - return m_rows; -} - - -AddressBookImpl::~AddressBookImpl() -{ - clearRows(); -} - } // namespace diff --git a/src/wallet/api/address_book.h b/src/wallet/api/address_book.h index 8de7f95ffa..3e6b5df4d3 100644 --- a/src/wallet/api/address_book.h +++ b/src/wallet/api/address_book.h @@ -41,12 +41,11 @@ class AddressBookImpl : public AddressBook AddressBookImpl(WalletImpl * wallet); ~AddressBookImpl(); - // Fetches addresses from Wallet2 - void refresh() override; std::vector getAll() const override; bool addRow(const std::string &dst_addr , const std::string &payment_id, const std::string &description) override; - bool setDescription(std::size_t index, const std::string &description) override; bool deleteRow(std::size_t rowId) override; + bool setDescription(std::size_t index, const std::string &description) override; + void refresh() override; // Error codes. See AddressBook:ErrorCode enum in wallet2_api.h std::string errorString() const override {return m_errorString;} diff --git a/src/wallet/api/pending_transaction.cpp b/src/wallet/api/pending_transaction.cpp index 47eb7a2432..3c7bc02c9c 100644 --- a/src/wallet/api/pending_transaction.cpp +++ b/src/wallet/api/pending_transaction.cpp @@ -70,14 +70,6 @@ string PendingTransactionImpl::errorString() const return m_errorString; } -std::vector PendingTransactionImpl::txid() const -{ - std::vector txid; - for (const auto &pt: m_pending_tx) - txid.push_back(epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(pt.tx))); - return txid; -} - bool PendingTransactionImpl::commit(const std::string &filename, bool overwrite) { @@ -191,6 +183,14 @@ uint64_t PendingTransactionImpl::fee() const return result; } +std::vector PendingTransactionImpl::txid() const +{ + std::vector txid; + for (const auto &pt: m_pending_tx) + txid.push_back(epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(pt.tx))); + return txid; +} + uint64_t PendingTransactionImpl::txCount() const { return m_pending_tx.size(); diff --git a/src/wallet/api/subaddress.cpp b/src/wallet/api/subaddress.cpp index 7a1460c8e2..5135d2b32a 100644 --- a/src/wallet/api/subaddress.cpp +++ b/src/wallet/api/subaddress.cpp @@ -41,6 +41,16 @@ Subaddress::~Subaddress() {} SubaddressImpl::SubaddressImpl(WalletImpl *wallet) : m_wallet(wallet) {} +SubaddressImpl::~SubaddressImpl() +{ + clearRows(); +} + +std::vector SubaddressImpl::getAll() const +{ + return m_rows; +} + void SubaddressImpl::addRow(uint32_t accountIndex, const std::string &label) { m_wallet->m_wallet->add_subaddress(accountIndex, label); @@ -78,14 +88,4 @@ void SubaddressImpl::clearRows() { m_rows.clear(); } -std::vector SubaddressImpl::getAll() const -{ - return m_rows; -} - -SubaddressImpl::~SubaddressImpl() -{ - clearRows(); -} - } // namespace diff --git a/src/wallet/api/subaddress.h b/src/wallet/api/subaddress.h index bcee105770..189f1fe220 100644 --- a/src/wallet/api/subaddress.h +++ b/src/wallet/api/subaddress.h @@ -39,11 +39,10 @@ class SubaddressImpl : public Subaddress SubaddressImpl(WalletImpl * wallet); ~SubaddressImpl(); - // Fetches addresses from Wallet2 - void refresh(uint32_t accountIndex) override; std::vector getAll() const override; void addRow(uint32_t accountIndex, const std::string &label) override; void setLabel(uint32_t accountIndex, uint32_t addressIndex, const std::string &label) override; + void refresh(uint32_t accountIndex) override; private: void clearRows(); diff --git a/src/wallet/api/subaddress_account.cpp b/src/wallet/api/subaddress_account.cpp index ebee80e7e8..05ab11ce67 100644 --- a/src/wallet/api/subaddress_account.cpp +++ b/src/wallet/api/subaddress_account.cpp @@ -41,6 +41,16 @@ SubaddressAccount::~SubaddressAccount() {} SubaddressAccountImpl::SubaddressAccountImpl(WalletImpl *wallet) : m_wallet(wallet) {} +SubaddressAccountImpl::~SubaddressAccountImpl() +{ + clearRows(); +} + +std::vector SubaddressAccountImpl::getAll() const +{ + return m_rows; +} + void SubaddressAccountImpl::addRow(const std::string &label) { m_wallet->m_wallet->add_subaddress_account(label); @@ -77,14 +87,4 @@ void SubaddressAccountImpl::clearRows() { m_rows.clear(); } -std::vector SubaddressAccountImpl::getAll() const -{ - return m_rows; -} - -SubaddressAccountImpl::~SubaddressAccountImpl() -{ - clearRows(); -} - } // namespace diff --git a/src/wallet/api/subaddress_account.h b/src/wallet/api/subaddress_account.h index 3934df3ef2..25c3395fae 100644 --- a/src/wallet/api/subaddress_account.h +++ b/src/wallet/api/subaddress_account.h @@ -39,11 +39,10 @@ class SubaddressAccountImpl : public SubaddressAccount SubaddressAccountImpl(WalletImpl * wallet); ~SubaddressAccountImpl(); - // Fetches addresses from Wallet2 - void refresh(); std::vector getAll() const; void addRow(const std::string &label); void setLabel(uint32_t accountIndex, const std::string &label); + void refresh(); private: void clearRows(); diff --git a/src/wallet/api/transaction_history.cpp b/src/wallet/api/transaction_history.cpp index ad797cc07d..fe3a49eb36 100644 --- a/src/wallet/api/transaction_history.cpp +++ b/src/wallet/api/transaction_history.cpp @@ -92,17 +92,6 @@ std::vector TransactionHistoryImpl::getAll() const return m_history; } -void TransactionHistoryImpl::setTxNote(const std::string &txid, const std::string ¬e) -{ - cryptonote::blobdata txid_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(txid, txid_data) || txid_data.size() != sizeof(crypto::hash)) - return; - const crypto::hash htxid = *reinterpret_cast(txid_data.data()); - - m_wallet->m_wallet->set_tx_note(htxid, note); - refresh(); -} - void TransactionHistoryImpl::refresh() { // multithreaded access: @@ -265,4 +254,15 @@ void TransactionHistoryImpl::refresh() } +void TransactionHistoryImpl::setTxNote(const std::string &txid, const std::string ¬e) +{ + cryptonote::blobdata txid_data; + if(!epee::string_tools::parse_hexstr_to_binbuff(txid, txid_data) || txid_data.size() != sizeof(crypto::hash)) + return; + const crypto::hash htxid = *reinterpret_cast(txid_data.data()); + + m_wallet->m_wallet->set_tx_note(htxid, note); + refresh(); +} + } // namespace diff --git a/src/wallet/api/transaction_info.cpp b/src/wallet/api/transaction_info.cpp index 8f5ee39a0e..1738a4178e 100644 --- a/src/wallet/api/transaction_info.cpp +++ b/src/wallet/api/transaction_info.cpp @@ -118,6 +118,15 @@ string TransactionInfoImpl::label() const return m_label; } +uint64_t TransactionInfoImpl::confirmations() const +{ + return m_confirmations; +} + +uint64_t TransactionInfoImpl::unlockTime() const +{ + return m_unlock_time; +} string TransactionInfoImpl::hash() const { @@ -139,14 +148,4 @@ const std::vector &TransactionInfoImpl::transfers() c return m_transfers; } -uint64_t TransactionInfoImpl::confirmations() const -{ - return m_confirmations; -} - -uint64_t TransactionInfoImpl::unlockTime() const -{ - return m_unlock_time; -} - } // namespace diff --git a/src/wallet/api/transaction_info.h b/src/wallet/api/transaction_info.h index 33dc8a7f41..8d0d6a15b0 100644 --- a/src/wallet/api/transaction_info.h +++ b/src/wallet/api/transaction_info.h @@ -55,13 +55,12 @@ class TransactionInfoImpl : public TransactionInfo virtual std::set subaddrIndex() const override; virtual uint32_t subaddrAccount() const override; virtual std::string label() const override; - + virtual uint64_t confirmations() const override; + virtual uint64_t unlockTime() const override; virtual std::string hash() const override; virtual std::time_t timestamp() const override; virtual std::string paymentId() const override; virtual const std::vector &transfers() const override; - virtual uint64_t confirmations() const override; - virtual uint64_t unlockTime() const override; private: int m_direction; diff --git a/src/wallet/api/unsigned_transaction.cpp b/src/wallet/api/unsigned_transaction.cpp index 07cf93f593..e60486e31f 100644 --- a/src/wallet/api/unsigned_transaction.cpp +++ b/src/wallet/api/unsigned_transaction.cpp @@ -68,6 +68,106 @@ string UnsignedTransactionImpl::errorString() const return m_errorString; } +std::vector UnsignedTransactionImpl::amount() const +{ + std::vector result; + for (const auto &utx : m_unsigned_tx_set.txes) { + for (const auto &unsigned_dest : utx.dests) { + result.push_back(unsigned_dest.amount); + } + } + return result; +} + +std::vector UnsignedTransactionImpl::fee() const +{ + std::vector result; + for (const auto &utx : m_unsigned_tx_set.txes) { + uint64_t fee = 0; + for (const auto &i: utx.sources) fee += i.amount; + for (const auto &i: utx.splitted_dsts) fee -= i.amount; + result.push_back(fee); + } + return result; +} + +std::vector UnsignedTransactionImpl::mixin() const +{ + std::vector result; + for (const auto &utx: m_unsigned_tx_set.txes) { + size_t min_mixin = ~0; + // TODO: Is this loop needed or is sources[0] ? + for (size_t s = 0; s < utx.sources.size(); ++s) { + size_t mixin = utx.sources[s].outputs.size() - 1; + if (mixin < min_mixin) + min_mixin = mixin; + } + result.push_back(min_mixin); + } + return result; +} + +std::vector UnsignedTransactionImpl::paymentId() const +{ + std::vector result; + for (const auto &utx: m_unsigned_tx_set.txes) { + crypto::hash payment_id = crypto::null_hash; + cryptonote::tx_extra_nonce extra_nonce; + std::vector tx_extra_fields; + cryptonote::parse_tx_extra(utx.extra, tx_extra_fields); + if (cryptonote::find_tx_extra_field_by_type(tx_extra_fields, extra_nonce)) + { + crypto::hash8 payment_id8 = crypto::null_hash8; + if(cryptonote::get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8)) + { + // We can't decrypt short pid without recipient key. + memcpy(payment_id.data, payment_id8.data, 8); + } + else if (!cryptonote::get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id)) + { + payment_id = crypto::null_hash; + } + } + if(payment_id != crypto::null_hash) + result.push_back(epee::string_tools::pod_to_hex(payment_id)); + else + result.push_back(""); + } + return result; +} + +std::vector UnsignedTransactionImpl::recipientAddress() const +{ + // TODO: return integrated address if short payment ID exists + std::vector result; + for (const auto &utx: m_unsigned_tx_set.txes) { + if (utx.dests.empty()) { + MERROR("empty destinations, skipped"); + continue; + } + result.push_back(cryptonote::get_account_address_as_str(m_wallet.m_wallet->nettype(), utx.dests[0].is_subaddress, utx.dests[0].addr)); + } + return result; +} + +uint64_t UnsignedTransactionImpl::minMixinCount() const +{ + uint64_t min_mixin = ~0; + for (const auto &utx: m_unsigned_tx_set.txes) { + for (size_t s = 0; s < utx.sources.size(); ++s) { + size_t mixin = utx.sources[s].outputs.size() - 1; + if (mixin < min_mixin) + min_mixin = mixin; + } + } + return min_mixin; +} + +uint64_t UnsignedTransactionImpl::txCount() const +{ + return m_unsigned_tx_set.txes.size(); +} + bool UnsignedTransactionImpl::sign(const std::string &signedFileName) { if(m_wallet.watchOnly()) @@ -96,6 +196,7 @@ bool UnsignedTransactionImpl::sign(const std::string &signedFileName) return true; } + //---------------------------------------------------------------------------------------------------- bool UnsignedTransactionImpl::checkLoadedTx(const std::function get_num_txes, const std::function &get_tx, const std::string &extra_message) { @@ -215,104 +316,4 @@ bool UnsignedTransactionImpl::checkLoadedTx(const std::function get_nu return true; } -std::vector UnsignedTransactionImpl::amount() const -{ - std::vector result; - for (const auto &utx : m_unsigned_tx_set.txes) { - for (const auto &unsigned_dest : utx.dests) { - result.push_back(unsigned_dest.amount); - } - } - return result; -} - -std::vector UnsignedTransactionImpl::fee() const -{ - std::vector result; - for (const auto &utx : m_unsigned_tx_set.txes) { - uint64_t fee = 0; - for (const auto &i: utx.sources) fee += i.amount; - for (const auto &i: utx.splitted_dsts) fee -= i.amount; - result.push_back(fee); - } - return result; -} - -std::vector UnsignedTransactionImpl::mixin() const -{ - std::vector result; - for (const auto &utx: m_unsigned_tx_set.txes) { - size_t min_mixin = ~0; - // TODO: Is this loop needed or is sources[0] ? - for (size_t s = 0; s < utx.sources.size(); ++s) { - size_t mixin = utx.sources[s].outputs.size() - 1; - if (mixin < min_mixin) - min_mixin = mixin; - } - result.push_back(min_mixin); - } - return result; -} - -uint64_t UnsignedTransactionImpl::txCount() const -{ - return m_unsigned_tx_set.txes.size(); -} - -std::vector UnsignedTransactionImpl::paymentId() const -{ - std::vector result; - for (const auto &utx: m_unsigned_tx_set.txes) { - crypto::hash payment_id = crypto::null_hash; - cryptonote::tx_extra_nonce extra_nonce; - std::vector tx_extra_fields; - cryptonote::parse_tx_extra(utx.extra, tx_extra_fields); - if (cryptonote::find_tx_extra_field_by_type(tx_extra_fields, extra_nonce)) - { - crypto::hash8 payment_id8 = crypto::null_hash8; - if(cryptonote::get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8)) - { - // We can't decrypt short pid without recipient key. - memcpy(payment_id.data, payment_id8.data, 8); - } - else if (!cryptonote::get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id)) - { - payment_id = crypto::null_hash; - } - } - if(payment_id != crypto::null_hash) - result.push_back(epee::string_tools::pod_to_hex(payment_id)); - else - result.push_back(""); - } - return result; -} - -std::vector UnsignedTransactionImpl::recipientAddress() const -{ - // TODO: return integrated address if short payment ID exists - std::vector result; - for (const auto &utx: m_unsigned_tx_set.txes) { - if (utx.dests.empty()) { - MERROR("empty destinations, skipped"); - continue; - } - result.push_back(cryptonote::get_account_address_as_str(m_wallet.m_wallet->nettype(), utx.dests[0].is_subaddress, utx.dests[0].addr)); - } - return result; -} - -uint64_t UnsignedTransactionImpl::minMixinCount() const -{ - uint64_t min_mixin = ~0; - for (const auto &utx: m_unsigned_tx_set.txes) { - for (size_t s = 0; s < utx.sources.size(); ++s) { - size_t mixin = utx.sources[s].outputs.size() - 1; - if (mixin < min_mixin) - min_mixin = mixin; - } - } - return min_mixin; -} - } // namespace diff --git a/src/wallet/api/unsigned_transaction.h b/src/wallet/api/unsigned_transaction.h index 4fd1a0b285..883eaba8fb 100644 --- a/src/wallet/api/unsigned_transaction.h +++ b/src/wallet/api/unsigned_transaction.h @@ -48,13 +48,12 @@ class UnsignedTransactionImpl : public UnsignedTransaction std::vector amount() const override; std::vector fee() const override; std::vector mixin() const override; + std::string confirmationMessage() const override {return m_confirmationMessage;} std::vector paymentId() const override; std::vector recipientAddress() const override; + uint64_t minMixinCount() const override; uint64_t txCount() const override; - // sign txs and save to file bool sign(const std::string &signedFileName) override; - std::string confirmationMessage() const override {return m_confirmationMessage;} - uint64_t minMixinCount() const override; private: // Callback function to check all loaded tx's and generate confirmationMessage diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index 58cb849473..6518543514 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -439,6 +439,181 @@ WalletImpl::~WalletImpl() LOG_PRINT_L1(__FUNCTION__ << " finished"); } +std::string WalletImpl::seed(const std::string& seed_offset) const +{ + epee::wipeable_string seed; + if (m_wallet) + m_wallet->get_seed(seed, seed_offset); + return std::string(seed.data(), seed.size()); // TODO +} + +std::string WalletImpl::getSeedLanguage() const +{ + return m_wallet->get_seed_language(); +} + +void WalletImpl::setSeedLanguage(const std::string &arg) +{ + m_wallet->set_seed_language(arg); +} + +int WalletImpl::status() const +{ + boost::lock_guard l(m_statusMutex); + return m_status; +} + +std::string WalletImpl::errorString() const +{ + boost::lock_guard l(m_statusMutex); + return m_errorString; +} + +void WalletImpl::statusWithErrorString(int& status, std::string& errorString) const { + boost::lock_guard l(m_statusMutex); + status = m_status; + errorString = m_errorString; +} + +bool WalletImpl::setPassword(const std::string &password) +{ + clearStatus(); + try { + m_wallet->change_password(m_wallet->get_wallet_file(), m_password, password); + m_password = password; + } catch (const std::exception &e) { + setStatusError(e.what()); + } + return status() == Status_Ok; +} + +const std::string& WalletImpl::getPassword() const +{ + return m_password; +} + +bool WalletImpl::setDevicePin(const std::string &pin) +{ + clearStatus(); + try { + m_wallet->get_account().get_device().set_pin(epee::wipeable_string(pin.data(), pin.size())); + } catch (const std::exception &e) { + setStatusError(e.what()); + } + return status() == Status_Ok; +} + +bool WalletImpl::setDevicePassphrase(const std::string &passphrase) +{ + clearStatus(); + try { + m_wallet->get_account().get_device().set_passphrase(epee::wipeable_string(passphrase.data(), passphrase.size())); + } catch (const std::exception &e) { + setStatusError(e.what()); + } + return status() == Status_Ok; +} + +std::string WalletImpl::address(uint32_t accountIndex, uint32_t addressIndex) const +{ + return m_wallet->get_subaddress_as_str({accountIndex, addressIndex}); +} + +std::string WalletImpl::path() const +{ + return m_wallet->path(); +} + +void WalletImpl::hardForkInfo(uint8_t &version, uint64_t &earliest_height) const +{ + m_wallet->get_hard_fork_info(version, earliest_height); +} + +bool WalletImpl::useForkRules(uint8_t version, int64_t early_blocks) const +{ + return m_wallet->use_fork_rules(version,early_blocks); +} + +std::string WalletImpl::integratedAddress(const std::string &payment_id) const +{ + crypto::hash8 pid; + if (!tools::wallet2::parse_short_payment_id(payment_id, pid)) { + return ""; + } + return m_wallet->get_integrated_address_as_str(pid); +} + +std::string WalletImpl::secretViewKey() const +{ + return epee::string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_view_secret_key); +} + +std::string WalletImpl::publicViewKey() const +{ + return epee::string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_account_address.m_view_public_key); +} + +std::string WalletImpl::secretSpendKey() const +{ + return epee::string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_spend_secret_key); +} + +std::string WalletImpl::publicSpendKey() const +{ + return epee::string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_account_address.m_spend_public_key); +} + +std::string WalletImpl::publicMultisigSignerKey() const +{ + try { + crypto::public_key signer = m_wallet->get_multisig_signer_public_key(); + return epee::string_tools::pod_to_hex(signer); + } catch (const std::exception&) { + return ""; + } +} + +void WalletImpl::stop() +{ + m_wallet->stop(); +} + +bool WalletImpl::store(const std::string &path) +{ + clearStatus(); + try { + if (path.empty()) { + m_wallet->store(); + } else { + m_wallet->store_to(path, m_password); + } + } catch (const std::exception &e) { + LOG_ERROR("Error saving wallet: " << e.what()); + setStatusError(e.what()); + return false; + } + + return true; +} + +string WalletImpl::filename() const +{ + return m_wallet->get_wallet_file(); +} + +string WalletImpl::keysFilename() const +{ + return m_wallet->get_keys_file(); +} + +bool WalletImpl::init(const std::string &daemon_address, uint64_t upper_transaction_size_limit, const std::string &daemon_username, const std::string &daemon_password, bool use_ssl, bool lightWallet, const std::string &proxy_address) +{ + clearStatus(); + if(daemon_username != "") + m_daemon_login.emplace(daemon_username, daemon_password); + return doInit(daemon_address, proxy_address, upper_transaction_size_limit, use_ssl); +} + bool WalletImpl::create(const std::string &path, const std::string &password, const std::string &language) { @@ -542,6 +717,47 @@ bool WalletImpl::createWatchOnly(const std::string &path, const std::string &pas return true; } +bool WalletImpl::recover(const std::string &path, const std::string &seed) +{ + return recover(path, "", seed); +} + +bool WalletImpl::recover(const std::string &path, const std::string &password, const std::string &seed, const std::string &seed_offset/* = {}*/) +{ + clearStatus(); + m_errorString.clear(); + if (seed.empty()) { + LOG_ERROR("Electrum seed is empty"); + setStatusError(tr("Electrum seed is empty")); + return false; + } + + m_recoveringFromSeed = true; + m_recoveringFromDevice = false; + crypto::secret_key recovery_key; + std::string old_language; + if (!crypto::ElectrumWords::words_to_bytes(seed, recovery_key, old_language)) { + setStatusError(tr("Electrum-style word list failed verification")); + return false; + } + if (!seed_offset.empty()) + { + recovery_key = cryptonote::decrypt_key(recovery_key, seed_offset); + } + + if (old_language == crypto::ElectrumWords::old_language_name) + old_language = Language::English().get_language_name(); + + try { + m_wallet->set_seed_language(old_language); + m_wallet->generate(path, password, recovery_key, true, false); + + } catch (const std::exception &e) { + setStatusCritical(e.what()); + } + return status() == Status_Ok; +} + bool WalletImpl::recoverFromKeys(const std::string &path, const std::string &language, const std::string &address_string, @@ -664,11 +880,6 @@ bool WalletImpl::recoverFromDevice(const std::string &path, const std::string &p return true; } -Wallet::Device WalletImpl::getDeviceType() const -{ - return static_cast(m_wallet->get_device_type()); -} - bool WalletImpl::open(const std::string &path, const std::string &password) { clearStatus(); @@ -695,274 +906,123 @@ bool WalletImpl::open(const std::string &path, const std::string &password) return status() == Status_Ok; } -bool WalletImpl::recover(const std::string &path, const std::string &seed) +bool WalletImpl::close(bool store) { - return recover(path, "", seed); -} -bool WalletImpl::recover(const std::string &path, const std::string &password, const std::string &seed, const std::string &seed_offset/* = {}*/) -{ - clearStatus(); - m_errorString.clear(); - if (seed.empty()) { - LOG_ERROR("Electrum seed is empty"); - setStatusError(tr("Electrum seed is empty")); - return false; + bool result = false; + LOG_PRINT_L1("closing wallet..."); + try { + if (store) { + // Do not store wallet with invalid status + // Status Critical refers to errors on opening or creating wallets. + if (status() != Status_Critical) + m_wallet->store(); + else + LOG_ERROR("Status_Critical - not saving wallet"); + LOG_PRINT_L1("wallet::store done"); + } + LOG_PRINT_L1("Calling wallet::stop..."); + m_wallet->stop(); + LOG_PRINT_L1("wallet::stop done"); + m_wallet->deinit(); + result = true; + clearStatus(); + } catch (const std::exception &e) { + setStatusCritical(e.what()); + LOG_ERROR("Error closing wallet: " << e.what()); } + return result; +} - m_recoveringFromSeed = true; - m_recoveringFromDevice = false; - crypto::secret_key recovery_key; - std::string old_language; - if (!crypto::ElectrumWords::words_to_bytes(seed, recovery_key, old_language)) { - setStatusError(tr("Electrum-style word list failed verification")); - return false; - } - if (!seed_offset.empty()) - { - recovery_key = cryptonote::decrypt_key(recovery_key, seed_offset); - } - - if (old_language == crypto::ElectrumWords::old_language_name) - old_language = Language::English().get_language_name(); - - try { - m_wallet->set_seed_language(old_language); - m_wallet->generate(path, password, recovery_key, true, false); - - } catch (const std::exception &e) { - setStatusCritical(e.what()); - } - return status() == Status_Ok; -} - -bool WalletImpl::close(bool store) -{ - - bool result = false; - LOG_PRINT_L1("closing wallet..."); - try { - if (store) { - // Do not store wallet with invalid status - // Status Critical refers to errors on opening or creating wallets. - if (status() != Status_Critical) - m_wallet->store(); - else - LOG_ERROR("Status_Critical - not saving wallet"); - LOG_PRINT_L1("wallet::store done"); - } - LOG_PRINT_L1("Calling wallet::stop..."); - m_wallet->stop(); - LOG_PRINT_L1("wallet::stop done"); - m_wallet->deinit(); - result = true; - clearStatus(); - } catch (const std::exception &e) { - setStatusCritical(e.what()); - LOG_ERROR("Error closing wallet: " << e.what()); - } - return result; -} - -std::string WalletImpl::seed(const std::string& seed_offset) const -{ - epee::wipeable_string seed; - if (m_wallet) - m_wallet->get_seed(seed, seed_offset); - return std::string(seed.data(), seed.size()); // TODO -} - -std::string WalletImpl::getSeedLanguage() const -{ - return m_wallet->get_seed_language(); -} - -void WalletImpl::setSeedLanguage(const std::string &arg) -{ - m_wallet->set_seed_language(arg); -} - -int WalletImpl::status() const -{ - boost::lock_guard l(m_statusMutex); - return m_status; -} - -std::string WalletImpl::errorString() const -{ - boost::lock_guard l(m_statusMutex); - return m_errorString; -} - -void WalletImpl::statusWithErrorString(int& status, std::string& errorString) const { - boost::lock_guard l(m_statusMutex); - status = m_status; - errorString = m_errorString; -} - -bool WalletImpl::setPassword(const std::string &password) -{ - clearStatus(); - try { - m_wallet->change_password(m_wallet->get_wallet_file(), m_password, password); - m_password = password; - } catch (const std::exception &e) { - setStatusError(e.what()); - } - return status() == Status_Ok; -} - -const std::string& WalletImpl::getPassword() const -{ - return m_password; -} - -bool WalletImpl::setDevicePin(const std::string &pin) -{ - clearStatus(); - try { - m_wallet->get_account().get_device().set_pin(epee::wipeable_string(pin.data(), pin.size())); - } catch (const std::exception &e) { - setStatusError(e.what()); - } - return status() == Status_Ok; -} - -bool WalletImpl::setDevicePassphrase(const std::string &passphrase) -{ - clearStatus(); - try { - m_wallet->get_account().get_device().set_passphrase(epee::wipeable_string(passphrase.data(), passphrase.size())); - } catch (const std::exception &e) { - setStatusError(e.what()); - } - return status() == Status_Ok; -} - -std::string WalletImpl::address(uint32_t accountIndex, uint32_t addressIndex) const -{ - return m_wallet->get_subaddress_as_str({accountIndex, addressIndex}); -} - -std::string WalletImpl::integratedAddress(const std::string &payment_id) const -{ - crypto::hash8 pid; - if (!tools::wallet2::parse_short_payment_id(payment_id, pid)) { - return ""; - } - return m_wallet->get_integrated_address_as_str(pid); -} - -std::string WalletImpl::secretViewKey() const +void WalletImpl::setRefreshFromBlockHeight(uint64_t refresh_from_block_height) { - return epee::string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_view_secret_key); + m_wallet->set_refresh_from_block_height(refresh_from_block_height); } -std::string WalletImpl::publicViewKey() const +void WalletImpl::setRecoveringFromSeed(bool recoveringFromSeed) { - return epee::string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_account_address.m_view_public_key); + m_recoveringFromSeed = recoveringFromSeed; } -std::string WalletImpl::secretSpendKey() const +void WalletImpl::setRecoveringFromDevice(bool recoveringFromDevice) { - return epee::string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_spend_secret_key); + m_recoveringFromDevice = recoveringFromDevice; } -std::string WalletImpl::publicSpendKey() const +void WalletImpl::setSubaddressLookahead(uint32_t major, uint32_t minor) { - return epee::string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_account_address.m_spend_public_key); + m_wallet->set_subaddress_lookahead(major, minor); } -std::string WalletImpl::publicMultisigSignerKey() const +bool WalletImpl::connectToDaemon() { - try { - crypto::public_key signer = m_wallet->get_multisig_signer_public_key(); - return epee::string_tools::pod_to_hex(signer); - } catch (const std::exception&) { - return ""; + bool result = m_wallet->check_connection(NULL, NULL, DEFAULT_CONNECTION_TIMEOUT_MILLIS); + if (!result) { + setStatusError("Error connecting to daemon at " + m_wallet->get_daemon_address()); + } else { + clearStatus(); + // start refreshing here } + return result; } -std::string WalletImpl::path() const -{ - return m_wallet->path(); -} - -void WalletImpl::stop() -{ - m_wallet->stop(); -} - -bool WalletImpl::store(const std::string &path) +Wallet::ConnectionStatus WalletImpl::connected() const { - clearStatus(); - try { - if (path.empty()) { - m_wallet->store(); - } else { - m_wallet->store_to(path, m_password); - } - } catch (const std::exception &e) { - LOG_ERROR("Error saving wallet: " << e.what()); - setStatusError(e.what()); - return false; + uint32_t version = 0; + bool wallet_is_outdated = false, daemon_is_outdated = false; + m_is_connected = m_wallet->check_connection(&version, NULL, DEFAULT_CONNECTION_TIMEOUT_MILLIS, &wallet_is_outdated, &daemon_is_outdated); + if (!m_is_connected) + { + if (wallet_is_outdated || daemon_is_outdated) + return Wallet::ConnectionStatus_WrongVersion; + else + return Wallet::ConnectionStatus_Disconnected; } - - return true; -} - -string WalletImpl::filename() const -{ - return m_wallet->get_wallet_file(); -} - -string WalletImpl::keysFilename() const -{ - return m_wallet->get_keys_file(); + if ((version >> 16) != CORE_RPC_VERSION_MAJOR) + return Wallet::ConnectionStatus_WrongVersion; + return Wallet::ConnectionStatus_Connected; } -bool WalletImpl::init(const std::string &daemon_address, uint64_t upper_transaction_size_limit, const std::string &daemon_username, const std::string &daemon_password, bool use_ssl, bool lightWallet, const std::string &proxy_address) +void WalletImpl::setTrustedDaemon(bool arg) { - clearStatus(); - if(daemon_username != "") - m_daemon_login.emplace(daemon_username, daemon_password); - return doInit(daemon_address, proxy_address, upper_transaction_size_limit, use_ssl); + m_wallet->set_trusted_daemon(arg); } -void WalletImpl::setRefreshFromBlockHeight(uint64_t refresh_from_block_height) +bool WalletImpl::trustedDaemon() const { - m_wallet->set_refresh_from_block_height(refresh_from_block_height); + return m_wallet->is_trusted_daemon(); } -void WalletImpl::setRecoveringFromSeed(bool recoveringFromSeed) +bool WalletImpl::setProxy(const std::string &address) { - m_recoveringFromSeed = recoveringFromSeed; + return m_wallet->set_proxy(address); } -void WalletImpl::setRecoveringFromDevice(bool recoveringFromDevice) +uint64_t WalletImpl::balance(uint32_t accountIndex) const { - m_recoveringFromDevice = recoveringFromDevice; + return m_wallet->balance(accountIndex, false); } -void WalletImpl::setSubaddressLookahead(uint32_t major, uint32_t minor) +uint64_t WalletImpl::unlockedBalance(uint32_t accountIndex) const { - m_wallet->set_subaddress_lookahead(major, minor); + return m_wallet->unlocked_balance(accountIndex, false); } -uint64_t WalletImpl::balance(uint32_t accountIndex) const +bool WalletImpl::watchOnly() const { - return m_wallet->balance(accountIndex, false); + return m_wallet->watch_only(); } -uint64_t WalletImpl::unlockedBalance(uint32_t accountIndex) const +bool WalletImpl::isDeterministic() const { - return m_wallet->unlocked_balance(accountIndex, false); + return m_wallet->is_deterministic(); } uint64_t WalletImpl::blockChainHeight() const { return m_wallet->get_blockchain_current_height(); } + uint64_t WalletImpl::approximateBlockChainHeight() const { return m_wallet->get_approximate_blockchain_height(); @@ -1002,25 +1062,35 @@ uint64_t WalletImpl::daemonBlockChainTargetHeight() const } else { clearStatus(); } - // Target height can be 0 when daemon is synced. Use blockchain height instead. + // Target height can be 0 when daemon is synced. Use blockchain height instead. if(result == 0) result = daemonBlockChainHeight(); return result; } -bool WalletImpl::daemonSynced() const -{ - if(connected() == Wallet::ConnectionStatus_Disconnected) - return false; - uint64_t blockChainHeight = daemonBlockChainHeight(); - return (blockChainHeight >= daemonBlockChainTargetHeight() && blockChainHeight > 1); -} - bool WalletImpl::synchronized() const { return m_synchronized; } +void WalletImpl::startRefresh() +{ + if (!m_refreshEnabled) { + LOG_PRINT_L2(__FUNCTION__ << ": refresh started/resumed..."); + m_refreshEnabled = true; + m_refreshCV.notify_one(); + } +} + +void WalletImpl::pauseRefresh() +{ + LOG_PRINT_L2(__FUNCTION__ << ": refresh paused..."); + // TODO synchronize access + if (!m_refreshThreadDone) { + m_refreshEnabled = false; + } +} + bool WalletImpl::refresh() { clearStatus(); @@ -1067,195 +1137,6 @@ int WalletImpl::autoRefreshInterval() const return m_refreshIntervalMillis; } -UnsignedTransaction *WalletImpl::loadUnsignedTx(const std::string &unsigned_filename) { - clearStatus(); - UnsignedTransactionImpl * transaction = new UnsignedTransactionImpl(*this); - if (!m_wallet->load_unsigned_tx(unsigned_filename, 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(); - std::unique_ptr transaction(new PendingTransactionImpl(*this)); - - bool r = m_wallet->load_tx(fileName, transaction->m_pending_tx); - 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::exportKeyImages(const string &filename, bool all) -{ - if (m_wallet->watch_only()) - { - setStatusError(tr("Wallet is view only")); - return false; - } - - try - { - if (!m_wallet->export_key_images(filename, all)) - { - setStatusError(tr("failed to save file ") + filename); - return false; - } - } - catch (const std::exception &e) - { - LOG_ERROR("Error exporting key images: " << e.what()); - setStatusError(e.what()); - return false; - } - return true; -} - -bool WalletImpl::importKeyImages(const string &filename) -{ - if (!trustedDaemon()) { - setStatusError(tr("Key images can only be imported with a trusted daemon")); - return false; - } - try - { - uint64_t spent = 0, unspent = 0; - uint64_t height = m_wallet->import_key_images(filename, 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 (m_wallet->key_on_device()) - { - setStatusError(string(tr("Not supported on HW wallets.")) + filename); - return false; - } - - try - { - std::string data = m_wallet->export_outputs_to_str(all); - bool r = m_wallet->save_to_file(filename, data); - if (!r) - { - LOG_ERROR("Failed to save file " << filename); - setStatusError(string(tr("Failed to save file: ")) + filename); - return false; - } - } - catch (const std::exception &e) - { - LOG_ERROR("Error exporting outputs: " << e.what()); - setStatusError(string(tr("Error exporting outputs: ")) + e.what()); - return false; - } - - LOG_PRINT_L2("Outputs exported to " << filename); - return true; -} - -bool WalletImpl::importOutputs(const string &filename) -{ - if (m_wallet->key_on_device()) - { - setStatusError(string(tr("Not supported on HW wallets.")) + filename); - return false; - } - - std::string data; - bool r = m_wallet->load_from_file(filename, data); - if (!r) - { - LOG_ERROR("Failed to read file: " << filename); - setStatusError(string(tr("Failed to read file: ")) + filename); - return false; - } - - try - { - size_t n_outputs = m_wallet->import_outputs_from_str(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 &txids) -{ - if (txids.empty()) - { - setStatusError(string(tr("Failed to scan transactions: no transaction ids provided."))); - return false; - } - - // Parse and dedup args - std::unordered_set txids_u; - for (const auto &s : txids) - { - crypto::hash txid; - if (!epee::string_tools::hex_to_pod(s, txid)) - { - setStatusError(string(tr("Invalid txid specified: ")) + s); - return false; - } - txids_u.insert(txid); - } - - try - { - m_wallet->scan_tx(txids_u); - } - catch (const tools::error::wont_reprocess_recent_txs_via_untrusted_daemon &e) - { - setStatusError(e.what()); - return false; - } - catch (const std::exception &e) - { - LOG_ERROR("Failed to scan transaction: " << e.what()); - setStatusError(string(tr("Failed to scan transaction: ")) + e.what()); - return false; - } - - return true; -} - void WalletImpl::addSubaddressAccount(const std::string& label) { m_wallet->add_subaddress_account(label); @@ -1298,178 +1179,36 @@ void WalletImpl::setSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex } } -MultisigState WalletImpl::multisig() const { - MultisigState state; - const multisig::multisig_account_status ms_status{m_wallet->get_multisig_status()}; +// TODO: +// 1 - properly handle payment id (add another menthod with explicit 'payment_id' param) +// 2 - check / design how "Transaction" can be single interface +// (instead of few different data structures within wallet2 implementation: +// - pending_tx; +// - transfer_details; +// - payment_details; +// - unconfirmed_transfer_details; +// - confirmed_transfer_details) - state.isMultisig = ms_status.multisig_is_active; - state.kexIsDone = ms_status.kex_is_done; - state.isReady = ms_status.is_ready; - state.threshold = ms_status.threshold; - state.total = ms_status.total; +PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector &dst_addr, const string &payment_id, optional> amount, uint32_t mixin_count, PendingTransaction::Priority priority, uint32_t subaddr_account, std::set subaddr_indices) - return state; -} +{ + clearStatus(); + // Pause refresh thread while creating transaction + pauseRefresh(); -string WalletImpl::getMultisigInfo() const { - try { - clearStatus(); - return m_wallet->get_multisig_first_kex_msg(); - } catch (const exception& e) { - LOG_ERROR("Error on generating multisig info: " << e.what()); - setStatusError(string(tr("Failed to get multisig info: ")) + e.what()); - } + cryptonote::address_parse_info info; - return string(); -} + uint32_t adjusted_priority = m_wallet->adjust_priority(static_cast(priority)); -string WalletImpl::makeMultisig(const vector& info, const uint32_t threshold) { - try { - clearStatus(); + PendingTransactionImpl * transaction = new PendingTransactionImpl(*this); - if (m_wallet->get_multisig_status().multisig_is_active) { - throw runtime_error("Wallet is already multisig"); - } - - return m_wallet->make_multisig(epee::wipeable_string(m_password), info, threshold); - } catch (const exception& e) { - LOG_ERROR("Error on making multisig wallet: " << e.what()); - setStatusError(string(tr("Failed to make multisig: ")) + e.what()); - } - - return string(); -} - -std::string WalletImpl::exchangeMultisigKeys(const std::vector &info, const bool force_update_use_with_caution /*= false*/) { - try { - clearStatus(); - checkMultisigWalletNotReady(m_wallet); - - return m_wallet->exchange_multisig_keys(epee::wipeable_string(m_password), info, force_update_use_with_caution); - } catch (const exception& e) { - LOG_ERROR("Error on exchanging multisig keys: " << e.what()); - setStatusError(string(tr("Failed to exchange multisig keys: ")) + e.what()); - } - - return string(); -} - -bool WalletImpl::exportMultisigImages(string& images) { - try { - clearStatus(); - checkMultisigWalletReady(m_wallet); - - auto blob = m_wallet->export_multisig(); - images = epee::string_tools::buff_to_hex_nodelimer(blob); - return true; - } catch (const exception& e) { - LOG_ERROR("Error on exporting multisig images: " << e.what()); - setStatusError(string(tr("Failed to export multisig images: ")) + e.what()); - } - - return false; -} - -size_t WalletImpl::importMultisigImages(const vector& images) { - try { - clearStatus(); - checkMultisigWalletReady(m_wallet); - - std::vector blobs; - blobs.reserve(images.size()); - - for (const auto& image: images) { - std::string blob; - if (!epee::string_tools::parse_hexstr_to_binbuff(image, blob)) { - LOG_ERROR("Failed to parse imported multisig images"); - setStatusError(tr("Failed to parse imported multisig images")); - return 0; - } - - blobs.emplace_back(std::move(blob)); - } - - return m_wallet->import_multisig(blobs); - } catch (const exception& e) { - LOG_ERROR("Error on importing multisig images: " << e.what()); - setStatusError(string(tr("Failed to import multisig images: ")) + e.what()); - } - - return 0; -} - -bool WalletImpl::hasMultisigPartialKeyImages() const { - try { - clearStatus(); - checkMultisigWalletReady(m_wallet); - - return m_wallet->has_multisig_partial_key_images(); - } catch (const exception& e) { - LOG_ERROR("Error on checking for partial multisig key images: " << e.what()); - setStatusError(string(tr("Failed to check for partial multisig key images: ")) + e.what()); - } - - return false; -} - -PendingTransaction* WalletImpl::restoreMultisigTransaction(const string& signData) { - try { - clearStatus(); - checkMultisigWalletReady(m_wallet); - - string binary; - if (!epee::string_tools::parse_hexstr_to_binbuff(signData, binary)) { - throw runtime_error("Failed to deserialize multisig transaction"); - } - - tools::wallet2::multisig_tx_set txSet; - if (!m_wallet->load_multisig_tx(binary, txSet, {})) { - throw runtime_error("couldn't parse multisig transaction data"); - } - - auto ptx = new PendingTransactionImpl(*this); - ptx->m_pending_tx = txSet.m_ptx; - ptx->m_signers = txSet.m_signers; - - return ptx; - } catch (exception& e) { - LOG_ERROR("Error on restoring multisig transaction: " << e.what()); - setStatusError(string(tr("Failed to restore multisig transaction: ")) + e.what()); - } - - return nullptr; -} - -// TODO: -// 1 - properly handle payment id (add another menthod with explicit 'payment_id' param) -// 2 - check / design how "Transaction" can be single interface -// (instead of few different data structures within wallet2 implementation: -// - pending_tx; -// - transfer_details; -// - payment_details; -// - unconfirmed_transfer_details; -// - confirmed_transfer_details) - -PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector &dst_addr, const string &payment_id, optional> amount, uint32_t mixin_count, PendingTransaction::Priority priority, uint32_t subaddr_account, std::set subaddr_indices) - -{ - clearStatus(); - // Pause refresh thread while creating transaction - pauseRefresh(); - - cryptonote::address_parse_info info; - - uint32_t adjusted_priority = m_wallet->adjust_priority(static_cast(priority)); - - PendingTransactionImpl * transaction = new PendingTransactionImpl(*this); - - do { - std::vector extra; - std::string extra_nonce; - vector dsts; - if (!amount && dst_addr.size() > 1) { - setStatusError(tr("Sending all requires one destination address")); - break; + do { + std::vector extra; + std::string extra_nonce; + vector dsts; + if (!amount && dst_addr.size() > 1) { + setStatusError(tr("Sending all requires one destination address")); + break; } if (amount && (dst_addr.size() != (*amount).size())) { setStatusError(tr("Destinations and amounts are unequal")); @@ -1703,6 +1442,45 @@ PendingTransaction *WalletImpl::createSweepUnmixableTransaction() return transaction; } +UnsignedTransaction *WalletImpl::loadUnsignedTx(const std::string &unsigned_filename) { + clearStatus(); + UnsignedTransactionImpl * transaction = new UnsignedTransactionImpl(*this); + if (!m_wallet->load_unsigned_tx(unsigned_filename, 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(); + std::unique_ptr transaction(new PendingTransactionImpl(*this)); + + bool r = m_wallet->load_tx(fileName, transaction->m_pending_tx); + 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; +} + void WalletImpl::disposeTransaction(PendingTransaction *t) { delete t; @@ -1730,35 +1508,185 @@ uint64_t WalletImpl::estimateTransactionFee(const std::vectorget_fee_quantization_mask()); } -TransactionHistory *WalletImpl::history() +bool WalletImpl::exportKeyImages(const string &filename, bool all) { - return m_history.get(); -} + if (m_wallet->watch_only()) + { + setStatusError(tr("Wallet is view only")); + return false; + } -AddressBook *WalletImpl::addressBook() -{ - return m_addressBook.get(); + try + { + if (!m_wallet->export_key_images(filename, all)) + { + setStatusError(tr("failed to save file ") + filename); + return false; + } + } + catch (const std::exception &e) + { + LOG_ERROR("Error exporting key images: " << e.what()); + setStatusError(e.what()); + return false; + } + return true; } -Subaddress *WalletImpl::subaddress() +bool WalletImpl::importKeyImages(const string &filename) { - return m_subaddress.get(); -} + if (!trustedDaemon()) { + setStatusError(tr("Key images can only be imported with a trusted daemon")); + return false; + } + try + { + uint64_t spent = 0, unspent = 0; + uint64_t height = m_wallet->import_key_images(filename, 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; + } -SubaddressAccount *WalletImpl::subaddressAccount() -{ - return m_subaddressAccount.get(); + return true; } -void WalletImpl::setListener(WalletListener *l) +bool WalletImpl::exportOutputs(const string &filename, bool all) { - // TODO thread synchronization; - m_wallet2Callback->setListener(l); -} + if (m_wallet->key_on_device()) + { + setStatusError(string(tr("Not supported on HW wallets.")) + filename); + return false; + } -uint32_t WalletImpl::defaultMixin() const -{ - return m_wallet->default_mixin(); + try + { + std::string data = m_wallet->export_outputs_to_str(all); + bool r = m_wallet->save_to_file(filename, data); + if (!r) + { + LOG_ERROR("Failed to save file " << filename); + setStatusError(string(tr("Failed to save file: ")) + filename); + return false; + } + } + catch (const std::exception &e) + { + LOG_ERROR("Error exporting outputs: " << e.what()); + setStatusError(string(tr("Error exporting outputs: ")) + e.what()); + return false; + } + + LOG_PRINT_L2("Outputs exported to " << filename); + return true; +} + +bool WalletImpl::importOutputs(const string &filename) +{ + if (m_wallet->key_on_device()) + { + setStatusError(string(tr("Not supported on HW wallets.")) + filename); + return false; + } + + std::string data; + bool r = m_wallet->load_from_file(filename, data); + if (!r) + { + LOG_ERROR("Failed to read file: " << filename); + setStatusError(string(tr("Failed to read file: ")) + filename); + return false; + } + + try + { + size_t n_outputs = m_wallet->import_outputs_from_str(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 &txids) +{ + if (txids.empty()) + { + setStatusError(string(tr("Failed to scan transactions: no transaction ids provided."))); + return false; + } + + // Parse and dedup args + std::unordered_set txids_u; + for (const auto &s : txids) + { + crypto::hash txid; + if (!epee::string_tools::hex_to_pod(s, txid)) + { + setStatusError(string(tr("Invalid txid specified: ")) + s); + return false; + } + txids_u.insert(txid); + } + + try + { + m_wallet->scan_tx(txids_u); + } + catch (const tools::error::wont_reprocess_recent_txs_via_untrusted_daemon &e) + { + setStatusError(e.what()); + return false; + } + catch (const std::exception &e) + { + LOG_ERROR("Failed to scan transaction: " << e.what()); + setStatusError(string(tr("Failed to scan transaction: ")) + e.what()); + return false; + } + + return true; +} + +TransactionHistory *WalletImpl::history() +{ + return m_history.get(); +} + +AddressBook *WalletImpl::addressBook() +{ + return m_addressBook.get(); +} + +Subaddress *WalletImpl::subaddress() +{ + return m_subaddress.get(); +} + +SubaddressAccount *WalletImpl::subaddressAccount() +{ + return m_subaddressAccount.get(); +} + +void WalletImpl::setListener(WalletListener *l) +{ + // TODO thread synchronization; + m_wallet2Callback->setListener(l); +} + +uint32_t WalletImpl::defaultMixin() const +{ + return m_wallet->default_mixin(); } void WalletImpl::setDefaultMixin(uint32_t arg) @@ -2058,544 +1986,611 @@ bool WalletImpl::verifySignedMessage(const std::string &message, const std::stri return m_wallet->verify(message, info.address, signature).valid; } -std::string WalletImpl::signMultisigParticipant(const std::string &message) const -{ - clearStatus(); - - const multisig::multisig_account_status ms_status{m_wallet->get_multisig_status()}; - if (!ms_status.multisig_is_active || !ms_status.is_ready) { - m_status = Status_Error; - m_errorString = tr("The wallet must be in multisig ready state"); - return {}; - } - - try { - return m_wallet->sign_multisig_participant(message); - } catch (const std::exception& e) { - m_status = Status_Error; - m_errorString = e.what(); - } - - return {}; -} - -bool WalletImpl::verifyMessageWithPublicKey(const std::string &message, const std::string &publicKey, const std::string &signature) const +bool WalletImpl::parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector &unknown_parameters, std::string &error) { - clearStatus(); - - cryptonote::blobdata pkeyData; - if(!epee::string_tools::parse_hexstr_to_binbuff(publicKey, pkeyData) || pkeyData.size() != sizeof(crypto::public_key)) - { - m_status = Status_Error; - m_errorString = tr("Given string is not a key"); - return false; - } - - try { - crypto::public_key pkey = *reinterpret_cast(pkeyData.data()); - return m_wallet->verify_with_public_key(message, pkey, signature); - } catch (const std::exception& e) { - m_status = Status_Error; - m_errorString = e.what(); - } - - return false; + return m_wallet->parse_uri(uri, address, payment_id, amount, tx_description, recipient_name, unknown_parameters, error); } -bool WalletImpl::connectToDaemon() +std::string WalletImpl::make_uri(const std::string &address, const std::string &payment_id, uint64_t amount, const std::string &tx_description, const std::string &recipient_name, std::string &error) const { - bool result = m_wallet->check_connection(NULL, NULL, DEFAULT_CONNECTION_TIMEOUT_MILLIS); - if (!result) { - setStatusError("Error connecting to daemon at " + m_wallet->get_daemon_address()); - } else { - clearStatus(); - // start refreshing here - } - return result; + return m_wallet->make_uri(address, payment_id, amount, tx_description, recipient_name, error); } -Wallet::ConnectionStatus WalletImpl::connected() const +std::string WalletImpl::getDefaultDataDir() const { - uint32_t version = 0; - bool wallet_is_outdated = false, daemon_is_outdated = false; - m_is_connected = m_wallet->check_connection(&version, NULL, DEFAULT_CONNECTION_TIMEOUT_MILLIS, &wallet_is_outdated, &daemon_is_outdated); - if (!m_is_connected) - { - if (wallet_is_outdated || daemon_is_outdated) - return Wallet::ConnectionStatus_WrongVersion; - else - return Wallet::ConnectionStatus_Disconnected; - } - if ((version >> 16) != CORE_RPC_VERSION_MAJOR) - return Wallet::ConnectionStatus_WrongVersion; - return Wallet::ConnectionStatus_Connected; + return tools::get_default_data_dir(); } -void WalletImpl::setTrustedDaemon(bool arg) +bool WalletImpl::rescanSpent() { - m_wallet->set_trusted_daemon(arg); + clearStatus(); + if (!trustedDaemon()) { + setStatusError(tr("Rescan spent can only be used with a trusted daemon")); + return false; + } + try { + m_wallet->rescan_spent(); + } catch (const std::exception &e) { + LOG_ERROR(__FUNCTION__ << " error: " << e.what()); + setStatusError(e.what()); + return false; + } + return true; } -bool WalletImpl::trustedDaemon() const +void WalletImpl::setOffline(bool offline) { - return m_wallet->is_trusted_daemon(); + m_wallet->set_offline(offline); } -bool WalletImpl::setProxy(const std::string &address) +bool WalletImpl::isOffline() const { - return m_wallet->set_proxy(address); + return m_wallet->is_offline(); } -bool WalletImpl::watchOnly() const +bool WalletImpl::blackballOutputs(const std::vector &outputs, bool add) { - return m_wallet->watch_only(); + std::vector> raw_outputs; + raw_outputs.reserve(outputs.size()); + uint64_t amount = std::numeric_limits::max(), offset, num_offsets; + for (const std::string &str: outputs) + { + if (sscanf(str.c_str(), "@%" PRIu64, &amount) == 1) + continue; + if (amount == std::numeric_limits::max()) + { + setStatusError("First line is not an amount"); + return true; + } + if (sscanf(str.c_str(), "%" PRIu64 "*%" PRIu64, &offset, &num_offsets) == 2 && num_offsets <= std::numeric_limits::max() - offset) + { + while (num_offsets--) + raw_outputs.push_back(std::make_pair(amount, offset++)); + } + else if (sscanf(str.c_str(), "%" PRIu64, &offset) == 1) + { + raw_outputs.push_back(std::make_pair(amount, offset)); + } + else + { + setStatusError(tr("Invalid output: ") + str); + return false; + } + } + bool ret = m_wallet->set_blackballed_outputs(raw_outputs, add); + if (!ret) + { + setStatusError(tr("Failed to mark outputs as spent")); + return false; + } + return true; } -bool WalletImpl::isDeterministic() const +bool WalletImpl::blackballOutput(const std::string &amount, const std::string &offset) { - return m_wallet->is_deterministic(); + uint64_t raw_amount, raw_offset; + if (!epee::string_tools::get_xtype_from_string(raw_amount, amount)) + { + setStatusError(tr("Failed to parse output amount")); + return false; + } + if (!epee::string_tools::get_xtype_from_string(raw_offset, offset)) + { + setStatusError(tr("Failed to parse output offset")); + return false; + } + bool ret = m_wallet->blackball_output(std::make_pair(raw_amount, raw_offset)); + if (!ret) + { + setStatusError(tr("Failed to mark output as spent")); + return false; + } + return true; } -void WalletImpl::clearStatus() const +bool WalletImpl::unblackballOutput(const std::string &amount, const std::string &offset) { - boost::lock_guard l(m_statusMutex); - m_status = Status_Ok; - m_errorString.clear(); + uint64_t raw_amount, raw_offset; + if (!epee::string_tools::get_xtype_from_string(raw_amount, amount)) + { + setStatusError(tr("Failed to parse output amount")); + return false; + } + if (!epee::string_tools::get_xtype_from_string(raw_offset, offset)) + { + setStatusError(tr("Failed to parse output offset")); + return false; + } + bool ret = m_wallet->unblackball_output(std::make_pair(raw_amount, raw_offset)); + if (!ret) + { + setStatusError(tr("Failed to mark output as unspent")); + return false; + } + return true; } -void WalletImpl::setStatusError(const std::string& message) const +bool WalletImpl::getRing(const std::string &key_image, std::vector &ring) const { - setStatus(Status_Error, message); + crypto::key_image raw_key_image; + if (!epee::string_tools::hex_to_pod(key_image, raw_key_image)) + { + setStatusError(tr("Failed to parse key image")); + return false; + } + bool ret = m_wallet->get_ring(raw_key_image, ring); + if (!ret) + { + setStatusError(tr("Failed to get ring")); + return false; + } + return true; } -void WalletImpl::setStatusCritical(const std::string& message) const +bool WalletImpl::getRings(const std::string &txid, std::vector>> &rings) const { - setStatus(Status_Critical, message); + crypto::hash raw_txid; + if (!epee::string_tools::hex_to_pod(txid, raw_txid)) + { + setStatusError(tr("Failed to parse txid")); + return false; + } + std::vector>> raw_rings; + bool ret = m_wallet->get_rings(raw_txid, raw_rings); + if (!ret) + { + setStatusError(tr("Failed to get rings")); + return false; + } + for (const auto &r: raw_rings) + { + rings.push_back(std::make_pair(epee::string_tools::pod_to_hex(r.first), r.second)); + } + return true; } -void WalletImpl::setStatus(int status, const std::string& message) const +bool WalletImpl::setRing(const std::string &key_image, const std::vector &ring, bool relative) { - boost::lock_guard l(m_statusMutex); - m_status = status; - m_errorString = message; + crypto::key_image raw_key_image; + if (!epee::string_tools::hex_to_pod(key_image, raw_key_image)) + { + setStatusError(tr("Failed to parse key image")); + return false; + } + bool ret = m_wallet->set_ring(raw_key_image, ring, relative); + if (!ret) + { + setStatusError(tr("Failed to set ring")); + return false; + } + return true; } -void WalletImpl::refreshThreadFunc() +void WalletImpl::segregatePreForkOutputs(bool segregate) { - LOG_PRINT_L3(__FUNCTION__ << ": starting refresh thread"); - - while (true) { - boost::mutex::scoped_lock lock(m_refreshMutex); - if (m_refreshThreadDone) { - break; - } - LOG_PRINT_L3(__FUNCTION__ << ": waiting for refresh..."); - // if auto refresh enabled, we wait for the "m_refreshIntervalSeconds" interval. - // if not - we wait forever - if (m_refreshIntervalMillis > 0) { - boost::posix_time::milliseconds wait_for_ms(m_refreshIntervalMillis.load()); - m_refreshCV.timed_wait(lock, wait_for_ms); - } else { - m_refreshCV.wait(lock); - } - - LOG_PRINT_L3(__FUNCTION__ << ": refresh lock acquired..."); - LOG_PRINT_L3(__FUNCTION__ << ": m_refreshEnabled: " << m_refreshEnabled); - LOG_PRINT_L3(__FUNCTION__ << ": m_status: " << status()); - LOG_PRINT_L3(__FUNCTION__ << ": m_refreshShouldRescan: " << m_refreshShouldRescan); - if (m_refreshEnabled) { - LOG_PRINT_L3(__FUNCTION__ << ": refreshing..."); - doRefresh(); - } - } - LOG_PRINT_L3(__FUNCTION__ << ": refresh thread stopped"); + m_wallet->segregate_pre_fork_outputs(segregate); } -void WalletImpl::doRefresh() +void WalletImpl::segregationHeight(uint64_t height) { - bool rescan = m_refreshShouldRescan.exchange(false); - // synchronizing async and sync refresh calls - boost::lock_guard guarg(m_refreshMutex2); - do try { - LOG_PRINT_L3(__FUNCTION__ << ": doRefresh, rescan = "<rescan_blockchain(false); - m_wallet->refresh(trustedDaemon()); - if (!m_synchronized) { - m_synchronized = true; - } - // assuming if we have empty history, it wasn't initialized yet - // for further history changes client need to update history in - // "on_money_received" and "on_money_sent" callbacks - if (m_history->count() == 0) { - m_history->refresh(); - } - m_wallet->find_and_save_rings(false); - } else { - LOG_PRINT_L3(__FUNCTION__ << ": skipping refresh - daemon is not synced"); - } - } catch (const std::exception &e) { - setStatusError(e.what()); - break; - }while(!rescan && (rescan=m_refreshShouldRescan.exchange(false))); // repeat if not rescanned and rescan was requested - - if (m_wallet2Callback->getListener()) { - m_wallet2Callback->getListener()->refreshed(); - } + m_wallet->segregation_height(height); } - -void WalletImpl::startRefresh() +void WalletImpl::keyReuseMitigation2(bool mitigation) { - if (!m_refreshEnabled) { - LOG_PRINT_L2(__FUNCTION__ << ": refresh started/resumed..."); - m_refreshEnabled = true; - m_refreshCV.notify_one(); - } + m_wallet->key_reuse_mitigation2(mitigation); } - - -void WalletImpl::stopRefresh() +bool WalletImpl::lockKeysFile() { - if (!m_refreshThreadDone) { - m_refreshEnabled = false; - m_refreshThreadDone = true; - m_refreshCV.notify_one(); - m_refreshThread.join(); - } + return m_wallet->lock_keys_file(); } -void WalletImpl::pauseRefresh() +bool WalletImpl::unlockKeysFile() { - LOG_PRINT_L2(__FUNCTION__ << ": refresh paused..."); - // TODO synchronize access - if (!m_refreshThreadDone) { - m_refreshEnabled = false; - } + return m_wallet->unlock_keys_file(); } +bool WalletImpl::isKeysFileLocked() +{ + return m_wallet->is_keys_file_locked(); +} -bool WalletImpl::isNewWallet() const +Wallet::Device WalletImpl::getDeviceType() const { - // in case wallet created without daemon connection, closed and opened again, - // it's the same case as if it created from scratch, i.e. we need "fast sync" - // with the daemon (pull hashes instead of pull blocks). - // If wallet cache is rebuilt, creation height stored in .keys is used. - // Watch only wallet is a copy of an existing wallet. - return !(blockChainHeight() > 1 || m_recoveringFromSeed || m_recoveringFromDevice || m_rebuildWalletCache) && !watchOnly(); + return static_cast(m_wallet->get_device_type()); } -void WalletImpl::pendingTxPostProcess(PendingTransactionImpl * pending) +uint64_t WalletImpl::coldKeyImageSync(uint64_t &spent, uint64_t &unspent) { - // If the device being used is HW device with cold signing protocol, cold sign then. - if (!m_wallet->get_account().get_device().has_tx_cold_sign()){ - return; - } + return m_wallet->cold_key_image_sync(spent, unspent); +} - tools::wallet2::signed_tx_set exported_txs; - std::vector dsts_info; +void WalletImpl::deviceShowAddress(uint32_t accountIndex, uint32_t addressIndex, const std::string &paymentId) +{ + boost::optional payment_id_param = boost::none; + if (!paymentId.empty()) + { + crypto::hash8 payment_id; + bool res = tools::wallet2::parse_short_payment_id(paymentId, payment_id); + if (!res) + { + throw runtime_error("Invalid payment ID"); + } + payment_id_param = payment_id; + } - m_wallet->cold_sign_tx(pending->m_pending_tx, exported_txs, dsts_info, pending->m_tx_device_aux); - pending->m_key_images = exported_txs.key_images; - pending->m_pending_tx = exported_txs.ptx; + m_wallet->device_show_address(accountIndex, addressIndex, payment_id_param); } -bool WalletImpl::doInit(const string &daemon_address, const std::string &proxy_address, uint64_t upper_transaction_size_limit, bool ssl) +bool WalletImpl::reconnectDevice() { - if (!m_wallet->init(daemon_address, m_daemon_login, proxy_address, upper_transaction_size_limit)) - return false; + clearStatus(); - // in case new wallet, this will force fast-refresh (pulling hashes instead of blocks) - // If daemon isn't synced a calculated block height will be used instead - if (isNewWallet() && daemonSynced()) { - LOG_PRINT_L2(__FUNCTION__ << ":New Wallet - fast refresh until " << daemonBlockChainHeight()); - m_wallet->set_refresh_from_block_height(daemonBlockChainHeight()); + bool r; + try { + r = m_wallet->reconnect_device(); } - - if (m_rebuildWalletCache) - LOG_PRINT_L2(__FUNCTION__ << ": Rebuilding wallet cache, fast refresh until block " << m_wallet->get_refresh_from_block_height()); - - if (Utils::isAddressLocal(daemon_address)) { - this->setTrustedDaemon(true); - m_refreshIntervalMillis = DEFAULT_REFRESH_INTERVAL_MILLIS; - } else { - this->setTrustedDaemon(false); - m_refreshIntervalMillis = DEFAULT_REMOTE_NODE_REFRESH_INTERVAL_MILLIS; + catch (const std::exception &e) { + LOG_ERROR(__FUNCTION__ << " error: " << e.what()); + setStatusError(e.what()); + return false; } - return true; -} -bool WalletImpl::parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector &unknown_parameters, std::string &error) -{ - return m_wallet->parse_uri(uri, address, payment_id, amount, tx_description, recipient_name, unknown_parameters, error); + return r; } -std::string WalletImpl::make_uri(const std::string &address, const std::string &payment_id, uint64_t amount, const std::string &tx_description, const std::string &recipient_name, std::string &error) const +uint64_t WalletImpl::getBytesReceived() { - return m_wallet->make_uri(address, payment_id, amount, tx_description, recipient_name, error); + return m_wallet->get_bytes_received(); } -std::string WalletImpl::getDefaultDataDir() const +uint64_t WalletImpl::getBytesSent() { - return tools::get_default_data_dir(); + return m_wallet->get_bytes_sent(); } -bool WalletImpl::rescanSpent() -{ - clearStatus(); - if (!trustedDaemon()) { - setStatusError(tr("Rescan spent can only be used with a trusted daemon")); - return false; - } - try { - m_wallet->rescan_spent(); - } catch (const std::exception &e) { - LOG_ERROR(__FUNCTION__ << " error: " << e.what()); - setStatusError(e.what()); - return false; - } - return true; -} -void WalletImpl::setOffline(bool offline) -{ - m_wallet->set_offline(offline); -} -bool WalletImpl::isOffline() const -{ - return m_wallet->is_offline(); -} +MultisigState WalletImpl::multisig() const { + MultisigState state; + state.isMultisig = m_wallet->multisig(&state.isReady, &state.threshold, &state.total); -void WalletImpl::hardForkInfo(uint8_t &version, uint64_t &earliest_height) const -{ - m_wallet->get_hard_fork_info(version, earliest_height); + return state; } -bool WalletImpl::useForkRules(uint8_t version, int64_t early_blocks) const -{ - return m_wallet->use_fork_rules(version,early_blocks); +string WalletImpl::getMultisigInfo() const { + try { + clearStatus(); + return m_wallet->get_multisig_first_kex_msg(); + } catch (const exception& e) { + LOG_ERROR("Error on generating multisig info: " << e.what()); + setStatusError(string(tr("Failed to get multisig info: ")) + e.what()); + } + + return string(); } -bool WalletImpl::blackballOutputs(const std::vector &outputs, bool add) -{ - std::vector> raw_outputs; - raw_outputs.reserve(outputs.size()); - uint64_t amount = std::numeric_limits::max(), offset, num_offsets; - for (const std::string &str: outputs) - { - if (sscanf(str.c_str(), "@%" PRIu64, &amount) == 1) - continue; - if (amount == std::numeric_limits::max()) - { - setStatusError("First line is not an amount"); - return true; - } - if (sscanf(str.c_str(), "%" PRIu64 "*%" PRIu64, &offset, &num_offsets) == 2 && num_offsets <= std::numeric_limits::max() - offset) - { - while (num_offsets--) - raw_outputs.push_back(std::make_pair(amount, offset++)); - } - else if (sscanf(str.c_str(), "%" PRIu64, &offset) == 1) - { - raw_outputs.push_back(std::make_pair(amount, offset)); - } - else - { - setStatusError(tr("Invalid output: ") + str); - return false; +string WalletImpl::makeMultisig(const vector& info, const uint32_t threshold) { + try { + clearStatus(); + + if (m_wallet->multisig()) { + throw runtime_error("Wallet is already multisig"); } + + return m_wallet->make_multisig(epee::wipeable_string(m_password), info, threshold); + } catch (const exception& e) { + LOG_ERROR("Error on making multisig wallet: " << e.what()); + setStatusError(string(tr("Failed to make multisig: ")) + e.what()); } - bool ret = m_wallet->set_blackballed_outputs(raw_outputs, add); - if (!ret) - { - setStatusError(tr("Failed to mark outputs as spent")); - return false; - } - return true; + + return string(); } -bool WalletImpl::blackballOutput(const std::string &amount, const std::string &offset) -{ - uint64_t raw_amount, raw_offset; - if (!epee::string_tools::get_xtype_from_string(raw_amount, amount)) - { - setStatusError(tr("Failed to parse output amount")); - return false; - } - if (!epee::string_tools::get_xtype_from_string(raw_offset, offset)) - { - setStatusError(tr("Failed to parse output offset")); - return false; - } - bool ret = m_wallet->blackball_output(std::make_pair(raw_amount, raw_offset)); - if (!ret) - { - setStatusError(tr("Failed to mark output as spent")); - return false; +std::string WalletImpl::exchangeMultisigKeys(const std::vector &info, const bool force_update_use_with_caution /*= false*/) { + try { + clearStatus(); + checkMultisigWalletNotReady(m_wallet); + + return m_wallet->exchange_multisig_keys(epee::wipeable_string(m_password), info, force_update_use_with_caution); + } catch (const exception& e) { + LOG_ERROR("Error on exchanging multisig keys: " << e.what()); + setStatusError(string(tr("Failed to exchange multisig keys: ")) + e.what()); } - return true; + + return string(); } -bool WalletImpl::unblackballOutput(const std::string &amount, const std::string &offset) -{ - uint64_t raw_amount, raw_offset; - if (!epee::string_tools::get_xtype_from_string(raw_amount, amount)) - { - setStatusError(tr("Failed to parse output amount")); - return false; +bool WalletImpl::exportMultisigImages(string& images) { + try { + clearStatus(); + checkMultisigWalletReady(m_wallet); + + auto blob = m_wallet->export_multisig(); + images = epee::string_tools::buff_to_hex_nodelimer(blob); + return true; + } catch (const exception& e) { + LOG_ERROR("Error on exporting multisig images: " << e.what()); + setStatusError(string(tr("Failed to export multisig images: ")) + e.what()); } - if (!epee::string_tools::get_xtype_from_string(raw_offset, offset)) - { - setStatusError(tr("Failed to parse output offset")); - return false; + + return false; +} + +size_t WalletImpl::importMultisigImages(const vector& images) { + try { + clearStatus(); + checkMultisigWalletReady(m_wallet); + + std::vector blobs; + blobs.reserve(images.size()); + + for (const auto& image: images) { + std::string blob; + if (!epee::string_tools::parse_hexstr_to_binbuff(image, blob)) { + LOG_ERROR("Failed to parse imported multisig images"); + setStatusError(tr("Failed to parse imported multisig images")); + return 0; + } + + blobs.emplace_back(std::move(blob)); + } + + return m_wallet->import_multisig(blobs); + } catch (const exception& e) { + LOG_ERROR("Error on importing multisig images: " << e.what()); + setStatusError(string(tr("Failed to import multisig images: ")) + e.what()); } - bool ret = m_wallet->unblackball_output(std::make_pair(raw_amount, raw_offset)); - if (!ret) - { - setStatusError(tr("Failed to mark output as unspent")); - return false; + + return 0; +} + +bool WalletImpl::hasMultisigPartialKeyImages() const { + try { + clearStatus(); + checkMultisigWalletReady(m_wallet); + + return m_wallet->has_multisig_partial_key_images(); + } catch (const exception& e) { + LOG_ERROR("Error on checking for partial multisig key images: " << e.what()); + setStatusError(string(tr("Failed to check for partial multisig key images: ")) + e.what()); } - return true; + + return false; } -bool WalletImpl::getRing(const std::string &key_image, std::vector &ring) const -{ - crypto::key_image raw_key_image; - if (!epee::string_tools::hex_to_pod(key_image, raw_key_image)) - { - setStatusError(tr("Failed to parse key image")); - return false; - } - bool ret = m_wallet->get_ring(raw_key_image, ring); - if (!ret) - { - setStatusError(tr("Failed to get ring")); - return false; +PendingTransaction* WalletImpl::restoreMultisigTransaction(const string& signData) { + try { + clearStatus(); + checkMultisigWalletReady(m_wallet); + + string binary; + if (!epee::string_tools::parse_hexstr_to_binbuff(signData, binary)) { + throw runtime_error("Failed to deserialize multisig transaction"); + } + + tools::wallet2::multisig_tx_set txSet; + if (!m_wallet->load_multisig_tx(binary, txSet, {})) { + throw runtime_error("couldn't parse multisig transaction data"); + } + + auto ptx = new PendingTransactionImpl(*this); + ptx->m_pending_tx = txSet.m_ptx; + ptx->m_signers = txSet.m_signers; + + return ptx; + } catch (exception& e) { + LOG_ERROR("Error on restoring multisig transaction: " << e.what()); + setStatusError(string(tr("Failed to restore multisig transaction: ")) + e.what()); } - return true; + + return nullptr; } -bool WalletImpl::getRings(const std::string &txid, std::vector>> &rings) const +std::string WalletImpl::signMultisigParticipant(const std::string &message) const { - crypto::hash raw_txid; - if (!epee::string_tools::hex_to_pod(txid, raw_txid)) - { - setStatusError(tr("Failed to parse txid")); - return false; - } - std::vector>> raw_rings; - bool ret = m_wallet->get_rings(raw_txid, raw_rings); - if (!ret) - { - setStatusError(tr("Failed to get rings")); - return false; + clearStatus(); + + bool ready = false; + if (!m_wallet->multisig(&ready) || !ready) { + m_status = Status_Error; + m_errorString = tr("The wallet must be in multisig ready state"); + return {}; } - for (const auto &r: raw_rings) - { - rings.push_back(std::make_pair(epee::string_tools::pod_to_hex(r.first), r.second)); + + try { + return m_wallet->sign_multisig_participant(message); + } catch (const std::exception& e) { + m_status = Status_Error; + m_errorString = e.what(); } - return true; + + return {}; } -bool WalletImpl::setRing(const std::string &key_image, const std::vector &ring, bool relative) +bool WalletImpl::verifyMessageWithPublicKey(const std::string &message, const std::string &publicKey, const std::string &signature) const { - crypto::key_image raw_key_image; - if (!epee::string_tools::hex_to_pod(key_image, raw_key_image)) + clearStatus(); + + cryptonote::blobdata pkeyData; + if(!epee::string_tools::parse_hexstr_to_binbuff(publicKey, pkeyData) || pkeyData.size() != sizeof(crypto::public_key)) { - setStatusError(tr("Failed to parse key image")); + m_status = Status_Error; + m_errorString = tr("Given string is not a key"); return false; } - bool ret = m_wallet->set_ring(raw_key_image, ring, relative); - if (!ret) - { - setStatusError(tr("Failed to set ring")); - return false; + + try { + crypto::public_key pkey = *reinterpret_cast(pkeyData.data()); + return m_wallet->verify_with_public_key(message, pkey, signature); + } catch (const std::exception& e) { + m_status = Status_Error; + m_errorString = e.what(); } - return true; + + return false; } -void WalletImpl::segregatePreForkOutputs(bool segregate) + + +void WalletImpl::clearStatus() const { - m_wallet->segregate_pre_fork_outputs(segregate); + boost::lock_guard l(m_statusMutex); + m_status = Status_Ok; + m_errorString.clear(); } -void WalletImpl::segregationHeight(uint64_t height) +void WalletImpl::setStatusError(const std::string& message) const { - m_wallet->segregation_height(height); + setStatus(Status_Error, message); } -void WalletImpl::keyReuseMitigation2(bool mitigation) +void WalletImpl::setStatusCritical(const std::string& message) const { - m_wallet->key_reuse_mitigation2(mitigation); + setStatus(Status_Critical, message); } -bool WalletImpl::lockKeysFile() +void WalletImpl::setStatus(int status, const std::string& message) const { - return m_wallet->lock_keys_file(); + boost::lock_guard l(m_statusMutex); + m_status = status; + m_errorString = message; } -bool WalletImpl::unlockKeysFile() +void WalletImpl::refreshThreadFunc() { - return m_wallet->unlock_keys_file(); + LOG_PRINT_L3(__FUNCTION__ << ": starting refresh thread"); + + while (true) { + boost::mutex::scoped_lock lock(m_refreshMutex); + if (m_refreshThreadDone) { + break; + } + LOG_PRINT_L3(__FUNCTION__ << ": waiting for refresh..."); + // if auto refresh enabled, we wait for the "m_refreshIntervalSeconds" interval. + // if not - we wait forever + if (m_refreshIntervalMillis > 0) { + boost::posix_time::milliseconds wait_for_ms(m_refreshIntervalMillis.load()); + m_refreshCV.timed_wait(lock, wait_for_ms); + } else { + m_refreshCV.wait(lock); + } + + LOG_PRINT_L3(__FUNCTION__ << ": refresh lock acquired..."); + LOG_PRINT_L3(__FUNCTION__ << ": m_refreshEnabled: " << m_refreshEnabled); + LOG_PRINT_L3(__FUNCTION__ << ": m_status: " << status()); + LOG_PRINT_L3(__FUNCTION__ << ": m_refreshShouldRescan: " << m_refreshShouldRescan); + if (m_refreshEnabled) { + LOG_PRINT_L3(__FUNCTION__ << ": refreshing..."); + doRefresh(); + } + } + LOG_PRINT_L3(__FUNCTION__ << ": refresh thread stopped"); } -bool WalletImpl::isKeysFileLocked() +void WalletImpl::doRefresh() { - return m_wallet->is_keys_file_locked(); + bool rescan = m_refreshShouldRescan.exchange(false); + // synchronizing async and sync refresh calls + boost::lock_guard guarg(m_refreshMutex2); + do try { + LOG_PRINT_L3(__FUNCTION__ << ": doRefresh, rescan = "<rescan_blockchain(false); + m_wallet->refresh(trustedDaemon()); + if (!m_synchronized) { + m_synchronized = true; + } + // assuming if we have empty history, it wasn't initialized yet + // for further history changes client need to update history in + // "on_money_received" and "on_money_sent" callbacks + if (m_history->count() == 0) { + m_history->refresh(); + } + m_wallet->find_and_save_rings(false); + } else { + LOG_PRINT_L3(__FUNCTION__ << ": skipping refresh - daemon is not synced"); + } + } catch (const std::exception &e) { + setStatusError(e.what()); + break; + }while(!rescan && (rescan=m_refreshShouldRescan.exchange(false))); // repeat if not rescanned and rescan was requested + + if (m_wallet2Callback->getListener()) { + m_wallet2Callback->getListener()->refreshed(); + } } -uint64_t WalletImpl::coldKeyImageSync(uint64_t &spent, uint64_t &unspent) +bool WalletImpl::daemonSynced() const { - return m_wallet->cold_key_image_sync(spent, unspent); + if(connected() == Wallet::ConnectionStatus_Disconnected) + return false; + uint64_t blockChainHeight = daemonBlockChainHeight(); + return (blockChainHeight >= daemonBlockChainTargetHeight() && blockChainHeight > 1); } -void WalletImpl::deviceShowAddress(uint32_t accountIndex, uint32_t addressIndex, const std::string &paymentId) +void WalletImpl::stopRefresh() { - boost::optional payment_id_param = boost::none; - if (!paymentId.empty()) - { - crypto::hash8 payment_id; - bool res = tools::wallet2::parse_short_payment_id(paymentId, payment_id); - if (!res) - { - throw runtime_error("Invalid payment ID"); - } - payment_id_param = payment_id; + if (!m_refreshThreadDone) { + m_refreshEnabled = false; + m_refreshThreadDone = true; + m_refreshCV.notify_one(); + m_refreshThread.join(); } +} - m_wallet->device_show_address(accountIndex, addressIndex, payment_id_param); +bool WalletImpl::isNewWallet() const +{ + // in case wallet created without daemon connection, closed and opened again, + // it's the same case as if it created from scratch, i.e. we need "fast sync" + // with the daemon (pull hashes instead of pull blocks). + // If wallet cache is rebuilt, creation height stored in .keys is used. + // Watch only wallet is a copy of an existing wallet. + return !(blockChainHeight() > 1 || m_recoveringFromSeed || m_recoveringFromDevice || m_rebuildWalletCache) && !watchOnly(); } -bool WalletImpl::reconnectDevice() +void WalletImpl::pendingTxPostProcess(PendingTransactionImpl * pending) { - clearStatus(); + // If the device being used is HW device with cold signing protocol, cold sign then. + if (!m_wallet->get_account().get_device().has_tx_cold_sign()){ + return; + } - bool r; - try { - r = m_wallet->reconnect_device(); - } - catch (const std::exception &e) { - LOG_ERROR(__FUNCTION__ << " error: " << e.what()); - setStatusError(e.what()); - return false; - } + tools::wallet2::signed_tx_set exported_txs; + std::vector dsts_info; - return r; + m_wallet->cold_sign_tx(pending->m_pending_tx, exported_txs, dsts_info, pending->m_tx_device_aux); + pending->m_key_images = exported_txs.key_images; + pending->m_pending_tx = exported_txs.ptx; } -uint64_t WalletImpl::getBytesReceived() +bool WalletImpl::doInit(const string &daemon_address, const std::string &proxy_address, uint64_t upper_transaction_size_limit, bool ssl) { - return m_wallet->get_bytes_received(); -} + if (!m_wallet->init(daemon_address, m_daemon_login, proxy_address, upper_transaction_size_limit)) + return false; -uint64_t WalletImpl::getBytesSent() -{ - return m_wallet->get_bytes_sent(); + // in case new wallet, this will force fast-refresh (pulling hashes instead of blocks) + // If daemon isn't synced a calculated block height will be used instead + if (isNewWallet() && daemonSynced()) { + LOG_PRINT_L2(__FUNCTION__ << ":New Wallet - fast refresh until " << daemonBlockChainHeight()); + m_wallet->set_refresh_from_block_height(daemonBlockChainHeight()); + } + + if (m_rebuildWalletCache) + LOG_PRINT_L2(__FUNCTION__ << ": Rebuilding wallet cache, fast refresh until block " << m_wallet->get_refresh_from_block_height()); + + if (Utils::isAddressLocal(daemon_address)) { + this->setTrustedDaemon(true); + m_refreshIntervalMillis = DEFAULT_REFRESH_INTERVAL_MILLIS; + } else { + this->setTrustedDaemon(false); + m_refreshIntervalMillis = DEFAULT_REMOTE_NODE_REFRESH_INTERVAL_MILLIS; + } + return true; } } // namespace diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index d1bf4f759b..9bf35b2e85 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -55,11 +55,35 @@ class WalletImpl : public Wallet public: WalletImpl(NetworkType nettype = MAINNET, uint64_t kdf_rounds = 1); ~WalletImpl(); + std::string seed(const std::string& seed_offset = "") const override; + std::string getSeedLanguage() const override; + void setSeedLanguage(const std::string &arg) override; + int status() const override; + std::string errorString() const override; + void statusWithErrorString(int& status, std::string& errorString) const override; + bool setPassword(const std::string &password) override; + const std::string& getPassword() const override; + bool setDevicePin(const std::string &password) override; + bool setDevicePassphrase(const std::string &password) override; + std::string address(uint32_t accountIndex = 0, uint32_t addressIndex = 0) const override; + std::string path() const override; + void hardForkInfo(uint8_t &version, uint64_t &earliest_height) const override; + bool useForkRules(uint8_t version, int64_t early_blocks) const override; + std::string integratedAddress(const std::string &payment_id) const override; + std::string secretViewKey() const override; + std::string publicViewKey() const override; + std::string secretSpendKey() const override; + std::string publicSpendKey() const override; + std::string publicMultisigSignerKey() const override; + void stop() override; + bool store(const std::string &path) override; + std::string filename() const override; + std::string keysFilename() const override; + bool init(const std::string &daemon_address, uint64_t upper_transaction_size_limit = 0, const std::string &daemon_username = "", const std::string &daemon_password = "", bool use_ssl = false, bool lightWallet = false, const std::string &proxy_address = "") override; bool create(const std::string &path, const std::string &password, const std::string &language); bool createWatchOnly(const std::string &path, const std::string &password, const std::string &language) const override; - bool open(const std::string &path, const std::string &password); bool recover(const std::string &path,const std::string &password, const std::string &seed, const std::string &seed_offset = {}); bool recoverFromKeysWithPassword(const std::string &path, @@ -80,32 +104,12 @@ class WalletImpl : public Wallet bool recoverFromDevice(const std::string &path, const std::string &password, const std::string &device_name); - Device getDeviceType() const override; + bool open(const std::string &path, const std::string &password); bool close(bool store = true); - std::string seed(const std::string& seed_offset = "") const override; - std::string getSeedLanguage() const override; - void setSeedLanguage(const std::string &arg) override; - // void setListener(Listener *) {} - int status() const override; - std::string errorString() const override; - void statusWithErrorString(int& status, std::string& errorString) const override; - bool setPassword(const std::string &password) override; - const std::string& getPassword() const override; - bool setDevicePin(const std::string &password) override; - bool setDevicePassphrase(const std::string &password) override; - std::string address(uint32_t accountIndex = 0, uint32_t addressIndex = 0) const override; - std::string integratedAddress(const std::string &payment_id) const override; - std::string secretViewKey() const override; - std::string publicViewKey() const override; - std::string secretSpendKey() const override; - std::string publicSpendKey() const override; - std::string publicMultisigSignerKey() const override; - std::string path() const override; - void stop() override; - bool store(const std::string &path) override; - std::string filename() const override; - std::string keysFilename() const override; - bool init(const std::string &daemon_address, uint64_t upper_transaction_size_limit = 0, const std::string &daemon_username = "", const std::string &daemon_password = "", bool use_ssl = false, bool lightWallet = false, const std::string &proxy_address = "") override; + void setRefreshFromBlockHeight(uint64_t refresh_from_block_height) override; + void setRecoveringFromSeed(bool recoveringFromSeed) override; + void setRecoveringFromDevice(bool recoveringFromDevice) override; + void setSubaddressLookahead(uint32_t major, uint32_t minor) override; bool connectToDaemon() override; ConnectionStatus connected() const override; void setTrustedDaemon(bool arg) override; @@ -113,46 +117,28 @@ class WalletImpl : public Wallet 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; + bool watchOnly() const override; + bool isDeterministic() const override; uint64_t blockChainHeight() const override; uint64_t approximateBlockChainHeight() const override; uint64_t estimateBlockChainHeight() const override; uint64_t daemonBlockChainHeight() const override; uint64_t daemonBlockChainTargetHeight() const override; bool synchronized() const override; + virtual void startRefresh() override; + virtual void pauseRefresh() override; bool refresh() override; void refreshAsync() override; bool rescanBlockchain() override; void rescanBlockchainAsync() override; void setAutoRefreshInterval(int millis) override; int autoRefreshInterval() const override; - void setRefreshFromBlockHeight(uint64_t refresh_from_block_height) override; - uint64_t getRefreshFromBlockHeight() const override { return m_wallet->get_refresh_from_block_height(); }; - void setRecoveringFromSeed(bool recoveringFromSeed) override; - void setRecoveringFromDevice(bool recoveringFromDevice) override; - void setSubaddressLookahead(uint32_t major, uint32_t minor) override; - bool watchOnly() const override; - bool isDeterministic() const override; - bool rescanSpent() override; - NetworkType nettype() const override {return static_cast(m_wallet->nettype());} - void hardForkInfo(uint8_t &version, uint64_t &earliest_height) const override; - bool useForkRules(uint8_t version, int64_t early_blocks) const override; - void addSubaddressAccount(const std::string& label) override; size_t numSubaddressAccounts() const override; size_t numSubaddresses(uint32_t accountIndex) const override; void addSubaddress(uint32_t accountIndex, const std::string& label) override; std::string getSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex) const override; void setSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex, const std::string &label) override; - - MultisigState multisig() const override; - std::string getMultisigInfo() const override; - std::string makeMultisig(const std::vector& info, uint32_t threshold) override; - std::string exchangeMultisigKeys(const std::vector &info, const bool force_update_use_with_caution = false) override; - bool exportMultisigImages(std::string& images) override; - size_t importMultisigImages(const std::vector& images) override; - bool hasMultisigPartialKeyImages() const override; - PendingTransaction* restoreMultisigTransaction(const std::string& signData) override; - PendingTransaction * createTransactionMultDest(const std::vector &dst_addr, const std::string &payment_id, optional> amount, uint32_t mixin_count, PendingTransaction::Priority priority = PendingTransaction::Priority_Low, @@ -164,17 +150,16 @@ class WalletImpl : public Wallet uint32_t subaddr_account = 0, std::set subaddr_indices = {}) override; virtual PendingTransaction * createSweepUnmixableTransaction() override; - bool submitTransaction(const std::string &fileName) override; virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename) override; + bool submitTransaction(const std::string &fileName) override; + virtual void disposeTransaction(PendingTransaction * t) override; + virtual uint64_t estimateTransactionFee(const std::vector> &destinations, + PendingTransaction::Priority priority) const override; bool exportKeyImages(const std::string &filename, bool all = false) override; bool importKeyImages(const std::string &filename) override; bool exportOutputs(const std::string &filename, bool all = false) override; bool importOutputs(const std::string &filename) override; bool scanTransactions(const std::vector &txids) override; - - virtual void disposeTransaction(PendingTransaction * t) override; - virtual uint64_t estimateTransactionFee(const std::vector> &destinations, - PendingTransaction::Priority priority) const override; virtual TransactionHistory * history() override; virtual AddressBook * addressBook() override; virtual Subaddress * subaddress() override; @@ -182,13 +167,8 @@ class WalletImpl : public Wallet virtual void setListener(WalletListener * l) override; virtual uint32_t defaultMixin() const override; virtual void setDefaultMixin(uint32_t arg) override; - virtual bool setCacheAttribute(const std::string &key, const std::string &val) override; virtual std::string getCacheAttribute(const std::string &key) const override; - - virtual void setOffline(bool offline) override; - virtual bool isOffline() const override; - virtual bool setUserNote(const std::string &txid, const std::string ¬e) override; virtual std::string getUserNote(const std::string &txid) const override; virtual std::string getTxKey(const std::string &txid) const override; @@ -201,13 +181,12 @@ class WalletImpl : public Wallet virtual bool checkReserveProof(const std::string &address, const std::string &message, const std::string &signature, bool &good, uint64_t &total, uint64_t &spent) const override; virtual std::string signMessage(const std::string &message, const std::string &address) override; virtual bool verifySignedMessage(const std::string &message, const std::string &address, const std::string &signature) const override; - virtual std::string signMultisigParticipant(const std::string &message) const override; - virtual bool verifyMessageWithPublicKey(const std::string &message, const std::string &publicKey, const std::string &signature) const override; - virtual void startRefresh() override; - virtual void pauseRefresh() override; virtual bool parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector &unknown_parameters, std::string &error) override; virtual std::string make_uri(const std::string &address, const std::string &payment_id, uint64_t amount, const std::string &tx_description, const std::string &recipient_name, std::string &error) const override; virtual std::string getDefaultDataDir() const override; + bool rescanSpent() override; + virtual void setOffline(bool offline) override; + virtual bool isOffline() const override; virtual bool blackballOutputs(const std::vector &outputs, bool add) override; virtual bool blackballOutput(const std::string &amount, const std::string &offset) override; virtual bool unblackballOutput(const std::string &amount, const std::string &offset) override; @@ -220,12 +199,29 @@ class WalletImpl : public Wallet virtual bool lockKeysFile() override; virtual bool unlockKeysFile() override; virtual bool isKeysFileLocked() override; + Device getDeviceType() const override; virtual uint64_t coldKeyImageSync(uint64_t &spent, uint64_t &unspent) override; virtual void deviceShowAddress(uint32_t accountIndex, uint32_t addressIndex, const std::string &paymentId) override; virtual bool reconnectDevice() override; virtual uint64_t getBytesReceived() override; virtual uint64_t getBytesSent() override; + NetworkType nettype() const override {return static_cast(m_wallet->nettype());} + uint64_t getRefreshFromBlockHeight() const override { return m_wallet->get_refresh_from_block_height(); }; + + // Multisig + MultisigState multisig() const override; + std::string getMultisigInfo() const override; + std::string makeMultisig(const std::vector& info, uint32_t threshold) override; + std::string exchangeMultisigKeys(const std::vector &info, const bool force_update_use_with_caution = false) override; + bool exportMultisigImages(std::string& images) override; + size_t importMultisigImages(const std::vector& images) override; + bool hasMultisigPartialKeyImages() const override; + PendingTransaction* restoreMultisigTransaction(const std::string& signData) override; + virtual std::string signMultisigParticipant(const std::string &message) const override; + virtual bool verifyMessageWithPublicKey(const std::string &message, const std::string &publicKey, const std::string &signature) const override; + + private: void clearStatus() const; void setStatusError(const std::string& message) const; diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h index 53210832ba..60e4e33713 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h @@ -222,20 +222,21 @@ struct AddressBookRow { AddressBookRow(std::size_t _rowId, const std::string &_address, const std::string &_paymentId, const std::string &_description): m_rowId(_rowId), m_address(_address), - m_paymentId(_paymentId), + m_paymentId(_paymentId), m_description(_description) {} - + + std::size_t getRowId() const {return m_rowId;} + std::string getAddress() const {return m_address;} + std::string getPaymentId() const {return m_paymentId;} + std::string getDescription() const {return m_description;} + +public: + std::string extra; private: std::size_t m_rowId; std::string m_address; std::string m_paymentId; std::string m_description; -public: - std::string extra; - std::string getAddress() const {return m_address;} - std::string getDescription() const {return m_description;} - std::string getPaymentId() const {return m_paymentId;} - std::size_t getRowId() const {return m_rowId;} }; /** @@ -267,16 +268,17 @@ struct SubaddressRow { m_rowId(_rowId), m_address(_address), m_label(_label) {} - + + std::size_t getRowId() const {return m_rowId;} + std::string getAddress() const {return m_address;} + std::string getLabel() const {return m_label;} + +public: + std::string extra; private: std::size_t m_rowId; std::string m_address; std::string m_label; -public: - std::string extra; - std::string getAddress() const {return m_address;} - std::string getLabel() const {return m_label;} - std::size_t getRowId() const {return m_rowId;} }; struct Subaddress @@ -297,19 +299,20 @@ struct SubaddressAccountRow { m_balance(_balance), m_unlockedBalance(_unlockedBalance) {} + std::size_t getRowId() const {return m_rowId;} + std::string getAddress() const {return m_address;} + std::string getLabel() const {return m_label;} + std::string getBalance() const {return m_balance;} + std::string getUnlockedBalance() const {return m_unlockedBalance;} + +public: + std::string extra; private: std::size_t m_rowId; std::string m_address; std::string m_label; std::string m_balance; std::string m_unlockedBalance; -public: - std::string extra; - std::string getAddress() const {return m_address;} - std::string getLabel() const {return m_label;} - std::string getBalance() const {return m_balance;} - std::string getUnlockedBalance() const {return m_unlockedBalance;} - std::size_t getRowId() const {return m_rowId;} }; struct SubaddressAccount @@ -672,35 +675,6 @@ struct Wallet */ virtual bool synchronized() const = 0; - static std::string displayAmount(uint64_t amount); - static uint64_t amountFromString(const std::string &amount); - static uint64_t amountFromDouble(double amount); - static std::string genPaymentId(); - static bool paymentIdValid(const std::string &paiment_id); - static bool addressValid(const std::string &str, NetworkType nettype); - static bool addressValid(const std::string &str, bool testnet) // deprecated - { - return addressValid(str, testnet ? TESTNET : MAINNET); - } - static bool keyValid(const std::string &secret_key_string, const std::string &address_string, bool isViewKey, NetworkType nettype, std::string &error); - static bool keyValid(const std::string &secret_key_string, const std::string &address_string, bool isViewKey, bool testnet, std::string &error) // deprecated - { - return keyValid(secret_key_string, address_string, isViewKey, testnet ? TESTNET : MAINNET, error); - } - static std::string paymentIdFromAddress(const std::string &str, NetworkType nettype); - static std::string paymentIdFromAddress(const std::string &str, bool testnet) // deprecated - { - return paymentIdFromAddress(str, testnet ? TESTNET : MAINNET); - } - static uint64_t maximumAllowedAmount(); - // Easylogger wrapper - static void init(const char *argv0, const char *default_log_base_name) { init(argv0, default_log_base_name, "", true); } - static void init(const char *argv0, const char *default_log_base_name, const std::string &log_path, bool console); - static void debug(const std::string &category, const std::string &str); - static void info(const std::string &category, const std::string &str); - static void warning(const std::string &category, const std::string &str); - static void error(const std::string &category, const std::string &str); - /** * @brief StartRefresh - Start/resume refresh thread (refresh every 10 seconds) */ @@ -777,56 +751,6 @@ struct Wallet * @param label - the new label for the specified subaddress */ virtual void setSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex, const std::string &label) = 0; - - /** - * @brief multisig - returns current state of multisig wallet creation process - * @return MultisigState struct - */ - virtual MultisigState multisig() const = 0; - /** - * @brief getMultisigInfo - * @return serialized and signed multisig info string - */ - virtual std::string getMultisigInfo() const = 0; - /** - * @brief makeMultisig - switches wallet in multisig state. The one and only creation phase for N / N wallets - * @param info - vector of multisig infos from other participants obtained with getMulitisInfo call - * @param threshold - number of required signers to make valid transaction. Must be <= number of participants - * @return in case of N / N wallets returns empty string since no more key exchanges needed. For N - 1 / N wallets returns base58 encoded extra multisig info - */ - virtual std::string makeMultisig(const std::vector& info, uint32_t threshold) = 0; - /** - * @brief exchange_multisig_keys - provides additional key exchange round for arbitrary multisig schemes (like N-1/N, M/N) - * @param info - base58 encoded key derivations returned by makeMultisig or exchangeMultisigKeys function call - * @param force_update_use_with_caution - force multisig account to update even if not all signers contribute round messages - * @return new info string if more rounds required or an empty string if wallet creation is done - */ - virtual std::string exchangeMultisigKeys(const std::vector &info, const bool force_update_use_with_caution) = 0; - /** - * @brief exportMultisigImages - exports transfers' key images - * @param images - output paramter for hex encoded array of images - * @return true if success - */ - virtual bool exportMultisigImages(std::string& images) = 0; - /** - * @brief importMultisigImages - imports other participants' multisig images - * @param images - array of hex encoded arrays of images obtained with exportMultisigImages - * @return number of imported images - */ - virtual size_t importMultisigImages(const std::vector& images) = 0; - /** - * @brief hasMultisigPartialKeyImages - checks if wallet needs to import multisig key images from other participants - * @return true if there are partial key images - */ - virtual bool hasMultisigPartialKeyImages() const = 0; - - /** - * @brief restoreMultisigTransaction creates PendingTransaction from signData - * @param signData encrypted unsigned transaction. Obtained with PendingTransaction::multisigSignData - * @return PendingTransaction - */ - virtual PendingTransaction* restoreMultisigTransaction(const std::string& signData) = 0; - /*! * \brief createTransactionMultDest creates transaction with multiple destinations. if dst_addr is an integrated address, payment_id is ignored * \param dst_addr vector of destination address as string @@ -1006,22 +930,6 @@ struct Wallet * \return true if the signature verified, false otherwise */ virtual bool verifySignedMessage(const std::string &message, const std::string &addres, const std::string &signature) const = 0; - - /*! - * \brief signMultisigParticipant signs given message with the multisig public signer key - * \param message message to sign - * \return signature in case of success. Sets status to Error and return empty string in case of error - */ - virtual std::string signMultisigParticipant(const std::string &message) const = 0; - /*! - * \brief verifyMessageWithPublicKey verifies that message was signed with the given public key - * \param message message - * \param publicKey hex encoded public key - * \param signature signature of the message - * \return true if the signature is correct. false and sets error state in case of error - */ - virtual bool verifyMessageWithPublicKey(const std::string &message, const std::string &publicKey, const std::string &signature) const = 0; - virtual bool parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector &unknown_parameters, std::string &error) = 0; virtual std::string make_uri(const std::string &address, const std::string &payment_id, uint64_t amount, const std::string &tx_description, const std::string &recipient_name, std::string &error) const = 0; @@ -1093,6 +1001,102 @@ struct Wallet //! get bytes sent virtual uint64_t getBytesSent() = 0; + + + /** + * @brief multisig - returns current state of multisig wallet creation process + * @return MultisigState struct + */ + virtual MultisigState multisig() const = 0; + /** + * @brief getMultisigInfo + * @return serialized and signed multisig info string + */ + virtual std::string getMultisigInfo() const = 0; + /** + * @brief makeMultisig - switches wallet in multisig state. The one and only creation phase for N / N wallets + * @param info - vector of multisig infos from other participants obtained with getMulitisInfo call + * @param threshold - number of required signers to make valid transaction. Must be <= number of participants + * @return in case of N / N wallets returns empty string since no more key exchanges needed. For N - 1 / N wallets returns base58 encoded extra multisig info + */ + virtual std::string makeMultisig(const std::vector& info, uint32_t threshold) = 0; + /** + * @brief exchange_multisig_keys - provides additional key exchange round for arbitrary multisig schemes (like N-1/N, M/N) + * @param info - base58 encoded key derivations returned by makeMultisig or exchangeMultisigKeys function call + * @param force_update_use_with_caution - force multisig account to update even if not all signers contribute round messages + * @return new info string if more rounds required or an empty string if wallet creation is done + */ + virtual std::string exchangeMultisigKeys(const std::vector &info, const bool force_update_use_with_caution) = 0; + /** + * @brief exportMultisigImages - exports transfers' key images + * @param images - output paramter for hex encoded array of images + * @return true if success + */ + virtual bool exportMultisigImages(std::string& images) = 0; + /** + * @brief importMultisigImages - imports other participants' multisig images + * @param images - array of hex encoded arrays of images obtained with exportMultisigImages + * @return number of imported images + */ + virtual size_t importMultisigImages(const std::vector& images) = 0; + /** + * @brief hasMultisigPartialKeyImages - checks if wallet needs to import multisig key images from other participants + * @return true if there are partial key images + */ + virtual bool hasMultisigPartialKeyImages() const = 0; + + /** + * @brief restoreMultisigTransaction creates PendingTransaction from signData + * @param signData encrypted unsigned transaction. Obtained with PendingTransaction::multisigSignData + * @return PendingTransaction + */ + virtual PendingTransaction* restoreMultisigTransaction(const std::string& signData) = 0; + + /*! + * \brief signMultisigParticipant signs given message with the multisig public signer key + * \param message message to sign + * \return signature in case of success. Sets status to Error and return empty string in case of error + */ + virtual std::string signMultisigParticipant(const std::string &message) const = 0; + /*! + * \brief verifyMessageWithPublicKey verifies that message was signed with the given public key + * \param message message + * \param publicKey hex encoded public key + * \param signature signature of the message + * \return true if the signature is correct. false and sets error state in case of error + */ + virtual bool verifyMessageWithPublicKey(const std::string &message, const std::string &publicKey, const std::string &signature) const = 0; + + // Static + static std::string displayAmount(uint64_t amount); + static uint64_t amountFromString(const std::string &amount); + static uint64_t amountFromDouble(double amount); + static std::string genPaymentId(); + static bool paymentIdValid(const std::string &paiment_id); + static bool addressValid(const std::string &str, NetworkType nettype); + static bool addressValid(const std::string &str, bool testnet) // deprecated + { + return addressValid(str, testnet ? TESTNET : MAINNET); + } + static bool keyValid(const std::string &secret_key_string, const std::string &address_string, bool isViewKey, NetworkType nettype, std::string &error); + static bool keyValid(const std::string &secret_key_string, const std::string &address_string, bool isViewKey, bool testnet, std::string &error) // deprecated + { + return keyValid(secret_key_string, address_string, isViewKey, testnet ? TESTNET : MAINNET, error); + } + static std::string paymentIdFromAddress(const std::string &str, NetworkType nettype); + static std::string paymentIdFromAddress(const std::string &str, bool testnet) // deprecated + { + return paymentIdFromAddress(str, testnet ? TESTNET : MAINNET); + } + static uint64_t maximumAllowedAmount(); + + // Easylogger wrapper + static void init(const char *argv0, const char *default_log_base_name) { init(argv0, default_log_base_name, "", true); } + static void init(const char *argv0, const char *default_log_base_name, const std::string &log_path, bool console); + static void debug(const std::string &category, const std::string &str); + static void info(const std::string &category, const std::string &str); + static void warning(const std::string &category, const std::string &str); + static void error(const std::string &category, const std::string &str); }; /** @@ -1338,15 +1342,16 @@ struct WalletManager //! resolves an OpenAlias address to a monero address virtual std::string resolveOpenAlias(const std::string &address, bool &dnssec_valid) const = 0; + //! sets proxy address, empty string to disable + virtual bool setProxy(const std::string &address) = 0; + + // Static //! checks for an update and returns version, hash and url static std::tuple checkUpdates( const std::string &software, std::string subdir, const char *buildtag = nullptr, const char *current_version = nullptr); - - //! sets proxy address, empty string to disable - virtual bool setProxy(const std::string &address) = 0; }; diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp index 1bb4bc27c2..779b88d817 100644 --- a/src/wallet/api/wallet_manager.cpp +++ b/src/wallet/api/wallet_manager.cpp @@ -347,6 +347,12 @@ std::string WalletManagerImpl::resolveOpenAlias(const std::string &address, bool return addresses.front(); } +bool WalletManagerImpl::setProxy(const std::string &address) +{ + return m_http_client.set_proxy(address); +} + +// Static std::tuple WalletManager::checkUpdates( const std::string &software, std::string subdir, @@ -380,11 +386,6 @@ std::tuple WalletManag return std::make_tuple(false, "", "", "", ""); } -bool WalletManagerImpl::setProxy(const std::string &address) -{ - return m_http_client.set_proxy(address); -} - ///////////////////// WalletManagerFactory implementation ////////////////////// WalletManager *WalletManagerFactory::getWalletManager() { diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h index 46ec362974..dd0a11e73f 100644 --- a/src/wallet/api/wallet_manager.h +++ b/src/wallet/api/wallet_manager.h @@ -47,6 +47,7 @@ class WalletManagerImpl : public WalletManager NetworkType nettype, uint64_t restoreHeight, uint64_t kdf_rounds = 1, + virtual Wallet * recoveryWallet(const std::string &path, const std::string &mnemonic, NetworkType nettype, uint64_t restoreHeight) override; const std::string &seed_offset = {}) override; virtual Wallet * createWalletFromKeys(const std::string &path, const std::string &password, @@ -57,9 +58,6 @@ class WalletManagerImpl : public WalletManager const std::string &viewKeyString, const std::string &spendKeyString = "", uint64_t kdf_rounds = 1) override; - // next two methods are deprecated - use the above version which allow setting of a password - virtual Wallet * recoveryWallet(const std::string &path, const std::string &mnemonic, NetworkType nettype, uint64_t restoreHeight) override; - // deprecated: use createWalletFromKeys(..., password, ...) instead virtual Wallet * createWalletFromKeys(const std::string &path, const std::string &language, NetworkType nettype, From 202b2fcb6a0e6be351ff81af884cf8820b338c17 Mon Sep 17 00:00:00 2001 From: SNeedlewoods Date: Thu, 20 Jun 2024 12:23:45 +0200 Subject: [PATCH 2/4] fix rebase --- src/wallet/api/wallet.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index 6518543514..9018c36277 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -2254,8 +2254,13 @@ uint64_t WalletImpl::getBytesSent() MultisigState WalletImpl::multisig() const { MultisigState state; - state.isMultisig = m_wallet->multisig(&state.isReady, &state.threshold, &state.total); + const multisig::multisig_account_status ms_status{m_wallet->get_multisig_status()}; + state.isMultisig = ms_status.multisig_is_active; + state.kexIsDone = ms_status.kex_is_done; + state.isReady = ms_status.is_ready; + state.threshold = ms_status.threshold; + state.total = ms_status.total; return state; } @@ -2275,7 +2280,7 @@ string WalletImpl::makeMultisig(const vector& info, const uint32_t thres try { clearStatus(); - if (m_wallet->multisig()) { + if (m_wallet->get_multisig_status().multisig_is_active) { throw runtime_error("Wallet is already multisig"); } @@ -2392,8 +2397,8 @@ std::string WalletImpl::signMultisigParticipant(const std::string &message) cons { clearStatus(); - bool ready = false; - if (!m_wallet->multisig(&ready) || !ready) { + const multisig::multisig_account_status ms_status{m_wallet->get_multisig_status()}; + if (!ms_status.multisig_is_active || !ms_status.is_ready) { m_status = Status_Error; m_errorString = tr("The wallet must be in multisig ready state"); return {}; From b1f7590037edb5451230fe72664086b930807967 Mon Sep 17 00:00:00 2001 From: SNeedlewoods Date: Thu, 20 Jun 2024 12:41:34 +0200 Subject: [PATCH 3/4] fix wallet manager --- src/wallet/api/wallet_manager.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h index dd0a11e73f..c4b1727d9e 100644 --- a/src/wallet/api/wallet_manager.h +++ b/src/wallet/api/wallet_manager.h @@ -47,8 +47,8 @@ class WalletManagerImpl : public WalletManager NetworkType nettype, uint64_t restoreHeight, uint64_t kdf_rounds = 1, - virtual Wallet * recoveryWallet(const std::string &path, const std::string &mnemonic, NetworkType nettype, uint64_t restoreHeight) override; const std::string &seed_offset = {}) override; + virtual Wallet * recoveryWallet(const std::string &path, const std::string &mnemonic, NetworkType nettype, uint64_t restoreHeight) override; virtual Wallet * createWalletFromKeys(const std::string &path, const std::string &password, const std::string &language, From c5ed7f8fbc3a4b64a13d9b64746555f4c81772dd Mon Sep 17 00:00:00 2001 From: SNeedlewoods Date: Mon, 24 Jun 2024 13:50:31 +0200 Subject: [PATCH 4/4] rbunner7 review --- src/wallet/api/unsigned_transaction.cpp | 1 - src/wallet/api/wallet2_api.h | 2 -- src/wallet/api/wallet_manager.cpp | 1 - 3 files changed, 4 deletions(-) diff --git a/src/wallet/api/unsigned_transaction.cpp b/src/wallet/api/unsigned_transaction.cpp index e60486e31f..1b174afa54 100644 --- a/src/wallet/api/unsigned_transaction.cpp +++ b/src/wallet/api/unsigned_transaction.cpp @@ -196,7 +196,6 @@ bool UnsignedTransactionImpl::sign(const std::string &signedFileName) return true; } - //---------------------------------------------------------------------------------------------------- bool UnsignedTransactionImpl::checkLoadedTx(const std::function get_num_txes, const std::function &get_tx, const std::string &extra_message) { diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h index 60e4e33713..e0e2bc8177 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h @@ -1067,7 +1067,6 @@ struct Wallet */ virtual bool verifyMessageWithPublicKey(const std::string &message, const std::string &publicKey, const std::string &signature) const = 0; - // Static static std::string displayAmount(uint64_t amount); static uint64_t amountFromString(const std::string &amount); static uint64_t amountFromDouble(double amount); @@ -1345,7 +1344,6 @@ struct WalletManager //! sets proxy address, empty string to disable virtual bool setProxy(const std::string &address) = 0; - // Static //! checks for an update and returns version, hash and url static std::tuple checkUpdates( const std::string &software, diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp index 779b88d817..4ca136409f 100644 --- a/src/wallet/api/wallet_manager.cpp +++ b/src/wallet/api/wallet_manager.cpp @@ -352,7 +352,6 @@ bool WalletManagerImpl::setProxy(const std::string &address) return m_http_client.set_proxy(address); } -// Static std::tuple WalletManager::checkUpdates( const std::string &software, std::string subdir,