wallet: cut per-entry copies in listtransactions and gettransaction
- ListTransactions computes a tx's "size" once, lazily, through a const CTransaction reference instead of a slicing copy per entry. - listtransactions copies the returned window once from the built array. - OrderedTxItems can open the wallet DB read-only without a flush on close; listtransactions uses it, so a read-only RPC no longer forces a BDB checkpoint (ThreadFlushWalletDB still flushes about 2 s later). - gettransaction hex-encodes the tx without copying it. Output is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+24
-20
@@ -1788,6 +1788,11 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe
|
|||||||
bool fAllAccounts = (strAccount == string("*"));
|
bool fAllAccounts = (strAccount == string("*"));
|
||||||
bool involvesWatchonly = wtx.IsFromMe(ISMINE_WATCH_ONLY);
|
bool involvesWatchonly = wtx.IsFromMe(ISMINE_WATCH_ONLY);
|
||||||
|
|
||||||
|
// "size" is a property of the transaction, not of an entry: compute it once, when the first
|
||||||
|
// entry is built, through a const reference instead of a by-value CTransaction copy.
|
||||||
|
bool fHaveTxSize = false;
|
||||||
|
uint64_t nTxSize = 0;
|
||||||
|
|
||||||
// Sent
|
// Sent
|
||||||
if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount))
|
if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount))
|
||||||
{
|
{
|
||||||
@@ -1804,7 +1809,11 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe
|
|||||||
entry.push_back(Pair("fee", ValueFromAmount(-nFee)));
|
entry.push_back(Pair("fee", ValueFromAmount(-nFee)));
|
||||||
if (fLong)
|
if (fLong)
|
||||||
WalletTxToJSON(wtx, entry);
|
WalletTxToJSON(wtx, entry);
|
||||||
entry.push_back(Pair("size", static_cast<uint64_t>(GetSerializeSize(static_cast<CTransaction>(wtx), SER_NETWORK, PROTOCOL_VERSION))));
|
if (!fHaveTxSize) {
|
||||||
|
nTxSize = static_cast<uint64_t>(GetSerializeSize(static_cast<const CTransaction&>(wtx), SER_NETWORK, PROTOCOL_VERSION));
|
||||||
|
fHaveTxSize = true;
|
||||||
|
}
|
||||||
|
entry.push_back(Pair("size", nTxSize));
|
||||||
ret.push_back(entry);
|
ret.push_back(entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1849,7 +1858,11 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe
|
|||||||
entry.push_back(Pair("vout", r.vout));
|
entry.push_back(Pair("vout", r.vout));
|
||||||
if (fLong)
|
if (fLong)
|
||||||
WalletTxToJSON(wtx, entry);
|
WalletTxToJSON(wtx, entry);
|
||||||
entry.push_back(Pair("size", static_cast<uint64_t>(GetSerializeSize(static_cast<CTransaction>(wtx), SER_NETWORK, PROTOCOL_VERSION))));
|
if (!fHaveTxSize) {
|
||||||
|
nTxSize = static_cast<uint64_t>(GetSerializeSize(static_cast<const CTransaction&>(wtx), SER_NETWORK, PROTOCOL_VERSION));
|
||||||
|
fHaveTxSize = true;
|
||||||
|
}
|
||||||
|
entry.push_back(Pair("size", nTxSize));
|
||||||
ret.push_back(entry);
|
ret.push_back(entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1955,7 +1968,7 @@ UniValue listtransactions(const UniValue& params, bool fHelp, const CPubKey& myp
|
|||||||
UniValue ret(UniValue::VARR);
|
UniValue ret(UniValue::VARR);
|
||||||
|
|
||||||
std::list<CAccountingEntry> acentries;
|
std::list<CAccountingEntry> acentries;
|
||||||
CWallet::TxItems txOrdered = pwalletMain->OrderedTxItems(acentries, strAccount);
|
CWallet::TxItems txOrdered = pwalletMain->OrderedTxItems(acentries, strAccount, true);
|
||||||
|
|
||||||
// iterate backwards until we have nCount items to return:
|
// iterate backwards until we have nCount items to return:
|
||||||
for (CWallet::TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
|
for (CWallet::TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
|
||||||
@@ -1978,23 +1991,14 @@ UniValue listtransactions(const UniValue& params, bool fHelp, const CPubKey& myp
|
|||||||
if ((nFrom + nCount) > (int)ret.size())
|
if ((nFrom + nCount) > (int)ret.size())
|
||||||
nCount = ret.size() - nFrom;
|
nCount = ret.size() - nFrom;
|
||||||
|
|
||||||
vector<UniValue> arrTmp = ret.getValues();
|
// ret is newest to oldest; the reply is its entries [nFrom, nFrom+nCount), oldest to newest.
|
||||||
|
// Copy them once, back to front, straight from ret's storage (no vector copy/erase/reverse).
|
||||||
|
UniValue result(UniValue::VARR);
|
||||||
|
const std::vector<UniValue>& vNewestFirst = ret.getValues();
|
||||||
|
for (int i = nFrom + nCount; i > nFrom; --i)
|
||||||
|
result.push_back(vNewestFirst[i - 1]);
|
||||||
|
|
||||||
vector<UniValue>::iterator first = arrTmp.begin();
|
return result;
|
||||||
std::advance(first, nFrom);
|
|
||||||
vector<UniValue>::iterator last = arrTmp.begin();
|
|
||||||
std::advance(last, nFrom+nCount);
|
|
||||||
|
|
||||||
if (last != arrTmp.end()) arrTmp.erase(last, arrTmp.end());
|
|
||||||
if (first != arrTmp.begin()) arrTmp.erase(arrTmp.begin(), first);
|
|
||||||
|
|
||||||
std::reverse(arrTmp.begin(), arrTmp.end()); // Return oldest to newest
|
|
||||||
|
|
||||||
ret.clear();
|
|
||||||
ret.setArray();
|
|
||||||
ret.push_backV(arrTmp);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UniValue listaccounts(const UniValue& params, bool fHelp, const CPubKey& mypk)
|
UniValue listaccounts(const UniValue& params, bool fHelp, const CPubKey& mypk)
|
||||||
@@ -2239,7 +2243,7 @@ UniValue gettransaction(const UniValue& params, bool fHelp, const CPubKey& mypk)
|
|||||||
ListTransactions(wtx, "*", 0, false, details, filter);
|
ListTransactions(wtx, "*", 0, false, details, filter);
|
||||||
entry.push_back(Pair("details", details));
|
entry.push_back(Pair("details", details));
|
||||||
|
|
||||||
string strHex = EncodeHexTx(static_cast<CTransaction>(wtx));
|
string strHex = EncodeHexTx(static_cast<const CTransaction&>(wtx));
|
||||||
entry.push_back(Pair("hex", strHex));
|
entry.push_back(Pair("hex", strHex));
|
||||||
|
|
||||||
return entry;
|
return entry;
|
||||||
|
|||||||
@@ -1701,10 +1701,12 @@ int64_t CWallet::IncOrderPosNext(CWalletDB *pwalletdb)
|
|||||||
return nRet;
|
return nRet;
|
||||||
}
|
}
|
||||||
|
|
||||||
CWallet::TxItems CWallet::OrderedTxItems(std::list<CAccountingEntry>& acentries, std::string strAccount)
|
CWallet::TxItems CWallet::OrderedTxItems(std::list<CAccountingEntry>& acentries, std::string strAccount, bool fReadOnlyDb)
|
||||||
{
|
{
|
||||||
AssertLockHeld(cs_wallet); // mapWallet
|
AssertLockHeld(cs_wallet); // mapWallet
|
||||||
CWalletDB walletdb(strWalletFile);
|
// Only a read cursor over the accounting entries runs on this handle. Pure readers open it
|
||||||
|
// read-only and without flush-on-close, so the call does not end in a txn_checkpoint.
|
||||||
|
CWalletDB walletdb(strWalletFile, fReadOnlyDb ? "r" : "r+", !fReadOnlyDb);
|
||||||
|
|
||||||
// First: get all CWalletTx and CAccountingEntry into a sorted-by-order multimap.
|
// First: get all CWalletTx and CAccountingEntry into a sorted-by-order multimap.
|
||||||
TxItems txOrdered;
|
TxItems txOrdered;
|
||||||
|
|||||||
+3
-1
@@ -1210,10 +1210,12 @@ public:
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the wallet's activity log
|
* Get the wallet's activity log
|
||||||
|
* @param fReadOnlyDb open wallet.dat read-only and without flush-on-close (pure readers such as
|
||||||
|
* listtransactions: no txn_checkpoint per call); false = read-write, flush-on-close
|
||||||
* @return multimap of ordered transactions and accounting entries
|
* @return multimap of ordered transactions and accounting entries
|
||||||
* @warning Returned pointers are *only* valid within the scope of passed acentries
|
* @warning Returned pointers are *only* valid within the scope of passed acentries
|
||||||
*/
|
*/
|
||||||
TxItems OrderedTxItems(std::list<CAccountingEntry>& acentries, std::string strAccount = "");
|
TxItems OrderedTxItems(std::list<CAccountingEntry>& acentries, std::string strAccount = "", bool fReadOnlyDb = false);
|
||||||
|
|
||||||
void MarkDirty();
|
void MarkDirty();
|
||||||
bool UpdateNullifierNoteMap();
|
bool UpdateNullifierNoteMap();
|
||||||
|
|||||||
Reference in New Issue
Block a user