wallet: walk GetFilteredNotes by reference, cheap filters first
GetFilteredNotes copied every CWalletTx by value before any filter ran, then copied each SaplingNoteData by value again. Both copies deep-copy the note's witness list (up to WITNESS_CACHE_SIZE witnesses, ~100KB per note on a mainnet wallet): ~40us per note per pass, measured. Spent and locked notes were also fully decrypted (~0.6ms of Jubjub arithmetic per note) before the spent and locked checks threw them away. -deletetx is off by default, so spent notes accumulate. - iterate mapWallet and mapSaplingNoteData by const reference; - skip txs with no notes of ours before CheckFinalTx; - read GetDepthInMainChain once per tx instead of 2+N times (cs_main is held, so it can't change during the walk); - run the spent and locked checks before the decryption; - z_listreceivedbyaddress no longer copies the whole CWalletTx per entry to read its time, and z_sendmany's sort comparator takes its entries by reference. The filters have no side effects, so the result set and its order are unchanged; the A0 equivalence test passes unchanged. One deliberate difference: a spent or locked note whose ciphertext no longer decrypts under its ivk is now skipped instead of tripping the decrypt assert. The new SpentAndLockedNotesAreNotDecrypted test pins this (it aborts on the old ordering). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -4662,7 +4662,7 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp, const CPubK
|
||||
}
|
||||
obj.push_back(Pair("outindex", (int)entry.op.n));
|
||||
obj.push_back(Pair("rawconfirmations", entry.confirmations));
|
||||
auto wtx = pwalletMain->mapWallet.at(entry.op.hash); //.ToString());
|
||||
const CWalletTx& wtx = pwalletMain->mapWallet.at(entry.op.hash);
|
||||
obj.push_back(Pair("time", wtx.GetTxTime()));
|
||||
|
||||
obj.push_back(Pair("confirmations", dpowconfs));
|
||||
@@ -5376,7 +5376,7 @@ UniValue z_sendmany(const UniValue& params, bool fHelp, const CPubKey& mypk)
|
||||
// sort notes from largest to smallest, which means
|
||||
// we will spend the largest first
|
||||
std::sort(saplingEntries.begin(), saplingEntries.end(),
|
||||
[](SaplingNoteEntry i, SaplingNoteEntry j) -> bool {
|
||||
[](const SaplingNoteEntry& i, const SaplingNoteEntry& j) -> bool {
|
||||
return i.note.value() > j.note.value();
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user