diff --git a/src/wallet/api/CMakeLists.txt b/src/wallet/api/CMakeLists.txt index 30eb4ce03..fca925ad2 100644 --- a/src/wallet/api/CMakeLists.txt +++ b/src/wallet/api/CMakeLists.txt @@ -35,12 +35,16 @@ set(wallet_api_sources wallet_manager.cpp transaction_info.cpp transaction_history.cpp + transaction_construction_info.cpp + pending_transaction_info.cpp pending_transaction.cpp utils.cpp address_book.cpp subaddress.cpp subaddress_account.cpp - unsigned_transaction.cpp) + unsigned_transaction.cpp + coins.cpp + coins_info.cpp) set(wallet_api_headers wallet2_api.h) @@ -50,12 +54,16 @@ set(wallet_api_private_headers wallet_manager.h transaction_info.h transaction_history.h + transaction_construction_info.h + pending_transaction_info.h pending_transaction.h common_defines.h address_book.h subaddress.h subaddress_account.h - unsigned_transaction.h) + unsigned_transaction.h + coins.h + coins_info.h) monero_private_headers(wallet_api ${wallet_api_private_headers}) diff --git a/src/wallet/api/coins.cpp b/src/wallet/api/coins.cpp new file mode 100644 index 000000000..99c3a51f8 --- /dev/null +++ b/src/wallet/api/coins.cpp @@ -0,0 +1,122 @@ +#include "coins.h" +#include "coins_info.h" +#include "wallet.h" +#include "crypto/hash.h" +#include "wallet/wallet2.h" +#include "common_defines.h" + +#include +#include + +using namespace epee; + +namespace Monero { + + Coins::~Coins() = default; + + CoinsImpl::CoinsImpl(WalletImpl *wallet) + : m_wallet(wallet) {} + + CoinsImpl::~CoinsImpl() + { + for (auto t : m_rows) + delete t; + } + + int CoinsImpl::count() const + { + boost::shared_lock lock(m_rowsMutex); + int result = m_rows.size(); + return result; + } + + CoinsInfo *CoinsImpl::coin(int index) const + { + boost::shared_lock lock(m_rowsMutex); + // sanity check + if (index < 0) + return nullptr; + auto index_ = static_cast(index); + return index_ < m_rows.size() ? m_rows[index_] : nullptr; + } + + std::vector CoinsImpl::getAll() const + { + boost::shared_lock lock(m_rowsMutex); + return m_rows; + } + + + void CoinsImpl::refresh() + { + LOG_PRINT_L2("Refreshing coins"); + + boost::unique_lock lock(m_rowsMutex); + boost::shared_lock transfers_lock(m_wallet->m_wallet->m_transfers_mutex); + + // delete old outputs; + for (auto t : m_rows) + delete t; + m_rows.clear(); + + for (size_t i = 0; i < m_wallet->m_wallet->get_num_transfer_details(); ++i) + { + const tools::wallet2::transfer_details &td = m_wallet->m_wallet->get_transfer_details(i); + + auto ci = new CoinsInfoImpl(); + ci->m_blockHeight = td.m_block_height; + ci->m_hash = string_tools::pod_to_hex(td.m_txid); + ci->m_internalOutputIndex = td.m_internal_output_index; + ci->m_globalOutputIndex = td.m_global_output_index; + ci->m_spent = td.m_spent; + ci->m_frozen = td.m_frozen; + ci->m_spentHeight = td.m_spent_height; + ci->m_amount = td.m_amount; + ci->m_rct = td.m_rct; + ci->m_keyImageKnown = td.m_key_image_known; + ci->m_pkIndex = td.m_pk_index; + ci->m_subaddrIndex = td.m_subaddr_index.minor; + ci->m_subaddrAccount = td.m_subaddr_index.major; + ci->m_address = m_wallet->m_wallet->get_subaddress_as_str(td.m_subaddr_index); // todo: this is expensive, cache maybe? + ci->m_addressLabel = m_wallet->m_wallet->get_subaddress_label(td.m_subaddr_index); + ci->m_keyImage = string_tools::pod_to_hex(td.m_key_image); + ci->m_unlockTime = td.m_tx.unlock_time; + ci->m_unlocked = m_wallet->m_wallet->is_transfer_unlocked(td); + ci->m_pubKey = string_tools::pod_to_hex(td.get_public_key()); + ci->m_coinbase = td.m_tx.vin.size() == 1 && td.m_tx.vin[0].type() == typeid(cryptonote::txin_gen); + + m_rows.push_back(ci); + } + } + + void CoinsImpl::setFrozen(int index) + { + try + { + m_wallet->m_wallet->freeze(index); + refresh(); + } + catch (const std::exception& e) + { + LOG_ERROR("setLabel: " << e.what()); + } + } + + void CoinsImpl::thaw(int index) + { + try + { + m_wallet->m_wallet->thaw(index); + refresh(); + } + catch (const std::exception& e) + { + LOG_ERROR("thaw: " << e.what()); + } + } + + bool CoinsImpl::isTransferUnlocked(uint64_t unlockTime, uint64_t blockHeight) { + return m_wallet->m_wallet->is_transfer_unlocked(unlockTime, blockHeight); + } + +} // namespace diff --git a/src/wallet/api/coins.h b/src/wallet/api/coins.h new file mode 100644 index 000000000..3293d8ae9 --- /dev/null +++ b/src/wallet/api/coins.h @@ -0,0 +1,34 @@ +#ifndef WOWLET_COINS_H +#define WOWLET_COINS_H + +#include "wallet/api/wallet2_api.h" +#include "wallet/wallet2.h" + +namespace Monero { + + class WalletImpl; + + class CoinsImpl : public Coins + { + public: + explicit CoinsImpl(WalletImpl * wallet); + ~CoinsImpl() override; + int count() const override; + CoinsInfo * coin(int index) const override; + std::vector getAll() const override; + void refresh() override; + + void setFrozen(int index) override; + void thaw(int index) override; + + bool isTransferUnlocked(uint64_t unlockTime, uint64_t blockHeight) override; + + private: + WalletImpl *m_wallet; + std::vector m_rows; + mutable boost::shared_mutex m_rowsMutex; + }; + +} + +#endif //WOWLET_COINS_H diff --git a/src/wallet/api/coins_info.cpp b/src/wallet/api/coins_info.cpp new file mode 100644 index 000000000..06d874ddf --- /dev/null +++ b/src/wallet/api/coins_info.cpp @@ -0,0 +1,118 @@ +#include "coins_info.h" + +using namespace std; + +namespace Monero { + + CoinsInfo::~CoinsInfo() = default; + + CoinsInfoImpl::CoinsInfoImpl() + : m_blockHeight(0) + , m_internalOutputIndex(0) + , m_globalOutputIndex(0) + , m_spent(false) + , m_frozen(false) + , m_spentHeight(0) + , m_amount(0) + , m_rct(false) + , m_keyImageKnown(false) + , m_pkIndex(0) + , m_subaddrAccount(0) + , m_subaddrIndex(0) + , m_unlockTime(0) + , m_unlocked(false) + { + + } + + CoinsInfoImpl::~CoinsInfoImpl() = default; + + uint64_t CoinsInfoImpl::blockHeight() const + { + return m_blockHeight; + } + + string CoinsInfoImpl::hash() const + { + return m_hash; + } + + size_t CoinsInfoImpl::internalOutputIndex() const { + return m_internalOutputIndex; + } + + uint64_t CoinsInfoImpl::globalOutputIndex() const + { + return m_globalOutputIndex; + } + + bool CoinsInfoImpl::spent() const + { + return m_spent; + } + + bool CoinsInfoImpl::frozen() const + { + return m_frozen; + } + + uint64_t CoinsInfoImpl::spentHeight() const + { + return m_spentHeight; + } + + uint64_t CoinsInfoImpl::amount() const + { + return m_amount; + } + + bool CoinsInfoImpl::rct() const { + return m_rct; + } + + bool CoinsInfoImpl::keyImageKnown() const { + return m_keyImageKnown; + } + + size_t CoinsInfoImpl::pkIndex() const { + return m_pkIndex; + } + + uint32_t CoinsInfoImpl::subaddrIndex() const { + return m_subaddrIndex; + } + + uint32_t CoinsInfoImpl::subaddrAccount() const { + return m_subaddrAccount; + } + + string CoinsInfoImpl::address() const { + return m_address; + } + + string CoinsInfoImpl::addressLabel() const { + return m_addressLabel; + } + + string CoinsInfoImpl::keyImage() const { + return m_keyImage; + } + + uint64_t CoinsInfoImpl::unlockTime() const { + return m_unlockTime; + } + + bool CoinsInfoImpl::unlocked() const { + return m_unlocked; + } + + string CoinsInfoImpl::pubKey() const { + return m_pubKey; + } + + bool CoinsInfoImpl::coinbase() const { + return m_coinbase; + } +} // namespace + +namespace Bitmonero = Monero; diff --git a/src/wallet/api/coins_info.h b/src/wallet/api/coins_info.h new file mode 100644 index 000000000..c76109334 --- /dev/null +++ b/src/wallet/api/coins_info.h @@ -0,0 +1,67 @@ +#ifndef WOWLET_COINS_INFO_H +#define WOWLET_COINS_INFO_H + +#include "wallet/api/wallet2_api.h" +#include +#include + +namespace Monero { + + class CoinsImpl; + + class CoinsInfoImpl : public CoinsInfo + { + public: + CoinsInfoImpl(); + ~CoinsInfoImpl(); + + virtual uint64_t blockHeight() const override; + virtual std::string hash() const override; + virtual size_t internalOutputIndex() const override; + virtual uint64_t globalOutputIndex() const override; + virtual bool spent() const override; + virtual bool frozen() const override; + virtual uint64_t spentHeight() const override; + virtual uint64_t amount() const override; + virtual bool rct() const override; + virtual bool keyImageKnown() const override; + virtual size_t pkIndex() const override; + virtual uint32_t subaddrIndex() const override; + virtual uint32_t subaddrAccount() const override; + virtual std::string address() const override; + virtual std::string addressLabel() const override; + virtual std::string keyImage() const override; + virtual uint64_t unlockTime() const override; + virtual bool unlocked() const override; + virtual std::string pubKey() const override; + virtual bool coinbase() const override; + + private: + uint64_t m_blockHeight; + std::string m_hash; + size_t m_internalOutputIndex; + uint64_t m_globalOutputIndex; + bool m_spent; + bool m_frozen; + uint64_t m_spentHeight; + uint64_t m_amount; + bool m_rct; + bool m_keyImageKnown; + size_t m_pkIndex; + uint32_t m_subaddrIndex; + uint32_t m_subaddrAccount; + std::string m_address; + std::string m_addressLabel; + std::string m_keyImage; + uint64_t m_unlockTime; + bool m_unlocked; + std::string m_pubKey; + bool m_coinbase; + + friend class CoinsImpl; + + }; + +} // namespace + +#endif //WOWLET_COINS_INFO_H diff --git a/src/wallet/api/pending_transaction.cpp b/src/wallet/api/pending_transaction.cpp index f7e74591f..7ccffc310 100644 --- a/src/wallet/api/pending_transaction.cpp +++ b/src/wallet/api/pending_transaction.cpp @@ -35,6 +35,7 @@ #include "cryptonote_basic/cryptonote_format_utils.h" #include "cryptonote_basic/cryptonote_basic_impl.h" #include "common/base58.h" +#include "string_coding.h" #include #include @@ -263,4 +264,50 @@ std::vector PendingTransactionImpl::signersKeys() const { return keys; } +std::string PendingTransactionImpl::unsignedTxToBin() const { + return m_wallet.m_wallet->dump_tx_to_str(m_pending_tx); +} + +std::string PendingTransactionImpl::unsignedTxToBase64() const { + return epee::string_encoding::base64_encode(m_wallet.m_wallet->dump_tx_to_str(m_pending_tx)); +} + +std::string PendingTransactionImpl::signedTxToHex(int index) const { + auto index_ = static_cast(index); + if (index < 0 || index_ >= m_pending_tx.size()) { + return ""; + } + + return epee::string_tools::buff_to_hex_nodelimer(cryptonote::tx_to_blob(m_pending_tx[index_].tx)); +} + +size_t PendingTransactionImpl::signedTxSize(int index) const { + auto index_ = static_cast(index); + if (index < 0 || index_ >= m_pending_tx.size()) { + return 0; + } + + return cryptonote::tx_to_blob(m_pending_tx[index_].tx).size(); +} + +PendingTransactionInfo * PendingTransactionImpl::transaction(int index) const { + if (index < 0) + return nullptr; + auto index_ = static_cast(index); + return index_ < m_pending_tx_info.size() ? m_pending_tx_info[index_] : nullptr; +} + +void PendingTransactionImpl::refresh() { + for (auto t : m_pending_tx_info) + delete t; + m_pending_tx_info.clear(); + + for (const auto& p : m_pending_tx) + m_pending_tx_info.push_back(new PendingTransactionInfoImpl(m_wallet, p)); +} + +std::vector PendingTransactionImpl::getAll() const { + return m_pending_tx_info; +} + } diff --git a/src/wallet/api/pending_transaction.h b/src/wallet/api/pending_transaction.h index 274c60851..de0c75377 100644 --- a/src/wallet/api/pending_transaction.h +++ b/src/wallet/api/pending_transaction.h @@ -30,6 +30,7 @@ #include "wallet/api/wallet2_api.h" #include "wallet/wallet2.h" +#include "pending_transaction_info.h" #include #include @@ -53,6 +54,13 @@ public: uint64_t txCount() const override; std::vector subaddrAccount() const override; std::vector> subaddrIndices() const override; + std::string unsignedTxToBin() const override; + std::string unsignedTxToBase64() const override; + std::string signedTxToHex(int index) const override; + size_t signedTxSize(int index) const override; + void refresh() override; + std::vector getAll() const override; + PendingTransactionInfo * transaction(int index) const override; // TODO: continue with interface; std::string multisigSignData() override; @@ -66,6 +74,7 @@ private: int m_status; std::string m_errorString; std::vector m_pending_tx; + std::vector m_pending_tx_info; std::unordered_set m_signers; std::vector m_tx_device_aux; std::vector m_key_images; diff --git a/src/wallet/api/pending_transaction_info.cpp b/src/wallet/api/pending_transaction_info.cpp new file mode 100644 index 000000000..86499e006 --- /dev/null +++ b/src/wallet/api/pending_transaction_info.cpp @@ -0,0 +1,47 @@ +#include "pending_transaction_info.h" +#include "transaction_construction_info.h" + +using namespace std; + +namespace Monero { + + PendingTransactionInfo::~PendingTransactionInfo() = default; + + PendingTransactionInfoImpl::PendingTransactionInfoImpl(WalletImpl &wallet, const tools::wallet2::pending_tx & ptx) + : m_wallet(wallet) + , m_ptx(ptx) + , m_constructionData(new TransactionConstructionInfoImpl(wallet, ptx.construction_data)) + { + } + + PendingTransactionInfoImpl::~PendingTransactionInfoImpl() = default; + + uint64_t PendingTransactionInfoImpl::fee() const + { + return m_ptx.fee; + } + + uint64_t PendingTransactionInfoImpl::dust() const + { + return m_ptx.dust; + } + + bool PendingTransactionInfoImpl::dustAddedToFee() const + { + return m_ptx.dust_added_to_fee; + } + + std::string PendingTransactionInfoImpl::txKey() const + { + return epee::string_tools::pod_to_hex(m_ptx.tx_key); + } + + TransactionConstructionInfo * PendingTransactionInfoImpl::constructionData() const { + return m_constructionData; + } + +// TransactionConstructionInfo::Output TransactionConstructionInfoImpl::change() const { +// return Output( +// {m_ptx.change_dts.amount, m_ptx.change_dts.address(m_wallet.m_wallet->nettype(), crypto::hash())}); +// } +} \ No newline at end of file diff --git a/src/wallet/api/pending_transaction_info.h b/src/wallet/api/pending_transaction_info.h new file mode 100644 index 000000000..58ccc4f33 --- /dev/null +++ b/src/wallet/api/pending_transaction_info.h @@ -0,0 +1,37 @@ +#ifndef WOWLET_PENDING_TX_H +#define WOWLET_PENDING_TX_H + +#include "wallet/api/wallet2_api.h" +#include "wallet/wallet2.h" +#include "wallet.h" +#include + +namespace Monero { + +class PendingTransactionImpl; + +class PendingTransactionInfoImpl : public PendingTransactionInfo +{ +public: + PendingTransactionInfoImpl(WalletImpl &wallet, const tools::wallet2::pending_tx & ptx); + ~PendingTransactionInfoImpl() override; + + uint64_t fee() const override; + uint64_t dust() const override; + bool dustAddedToFee() const override; + std::string txKey() const override; + TransactionConstructionInfo *constructionData() const override; +// Output change() const override; + +private: + friend class WalletImpl; + WalletImpl &m_wallet; + tools::wallet2::pending_tx m_ptx; + TransactionConstructionInfo *m_constructionData; +}; + +} + + + +#endif //FEATHER_PENDING_TX_H \ No newline at end of file diff --git a/src/wallet/api/subaddress.cpp b/src/wallet/api/subaddress.cpp index 227bb343d..8e2522d87 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_construction_info.cpp b/src/wallet/api/transaction_construction_info.cpp new file mode 100644 index 000000000..10cef2a57 --- /dev/null +++ b/src/wallet/api/transaction_construction_info.cpp @@ -0,0 +1,63 @@ +#include "transaction_construction_info.h" + +using namespace std; + +namespace Monero { + TransactionConstructionInfo::~TransactionConstructionInfo() = default; + + TransactionConstructionInfo::Input::Input(uint64_t _amount, const std::string &_pubkey) + : amount(_amount), pubkey(_pubkey) {} + + TransactionConstructionInfo::Output::Output(uint64_t _amount, const std::string &_address) + : amount(_amount), address(_address) {} + + TransactionConstructionInfoImpl::TransactionConstructionInfoImpl(WalletImpl &wallet, const tools::wallet2::tx_construction_data & txcd) + : m_wallet(wallet) + , m_txcd(txcd) {} + + TransactionConstructionInfoImpl::~TransactionConstructionInfoImpl() = default; + + uint64_t TransactionConstructionInfoImpl::unlockTime() const { + return m_txcd.unlock_time; + } + + std::set TransactionConstructionInfoImpl::subaddressIndices() const { + return m_txcd.subaddr_indices; + } + + std::vector TransactionConstructionInfoImpl::subaddresses() const { + std::vector s; + auto major = m_txcd.subaddr_account; + for (const auto &minor : m_txcd.subaddr_indices) { + s.push_back(m_wallet.m_wallet->get_subaddress_as_str({major, minor})); + } + return s; + } + + uint64_t TransactionConstructionInfoImpl::minMixinCount() const { + uint64_t min_mixin = -1; + for (const auto &source : m_txcd.sources) { + size_t mixin = source.outputs.size() - 1; + if (mixin < min_mixin) + min_mixin = mixin; + } + + return min_mixin; + } + + std::vector TransactionConstructionInfoImpl::inputs() const { + std::vector inputs; + for (const auto &i : m_txcd.sources) { + inputs.emplace_back(i.amount, epee::string_tools::pod_to_hex(i.real_out_tx_key)); + } + return inputs; + } + + std::vector TransactionConstructionInfoImpl::outputs() const { + std::vector outputs; + for (const auto &o : m_txcd.splitted_dsts) { + outputs.emplace_back(o.amount, o.address(m_wallet.m_wallet->nettype(), crypto::hash())); + } + return outputs; + } +} \ No newline at end of file diff --git a/src/wallet/api/transaction_construction_info.h b/src/wallet/api/transaction_construction_info.h new file mode 100644 index 000000000..48c20392a --- /dev/null +++ b/src/wallet/api/transaction_construction_info.h @@ -0,0 +1,32 @@ +#ifndef WOWLET_TRANSACTION_CONSTRUCTION_INFO_H +#define WOWLET_TRANSACTION_CONSTRUCTION_INFO_H + +#include "wallet/api/wallet2_api.h" +#include "wallet/wallet2.h" +#include "wallet.h" +#include + +namespace Monero { + +class TransactionConstructionInfoImpl : public TransactionConstructionInfo +{ +public: + TransactionConstructionInfoImpl(WalletImpl &wallet, const tools::wallet2::tx_construction_data & ptx); + ~TransactionConstructionInfoImpl() override; + + uint64_t unlockTime() const override; + std::set subaddressIndices() const override; + std::vector subaddresses() const override; + uint64_t minMixinCount() const override; + std::vector inputs() const override; + std::vector outputs() const override; + +private: + friend class WalletImpl; + WalletImpl &m_wallet; + tools::wallet2::tx_construction_data m_txcd; +}; + +} + +#endif //WOWLET_TRANSACTION_CONSTRUCTION_INFO_H \ No newline at end of file diff --git a/src/wallet/api/transaction_history.cpp b/src/wallet/api/transaction_history.cpp index 4649089ea..a8c968b38 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); } @@ -266,3 +274,4 @@ void TransactionHistoryImpl::refresh() } } // namespace +} \ No newline at end of file diff --git a/src/wallet/api/transaction_info.cpp b/src/wallet/api/transaction_info.cpp index edbdc469a..e958f7b6e 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 5eeeb04c2..674396011 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/unsigned_transaction.cpp b/src/wallet/api/unsigned_transaction.cpp index 4ccfafebd..baca81973 100644 --- a/src/wallet/api/unsigned_transaction.cpp +++ b/src/wallet/api/unsigned_transaction.cpp @@ -31,6 +31,7 @@ #include "unsigned_transaction.h" #include "wallet.h" #include "common_defines.h" +#include "transaction_construction_info.h" #include "cryptonote_basic/cryptonote_format_utils.h" #include "cryptonote_basic/cryptonote_basic_impl.h" @@ -315,4 +316,24 @@ uint64_t UnsignedTransactionImpl::minMixinCount() const return min_mixin; } +TransactionConstructionInfo * UnsignedTransactionImpl::transaction(int index) const { + if (index < 0) + return nullptr; + auto index_ = static_cast(index); + return index_ < m_constructionInfo.size() ? m_constructionInfo[index_] : nullptr; +} + +void UnsignedTransactionImpl::refresh() { + for (auto t : m_constructionInfo) + delete t; + m_constructionInfo.clear(); + + for (const auto& p : m_unsigned_tx_set.txes) + m_constructionInfo.push_back(new TransactionConstructionInfoImpl(m_wallet, p)); +} + +std::vector UnsignedTransactionImpl::getAll() const { + return m_constructionInfo; +} + } // namespace diff --git a/src/wallet/api/unsigned_transaction.h b/src/wallet/api/unsigned_transaction.h index 07649e39e..f75216eca 100644 --- a/src/wallet/api/unsigned_transaction.h +++ b/src/wallet/api/unsigned_transaction.h @@ -55,6 +55,9 @@ public: bool sign(const std::string &signedFileName) override; std::string confirmationMessage() const override {return m_confirmationMessage;} uint64_t minMixinCount() const override; + void refresh() override; + std::vector getAll() const override; + TransactionConstructionInfo * transaction(int index) const override; private: // Callback function to check all loaded tx's and generate confirmationMessage @@ -67,7 +70,7 @@ private: std::string m_errorString; tools::wallet2::unsigned_tx_set m_unsigned_tx_set; std::string m_confirmationMessage; + std::vector m_constructionInfo; }; - } diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index a7f27f6d4..09cb8ecfa 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -35,9 +35,11 @@ #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" +#include "string_coding.h" #include "mnemonics/electrum-words.h" #include "mnemonics/english.h" @@ -63,8 +65,8 @@ namespace { static const int MAX_REFRESH_INTERVAL_MILLIS = 1000 * 60 * 1; // Default refresh interval when connected to remote node static const int DEFAULT_REMOTE_NODE_REFRESH_INTERVAL_MILLIS = 1000 * 10; - // Connection timeout 30 sec - static const int DEFAULT_CONNECTION_TIMEOUT_MILLIS = 1000 * 30; + // Connection timeout 10 sec + static const int DEFAULT_CONNECTION_TIMEOUT_MILLIS = 1000 * 10; std::string get_default_ringdb_path(cryptonote::network_type nettype) { @@ -163,11 +165,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->moneyReceived(tx_hash, amount); - 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) @@ -179,11 +178,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, @@ -195,11 +191,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) @@ -433,15 +426,12 @@ 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)); m_refreshIntervalMillis = DEFAULT_REFRESH_INTERVAL_MILLIS; - m_refreshThread = boost::thread([this] () { - this->refreshThreadFunc(); - }); - } WalletImpl::~WalletImpl() @@ -760,6 +750,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) { @@ -836,6 +855,11 @@ bool WalletImpl::setPassword(const std::string &password) return status() == Status_Ok; } +std::string WalletImpl::getPassword() const +{ + return m_password; +} + bool WalletImpl::setDevicePin(const std::string &pin) { clearStatus(); @@ -863,6 +887,27 @@ std::string WalletImpl::address(uint32_t accountIndex, uint32_t addressIndex) co return m_wallet->get_subaddress_as_str({accountIndex, addressIndex}); } +bool WalletImpl::subaddressIndex(std::string address, std::pair &index) const +{ + clearStatus(); + cryptonote::address_parse_info info; + + if (!cryptonote::get_account_address_from_str(info, m_wallet->nettype(), address)) { + setStatusError(tr("Failed to parse address")); + return false; + } + + auto i = m_wallet->get_subaddress_index(info.address); + if (!i) { + setStatusError(tr("Address doesn't belong to the wallet")); + return false; + } + + index.first = i->major; + index.second = i->minor; + return true; +} + std::string WalletImpl::integratedAddress(const std::string &payment_id) const { crypto::hash8 pid; @@ -1031,7 +1076,7 @@ uint64_t WalletImpl::daemonBlockChainHeight() const if(m_wallet->light_wallet()) { return m_wallet->get_light_wallet_scanned_block_height(); } - if (!m_is_connected) + if (!m_is_connected && m_synchronized) return 0; std::string err; uint64_t result = m_wallet->get_daemon_blockchain_height(err); @@ -1050,7 +1095,7 @@ uint64_t WalletImpl::daemonBlockChainTargetHeight() const if(m_wallet->light_wallet()) { return m_wallet->get_light_wallet_blockchain_height(); } - if (!m_is_connected) + if (!m_is_connected && m_synchronized) return 0; std::string err; uint64_t result = m_wallet->get_daemon_blockchain_target_height(err); @@ -1147,6 +1192,48 @@ UnsignedTransaction *WalletImpl::loadUnsignedTx(const std::string &unsigned_file return transaction; } +UnsignedTransaction *WalletImpl::loadUnsignedTxFromStr(const std::string &unsigned_tx) { + clearStatus(); + + UnsignedTransactionImpl * transaction = new UnsignedTransactionImpl(*this); + if (!m_wallet->parse_unsigned_tx_from_str(unsigned_tx, 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 (!transaction->m_unsigned_tx_set.transfers.second.empty()) + extra_message = (boost::format("%u outputs to import. ") % (unsigned)transaction->m_unsigned_tx_set.transfers.second.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; +} + +UnsignedTransaction *WalletImpl::loadUnsignedTxFromBase64Str(const std::string &unsigned_tx_base64) { + clearStatus(); + + std::string decoded_tx = epee::string_encoding::base64_decode(unsigned_tx_base64); + + return this->loadUnsignedTxFromStr(decoded_tx); +} + +PendingTransaction *WalletImpl::loadSignedTx(const std::string &signed_filename) { + clearStatus(); + PendingTransactionImpl * transaction = new PendingTransactionImpl(*this); + + if (!m_wallet->load_tx(signed_filename, transaction->m_pending_tx)) { + setStatusError(tr("Failed to load unsigned transactions")); + return transaction; + } + + return transaction; +} + bool WalletImpl::submitTransaction(const string &fileName) { clearStatus(); std::unique_ptr transaction(new PendingTransactionImpl(*this)); @@ -1175,7 +1262,7 @@ bool WalletImpl::exportKeyImages(const string &filename, bool all) try { - if (!m_wallet->export_key_images(filename), all) + if (!m_wallet->export_key_images(filename, all)) { setStatusError(tr("failed to save file ") + filename); return false; @@ -1275,6 +1362,91 @@ bool WalletImpl::importOutputs(const string &filename) return true; } +bool WalletImpl::importTransaction(const std::string &txid, std::vector &o_indices, uint64_t height, uint8_t block_version, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen) +{ + try + { + m_wallet->import_tx(txid, o_indices, height, block_version, ts, miner_tx, pool, double_spend_seen); + } + catch (const std::exception &e) + { + LOG_ERROR("Failed to import transaction: " << e.what()); + setStatusError(string(tr("Failed to import transaction: ")) + e.what()); + return false; + } + + return true; +} + +std::string WalletImpl::printBlockchain() +{ + return m_wallet->printBlockchain(); +} +std::string WalletImpl::printTransfers() +{ + return m_wallet->printTransfers(); +} +std::string WalletImpl::printPayments() +{ + return m_wallet->printPayments(); +} +std::string WalletImpl::printUnconfirmedPayments() +{ + return m_wallet->printUnconfirmedPayments(); +} +std::string WalletImpl::printConfirmedTransferDetails() +{ + return m_wallet->printConfirmedTransferDetails(); +} +std::string WalletImpl::printUnconfirmedTransferDetails() +{ + return m_wallet->printUnconfirmedTransferDetails(); +} +std::string WalletImpl::printPubKeys() +{ + return m_wallet->printPubKeys(); +} +std::string WalletImpl::printTxNotes() +{ + return m_wallet->printTxNotes(); +} +std::string WalletImpl::printSubaddresses() +{ + return m_wallet->printSubaddresses(); +} +std::string WalletImpl::printSubaddressLabels() +{ + return m_wallet->printSubaddressLabels(); +} +std::string WalletImpl::printAdditionalTxKeys() +{ + return m_wallet->printAdditionalTxKeys(); +} +std::string WalletImpl::printAttributes() +{ + return m_wallet->printAttributes(); +} +std::string WalletImpl::printKeyImages() +{ + return m_wallet->printKeyImages(); +} +std::string WalletImpl::printAccountTags() +{ + return m_wallet->printAccountTags(); +} +std::string WalletImpl::printTxKeys() +{ + return m_wallet->printTxKeys(); +} +std::string WalletImpl::printAddressBook() +{ + return m_wallet->printAddressBook(); +} +std::string WalletImpl::printScannedPoolTxs() +{ + return m_wallet->printScannedPoolTxs(); +} + void WalletImpl::addSubaddressAccount(const std::string& label) { m_wallet->add_subaddress_account(label); @@ -1652,6 +1824,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() { @@ -1755,6 +2058,7 @@ uint64_t WalletImpl::estimateTransactionFee(const std::vectoruse_fork_rules(8, 0), m_wallet->use_fork_rules(HF_VERSION_CLSAG, 0), + true, m_wallet->get_base_fee(), m_wallet->get_fee_multiplier(m_wallet->adjust_priority(static_cast(priority))), m_wallet->get_fee_quantization_mask()); @@ -1770,6 +2074,11 @@ AddressBook *WalletImpl::addressBook() return m_addressBook.get(); } +Coins *WalletImpl::coins() +{ + return m_coins.get(); +} + Subaddress *WalletImpl::subaddress() { return m_subaddress.get(); @@ -2238,37 +2547,34 @@ void WalletImpl::refreshThreadFunc() void WalletImpl::doRefresh() { + bool success = true; 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 = "<light_wallet() || daemonSynced()) { - if(rescan) - m_wallet->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"); + if(rescan) + m_wallet->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); } catch (const std::exception &e) { + success = false; setStatusError(e.what()); break; }while(!rescan && (rescan=m_refreshShouldRescan.exchange(false))); // repeat if not rescanned and rescan was requested + m_is_connected = success; if (m_wallet2Callback->getListener()) { - m_wallet2Callback->getListener()->refreshed(); + m_wallet2Callback->getListener()->refreshed(success); } } @@ -2331,8 +2637,14 @@ void WalletImpl::pendingTxPostProcess(PendingTransactionImpl * pending) bool WalletImpl::doInit(const string &daemon_address, const std::string &proxy_address, uint64_t upper_transaction_size_limit, bool ssl) { - if (!m_wallet->init(daemon_address, m_daemon_login, proxy_address, upper_transaction_size_limit)) - return false; + if (!m_wallet->init(daemon_address, + m_daemon_login, + proxy_address, + upper_transaction_size_limit, + trustedDaemon(), + ssl ? epee::net_utils::ssl_support_t::e_ssl_support_autodetect : epee::net_utils::ssl_support_t::e_ssl_support_disabled)) { + return false; + } // 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 diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index d0900daa2..a36dc1447 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -46,8 +46,11 @@ class PendingTransactionImpl; class UnsignedTransactionImpl; class AddressBookImpl; class SubaddressImpl; +class CoinsImpl; class SubaddressAccountImpl; struct Wallet2CallbackImpl; +class PendingTransactionInfoImpl; +class TransactionConstructionInfoImpl; class WalletImpl : public Wallet { @@ -76,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); @@ -89,9 +96,11 @@ public: std::string errorString() const override; void statusWithErrorString(int& status, std::string& errorString) const override; bool setPassword(const std::string &password) override; + 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; + bool subaddressIndex(std::string address, std::pair &index) const override; std::string integratedAddress(const std::string &payment_id) const override; std::string secretViewKey() const override; std::string publicViewKey() const override; @@ -162,19 +171,46 @@ 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; + virtual UnsignedTransaction * loadUnsignedTxFromStr(const std::string &unsigned_tx) override; + virtual UnsignedTransaction * loadUnsignedTxFromBase64Str(const std::string &unsigned_tx) override; + virtual PendingTransaction * loadSignedTx(const std::string &signed_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; + bool importTransaction(const std::string &txid, std::vector &o_indices, uint64_t height, uint8_t block_version, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen) override; + + virtual std::string printBlockchain() override; + virtual std::string printTransfers() override; + virtual std::string printPayments() override; + virtual std::string printUnconfirmedPayments() override; + virtual std::string printConfirmedTransferDetails() override; + virtual std::string printUnconfirmedTransferDetails() override; + virtual std::string printPubKeys() override; + virtual std::string printTxNotes() override; + virtual std::string printSubaddresses() override; + virtual std::string printSubaddressLabels() override; + virtual std::string printAdditionalTxKeys() override; + virtual std::string printAttributes() override; + virtual std::string printKeyImages() override; + virtual std::string printAccountTags() override; + virtual std::string printTxKeys() override; + virtual std::string printAddressBook() override; + virtual std::string printScannedPoolTxs() 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 Coins * coins() override; virtual Subaddress * subaddress() override; virtual SubaddressAccount * subaddressAccount() override; virtual void setListener(WalletListener * l) override; @@ -244,7 +280,10 @@ private: friend struct Wallet2CallbackImpl; friend class AddressBookImpl; friend class SubaddressImpl; + friend class CoinsImpl; friend class SubaddressAccountImpl; + friend class PendingTransactionInfoImpl; + friend class TransactionConstructionInfoImpl; std::unique_ptr m_wallet; mutable boost::mutex m_statusMutex; @@ -255,6 +294,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 99a4de731..81cfc36ac 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h @@ -66,6 +66,47 @@ enum NetworkType : uint8_t { bool set; }; +/* + * @brief Transaction construction data + */ +struct TransactionConstructionInfo +{ + struct Input { + Input(uint64_t _amount, const std::string &_pubkey); + const uint64_t amount; + const std::string pubkey; + }; + + struct Output { + Output(uint64_t _amount, const std::string &_address); + const uint64_t amount; + const std::string address; + }; + + virtual ~TransactionConstructionInfo() = 0; + virtual uint64_t unlockTime() const = 0; + virtual std::set subaddressIndices() const = 0; + virtual std::vector subaddresses() const = 0; + virtual uint64_t minMixinCount() const = 0; + virtual std::vector inputs() const = 0; + virtual std::vector outputs() const = 0; +}; + + +/* +* @brief Detailed pending transaction information +*/ +struct PendingTransactionInfo +{ + virtual ~PendingTransactionInfo() = 0; + virtual uint64_t fee() const = 0; + virtual uint64_t dust() const = 0; + virtual bool dustAddedToFee() const = 0; + virtual std::string txKey() const = 0; + virtual TransactionConstructionInfo * constructionData() const = 0; +}; + + /** * @brief Transaction-like interface for sending money */ @@ -101,6 +142,13 @@ struct PendingTransaction virtual uint64_t txCount() const = 0; virtual std::vector subaddrAccount() const = 0; virtual std::vector> subaddrIndices() const = 0; + virtual std::string unsignedTxToBin() const = 0; + virtual std::string unsignedTxToBase64() const = 0; + virtual std::string signedTxToHex(int index) const = 0; + virtual size_t signedTxSize(int index) const = 0; + virtual PendingTransactionInfo * transaction(int index) const = 0; + virtual void refresh() = 0; + virtual std::vector getAll() const = 0; /** * @brief multisigSignData @@ -160,6 +208,9 @@ struct UnsignedTransaction * return - true on success */ virtual bool sign(const std::string &signedFileName) = 0; + virtual void refresh() = 0; + virtual std::vector getAll() const = 0; + virtual TransactionConstructionInfo * transaction(int index) const = 0; }; /** @@ -198,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 @@ -260,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 @@ -382,7 +478,7 @@ struct WalletListener /** * @brief refreshed - called when wallet refreshed by background thread or explicitly refreshed by calling "refresh" synchronously */ - virtual void refreshed() = 0; + virtual void refreshed(bool success) = 0; /** * @brief called by device if the action is required @@ -456,9 +552,11 @@ struct Wallet //! returns both error and error string atomically. suggested to use in instead of status() and errorString() virtual void statusWithErrorString(int& status, std::string& errorString) const = 0; virtual bool setPassword(const std::string &password) = 0; + virtual std::string getPassword() const = 0; virtual bool setDevicePin(const std::string &pin) { (void)pin; return false; }; virtual bool setDevicePassphrase(const std::string &passphrase) { (void)passphrase; return false; }; virtual std::string address(uint32_t accountIndex = 0, uint32_t addressIndex = 0) const = 0; + virtual bool subaddressIndex(std::string address, std::pair &index) const = 0; std::string mainAddress() const { return address(0, 0); } virtual std::string path() const = 0; virtual NetworkType nettype() const = 0; @@ -868,6 +966,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() @@ -882,7 +992,27 @@ struct Wallet * after object returned */ virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename) = 0; - + + /*! + * \brief loadUnsignedTx - creates transaction from unsigned tx string + * \return - UnsignedTransaction object. caller is responsible to check UnsignedTransaction::status() + * after object returned + */ + virtual UnsignedTransaction * loadUnsignedTxFromStr(const std::string &unsigned_tx) = 0; + + /*! + * \brief loadUnsignedTx - creates transaction from unsigned base64 encoded tx string + * \return - UnsignedTransaction object. caller is responsible to check UnsignedTransaction::status() + * after object returned + */ + virtual UnsignedTransaction * loadUnsignedTxFromBase64Str(const std::string &unsigned_tx_base64) = 0; + + /*! + * \brief loadSignedTx - creates transaction from signed tx file + * \return - PendingTransaction object. + */ + virtual PendingTransaction * loadSignedTx(const std::string &signed_filename) = 0; + /*! * \brief submitTransaction - submits transaction in signed tx file * \return - true on success @@ -933,8 +1063,29 @@ struct Wallet */ virtual bool importOutputs(const std::string &filename) = 0; + virtual bool importTransaction(const std::string &txid, std::vector &o_indices, uint64_t height, uint8_t block_version, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen) = 0; + + virtual std::string printBlockchain() = 0; + virtual std::string printTransfers() = 0; + virtual std::string printPayments() = 0; + virtual std::string printUnconfirmedPayments() = 0; + virtual std::string printConfirmedTransferDetails() = 0; + virtual std::string printUnconfirmedTransferDetails() = 0; + virtual std::string printPubKeys() = 0; + virtual std::string printTxNotes() = 0; + virtual std::string printSubaddresses() = 0; + virtual std::string printSubaddressLabels() = 0; + virtual std::string printAdditionalTxKeys() = 0; + virtual std::string printAttributes() = 0; + virtual std::string printKeyImages() = 0; + virtual std::string printAccountTags() = 0; + virtual std::string printTxKeys() = 0; + virtual std::string printAddressBook() = 0; + virtual std::string printScannedPoolTxs() = 0; + 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; @@ -991,7 +1142,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; /*! @@ -1200,6 +1352,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 417a27db5..b52fa85f6 100644 --- a/src/wallet/api/wallet_manager.cpp +++ b/src/wallet/api/wallet_manager.cpp @@ -122,6 +122,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 cf3056a17..d96e65e37 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 a576c267c..d310ea73f 100644 --- a/src/wallet/node_rpc_proxy.cpp +++ b/src/wallet/node_rpc_proxy.cpp @@ -51,7 +51,7 @@ using namespace epee; namespace tools { -static const std::chrono::seconds rpc_timeout = std::chrono::minutes(3) + std::chrono::seconds(30); +static const std::chrono::seconds rpc_timeout = std::chrono::seconds(10); NodeRPCProxy::NodeRPCProxy(epee::net_utils::http::abstract_http_client &http_client, rpc_payment_state_t &rpc_payment_state, boost::recursive_mutex &mutex) : m_http_client(http_client) @@ -162,7 +162,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 66145d5a1..6bcc55cde 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1537,6 +1537,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); @@ -2169,13 +2177,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) @@ -2233,6 +2242,8 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote update_multisig_rescan_info(*m_multisig_rescan_k, *m_multisig_rescan_info, m_transfers.size() - 1); } LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << txid); + lock.unlock(); + if (0 != m_callback) m_callback->on_money_received(height, txid, tx, td.m_amount, td.m_subaddr_index, spends_one_of_ours(tx), td.m_tx.unlock_time); } @@ -2271,12 +2282,13 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote uint64_t extra_amount = amount - m_transfers[kit->second].amount(); 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; @@ -3212,6 +3224,54 @@ void wallet2::update_pool_state(std::vector &o_indices, uint64_t height, uint8_t block_version, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen) +{ + crypto::hash hash; + epee::string_tools::hex_to_pod(txid, hash); + + cryptonote::COMMAND_RPC_GET_TRANSACTIONS::request req; + cryptonote::COMMAND_RPC_GET_TRANSACTIONS::response res; + req.txs_hashes.push_back(epee::string_tools::pod_to_hex(hash)); + + req.decode_as_json = false; + req.prune = true; + + bool r; + { + const boost::lock_guard lock{m_daemon_rpc_mutex}; + uint64_t pre_call_credits = m_rpc_payment_state.credits; + req.client = get_client_signature(); + r = epee::net_utils::invoke_http_json("/gettransactions", req, res, *m_http_client, rpc_timeout); + if (r && res.status == CORE_RPC_STATUS_OK) + check_rpc_cost("/gettransactions", res.credits, pre_call_credits, res.txs.size() * COST_PER_TX); + } + + MDEBUG("Got " << r << " and " << res.status); + if (!(r && res.status == CORE_RPC_STATUS_OK)) { + THROW_WALLET_EXCEPTION(error::wallet_internal_error, "Error calling gettransactions daemon RPC: r " + std::to_string(r) + ", status " + get_rpc_status(res.status)); + } + + if (res.txs.size() != 1) { + THROW_WALLET_EXCEPTION(error::wallet_internal_error, "Expected 1 tx, got " + std::to_string(res.txs.size())); + } + + const auto &tx_entry = res.txs[0]; + cryptonote::transaction tx; + cryptonote::blobdata bd; + crypto::hash tx_hash; + + if (!get_pruned_tx(tx_entry, tx, tx_hash)) { + THROW_WALLET_EXCEPTION(error::wallet_internal_error, "Failed to parse transaction from daemon"); + } + + if (tx_hash != hash) { + THROW_WALLET_EXCEPTION(error::wallet_internal_error, "Got txid " + epee::string_tools::pod_to_hex(tx_hash) + " which we did not ask for"); + } + + process_new_transaction(tx_hash, tx, o_indices, height, block_version, ts, miner_tx, pool, double_spend_seen, {}); +} //---------------------------------------------------------------------------------------------------- void wallet2::process_pool_state(const std::vector> &txs) { @@ -3235,11 +3295,19 @@ void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, { std::vector hashes; - const uint64_t checkpoint_height = m_checkpoints.get_max_height(); + // Get highest checkpoint that is lower than stop_height + uint64_t checkpoint_height = 0; + for (auto i : m_checkpoints.get_points()) { + if (i.first > stop_height) { + break; + } + checkpoint_height = i.first; + } + if ((stop_height > checkpoint_height && m_blockchain.size()-1 < checkpoint_height) && !force) { // we will drop all these, so don't bother getting them - uint64_t missing_blocks = m_checkpoints.get_max_height() - m_blockchain.size(); + uint64_t missing_blocks = checkpoint_height - m_blockchain.size(); while (missing_blocks-- > 0) m_blockchain.push_back(crypto::null_hash); // maybe a bit suboptimal, but deque won't do huge reallocs like vector m_blockchain.push_back(m_checkpoints.get_points().at(checkpoint_height)); @@ -5504,6 +5572,315 @@ void wallet2::write_watch_only_wallet(const std::string& wallet_name, const epee bool r = store_keys(new_keys_filename, password, true); THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, new_keys_filename); } + +std::string wallet2::printBlockchain() +{ + std::string blstr; + blstr += "offset: " + std::to_string(m_blockchain.offset()) + "\n"; + blstr += "genesis: " + string_tools::pod_to_hex(m_blockchain.genesis()) + "\n"; + + for (size_t i = m_blockchain.offset(); i < m_blockchain.size(); i++) { + blstr += std::to_string(i) + " : " + string_tools::pod_to_hex(m_blockchain[i]) + "\n"; + } + + return blstr; +} + +std::string wallet2::printTransfers() +{ + std::string str; + for (const auto &td : m_transfers) { + str += "block_height: " + std::to_string(td.m_block_height) + "\n"; + str += printTxPrefix(td.m_tx); + str += "txid: " + string_tools::pod_to_hex(td.m_txid) + "\n"; + str += "internal_output_index: " + std::to_string(td.m_internal_output_index) + "\n"; + str += "global_output_index: " + std::to_string(td.m_global_output_index) + "\n"; + str += "spent: " + std::to_string(td.m_spent) + "\n"; + str += "frozen: " + std::to_string(td.m_frozen) + "\n"; + str += "spent_height: " + std::to_string(td.m_spent_height) + "\n"; + str += "key_image: " + string_tools::pod_to_hex(td.m_key_image) + "\n"; + str += "mask: " + string_tools::pod_to_hex(td.m_mask) + "\n"; + str += "amount: " + std::to_string(td.m_amount) + "\n"; + str += "rct: " + std::to_string(td.m_rct) + "\n"; + str += "key_image_known: " + std::to_string(td.m_key_image_known) + "\n"; + str += "key_image_request: " + std::to_string(td.m_key_image_request) + "\n"; + str += "pk_index: " + std::to_string(td.m_pk_index) + "\n"; + str += "subaddr_index: " + std::to_string(td.m_subaddr_index.major) + "," + std::to_string(td.m_subaddr_index.minor) + "\n"; + str += "key_image_partial: " + std::to_string(td.m_key_image_partial) + "\n"; + str += "multisig_k:\n"; + for (const auto &el : td.m_multisig_k) { + str += " " + string_tools::pod_to_hex(el) + "\n"; + } + + str += "multisig_info:\n"; + for (const auto &el : td.m_multisig_info) { + str += " signer: " + string_tools::pod_to_hex(el.m_signer) + "\n"; + str += " LR:\n"; + for (const auto &em : el.m_LR) { + str += " L: " + string_tools::pod_to_hex(em.m_L) + "\n"; + str += " R: " + string_tools::pod_to_hex(em.m_R) + "\n"; + } + str += "\n"; + str += " partial_key_images:\n"; + for (const auto &em : el.m_partial_key_images) { + str += " " + string_tools::pod_to_hex(em) + "\n"; + } + str += "\n"; + } + + str += "uses:\n"; + for (const auto &el : td.m_uses) { + str += " " + std::to_string(el.first) + " : " + string_tools::pod_to_hex(el.second) + "\n"; + } + str += "\n"; + } + return str; +} + +std::string wallet2::printUnconfirmedPayments() +{ + std::string str; + for (const auto &el : m_unconfirmed_payments) { + auto ppd = el.second; + str += "double_spend_seen: " + std::to_string(ppd.m_double_spend_seen) + "\n"; + str += printPaymentDetails(ppd.m_pd); + str += "\n"; + } + return str; +} + +std::string wallet2::printConfirmedTransferDetails() +{ + std::string str; + for (const auto &el : m_confirmed_txs) { + auto ctd = el.second; + str += "amount_in: " + std::to_string(ctd.m_amount_in) + "\n"; + str += "amount_out: " + std::to_string(ctd.m_amount_out) + "\n"; + str += "change: " + std::to_string(ctd.m_change) + "\n"; + str += "block_height: " + std::to_string(ctd.m_block_height) + "\n"; + str += "dests:\n"; + for (const auto &em : ctd.m_dests) { + str += printTxDestinationEntry(em); + } + str += "payment_id: " + string_tools::pod_to_hex(ctd.m_payment_id) + "\n"; + str += "timestamp: " + std::to_string(ctd.m_timestamp) + "\n"; + str += "unlock_time: " + std::to_string(ctd.m_unlock_time) + "\n"; + str += "subaddr_account: " + std::to_string(ctd.m_subaddr_account) + "\n"; + str += "subaddr_indices: "; + for (auto em : ctd.m_subaddr_indices) { + str += std::to_string(em); + } + str += "\n\n"; + } + return str; +} + +std::string wallet2::printUnconfirmedTransferDetails() +{ + std::string str; + for (const auto &el : m_unconfirmed_txs) { + auto utd = el.second; + str += printTxPrefix(utd.m_tx); + str += "amount_in: " + std::to_string(utd.m_amount_in) + "\n"; + str += "amount_out: " + std::to_string(utd.m_amount_out) + "\n"; + str += "change: " + std::to_string(utd.m_change) + "\n"; + str += "sent_time: " + std::to_string(utd.m_sent_time) + "\n"; + str += "dests:\n"; + for (const auto &em : utd.m_dests) { + str += printTxDestinationEntry(em); + } + str += "payment_id: " + string_tools::pod_to_hex(utd.m_payment_id) + "\n"; + str += "timestamp: " + std::to_string(utd.m_timestamp) + "\n"; + str += "subaddr_account: " + std::to_string(utd.m_subaddr_account) + "\n"; + str += "subaddr_indices: "; + for (auto em : utd.m_subaddr_indices) { + str += std::to_string(em); + } + str += "\n\n"; + } + return str; +} + +std::string wallet2::printPayments() +{ + std::string str; + for (const auto &el : m_payments) { + str += printPaymentDetails(el.second) + "\n"; + } + return str; +} + +std::string wallet2::printPubKeys() +{ + std::string str; + vector> v; + for (const auto &el : m_pub_keys) { + v.push_back(el); + } + std::sort(v.begin(), v.end(), + [](std::pair a, std::pair b){return a.second < b.second;}); + for (const auto &el : v){ + str += string_tools::pod_to_hex(el.first) + " : " + boost::to_string(el.second) + "\n"; + } + return str; +} + +std::string wallet2::printTxNotes() +{ + std::string str; + for (std::pair el : m_tx_notes) { + str += string_tools::pod_to_hex(el.first) + " : " + el.second + "\n"; + } + return str; +} + +std::string wallet2::printSubaddresses() +{ + std::string str; + vector> v; + for (const auto &el : m_subaddresses) { + v.push_back(el); + } + std::sort(v.begin(), v.end(), [](std::pair a, std::pair b) { + if (a.second.major == b.second.major) { + return a.second.minor < b.second.minor; + } + return a.second.major < b.second.major; + }); + for (const auto &el : v) { + str += string_tools::pod_to_hex(el.first) + " : " + std::to_string(el.second.major) + "," + std::to_string(el.second.minor) + "\n"; + } + return str; +} + +std::string wallet2::printSubaddressLabels() +{ + std::string str; + for (size_t i = 0; i < m_subaddress_labels.size(); i++) { + for (size_t j = 0; j < m_subaddress_labels[i].size(); j++) { + str += std::to_string(i) + "," + std::to_string(j) + " : " + m_subaddress_labels[i][j] + "\n"; + } + } + return str; +} + +std::string wallet2::printAdditionalTxKeys() +{ + std::string str; + for (std::pair> el : m_additional_tx_keys) { + str += "Txid: " + string_tools::pod_to_hex(el.first) + " (" + std::to_string(el.second.size()) + ")\n"; + for (auto em : el.second) { + str += " " + string_tools::pod_to_hex(em) + "\n"; + } + } + return str; +} + +std::string wallet2::printAttributes() +{ + std::string str; + for (auto el : m_attributes) { + str += el.first + " : " + el.second + "\n"; + } + return str; +} + +std::string wallet2::printKeyImages() +{ + std::string str; + vector> v; + for (const auto &el : m_key_images) { + v.push_back(el); + } + std::sort(v.begin(), v.end(), [](std::pair a, std::pair b){return a.second < b.second;}); + for (const auto &el: v) { + str += string_tools::pod_to_hex(el.first) + " : " + boost::to_string(el.second) + "\n"; + } + return str; +} + +std::string wallet2::printAccountTags() +{ + std::string str; + for (size_t i = 0; i < m_account_tags.second.size(); i++) { + str += std::to_string(i) + " : " + m_account_tags.second[i] + "\n"; + } + return str; +} + +std::string wallet2::printTxKeys() +{ + std::string str; + for (std::pair el : m_tx_keys) { + str += string_tools::pod_to_hex(el.first) + " : " + string_tools::pod_to_hex(el.second) + "\n"; + } + return str; +} + +std::string wallet2::printAddressBook() +{ + std::string str; + for (auto el : m_address_book) { + str += "address: " + string_tools::pod_to_hex(el.m_address) + "\n"; + str += "payment_id: " + string_tools::pod_to_hex(el.m_payment_id) + "\n"; + str += "description: " + el.m_description + "\n"; + str += "is_subaddress: " + std::to_string(el.m_is_subaddress) + "\n"; + str += "has_payment_id: " + std::to_string(el.m_has_payment_id) + "\n"; + str += "\n"; + } + return str; +} + +std::string wallet2::printScannedPoolTxs() +{ + std::string str; + for (size_t i = 0; i < 2; i++) { + str += "scanned_pool_txs[" + std::to_string(i) + "]\n"; + for (auto el : m_scanned_pool_txs[i]) { + str += string_tools::pod_to_hex(el) + "\n"; + } + str += "\n"; + } + return str; +} + +std::string wallet2::printTxPrefix(const cryptonote::transaction_prefix &tx) +{ + std::string str; + str += "tx.version: " + std::to_string(tx.version) + "\n"; + str += "tx.unlock_time: " + std::to_string(tx.unlock_time) + "\n"; + return str; +} + +std::string wallet2::printPaymentDetails(const payment_details &pd) +{ + std::string str; + str += "tx_hash: " + string_tools::pod_to_hex(pd.m_tx_hash) + "\n"; + str += "amount: " + std::to_string(pd.m_amount) + "\n"; + str += "amounts: "; + for (auto em : pd.m_amounts) { + str += std::to_string(em); + } + str += "\n"; + str += "fee: " + std::to_string(pd.m_fee) + "\n"; + str += "block_height: " + std::to_string(pd.m_block_height) + "\n"; + str += "unlock_time: " + std::to_string(pd.m_unlock_time) + "\n"; + str += "timestamp: " + std::to_string(pd.m_timestamp) + "\n"; + str += "coinbase: " + std::to_string(pd.m_coinbase) + "\n"; + str += "subaddr_index: " + std::to_string(pd.m_subaddr_index.major) + "," + std::to_string(pd.m_subaddr_index.minor) + "\n"; + return str; +} + +std::string wallet2::printTxDestinationEntry(const cryptonote::tx_destination_entry &tx) +{ +std::string str; +str += " original: " + tx.original + "\n"; +str += " amount: " + std::to_string(tx.amount) + "\n"; +str += " addr: " + string_tools::pod_to_hex(tx.addr) + "\n"; +str += " is_subaddress: " + std::to_string(tx.is_subaddress) + "\n"; +str += " is_integrated: " + std::to_string(tx.is_integrated) + "\n"; +return str; +} //---------------------------------------------------------------------------------------------------- void wallet2::wallet_exists(const std::string& file_path, bool& keys_file_exists, bool& wallet_file_exists) { diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 44adfeb49..6bb841914 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -232,7 +232,7 @@ private: friend class wallet_keys_unlocker; friend class wallet_device_callback; public: - static constexpr const std::chrono::seconds rpc_timeout = std::chrono::minutes(3) + std::chrono::seconds(30); + static constexpr const std::chrono::seconds rpc_timeout = std::chrono::seconds(10); enum RefreshType { RefreshFull, @@ -910,6 +910,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; } @@ -1388,6 +1389,7 @@ private: bool import_key_images(std::vector key_images, size_t offset=0, boost::optional> selected_transfers=boost::none); bool import_key_images(signed_tx_set & signed_tx, size_t offset=0, bool only_selected_transfers=false); crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const; + void import_tx(const std::string &txid, std::vector &o_indices, uint64_t height, uint8_t block_version, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen); void update_pool_state(std::vector> &process_txs, bool refreshed = false); void process_pool_state(const std::vector> &txs); @@ -1532,6 +1534,27 @@ private: uint64_t get_bytes_sent() const; uint64_t get_bytes_received() const; + std::string printBlockchain(); + std::string printTransfers(); + std::string printKeyImages(); + std::string printUnconfirmedTransferDetails(); + std::string printPayments(); + std::string printUnconfirmedPayments(); + std::string printConfirmedTransferDetails(); + std::string printPubKeys(); + std::string printTxNotes(); + std::string printSubaddresses(); + std::string printSubaddressLabels(); + std::string printAdditionalTxKeys(); + std::string printAttributes(); + std::string printAccountTags(); + std::string printTxKeys(); + std::string printAddressBook(); + std::string printScannedPoolTxs(); + std::string printTxPrefix(const cryptonote::transaction_prefix &tx); + std::string printPaymentDetails(const payment_details &pd); + std::string printTxDestinationEntry(const cryptonote::tx_destination_entry &tx); + // MMS ------------------------------------------------------------------------------------------------- mms::message_store& get_message_store() { return m_message_store; }; const mms::message_store& get_message_store() const { return m_message_store; }; @@ -1558,6 +1581,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.