Skip to content

Commit

Permalink
Merge pull request #6851
Browse files Browse the repository at this point in the history
3e7c891 Optimisation: Store transaction list order in memory rather than compute it every need (Luke Dashjr)
  • Loading branch information
gmaxwell committed Nov 21, 2015
2 parents 776848a + 3e7c891 commit 616d61b
Show file tree
Hide file tree
Showing 6 changed files with 44 additions and 52 deletions.
8 changes: 4 additions & 4 deletions src/test/accounting_tests.cpp
Expand Up @@ -45,7 +45,7 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade)
ae.nTime = 1333333333;
ae.strOtherAccount = "b";
ae.strComment = "";
walletdb.WriteAccountingEntry(ae);
pwalletMain->AddAccountingEntry(ae, walletdb);

wtx.mapValue["comment"] = "z";
pwalletMain->AddToWallet(wtx, false, &walletdb);
Expand All @@ -55,7 +55,7 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade)

ae.nTime = 1333333336;
ae.strOtherAccount = "c";
walletdb.WriteAccountingEntry(ae);
pwalletMain->AddAccountingEntry(ae, walletdb);

GetResults(walletdb, results);

Expand All @@ -71,7 +71,7 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade)
ae.nTime = 1333333330;
ae.strOtherAccount = "d";
ae.nOrderPos = pwalletMain->IncOrderPosNext();
walletdb.WriteAccountingEntry(ae);
pwalletMain->AddAccountingEntry(ae, walletdb);

GetResults(walletdb, results);

Expand Down Expand Up @@ -121,7 +121,7 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade)
ae.nTime = 1333333334;
ae.strOtherAccount = "e";
ae.nOrderPos = -1;
walletdb.WriteAccountingEntry(ae);
pwalletMain->AddAccountingEntry(ae, walletdb);

GetResults(walletdb, results);

Expand Down
12 changes: 5 additions & 7 deletions src/wallet/rpcwallet.cpp
Expand Up @@ -835,7 +835,7 @@ UniValue movecmd(const UniValue& params, bool fHelp)
debit.nTime = nNow;
debit.strOtherAccount = strTo;
debit.strComment = strComment;
walletdb.WriteAccountingEntry(debit);
pwalletMain->AddAccountingEntry(debit, walletdb);

// Credit
CAccountingEntry credit;
Expand All @@ -845,7 +845,7 @@ UniValue movecmd(const UniValue& params, bool fHelp)
credit.nTime = nNow;
credit.strOtherAccount = strFrom;
credit.strComment = strComment;
walletdb.WriteAccountingEntry(credit);
pwalletMain->AddAccountingEntry(credit, walletdb);

if (!walletdb.TxnCommit())
throw JSONRPCError(RPC_DATABASE_ERROR, "database error");
Expand Down Expand Up @@ -1470,11 +1470,10 @@ UniValue listtransactions(const UniValue& params, bool fHelp)

UniValue ret(UniValue::VARR);

std::list<CAccountingEntry> acentries;
CWallet::TxItems txOrdered = pwalletMain->OrderedTxItems(acentries, strAccount);
const CWallet::TxItems & txOrdered = pwalletMain->wtxOrdered;

// iterate backwards until we have nCount items to return:
for (CWallet::TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
for (CWallet::TxItems::const_reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
{
CWalletTx *const pwtx = (*it).second.first;
if (pwtx != 0)
Expand Down Expand Up @@ -1579,8 +1578,7 @@ UniValue listaccounts(const UniValue& params, bool fHelp)
}
}

list<CAccountingEntry> acentries;
CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries);
const list<CAccountingEntry> & acentries = pwalletMain->laccentries;
BOOST_FOREACH(const CAccountingEntry& entry, acentries)
mapAccountBalances[entry.strAccount] += entry.nCreditDebit;

Expand Down
47 changes: 18 additions & 29 deletions src/wallet/wallet.cpp
Expand Up @@ -588,31 +588,6 @@ int64_t CWallet::IncOrderPosNext(CWalletDB *pwalletdb)
return nRet;
}

CWallet::TxItems CWallet::OrderedTxItems(std::list<CAccountingEntry>& acentries, std::string strAccount)
{
AssertLockHeld(cs_wallet); // mapWallet
CWalletDB walletdb(strWalletFile);

// First: get all CWalletTx and CAccountingEntry into a sorted-by-order multimap.
TxItems txOrdered;

// Note: maintaining indices in the database of (account,time) --> txid and (account, time) --> acentry
// would make this much faster for applications that do this a lot.
for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
{
CWalletTx* wtx = &((*it).second);
txOrdered.insert(make_pair(wtx->nOrderPos, TxPair(wtx, (CAccountingEntry*)0)));
}
acentries.clear();
walletdb.ListAccountCreditDebit(strAccount, acentries);
BOOST_FOREACH(CAccountingEntry& entry, acentries)
{
txOrdered.insert(make_pair(entry.nOrderPos, TxPair((CWalletTx*)0, &entry)));
}

return txOrdered;
}

void CWallet::MarkDirty()
{
{
Expand All @@ -629,7 +604,9 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletD
if (fFromLoadWallet)
{
mapWallet[hash] = wtxIn;
mapWallet[hash].BindWallet(this);
CWalletTx& wtx = mapWallet[hash];
wtx.BindWallet(this);
wtxOrdered.insert(make_pair(wtx.nOrderPos, TxPair(&wtx, (CAccountingEntry*)0)));
AddToSpends(hash);
}
else
Expand All @@ -644,6 +621,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletD
{
wtx.nTimeReceived = GetAdjustedTime();
wtx.nOrderPos = IncOrderPosNext(pwalletdb);
wtxOrdered.insert(make_pair(wtx.nOrderPos, TxPair(&wtx, (CAccountingEntry*)0)));

wtx.nTimeSmart = wtx.nTimeReceived;
if (!wtxIn.hashBlock.IsNull())
Expand All @@ -655,9 +633,8 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletD
{
// Tolerate times up to the last timestamp in the wallet not more than 5 minutes into the future
int64_t latestTolerated = latestNow + 300;
std::list<CAccountingEntry> acentries;
TxItems txOrdered = OrderedTxItems(acentries);
for (TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
const TxItems & txOrdered = wtxOrdered;
for (TxItems::const_reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
{
CWalletTx *const pwtx = (*it).second.first;
if (pwtx == &wtx)
Expand Down Expand Up @@ -2118,6 +2095,18 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey)
return true;
}

bool CWallet::AddAccountingEntry(const CAccountingEntry& acentry, CWalletDB & pwalletdb)
{
if (!pwalletdb.WriteAccountingEntry_Backend(acentry))
return false;

laccentries.push_back(acentry);
CAccountingEntry & entry = laccentries.back();
wtxOrdered.insert(make_pair(entry.nOrderPos, TxPair((CWalletTx*)0, &entry)));

return true;
}

CAmount CWallet::GetRequiredFee(unsigned int nTxBytes)
{
return std::max(minTxFee.GetFee(nTxBytes), ::minRelayTxFee.GetFee(nTxBytes));
Expand Down
17 changes: 7 additions & 10 deletions src/wallet/wallet.h
Expand Up @@ -531,6 +531,11 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface
}

std::map<uint256, CWalletTx> mapWallet;
std::list<CAccountingEntry> laccentries;

typedef std::pair<CWalletTx*, CAccountingEntry*> TxPair;
typedef std::multimap<int64_t, TxPair > TxItems;
TxItems wtxOrdered;

int64_t nOrderPosNext;
std::map<uint256, int> mapRequestCount;
Expand Down Expand Up @@ -617,16 +622,6 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface
*/
int64_t IncOrderPosNext(CWalletDB *pwalletdb = NULL);

typedef std::pair<CWalletTx*, CAccountingEntry*> TxPair;
typedef std::multimap<int64_t, TxPair > TxItems;

/**
* Get the wallet's activity log
* @return multimap of ordered transactions and accounting entries
* @warning Returned pointers are *only* valid within the scope of passed acentries
*/
TxItems OrderedTxItems(std::list<CAccountingEntry>& acentries, std::string strAccount = "");

void MarkDirty();
bool AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletDB* pwalletdb);
void SyncTransaction(const CTransaction& tx, const CBlock* pblock);
Expand Down Expand Up @@ -656,6 +651,8 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface
std::string& strFailReason, const CCoinControl *coinControl = NULL, bool sign = true);
bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey);

bool AddAccountingEntry(const CAccountingEntry&, CWalletDB & pwalletdb);

static CFeeRate minTxFee;
/**
* Estimate the minimum fee considering user set parameters
Expand Down
8 changes: 7 additions & 1 deletion src/wallet/walletdb.cpp
Expand Up @@ -191,7 +191,7 @@ bool CWalletDB::WriteAccountingEntry(const uint64_t nAccEntryNum, const CAccount
return Write(std::make_pair(std::string("acentry"), std::make_pair(acentry.strAccount, nAccEntryNum)), acentry);
}

bool CWalletDB::WriteAccountingEntry(const CAccountingEntry& acentry)
bool CWalletDB::WriteAccountingEntry_Backend(const CAccountingEntry& acentry)
{
return WriteAccountingEntry(++nAccountingEntryNumber, acentry);
}
Expand Down Expand Up @@ -709,6 +709,12 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
if (wss.fAnyUnordered)
result = ReorderTransactions(pwallet);

pwallet->laccentries.clear();
ListAccountCreditDebit("*", pwallet->laccentries);
BOOST_FOREACH(CAccountingEntry& entry, pwallet->laccentries) {
pwallet->wtxOrdered.insert(make_pair(entry.nOrderPos, CWallet::TxPair((CWalletTx*)0, &entry)));
}

return result;
}

Expand Down
4 changes: 3 additions & 1 deletion src/wallet/walletdb.h
Expand Up @@ -110,6 +110,9 @@ class CWalletDB : public CDB

bool WriteMinVersion(int nVersion);

/// This writes directly to the database, and will not update the CWallet's cached accounting entries!
/// Use wallet.AddAccountingEntry instead, to write *and* update its caches.
bool WriteAccountingEntry_Backend(const CAccountingEntry& acentry);
bool ReadAccount(const std::string& strAccount, CAccount& account);
bool WriteAccount(const std::string& strAccount, const CAccount& account);

Expand All @@ -118,7 +121,6 @@ class CWalletDB : public CDB
/// Erase destination data tuple from wallet database
bool EraseDestData(const std::string &address, const std::string &key);

bool WriteAccountingEntry(const CAccountingEntry& acentry);
CAmount GetAccountCreditDebit(const std::string& strAccount);
void ListAccountCreditDebit(const std::string& strAccount, std::list<CAccountingEntry>& acentries);

Expand Down

0 comments on commit 616d61b

Please sign in to comment.