From ee313525ad827e4d3819cb35989927436ee0dbce Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Sun, 24 Sep 2023 20:20:21 +0530 Subject: [PATCH] refactor: decouple db hooks from CFlatDB-based C*Manager objects, migrate to *Store structs (#5555) ## Motivation As highlighted in https://github.com/dashpay/dash-issues/issues/52, decoupling of `CFlatDB`-interacting components from managers of objects like `CGovernanceManager` and `CSporkManager` is a key task for achieving deglobalization of Dash-specific components. The design of `CFlatDB` as a flat database agent relies on hooking into the object's state its meant to load and store, using its (de)serialization routines and other miscellaneous functions (notably, without defining an interface) to achieve those ends. This approach was taken predominantly for components that want a single-file cache. Because of the method it uses to hook into the object (templates and the use of temporary objects), it explicitly prevented passing arguments into the object constructor, an explicit requirement for storing references to other components during construction. This, in turn, created an explicit dependency on those same components being available in the global context, which would block the backport of bitcoin#21866, a requirement for future backports meant to achieve parity in `assumeutxo` support. The design of these objects made no separation between persistent (i.e. cached) and ephemeral (i.e. generated/fetched during initialization or state transitions) data and the design of `CFlatDB` attempts to "clean" the database by breaching this separation and attempting to access this ephemeral data. This might be acceptable if it is contained within the manager itself, like `CSporkManager`'s `CheckAndRemove()` but is utterly unacceptable when it relies on other managers (that, as a reminder, are only accessible through the global state because of restrictions caused by existing design), like `CGovernanceManager`'s `UpdateCachesAndClean()`. This pull request aims to separate the `CFlatDB`-interacting portions of these managers into a struct, with `CFlatDB` interacting only with this struct, while the manager inherits the struct and manages load/store/update of the database through the `CFlatDB` instance initialized within its scope, though the instance only has knowledge of what is exposed through the limited parent struct. ## Additional information * As regards to existing behaviour, `CFlatDB` is written entirely as a header as it relies on templates to specialize itself for the object it hooks into. Attempting to split the logic and function definitions into separate files will require you to explicitly define template specializations, which is tedious. * `m_db` is defined as a pointer as you cannot instantiate a forward-declared template (see [this Stack Overflow answer](https://stackoverflow.com/a/12797282) for more information), which is done when defined as a member in the object scope. * The conditional cache flush predicating on RPC _not_ being in the warm-up state has been replaced with unconditional flushing of the database on object destruction (@UdjinM6, is this acceptable?) ## TODOs This is a list of things that aren't within the scope of this pull request but should be addressed in subsequent pull requests * [ ] Definition of an interface that `CFlatDB` stores are expected to implement * [ ] Lock annotations for all potential uses of members protected by the `cs` mutex in each manager object and store * [ ] Additional comments documenting what each function and member does * [ ] Deglobalization of affected managers --------- Co-authored-by: Kittywhiskers Van Gogh <63189531+kittywhiskers@users.noreply.github.com> --- src/coinjoin/client.cpp | 18 ++-- src/coinjoin/server.cpp | 16 +-- src/evo/mnauth.cpp | 2 +- src/flat-database.h | 60 ++++------- src/governance/governance.cpp | 47 ++++++--- src/governance/governance.h | 184 +++++++++++++++++---------------- src/governance/object.cpp | 2 +- src/init.cpp | 125 +++++++++------------- src/llmq/dkgsession.cpp | 2 +- src/llmq/utils.cpp | 4 +- src/masternode/meta.cpp | 31 ++++-- src/masternode/meta.h | 51 ++++++--- src/masternode/sync.cpp | 24 ++--- src/net.cpp | 12 ++- src/net_processing.cpp | 6 +- src/netfulfilledman.cpp | 27 ++++- src/netfulfilledman.h | 48 ++++++--- src/rpc/evo.cpp | 3 +- src/spork.cpp | 31 +++++- src/spork.h | 82 +++++++++------ src/test/util/setup_common.cpp | 7 ++ 21 files changed, 444 insertions(+), 338 deletions(-) diff --git a/src/coinjoin/client.cpp b/src/coinjoin/client.cpp index 5fd6f69172..ebb90ac623 100644 --- a/src/coinjoin/client.cpp +++ b/src/coinjoin/client.cpp @@ -106,18 +106,18 @@ void CCoinJoinClientQueueManager::ProcessDSQueue(const CNode& peer, PeerManager& dmn->pdmnState->addr.ToString()); return; } else { - int64_t nLastDsq = mmetaman.GetMetaInfo(dmn->proTxHash)->GetLastDsq(); - int64_t nDsqThreshold = mmetaman.GetDsqThreshold(dmn->proTxHash, mnList.GetValidMNsCount()); + int64_t nLastDsq = mmetaman->GetMetaInfo(dmn->proTxHash)->GetLastDsq(); + int64_t nDsqThreshold = mmetaman->GetDsqThreshold(dmn->proTxHash, mnList.GetValidMNsCount()); LogPrint(BCLog::COINJOIN, "DSQUEUE -- nLastDsq: %d nDsqThreshold: %d nDsqCount: %d\n", nLastDsq, - nDsqThreshold, mmetaman.GetDsqCount()); + nDsqThreshold, mmetaman->GetDsqCount()); // don't allow a few nodes to dominate the queuing process - if (nLastDsq != 0 && nDsqThreshold > mmetaman.GetDsqCount()) { + if (nLastDsq != 0 && nDsqThreshold > mmetaman->GetDsqCount()) { LogPrint(BCLog::COINJOIN, "DSQUEUE -- Masternode %s is sending too many dsq messages\n", dmn->proTxHash.ToString()); return; } - mmetaman.AllowMixing(dmn->proTxHash); + mmetaman->AllowMixing(dmn->proTxHash); LogPrint(BCLog::COINJOIN, "DSQUEUE -- new CoinJoin queue (%s) from masternode %s\n", dsq.ToString(), dmn->pdmnState->addr.ToString()); @@ -1144,13 +1144,13 @@ bool CCoinJoinClientSession::StartNewQueue(CAmount nBalanceNeedsAnonymized, CCon continue; } - int64_t nLastDsq = mmetaman.GetMetaInfo(dmn->proTxHash)->GetLastDsq(); - int64_t nDsqThreshold = mmetaman.GetDsqThreshold(dmn->proTxHash, nMnCount); - if (nLastDsq != 0 && nDsqThreshold > mmetaman.GetDsqCount()) { + int64_t nLastDsq = mmetaman->GetMetaInfo(dmn->proTxHash)->GetLastDsq(); + int64_t nDsqThreshold = mmetaman->GetDsqThreshold(dmn->proTxHash, nMnCount); + if (nLastDsq != 0 && nDsqThreshold > mmetaman->GetDsqCount()) { WalletCJLogPrint(m_wallet, "CCoinJoinClientSession::StartNewQueue -- Too early to mix on this masternode!" /* Continued */ " masternode=%s addr=%s nLastDsq=%d nDsqThreshold=%d nDsqCount=%d\n", dmn->proTxHash.ToString(), dmn->pdmnState->addr.ToString(), nLastDsq, - nDsqThreshold, mmetaman.GetDsqCount()); + nDsqThreshold, mmetaman->GetDsqCount()); nTries++; continue; } diff --git a/src/coinjoin/server.cpp b/src/coinjoin/server.cpp index 5eae338f69..9b4b615a57 100644 --- a/src/coinjoin/server.cpp +++ b/src/coinjoin/server.cpp @@ -78,9 +78,9 @@ void CCoinJoinServer::ProcessDSACCEPT(CNode& peer, CDataStream& vRecv) } } - int64_t nLastDsq = mmetaman.GetMetaInfo(dmn->proTxHash)->GetLastDsq(); - int64_t nDsqThreshold = mmetaman.GetDsqThreshold(dmn->proTxHash, mnList.GetValidMNsCount()); - if (nLastDsq != 0 && nDsqThreshold > mmetaman.GetDsqCount()) { + int64_t nLastDsq = mmetaman->GetMetaInfo(dmn->proTxHash)->GetLastDsq(); + int64_t nDsqThreshold = mmetaman->GetDsqThreshold(dmn->proTxHash, mnList.GetValidMNsCount()); + if (nLastDsq != 0 && nDsqThreshold > mmetaman->GetDsqCount()) { if (fLogIPs) { LogPrint(BCLog::COINJOIN, "DSACCEPT -- last dsq too recent, must wait: peer=%d, addr=%s\n", peer.GetId(), peer.addr.ToString()); } else { @@ -161,15 +161,15 @@ void CCoinJoinServer::ProcessDSQUEUE(const CNode& peer, PeerManager& peerman, CD } if (!dsq.fReady) { - int64_t nLastDsq = mmetaman.GetMetaInfo(dmn->proTxHash)->GetLastDsq(); - int64_t nDsqThreshold = mmetaman.GetDsqThreshold(dmn->proTxHash, mnList.GetValidMNsCount()); - LogPrint(BCLog::COINJOIN, "DSQUEUE -- nLastDsq: %d nDsqThreshold: %d nDsqCount: %d\n", nLastDsq, nDsqThreshold, mmetaman.GetDsqCount()); + int64_t nLastDsq = mmetaman->GetMetaInfo(dmn->proTxHash)->GetLastDsq(); + int64_t nDsqThreshold = mmetaman->GetDsqThreshold(dmn->proTxHash, mnList.GetValidMNsCount()); + LogPrint(BCLog::COINJOIN, "DSQUEUE -- nLastDsq: %d nDsqThreshold: %d nDsqCount: %d\n", nLastDsq, nDsqThreshold, mmetaman->GetDsqCount()); //don't allow a few nodes to dominate the queuing process - if (nLastDsq != 0 && nDsqThreshold > mmetaman.GetDsqCount()) { + if (nLastDsq != 0 && nDsqThreshold > mmetaman->GetDsqCount()) { LogPrint(BCLog::COINJOIN, "DSQUEUE -- Masternode %s is sending too many dsq messages\n", dmn->pdmnState->addr.ToString()); return; } - mmetaman.AllowMixing(dmn->proTxHash); + mmetaman->AllowMixing(dmn->proTxHash); LogPrint(BCLog::COINJOIN, "DSQUEUE -- new CoinJoin queue (%s) from masternode %s\n", dsq.ToString(), dmn->pdmnState->addr.ToString()); diff --git a/src/evo/mnauth.cpp b/src/evo/mnauth.cpp index 107eecd005..569f1b2c77 100644 --- a/src/evo/mnauth.cpp +++ b/src/evo/mnauth.cpp @@ -123,7 +123,7 @@ void CMNAuth::ProcessMessage(CNode& peer, PeerManager& peerman, CConnman& connma } if (!peer.fInbound) { - mmetaman.GetMetaInfo(mnauth.proRegTxHash)->SetLastOutboundSuccess(GetAdjustedTime()); + mmetaman->GetMetaInfo(mnauth.proRegTxHash)->SetLastOutboundSuccess(GetAdjustedTime()); if (peer.m_masternode_probe_connection) { LogPrint(BCLog::NET_NETCONN, "CMNAuth::ProcessMessage -- Masternode probe successful for %s, disconnecting. peer=%d\n", mnauth.proRegTxHash.ToString(), peer.GetId()); diff --git a/src/flat-database.h b/src/flat-database.h index 3235569c0c..4057076772 100644 --- a/src/flat-database.h +++ b/src/flat-database.h @@ -6,6 +6,7 @@ #define BITCOIN_FLAT_DATABASE_H #include +#include #include #include #include @@ -35,7 +36,7 @@ private: std::string strFilename; std::string strMagicMessage; - bool Write(const T& objToSave) + bool CoreWrite(const T& objToSave) { // LOCK(objToSave.cs); @@ -70,7 +71,7 @@ private: return true; } - ReadResult Read(T& objToLoad, bool fDryRun = false) + ReadResult CoreRead(T& objToLoad) { //LOCK(objToLoad.cs); @@ -151,28 +152,13 @@ private: LogPrintf("Loaded info from %s %dms\n", strFilename, GetTimeMillis() - nStart); LogPrintf(" %s\n", objToLoad.ToString()); - if(!fDryRun) { - LogPrintf("%s: Cleaning....\n", __func__); - objToLoad.CheckAndRemove(); - LogPrintf(" %s\n", objToLoad.ToString()); - } return Ok; } - -public: - CFlatDB(std::string strFilenameIn, std::string strMagicMessageIn) + bool Read(T& objToLoad) { - pathDB = GetDataDir() / strFilenameIn; - strFilename = strFilenameIn; - strMagicMessage = strMagicMessageIn; - } - - bool Load(T& objToLoad) - { - LogPrintf("Reading info from %s...\n", strFilename); - ReadResult readResult = Read(objToLoad); + ReadResult readResult = CoreRead(objToLoad); if (readResult == FileError) LogPrintf("Missing file %s, will try to recreate\n", strFilename); else if (readResult != Ok) @@ -191,36 +177,34 @@ public: return true; } - bool Dump(T& objToSave) +public: + CFlatDB(std::string strFilenameIn, std::string strMagicMessageIn) { - int64_t nStart = GetTimeMillis(); + pathDB = GetDataDir() / strFilenameIn; + strFilename = strFilenameIn; + strMagicMessage = strMagicMessageIn; + } + bool Load(T& objToLoad) + { + LogPrintf("Reading info from %s...\n", strFilename); + return Read(objToLoad); + } + + bool Store(T& objToSave) + { LogPrintf("Verifying %s format...\n", strFilename); T tmpObjToLoad; - ReadResult readResult = Read(tmpObjToLoad, true); + if (!Read(tmpObjToLoad)) return false; - // there was an error and it was not an error on file opening => do not proceed - if (readResult == FileError) - LogPrintf("Missing file %s, will try to recreate\n", strFilename); - else if (readResult != Ok) - { - LogPrintf("Error reading %s: ", strFilename); - if(readResult == IncorrectFormat) - LogPrintf("%s: Magic is ok but data has invalid format, will try to recreate\n", __func__); - else - { - LogPrintf("%s: File format is unknown or invalid, please fix it manually\n", __func__); - return false; - } - } + int64_t nStart = GetTimeMillis(); LogPrintf("Writing info to %s...\n", strFilename); - Write(objToSave); + CoreWrite(objToSave); LogPrintf("%s dump finished %dms\n", strFilename, GetTimeMillis() - nStart); return true; } - }; diff --git a/src/governance/governance.cpp b/src/governance/governance.cpp index db7baad53a..ddf5515ed7 100644 --- a/src/governance/governance.cpp +++ b/src/governance/governance.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -26,27 +27,49 @@ std::unique_ptr governance; int nSubmittedFinalBudget; -const std::string CGovernanceManager::SERIALIZATION_VERSION_STRING = "CGovernanceManager-Version-16"; +const std::string GovernanceStore::SERIALIZATION_VERSION_STRING = "CGovernanceManager-Version-16"; const int CGovernanceManager::MAX_TIME_FUTURE_DEVIATION = 60 * 60; const int CGovernanceManager::RELIABLE_PROPAGATION_TIME = 60; -CGovernanceManager::CGovernanceManager() : - nTimeLastDiff(0), - nCachedBlockHeight(0), +GovernanceStore::GovernanceStore() : + cs(), mapObjects(), mapErasedGovernanceObjects(), cmapVoteToObject(MAX_CACHE_SIZE), cmapInvalidVotes(MAX_CACHE_SIZE), cmmapOrphanVotes(MAX_CACHE_SIZE), mapLastMasternodeObject(), + lastMNListForVotingKeys(std::make_shared()) +{ +} + +CGovernanceManager::CGovernanceManager() : + m_db{std::make_unique("governance.dat", "magicGovernanceCache")}, + nTimeLastDiff(0), + nCachedBlockHeight(0), setRequestedObjects(), fRateChecksEnabled(true), - lastMNListForVotingKeys(std::make_shared()), - votedFundingYesTriggerHash(std::nullopt), - cs() + votedFundingYesTriggerHash(std::nullopt) { } +CGovernanceManager::~CGovernanceManager() +{ + if (!is_valid) return; + m_db->Store(*this); +} + +bool CGovernanceManager::LoadCache(bool load_cache) +{ + assert(m_db != nullptr); + is_valid = load_cache ? m_db->Load(*this) : m_db->Store(*this); + if (is_valid && load_cache) { + CheckAndRemove(); + InitOnLoad(); + } + return is_valid; +} + // Accessors for thread-safe access to maps bool CGovernanceManager::HaveObjectForHash(const uint256& nHash) const { @@ -318,7 +341,7 @@ void CGovernanceManager::UpdateCachesAndClean() LogPrint(BCLog::GOBJECT, "CGovernanceManager::UpdateCachesAndClean\n"); - std::vector vecDirtyHashes = mmetaman.GetAndClearDirtyGovernanceObjectHashes(); + std::vector vecDirtyHashes = mmetaman->GetAndClearDirtyGovernanceObjectHashes(); LOCK2(cs_main, cs); @@ -368,7 +391,7 @@ void CGovernanceManager::UpdateCachesAndClean() if ((pObj->IsSetCachedDelete() || pObj->IsSetExpired()) && (nTimeSinceDeletion >= GOVERNANCE_DELETION_DELAY)) { LogPrint(BCLog::GOBJECT, "CGovernanceManager::UpdateCachesAndClean -- erase obj %s\n", (*it).first.ToString()); - mmetaman.RemoveGovernanceObject(pObj->GetHash()); + mmetaman->RemoveGovernanceObject(pObj->GetHash()); // Remove vote references const object_ref_cm_t::list_t& listItems = cmapVoteToObject.GetItemList(); @@ -850,13 +873,13 @@ void CGovernanceManager::SyncObjects(CNode& peer, PeerManager& peerman, CConnman // do not provide any data until our node is synced if (!::masternodeSync->IsSynced()) return; - if (netfulfilledman.HasFulfilledRequest(peer.addr, NetMsgType::MNGOVERNANCESYNC)) { + if (netfulfilledman->HasFulfilledRequest(peer.addr, NetMsgType::MNGOVERNANCESYNC)) { // Asking for the whole list multiple times in a short period of time is no good LogPrint(BCLog::GOBJECT, "CGovernanceManager::%s -- peer already asked me for the list\n", __func__); peerman.Misbehaving(peer.GetId(), 20); return; } - netfulfilledman.AddFulfilledRequest(peer.addr, NetMsgType::MNGOVERNANCESYNC); + netfulfilledman->AddFulfilledRequest(peer.addr, NetMsgType::MNGOVERNANCESYNC); int nObjCount = 0; @@ -1323,7 +1346,7 @@ void CGovernanceManager::InitOnLoad() LogPrintf(" %s\n", ToString()); } -std::string CGovernanceManager::ToString() const +std::string GovernanceStore::ToString() const { LOCK(cs); diff --git a/src/governance/governance.h b/src/governance/governance.h index d13824bdd2..fa569caaa5 100644 --- a/src/governance/governance.h +++ b/src/governance/governance.h @@ -13,6 +13,8 @@ class CBloomFilter; class CBlockIndex; +template +class CFlatDB; class CInv; class CGovernanceManager; @@ -124,14 +126,9 @@ public: } }; -// -// Governance Manager : Contains all proposals for the budget -// -class CGovernanceManager +class GovernanceStore { - friend class CGovernanceObject; - -public: // Types +protected: struct last_object_rec { explicit last_object_rec(bool fStatusOKIn = true) : triggerBuffer(), @@ -148,57 +145,95 @@ public: // Types bool fStatusOK; }; - using object_ref_cm_t = CacheMap; - + using txout_m_t = std::map; using vote_cmm_t = CacheMultiMap; - using txout_m_t = std::map; - - using hash_s_t = std::set; - -private: +protected: static constexpr int MAX_CACHE_SIZE = 1000000; - static const std::string SERIALIZATION_VERSION_STRING; - static const int MAX_TIME_FUTURE_DEVIATION; - static const int RELIABLE_PROPAGATION_TIME; - - int64_t nTimeLastDiff; - - // keep track of current block height - int nCachedBlockHeight; +public: + // critical section to protect the inner data structures + mutable RecursiveMutex cs; +protected: // keep track of the scanning errors std::map mapObjects GUARDED_BY(cs); - // mapErasedGovernanceObjects contains key-value pairs, where // key - governance object's hash // value - expiration time for deleted objects std::map mapErasedGovernanceObjects; - - std::map mapPostponedObjects; - hash_s_t setAdditionalRelayObjects; - object_ref_cm_t cmapVoteToObject; - CacheMap cmapInvalidVotes; - vote_cmm_t cmmapOrphanVotes; - txout_m_t mapLastMasternodeObject; - - hash_s_t setRequestedObjects; - - hash_s_t setRequestedVotes; - - bool fRateChecksEnabled; - // used to check for changed voting keys CDeterministicMNListPtr lastMNListForVotingKeys; - std::optional votedFundingYesTriggerHash; +public: + GovernanceStore(); + ~GovernanceStore() = default; + + template + void Serialize(Stream &s) const + { + LOCK(cs); + s << SERIALIZATION_VERSION_STRING + << mapErasedGovernanceObjects + << cmapInvalidVotes + << cmmapOrphanVotes + << mapObjects + << mapLastMasternodeObject + << *lastMNListForVotingKeys; + } + + template + void Unserialize(Stream &s) + { + Clear(); + + LOCK(cs); + std::string strVersion; + s >> strVersion; + if (strVersion != SERIALIZATION_VERSION_STRING) { + return; + } + + s >> mapErasedGovernanceObjects + >> cmapInvalidVotes + >> cmmapOrphanVotes + >> mapObjects + >> mapLastMasternodeObject + >> *lastMNListForVotingKeys; + } + + void Clear() + { + LOCK(cs); + + LogPrint(BCLog::GOBJECT, "Governance object manager was cleared\n"); + mapObjects.clear(); + mapErasedGovernanceObjects.clear(); + cmapVoteToObject.Clear(); + cmapInvalidVotes.Clear(); + cmmapOrphanVotes.Clear(); + mapLastMasternodeObject.clear(); + } + + std::string ToString() const; +}; + +// +// Governance Manager : Contains all proposals for the budget +// +class CGovernanceManager : public GovernanceStore +{ + friend class CGovernanceObject; + +private: + using hash_s_t = std::set; + using db_type = CFlatDB; class ScopedLockBool { @@ -220,13 +255,31 @@ private: } }; +private: + static const int MAX_TIME_FUTURE_DEVIATION; + static const int RELIABLE_PROPAGATION_TIME; + +private: + const std::unique_ptr m_db; + bool is_valid{false}; + + int64_t nTimeLastDiff; + // keep track of current block height + int nCachedBlockHeight; + std::map mapPostponedObjects; + hash_s_t setAdditionalRelayObjects; + hash_s_t setRequestedObjects; + hash_s_t setRequestedVotes; + bool fRateChecksEnabled; + std::optional votedFundingYesTriggerHash; + public: - // critical section to protect the inner data structures - mutable RecursiveMutex cs; - CGovernanceManager(); + ~CGovernanceManager(); - virtual ~CGovernanceManager() = default; + bool LoadCache(bool load_cache); + + bool IsValid() const { return is_valid; } /** * This is called by AlreadyHave in net_processing.cpp as part of the inventory @@ -263,55 +316,8 @@ public: void CheckAndRemove() { UpdateCachesAndClean(); } - void Clear() - { - LOCK(cs); - - LogPrint(BCLog::GOBJECT, "Governance object manager was cleared\n"); - mapObjects.clear(); - mapErasedGovernanceObjects.clear(); - cmapVoteToObject.Clear(); - cmapInvalidVotes.Clear(); - cmmapOrphanVotes.Clear(); - mapLastMasternodeObject.clear(); - } - - std::string ToString() const; UniValue ToJson() const; - template - void Serialize(Stream &s) const - { - LOCK(cs); - s << SERIALIZATION_VERSION_STRING - << mapErasedGovernanceObjects - << cmapInvalidVotes - << cmmapOrphanVotes - << mapObjects - << mapLastMasternodeObject - << *lastMNListForVotingKeys; - } - - template - void Unserialize(Stream &s) - { - LOCK(cs); - Clear(); - - std::string strVersion; - s >> strVersion; - if (strVersion != SERIALIZATION_VERSION_STRING) { - return; - } - - s >> mapErasedGovernanceObjects - >> cmapInvalidVotes - >> cmmapOrphanVotes - >> mapObjects - >> mapLastMasternodeObject - >> *lastMNListForVotingKeys; - } - void UpdatedBlockTip(const CBlockIndex* pindex, CConnman& connman); int64_t GetLastDiffTime() const { return nTimeLastDiff; } void UpdateLastDiffTime(int64_t nTimeIn) { nTimeLastDiff = nTimeIn; } diff --git a/src/governance/object.cpp b/src/governance/object.cpp index 87d846cf60..01dfb8b00c 100644 --- a/src/governance/object.cpp +++ b/src/governance/object.cpp @@ -199,7 +199,7 @@ bool CGovernanceObject::ProcessVote(const CGovernanceVote& vote, CGovernanceExce return false; } - if (!mmetaman.AddGovernanceVote(dmn->proTxHash, vote.GetParentHash())) { + if (!mmetaman->AddGovernanceVote(dmn->proTxHash, vote.GetParentHash())) { std::ostringstream ostr; ostr << "CGovernanceObject::ProcessVote -- Unable to add governance vote" << ", MN outpoint = " << vote.GetMasternodeOutpoint().ToStringShort() diff --git a/src/init.cpp b/src/init.cpp index c5cfa05960..9d206ab879 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -237,11 +237,6 @@ void PrepareShutdown(NodeContext& node) StopHTTPServer(); if (node.llmq_ctx) node.llmq_ctx->Stop(); - // fRPCInWarmup should be `false` if we completed the loading sequence - // before a shutdown request was received - std::string statusmessage; - bool fRPCInWarmup = RPCIsInWarmup(&statusmessage); - for (const auto& client : node.chain_clients) { client->flush(); } @@ -277,20 +272,6 @@ void PrepareShutdown(NodeContext& node) // CValidationInterface callbacks, flush them... GetMainSignals().FlushBackgroundCallbacks(); - if (!fRPCInWarmup) { - // STORE DATA CACHES INTO SERIALIZED DAT FILES - CFlatDB flatdb1("mncache.dat", "magicMasternodeCache"); - flatdb1.Dump(mmetaman); - CFlatDB flatdb4("netfulfilled.dat", "magicFulfilledCache"); - flatdb4.Dump(netfulfilledman); - CFlatDB flatdb6("sporks.dat", "magicSporkCache"); - flatdb6.Dump(*::sporkManager); - if (!fDisableGovernance) { - CFlatDB flatdb3("governance.dat", "magicGovernanceCache"); - flatdb3.Dump(*::governance); - } - } - // After the threads that potentially access these pointers have been stopped, // destruct and reset all to nullptr. node.peerman.reset(); @@ -324,6 +305,8 @@ void PrepareShutdown(NodeContext& node) ::governance.reset(); ::sporkManager.reset(); ::masternodeSync.reset(); + ::netfulfilledman.reset(); + ::mmetaman.reset(); // Stop and delete all indexes only after flushing background callbacks. if (g_txindex) { @@ -1696,6 +1679,7 @@ bool AppInitMain(const CoreContext& context, NodeContext& node, interfaces::Bloc node.chainman = &g_chainman; ChainstateManager& chainman = *Assert(node.chainman); + assert(!::governance); ::governance = std::make_unique(); assert(!node.peerman); @@ -1706,7 +1690,6 @@ bool AppInitMain(const CoreContext& context, NodeContext& node, interfaces::Bloc assert(!::sporkManager); ::sporkManager = std::make_unique(); - ::masternodeSync = std::make_unique(*node.connman, *::governance); std::vector vSporkAddresses; if (args.IsArgSet("-sporkaddr")) { @@ -1732,6 +1715,8 @@ bool AppInitMain(const CoreContext& context, NodeContext& node, interfaces::Bloc } } + ::masternodeSync = std::make_unique(*node.connman, *::governance); + // sanitize comments per BIP-0014, format user agent and check total size std::vector uacomments; @@ -1852,8 +1837,8 @@ bool AppInitMain(const CoreContext& context, NodeContext& node, interfaces::Bloc } #endif - assert(masternodeSync != nullptr); - assert(governance != nullptr); + assert(::governance != nullptr); + assert(::masternodeSync != nullptr); pdsNotificationInterface = new CDSNotificationInterface( *node.connman, *::masternodeSync, ::deterministicMNManager, *::governance, node.llmq_ctx, node.cj_ctx ); @@ -1867,10 +1852,9 @@ bool AppInitMain(const CoreContext& context, NodeContext& node, interfaces::Bloc // ********************************************************* Step 7a: Load sporks - uiInterface.InitMessage(_("Loading sporks cache...").translated); - CFlatDB flatdb6("sporks.dat", "magicSporkCache"); - if (!flatdb6.Load(*::sporkManager)) { - return InitError(strprintf(_("Failed to load sporks cache from %s"), (GetDataDir() / "sporks.dat").string())); + if (!::sporkManager->LoadCache()) { + auto file_path = (GetDataDir() / "sporks.dat").string(); + return InitError(strprintf(_("Failed to load sporks cache from %s"), file_path)); } // ********************************************************* Step 7b: load block chain @@ -2202,6 +2186,40 @@ bool AppInitMain(const CoreContext& context, NodeContext& node, interfaces::Bloc g_wallet_init_interface.InitCoinJoinSettings(*node.cj_ctx->clientman); #endif // ENABLE_WALLET + // ********************************************************* Step 7d: Setup other Dash services + + bool fLoadCacheFiles = !(fReindex || fReindexChainState) && (::ChainActive().Tip() != nullptr); + + if (!fDisableGovernance) { + if (!::governance->LoadCache(fLoadCacheFiles)) { + auto file_path = (GetDataDir() / "governance.dat").string(); + if (fLoadCacheFiles && !fDisableGovernance) { + return InitError(strprintf(_("Failed to load governance cache from %s"), file_path)); + } + return InitError(strprintf(_("Failed to clear governance cache at %s"), file_path)); + } + } + + assert(!::mmetaman); + ::mmetaman = std::make_unique(fLoadCacheFiles); + if (!::mmetaman->IsValid()) { + auto file_path = (GetDataDir() / "mncache.dat").string(); + if (fLoadCacheFiles) { + return InitError(strprintf(_("Failed to load masternode cache from %s"), file_path)); + } + return InitError(strprintf(_("Failed to clear masternode cache at %s"), file_path)); + } + + assert(!::netfulfilledman); + ::netfulfilledman = std::make_unique(fLoadCacheFiles); + if (!::netfulfilledman->IsValid()) { + auto file_path = (GetDataDir() / "netfulfilled.dat").string(); + if (fLoadCacheFiles) { + return InitError(strprintf(_("Failed to load fulfilled requests cache from %s"), file_path)); + } + return InitError(strprintf(_("Failed to clear fulfilled requests cache at %s"), file_path)); + } + // ********************************************************* Step 8: start indexers if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) { g_txindex = std::make_unique(nTxIndexCache, false, fReindex); @@ -2263,60 +2281,9 @@ bool AppInitMain(const CoreContext& context, NodeContext& node, interfaces::Bloc return false; } - // ********************************************************* Step 10a: Load cache data + // ********************************************************* Step 10a: schedule Dash-specific tasks - // LOAD SERIALIZED DAT FILES INTO DATA CACHES FOR INTERNAL USE - - bool fLoadCacheFiles = !(fReindex || fReindexChainState) && (::ChainActive().Tip() != nullptr); - fs::path pathDB = GetDataDir(); - std::string strDBName; - - strDBName = "mncache.dat"; - uiInterface.InitMessage(_("Loading masternode cache...").translated); - CFlatDB flatdb1(strDBName, "magicMasternodeCache"); - if (fLoadCacheFiles) { - if(!flatdb1.Load(mmetaman)) { - return InitError(strprintf(_("Failed to load masternode cache from %s"), (pathDB / strDBName).string())); - } - } else { - CMasternodeMetaMan mmetamanTmp; - if(!flatdb1.Dump(mmetamanTmp)) { - return InitError(strprintf(_("Failed to clear masternode cache at %s"), (pathDB / strDBName).string())); - } - } - - strDBName = "governance.dat"; - uiInterface.InitMessage(_("Loading governance cache...").translated); - CFlatDB flatdb3(strDBName, "magicGovernanceCache"); - if (fLoadCacheFiles && !fDisableGovernance) { - if(!flatdb3.Load(*::governance)) { - return InitError(strprintf(_("Failed to load governance cache from %s"), (pathDB / strDBName).string())); - } - ::governance->InitOnLoad(); - } else { - CGovernanceManager governanceTmp; - if(!flatdb3.Dump(governanceTmp)) { - return InitError(strprintf(_("Failed to clear governance cache at %s"), (pathDB / strDBName).string())); - } - } - - strDBName = "netfulfilled.dat"; - uiInterface.InitMessage(_("Loading fulfilled requests cache...").translated); - CFlatDB flatdb4(strDBName, "magicFulfilledCache"); - if (fLoadCacheFiles) { - if(!flatdb4.Load(netfulfilledman)) { - return InitError(strprintf(_("Failed to load fulfilled requests cache from %s"),(pathDB / strDBName).string())); - } - } else { - CNetFulfilledRequestManager netfulfilledmanTmp; - if(!flatdb4.Dump(netfulfilledmanTmp)) { - return InitError(strprintf(_("Failed to clear fulfilled requests cache at %s"),(pathDB / strDBName).string())); - } - } - - // ********************************************************* Step 10b: schedule Dash-specific tasks - - node.scheduler->scheduleEvery(std::bind(&CNetFulfilledRequestManager::DoMaintenance, std::ref(netfulfilledman)), std::chrono::minutes{1}); + node.scheduler->scheduleEvery(std::bind(&CNetFulfilledRequestManager::DoMaintenance, std::ref(*netfulfilledman)), std::chrono::minutes{1}); node.scheduler->scheduleEvery(std::bind(&CMasternodeSync::DoMaintenance, std::ref(*::masternodeSync)), std::chrono::seconds{1}); node.scheduler->scheduleEvery(std::bind(&CMasternodeUtils::DoMaintenance, std::ref(*node.connman), std::ref(*::masternodeSync), std::ref(*node.cj_ctx)), std::chrono::minutes{1}); node.scheduler->scheduleEvery(std::bind(&CDeterministicMNManager::DoMaintenance, std::ref(*deterministicMNManager)), std::chrono::seconds{10}); diff --git a/src/llmq/dkgsession.cpp b/src/llmq/dkgsession.cpp index e79e6596ec..9586b7df5d 100644 --- a/src/llmq/dkgsession.cpp +++ b/src/llmq/dkgsession.cpp @@ -463,7 +463,7 @@ void CDKGSession::VerifyConnectionAndMinProtoVersions() const logger.Batch("%s does not have min proto version %d (has %d)", m->dmn->proTxHash.ToString(), MIN_MASTERNODE_PROTO_VERSION, it->second); } - if (mmetaman.GetMetaInfo(m->dmn->proTxHash)->OutboundFailedTooManyTimes()) { + if (mmetaman->GetMetaInfo(m->dmn->proTxHash)->OutboundFailedTooManyTimes()) { m->badConnection = true; logger.Batch("%s failed to connect to it too many times", m->dmn->proTxHash.ToString()); } diff --git a/src/llmq/utils.cpp b/src/llmq/utils.cpp index b21b4ed031..d35b70b9df 100644 --- a/src/llmq/utils.cpp +++ b/src/llmq/utils.cpp @@ -924,7 +924,7 @@ bool EnsureQuorumConnections(const Consensus::LLMQParams& llmqParams, const CBlo } void AddQuorumProbeConnections(const Consensus::LLMQParams& llmqParams, const CBlockIndex *pQuorumBaseBlockIndex, - CConnman& connman, const uint256 &myProTxHash) + CConnman& connman, const uint256 &myProTxHash) { if (!IsQuorumPoseEnabled(llmqParams.type)) { return; @@ -938,7 +938,7 @@ void AddQuorumProbeConnections(const Consensus::LLMQParams& llmqParams, const CB if (dmn->proTxHash == myProTxHash) { continue; } - auto lastOutbound = mmetaman.GetMetaInfo(dmn->proTxHash)->GetLastOutboundSuccess(); + auto lastOutbound = mmetaman->GetMetaInfo(dmn->proTxHash)->GetLastOutboundSuccess(); if (curTime - lastOutbound < 10 * 60) { // avoid re-probing nodes too often continue; diff --git a/src/masternode/meta.cpp b/src/masternode/meta.cpp index 245109d127..178980c6e8 100644 --- a/src/masternode/meta.cpp +++ b/src/masternode/meta.cpp @@ -4,13 +4,31 @@ #include +#include #include #include -CMasternodeMetaMan mmetaman; +std::unique_ptr mmetaman; -const std::string CMasternodeMetaMan::SERIALIZATION_VERSION_STRING = "CMasternodeMetaMan-Version-3"; +const std::string MasternodeMetaStore::SERIALIZATION_VERSION_STRING = "CMasternodeMetaMan-Version-3"; + +CMasternodeMetaMan::CMasternodeMetaMan(bool load_cache) : + m_db{std::make_unique("mncache.dat", "magicMasternodeCache")}, + is_valid{ + [&]() -> bool { + assert(m_db != nullptr); + return load_cache ? m_db->Load(*this) : m_db->Store(*this); + }() + } +{ +} + +CMasternodeMetaMan::~CMasternodeMetaMan() +{ + if (!is_valid) return; + m_db->Store(*this); +} UniValue CMasternodeMetaInfo::ToJson() const { @@ -110,14 +128,7 @@ std::vector CMasternodeMetaMan::GetAndClearDirtyGovernanceObjectHashes( return vecTmp; } -void CMasternodeMetaMan::Clear() -{ - LOCK(cs); - metaInfos.clear(); - vecDirtyGovernanceObjectHashes.clear(); -} - -std::string CMasternodeMetaMan::ToString() const +std::string MasternodeMetaStore::ToString() const { std::ostringstream info; LOCK(cs); diff --git a/src/masternode/meta.h b/src/masternode/meta.h index 428bb67925..c723113b63 100644 --- a/src/masternode/meta.h +++ b/src/masternode/meta.h @@ -6,14 +6,17 @@ #define BITCOIN_MASTERNODE_META_H #include +#include +#include #include #include -#include -#include +#include class CConnman; +template +class CFlatDB; static constexpr int MASTERNODE_MAX_MIXING_TXES{5}; static constexpr int MASTERNODE_MAX_FAILED_OUTBOUND_ATTEMPTS{5}; @@ -89,16 +92,13 @@ public: }; using CMasternodeMetaInfoPtr = std::shared_ptr; -class CMasternodeMetaMan +class MasternodeMetaStore { -private: +protected: static const std::string SERIALIZATION_VERSION_STRING; mutable RecursiveMutex cs; - std::map metaInfos GUARDED_BY(cs); - std::vector vecDirtyGovernanceObjectHashes GUARDED_BY(cs); - // keep track of dsq count to prevent masternodes from gaming coinjoin queue std::atomic nDsqCount{0}; @@ -117,8 +117,9 @@ public: template void Unserialize(Stream &s) { - LOCK(cs); Clear(); + + LOCK(cs); std::string strVersion; s >> strVersion; if (strVersion != SERIALIZATION_VERSION_STRING) { @@ -132,7 +133,33 @@ public: } } + void Clear() + { + LOCK(cs); + + metaInfos.clear(); + } + + std::string ToString() const; +}; + +class CMasternodeMetaMan : public MasternodeMetaStore +{ +private: + using db_type = CFlatDB; + +private: + const std::unique_ptr m_db; + const bool is_valid{false}; + + std::vector vecDirtyGovernanceObjectHashes GUARDED_BY(cs); + public: + explicit CMasternodeMetaMan(bool load_cache); + ~CMasternodeMetaMan(); + + bool IsValid() const { return is_valid; } + CMasternodeMetaInfoPtr GetMetaInfo(const uint256& proTxHash, bool fCreate = true); int64_t GetDsqCount() const { return nDsqCount; } @@ -145,14 +172,8 @@ public: void RemoveGovernanceObject(const uint256& nGovernanceObjectHash); std::vector GetAndClearDirtyGovernanceObjectHashes(); - - void Clear(); - // Needed to avoid errors in flat-database.h - void CheckAndRemove() const {}; - - std::string ToString() const; }; -extern CMasternodeMetaMan mmetaman; +extern std::unique_ptr mmetaman; #endif // BITCOIN_MASTERNODE_META_H diff --git a/src/masternode/sync.cpp b/src/masternode/sync.cpp index 81ed9e2009..e99dd6aba4 100644 --- a/src/masternode/sync.cpp +++ b/src/masternode/sync.cpp @@ -76,7 +76,7 @@ void CMasternodeSync::SwitchToNextAsset() uiInterface.NotifyAdditionalDataSyncProgressChanged(1); connman.ForEachNode(CConnman::AllNodes, [](CNode* pnode) { - netfulfilledman.AddFulfilledRequest(pnode->addr, "full-sync"); + netfulfilledman->AddFulfilledRequest(pnode->addr, "full-sync"); }); LogPrintf("CMasternodeSync::SwitchToNextAsset -- Sync has finished\n"); @@ -159,13 +159,13 @@ void CMasternodeSync::ProcessTick() if (!pnode->CanRelay() || (fMasternodeMode && pnode->fInbound)) continue; { - if ((pnode->HasPermission(PF_NOBAN) || pnode->m_manual_connection) && !netfulfilledman.HasFulfilledRequest(pnode->addr, strAllow)) { - netfulfilledman.RemoveAllFulfilledRequests(pnode->addr); - netfulfilledman.AddFulfilledRequest(pnode->addr, strAllow); + if ((pnode->HasPermission(PF_NOBAN) || pnode->m_manual_connection) && !netfulfilledman->HasFulfilledRequest(pnode->addr, strAllow)) { + netfulfilledman->RemoveAllFulfilledRequests(pnode->addr); + netfulfilledman->AddFulfilledRequest(pnode->addr, strAllow); LogPrintf("CMasternodeSync::ProcessTick -- skipping mnsync restrictions for peer=%d\n", pnode->GetId()); } - if(netfulfilledman.HasFulfilledRequest(pnode->addr, "full-sync")) { + if(netfulfilledman->HasFulfilledRequest(pnode->addr, "full-sync")) { // We already fully synced from this node recently, // disconnect to free this connection slot for another peer. pnode->fDisconnect = true; @@ -175,9 +175,9 @@ void CMasternodeSync::ProcessTick() // SPORK : ALWAYS ASK FOR SPORKS AS WE SYNC - if(!netfulfilledman.HasFulfilledRequest(pnode->addr, "spork-sync")) { + if(!netfulfilledman->HasFulfilledRequest(pnode->addr, "spork-sync")) { // always get sporks first, only request once from each peer - netfulfilledman.AddFulfilledRequest(pnode->addr, "spork-sync"); + netfulfilledman->AddFulfilledRequest(pnode->addr, "spork-sync"); // get current network sporks connman.PushMessage(pnode, msgMaker.Make(NetMsgType::GETSPORKS)); LogPrint(BCLog::MNSYNC, "CMasternodeSync::ProcessTick -- nTick %d nCurrentAsset %d -- requesting sporks from peer=%d\n", nTick, nCurrentAsset, pnode->GetId()); @@ -201,9 +201,9 @@ void CMasternodeSync::ProcessTick() if (gArgs.GetBoolArg("-syncmempool", DEFAULT_SYNC_MEMPOOL)) { // Now that the blockchain is synced request the mempool from the connected outbound nodes if possible for (auto pNodeTmp : vNodesCopy) { - bool fRequestedEarlier = netfulfilledman.HasFulfilledRequest(pNodeTmp->addr, "mempool-sync"); + bool fRequestedEarlier = netfulfilledman->HasFulfilledRequest(pNodeTmp->addr, "mempool-sync"); if (pNodeTmp->nVersion >= 70216 && !pNodeTmp->fInbound && !fRequestedEarlier && !pNodeTmp->IsBlockRelayOnly()) { - netfulfilledman.AddFulfilledRequest(pNodeTmp->addr, "mempool-sync"); + netfulfilledman->AddFulfilledRequest(pNodeTmp->addr, "mempool-sync"); connman.PushMessage(pNodeTmp, msgMaker.Make(NetMsgType::MEMPOOL)); LogPrint(BCLog::MNSYNC, "CMasternodeSync::ProcessTick -- nTick %d nCurrentAsset %d -- syncing mempool from peer=%d\n", nTick, nCurrentAsset, pNodeTmp->GetId()); } @@ -235,12 +235,12 @@ void CMasternodeSync::ProcessTick() } // only request obj sync once from each peer - if(netfulfilledman.HasFulfilledRequest(pnode->addr, "governance-sync")) { + if(netfulfilledman->HasFulfilledRequest(pnode->addr, "governance-sync")) { // will request votes on per-obj basis from each node in a separate loop below // to avoid deadlocks here continue; } - netfulfilledman.AddFulfilledRequest(pnode->addr, "governance-sync"); + netfulfilledman->AddFulfilledRequest(pnode->addr, "governance-sync"); nTriedPeerCount++; @@ -260,7 +260,7 @@ void CMasternodeSync::ProcessTick() // request votes on per-obj basis from each node for (auto& pnode : vNodesCopy) { - if(!netfulfilledman.HasFulfilledRequest(pnode->addr, "governance-sync")) { + if(!netfulfilledman->HasFulfilledRequest(pnode->addr, "governance-sync")) { continue; // to early for this node } int nObjsLeftToAsk = m_govman.RequestGovernanceObjectVotes(*pnode, connman); diff --git a/src/net.cpp b/src/net.cpp index f5a606dd78..5698ad6977 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -2513,6 +2513,8 @@ void CConnman::ThreadOpenMasternodeConnections() if (gArgs.IsArgSet("-connect") && gArgs.GetArgs("-connect").size() > 0) return; + assert(::mmetaman != nullptr); + auto& chainParams = Params(); bool didConnect = false; @@ -2579,7 +2581,7 @@ void CConnman::ThreadOpenMasternodeConnections() continue; } if (!connectedNodes.count(addr2) && !IsMasternodeOrDisconnectRequested(addr2) && !connectedProRegTxHashes.count(proRegTxHash)) { - int64_t lastAttempt = mmetaman.GetMetaInfo(dmn->proTxHash)->GetLastOutboundAttempt(); + int64_t lastAttempt = mmetaman->GetMetaInfo(dmn->proTxHash)->GetLastOutboundAttempt(); // back off trying connecting to an address if we already tried recently if (nANow - lastAttempt < chainParams.LLMQConnectionRetryTimeout()) { continue; @@ -2603,14 +2605,14 @@ void CConnman::ThreadOpenMasternodeConnections() bool connectedAndOutbound = connectedProRegTxHashes.count(dmn->proTxHash) && !connectedProRegTxHashes[dmn->proTxHash]; if (connectedAndOutbound) { // we already have an outbound connection to this MN so there is no theed to probe it again - mmetaman.GetMetaInfo(dmn->proTxHash)->SetLastOutboundSuccess(nANow); + mmetaman->GetMetaInfo(dmn->proTxHash)->SetLastOutboundSuccess(nANow); it = masternodePendingProbes.erase(it); continue; } ++it; - int64_t lastAttempt = mmetaman.GetMetaInfo(dmn->proTxHash)->GetLastOutboundAttempt(); + int64_t lastAttempt = mmetaman->GetMetaInfo(dmn->proTxHash)->GetLastOutboundAttempt(); // back off trying connecting to an address if we already tried recently if (nANow - lastAttempt < chainParams.LLMQConnectionRetryTimeout()) { continue; @@ -2661,7 +2663,7 @@ void CConnman::ThreadOpenMasternodeConnections() didConnect = true; - mmetaman.GetMetaInfo(connectToDmn->proTxHash)->SetLastOutboundAttempt(nANow); + mmetaman->GetMetaInfo(connectToDmn->proTxHash)->SetLastOutboundAttempt(nANow); OpenMasternodeConnection(CAddress(connectToDmn->pdmnState->addr, NODE_NETWORK), isProbe); // should be in the list now if connection was opened @@ -2674,7 +2676,7 @@ void CConnman::ThreadOpenMasternodeConnections() if (!connected) { LogPrint(BCLog::NET_NETCONN, "CConnman::%s -- connection failed for masternode %s, service=%s\n", __func__, connectToDmn->proTxHash.ToString(), connectToDmn->pdmnState->addr.ToString(false)); // Will take a few consequent failed attempts to PoSe-punish a MN. - if (mmetaman.GetMetaInfo(connectToDmn->proTxHash)->OutboundFailedTooManyTimes()) { + if (mmetaman->GetMetaInfo(connectToDmn->proTxHash)->OutboundFailedTooManyTimes()) { LogPrint(BCLog::NET_NETCONN, "CConnman::%s -- failed to connect to masternode %s too many times\n", __func__, connectToDmn->proTxHash.ToString()); } } diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 46f4bb30e9..e017cb14eb 100755 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -2731,6 +2731,8 @@ void PeerManagerImpl::ProcessGetCFCheckPt(CNode& peer, CDataStream& vRecv, const std::pair static ValidateDSTX(CTxMemPool& mempool, ChainstateManager& chainman, CCoinJoinBroadcastTx& dstx, uint256 hashTx) { + assert(::mmetaman != nullptr); + if (!dstx.IsValidStructure()) { LogPrint(BCLog::COINJOIN, "DSTX -- Invalid DSTX structure: %s\n", hashTx.ToString()); return {false, true}; @@ -2773,7 +2775,7 @@ std::pair static ValidateDSTX(CTxMemPool& memp return {false, true}; } - if (!mmetaman.GetMetaInfo(dmn->proTxHash)->IsValidForMixingTxes()) { + if (!mmetaman->GetMetaInfo(dmn->proTxHash)->IsValidForMixingTxes()) { LogPrint(BCLog::COINJOIN, "DSTX -- Masternode %s is sending too many transactions %s\n", dstx.masternodeOutpoint.ToStringShort(), hashTx.ToString()); return {true, true}; // TODO: Not an error? Could it be that someone is relaying old DSTXes @@ -2787,7 +2789,7 @@ std::pair static ValidateDSTX(CTxMemPool& memp LogPrint(BCLog::COINJOIN, "DSTX -- Got Masternode transaction %s\n", hashTx.ToString()); mempool.PrioritiseTransaction(hashTx, 0.1*COIN); - mmetaman.DisallowMixing(dmn->proTxHash); + mmetaman->DisallowMixing(dmn->proTxHash); return {true, false}; } diff --git a/src/netfulfilledman.cpp b/src/netfulfilledman.cpp index 03fe91ea87..f7e897d290 100644 --- a/src/netfulfilledman.cpp +++ b/src/netfulfilledman.cpp @@ -3,11 +3,32 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include +#include #include #include #include -CNetFulfilledRequestManager netfulfilledman; +std::unique_ptr netfulfilledman; + +CNetFulfilledRequestManager::CNetFulfilledRequestManager(bool load_cache) : + m_db{std::make_unique("netfulfilled.dat", "magicFulfilledCache")}, + is_valid{ + [&]() -> bool { + assert(m_db != nullptr); + return load_cache ? m_db->Load(*this) : m_db->Store(*this); + }() + } +{ + if (is_valid && load_cache) { + CheckAndRemove(); + } +} + +CNetFulfilledRequestManager::~CNetFulfilledRequestManager() +{ + if (!is_valid) return; + m_db->Store(*this); +} void CNetFulfilledRequestManager::AddFulfilledRequest(const CService& addr, const std::string& strRequest) { @@ -62,13 +83,13 @@ void CNetFulfilledRequestManager::CheckAndRemove() } } -void CNetFulfilledRequestManager::Clear() +void NetFulfilledRequestStore::Clear() { LOCK(cs_mapFulfilledRequests); mapFulfilledRequests.clear(); } -std::string CNetFulfilledRequestManager::ToString() const +std::string NetFulfilledRequestStore::ToString() const { std::ostringstream info; info << "Nodes with fulfilled requests: " << (int)mapFulfilledRequests.size(); diff --git a/src/netfulfilledman.h b/src/netfulfilledman.h index 412275a237..6dfb5fbb35 100644 --- a/src/netfulfilledman.h +++ b/src/netfulfilledman.h @@ -9,41 +9,61 @@ #include #include -class CNetFulfilledRequestManager; -extern CNetFulfilledRequestManager netfulfilledman; +#include -// Fulfilled requests are used to prevent nodes from asking for the same data on sync -// and from being banned for doing so too often. -class CNetFulfilledRequestManager +template +class CFlatDB; +class CNetFulfilledRequestManager; + +class NetFulfilledRequestStore { -private: +protected: typedef std::map fulfilledreqmapentry_t; typedef std::map fulfilledreqmap_t; +protected: //keep track of what node has/was asked for and when fulfilledreqmap_t mapFulfilledRequests; mutable RecursiveMutex cs_mapFulfilledRequests; public: - CNetFulfilledRequestManager() {} - - SERIALIZE_METHODS(CNetFulfilledRequestManager, obj) + SERIALIZE_METHODS(NetFulfilledRequestStore, obj) { LOCK(obj.cs_mapFulfilledRequests); READWRITE(obj.mapFulfilledRequests); } + void Clear(); + + std::string ToString() const; +}; + +// Fulfilled requests are used to prevent nodes from asking for the same data on sync +// and from being banned for doing so too often. +class CNetFulfilledRequestManager : public NetFulfilledRequestStore +{ +private: + using db_type = CFlatDB; + +private: + const std::unique_ptr m_db; + const bool is_valid{false}; + +public: + explicit CNetFulfilledRequestManager(bool load_cache); + ~CNetFulfilledRequestManager(); + + bool IsValid() const { return is_valid; } + void CheckAndRemove(); + void AddFulfilledRequest(const CService& addr, const std::string& strRequest); bool HasFulfilledRequest(const CService& addr, const std::string& strRequest); void RemoveAllFulfilledRequests(const CService& addr); - void CheckAndRemove(); - void Clear(); - - std::string ToString() const; - void DoMaintenance(); }; +extern std::unique_ptr netfulfilledman; + #endif // BITCOIN_NETFULFILLEDMAN_H diff --git a/src/rpc/evo.cpp b/src/rpc/evo.cpp index c26258d614..67a7c8075f 100644 --- a/src/rpc/evo.cpp +++ b/src/rpc/evo.cpp @@ -1303,7 +1303,8 @@ static UniValue BuildDMNListEntry(CWallet* pwallet, const CDeterministicMN& dmn, } #endif - auto metaInfo = mmetaman.GetMetaInfo(dmn.proTxHash); + CHECK_NONFATAL(mmetaman != nullptr); + auto metaInfo = mmetaman->GetMetaInfo(dmn.proTxHash); o.pushKV("metaInfo", metaInfo->ToJson()); return o; diff --git a/src/spork.cpp b/src/spork.cpp index 7dc55bd297..73873e05b3 100644 --- a/src/spork.cpp +++ b/src/spork.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -25,6 +26,8 @@ std::unique_ptr sporkManager; +const std::string SporkStore::SERIALIZATION_VERSION_STRING = "CSporkManager-Version-2"; + std::optional CSporkManager::SporkValueIfActive(SporkId nSporkID) const { AssertLockHeld(cs); @@ -56,7 +59,7 @@ std::optional CSporkManager::SporkValueIfActive(SporkId nSporkID) co return std::nullopt; } -void CSporkManager::Clear() +void SporkStore::Clear() { LOCK(cs); mapSporksActive.clear(); @@ -65,10 +68,32 @@ void CSporkManager::Clear() // we should not alter them here. } +CSporkManager::CSporkManager() : + m_db{std::make_unique("sporks.dat", "magicSporkCache")} +{ +} + +CSporkManager::~CSporkManager() +{ + if (!is_valid) return; + m_db->Store(*this); +} + +bool CSporkManager::LoadCache() +{ + assert(m_db != nullptr); + is_valid = m_db->Load(*this); + if (is_valid) { + CheckAndRemove(); + } + return is_valid; +} + void CSporkManager::CheckAndRemove() { LOCK(cs); - assert(!setSporkPubKeyIDs.empty()); + + if (setSporkPubKeyIDs.empty()) return; for (auto itActive = mapSporksActive.begin(); itActive != mapSporksActive.end();) { auto itSignerPair = itActive->second.begin(); @@ -327,7 +352,7 @@ bool CSporkManager::SetPrivKey(const std::string& strPrivKey) return true; } -std::string CSporkManager::ToString() const +std::string SporkStore::ToString() const { LOCK(cs); return strprintf("Sporks: %llu", mapSporksActive.size()); diff --git a/src/spork.h b/src/spork.h index 75df308056..7d63de440c 100644 --- a/src/spork.h +++ b/src/spork.h @@ -20,6 +20,8 @@ #include class CConnman; +template +class CFlatDB; class CNode; class CDataStream; class PeerManager; @@ -155,41 +157,17 @@ public: void Relay(CConnman& connman) const; }; -/** - * CSporkManager is a higher-level class which manages the node's spork - * messages, rules for which sporks should be considered active/inactive, and - * processing for certain sporks (e.g. spork 12). - */ -class CSporkManager +class SporkStore { -private: - static constexpr std::string_view SERIALIZATION_VERSION_STRING = "CSporkManager-Version-2"; - - mutable Mutex cs_mapSporksCachedActive; - mutable std::unordered_map mapSporksCachedActive GUARDED_BY(cs_mapSporksCachedActive); - - mutable Mutex cs_mapSporksCachedValues; - mutable std::unordered_map mapSporksCachedValues GUARDED_BY(cs_mapSporksCachedValues); +protected: + static const std::string SERIALIZATION_VERSION_STRING; mutable Mutex cs; std::unordered_map mapSporksByHash GUARDED_BY(cs); std::unordered_map > mapSporksActive GUARDED_BY(cs); - std::set setSporkPubKeyIDs GUARDED_BY(cs); - int nMinSporkKeys GUARDED_BY(cs) {std::numeric_limits::max()}; - CKey sporkPrivKey GUARDED_BY(cs); - - /** - * SporkValueIfActive is used to get the value agreed upon by the majority - * of signed spork messages for a given Spork ID. - */ - std::optional SporkValueIfActive(SporkId nSporkID) const EXCLUSIVE_LOCKS_REQUIRED(cs); - public: - - CSporkManager() = default; - template void Serialize(Stream &s) const LOCKS_EXCLUDED(cs) { @@ -221,6 +199,50 @@ public: */ void Clear() LOCKS_EXCLUDED(cs); + /** + * ToString returns the string representation of the SporkManager. + */ + std::string ToString() const LOCKS_EXCLUDED(cs); +}; + +/** + * CSporkManager is a higher-level class which manages the node's spork + * messages, rules for which sporks should be considered active/inactive, and + * processing for certain sporks (e.g. spork 12). + */ +class CSporkManager : public SporkStore +{ +private: + using db_type = CFlatDB; + +private: + const std::unique_ptr m_db; + bool is_valid{false}; + + mutable Mutex cs_mapSporksCachedActive; + mutable std::unordered_map mapSporksCachedActive GUARDED_BY(cs_mapSporksCachedActive); + + mutable Mutex cs_mapSporksCachedValues; + mutable std::unordered_map mapSporksCachedValues GUARDED_BY(cs_mapSporksCachedValues); + + std::set setSporkPubKeyIDs GUARDED_BY(cs); + int nMinSporkKeys GUARDED_BY(cs) {std::numeric_limits::max()}; + CKey sporkPrivKey GUARDED_BY(cs); + + /** + * SporkValueIfActive is used to get the value agreed upon by the majority + * of signed spork messages for a given Spork ID. + */ + std::optional SporkValueIfActive(SporkId nSporkID) const EXCLUSIVE_LOCKS_REQUIRED(cs); + +public: + CSporkManager(); + ~CSporkManager(); + + bool LoadCache(); + + bool IsValid() const { return is_valid; } + /** * CheckAndRemove is defined to fulfill an interface as part of the on-disk * cache used to cache sporks between runs. If sporks that are restored @@ -244,7 +266,6 @@ public: */ void ProcessSpork(const CNode& peer, PeerManager& peerman, CConnman& connman, CDataStream& vRecv) LOCKS_EXCLUDED(cs); - /** * ProcessGetSporks is used to handle the 'getsporks' p2p message. * @@ -315,11 +336,6 @@ public: * address in the set of valid spork signers (see SetSporkAddress). */ bool SetPrivKey(const std::string& strPrivKey) LOCKS_EXCLUDED(cs); - - /** - * ToString returns the string representation of the SporkManager. - */ - std::string ToString() const LOCKS_EXCLUDED(cs); }; #endif // BITCOIN_SPORK_H diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index 671ec1cbfb..c393f1d8de 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -11,10 +11,13 @@ #include #include #include +#include #include #include #include #include +#include +#include #include #include #include @@ -206,6 +209,8 @@ ChainTestingSetup::ChainTestingSetup(const std::string& chainName, const std::ve ::sporkManager = std::make_unique(); ::governance = std::make_unique(); ::masternodeSync = std::make_unique(*m_node.connman, *::governance); + ::mmetaman = std::make_unique(/* load_cache */ false); + ::netfulfilledman = std::make_unique(/* load_cache */ false); m_node.creditPoolManager = std::make_unique(*m_node.evodb); @@ -222,6 +227,8 @@ ChainTestingSetup::~ChainTestingSetup() StopScriptCheckWorkerThreads(); GetMainSignals().FlushBackgroundCallbacks(); GetMainSignals().UnregisterBackgroundSignalScheduler(); + ::netfulfilledman.reset(); + ::mmetaman.reset(); ::masternodeSync.reset(); ::governance.reset(); ::sporkManager.reset();