diff options
Diffstat (limited to 'src/wallet')
-rw-r--r-- | src/wallet/api/transaction_history.cpp | 21 | ||||
-rw-r--r-- | src/wallet/api/transaction_history.h | 1 | ||||
-rw-r--r-- | src/wallet/api/transaction_info.cpp | 11 | ||||
-rw-r--r-- | src/wallet/api/transaction_info.h | 4 | ||||
-rw-r--r-- | src/wallet/api/wallet2_api.h | 3 | ||||
-rw-r--r-- | src/wallet/wallet2.cpp | 44 | ||||
-rw-r--r-- | src/wallet/wallet2.h | 4 | ||||
-rw-r--r-- | src/wallet/wallet_errors.h | 10 |
8 files changed, 96 insertions, 2 deletions
diff --git a/src/wallet/api/transaction_history.cpp b/src/wallet/api/transaction_history.cpp index bcb300889..b6d52c58d 100644 --- a/src/wallet/api/transaction_history.cpp +++ b/src/wallet/api/transaction_history.cpp @@ -92,6 +92,17 @@ std::vector<TransactionInfo *> 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<const crypto::hash*>(txid_data.data()); + + m_wallet->m_wallet->set_tx_note(htxid, note); + refresh(); +} + void TransactionHistoryImpl::refresh() { // multithreaded access: @@ -126,10 +137,12 @@ 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_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 +186,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 +197,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 +222,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 +250,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<TransactionInfo*> 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<uint32_t> 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<uint32_t> 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<uint32_t> 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/wallet2_api.h b/src/wallet/api/wallet2_api.h index e8efc58b8..44928a422 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<uint32_t> 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<TransactionInfo*> getAll() const = 0; virtual void refresh() = 0; + virtual void setTxNote(const std::string &txid, const std::string ¬e) = 0; }; /** diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index a3755ff08..920e0413c 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1158,6 +1158,7 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended, std m_explicit_refresh_from_block_height(true), m_confirm_non_default_ring_size(true), m_ask_password(AskPasswordToDecrypt), + m_max_reorg_depth(ORPHANED_BLOCKS_MAX_COUNT), m_min_output_count(0), m_min_output_value(0), m_merge_destinations(false), @@ -2961,6 +2962,9 @@ void wallet2::update_pool_state(std::vector<std::tuple<cryptonote::transaction, MTRACE("update_pool_state got pool"); // remove any pending tx that's not in the pool + // TODO: set tx_propagation_timeout to CRYPTONOTE_DANDELIONPP_EMBARGO_AVERAGE * 3 / 2 after v15 hardfork + constexpr const std::chrono::seconds tx_propagation_timeout{500}; + const auto now = std::chrono::system_clock::now(); std::unordered_map<crypto::hash, wallet2::unconfirmed_transfer_details>::iterator it = m_unconfirmed_txs.begin(); while (it != m_unconfirmed_txs.end()) { @@ -2988,9 +2992,11 @@ void wallet2::update_pool_state(std::vector<std::tuple<cryptonote::transaction, LOG_PRINT_L1("Pending txid " << txid << " not in pool, marking as not in pool"); pit->second.m_state = wallet2::unconfirmed_transfer_details::pending_not_in_pool; } - else if (pit->second.m_state == wallet2::unconfirmed_transfer_details::pending_not_in_pool && refreshed) + else if (pit->second.m_state == wallet2::unconfirmed_transfer_details::pending_not_in_pool && refreshed && + now > std::chrono::system_clock::from_time_t(pit->second.m_sent_time) + tx_propagation_timeout) { - LOG_PRINT_L1("Pending txid " << txid << " not in pool, marking as failed"); + LOG_PRINT_L1("Pending txid " << txid << " not in pool after " << tx_propagation_timeout.count() << + " seconds, marking as failed"); pit->second.m_state = wallet2::unconfirmed_transfer_details::failed; // the inputs aren't spent anymore, since the tx failed @@ -3465,6 +3471,15 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo throw std::runtime_error("proxy exception in refresh thread"); } + if (!next_blocks.empty()) + { + const uint64_t expected_start_height = std::max(static_cast<uint64_t>(m_blockchain.size()), uint64_t(1)) - 1; + const uint64_t reorg_depth = expected_start_height - std::min(expected_start_height, next_blocks_start_height); + THROW_WALLET_EXCEPTION_IF(reorg_depth > m_max_reorg_depth, error::reorg_depth_error, + tr("reorg exceeds maximum allowed depth, use 'set max-reorg-depth N' to allow it, reorg depth: ") + + std::to_string(reorg_depth)); + } + // if we've got at least 10 blocks to refresh, assume we're starting // a long refresh, and setup a tracking output cache if we need to if (m_track_uses && (!output_tracker_cache || output_tracker_cache->empty()) && next_blocks.size() >= 10) @@ -3487,6 +3502,11 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo THROW_WALLET_EXCEPTION_IF(!waiter.wait(), error::wallet_internal_error, "Exception in thread pool"); throw; } + catch (const error::reorg_depth_error&) + { + THROW_WALLET_EXCEPTION_IF(!waiter.wait(), error::wallet_internal_error, "Exception in thread pool"); + throw; + } catch (const std::exception&) { blocks_fetched += added_blocks; @@ -3863,6 +3883,9 @@ boost::optional<wallet2::keys_file_data> wallet2::get_keys_file_data(const epee: value2.SetInt(m_ask_password); json.AddMember("ask_password", value2, json.GetAllocator()); + value2.SetUint64(m_max_reorg_depth); + json.AddMember("max_reorg_depth", value2, json.GetAllocator()); + value2.SetUint(m_min_output_count); json.AddMember("min_output_count", value2, json.GetAllocator()); @@ -4081,6 +4104,7 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st m_confirm_non_default_ring_size = true; m_ask_password = AskPasswordToDecrypt; cryptonote::set_default_decimal_point(CRYPTONOTE_DISPLAY_DECIMAL_POINT); + m_max_reorg_depth = ORPHANED_BLOCKS_MAX_COUNT; m_min_output_count = 0; m_min_output_value = 0; m_merge_destinations = false; @@ -4233,6 +4257,8 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st m_ask_password = field_ask_password; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, default_decimal_point, int, Int, false, CRYPTONOTE_DISPLAY_DECIMAL_POINT); cryptonote::set_default_decimal_point(field_default_decimal_point); + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, max_reorg_depth, uint64_t, Uint64, false, ORPHANED_BLOCKS_MAX_COUNT); + m_max_reorg_depth = field_max_reorg_depth; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, min_output_count, uint32_t, Uint, false, 0); m_min_output_count = field_min_output_count; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, min_output_value, uint64_t, Uint64, false, 0); @@ -13377,6 +13403,20 @@ size_t wallet2::import_multisig(std::vector<cryptonote::blobdata> blobs) loaded = true; } CHECK_AND_ASSERT_THROW_MES(loaded, "Failed to load output data"); + + for (const auto &e: i) + { + for (const auto &lr: e.m_LR) + { + CHECK_AND_ASSERT_THROW_MES(rct::isInMainSubgroup(lr.m_L), "Multisig value is not in the main subgroup"); + CHECK_AND_ASSERT_THROW_MES(rct::isInMainSubgroup(lr.m_R), "Multisig value is not in the main subgroup"); + } + for (const auto &ki: e.m_partial_key_images) + { + CHECK_AND_ASSERT_THROW_MES(rct::isInMainSubgroup(rct::ki2rct(ki)), "Multisig partial key image is not in the main subgroup"); + } + } + MINFO(boost::format("%u outputs found") % boost::lexical_cast<std::string>(i.size())); info.push_back(std::move(i)); } diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index fed7d745c..68f03db72 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -855,6 +855,9 @@ private: void explicit_refresh_from_block_height(bool expl) {m_explicit_refresh_from_block_height = expl;} bool explicit_refresh_from_block_height() const {return m_explicit_refresh_from_block_height;} + void max_reorg_depth(uint64_t depth) {m_max_reorg_depth = depth;} + uint64_t max_reorg_depth() const {return m_max_reorg_depth;} + bool deinit(); bool init(std::string daemon_address = "http://localhost:8080", boost::optional<epee::net_utils::http::login> daemon_login = boost::none, @@ -1728,6 +1731,7 @@ private: bool m_explicit_refresh_from_block_height; bool m_confirm_non_default_ring_size; AskPasswordType m_ask_password; + uint64_t m_max_reorg_depth; uint32_t m_min_output_count; uint64_t m_min_output_value; bool m_merge_destinations; diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h index e889ed7d1..4a89ed81a 100644 --- a/src/wallet/wallet_errors.h +++ b/src/wallet/wallet_errors.h @@ -428,6 +428,16 @@ namespace tools std::string to_string() const { return refresh_error::to_string(); } }; //---------------------------------------------------------------------------------------------------- + struct reorg_depth_error : public refresh_error + { + explicit reorg_depth_error(std::string&& loc, const std::string& message) + : refresh_error(std::move(loc), message) + { + } + + std::string to_string() const { return refresh_error::to_string(); } + }; + //---------------------------------------------------------------------------------------------------- struct signature_check_failed : public wallet_logic_error { explicit signature_check_failed(std::string&& loc, const std::string& message) |