diff --git a/src/wallet/api/address_book.cpp b/src/wallet/api/address_book.cpp index f69a69ca3..96090d9f5 100644 --- a/src/wallet/api/address_book.cpp +++ b/src/wallet/api/address_book.cpp @@ -70,6 +70,25 @@ bool AddressBookImpl::addRow(const std::string &dst_addr , const std::string &pa return r; } +bool AddressBookImpl::setDescription(std::size_t index, const std::string &description) +{ + clearStatus(); + + const auto ab = m_wallet->m_wallet->get_address_book(); + if (index >= ab.size()){ + return false; + } + + tools::wallet2::address_book_row entry = ab[index]; + entry.m_description = description; + bool r = m_wallet->m_wallet->set_address_book_row(index, entry.m_address, NULL, entry.m_description, entry.m_is_subaddress); + if (r) + refresh(); + else + m_errorCode = General_Error; + return r; +} + void AddressBookImpl::refresh() { LOG_PRINT_L2("Refreshing addressbook"); diff --git a/src/wallet/api/address_book.h b/src/wallet/api/address_book.h index f287969f3..e22f474fb 100644 --- a/src/wallet/api/address_book.h +++ b/src/wallet/api/address_book.h @@ -45,6 +45,7 @@ public: 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; // Error codes. See AddressBook:ErrorCode enum in wallet2_api.h diff --git a/src/wallet/api/transaction_history.cpp b/src/wallet/api/transaction_history.cpp index bcb300889..d8e4aab65 100644 --- a/src/wallet/api/transaction_history.cpp +++ b/src/wallet/api/transaction_history.cpp @@ -92,6 +92,17 @@ 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: @@ -126,10 +137,13 @@ void TransactionHistoryImpl::refresh() payment_id = payment_id.substr(0,16); TransactionInfoImpl * ti = new TransactionInfoImpl(); ti->m_paymentid = payment_id; + ti->m_coinbase = pd.m_coinbase; ti->m_amount = pd.m_amount; + ti->m_fee = pd.m_fee; ti->m_direction = TransactionInfo::Direction_In; ti->m_hash = string_tools::pod_to_hex(pd.m_tx_hash); ti->m_blockheight = pd.m_block_height; + ti->m_description = m_wallet->m_wallet->get_tx_note(pd.m_tx_hash); ti->m_subaddrIndex = { pd.m_subaddr_index.minor }; ti->m_subaddrAccount = pd.m_subaddr_index.major; ti->m_label = m_wallet->m_wallet->get_subaddress_label(pd.m_subaddr_index); @@ -173,6 +187,7 @@ void TransactionHistoryImpl::refresh() ti->m_direction = TransactionInfo::Direction_Out; ti->m_hash = string_tools::pod_to_hex(hash); ti->m_blockheight = pd.m_block_height; + ti->m_description = m_wallet->m_wallet->get_tx_note(hash); ti->m_subaddrIndex = pd.m_subaddr_indices; ti->m_subaddrAccount = pd.m_subaddr_account; 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()}) : ""; @@ -183,6 +198,7 @@ void TransactionHistoryImpl::refresh() 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)}); } + m_history.push_back(ti); } @@ -207,11 +223,16 @@ void TransactionHistoryImpl::refresh() ti->m_failed = is_failed; ti->m_pending = true; ti->m_hash = string_tools::pod_to_hex(hash); + ti->m_description = m_wallet->m_wallet->get_tx_note(hash); ti->m_subaddrIndex = pd.m_subaddr_indices; ti->m_subaddrAccount = pd.m_subaddr_account; 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) + { + ti->m_transfers.push_back({d.amount, d.address(m_wallet->m_wallet->nettype(), pd.m_payment_id)}); + } m_history.push_back(ti); } @@ -230,6 +251,7 @@ void TransactionHistoryImpl::refresh() ti->m_direction = TransactionInfo::Direction_In; ti->m_hash = string_tools::pod_to_hex(pd.m_tx_hash); ti->m_blockheight = pd.m_block_height; + ti->m_description = m_wallet->m_wallet->get_tx_note(pd.m_tx_hash); ti->m_pending = true; ti->m_subaddrIndex = { pd.m_subaddr_index.minor }; ti->m_subaddrAccount = pd.m_subaddr_index.major; diff --git a/src/wallet/api/transaction_history.h b/src/wallet/api/transaction_history.h index 8f3805788..60f12d771 100644 --- a/src/wallet/api/transaction_history.h +++ b/src/wallet/api/transaction_history.h @@ -45,6 +45,7 @@ public: virtual TransactionInfo * transaction(const std::string &id) const; virtual std::vector getAll() const; virtual void refresh(); + virtual void setTxNote(const std::string &txid, const std::string ¬e); private: diff --git a/src/wallet/api/transaction_info.cpp b/src/wallet/api/transaction_info.cpp index 5ae3a6937..33e7856db 100644 --- a/src/wallet/api/transaction_info.cpp +++ b/src/wallet/api/transaction_info.cpp @@ -45,6 +45,7 @@ TransactionInfoImpl::TransactionInfoImpl() : m_direction(Direction_Out) , m_pending(false) , m_failed(false) + , m_coinbase(false) , m_amount(0) , m_fee(0) , m_blockheight(0) @@ -77,6 +78,11 @@ bool TransactionInfoImpl::isFailed() const return m_failed; } +bool TransactionInfoImpl::isCoinbase() const +{ + return m_coinbase; +} + uint64_t TransactionInfoImpl::amount() const { return m_amount; @@ -92,6 +98,11 @@ uint64_t TransactionInfoImpl::blockHeight() const return m_blockheight; } +std::string TransactionInfoImpl::description() const +{ + return m_description; +} + std::set TransactionInfoImpl::subaddrIndex() const { return m_subaddrIndex; diff --git a/src/wallet/api/transaction_info.h b/src/wallet/api/transaction_info.h index 73bb7689d..8bc36a8e9 100644 --- a/src/wallet/api/transaction_info.h +++ b/src/wallet/api/transaction_info.h @@ -46,10 +46,12 @@ public: //! true if hold virtual bool isPending() const override; virtual bool isFailed() const override; + virtual bool isCoinbase() const override; virtual uint64_t amount() const override; //! always 0 for incoming txes virtual uint64_t fee() const override; virtual uint64_t blockHeight() const override; + virtual std::string description() const override; virtual std::set subaddrIndex() const override; virtual uint32_t subaddrAccount() const override; virtual std::string label() const override; @@ -65,9 +67,11 @@ private: int m_direction; bool m_pending; bool m_failed; + bool m_coinbase; uint64_t m_amount; uint64_t m_fee; uint64_t m_blockheight; + std::string m_description; std::set m_subaddrIndex; // always unique index for incoming transfers; can be multiple indices for outgoing transfers uint32_t m_subaddrAccount; std::string m_label; diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index 9acc8484c..b2ffce229 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -791,11 +791,11 @@ bool WalletImpl::close(bool store) return result; } -std::string WalletImpl::seed() const +std::string WalletImpl::seed(const std::string& seed_offset) const { epee::wipeable_string seed; if (m_wallet) - m_wallet->get_seed(seed); + m_wallet->get_seed(seed, seed_offset); return std::string(seed.data(), seed.size()); // TODO } @@ -1168,7 +1168,7 @@ bool WalletImpl::submitTransaction(const string &fileName) { return true; } -bool WalletImpl::exportKeyImages(const string &filename) +bool WalletImpl::exportKeyImages(const string &filename, bool all) { if (m_wallet->watch_only()) { @@ -1178,7 +1178,7 @@ bool WalletImpl::exportKeyImages(const string &filename) try { - if (!m_wallet->export_key_images(filename)) + if (!m_wallet->export_key_images(filename), all) { setStatusError(tr("failed to save file ") + filename); return false; @@ -1216,6 +1216,68 @@ bool WalletImpl::importKeyImages(const string &filename) 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; +} + void WalletImpl::addSubaddressAccount(const std::string& label) { m_wallet->add_subaddress_account(label); @@ -2104,6 +2166,11 @@ bool WalletImpl::watchOnly() const return m_wallet->watch_only(); } +bool WalletImpl::isDeterministic() const +{ + return m_wallet->is_deterministic(); +} + void WalletImpl::clearStatus() const { boost::lock_guard l(m_statusMutex); @@ -2305,6 +2372,10 @@ bool WalletImpl::rescanSpent() return true; } +void WalletImpl::setOffline(bool offline) +{ + m_wallet->set_offline(offline); +} void WalletImpl::hardForkInfo(uint8_t &version, uint64_t &earliest_height) const { diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index 3bf3e759b..ce2d7d7e4 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -81,7 +81,7 @@ public: const std::string &device_name); Device getDeviceType() const override; bool close(bool store = true); - std::string seed() const override; + 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 *) {} @@ -129,6 +129,7 @@ public: 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; @@ -164,8 +165,10 @@ public: virtual PendingTransaction * createSweepUnmixableTransaction() override; bool submitTransaction(const std::string &fileName) override; virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename) override; - bool exportKeyImages(const std::string &filename) 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; virtual void disposeTransaction(PendingTransaction * t) override; virtual uint64_t estimateTransactionFee(const std::vector> &destinations, @@ -181,6 +184,8 @@ public: 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 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; diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h index 999823ff1..e34332734 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h @@ -182,9 +182,11 @@ struct TransactionInfo virtual int direction() const = 0; virtual bool isPending() const = 0; virtual bool isFailed() const = 0; + virtual bool isCoinbase() const = 0; virtual uint64_t amount() const = 0; virtual uint64_t fee() const = 0; virtual uint64_t blockHeight() const = 0; + virtual std::string description() const = 0; virtual std::set subaddrIndex() const = 0; virtual uint32_t subaddrAccount() const = 0; virtual std::string label() const = 0; @@ -208,6 +210,7 @@ struct TransactionHistory virtual TransactionInfo * transaction(const std::string &id) const = 0; virtual std::vector getAll() const = 0; virtual void refresh() = 0; + virtual void setTxNote(const std::string &txid, const std::string ¬e) = 0; }; /** @@ -250,6 +253,7 @@ struct AddressBook virtual std::vector getAll() const = 0; virtual bool addRow(const std::string &dst_addr , const std::string &payment_id, const std::string &description) = 0; virtual bool deleteRow(std::size_t rowId) = 0; + virtual bool setDescription(std::size_t index, const std::string &description) = 0; virtual void refresh() = 0; virtual std::string errorString() const = 0; virtual int errorCode() const = 0; @@ -442,7 +446,7 @@ struct Wallet }; virtual ~Wallet() = 0; - virtual std::string seed() const = 0; + virtual std::string seed(const std::string& seed_offset = "") const = 0; virtual std::string getSeedLanguage() const = 0; virtual void setSeedLanguage(const std::string &arg) = 0; //! returns wallet status (Status_Ok | Status_Error) @@ -622,6 +626,12 @@ struct Wallet */ virtual bool watchOnly() const = 0; + /** + * @brief isDeterministic - checks if wallet keys are deterministic + * @return - true if deterministic + */ + virtual bool isDeterministic() const = 0; + /** * @brief blockChainHeight - returns current blockchain height * @return @@ -897,9 +907,10 @@ struct Wallet /*! * \brief exportKeyImages - exports key images to file * \param filename + * \param all - export all key images or only those that have not yet been exported * \return - true on success */ - virtual bool exportKeyImages(const std::string &filename) = 0; + virtual bool exportKeyImages(const std::string &filename, bool all = false) = 0; /*! * \brief importKeyImages - imports key images from file @@ -908,6 +919,19 @@ struct Wallet */ virtual bool importKeyImages(const std::string &filename) = 0; + /*! + * \brief importOutputs - exports outputs to file + * \param filename + * \return - true on success + */ + virtual bool exportOutputs(const std::string &filename, bool all = false) = 0; + + /*! + * \brief importOutputs - imports outputs from file + * \param filename + * \return - true on success + */ + virtual bool importOutputs(const std::string &filename) = 0; virtual TransactionHistory * history() = 0; virtual AddressBook * addressBook() = 0; @@ -1003,6 +1027,12 @@ struct Wallet * \return true on success */ virtual bool rescanSpent() = 0; + + /* + * \brief setOffline - toggle set offline on/off + * \param offline - true/false + */ + virtual void setOffline(bool offline) = 0; //! blackballs a set of outputs virtual bool blackballOutputs(const std::vector &outputs, bool add) = 0;