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();