From 7c3563cd5a3edc49bbe4950375c7596dadb7e15e Mon Sep 17 00:00:00 2001 From: DanS Date: Fri, 16 Oct 2026 08:01:20 -0500 Subject: [PATCH] wallet/rpc: order history RPC txs by pointer, not by deep copy getalldata, z_listreceivedaddress and z_listsentbyaddress build a map of the whole wallet, keyed by nOrderPos, before any filter runs. Every insert deep-copies the CWalletTx, including each note's witness list (~100KB per note on a mainnet wallet). That is gigabytes of allocation per call on a large wallet, under LOCK2(cs_main, cs_wallet). The GUI's history scan runs z_listsentbyaddress on every launch and about once per new tip. The map now holds const CWalletTx* into mapWallet. It is still a std::map, so the first tx inserted for an nOrderPos wins, exactly as before. mapWallet can't change while the RPC holds cs_wallet, so the pointers stay valid. Output is unchanged for a consistent wallet. One corner case differs: with a nullifier map entry pointing at a note its tx lacks, the spends helper's operator[] inserts an empty note into that tx, and a later iteration now sees it where the old copies didn't. The next commit removes that insertion. Co-Authored-By: Claude Opus 5.5 --- src/wallet/rpcwallet.cpp | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index ea129d510..0bbf6b8ba 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -3032,11 +3032,13 @@ UniValue getalldata(const UniValue& params, bool fHelp,const CPubKey&) } //Create Ordered List - map orderedTxs; + // Pointers into mapWallet, not copies (a CWalletTx copy deep-copies every note's witness list). + // As before, the first tx inserted for an nOrderPos wins. + map orderedTxs; for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { const uint256& wtxid = it->first; const CWalletTx& wtx = (*it).second; - orderedTxs.insert(std::pair(wtx.nOrderPos, wtx)); + orderedTxs.insert(std::make_pair(wtx.nOrderPos, &wtx)); unsigned int txType = 0; // 0 Unassigend @@ -3229,9 +3231,9 @@ UniValue getalldata(const UniValue& params, bool fHelp,const CPubKey&) uint64_t t = GetTime(); - for (map::reverse_iterator it = orderedTxs.rbegin(); it != orderedTxs.rend(); ++it) + for (map::reverse_iterator it = orderedTxs.rbegin(); it != orderedTxs.rend(); ++it) { - const CWalletTx& wtx = (*it).second; + const CWalletTx& wtx = *(*it).second; if (!CheckFinalTx(wtx)) continue; @@ -3515,17 +3517,19 @@ UniValue z_listreceivedaddress(const UniValue& params, bool fHelp,const CPubKey& throw runtime_error("Filter must be greater that 0."); //Created Ordered Transaction Map - map orderedTxs; + // Pointers into mapWallet, not copies (a CWalletTx copy deep-copies every note's witness list). + // As before, the first tx inserted for an nOrderPos wins. + map orderedTxs; for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { const CWalletTx& wtx = (*it).second; - orderedTxs.insert(std::pair(wtx.nOrderPos, wtx)); + orderedTxs.insert(std::make_pair(wtx.nOrderPos, &wtx)); } uint64_t t = GetTime(); //Reverse Iterate thru transactions - for (map::reverse_iterator it = orderedTxs.rbegin(); it != orderedTxs.rend(); ++it) { - const CWalletTx& wtx = (*it).second; + for (map::reverse_iterator it = orderedTxs.rbegin(); it != orderedTxs.rend(); ++it) { + const CWalletTx& wtx = *(*it).second; bool includeTransaction = true; //Excude transactions with less confirmations than required @@ -3698,17 +3702,19 @@ UniValue z_listsentbyaddress(const UniValue& params, bool fHelp,const CPubKey&) throw runtime_error("Filter must be greater that 0."); //Created Ordered Transaction Map - map orderedTxs; + // Pointers into mapWallet, not copies (a CWalletTx copy deep-copies every note's witness list). + // As before, the first tx inserted for an nOrderPos wins. + map orderedTxs; for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { const CWalletTx& wtx = (*it).second; - orderedTxs.insert(std::pair(wtx.nOrderPos, wtx)); + orderedTxs.insert(std::make_pair(wtx.nOrderPos, &wtx)); } uint64_t t = GetTime(); //Reverse Iterate thru transactions - for (map::reverse_iterator it = orderedTxs.rbegin(); it != orderedTxs.rend(); ++it) { - const CWalletTx& wtx = (*it).second; + for (map::reverse_iterator it = orderedTxs.rbegin(); it != orderedTxs.rend(); ++it) { + const CWalletTx& wtx = *(*it).second; if (!CheckFinalTx(wtx)) continue;