aboutsummaryrefslogtreecommitdiff
path: root/src/wallet
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet')
-rw-r--r--src/wallet/api/transaction_history.cpp21
-rw-r--r--src/wallet/api/transaction_history.h1
-rw-r--r--src/wallet/api/transaction_info.cpp11
-rw-r--r--src/wallet/api/transaction_info.h4
-rw-r--r--src/wallet/api/wallet2_api.h3
-rw-r--r--src/wallet/wallet2.cpp44
-rw-r--r--src/wallet/wallet2.h4
-rw-r--r--src/wallet/wallet_errors.h10
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 &note)
+{
+ 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 &note);
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 &note) = 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)