Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
1aed37b
feat: add the evo snapshot v3 canonical bounded codec and context-fre…
PastaPastaPasta Aug 13, 2026
67b4ae9
fix: build the ReadFixedBitSet trailing-bits mask without an implicit…
PastaPastaPasta Aug 13, 2026
5bce0fc
test: cover the bounded CRangesSet unserializer
PastaPastaPasta Aug 13, 2026
57dc6b1
fix: allow partial evo snapshot quorum history
PastaPastaPasta Aug 13, 2026
fd07f83
fix: reject full-domain ranges during deserialization
PastaPastaPasta Aug 13, 2026
484fcec
fix: reject noncanonical MNHF signal order during deserialization
PastaPastaPasta Aug 20, 2026
5b9e646
fix: bound commitment bitsets by a format ceiling instead of static L…
PastaPastaPasta Aug 20, 2026
138387f
fix: validate credit-pool amounts and MNHF signal semantics context-free
PastaPastaPasta Aug 20, 2026
18515f4
fix: replace previous contents when deserializing an evo snapshot
PastaPastaPasta Aug 20, 2026
a72328e
fix: bound same-prefix proTxHash runs against HAMT collision blowup
PastaPastaPasta Aug 20, 2026
2de4d28
fix: bind the CbTx height in VerifyEvoSnapshotCbTx
PastaPastaPasta Aug 20, 2026
6041092
fix: replace per-quorum contents when deserializing CQuorumSnapshotData
PastaPastaPasta Aug 20, 2026
d5ea6d5
fix: bound cumulative historical MN-list reconstruction work
PastaPastaPasta Aug 20, 2026
9a20e94
refactor: drop the C prefix from the new evo snapshot types
PastaPastaPasta Aug 28, 2026
0705557
refactor: give every evo snapshot collection one explicit canonical c…
PastaPastaPasta Aug 28, 2026
039ce9e
build: list evo/snapshot.cpp where it is actually used
PastaPastaPasta Aug 28, 2026
48effe4
fix: enforce the decoder's historical operation budget during validation
PastaPastaPasta Aug 28, 2026
4810d04
test: exercise evo per-quorum replacement for every cleared vector
PastaPastaPasta Aug 28, 2026
155ae33
test: name the noncanonical CRangesSet case for its actual defect
PastaPastaPasta Aug 28, 2026
303e5e5
fix: enforce canonical evo snapshot encodings and bounds
PastaPastaPasta Sep 8, 2026
1464506
fix: validate evo snapshot IDs and canonical BLS encodings
PastaPastaPasta Sep 9, 2026
7ca7423
fix: apply evo snapshot diffs atomically and pin lazy BLS and update-…
PastaPastaPasta Sep 11, 2026
5fff4fb
fix: reject revisited blocks and counter growth in the historical dif…
PastaPastaPasta Sep 11, 2026
913fc7b
fix: bound evo snapshot MNHF signal count by VERSIONBITS_NUM_BITS
UdjinM6 Sep 13, 2026
fa15a42
fix: check reconstructed snapshot list insertion
PastaPastaPasta Sep 13, 2026
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
2 changes: 2 additions & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ BITCOIN_CORE_H = \
evo/providertx_service.h \
evo/simplifiedmns.h \
evo/smldiff.h \
evo/snapshot.h \
evo/specialtx.h \
evo/specialtx_filter.h \
evo/specialtxman.h \
Expand Down Expand Up @@ -551,6 +552,7 @@ libbitcoin_node_a_SOURCES = \
evo/providertx_service.cpp \
evo/simplifiedmns.cpp \
evo/smldiff.cpp \
evo/snapshot.cpp \
evo/specialtx.cpp \
evo/specialtx_filter.cpp \
evo/specialtxman.cpp \
Expand Down
1 change: 1 addition & 0 deletions src/Makefile.test.include
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ BITCOIN_TESTS =\
test/evo_mnhf_tests.cpp \
test/evo_netinfo_tests.cpp \
test/evo_simplifiedmns_tests.cpp \
test/evo_snapshot_tests.cpp \
test/evo_trivialvalidation.cpp \
test/evo_utils_tests.cpp \
test/flatfile_tests.cpp \
Expand Down
4 changes: 2 additions & 2 deletions src/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -882,11 +882,11 @@ class CRegTestParams : public CChainParams {
m_assumeutxo_data = MapAssumeutxo{
{
110,
{AssumeutxoHash{uint256S("0x9b2a277a3e3b979f1a539d57e949495d7f8247312dbc32bce6619128c192b44b")}, 110},
{AssumeutxoHash{uint256S("0x9b2a277a3e3b979f1a539d57e949495d7f8247312dbc32bce6619128c192b44b")}, EvoSnapshotHash{uint256{}}, 110},
},
{
200,
{AssumeutxoHash{uint256S("0x8a5bdd92252fc6b24663244bbe958c947bb036dc1f94ccd15439f48d8d1cb4e3")}, 200},
{AssumeutxoHash{uint256S("0x8a5bdd92252fc6b24663244bbe958c947bb036dc1f94ccd15439f48d8d1cb4e3")}, EvoSnapshotHash{uint256{}}, 200},
},
};

Expand Down
7 changes: 7 additions & 0 deletions src/chainparams.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ struct AssumeutxoHash : public BaseHash<uint256> {
explicit AssumeutxoHash(const uint256& hash) : BaseHash(hash) {}
};

struct EvoSnapshotHash : public BaseHash<uint256> {
explicit EvoSnapshotHash(const uint256& hash) : BaseHash(hash) {}
};

/**
* Holds configuration for use during UTXO snapshot load and validation. The contents
* here are security critical, since they dictate which UTXO snapshots are recognized
Expand All @@ -43,6 +47,9 @@ struct AssumeutxoData {
//! The expected hash of the deserialized UTXO set.
const AssumeutxoHash hash_serialized;

//! The expected single-SHA256 hash of the canonical Dash evo section.
const EvoSnapshotHash evo_hash;

//! Used to populate the nChainTx value, which is used during BlockManager::LoadBlockIndex().
//!
//! We need to hardcode the value here because this is computed cumulatively using block data,
Expand Down
39 changes: 39 additions & 0 deletions src/evo/deterministicmns.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,45 @@ void CDeterministicMNList::ApplyDiff(gsl::not_null<const CBlockIndex*> pindex, c
}
}

void CDeterministicMNList::ApplyDiffForSnapshot(const uint256& block_hash, int height,
uint32_t total_registered_count,
const CDeterministicMNListDiff& diff)
{
if (height < 0) throw std::runtime_error("negative historical MN-list height");
blockHash = block_hash;
nHeight = height;

for (const auto& id : diff.removedMns) {
auto dmn = GetMNByInternalId(id);
if (!dmn) throw std::runtime_error(strprintf("%s: can't find a removed masternode, id=%d", __func__, id));
RemoveMN(dmn->proTxHash);
}
// A historical diff spans many blocks, so surviving MNs can have exchanged
// a unique property (an address, an operator key) between the endpoints,
// and a new registration can hold an address an updated MN gave up.
// Sequential UpdateMN() would reject the first claimant as a duplicate:
// release every updated MN's old properties before any new state claims.
std::vector<CDeterministicMNCPtr> updated;
updated.reserve(diff.updatedMNs.size());
for (const auto& [id, state_diff] : diff.updatedMNs) {
auto dmn = GetMNByInternalId(id);
if (!dmn) throw std::runtime_error(strprintf("%s: can't find an updated masternode, id=%d", __func__, id));
auto new_state = std::make_shared<CDeterministicMNState>(*dmn->pdmnState);
state_diff.ApplyToState(*new_state);
auto new_dmn = std::make_shared<CDeterministicMN>(*dmn);
new_dmn->pdmnState = std::move(new_state);
RemoveMN(dmn->proTxHash);
updated.push_back(std::move(new_dmn));
}
for (const auto& dmn : updated) {
AddMN(dmn, /*fBumpTotalCount=*/false);
}
for (const auto& dmn : diff.addedMNs) {
AddMN(dmn, /*fBumpTotalCount=*/false);
}
nTotalRegisteredCount = total_registered_count;
}

void CDeterministicMNList::AddMN(const CDeterministicMNCPtr& dmn, bool fBumpTotalCount)
{
assert(dmn != nullptr);
Expand Down
13 changes: 11 additions & 2 deletions src/evo/deterministicmns.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ class CDeterministicMN
}
}

[[nodiscard]] bool HasInternalId() const { return internalId != std::numeric_limits<uint64_t>::max(); }
[[nodiscard]] uint64_t GetInternalId() const;

[[nodiscard]] CSimplifiedMNListEntry to_sml_entry() const;
Expand Down Expand Up @@ -339,6 +340,8 @@ class CDeterministicMNList
assert(nHeight >= 0);
return nHeight;
}
/** Snapshot hashing also covers the pre-DIP3 default list (height -1). */
[[nodiscard]] int GetHeightForSnapshotCodec() const noexcept { return nHeight; }
void SetHeight(int _height)
{
assert(_height >= 0);
Expand Down Expand Up @@ -423,6 +426,11 @@ class CDeterministicMNList
void ApplyDiff(gsl::not_null<const CBlockIndex*> pindex, const CDeterministicMNListDiff& diff)
EXCLUSIVE_LOCKS_REQUIRED(!m_cached_sml_mutex);

/** Apply a snapshot-local historical diff without dereferencing block data. */
void ApplyDiffForSnapshot(const uint256& block_hash, int height, uint32_t total_registered_count,
const CDeterministicMNListDiff& diff)
EXCLUSIVE_LOCKS_REQUIRED(!m_cached_sml_mutex);

void AddMN(const CDeterministicMNCPtr& dmn, bool fBumpTotalCount = true) EXCLUSIVE_LOCKS_REQUIRED(!m_cached_sml_mutex);
void UpdateMN(const CDeterministicMN& oldDmn, const std::shared_ptr<const CDeterministicMNState>& pdmnState)
EXCLUSIVE_LOCKS_REQUIRED(!m_cached_sml_mutex);
Expand Down Expand Up @@ -625,8 +633,9 @@ class CDeterministicMNListDiff
int nHeight{-1}; //memory only

std::vector<CDeterministicMNCPtr> addedMNs;
// keys are all relating to the internalId of MNs
std::unordered_map<uint64_t, CDeterministicMNStateDiff> updatedMNs;
// keys are all relating to the internalId of MNs. Keys can come from
// untrusted input (evo snapshots), so the hash is salted against flooding.
std::unordered_map<uint64_t, CDeterministicMNStateDiff, StaticSaltedHasher> updatedMNs;
std::set<uint64_t> removedMns;

template<typename Stream>
Expand Down
Loading
Loading