Merge branch 'reintroduce_hashtable'

This commit is contained in:
Brannon King 2020-03-03 15:02:32 -07:00
commit 2d01cf1ece
17 changed files with 2404 additions and 127 deletions

4
.gitignore vendored
View file

@ -121,3 +121,7 @@ contrib/devtools/split-debug.sh
cmake-build-*/ cmake-build-*/
compile_commands\.json compile_commands\.json
**/compiler*\.d
conftest.dir/
confdefs.h

View file

@ -148,6 +148,7 @@ BITCOIN_CORE_H = \
policy/fees.h \ policy/fees.h \
policy/policy.h \ policy/policy.h \
policy/rbf.h \ policy/rbf.h \
primitives/robin_hood.h \
pow.h \ pow.h \
protocol.h \ protocol.h \
random.h \ random.h \

View file

@ -9,9 +9,8 @@
/** Template base class for fixed-sized opaque blobs. */ /** Template base class for fixed-sized opaque blobs. */
template<uint32_t BITS> template<uint32_t BITS>
CBaseBlob<BITS>::CBaseBlob() CBaseBlob<BITS>::CBaseBlob() noexcept : data{}
{ {
SetNull();
} }
template<uint32_t BITS> template<uint32_t BITS>
@ -22,16 +21,15 @@ CBaseBlob<BITS>::CBaseBlob(const std::vector<uint8_t>& vec)
} }
template<uint32_t BITS> template<uint32_t BITS>
CBaseBlob<BITS>::CBaseBlob(const CBaseBlob& o) CBaseBlob<BITS>::CBaseBlob(const CBaseBlob& o) noexcept
{ {
*this = o; *this = o;
} }
template<uint32_t BITS> template<uint32_t BITS>
CBaseBlob<BITS>& CBaseBlob<BITS>::operator=(const CBaseBlob& o) CBaseBlob<BITS>& CBaseBlob<BITS>::operator=(const CBaseBlob& o) noexcept
{ {
if (this != &o) data = o.data;
std::copy(o.begin(), o.end(), begin());
return *this; return *this;
} }
@ -56,7 +54,7 @@ bool CBaseBlob<BITS>::operator==(const CBaseBlob& b) const
template<uint32_t BITS> template<uint32_t BITS>
bool CBaseBlob<BITS>::operator!=(const CBaseBlob& b) const bool CBaseBlob<BITS>::operator!=(const CBaseBlob& b) const
{ {
return !(*this == b); return Compare(b) != 0;
} }
template<uint32_t BITS> template<uint32_t BITS>

View file

@ -12,15 +12,15 @@ class CBaseBlob
{ {
std::array<uint8_t, BITS / 8> data; std::array<uint8_t, BITS / 8> data;
public: public:
CBaseBlob(); CBaseBlob() noexcept;
explicit CBaseBlob(const std::vector<uint8_t>& vec); explicit CBaseBlob(const std::vector<uint8_t>& vec);
CBaseBlob(CBaseBlob&&) = default; CBaseBlob(CBaseBlob&&) = default;
CBaseBlob& operator=(CBaseBlob&&) = default; CBaseBlob& operator=(CBaseBlob&&) = default;
CBaseBlob(const CBaseBlob& o); CBaseBlob(const CBaseBlob& o) noexcept;
CBaseBlob& operator=(const CBaseBlob& o); CBaseBlob& operator=(const CBaseBlob& o) noexcept;
int Compare(const CBaseBlob& b) const; int Compare(const CBaseBlob& b) const;
bool operator<(const CBaseBlob& b) const; bool operator<(const CBaseBlob& b) const;

View file

@ -105,14 +105,15 @@ namespace sqlite
return code; return code;
} }
inline int sync(database& db, std::size_t attempts = 200) inline int sync(database& db, std::size_t attempts = 20)
{ {
int code = SQLITE_OK; int code = SQLITE_OK;
for (auto i = 0u; i < attempts; ++i) { for (auto i = 0u; i < attempts; ++i) {
code = sqlite3_wal_checkpoint_v2(db.connection().get(), nullptr, SQLITE_CHECKPOINT_FULL, nullptr, nullptr); code = sqlite3_wal_checkpoint_v2(db.connection().get(), nullptr, SQLITE_CHECKPOINT_FULL, nullptr, nullptr);
if (code != SQLITE_OK) { if (code != SQLITE_OK) {
sqlite3_wal_checkpoint_v2(db.connection().get(), nullptr, SQLITE_CHECKPOINT_PASSIVE, nullptr, nullptr);
using namespace std::chrono_literals; using namespace std::chrono_literals;
std::this_thread::sleep_for(100ms); std::this_thread::sleep_for(200ms);
continue; continue;
} }
break; break;

View file

@ -5,6 +5,8 @@
#include <blob.h> #include <blob.h>
#include <string> #include <string>
#include <primitives/robin_hood.h>
class uint160 : public CBaseBlob<160> class uint160 : public CBaseBlob<160>
{ {
public: public:
@ -40,4 +42,26 @@ uint160 uint160S(const std::string& s);
uint256 uint256S(const char* str); uint256 uint256S(const char* str);
uint256 uint256S(const std::string& s); uint256 uint256S(const std::string& s);
namespace std {
template <>
struct hash<uint160>
{
size_t operator()(const uint160& k) const
{
return robin_hood::hash_bytes(k.begin(), k.size());
}
};
template <>
struct hash<uint256>
{
size_t operator()(const uint256& k) const
{
return robin_hood::hash_bytes(k.begin(), k.size());
}
};
}
#endif // CLAIMTRIE_UINTS_H #endif // CLAIMTRIE_UINTS_H

View file

@ -15,6 +15,9 @@
#include <boost/signals2/signal.hpp> #include <boost/signals2/signal.hpp>
#include <primitives/robin_hood.h>
#include <set>
/** A virtual base class for key stores */ /** A virtual base class for key stores */
class CKeyStore : public SigningProvider class CKeyStore : public SigningProvider
{ {
@ -44,9 +47,9 @@ class CBasicKeyStore : public CKeyStore
protected: protected:
mutable CCriticalSection cs_KeyStore; mutable CCriticalSection cs_KeyStore;
using KeyMap = std::map<CKeyID, CKey>; using KeyMap = robin_hood::unordered_map<CKeyID, CKey>;
using WatchKeyMap = std::map<CKeyID, CPubKey>; using WatchKeyMap = robin_hood::unordered_map<CKeyID, CPubKey>;
using ScriptMap = std::map<CScriptID, CScript>; using ScriptMap = robin_hood::unordered_map<CScriptID, CScript>;
using WatchOnlySet = std::set<CScript>; using WatchOnlySet = std::set<CScript>;
KeyMap mapKeys GUARDED_BY(cs_KeyStore); KeyMap mapKeys GUARDED_BY(cs_KeyStore);

2198
src/primitives/robin_hood.h Normal file

File diff suppressed because it is too large Load diff

View file

@ -36,6 +36,17 @@ public:
} }
}; };
namespace std {
template <>
struct hash<CKeyID>
{
size_t operator()(const CKeyID& k) const
{
return robin_hood::hash_bytes(k.begin(), k.size());
}
};
}
typedef uint256 ChainCode; typedef uint256 ChainCode;
/** An encapsulated public key. */ /** An encapsulated public key. */

View file

@ -27,6 +27,17 @@ public:
CScriptID(const uint160& in) : uint160(in) {} CScriptID(const uint160& in) : uint160(in) {}
}; };
namespace std {
template <>
struct hash<CScriptID>
{
size_t operator()(const CScriptID& k) const
{
return robin_hood::hash_bytes(k.begin(), k.size());
}
};
}
/** /**
* Default setting for nMaxDatacarrierBytes. 80 bytes of data, +1 for OP_RETURN, * Default setting for nMaxDatacarrierBytes. 80 bytes of data, +1 for OP_RETURN,
* +2 for the pushdata opcodes. * +2 for the pushdata opcodes.

View file

@ -148,7 +148,7 @@ bool CCoinsViewDB::BatchWrite(const CCoinsMap &mapCoins, const uint256 &hashBloc
code = sqlite::sync(db); code = sqlite::sync(db);
if (code != SQLITE_OK) { if (code != SQLITE_OK) {
LogPrintf("%s: Error syncing coin database. SQLite error: %d\n", __func__, code); LogPrintf("%s: Error syncing coin database. SQLite error: %d\n", __func__, code);
return false; // don't return false as it may happen if someone else is reading our DB
} }
} }
return true; return true;
@ -338,7 +338,7 @@ bool CBlockTreeDB::BatchWrite(const std::vector<std::pair<int, const CBlockFileI
code = sqlite::sync(db); code = sqlite::sync(db);
if (code != SQLITE_OK) { if (code != SQLITE_OK) {
LogPrintf("%s: Error syncing block database. SQLite error: %d\n", __func__, code); LogPrintf("%s: Error syncing block database. SQLite error: %d\n", __func__, code);
return false; // don't return false here as this may happen if someone else is reading our DB
} }
} }
return true; return true;

View file

@ -2206,7 +2206,7 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &
if (!CheckDiskSpace(48 * 2 * 2 * pcoinsTip->GetCacheSize())) if (!CheckDiskSpace(48 * 2 * 2 * pcoinsTip->GetCacheSize()))
return state.Error("out of disk space"); return state.Error("out of disk space");
if (syncToDisk && !pclaimTrie->SyncToDisk()) if (syncToDisk && !pclaimTrie->SyncToDisk())
return state.Error("Failed to write to claim trie database"); LogPrintf("Failed to sync claim trie database to disk");
// Flush the chainstate (which may refer to block index entries). // Flush the chainstate (which may refer to block index entries).
if (!pcoinsTip->Flush(syncToDisk)) if (!pcoinsTip->Flush(syncToDisk))
return AbortNode(state, "Failed to write to coin database"); return AbortNode(state, "Failed to write to coin database");

View file

@ -17,6 +17,7 @@
#include <nameclaim.h> #include <nameclaim.h>
#include <protocol.h> // For CMessageHeader::MessageStartChars #include <protocol.h> // For CMessageHeader::MessageStartChars
#include <policy/feerate.h> #include <policy/feerate.h>
#include <primitives/robin_hood.h>
#include <script/script_error.h> #include <script/script_error.h>
#include <sync.h> #include <sync.h>
#include <txmempool.h> #include <txmempool.h>
@ -145,21 +146,27 @@ extern CBlockPolicyEstimator feeEstimator;
extern CTxMemPool mempool; extern CTxMemPool mempool;
extern std::atomic_bool g_is_mempool_loaded; extern std::atomic_bool g_is_mempool_loaded;
struct BlockIndexPointerCompare { struct BlockMapHasher {
inline bool operator() (const CBlockIndex* lhs, const CBlockIndex* rhs) const { std::size_t operator()(const CBlockIndex* block) const {
return lhs->hash < rhs->hash; return robin_hood::hash_bytes(block->hash.begin(), block->hash.size());
} }
}; };
struct BlockMap : public std::set<CBlockIndex*, BlockIndexPointerCompare> { struct BlockMapComparer {
bool operator()(const CBlockIndex* a, const CBlockIndex* b) const {
return a == b || a->hash == b->hash;
}
};
struct BlockMap : public robin_hood::unordered_set<CBlockIndex*, BlockMapHasher, BlockMapComparer> {
inline iterator find(const uint256& blockHash) { inline iterator find(const uint256& blockHash) {
CBlockIndex temp(blockHash); CBlockIndex temp(blockHash);
return std::set<CBlockIndex*, BlockIndexPointerCompare>::find(&temp); return robin_hood::unordered_set<CBlockIndex*, BlockMapHasher, BlockMapComparer>::find(&temp);
} }
inline const_iterator find(const uint256& blockHash) const { inline const_iterator find(const uint256& blockHash) const {
CBlockIndex temp(blockHash); CBlockIndex temp(blockHash);
return std::set<CBlockIndex*, BlockIndexPointerCompare>::find(&temp); return robin_hood::unordered_set<CBlockIndex*, BlockMapHasher, BlockMapComparer>::find(&temp);
} }
}; };

View file

@ -126,7 +126,7 @@ private:
bool fDecryptionThoroughlyChecked; bool fDecryptionThoroughlyChecked;
protected: protected:
using CryptedKeyMap = std::map<CKeyID, std::pair<CPubKey, std::vector<unsigned char>>>; using CryptedKeyMap = std::unordered_map<CKeyID, std::pair<CPubKey, std::vector<unsigned char>>>;
bool SetCrypted(); bool SetCrypted();

View file

@ -467,15 +467,11 @@ static UniValue getaddressesbyaccount(const JSONRPCRequest& request)
static CTransactionRef SendMoney(CWallet * const pwallet, const CTxDestination &address, CAmount nValue, static CTransactionRef SendMoney(CWallet * const pwallet, const CTxDestination &address, CAmount nValue,
bool fSubtractFeeFromAmount, const CCoinControl& coin_control, mapValue_t mapValue, std::string fromAccount, bool fSubtractFeeFromAmount, const CCoinControl& coin_control, mapValue_t mapValue, std::string fromAccount,
const CScript& prefix = CScript()){ const CScript& prefix = CScript()){
CAmount curBalance = pwallet->GetBalance();
// Check amount // Check amount
if (nValue <= 0) if (nValue <= 0)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount"); throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount");
if (nValue > curBalance)
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds");
if (pwallet->GetBroadcastTransactions() && !g_connman) { if (pwallet->GetBroadcastTransactions() && !g_connman) {
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
} }
@ -493,6 +489,7 @@ static CTransactionRef SendMoney(CWallet * const pwallet, const CTxDestination &
vecSend.push_back(recipient); vecSend.push_back(recipient);
CTransactionRef tx; CTransactionRef tx;
if (!pwallet->CreateTransaction(vecSend, tx, reservekey, nFeeRequired, nChangePosRet, strError, coin_control)) { if (!pwallet->CreateTransaction(vecSend, tx, reservekey, nFeeRequired, nChangePosRet, strError, coin_control)) {
CAmount curBalance = pwallet->GetBalance();
if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance) if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance)
strError = strprintf("Error: This transaction requires a transaction fee of at least %s", FormatMoney(nFeeRequired)); strError = strprintf("Error: This transaction requires a transaction fee of at least %s", FormatMoney(nFeeRequired));
throw JSONRPCError(RPC_WALLET_ERROR, strError); throw JSONRPCError(RPC_WALLET_ERROR, strError);
@ -1289,7 +1286,7 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
// Tally // Tally
CAmount nAmount = 0; CAmount nAmount = 0;
for (const std::pair<const uint256, CWalletTx>& pairWtx : pwallet->mapWallet) { for (const auto& pairWtx : pwallet->mapWallet) {
const CWalletTx& wtx = pairWtx.second; const CWalletTx& wtx = pairWtx.second;
if (wtx.IsCoinBase() || !CheckFinalTx(*wtx.tx)) if (wtx.IsCoinBase() || !CheckFinalTx(*wtx.tx))
continue; continue;
@ -1357,7 +1354,7 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request)
// Tally // Tally
CAmount nAmount = 0; CAmount nAmount = 0;
for (const std::pair<const uint256, CWalletTx>& pairWtx : pwallet->mapWallet) { for (const auto& pairWtx : pwallet->mapWallet) {
const CWalletTx& wtx = pairWtx.second; const CWalletTx& wtx = pairWtx.second;
if (wtx.IsCoinBase() || !CheckFinalTx(*wtx.tx)) if (wtx.IsCoinBase() || !CheckFinalTx(*wtx.tx))
continue; continue;
@ -2141,7 +2138,7 @@ static UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bo
// Tally // Tally
std::map<CTxDestination, tallyitem> mapTally; std::map<CTxDestination, tallyitem> mapTally;
for (const std::pair<const uint256, CWalletTx>& pairWtx : pwallet->mapWallet) { for (const auto& pairWtx : pwallet->mapWallet) {
const CWalletTx& wtx = pairWtx.second; const CWalletTx& wtx = pairWtx.second;
if (wtx.IsCoinBase() || !CheckFinalTx(*wtx.tx)) if (wtx.IsCoinBase() || !CheckFinalTx(*wtx.tx))
@ -2193,7 +2190,6 @@ static UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bo
for (auto item_it = start; item_it != end; ++item_it) for (auto item_it = start; item_it != end; ++item_it)
{ {
const CTxDestination& address = item_it->first; const CTxDestination& address = item_it->first;
const std::string& label = item_it->second.name;
auto it = mapTally.find(address); auto it = mapTally.find(address);
if (it == mapTally.end() && !fIncludeEmpty) if (it == mapTally.end() && !fIncludeEmpty)
continue; continue;
@ -2208,6 +2204,7 @@ static UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bo
fIsWatchonly = (*it).second.fIsWatchonly; fIsWatchonly = (*it).second.fIsWatchonly;
} }
const std::string& label = item_it->second.name;
if (by_label) if (by_label)
{ {
tallyitem& _item = label_tally[label]; tallyitem& _item = label_tally[label];
@ -2225,6 +2222,7 @@ static UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bo
obj.pushKV("amount", ValueFromAmount(nAmount)); obj.pushKV("amount", ValueFromAmount(nAmount));
obj.pushKV("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)); obj.pushKV("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf));
obj.pushKV("label", label); obj.pushKV("label", label);
obj.pushKV("purpose", item_it->second.purpose);
UniValue transactions(UniValue::VARR); UniValue transactions(UniValue::VARR);
if (it != mapTally.end()) if (it != mapTally.end())
{ {
@ -2733,7 +2731,7 @@ static UniValue listaccounts(const JSONRPCRequest& request)
} }
} }
for (const std::pair<const uint256, CWalletTx>& pairWtx : pwallet->mapWallet) { for (const auto& pairWtx : pwallet->mapWallet) {
const CWalletTx& wtx = pairWtx.second; const CWalletTx& wtx = pairWtx.second;
CAmount nFee; CAmount nFee;
std::string strSentAccount; std::string strSentAccount;
@ -2871,7 +2869,7 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
UniValue transactions(UniValue::VARR); UniValue transactions(UniValue::VARR);
for (const std::pair<const uint256, CWalletTx>& pairWtx : pwallet->mapWallet) { for (const auto& pairWtx : pwallet->mapWallet) {
CWalletTx tx = pairWtx.second; CWalletTx tx = pairWtx.second;
if (depth == -1 || tx.GetDepthInMainChain() < depth) { if (depth == -1 || tx.GetDepthInMainChain() < depth) {

View file

@ -30,7 +30,7 @@
#include <wallet/fees.h> #include <wallet/fees.h>
#include <algorithm> #include <algorithm>
#include <assert.h> #include <cassert>
#include <future> #include <future>
#include <boost/algorithm/string/replace.hpp> #include <boost/algorithm/string/replace.hpp>
@ -54,7 +54,7 @@ bool RemoveWallet(const std::shared_ptr<CWallet>& wallet)
{ {
LOCK(cs_wallets); LOCK(cs_wallets);
assert(wallet); assert(wallet);
std::vector<std::shared_ptr<CWallet>>::iterator i = std::find(vpwallets.begin(), vpwallets.end(), wallet); auto i = std::find(vpwallets.begin(), vpwallets.end(), wallet);
if (i == vpwallets.end()) return false; if (i == vpwallets.end()) return false;
vpwallets.erase(i); vpwallets.erase(i);
return true; return true;
@ -208,7 +208,7 @@ public:
const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(hash); auto it = mapWallet.find(hash);
if (it == mapWallet.end()) if (it == mapWallet.end())
return nullptr; return nullptr;
return &(it->second); return &(it->second);
@ -560,7 +560,7 @@ std::set<uint256> CWallet::GetConflicts(const uint256& txid) const
std::set<uint256> result; std::set<uint256> result;
AssertLockHeld(cs_wallet); AssertLockHeld(cs_wallet);
std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(txid); auto it = mapWallet.find(txid);
if (it == mapWallet.end()) if (it == mapWallet.end())
return result; return result;
const CWalletTx& wtx = it->second; const CWalletTx& wtx = it->second;
@ -569,11 +569,16 @@ std::set<uint256> CWallet::GetConflicts(const uint256& txid) const
for (const CTxIn& txin : wtx.tx->vin) for (const CTxIn& txin : wtx.tx->vin)
{ {
if (mapTxSpends.count(txin.prevout) <= 1) auto hitTx = mapTxSpends.find(txin.prevout.hash);
if (hitTx == mapTxSpends.end())
continue;
auto hitN = hitTx->second.find(txin.prevout.n);
if (hitN == hitTx->second.end())
continue;
if (hitN->second.size() <= 1U)
continue; // No conflict if zero or one spends continue; // No conflict if zero or one spends
range = mapTxSpends.equal_range(txin.prevout); for (auto& spend: hitN->second)
for (TxSpends::const_iterator _it = range.first; _it != range.second; ++_it) result.insert(spend);
result.insert(_it->second);
} }
return result; return result;
} }
@ -581,8 +586,14 @@ std::set<uint256> CWallet::GetConflicts(const uint256& txid) const
bool CWallet::HasWalletSpend(const uint256& txid) const bool CWallet::HasWalletSpend(const uint256& txid) const
{ {
AssertLockHeld(cs_wallet); AssertLockHeld(cs_wallet);
auto iter = mapTxSpends.lower_bound(COutPoint(txid, 0)); auto hitTx = mapTxSpends.find(txid);
return (iter != mapTxSpends.end() && iter->first.hash == txid); if (hitTx == mapTxSpends.end())
return false;
for (auto& kvp: hitTx->second) {
if (!kvp.second.empty())
return true;
}
return false;
} }
void CWallet::Flush(bool shutdown) void CWallet::Flush(bool shutdown)
@ -590,7 +601,7 @@ void CWallet::Flush(bool shutdown)
database->Flush(shutdown); database->Flush(shutdown);
} }
void CWallet::SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator> range) void CWallet::SyncMetaData(const COutPoint& outPoint)
{ {
// We want all the wallet transactions in range to have the same metadata as // We want all the wallet transactions in range to have the same metadata as
// the oldest (smallest nOrderPos). // the oldest (smallest nOrderPos).
@ -598,36 +609,40 @@ void CWallet::SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator> ran
int nMinOrderPos = std::numeric_limits<int>::max(); int nMinOrderPos = std::numeric_limits<int>::max();
const CWalletTx* copyFrom = nullptr; const CWalletTx* copyFrom = nullptr;
for (TxSpends::iterator it = range.first; it != range.second; ++it) {
const CWalletTx* wtx = &mapWallet.at(it->second); auto hitTx = mapTxSpends.find(outPoint.hash);
if (wtx->nOrderPos < nMinOrderPos) { if (hitTx != mapTxSpends.end()) {
nMinOrderPos = wtx->nOrderPos; auto hitN = hitTx->second.find(outPoint.n);
copyFrom = wtx; if (hitN != hitTx->second.end()) {
for (auto &spend: hitN->second) {
const CWalletTx *wtx = &mapWallet.at(spend);
if (wtx->nOrderPos < nMinOrderPos) {
nMinOrderPos = wtx->nOrderPos;
copyFrom = wtx;
}
}
if (!copyFrom) {
return;
}
// Now copy data from copyFrom to rest:
for (auto &hash: hitN->second) {
CWalletTx *copyTo = &mapWallet.at(hash);
if (copyFrom == copyTo) continue;
assert(copyFrom && "Oldest wallet transaction in range assumed to have been found.");
if (!copyFrom->IsEquivalentTo(*copyTo)) continue;
copyTo->mapValue = copyFrom->mapValue;
copyTo->vOrderForm = copyFrom->vOrderForm;
// fTimeReceivedIsTxTime not copied on purpose
// nTimeReceived not copied on purpose
copyTo->nTimeSmart = copyFrom->nTimeSmart;
copyTo->fFromMe = copyFrom->fFromMe;
copyTo->strFromAccount = copyFrom->strFromAccount;
// nOrderPos not copied on purpose
// cached members not copied on purpose
}
} }
} }
if (!copyFrom) {
return;
}
// Now copy data from copyFrom to rest:
for (TxSpends::iterator it = range.first; it != range.second; ++it)
{
const uint256& hash = it->second;
CWalletTx* copyTo = &mapWallet.at(hash);
if (copyFrom == copyTo) continue;
assert(copyFrom && "Oldest wallet transaction in range assumed to have been found.");
if (!copyFrom->IsEquivalentTo(*copyTo)) continue;
copyTo->mapValue = copyFrom->mapValue;
copyTo->vOrderForm = copyFrom->vOrderForm;
// fTimeReceivedIsTxTime not copied on purpose
// nTimeReceived not copied on purpose
copyTo->nTimeSmart = copyFrom->nTimeSmart;
copyTo->fFromMe = copyFrom->fFromMe;
copyTo->strFromAccount = copyFrom->strFromAccount;
// nOrderPos not copied on purpose
// cached members not copied on purpose
}
} }
/** /**
@ -636,14 +651,15 @@ void CWallet::SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator> ran
*/ */
bool CWallet::IsSpent(const uint256& hash, unsigned int n) const bool CWallet::IsSpent(const uint256& hash, unsigned int n) const
{ {
const COutPoint outpoint(hash, n); auto hitTx = mapTxSpends.find(hash);
std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range; if (hitTx == mapTxSpends.end())
range = mapTxSpends.equal_range(outpoint); return false;
auto hitN = hitTx->second.find(n);
if (hitN == hitTx->second.end())
return false;
for (TxSpends::const_iterator it = range.first; it != range.second; ++it) for (auto& wtxid: hitN->second) {
{ auto mit = mapWallet.find(wtxid);
const uint256& wtxid = it->second;
std::map<uint256, CWalletTx>::const_iterator mit = mapWallet.find(wtxid);
if (mit != mapWallet.end()) { if (mit != mapWallet.end()) {
int depth = mit->second.GetDepthInMainChain(); int depth = mit->second.GetDepthInMainChain();
if (depth > 0 || (depth == 0 && !mit->second.isAbandoned())) if (depth > 0 || (depth == 0 && !mit->second.isAbandoned()))
@ -655,13 +671,9 @@ bool CWallet::IsSpent(const uint256& hash, unsigned int n) const
void CWallet::AddToSpends(const COutPoint& outpoint, const uint256& wtxid) void CWallet::AddToSpends(const COutPoint& outpoint, const uint256& wtxid)
{ {
mapTxSpends.insert(std::make_pair(outpoint, wtxid)); mapTxSpends[outpoint.hash][outpoint.n].push_back(wtxid);
setLockedCoins.erase(outpoint); setLockedCoins.erase(outpoint);
SyncMetaData(outpoint);
std::pair<TxSpends::iterator, TxSpends::iterator> range;
range = mapTxSpends.equal_range(outpoint);
SyncMetaData(range);
} }
@ -907,9 +919,7 @@ bool CWallet::GetLabelDestination(CTxDestination &dest, const std::string& label
else { else {
// Check if the current key has been used (TODO: check other addresses with the same key) // Check if the current key has been used (TODO: check other addresses with the same key)
CScript scriptPubKey = GetScriptForDestination(GetDestinationForKey(account.vchPubKey, m_default_address_type)); CScript scriptPubKey = GetScriptForDestination(GetDestinationForKey(account.vchPubKey, m_default_address_type));
for (std::map<uint256, CWalletTx>::iterator it = mapWallet.begin(); for (auto it = mapWallet.begin(); it != mapWallet.end() && account.vchPubKey.IsValid(); ++it)
it != mapWallet.end() && account.vchPubKey.IsValid();
++it)
for (const CTxOut& txout : (*it).second.tx->vout) for (const CTxOut& txout : (*it).second.tx->vout)
if (txout.scriptPubKey == scriptPubKey) { if (txout.scriptPubKey == scriptPubKey) {
bForceNew = true; bForceNew = true;
@ -938,7 +948,7 @@ void CWallet::MarkDirty()
{ {
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
for (std::pair<const uint256, CWalletTx>& item : mapWallet) for (auto& item : mapWallet)
item.second.MarkDirty(); item.second.MarkDirty();
} }
} }
@ -981,7 +991,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
uint256 hash = wtxIn.GetHash(); uint256 hash = wtxIn.GetHash();
// Inserts only if not already there, returns tx inserted or tx found // Inserts only if not already there, returns tx inserted or tx found
std::pair<std::map<uint256, CWalletTx>::iterator, bool> ret = mapWallet.insert(std::make_pair(hash, wtxIn)); auto ret = mapWallet.emplace(hash, wtxIn);
CWalletTx& wtx = (*ret.first).second; CWalletTx& wtx = (*ret.first).second;
wtx.BindWallet(this); wtx.BindWallet(this);
bool fInsertedNew = ret.second; bool fInsertedNew = ret.second;
@ -1086,13 +1096,19 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, const CBlockI
if (pIndex != nullptr) { if (pIndex != nullptr) {
for (const CTxIn& txin : tx.vin) { for (const CTxIn& txin : tx.vin) {
std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range = mapTxSpends.equal_range(txin.prevout); auto hitTx = mapTxSpends.find(txin.prevout.hash);
while (range.first != range.second) { if (hitTx == mapTxSpends.end())
if (range.first->second != tx.GetHash()) { continue;
WalletLogPrintf("Transaction %s (in block %s) conflicts with wallet transaction %s (both spend %s:%i)\n", tx.GetHash().ToString(), pIndex->GetBlockHash().ToString(), range.first->second.ToString(), range.first->first.hash.ToString(), range.first->first.n); auto hitN = hitTx->second.find(txin.prevout.n);
MarkConflicted(pIndex->GetBlockHash(), range.first->second); if (hitN == hitTx->second.end())
continue;
for (auto& spend: hitN->second) {
if (spend != tx.GetHash()) {
WalletLogPrintf("Transaction %s (in block %s) conflicts with wallet transaction %s (both spend %s:%i)\n",
tx.GetHash().ToString(), pIndex->GetBlockHash().ToString(), spend.ToString(), hitTx->first.ToString(), hitN->first);
MarkConflicted(pIndex->GetBlockHash(), spend);
} }
range.first++;
} }
} }
} }
@ -1113,7 +1129,7 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, const CBlockI
std::vector<CKeyID> vAffected; std::vector<CKeyID> vAffected;
CAffectedKeysVisitor(*this, vAffected).Process(txout.scriptPubKey); CAffectedKeysVisitor(*this, vAffected).Process(txout.scriptPubKey);
for (const CKeyID &keyid : vAffected) { for (const CKeyID &keyid : vAffected) {
std::map<CKeyID, int64_t>::const_iterator mi = m_pool_key_to_index.find(keyid); auto mi = m_pool_key_to_index.find(keyid);
if (mi != m_pool_key_to_index.end()) { if (mi != m_pool_key_to_index.end()) {
WalletLogPrintf("%s: Detected a used keypool key, mark all keypool key up to this key as used\n", __func__); WalletLogPrintf("%s: Detected a used keypool key, mark all keypool key up to this key as used\n", __func__);
MarkReserveKeysAsUsed(mi->second); MarkReserveKeysAsUsed(mi->second);
@ -1177,7 +1193,7 @@ bool CWallet::AbandonTransaction(const uint256& hashTx)
uint256 now = *todo.begin(); uint256 now = *todo.begin();
todo.erase(now); todo.erase(now);
done.insert(now); done.insert(now);
auto it = mapWallet.find(now); it = mapWallet.find(now);
assert(it != mapWallet.end()); assert(it != mapWallet.end());
CWalletTx& wtx = it->second; CWalletTx& wtx = it->second;
int currentconfirm = wtx.GetDepthInMainChain(); int currentconfirm = wtx.GetDepthInMainChain();
@ -1193,12 +1209,14 @@ bool CWallet::AbandonTransaction(const uint256& hashTx)
batch.WriteTx(wtx); batch.WriteTx(wtx);
NotifyTransactionChanged(this, wtx.GetHash(), CT_UPDATED); NotifyTransactionChanged(this, wtx.GetHash(), CT_UPDATED);
// Iterate over all its outputs, and mark transactions in the wallet that spend them abandoned too // 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)); auto hitTx = mapTxSpends.find(now);
while (iter != mapTxSpends.end() && iter->first.hash == now) { if (hitTx != mapTxSpends.end()) {
if (!done.count(iter->second)) { for (auto &kvp: hitTx->second) {
todo.insert(iter->second); for (auto &spent: kvp.second) {
if (!done.count(spent))
todo.insert(spent);
}
} }
iter++;
} }
// If a transaction changes 'conflicted' state, that changes the balance // If a transaction changes 'conflicted' state, that changes the balance
// available of the outputs it spends. So force those to be recomputed // available of the outputs it spends. So force those to be recomputed
@ -1249,12 +1267,14 @@ void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx)
wtx.MarkDirty(); wtx.MarkDirty();
batch.WriteTx(wtx); batch.WriteTx(wtx);
// Iterate over all its outputs, and mark transactions in the wallet that spend them conflicted too // 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)); auto hitTx = mapTxSpends.find(now);
while (iter != mapTxSpends.end() && iter->first.hash == now) { if (hitTx != mapTxSpends.end()) {
if (!done.count(iter->second)) { for (auto &kvp: hitTx->second) {
todo.insert(iter->second); for (auto &spent: kvp.second) {
} if (!done.count(spent))
iter++; todo.insert(spent);
}
}
} }
// If a transaction changes 'conflicted' state, that changes the balance // If a transaction changes 'conflicted' state, that changes the balance
// available of the outputs it spends. So force those to be recomputed // available of the outputs it spends. So force those to be recomputed
@ -1352,7 +1372,7 @@ isminetype CWallet::IsMine(const CTxIn &txin) const
{ {
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
std::map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(txin.prevout.hash); auto mi = mapWallet.find(txin.prevout.hash);
if (mi != mapWallet.end()) if (mi != mapWallet.end())
{ {
const CWalletTx& prev = (*mi).second; const CWalletTx& prev = (*mi).second;
@ -1369,7 +1389,7 @@ CAmount CWallet::GetDebit(const CTxIn &txin, const isminefilter& filter) const
{ {
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
std::map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(txin.prevout.hash); auto mi = mapWallet.find(txin.prevout.hash);
if (mi != mapWallet.end()) if (mi != mapWallet.end())
{ {
const CWalletTx& prev = (*mi).second; const CWalletTx& prev = (*mi).second;
@ -1848,7 +1868,7 @@ void CWallet::ReacceptWalletTransactions()
std::map<int64_t, CWalletTx*> mapSorted; std::map<int64_t, CWalletTx*> mapSorted;
// Sort pending wallet transactions based on their initial wallet insertion order // Sort pending wallet transactions based on their initial wallet insertion order
for (std::pair<const uint256, CWalletTx>& item : mapWallet) for (auto& item : mapWallet)
{ {
const uint256& wtxid = item.first; const uint256& wtxid = item.first;
CWalletTx& wtx = item.second; CWalletTx& wtx = item.second;
@ -2103,7 +2123,7 @@ std::vector<uint256> CWallet::ResendWalletTransactionsBefore(int64_t nTime, CCon
// Sort them in chronological order // Sort them in chronological order
std::multimap<unsigned int, CWalletTx*> mapSorted; std::multimap<unsigned int, CWalletTx*> mapSorted;
for (std::pair<const uint256, CWalletTx>& item : mapWallet) for (auto& item : mapWallet)
{ {
CWalletTx& wtx = item.second; CWalletTx& wtx = item.second;
// Don't rebroadcast if newer than nTime: // Don't rebroadcast if newer than nTime:
@ -2388,7 +2408,7 @@ void CWallet::AvailableCoins(std::vector<COutput> &vCoins, bool fOnlySafe, const
solvable = IsSolvable(*this, pcoin->tx->vout[i].scriptPubKey); // this is a slow call solvable = IsSolvable(*this, pcoin->tx->vout[i].scriptPubKey); // this is a slow call
spendable = solvable; spendable = solvable;
computedSolvable = true; computedSolvable = true;
}; }
if (computeSolvable && !computedSolvable) if (computeSolvable && !computedSolvable)
solvable = IsSolvable(*this, pcoin->tx->vout[i].scriptPubKey); solvable = IsSolvable(*this, pcoin->tx->vout[i].scriptPubKey);
@ -2560,7 +2580,7 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
bnb_used = false; bnb_used = false;
coin_selection_params.use_bnb = false; coin_selection_params.use_bnb = false;
std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(outpoint.hash); auto it = mapWallet.find(outpoint.hash);
if (it != mapWallet.end()) if (it != mapWallet.end())
{ {
const CWalletTx* pcoin = &it->second; const CWalletTx* pcoin = &it->second;
@ -2622,7 +2642,7 @@ bool CWallet::SignTransaction(CMutableTransaction &tx)
// sign the new tx // sign the new tx
int nIn = 0; int nIn = 0;
for (auto& input : tx.vin) { for (auto& input : tx.vin) {
std::map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(input.prevout.hash); auto mi = mapWallet.find(input.prevout.hash);
if(mi == mapWallet.end() || input.prevout.n >= mi->second.tx->vout.size()) { if(mi == mapWallet.end() || input.prevout.n >= mi->second.tx->vout.size()) {
return false; return false;
} }
@ -3886,7 +3906,7 @@ void CWallet::GetKeyBirthTimes(std::map<CTxDestination, int64_t> &mapKeyBirth) c
CAffectedKeysVisitor(*this, vAffected).Process(txout.scriptPubKey); CAffectedKeysVisitor(*this, vAffected).Process(txout.scriptPubKey);
for (const CKeyID &keyid : vAffected) { for (const CKeyID &keyid : vAffected) {
// ... and all their affected keys // ... and all their affected keys
std::map<CKeyID, CBlockIndex*>::iterator rit = mapKeyFirstBlock.find(keyid); auto rit = mapKeyFirstBlock.find(keyid);
if (rit != mapKeyFirstBlock.end() && nHeight < rit->second->nHeight) if (rit != mapKeyFirstBlock.end() && nHeight < rit->second->nHeight)
rit->second = pindex; rit->second = pindex;
} }
@ -4381,7 +4401,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const WalletLocation& loc
for (const CWalletTx& wtxOld : vWtx) for (const CWalletTx& wtxOld : vWtx)
{ {
uint256 hash = wtxOld.GetHash(); uint256 hash = wtxOld.GetHash();
std::map<uint256, CWalletTx>::iterator mi = walletInstance->mapWallet.find(hash); auto mi = walletInstance->mapWallet.find(hash);
if (mi != walletInstance->mapWallet.end()) if (mi != walletInstance->mapWallet.end())
{ {
const CWalletTx* copyFrom = &wtxOld; const CWalletTx* copyFrom = &wtxOld;

View file

@ -9,6 +9,7 @@
#include <amount.h> #include <amount.h>
#include <outputtype.h> #include <outputtype.h>
#include <policy/feerate.h> #include <policy/feerate.h>
#include <primitives/robin_hood.h>
#include <streams.h> #include <streams.h>
#include <tinyformat.h> #include <tinyformat.h>
#include <ui_interface.h> #include <ui_interface.h>
@ -28,7 +29,6 @@
#include <memory> #include <memory>
#include <set> #include <set>
#include <stdexcept> #include <stdexcept>
#include <stdint.h>
#include <string> #include <string>
#include <utility> #include <utility>
#include <vector> #include <vector>
@ -699,7 +699,7 @@ private:
* detect and report conflicts (double-spends or * detect and report conflicts (double-spends or
* mutated transactions where the mutant gets mined). * mutated transactions where the mutant gets mined).
*/ */
typedef std::multimap<COutPoint, uint256> TxSpends; typedef robin_hood::unordered_map<uint256, robin_hood::unordered_map<uint32_t, std::vector<uint256>>> TxSpends;
TxSpends mapTxSpends; TxSpends mapTxSpends;
void AddToSpends(const COutPoint& outpoint, const uint256& wtxid); void AddToSpends(const COutPoint& outpoint, const uint256& wtxid);
void AddToSpends(const uint256& wtxid); void AddToSpends(const uint256& wtxid);
@ -725,7 +725,7 @@ private:
/* Mark a transaction's inputs dirty, thus forcing the outputs to be recomputed */ /* Mark a transaction's inputs dirty, thus forcing the outputs to be recomputed */
void MarkInputsDirty(const CTransactionRef& tx); void MarkInputsDirty(const CTransactionRef& tx);
void SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator>); void SyncMetaData(const COutPoint& outpoint);
/* Used by TransactionAddedToMemorypool/BlockConnected/Disconnected/ScanForWalletTransactions. /* Used by TransactionAddedToMemorypool/BlockConnected/Disconnected/ScanForWalletTransactions.
* Should be called with pindexBlock and posInBlock if this is for a transaction that is included in a block. */ * Should be called with pindexBlock and posInBlock if this is for a transaction that is included in a block. */
@ -808,17 +808,18 @@ public:
void MarkPreSplitKeys(); void MarkPreSplitKeys();
// Map from Key ID to key metadata. // Map from Key ID to key metadata.
std::map<CKeyID, CKeyMetadata> mapKeyMetadata; robin_hood::unordered_map<CKeyID, CKeyMetadata> mapKeyMetadata;
// Map from Script ID to key metadata (for watch-only keys). // Map from Script ID to key metadata (for watch-only keys).
std::map<CScriptID, CKeyMetadata> m_script_metadata; robin_hood::unordered_map<CScriptID, CKeyMetadata> m_script_metadata;
typedef std::map<unsigned int, CMasterKey> MasterKeyMap; typedef std::map<unsigned int, CMasterKey> MasterKeyMap;
MasterKeyMap mapMasterKeys; MasterKeyMap mapMasterKeys;
unsigned int nMasterKeyMaxID = 0; unsigned int nMasterKeyMaxID = 0;
/** Construct wallet with specified name and database implementation. */ /** Construct wallet with specified name and database implementation. */
CWallet(const WalletLocation& location, std::unique_ptr<WalletDatabase> database) : m_location(location), database(std::move(database)) CWallet(const WalletLocation& location, std::unique_ptr<WalletDatabase> database)
: m_location(location), database(std::move(database))
{ {
} }
@ -830,7 +831,7 @@ public:
encrypted_batch = nullptr; encrypted_batch = nullptr;
} }
std::map<uint256, CWalletTx> mapWallet; robin_hood::unordered_node_map<uint256, CWalletTx> mapWallet;
std::list<CAccountingEntry> laccentries; std::list<CAccountingEntry> laccentries;
typedef std::pair<CWalletTx*, CAccountingEntry*> TxPair; typedef std::pair<CWalletTx*, CAccountingEntry*> TxPair;