From 60ff2f668e1b63b79702b996f622b7adc87a05cc Mon Sep 17 00:00:00 2001 From: thotbot Date: Sat, 3 Jul 2021 22:54:49 +0200 Subject: [PATCH] Misc. wallet API and wallet2 changes --- src/wallet/api/subaddress.cpp | 5 +- src/wallet/api/transaction_history.cpp | 12 +- src/wallet/api/transaction_info.cpp | 5 + src/wallet/api/transaction_info.h | 3 + src/wallet/api/wallet.cpp | 188 +++++++++++++++++++++++-- src/wallet/api/wallet.h | 12 ++ src/wallet/api/wallet2_api.h | 84 ++++++++++- src/wallet/api/wallet_manager.cpp | 16 +++ src/wallet/api/wallet_manager.h | 7 + src/wallet/node_rpc_proxy.cpp | 2 +- src/wallet/wallet2.cpp | 34 +++-- src/wallet/wallet2.h | 2 + 12 files changed, 336 insertions(+), 34 deletions(-) diff --git a/src/wallet/api/subaddress.cpp b/src/wallet/api/subaddress.cpp index 9e358b4c8..0fe912a21 100644 --- a/src/wallet/api/subaddress.cpp +++ b/src/wallet/api/subaddress.cpp @@ -67,7 +67,10 @@ void SubaddressImpl::refresh(uint32_t accountIndex) clearRows(); for (size_t i = 0; i < m_wallet->m_wallet->get_num_subaddresses(accountIndex); ++i) { - m_rows.push_back(new SubaddressRow(i, m_wallet->m_wallet->get_subaddress_as_str({accountIndex, (uint32_t)i}), m_wallet->m_wallet->get_subaddress_label({accountIndex, (uint32_t)i}))); + m_rows.push_back(new SubaddressRow(i, + m_wallet->m_wallet->get_subaddress_as_str({accountIndex, (uint32_t)i}), + m_wallet->m_wallet->get_subaddress_label({accountIndex, (uint32_t)i}), + m_wallet->m_wallet->get_subaddress_used({accountIndex, (uint32_t)i}))); } } diff --git a/src/wallet/api/transaction_history.cpp b/src/wallet/api/transaction_history.cpp index 9f5e41156..218bd9cfd 100644 --- a/src/wallet/api/transaction_history.cpp +++ b/src/wallet/api/transaction_history.cpp @@ -199,6 +199,9 @@ void TransactionHistoryImpl::refresh() ti->m_transfers.push_back({d.amount, d.address(m_wallet->m_wallet->nettype(), pd.m_payment_id)}); } + for (const auto &r: pd.m_rings) { + ti->m_rings.push_back({string_tools::pod_to_hex(r.first), cryptonote::relative_output_offsets_to_absolute(r.second)}); + } m_history.push_back(ti); } @@ -229,10 +232,15 @@ void TransactionHistoryImpl::refresh() ti->m_label = pd.m_subaddr_indices.size() == 1 ? m_wallet->m_wallet->get_subaddress_label({pd.m_subaddr_account, *pd.m_subaddr_indices.begin()}) : ""; ti->m_timestamp = pd.m_timestamp; ti->m_confirmations = 0; - for (const auto &d : pd.m_dests) + + for (const auto &d : pd.m_dests) { { ti->m_transfers.push_back({d.amount, d.address(m_wallet->m_wallet->nettype(), pd.m_payment_id)}); - } + } + + for (const auto &r: pd.m_rings) { + ti->m_rings.push_back({string_tools::pod_to_hex(r.first), cryptonote::relative_output_offsets_to_absolute(r.second)}); + } m_history.push_back(ti); } diff --git a/src/wallet/api/transaction_info.cpp b/src/wallet/api/transaction_info.cpp index 572b04316..6f4794241 100644 --- a/src/wallet/api/transaction_info.cpp +++ b/src/wallet/api/transaction_info.cpp @@ -139,6 +139,11 @@ const std::vector &TransactionInfoImpl::transfers() c return m_transfers; } +const std::vector>> &TransactionInfoImpl::rings() const +{ + return m_rings; +} + uint64_t TransactionInfoImpl::confirmations() const { return m_confirmations; diff --git a/src/wallet/api/transaction_info.h b/src/wallet/api/transaction_info.h index 6337f2aaa..e931a2b4e 100644 --- a/src/wallet/api/transaction_info.h +++ b/src/wallet/api/transaction_info.h @@ -63,6 +63,8 @@ public: virtual uint64_t confirmations() const override; virtual uint64_t unlockTime() const override; + virtual const std::vector>> &rings() const override; + private: int m_direction; bool m_pending; @@ -81,6 +83,7 @@ private: std::vector m_transfers; uint64_t m_confirmations; uint64_t m_unlock_time; + std::vector>> m_rings; friend class TransactionHistoryImpl; diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index 1debd9849..be11d9d4f 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -35,6 +35,7 @@ #include "transaction_history.h" #include "address_book.h" #include "subaddress.h" +#include "coins.h" #include "subaddress_account.h" #include "common_defines.h" #include "common/util.h" @@ -166,11 +167,8 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback << ", burnt: " << print_money(burnt) << ", raw_output_value: " << print_money(amount) << ", idx: " << subaddr_index); - // do not signal on received tx if wallet is not syncronized completely - if (m_listener && m_wallet->synchronized()) { - m_listener->moneyReceived(tx_hash, amount - burnt); - m_listener->updated(); - } + m_listener->moneyReceived(tx_hash, amount); + m_listener->updated(); } virtual void on_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index) @@ -182,11 +180,8 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback << ", tx: " << tx_hash << ", amount: " << print_money(amount) << ", idx: " << subaddr_index); - // do not signal on received tx if wallet is not syncronized completely - if (m_listener && m_wallet->synchronized()) { - m_listener->unconfirmedMoneyReceived(tx_hash, amount); - m_listener->updated(); - } + m_listener->unconfirmedMoneyReceived(tx_hash, amount); + m_listener->updated(); } virtual void on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx, @@ -198,11 +193,8 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback << ", tx: " << tx_hash << ", amount: " << print_money(amount) << ", idx: " << subaddr_index); - // do not signal on sent tx if wallet is not syncronized completely - if (m_listener && m_wallet->synchronized()) { - m_listener->moneySpent(tx_hash, amount); - m_listener->updated(); - } + m_listener->moneySpent(tx_hash, amount); + m_listener->updated(); } virtual void on_skip_transaction(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx) @@ -436,6 +428,7 @@ WalletImpl::WalletImpl(NetworkType nettype, uint64_t kdf_rounds) m_refreshEnabled = false; m_addressBook.reset(new AddressBookImpl(this)); m_subaddress.reset(new SubaddressImpl(this)); + m_coins.reset(new CoinsImpl(this)); m_subaddressAccount.reset(new SubaddressAccountImpl(this)); @@ -763,6 +756,35 @@ bool WalletImpl::recover(const std::string &path, const std::string &password, c return status() == Status_Ok; } +bool WalletImpl::recoverDeterministicWalletFromSpendKey(const std::string &path, const std::string &password, const std::string &language, const std::string &spendkey_string) +{ + clearStatus(); + m_errorString.clear(); + + m_recoveringFromSeed = true; + m_recoveringFromDevice = false; + + // parse spend key + crypto::secret_key spendkey; + if (!spendkey_string.empty()) { + cryptonote::blobdata spendkey_data; + if(!epee::string_tools::parse_hexstr_to_binbuff(spendkey_string, spendkey_data) || spendkey_data.size() != sizeof(crypto::secret_key)) + { + setStatusError(tr("failed to parse secret spend key")); + return false; + } + spendkey = *reinterpret_cast(spendkey_data.data()); + } + + try { + m_wallet->generate(path, password, spendkey, true, false); + setSeedLanguage(language); + } catch (const std::exception &e) { + setStatusCritical(e.what()); + } + return status() == Status_Ok; +} + bool WalletImpl::close(bool store) { @@ -1720,6 +1742,137 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const return createTransactionMultDest(std::vector {dst_addr}, payment_id, amount ? (std::vector {*amount}) : (optional>()), mixin_count, priority, subaddr_account, subaddr_indices); } +PendingTransaction *WalletImpl::createTransactionSingle(const string &key_image, const string &dst_addr, + const size_t outputs, PendingTransaction::Priority priority) +{ + clearStatus(); + // Pause refresh thread while creating transaction + pauseRefresh(); + + cryptonote::address_parse_info info; + + size_t fake_outs_count = m_wallet->adjust_mixin(m_wallet->default_mixin()); + + 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; + + bool error = false; + + crypto::key_image ki; + if (!epee::string_tools::hex_to_pod(key_image, ki)) + { + setStatusError(tr("failed to parse key image")); + error = true; + break; + } + + if (!cryptonote::get_account_address_from_str_or_url(info, m_wallet->nettype(), dst_addr)) + { + setStatusError(tr("Invalid destination address")); + error = true; + break; + } + if (info.has_payment_id) { + if (!extra_nonce.empty()) { + setStatusError(tr("a single transaction cannot use more than one payment id")); + error = true; + break; + } + set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, info.payment_id); + } + if (error) { + break; + } + if (!extra_nonce.empty() && !add_extra_nonce_to_tx_extra(extra, extra_nonce)) { + setStatusError(tr("failed to set up payment id, though it was decoded correctly")); + break; + } + try { + transaction->m_pending_tx = m_wallet->create_transactions_single(ki, info.address, info.is_subaddress, + outputs, fake_outs_count, 0 /* unlock time */, priority, extra); + + pendingTxPostProcess(transaction); + + if (multisig().isMultisig) { + auto tx_set = m_wallet->make_multisig_tx_set(transaction->m_pending_tx); + transaction->m_pending_tx = tx_set.m_ptx; + transaction->m_signers = tx_set.m_signers; + } + } catch (const tools::error::daemon_busy&) { + // TODO: make it translatable with "tr"? + setStatusError(tr("daemon is busy. Please try again later.")); + } catch (const tools::error::no_connection_to_daemon&) { + setStatusError(tr("no connection to daemon. Please make sure daemon is running.")); + } catch (const tools::error::wallet_rpc_error& e) { + setStatusError(tr("RPC error: ") + e.to_string()); + } catch (const tools::error::get_outs_error &e) { + setStatusError((boost::format(tr("failed to get outputs to mix: %s")) % e.what()).str()); + } catch (const tools::error::not_enough_unlocked_money& e) { + std::ostringstream writer; + + writer << boost::format(tr("not enough money to transfer, available only %s, sent amount %s")) % + print_money(e.available()) % + print_money(e.tx_amount()); + setStatusError(writer.str()); + } catch (const tools::error::not_enough_money& e) { + std::ostringstream writer; + + writer << boost::format(tr("not enough money to transfer, overall balance only %s, sent amount %s")) % + print_money(e.available()) % + print_money(e.tx_amount()); + setStatusError(writer.str()); + } catch (const tools::error::tx_not_possible& e) { + std::ostringstream writer; + + writer << boost::format(tr("not enough money to transfer, available only %s, transaction amount %s = %s + %s (fee)")) % + print_money(e.available()) % + print_money(e.tx_amount() + e.fee()) % + print_money(e.tx_amount()) % + print_money(e.fee()); + setStatusError(writer.str()); + } catch (const tools::error::not_enough_outs_to_mix& e) { + std::ostringstream writer; + writer << tr("not enough outputs for specified ring size") << " = " << (e.mixin_count() + 1) << ":"; + for (const std::pair outs_for_amount : e.scanty_outs()) { + writer << "\n" << tr("output amount") << " = " << print_money(outs_for_amount.first) << ", " << tr("found outputs to use") << " = " << outs_for_amount.second; + } + writer << "\n" << tr("Please sweep unmixable outputs."); + setStatusError(writer.str()); + } catch (const tools::error::tx_not_constructed&) { + setStatusError(tr("transaction was not constructed")); + } catch (const tools::error::tx_rejected& e) { + std::ostringstream writer; + writer << (boost::format(tr("transaction %s was rejected by daemon with status: ")) % get_transaction_hash(e.tx())) << e.status(); + setStatusError(writer.str()); + } catch (const tools::error::tx_sum_overflow& e) { + setStatusError(e.what()); + } catch (const tools::error::zero_destination&) { + setStatusError(tr("one of destinations is zero")); + } catch (const tools::error::tx_too_big& e) { + setStatusError(tr("failed to find a suitable way to split transactions")); + } catch (const tools::error::transfer_error& e) { + setStatusError(string(tr("unknown transfer error: ")) + e.what()); + } catch (const tools::error::wallet_internal_error& e) { + setStatusError(string(tr("internal error: ")) + e.what()); + } catch (const std::exception& e) { + setStatusError(string(tr("unexpected error: ")) + e.what()); + } catch (...) { + setStatusError(tr("unknown error")); + } + } while (false); + + statusWithErrorString(transaction->m_status, transaction->m_errorString); + // Resume refresh thread + startRefresh(); + return transaction; +} + PendingTransaction *WalletImpl::createSweepUnmixableTransaction() { @@ -1839,6 +1992,11 @@ AddressBook *WalletImpl::addressBook() return m_addressBook.get(); } +Coins *WalletImpl::coins() +{ + return m_coins.get(); +} + Subaddress *WalletImpl::subaddress() { return m_subaddress.get(); diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index f813db83e..84e2fdc58 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -46,6 +46,7 @@ class PendingTransactionImpl; class UnsignedTransactionImpl; class AddressBookImpl; class SubaddressImpl; +class CoinsImpl; class SubaddressAccountImpl; struct Wallet2CallbackImpl; class PendingTransactionInfoImpl; @@ -78,6 +79,10 @@ public: const std::string &address_string, const std::string &viewkey_string, const std::string &spendkey_string = ""); + bool recoverDeterministicWalletFromSpendKey(const std::string &path, + const std::string &password, + const std::string &language, + const std::string &spendkey_string); bool recoverFromDevice(const std::string &path, const std::string &password, const std::string &device_name); @@ -164,6 +169,10 @@ public: PendingTransaction::Priority priority = PendingTransaction::Priority_Low, uint32_t subaddr_account = 0, std::set subaddr_indices = {}) override; + + PendingTransaction * createTransactionSingle(const std::string &key_image, const std::string &dst_addr, + size_t outputs = 1, PendingTransaction::Priority priority = PendingTransaction::Priority_Low) override; + virtual PendingTransaction * createSweepUnmixableTransaction() override; bool submitTransaction(const std::string &fileName) override; virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename) override; @@ -181,6 +190,7 @@ public: PendingTransaction::Priority priority) const override; virtual TransactionHistory * history() override; virtual AddressBook * addressBook() override; + virtual Coins * coins() override; virtual Subaddress * subaddress() override; virtual SubaddressAccount * subaddressAccount() override; virtual void setListener(WalletListener * l) override; @@ -252,6 +262,7 @@ private: friend struct Wallet2CallbackImpl; friend class AddressBookImpl; friend class SubaddressImpl; + friend class CoinsImpl; friend class SubaddressAccountImpl; friend class PendingTransactionInfoImpl; friend class TransactionConstructionInfoImpl; @@ -265,6 +276,7 @@ private: std::unique_ptr m_wallet2Callback; std::unique_ptr m_addressBook; std::unique_ptr m_subaddress; + std::unique_ptr m_coins; std::unique_ptr m_subaddressAccount; // multi-threaded refresh stuff diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h index 7a49e4ee9..96e022987 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h @@ -249,6 +249,7 @@ struct TransactionInfo virtual std::string paymentId() const = 0; //! only applicable for output transactions virtual const std::vector & transfers() const = 0; + virtual const std::vector>> & rings() const = 0; }; /** * @brief The TransactionHistory - interface for displaying transaction history @@ -311,22 +312,66 @@ struct AddressBook virtual int lookupPaymentID(const std::string &payment_id) const = 0; }; +/** + * @brief The CoinsInfo - interface for displaying coins information + */ +struct CoinsInfo +{ + virtual ~CoinsInfo() = 0; + + virtual uint64_t blockHeight() const = 0; + virtual std::string hash() const = 0; + virtual size_t internalOutputIndex() const = 0; + virtual uint64_t globalOutputIndex() const = 0; + virtual bool spent() const = 0; + virtual bool frozen() const = 0; + virtual uint64_t spentHeight() const = 0; + virtual uint64_t amount() const = 0; + virtual bool rct() const = 0; + virtual bool keyImageKnown() const = 0; + virtual size_t pkIndex() const = 0; + virtual uint32_t subaddrIndex() const = 0; + virtual uint32_t subaddrAccount() const = 0; + virtual std::string address() const = 0; + virtual std::string addressLabel() const = 0; + virtual std::string keyImage() const = 0; + virtual uint64_t unlockTime() const = 0; + virtual bool unlocked() const = 0; + virtual std::string pubKey() const = 0; + virtual bool coinbase() const = 0; +}; + +struct Coins +{ + virtual ~Coins() = 0; + virtual int count() const = 0; + virtual CoinsInfo * coin(int index) const = 0; + virtual std::vector getAll() const = 0; + virtual void refresh() = 0; + virtual void setFrozen(int index) = 0; + virtual void thaw(int index) = 0; + virtual bool isTransferUnlocked(uint64_t unlockTime, uint64_t blockHeight) = 0; +}; + struct SubaddressRow { public: - SubaddressRow(std::size_t _rowId, const std::string &_address, const std::string &_label): + SubaddressRow(std::size_t _rowId, const std::string &_address, const std::string &_label, bool _used): m_rowId(_rowId), m_address(_address), - m_label(_label) {} + m_label(_label), + m_used(_used) {} private: std::size_t m_rowId; std::string m_address; std::string m_label; + bool m_used; 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;} + bool isUsed() const {return m_used;} }; struct Subaddress @@ -914,6 +959,18 @@ struct Wallet uint32_t subaddr_account = 0, std::set subaddr_indices = {}) = 0; + /*! + * \brief createTransactionSingle creates transaction with single input + * \param key_image key image as string + * \param dst_addr destination address as string + * \param priority + * \return PendingTransaction object. caller is responsible to check PendingTransaction::status() + * after object returned + */ + + virtual PendingTransaction * createTransactionSingle(const std::string &key_image, const std::string &dst_addr, + size_t outputs = 1, PendingTransaction::Priority = PendingTransaction::Priority_Low) = 0; + /*! * \brief createSweepUnmixableTransaction creates transaction with unmixable outputs. * \return PendingTransaction object. caller is responsible to check PendingTransaction::status() @@ -1008,6 +1065,7 @@ struct Wallet virtual TransactionHistory * history() = 0; virtual AddressBook * addressBook() = 0; + virtual Coins * coins() = 0; virtual Subaddress * subaddress() = 0; virtual SubaddressAccount * subaddressAccount() = 0; virtual void setListener(WalletListener *) = 0; @@ -1064,7 +1122,8 @@ struct Wallet /* * \brief signMessage - sign a message with the spend private key * \param message - the message to sign (arbitrary byte data) - * \return the signature + * \param address - the address to make the signature with, defaults to primary address (optional) + * \return the signature, empty string if the address is invalid or does not belong to the wallet */ virtual std::string signMessage(const std::string &message, const std::string &address = "") = 0; /*! @@ -1275,6 +1334,25 @@ struct WalletManager return createWalletFromKeys(path, password, language, testnet ? TESTNET : MAINNET, restoreHeight, addressString, viewKeyString, spendKeyString); } + /*! + * \brief recover deterministic wallet from spend key. + * \param path Name of wallet file to be created + * \param password Password of wallet file + * \param language language + * \param nettype Network type + * \param restoreHeight restore from start height + * \param spendKeyString spend key + * \param kdf_rounds Number of rounds for key derivation function + * \return Wallet instance (Wallet::status() needs to be called to check if recovered successfully) + */ + virtual Wallet * createDeterministicWalletFromSpendKey(const std::string &path, + const std::string &password, + const std::string &language, + NetworkType nettype, + uint64_t restoreHeight, + const std::string &spendKeyString, + uint64_t kdf_rounds = 1) = 0; + /*! * \deprecated this method creates a wallet WITHOUT a passphrase, use createWalletFromKeys(..., password, ...) instead * \brief recovers existing wallet using keys. Creates a view only wallet if spend key is omitted diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp index e81b8f83a..b166d8ac7 100644 --- a/src/wallet/api/wallet_manager.cpp +++ b/src/wallet/api/wallet_manager.cpp @@ -127,6 +127,22 @@ Wallet *WalletManagerImpl::createWalletFromKeys(const std::string &path, return wallet; } +Wallet *WalletManagerImpl::createDeterministicWalletFromSpendKey(const std::string &path, + const std::string &password, + const std::string &language, + NetworkType nettype, + uint64_t restoreHeight, + const std::string &spendkey_string, + uint64_t kdf_rounds) +{ + WalletImpl * wallet = new WalletImpl(nettype, kdf_rounds); + if(restoreHeight > 0){ + wallet->setRefreshFromBlockHeight(restoreHeight); + } + wallet->recoverDeterministicWalletFromSpendKey(path, password, language, spendkey_string); + return wallet; +} + Wallet *WalletManagerImpl::createWalletFromDevice(const std::string &path, const std::string &password, NetworkType nettype, diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h index a223e1df9..206aedc14 100644 --- a/src/wallet/api/wallet_manager.h +++ b/src/wallet/api/wallet_manager.h @@ -67,6 +67,13 @@ public: const std::string &addressString, const std::string &viewKeyString, const std::string &spendKeyString = "") override; + virtual Wallet * createDeterministicWalletFromSpendKey(const std::string &path, + const std::string &password, + const std::string &language, + NetworkType nettype, + uint64_t restoreHeight, + const std::string &spendkey_string, + uint64_t kdf_rounds) override; virtual Wallet * createWalletFromDevice(const std::string &path, const std::string &password, NetworkType nettype, diff --git a/src/wallet/node_rpc_proxy.cpp b/src/wallet/node_rpc_proxy.cpp index 0a9ea8f7b..5557cd25c 100644 --- a/src/wallet/node_rpc_proxy.cpp +++ b/src/wallet/node_rpc_proxy.cpp @@ -193,7 +193,7 @@ boost::optional NodeRPCProxy::get_target_height(uint64_t &height) auto res = get_info(); if (res) return res; - height = m_target_height; + height = m_target_height > m_height ? m_target_height : m_height; return boost::optional(); } diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 3c48ec0f6..0a68b085b 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1550,6 +1550,14 @@ void wallet2::add_subaddress_account(const std::string& label) m_subaddress_labels[index_major][0] = label; } //---------------------------------------------------------------------------------------------------- +bool wallet2::get_subaddress_used(const cryptonote::subaddress_index& index) +{ + return std::find_if(m_transfers.begin(), m_transfers.end(), + [this, index](const transfer_details &td) { + return td.m_subaddr_index == index; + }) != m_transfers.end(); +} +//---------------------------------------------------------------------------------------------------- void wallet2::add_subaddress(uint32_t index_major, const std::string& label) { THROW_WALLET_EXCEPTION_IF(index_major >= m_subaddress_labels.size(), error::account_index_outofbound); @@ -2155,13 +2163,14 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote uint64_t amount = tx.vout[o].amount ? tx.vout[o].amount : tx_scan_info[o].amount; if (!pool) { - m_transfers.push_back(transfer_details{}); - transfer_details& td = m_transfers.back(); - td.m_block_height = height; - td.m_internal_output_index = o; - td.m_global_output_index = o_indices[o]; - td.m_tx = (const cryptonote::transaction_prefix&)tx; - td.m_txid = txid; + boost::unique_lock lock(m_transfers_mutex); + m_transfers.push_back(transfer_details{}); + transfer_details& td = m_transfers.back(); + td.m_block_height = height; + td.m_internal_output_index = o; + td.m_global_output_index = o_indices[o]; + td.m_tx = (const cryptonote::transaction_prefix&)tx; + td.m_txid = txid; td.m_key_image = tx_scan_info[o].ki; td.m_key_image_known = !m_watch_only && !m_multisig; if (!td.m_key_image_known) @@ -2258,12 +2267,13 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote uint64_t extra_amount = amount - burnt; if (!pool) { + boost::unique_lock lock(m_transfers_mutex); transfer_details &td = m_transfers[kit->second]; - td.m_block_height = height; - td.m_internal_output_index = o; - td.m_global_output_index = o_indices[o]; - td.m_tx = (const cryptonote::transaction_prefix&)tx; - td.m_txid = txid; + td.m_block_height = height; + td.m_internal_output_index = o; + td.m_global_output_index = o_indices[o]; + td.m_tx = (const cryptonote::transaction_prefix&)tx; + td.m_txid = txid; td.m_amount = amount; td.m_pk_index = pk_index - 1; td.m_subaddr_index = tx_scan_info[o].received->index; diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 4bcc6a2c0..a8bbd3101 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1007,6 +1007,7 @@ private: std::string get_subaddress_as_str(const cryptonote::subaddress_index& index) const; std::string get_address_as_str() const { return get_subaddress_as_str({0, 0}); } std::string get_integrated_address_as_str(const crypto::hash8& payment_id) const; + bool get_subaddress_used(const cryptonote::subaddress_index& index); void add_subaddress_account(const std::string& label); size_t get_num_subaddress_accounts() const { return m_subaddress_labels.size(); } size_t get_num_subaddresses(uint32_t index_major) const { return index_major < m_subaddress_labels.size() ? m_subaddress_labels[index_major].size() : 0; } @@ -1677,6 +1678,7 @@ private: static std::string get_default_daemon_address() { CRITICAL_REGION_LOCAL(default_daemon_address_lock); return default_daemon_address; } + boost::shared_mutex m_transfers_mutex; private: /*! * \brief Stores wallet information to wallet file.