From 47bb53dcabbff55337c52ea36aff45b34eff7d28 Mon Sep 17 00:00:00 2001 From: "James C. Owens" Date: Fri, 5 Apr 2024 15:56:27 -0400 Subject: [PATCH 1/4] Enhance verify checkpoints fail handling This changes the verify checkpoints fail handling to use the GUI reset blockchain facility if in the GUI, or direct the user to use the -resetblockchaindata option if using the daemon, rather than instructing the user to manually delete the blockchain data files. --- src/gridcoin/gridcoin.cpp | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/src/gridcoin/gridcoin.cpp b/src/gridcoin/gridcoin.cpp index 39853e113f..b9292896d5 100644 --- a/src/gridcoin/gridcoin.cpp +++ b/src/gridcoin/gridcoin.cpp @@ -27,6 +27,7 @@ extern bool fExplorer; extern unsigned int nScraperSleep; extern unsigned int nActiveBeforeSB; extern bool fScraperActive; +extern bool fQtActive; void Scraper(bool bSingleShot = false); void ScraperSubscriber(); @@ -40,17 +41,29 @@ namespace { //! void ShowChainCorruptedMessage() { - uiInterface.ThreadSafeMessageBox( - _("WARNING: Blockchain data may be corrupted.\n\n" - "Gridcoin detected bad index entries. This may occur because of an " - "unexpected exit, a power failure, or a late software upgrade.\n\n" - "Please exit Gridcoin, open the data directory, and delete:\n" - " - the blk****.dat files\n" - " - the txleveldb folder\n\n" - "Your wallet will re-download the blockchain. Your balance may " - "appear incorrect until the synchronization finishes.\n" ), - "Gridcoin", - CClientUIInterface::BTN_OK | CClientUIInterface::MODAL); + fResetBlockchainRequest = true; + + if (fQtActive) { + uiInterface.ThreadSafeMessageBox( + _("ERROR: Checkpoint mismatch: Blockchain data may be corrupted.\n\n" + "Gridcoin detected bad index entries. This may occur because of a " + "late software upgrade, unexpected exit, or a power failure. " + "Your blockchain data is being reset and your wallet will resync " + "from genesis when you restart. Your balance may appear incorrect " + "until the synchronization finishes."), + "Gridcoin", + CClientUIInterface::BTN_OK | CClientUIInterface::MODAL); + } else { + uiInterface.ThreadSafeMessageBox( + _("ERROR: Checkpoint mismatch: Blockchain data may be corrupted.\n\n" + "Gridcoin detected bad index entries. This may occur because of a " + "late software upgrade, unexpected exit, or a power failure. " + "Please run gridcoinresearchd with the -resetblockchaindata " + "parameter. Your wallet will re-download the blockchain. Your " + "balance may appear incorrect until the synchronization finishes." ), + "Gridcoin", + CClientUIInterface::BTN_OK | CClientUIInterface::MODAL); + } } //! From 21f28f5fd668da69fbc1e0a8daf8fca0cffdf5ee Mon Sep 17 00:00:00 2001 From: "James C. Owens" Date: Sun, 7 Apr 2024 21:26:31 -0400 Subject: [PATCH 2/4] Change CheckBlockIndexJob to initialization call This will cause the wallet to ShowChainCorruptedMessage() and either delete the blockchain files (in the GUI case) and end, or post a log entry with instructions and end. A missing pprev on any blockindex entry is fatal and the wallet should not be allowed to continue operation without rebuilding the chain data. --- src/gridcoin/gridcoin.cpp | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/gridcoin/gridcoin.cpp b/src/gridcoin/gridcoin.cpp index b9292896d5..acb79106f3 100644 --- a/src/gridcoin/gridcoin.cpp +++ b/src/gridcoin/gridcoin.cpp @@ -374,20 +374,23 @@ void InitializeExplorerFeatures() //! whether the index contains invalid state caused by an unclean shutdown. //! This condition was originally detected by an assertion in a routine for //! stake modifier checksum verification. Because Gridcoin removed modifier -//! checksums and checkpoints, we reinstate that assertion here as a formal -//! inspection. +//! checksums, we reinstate that assertion here as a formal inspection done +//! at initialization before the VerifyCheckpoints. //! //! This function checks that no block index entries contain a null pointer //! to a previous block. The symptom may indicate a deeper problem that can //! be resolved by tuning disk synchronization in LevelDB. Until then, this //! heuristic has proven itself to be effective for identifying a corrupted -//! database. +//! database. This type of error has not been seen in the wild in several +//! years as of Gridcoin 5.4.7.0, but is retained for thoroughness. //! -void CheckBlockIndexJob() +bool CheckBlockIndex() { LogPrintf("Gridcoin: checking block index..."); + uiInterface.InitMessage(_("Checking block index...")); - bool corrupted = false; + // Block index integrity status + bool status = true; if (pindexGenesisBlock) { LOCK(cs_main); @@ -396,18 +399,20 @@ void CheckBlockIndexJob() const CBlockIndex* const pindex = index_pair.second; if (!pindex || !(pindex->pprev || pindex == pindexGenesisBlock)) { - corrupted = true; + status = false; break; } } } - if (!corrupted) { + if (status) { LogPrintf("Gridcoin: block index is clean"); - return; + return status; } ShowChainCorruptedMessage(); + + return status; } //! @@ -503,6 +508,9 @@ bool GRC::Initialize(ThreadHandlerPtr threads, CBlockIndex* pindexBest) { LogPrintf("Gridcoin: initializing..."); + if (!CheckBlockIndex()) { + return false; + } if (!VerifyCheckpoints(pindexBest)) { return false; } @@ -531,8 +539,6 @@ void GRC::CloseResearcherRegistryFile() void GRC::ScheduleBackgroundJobs(CScheduler& scheduler) { - scheduler.schedule(CheckBlockIndexJob, std::chrono::system_clock::now()); - // Primitive, but this is what the scraper does in the scraper housekeeping // loop. It checks to see if the logs need to be archived by default every // 5 mins. Note that passing false to the archive function means that if we From 8154056179c2bb81434f4c15c68ab3f8e44025a8 Mon Sep 17 00:00:00 2001 From: "James C. Owens" Date: Sun, 7 Apr 2024 22:12:34 -0400 Subject: [PATCH 3/4] Change scheduled DB passivations to use RegistryBookmarks --- src/gridcoin/beacon.cpp | 15 --------------- src/gridcoin/beacon.h | 7 +------ src/gridcoin/contract/handler.h | 15 +++++++++++++++ src/gridcoin/gridcoin.cpp | 17 ++++++++++++----- src/gridcoin/gridcoin.h | 6 ++++++ src/gridcoin/project.cpp | 15 --------------- src/gridcoin/project.h | 9 ++------- src/gridcoin/protocol.cpp | 15 --------------- src/gridcoin/protocol.h | 7 +------ src/gridcoin/scraper/scraper_registry.cpp | 15 --------------- src/gridcoin/scraper/scraper_registry.h | 7 +------ src/gridcoin/sidestake.cpp | 15 --------------- src/gridcoin/sidestake.h | 7 +------ 13 files changed, 39 insertions(+), 111 deletions(-) diff --git a/src/gridcoin/beacon.cpp b/src/gridcoin/beacon.cpp index e529fdff07..3fdcfc9e61 100644 --- a/src/gridcoin/beacon.cpp +++ b/src/gridcoin/beacon.cpp @@ -1391,21 +1391,6 @@ BeaconRegistry::BeaconDB &BeaconRegistry::GetBeaconDB() return m_beacon_db; } -// This is static and called by the scheduler. -void BeaconRegistry::RunDBPassivation() -{ - TRY_LOCK(cs_main, locked_main); - - if (!locked_main) - { - return; - } - - BeaconRegistry& beacons = GetBeaconRegistry(); - - beacons.PassivateDB(); -} - template<> const std::string BeaconRegistry::BeaconDB::KeyType() { return std::string("beacon"); diff --git a/src/gridcoin/beacon.h b/src/gridcoin/beacon.h index 64970d7bac..e97d1fd0f3 100644 --- a/src/gridcoin/beacon.h +++ b/src/gridcoin/beacon.h @@ -768,7 +768,7 @@ class BeaconRegistry : public IContractHandler //! //! \return The number of elements passivated. //! - uint64_t PassivateDB(); + uint64_t PassivateDB() override; //! //! \brief This function walks the linked beacon entries back (using the m_previous_hash member) from a provided @@ -795,11 +795,6 @@ class BeaconRegistry : public IContractHandler //! bool SetNeedsIsContractCorrection(bool flag); - //! - //! \brief A static function that is called by the scheduler to run the beacon database passivation. - //! - static void RunDBPassivation(); - //! //! \brief Specializes the template RegistryDB for the ScraperEntry class //! diff --git a/src/gridcoin/contract/handler.h b/src/gridcoin/contract/handler.h index 9d2d462d5a..5bb121e1bf 100644 --- a/src/gridcoin/contract/handler.h +++ b/src/gridcoin/contract/handler.h @@ -5,6 +5,7 @@ #ifndef GRIDCOIN_CONTRACT_HANDLER_H #define GRIDCOIN_CONTRACT_HANDLER_H +#include #include class CBlockIndex; @@ -153,6 +154,20 @@ struct IContractHandler //! \param height //! virtual void SetDBHeight(int& height); + + //! + //! \brief Passivates the elements in the underlying db, which means remove from memory elements in the + //! historical map that are not referenced by any of the in memory maps. The backing store of + //! the element removed from memory is retained and will be transparently restored if find() + //! is called on the hash key for the element. + //! + //! \return The number of elements passivated. + //! + virtual uint64_t PassivateDB() + { + // The default method here does nothing. + return uint64_t {0}; + }; }; } // namespace GRC diff --git a/src/gridcoin/gridcoin.cpp b/src/gridcoin/gridcoin.cpp index acb79106f3..693eb318fd 100644 --- a/src/gridcoin/gridcoin.cpp +++ b/src/gridcoin/gridcoin.cpp @@ -484,11 +484,8 @@ void ScheduleRegistriesPassivation(CScheduler& scheduler) { // Run registry database passivation every 5 minutes. This is a very thin call most of the time. // Please see the PassivateDB function and passivate_db. - // TODO: Turn into a loop using extension of RegistryBookmarks - scheduler.scheduleEvery(BeaconRegistry::RunDBPassivation, std::chrono::minutes{5}); - scheduler.scheduleEvery(ScraperRegistry::RunDBPassivation, std::chrono::minutes{5}); - scheduler.scheduleEvery(ProtocolRegistry::RunDBPassivation, std::chrono::minutes{5}); - scheduler.scheduleEvery(Whitelist::RunDBPassivation, std::chrono::minutes{5}); + + scheduler.scheduleEvery(RunDBPassivation, std::chrono::minutes{5}); } } // Anonymous namespace @@ -608,3 +605,13 @@ skip:; return true; } +void GRC::RunDBPassivation() +{ + LOCK(cs_main); + + for (const auto& contract_type : RegistryBookmarks::CONTRACT_TYPES_WITH_REG_DB) { + Registry& registry = RegistryBookmarks::GetRegistryWithDB(contract_type); + + registry.PassivateDB(); + } +} diff --git a/src/gridcoin/gridcoin.h b/src/gridcoin/gridcoin.h index dbd6bde049..d9c17e21bf 100644 --- a/src/gridcoin/gridcoin.h +++ b/src/gridcoin/gridcoin.h @@ -40,6 +40,12 @@ void ScheduleBackgroundJobs(CScheduler& scheduler); //! \return \c true if no errors occurred. //! bool CleanConfig(); + +//! +//! \brief Function to allow cycling through DB passivation for all contract types backed +//! with registry db. +//! +void RunDBPassivation(); } // namespace GRC #endif // GRIDCOIN_GRIDCOIN_H diff --git a/src/gridcoin/project.cpp b/src/gridcoin/project.cpp index 6db6511067..cf5c805e18 100644 --- a/src/gridcoin/project.cpp +++ b/src/gridcoin/project.cpp @@ -541,21 +541,6 @@ Whitelist::ProjectEntryDB &Whitelist::GetProjectDB() return m_project_db; } -// This is static and called by the scheduler. -void Whitelist::RunDBPassivation() -{ - TRY_LOCK(cs_main, locked_main); - - if (!locked_main) - { - return; - } - - Whitelist& project_entries = GetWhitelist(); - - project_entries.PassivateDB(); -} - template<> const std::string Whitelist::ProjectEntryDB::KeyType() { return std::string("project"); diff --git a/src/gridcoin/project.h b/src/gridcoin/project.h index 5b6ff5fae2..9e4c55ae87 100644 --- a/src/gridcoin/project.h +++ b/src/gridcoin/project.h @@ -603,19 +603,14 @@ class Whitelist : public IContractHandler void ResetInMemoryOnly(); //! - //! \brief Passivates the elements in the scraper db, which means remove from memory elements in the + //! \brief Passivates the elements in the project db, which means remove from memory elements in the //! historical map that are not referenced by m_projects. The backing store of the element removed //! from memory is retained and will be transparently restored if find() is called on the hash key //! for the element. //! //! \return The number of elements passivated. //! - uint64_t PassivateDB(); - - //! - //! \brief A static function that is called by the scheduler to run the project entry database passivation. - //! - static void RunDBPassivation(); + uint64_t PassivateDB() override; //! //! \brief Specializes the template RegistryDB for the ScraperEntry class. Note that std::set is not diff --git a/src/gridcoin/protocol.cpp b/src/gridcoin/protocol.cpp index ec5f0831e9..bd32a6838d 100644 --- a/src/gridcoin/protocol.cpp +++ b/src/gridcoin/protocol.cpp @@ -528,21 +528,6 @@ ProtocolRegistry::ProtocolEntryDB &ProtocolRegistry::GetProtocolEntryDB() return m_protocol_db; } -// This is static and called by the scheduler. -void ProtocolRegistry::RunDBPassivation() -{ - TRY_LOCK(cs_main, locked_main); - - if (!locked_main) - { - return; - } - - ProtocolRegistry& protocol_entries = GetProtocolRegistry(); - - protocol_entries.PassivateDB(); -} - template<> const std::string ProtocolRegistry::ProtocolEntryDB::KeyType() { return std::string("protocol"); diff --git a/src/gridcoin/protocol.h b/src/gridcoin/protocol.h index 37aa39bc53..3e8f400e17 100644 --- a/src/gridcoin/protocol.h +++ b/src/gridcoin/protocol.h @@ -559,12 +559,7 @@ class ProtocolRegistry : public IContractHandler //! //! \return The number of elements passivated. //! - uint64_t PassivateDB(); - - //! - //! \brief A static function that is called by the scheduler to run the protocol entry database passivation. - //! - static void RunDBPassivation(); + uint64_t PassivateDB() override; //! //! \brief Specializes the template RegistryDB for the ProtocolEntry class. Note that std::set diff --git a/src/gridcoin/scraper/scraper_registry.cpp b/src/gridcoin/scraper/scraper_registry.cpp index bf481e0216..5a22fb11ad 100644 --- a/src/gridcoin/scraper/scraper_registry.cpp +++ b/src/gridcoin/scraper/scraper_registry.cpp @@ -573,21 +573,6 @@ ScraperRegistry::ScraperEntryDB &ScraperRegistry::GetScraperDB() return m_scraper_db; } -// This is static and called by the scheduler. -void ScraperRegistry::RunDBPassivation() -{ - TRY_LOCK(cs_main, locked_main); - - if (!locked_main) - { - return; - } - - ScraperRegistry& scraper_entries = GetScraperRegistry(); - - scraper_entries.PassivateDB(); -} - template<> const std::string ScraperRegistry::ScraperEntryDB::KeyType() { return std::string("scraper"); diff --git a/src/gridcoin/scraper/scraper_registry.h b/src/gridcoin/scraper/scraper_registry.h index 5686c25991..8256b7e11b 100644 --- a/src/gridcoin/scraper/scraper_registry.h +++ b/src/gridcoin/scraper/scraper_registry.h @@ -598,12 +598,7 @@ class ScraperRegistry : public IContractHandler //! //! \return The number of elements passivated. //! - uint64_t PassivateDB(); - - //! - //! \brief A static function that is called by the scheduler to run the scraper entry database passivation. - //! - static void RunDBPassivation(); + uint64_t PassivateDB() override; //! //! \brief Specializes the template RegistryDB for the ScraperEntry class. Note that std::set is diff --git a/src/gridcoin/sidestake.cpp b/src/gridcoin/sidestake.cpp index 2c9e53966d..0c553d6eef 100644 --- a/src/gridcoin/sidestake.cpp +++ b/src/gridcoin/sidestake.cpp @@ -1164,21 +1164,6 @@ SideStakeRegistry::SideStakeDB &SideStakeRegistry::GetSideStakeDB() return m_sidestake_db; } -// This is static and called by the scheduler. -void SideStakeRegistry::RunDBPassivation() -{ - TRY_LOCK(cs_main, locked_main); - - if (!locked_main) - { - return; - } - - SideStakeRegistry& SideStake_entries = GetSideStakeRegistry(); - - SideStake_entries.PassivateDB(); -} - template<> const std::string SideStakeRegistry::SideStakeDB::KeyType() { return std::string("SideStake"); diff --git a/src/gridcoin/sidestake.h b/src/gridcoin/sidestake.h index 2737085e6c..d909e7f27a 100644 --- a/src/gridcoin/sidestake.h +++ b/src/gridcoin/sidestake.h @@ -830,7 +830,7 @@ class SideStakeRegistry : public IContractHandler //! //! \return The number of elements passivated. //! - uint64_t PassivateDB(); + uint64_t PassivateDB() override; //! //! \brief This method parses the config file for local sidestakes. It is based on the original GetSideStakingStatusAndAlloc() @@ -838,11 +838,6 @@ class SideStakeRegistry : public IContractHandler //! void LoadLocalSideStakesFromConfig(); - //! - //! \brief A static function that is called by the scheduler to run the sidestake entry database passivation. - //! - static void RunDBPassivation(); - //! //! \brief Specializes the template RegistryDB for the SideStake class. Note that std::set //! is not actually used. From ddf33f624df11b7211e203d67dbb63cbda71144f Mon Sep 17 00:00:00 2001 From: div72 Date: Mon, 8 Apr 2024 20:11:39 +0300 Subject: [PATCH 4/4] staking: add a missing check --- src/gridcoin/staking/kernel.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/gridcoin/staking/kernel.cpp b/src/gridcoin/staking/kernel.cpp index 7f1952140d..ccb154862e 100644 --- a/src/gridcoin/staking/kernel.cpp +++ b/src/gridcoin/staking/kernel.cpp @@ -603,21 +603,21 @@ bool GRC::CheckProofOfStakeV8( CTransaction txPrev; if (!ReadStakedInput(txdb, prevout.hash, header, txPrev, pindexPrev)) - return tx.DoS(1, error("%s: read staked input failed", __func__)); + return tx.DoS(10, error("%s: read staked input failed", __func__)); if (!VerifySignature(txPrev, tx, 0, 0)) return tx.DoS(100, error("%s: VerifySignature failed on coinstake %s", __func__, tx.GetHash().ToString())); // Check times if (tx.nTime < txPrev.nTime) - return error("%s: nTime violation", __func__); + return tx.DoS(100, error("%s: nTime violation", __func__)); if (header.nTime + nStakeMinAge > tx.nTime) - return error("%s: min age violation", __func__); + return tx.DoS(100, error("%s: min age violation", __func__)); if (Block.nVersion >= 12) { if (tx.nTime != MaskStakeTime(tx.nTime)) { - return error("%s: mask violation", __func__); + return tx.DoS(100, error("%s: mask violation", __func__)); } } @@ -649,5 +649,9 @@ bool GRC::CheckProofOfStakeV8( ); // Now check if proof-of-stake hash meets target protocol - return bnHashProof <= bnTarget; + if (bnHashProof > bnTarget) { + return tx.DoS(100, error("%s: invalid proof (proof: %s, target: %s)", __func__, bnHashProof.GetHex(), bnTarget.GetHex())); + } + + return true; }