aboutsummaryrefslogtreecommitdiff
path: root/src/wallet/wallet2.cpp
diff options
context:
space:
mode:
authorj-berman <justinberman@protonmail.com>2025-09-15 09:47:17 -0700
committerj-berman <justinberman@protonmail.com>2025-10-07 11:02:17 -0700
commit9239d36691bfe94dda12cc925903fed5e761fbe3 (patch)
tree6cd7880b0d335d007ac04794c90655938a698f53 /src/wallet/wallet2.cpp
parenta440e917906b92b74daa107e49fcb963266242fa (diff)
downloadmonzero-core-9239d36691bfe94dda12cc925903fed5e761fbe3.tar.gz
monzero-core-9239d36691bfe94dda12cc925903fed5e761fbe3.tar.xz
monzero-core-9239d36691bfe94dda12cc925903fed5e761fbe3.zip
wallet: identify spends in pool when scanning
- Make sure to mark identified spends in the pool as spends. The wallet might not know these have been spent if it wasn't the wallet that relayed the tx to the daemon, or the wallet was cleared via rescan_bc. - Make sure to add spends to m_unconfirmed_txs if not present. - Make sure to process the entire pool again if refreshing for the first time. The wallet fetches pool and blocks at the same time. The wallet scans blocks first, then pool. If the wallet identifies received outputs in the chain, then it may have spent those received outputs in the pool. So we make sure to re-process the entire pool again after scanning the chain for the first time. - Multisig wallets that know about spent key images can now detect spend txs in the pool. Update tests for that.
Diffstat (limited to 'src/wallet/wallet2.cpp')
-rw-r--r--src/wallet/wallet2.cpp60
1 files changed, 43 insertions, 17 deletions
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 3cb583afe..d93f6382a 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -922,6 +922,11 @@ bool get_short_payment_id(crypto::hash8 &payment_id8, const tools::wallet2::pend
return false;
}
+uint64_t get_outgoing_amount(const cryptonote::transaction &tx, const uint64_t amount_spent)
+{
+ return tx.version == 1 ? get_outs_money_amount(tx) : (amount_spent - tx.rct_signatures.txnFee);
+}
+
tools::wallet2::tx_construction_data get_construction_data_with_decrypted_short_payment_id(const tools::wallet2::pending_tx &ptx, hw::device &hwdev)
{
tools::wallet2::tx_construction_data construction_data = ptx.construction_data;
@@ -2712,10 +2717,10 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
LOG_ERROR("spent funds are from different subaddress accounts; count of incoming/outgoing payments will be incorrect");
subaddr_account = td.m_subaddr_index.major;
subaddr_indices.insert(td.m_subaddr_index.minor);
+ LOG_PRINT_L0("Spent money: " << print_money(amount) << ", with tx: " << txid);
+ set_spent(it->second, height);
if (!pool)
{
- LOG_PRINT_L0("Spent money: " << print_money(amount) << ", with tx: " << txid);
- set_spent(it->second, height);
if (!ignore_callbacks && 0 != m_callback)
m_callback->on_money_spent(height, txid, tx, amount, tx, td.m_subaddr_index);
@@ -2804,21 +2809,33 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
uint64_t fee = miner_tx ? 0 : tx.version == 1 ? tx_money_spent_in_ins - get_outs_money_amount(tx) : tx.rct_signatures.txnFee;
- if (tx_money_spent_in_ins > 0 && !pool)
+ if (tx_money_spent_in_ins > 0)
{
uint64_t self_received = std::accumulate<decltype(tx_money_got_in_outs.begin()), uint64_t>(tx_money_got_in_outs.begin(), tx_money_got_in_outs.end(), 0,
[&subaddr_account] (uint64_t acc, const std::pair<cryptonote::subaddress_index, uint64_t>& p)
{
return acc + (p.first.major == *subaddr_account ? p.second : 0);
});
- process_outgoing(txid, tx, height, ts, tx_money_spent_in_ins, self_received, *subaddr_account, subaddr_indices);
- // if sending to yourself at the same subaddress account, set the outgoing payment amount to 0 so that it's less confusing
- if (tx_money_spent_in_ins == self_received + fee)
+ if (!pool)
+ {
+ process_outgoing(txid, tx, height, ts, tx_money_spent_in_ins, self_received, *subaddr_account, subaddr_indices);
+ // if sending to yourself at the same subaddress account, set the outgoing payment amount to 0 so that it's less confusing
+ if (tx_money_spent_in_ins == self_received + fee)
+ {
+ auto i = m_confirmed_txs.find(txid);
+ THROW_WALLET_EXCEPTION_IF(i == m_confirmed_txs.end(), error::wallet_internal_error,
+ "confirmed tx wasn't found: " + string_tools::pod_to_hex(txid));
+ i->second.m_change = self_received;
+ }
+ }
+ else if (!m_unconfirmed_txs.count(txid))
{
- auto i = m_confirmed_txs.find(txid);
- THROW_WALLET_EXCEPTION_IF(i == m_confirmed_txs.end(), error::wallet_internal_error,
- "confirmed tx wasn't found: " + string_tools::pod_to_hex(txid));
- i->second.m_change = self_received;
+ // Add to unconfirmed txs if not already there (e.g. restoring wallet, or running the wallet in parallel to the sending wallet w/same seed)
+ add_unconfirmed_tx(txid, tx, tx_money_spent_in_ins, {}/*don't know dests*/, crypto::null_hash/*don't know payment_id*/, self_received, *subaddr_account, subaddr_indices);
+ auto i = m_unconfirmed_txs.find(txid);
+ THROW_WALLET_EXCEPTION_IF(i == m_unconfirmed_txs.end(), error::wallet_internal_error,
+ "unconfirmed tx wasn't found: " + string_tools::pod_to_hex(txid));
+ i->second.m_amount_out = get_outgoing_amount(tx, tx_money_spent_in_ins);
}
}
@@ -2967,10 +2984,7 @@ void wallet2::process_outgoing(const crypto::hash &txid, const cryptonote::trans
// wallet (eg, we're a cold wallet and the hot wallet sent it). For RCT transactions,
// we only see 0 input amounts, so have to deduce amount out from other parameters.
entry.first->second.m_amount_in = spent;
- if (tx.version == 1)
- entry.first->second.m_amount_out = get_outs_money_amount(tx);
- else
- entry.first->second.m_amount_out = spent - tx.rct_signatures.txnFee;
+ entry.first->second.m_amount_out = get_outgoing_amount(tx, spent);
entry.first->second.m_change = received;
std::vector<tx_extra_field> tx_extra_fields;
@@ -4081,6 +4095,18 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
// Lighwallet refresh done
return;
}
+
+ if (!m_first_refresh_done)
+ {
+ // We want to process the whole pool again, in case we identify received outputs in the chain we might have spent in the pool
+ m_pool_info_query_time = 0;
+ m_scanned_pool_txs[0].clear();
+ m_scanned_pool_txs[1].clear();
+ // Clear unconfirmed (received) payments because the data is 100% recovered when scanning
+ m_unconfirmed_payments.clear();
+ // Don't clear unconfirmed (sent) txs because some data is not recover-able when scanning (dests)
+ }
+
received_money = false;
blocks_fetched = 0;
uint64_t added_blocks = 0;
@@ -7475,9 +7501,9 @@ uint64_t wallet2::select_transfers(uint64_t needed_money, std::vector<size_t> un
return found_money;
}
//----------------------------------------------------------------------------------------------------
-void wallet2::add_unconfirmed_tx(const cryptonote::transaction& tx, uint64_t amount_in, const std::vector<cryptonote::tx_destination_entry> &dests, const crypto::hash &payment_id, uint64_t change_amount, uint32_t subaddr_account, const std::set<uint32_t>& subaddr_indices)
+void wallet2::add_unconfirmed_tx(const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount_in, const std::vector<cryptonote::tx_destination_entry> &dests, const crypto::hash &payment_id, uint64_t change_amount, uint32_t subaddr_account, const std::set<uint32_t>& subaddr_indices)
{
- unconfirmed_transfer_details& utd = m_unconfirmed_txs[cryptonote::get_transaction_hash(tx)];
+ unconfirmed_transfer_details& utd = m_unconfirmed_txs[txid];
utd.m_amount_in = amount_in;
utd.m_amount_out = 0;
for (const auto &d: dests)
@@ -7590,7 +7616,7 @@ void wallet2::commit_tx(pending_tx& ptx)
for(size_t idx: ptx.selected_transfers)
amount_in += m_transfers[idx].amount();
}
- add_unconfirmed_tx(ptx.tx, amount_in, dests, payment_id, ptx.change_dts.amount, ptx.construction_data.subaddr_account, ptx.construction_data.subaddr_indices);
+ add_unconfirmed_tx(txid, ptx.tx, amount_in, dests, payment_id, ptx.change_dts.amount, ptx.construction_data.subaddr_account, ptx.construction_data.subaddr_indices);
if (store_tx_info() && ptx.tx_key != crypto::null_skey)
{
m_tx_keys[txid] = ptx.tx_key;