aboutsummaryrefslogtreecommitdiff
path: root/src/wallet/wallet.cpp
diff options
context:
space:
mode:
authorAndrew Chow <achow101-github@achow101.com>2022-08-05 15:21:23 -0400
committerAndrew Chow <achow101-github@achow101.com>2022-08-05 15:31:45 -0400
commit59bd6b6d37184b5af0d35adc9b3b52a27e1c1f7c (patch)
tree89848a7be7e234594cf56329dafce2173d00d624 /src/wallet/wallet.cpp
parent35305c759a4afe983272c3509675095743987aa5 (diff)
parentbc886fcb31e1afa7bbf7b86bfd93e51da7076ccf (diff)
downloadbitcoin-59bd6b6d37184b5af0d35adc9b3b52a27e1c1f7c.tar.xz
Merge bitcoin/bitcoin#24699: wallet: Improve AvailableCoins performance by reducing duplicated operations
bc886fcb31e1afa7bbf7b86bfd93e51da7076ccf Change mapWallet to be a std::unordered_map (Andrew Chow) 272356024db978c92112167f8d8e4cc62adad63d Change getWalletTxs to return a set instead of a vector (Andrew Chow) 97532867cf51db3e941231fbdc60f9f4fa0012a0 Change mapTxSpends to be a std::unordered_multimap (Andrew Chow) 1f798fe85ba952273005f68e36ed48cfc36f4c9d wallet: Cache SigningProviders (Andrew Chow) 8a105ecd1aeff15f84c3883e2762bf71ad59d920 wallet: Use CalculateMaximumSignedInputSize to indicate solvability (Andrew Chow) Pull request description: While running my coin selection simulations, I noticed that towards the end of the simulation, the wallet would become slow to make new transactions. The wallet generally performs much more slowly when there are a large number of transactions and/or a large number of keys. The improvements here are focused on wallets with a large number of transactions as that is what the simulations produce. Most of the slowdown I observed was due to `DescriptorScriptPubKeyMan::GetSigningProvider` re-deriving keys every time it is called. To avoid this, it will now cache the `SigningProvider` produced so that repeatedly fetching the `SigningProvider` for the same script will not result in the same key being derived over and over. This has a side effect of making the function non-const, which makes a lot of other functions non-const as well. This helps with wallets with lots of address reuse (as my coin selection simulations are), but not if addresses are not reused as keys will end up needing to be derived the first time `GetSigningProvider` is called for a script. The `GetSigningProvider` problem was also exacerbated by unnecessarily fetching a `SigningProvider` for the same script multiple times. A `SigningProvider` is retrieved to be used inside of `IsSolvable`. A few lines later, we use `GetTxSpendSize` which fetches a `SigningProvider` and then calls `CalculateMaximumSignedInputSize`. We can avoid a second call to `GetSigningProvider` by using `CalculateMaximumSignedInputSize` directly with the `SigningProvider` already retrieved for `IsSolvable`. There is an additional slowdown where `ProduceSignature` with a dummy signer is called twice for each output. The first time is `IsSolvable` checks that `ProduceSignature` succeeds, thereby informing whether we have solving data. The second is `CalculateMaximumSignedInputSize` which returns -1 if `ProduceSignature` fails, and returns the input size otherwise. We can reduce this to one call of `ProduceSignature` by using `CalculateMaximumSignedInputSize`'s result to set `solvable`. Lastly, a lot of time is spent looking in `mapWallet` and `mapTxSpends` to determine whether an output is already spent. The performance of these lookups is slightly improved by changing those maps to use `std::unordered_map` and `std::unordered_multimap` respectively. ACKs for top commit: Xekyo: ACK bc886fcb31e1afa7bbf7b86bfd93e51da7076ccf furszy: diff re-reACK bc886fcb Tree-SHA512: fd710fe1224ef67d2bb83d6ac9e7428d9f76a67f14085915f9d80e1a492d2c51cb912edfcaad1db11c2edf8d2d97eb7ddd95bfb364587fb1f143490fd72c9ec1
Diffstat (limited to 'src/wallet/wallet.cpp')
-rw-r--r--src/wallet/wallet.cpp46
1 files changed, 27 insertions, 19 deletions
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index e750cd5a2c..30ba95c3c1 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -414,7 +414,7 @@ std::shared_ptr<CWallet> RestoreWallet(WalletContext& context, const fs::path& b
const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const
{
AssertLockHeld(cs_wallet);
- std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(hash);
+ const auto it = mapWallet.find(hash);
if (it == mapWallet.end())
return nullptr;
return &(it->second);
@@ -551,7 +551,7 @@ std::set<uint256> CWallet::GetConflicts(const uint256& txid) const
std::set<uint256> result;
AssertLockHeld(cs_wallet);
- std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(txid);
+ const auto it = mapWallet.find(txid);
if (it == mapWallet.end())
return result;
const CWalletTx& wtx = it->second;
@@ -569,11 +569,17 @@ std::set<uint256> CWallet::GetConflicts(const uint256& txid) const
return result;
}
-bool CWallet::HasWalletSpend(const uint256& txid) const
+bool CWallet::HasWalletSpend(const CTransactionRef& tx) const
{
AssertLockHeld(cs_wallet);
- auto iter = mapTxSpends.lower_bound(COutPoint(txid, 0));
- return (iter != mapTxSpends.end() && iter->first.hash == txid);
+ const uint256& txid = tx->GetHash();
+ for (unsigned int i = 0; i < tx->vout.size(); ++i) {
+ auto iter = mapTxSpends.find(COutPoint(txid, i));
+ if (iter != mapTxSpends.end()) {
+ return true;
+ }
+ }
+ return false;
}
void CWallet::Flush()
@@ -636,7 +642,7 @@ bool CWallet::IsSpent(const COutPoint& outpoint) const
for (TxSpends::const_iterator it = range.first; it != range.second; ++it) {
const uint256& wtxid = it->second;
- std::map<uint256, CWalletTx>::const_iterator mit = mapWallet.find(wtxid);
+ const auto mit = mapWallet.find(wtxid);
if (mit != mapWallet.end()) {
int depth = GetTxDepthInMainChain(mit->second);
if (depth > 0 || (depth == 0 && !mit->second.isAbandoned()))
@@ -1197,12 +1203,13 @@ bool CWallet::AbandonTransaction(const uint256& hashTx)
batch.WriteTx(wtx);
NotifyTransactionChanged(wtx.GetHash(), CT_UPDATED);
// Iterate over all its outputs, and mark transactions in the wallet that spend them abandoned too
- TxSpends::const_iterator iter = mapTxSpends.lower_bound(COutPoint(now, 0));
- while (iter != mapTxSpends.end() && iter->first.hash == now) {
- if (!done.count(iter->second)) {
- todo.insert(iter->second);
+ for (unsigned int i = 0; i < wtx.tx->vout.size(); ++i) {
+ std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range = mapTxSpends.equal_range(COutPoint(now, i));
+ for (TxSpends::const_iterator iter = range.first; iter != range.second; ++iter) {
+ if (!done.count(iter->second)) {
+ todo.insert(iter->second);
+ }
}
- iter++;
}
// If a transaction changes 'conflicted' state, that changes the balance
// available of the outputs it spends. So force those to be recomputed
@@ -1248,12 +1255,13 @@ void CWallet::MarkConflicted(const uint256& hashBlock, int conflicting_height, c
wtx.MarkDirty();
batch.WriteTx(wtx);
// Iterate over all its outputs, and mark transactions in the wallet that spend them conflicted too
- TxSpends::const_iterator iter = mapTxSpends.lower_bound(COutPoint(now, 0));
- while (iter != mapTxSpends.end() && iter->first.hash == now) {
- if (!done.count(iter->second)) {
- todo.insert(iter->second);
- }
- iter++;
+ for (unsigned int i = 0; i < wtx.tx->vout.size(); ++i) {
+ std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range = mapTxSpends.equal_range(COutPoint(now, i));
+ for (TxSpends::const_iterator iter = range.first; iter != range.second; ++iter) {
+ if (!done.count(iter->second)) {
+ todo.insert(iter->second);
+ }
+ }
}
// If a transaction changes 'conflicted' state, that changes the balance
// available of the outputs it spends. So force those to be recomputed
@@ -1370,7 +1378,7 @@ CAmount CWallet::GetDebit(const CTxIn &txin, const isminefilter& filter) const
{
{
LOCK(cs_wallet);
- std::map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(txin.prevout.hash);
+ const auto mi = mapWallet.find(txin.prevout.hash);
if (mi != mapWallet.end())
{
const CWalletTx& prev = (*mi).second;
@@ -1959,7 +1967,7 @@ bool CWallet::SignTransaction(CMutableTransaction& tx) const
// Build coins map
std::map<COutPoint, Coin> coins;
for (auto& input : tx.vin) {
- std::map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(input.prevout.hash);
+ const auto mi = mapWallet.find(input.prevout.hash);
if(mi == mapWallet.end() || input.prevout.n >= mi->second.tx->vout.size()) {
return false;
}