wallet/rpc: order history RPC txs by pointer, not by deep copy
getalldata, z_listreceivedaddress and z_listsentbyaddress build a map<int64_t, CWalletTx> 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 <noreply@anthropic.com>
This commit is contained in:
+18
-12
@@ -3032,11 +3032,13 @@ UniValue getalldata(const UniValue& params, bool fHelp,const CPubKey&)
|
||||
}
|
||||
|
||||
//Create Ordered List
|
||||
map<int64_t,CWalletTx> 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<int64_t,const CWalletTx*> orderedTxs;
|
||||
for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) {
|
||||
const uint256& wtxid = it->first;
|
||||
const CWalletTx& wtx = (*it).second;
|
||||
orderedTxs.insert(std::pair<int64_t,CWalletTx>(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<int64_t,CWalletTx>::reverse_iterator it = orderedTxs.rbegin(); it != orderedTxs.rend(); ++it)
|
||||
for (map<int64_t,const CWalletTx*>::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<int64_t,CWalletTx> 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<int64_t,const CWalletTx*> orderedTxs;
|
||||
for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) {
|
||||
const CWalletTx& wtx = (*it).second;
|
||||
orderedTxs.insert(std::pair<int64_t,CWalletTx>(wtx.nOrderPos, wtx));
|
||||
orderedTxs.insert(std::make_pair(wtx.nOrderPos, &wtx));
|
||||
}
|
||||
|
||||
|
||||
uint64_t t = GetTime();
|
||||
//Reverse Iterate thru transactions
|
||||
for (map<int64_t,CWalletTx>::reverse_iterator it = orderedTxs.rbegin(); it != orderedTxs.rend(); ++it) {
|
||||
const CWalletTx& wtx = (*it).second;
|
||||
for (map<int64_t,const CWalletTx*>::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<int64_t,CWalletTx> 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<int64_t,const CWalletTx*> orderedTxs;
|
||||
for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) {
|
||||
const CWalletTx& wtx = (*it).second;
|
||||
orderedTxs.insert(std::pair<int64_t,CWalletTx>(wtx.nOrderPos, wtx));
|
||||
orderedTxs.insert(std::make_pair(wtx.nOrderPos, &wtx));
|
||||
}
|
||||
|
||||
|
||||
uint64_t t = GetTime();
|
||||
//Reverse Iterate thru transactions
|
||||
for (map<int64_t,CWalletTx>::reverse_iterator it = orderedTxs.rbegin(); it != orderedTxs.rend(); ++it) {
|
||||
const CWalletTx& wtx = (*it).second;
|
||||
for (map<int64_t,const CWalletTx*>::reverse_iterator it = orderedTxs.rbegin(); it != orderedTxs.rend(); ++it) {
|
||||
const CWalletTx& wtx = *(*it).second;
|
||||
|
||||
if (!CheckFinalTx(wtx))
|
||||
continue;
|
||||
|
||||
Reference in New Issue
Block a user