Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ BITCOIN_CORE_H = \
evo/simplifiedmns.h \
evo/smldiff.h \
evo/snapshot.h \
evo/snapshot_types.h \
evo/specialtx.h \
evo/specialtx_filter.h \
evo/specialtxman.h \
Expand Down Expand Up @@ -560,6 +561,7 @@ libbitcoin_node_a_SOURCES = \
evo/simplifiedmns.cpp \
evo/smldiff.cpp \
evo/snapshot.cpp \
evo/snapshot_chain.cpp \
evo/specialtx.cpp \
evo/specialtx_filter.cpp \
evo/specialtxman.cpp \
Expand Down Expand Up @@ -1293,6 +1295,7 @@ libdashkernel_la_SOURCES = \
evo/providertx_util.cpp \
evo/simplifiedmns.cpp \
evo/smldiff.cpp \
evo/snapshot.cpp \
evo/specialtx.cpp \
evo/specialtx_filter.cpp \
evo/specialtxman.cpp \
Expand Down
10 changes: 9 additions & 1 deletion src/evo/chainhelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <evo/creditpool.h>
#include <evo/deterministicmns.h>
#include <evo/mnhftx.h>
#include <evo/snapshot.h>
#include <evo/specialtxman.h>
#include <governance/superblock.h>
#include <hash.h>
Expand All @@ -27,6 +28,8 @@ CChainstateHelper::CChainstateHelper(CEvoDB& evodb, CDeterministicMNManager& dmn
isman{isman},
mn_sync{mn_sync},
m_dmnman{dmnman},
m_qblockman{qblockman},
m_qsnapman{qsnapman},
credit_pool_manager{std::make_unique<CCreditPoolManager>(evodb, chainman)},
m_chainlocks{chainlocks},
ehf_manager{std::make_unique<CMNHFManager>(evodb, consensus_params)},
Expand Down Expand Up @@ -66,7 +69,12 @@ int32_t CChainstateHelper::GetBestChainLockHeight() const { return m_chainlocks.

uint256 CChainstateHelper::GetDeterministicMNListHash(const CBlockIndex* pindex) const
{
return SerializeHash(m_dmnman.GetListForBlock(Assert(pindex)));
const CBlockIndex* index{Assert(pindex)};
CDeterministicMNList list{m_dmnman.GetListForBlock(index)};
if (list.GetBlockHash().IsNull()) {
list = CDeterministicMNList{index->GetBlockHash(), index->nHeight, 0};
}
return evo::CanonicalMNListHash(list);
}

/** Passthrough functions to CCreditPoolManager */
Expand Down
5 changes: 5 additions & 0 deletions src/evo/chainhelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ class CChainstateHelper
llmq::CInstantSendManager& isman;
const CMasternodeSync& mn_sync;
CDeterministicMNManager& m_dmnman;
llmq::CQuorumBlockProcessor& m_qblockman;
llmq::CQuorumSnapshotManager& m_qsnapman;

public:
const std::unique_ptr<CCreditPoolManager> credit_pool_manager;
Expand Down Expand Up @@ -75,6 +77,9 @@ class CChainstateHelper

/** Return a canonical hash of the deterministic MN list derived at a block. */
uint256 GetDeterministicMNListHash(const CBlockIndex* pindex) const;
CDeterministicMNManager& DeterministicMNManager() { return m_dmnman; }
llmq::CQuorumBlockProcessor& QuorumBlockProcessor() { return m_qblockman; }
llmq::CQuorumSnapshotManager& QuorumSnapshotManager() { return m_qsnapman; }

/** Passthrough functions to CCreditPoolManager */
CCreditPool GetCreditPool(const CBlockIndex* const pindex);
Expand Down
19 changes: 13 additions & 6 deletions src/evo/creditpool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <masternode/payments.h>
#include <node/blockstorage.h>
#include <shutdown.h>
#include <util/check.h>
#include <validation.h>

#include <algorithm>
Expand Down Expand Up @@ -127,12 +128,12 @@ std::optional<CCreditPool> CCreditPoolManager::GetFromCache(const CBlockIndex& b
MaybeWriteSnapshot(block_hash, block_index.nHeight, pool);
return pool;
}
if (block_index.nHeight % DISK_SNAPSHOT_PERIOD == 0) {
if (evoDb.Read(std::make_pair(DB_CREDITPOOL_SNAPSHOT, block_hash), pool)) {
LOCK(cache_mutex);
creditPoolCache.insert(block_hash, pool);
return pool;
}
// Snapshot activation may deliberately seed a full state at a height that
// is not one of the normal periodic checkpoints.
if (evoDb.Read(std::make_pair(DB_CREDITPOOL_SNAPSHOT, block_hash), pool)) {
LOCK(cache_mutex);
creditPoolCache.insert(block_hash, pool);
return pool;
}
return std::nullopt;
}
Expand Down Expand Up @@ -164,6 +165,12 @@ void CCreditPoolManager::AddToCache(const uint256& block_hash, int height, const
creditPoolCache.insert(block_hash, pool);
}

bool CCreditPoolManager::SeedSnapshot(const CBlockIndex* block, const CCreditPool& pool)
{
if (!Assume(block != nullptr)) return false;
return evoDb.WriteDerived(std::make_pair(DB_CREDITPOOL_SNAPSHOT, block->GetBlockHash()), pool);
}
Comment thread
PastaPastaPasta marked this conversation as resolved.

CCreditPool CCreditPoolManager::ConstructCreditPool(const gsl::not_null<const CBlockIndex*> block_index, CCreditPool prev)
{
std::optional<CreditPoolDataPerBlock> opt_block_data = GetCreditDataFromBlock(block_index, m_chainman.GetConsensus());
Expand Down
2 changes: 2 additions & 0 deletions src/evo/creditpool.h
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,8 @@ class CCreditPoolManager
* it can happen if there limits of withdrawal (unlock) exceed
*/
CCreditPool GetCreditPool(const CBlockIndex* block) EXCLUSIVE_LOCKS_REQUIRED(!cache_mutex);
/** Seed a full pool snapshot in the current EvoDB transaction. */
bool SeedSnapshot(const CBlockIndex* block, const CCreditPool& pool) EXCLUSIVE_LOCKS_REQUIRED(!cache_mutex);

private:
std::optional<CCreditPool> GetFromCache(const CBlockIndex& block_index) EXCLUSIVE_LOCKS_REQUIRED(!cache_mutex);
Expand Down
13 changes: 13 additions & 0 deletions src/evo/deterministicmns.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,18 @@ CDeterministicMNManager::CDeterministicMNManager(CEvoDB& evoDb, CMasternodeMetaM

CDeterministicMNManager::~CDeterministicMNManager() = default;

bool CDeterministicMNManager::SeedListForBlock(const CDeterministicMNList& list)
{
return m_evoDb.WriteDerived(std::make_pair(DB_LIST_SNAPSHOT, list.GetBlockHash()), list);
}

void CDeterministicMNManager::InvalidateListCacheForBlock(const uint256& block_hash)
{
LOCK(cs);
mnListsCache.erase(block_hash);
mnListDiffsCache.erase(block_hash);
}

bool CDeterministicMNManager::ProcessBlock(const CBlock& block, gsl::not_null<const CBlockIndex*> pindex,
BlockValidationState& state, const CDeterministicMNList& newList,
MNListUpdates& updatesRet)
Expand Down Expand Up @@ -851,6 +863,7 @@ CDeterministicMNList CDeterministicMNManager::GetListForBlockInternal(gsl::not_n
mnListsCache.emplace(pindex->GetBlockHash(), snapshot);
break;
}
if (m_list_snapshot_miss_hook) m_list_snapshot_miss_hook(pindex);

// no snapshot found yet, check diffs
auto itDiffs = mnListDiffsCache.find(pindex->GetBlockHash());
Expand Down
17 changes: 17 additions & 0 deletions src/evo/deterministicmns.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <immer/map.hpp>

#include <algorithm>
#include <functional>
#include <limits>
#include <numeric>
#include <stdexcept>
Expand Down Expand Up @@ -776,6 +777,7 @@ class CDeterministicMNManager

Uint256HashMap<CDeterministicMNList> mnListsCache GUARDED_BY(cs);
Uint256HashMap<CDeterministicMNListDiff> mnListDiffsCache GUARDED_BY(cs);
std::function<void(const CBlockIndex*)> m_list_snapshot_miss_hook GUARDED_BY(cs);
const CBlockIndex* tipIndex GUARDED_BY(cs) {nullptr};
const CBlockIndex* m_initial_snapshot_index GUARDED_BY(cs) {nullptr};

Expand All @@ -799,6 +801,21 @@ class CDeterministicMNManager
};
CDeterministicMNList GetListAtChainTip() EXCLUSIVE_LOCKS_REQUIRED(!cs);

/** Seed a canonical full-list snapshot in the current EvoDB transaction. */
bool SeedListForBlock(const CDeterministicMNList& list) EXCLUSIVE_LOCKS_REQUIRED(!cs);

/** Invalidate cached list data so the next lookup reloads it from EvoDB. */
void InvalidateListCacheForBlock(const uint256& block_hash) EXCLUSIVE_LOCKS_REQUIRED(!cs);

/** Test-only guard invoked after a full-list cache/EvoDB miss, before
* ordinary diff-chain reconstruction can access earlier NORMAL state. */
void SetListSnapshotMissHookForTesting(std::function<void(const CBlockIndex*)> hook)
EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
m_list_snapshot_miss_hook = std::move(hook);
}

void SetListForBlockForTesting(const CDeterministicMNList& list) EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
Expand Down
43 changes: 42 additions & 1 deletion src/evo/evodb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,13 +147,28 @@ bool CEvoDB::HasDualChainstateMarker()
return db->Exists(EVODB_DUAL_CHAINSTATE);
}

// Reader is the raw DB or a transaction; Eraser a batch or the same transaction.
template <typename Reader, typename Eraser>
static void EraseHistoricalMNListMarkers(Reader& reader, Eraser& eraser)
{
std::vector<uint256> required;
if (reader.Read(EVODB_REQUIRED_WORK_MNLISTS, required)) {
for (const auto& block_hash : required) {
eraser.Erase(std::make_pair(EVODB_BACKGROUND_WORK_MNLIST_HASH, block_hash));
}
}
eraser.Erase(EVODB_REQUIRED_WORK_MNLISTS);
}

void CEvoDB::EraseSnapshotMarkers()
{
LOCK(cs);
auto& transaction = GetContext(GetCurrentIdentity()).cur_transaction;
transaction.Erase(std::make_pair(EVODB_BEST_BLOCK, uint8_t{1}));
transaction.Erase(EVODB_SNAPSHOT_MNLIST_HASH);
transaction.Erase(EVODB_BACKGROUND_MNLIST_HASH);
EraseHistoricalMNListMarkers(transaction, transaction);
transaction.Erase(EVODB_SNAPSHOT_EVO_SECTION);
transaction.Erase(EVODB_DUAL_CHAINSTATE);
}

Expand Down Expand Up @@ -186,6 +201,26 @@ bool CEvoDB::ReadBackgroundMNListHash(uint256& block_hash, uint256& mn_list_hash
return true;
}

void CEvoDB::WriteRequiredWorkMNListHashes(const std::vector<uint256>& block_hashes)
{
Write(EVODB_REQUIRED_WORK_MNLISTS, block_hashes);
}

bool CEvoDB::ReadRequiredWorkMNListHashes(std::vector<uint256>& block_hashes)
{
return Read(EVODB_REQUIRED_WORK_MNLISTS, block_hashes);
}

void CEvoDB::WriteBackgroundWorkMNListHash(const uint256& block_hash, const uint256& mn_list_hash)
{
Write(std::make_pair(EVODB_BACKGROUND_WORK_MNLIST_HASH, block_hash), mn_list_hash);
}

bool CEvoDB::ReadBackgroundWorkMNListHash(const uint256& block_hash, uint256& mn_list_hash)
{
return Read(std::make_pair(EVODB_BACKGROUND_WORK_MNLIST_HASH, block_hash), mn_list_hash);
}

bool CEvoDB::PromoteSnapshotMarkers(const uint256& expected_snapshot_tip)
{
LOCK(cs);
Expand All @@ -202,7 +237,9 @@ bool CEvoDB::PromoteSnapshotMarkers(const uint256& expected_snapshot_tip)
uint256 normal_tip;
const bool already_promoted = db->Read(EVODB_BEST_BLOCK, normal_tip) && normal_tip == expected_snapshot_tip &&
!db->Exists(EVODB_DUAL_CHAINSTATE) && !db->Exists(EVODB_SNAPSHOT_MNLIST_HASH) &&
!db->Exists(EVODB_BACKGROUND_MNLIST_HASH);
!db->Exists(EVODB_BACKGROUND_MNLIST_HASH) &&
!db->Exists(EVODB_REQUIRED_WORK_MNLISTS) &&
!db->Exists(EVODB_SNAPSHOT_EVO_SECTION);
if (already_promoted) m_default_identity = EvoDbIdentity::NORMAL;
return already_promoted;
}
Expand All @@ -213,6 +250,8 @@ bool CEvoDB::PromoteSnapshotMarkers(const uint256& expected_snapshot_tip)
batch.Erase(snapshot_key);
batch.Erase(EVODB_SNAPSHOT_MNLIST_HASH);
batch.Erase(EVODB_BACKGROUND_MNLIST_HASH);
EraseHistoricalMNListMarkers(*db, batch);
batch.Erase(EVODB_SNAPSHOT_EVO_SECTION);
batch.Erase(EVODB_DUAL_CHAINSTATE);
if (!db->WriteBatch(batch, /*fSync=*/true)) return false;
// The dual-chainstate run is over: the promoted state is the NORMAL
Expand All @@ -235,6 +274,8 @@ bool CEvoDB::DiscardSnapshotMarkers()
batch.Erase(std::make_pair(EVODB_BEST_BLOCK, uint8_t{1}));
batch.Erase(EVODB_SNAPSHOT_MNLIST_HASH);
batch.Erase(EVODB_BACKGROUND_MNLIST_HASH);
EraseHistoricalMNListMarkers(*db, batch);
batch.Erase(EVODB_SNAPSHOT_EVO_SECTION);
batch.Erase(EVODB_DUAL_CHAINSTATE);
if (!db->WriteBatch(batch, /*fSync=*/true)) return false;
// The snapshot chainstate is gone; transaction-less access must resolve
Expand Down
7 changes: 7 additions & 0 deletions src/evo/evodb.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ static const std::string EVODB_BEST_BLOCK = "b_b4";
static const std::string EVODB_DUAL_CHAINSTATE = "b_dcs";
static const std::string EVODB_SNAPSHOT_MNLIST_HASH = "b_dcs_mn";
static const std::string EVODB_BACKGROUND_MNLIST_HASH = "b_dcs_bg_mn";
static const std::string EVODB_REQUIRED_WORK_MNLISTS = "b_dcs_req_mn";
static const std::string EVODB_BACKGROUND_WORK_MNLIST_HASH = "b_dcs_bg_work_mn";
static const std::string EVODB_SNAPSHOT_EVO_SECTION = "b_dcs_evo";

enum class EvoDbIdentity {
NORMAL,
Expand Down Expand Up @@ -263,6 +266,10 @@ class CEvoDB
bool ReadSnapshotBaseMNListHash(uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(!cs);
void WriteBackgroundMNListHash(const uint256& block_hash, const uint256& mn_list_hash) EXCLUSIVE_LOCKS_REQUIRED(!cs);
bool ReadBackgroundMNListHash(uint256& block_hash, uint256& mn_list_hash) EXCLUSIVE_LOCKS_REQUIRED(!cs);
void WriteRequiredWorkMNListHashes(const std::vector<uint256>& block_hashes) EXCLUSIVE_LOCKS_REQUIRED(!cs);
bool ReadRequiredWorkMNListHashes(std::vector<uint256>& block_hashes) EXCLUSIVE_LOCKS_REQUIRED(!cs);
void WriteBackgroundWorkMNListHash(const uint256& block_hash, const uint256& mn_list_hash) EXCLUSIVE_LOCKS_REQUIRED(!cs);
bool ReadBackgroundWorkMNListHash(const uint256& block_hash, uint256& mn_list_hash) EXCLUSIVE_LOCKS_REQUIRED(!cs);

/**
* Atomically promote the surviving snapshot marker to the legacy NORMAL key
Expand Down
7 changes: 7 additions & 0 deletions src/evo/mnhftx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <llmq/signhash.h>
#include <node/blockstorage.h>
#include <shutdown.h>
#include <util/check.h>
#include <util/std23.h>

#include <chain.h>
Expand Down Expand Up @@ -382,6 +383,12 @@ void CMNHFManager::AddToCache(const Signals& signals, const CBlockIndex* const p
}
}

bool CMNHFManager::SeedSignals(const CBlockIndex* pindex, const Signals& signals)
{
if (!Assume(pindex != nullptr)) return false;
return m_evoDb.WriteDerived(std::make_pair(DB_SIGNALS_v2, pindex->GetBlockHash()), signals);
}
Comment thread
PastaPastaPasta marked this conversation as resolved.

void CMNHFManager::AddSignal(const CBlockIndex* const pindex, int bit)
{
auto signals = GetForBlock(pindex->pprev);
Expand Down
2 changes: 2 additions & 0 deletions src/evo/mnhftx.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ class CMNHFManager : public AbstractEHFManager
void AddSignal(const CBlockIndex* const pindex, int bit) EXCLUSIVE_LOCKS_REQUIRED(!cs_cache);

bool ForceSignalDBUpdate(const CBlockIndex* tip) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, !cs_cache);
/** Seed the signals at a block in the current EvoDB transaction. */
bool SeedSignals(const CBlockIndex* pindex, const Signals& signals) EXCLUSIVE_LOCKS_REQUIRED(!cs_cache);

private:
void AddToCache(const Signals& signals, const CBlockIndex* const pindex) EXCLUSIVE_LOCKS_REQUIRED(!cs_cache);
Expand Down
24 changes: 23 additions & 1 deletion src/evo/snapshot.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <crypto/common.h>
#include <evo/creditpool.h>
#include <evo/deterministicmns.h>
#include <evo/snapshot_types.h>
#include <llmq/commitment.h>
#include <llmq/params.h>
#include <llmq/snapshot.h>
Expand All @@ -30,7 +31,17 @@
#include <tuple>
#include <vector>

class CBlockIndex;
class CChainParams;
class ChainstateManager;
class CCbTx;
class CCreditPoolManager;
class CMNHFManager;

namespace llmq {
class CQuorumBlockProcessor;
class CQuorumSnapshotManager;
} // namespace llmq

namespace evo {

Expand Down Expand Up @@ -586,7 +597,7 @@ QuorumSnapshotEntry ReadRotationSnapshot(Stream& s, const Consensus::LLMQParams&
s >> entry.cycle_base_block_hash >> entry.work_block_hash >> entry.snapshot.mnSkipListMode;
// BuildQuorumSnapshot sizes this bitset to the complete work-block MN list,
// not to the quorum size. The exact historical-list size is chain-aware and
// is checked by the chain-aware validation layered on later in the series.
// is checked by ValidateEvoSnapshotAgainstChain.
const size_t bit_count{ReadBoundedCompactSize(s, EVO_SNAPSHOT_MAX_MNS, "rotation bitset")};
ReadFixedBitSet(s, entry.snapshot.activeQuorumMembers, bit_count);
const size_t skip_count{ReadBoundedCompactSize(s, EVO_SNAPSHOT_MAX_SKIPLIST_ENTRIES, "rotation skip list")};
Expand Down Expand Up @@ -722,6 +733,12 @@ void EvoSnapshot::Unserialize(Stream& s)
/** Single SHA256 of the canonical SER_DISK/CLIENT_VERSION encoding. */
uint256 GetEvoSnapshotHash(const EvoSnapshot& snapshot);

bool BuildEvoSnapshot(const CChainParams& chainparams, const ChainstateManager& chainman,
CDeterministicMNManager& dmnman,
const llmq::CQuorumBlockProcessor& qblockman, llmq::CQuorumSnapshotManager& qsnapman,
CCreditPoolManager& cpoolman, CMNHFManager& mnhfman, const CBlockIndex* base_index,
EvoSnapshot& snapshot, std::string& error) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);

struct QuorumReconstructionHeight {
Consensus::LLMQType llmq_type;
bool rotation;
Expand All @@ -737,6 +754,11 @@ std::vector<QuorumReconstructionHeight> EvoSnapshotReconstructionHeights(
bool ReconstructHistoricalMNLists(const EvoSnapshot& snapshot, std::map<uint256, CDeterministicMNList>& lists,
std::string& error, size_t max_records = EVO_SNAPSHOT_MAX_RECONSTRUCTION_RECORDS);

/** Validate all snapshot invariants requiring the block index or deployments. */
bool ValidateEvoSnapshotAgainstChain(const EvoSnapshot& snapshot, const ChainstateManager& chainman,
const CBlockIndex* base_index, std::string& error)
EXCLUSIVE_LOCKS_REQUIRED(::cs_main);

/** Pure CbTx checks over already-built snapshot content. */
bool VerifyEvoSnapshotCbTx(const EvoSnapshot& snapshot, const CCbTx& cbtx, std::string& error);

Expand Down
Loading
Loading