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
|
//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) {
|
for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) {
|
||||||
const uint256& wtxid = it->first;
|
const uint256& wtxid = it->first;
|
||||||
const CWalletTx& wtx = (*it).second;
|
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;
|
unsigned int txType = 0;
|
||||||
// 0 Unassigend
|
// 0 Unassigend
|
||||||
@@ -3229,9 +3231,9 @@ UniValue getalldata(const UniValue& params, bool fHelp,const CPubKey&)
|
|||||||
|
|
||||||
|
|
||||||
uint64_t t = GetTime();
|
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))
|
if (!CheckFinalTx(wtx))
|
||||||
continue;
|
continue;
|
||||||
@@ -3515,17 +3517,19 @@ UniValue z_listreceivedaddress(const UniValue& params, bool fHelp,const CPubKey&
|
|||||||
throw runtime_error("Filter must be greater that 0.");
|
throw runtime_error("Filter must be greater that 0.");
|
||||||
|
|
||||||
//Created Ordered Transaction Map
|
//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) {
|
for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) {
|
||||||
const CWalletTx& wtx = (*it).second;
|
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();
|
uint64_t t = GetTime();
|
||||||
//Reverse Iterate thru transactions
|
//Reverse Iterate thru transactions
|
||||||
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;
|
||||||
bool includeTransaction = true;
|
bool includeTransaction = true;
|
||||||
|
|
||||||
//Excude transactions with less confirmations than required
|
//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.");
|
throw runtime_error("Filter must be greater that 0.");
|
||||||
|
|
||||||
//Created Ordered Transaction Map
|
//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) {
|
for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) {
|
||||||
const CWalletTx& wtx = (*it).second;
|
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();
|
uint64_t t = GetTime();
|
||||||
//Reverse Iterate thru transactions
|
//Reverse Iterate thru transactions
|
||||||
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))
|
if (!CheckFinalTx(wtx))
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
Reference in New Issue
Block a user