Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
858f90e
fix: idle DKG phase threads while snapshot background validation is i…
PastaPastaPasta Nov 4, 2026
0c4b7db
refactor(bls): resolve the implicit BLS scheme per thread
PastaPastaPasta Nov 6, 2026
53c58d8
fix(validation): keep a non-active chainstate's BLS scheme on its thread
PastaPastaPasta Nov 6, 2026
5dc873e
fix(validation): establish the BLS scheme when the active chainstate …
PastaPastaPasta Nov 6, 2026
409e1c1
feat(llmq): report whether a mined-commitment scan covered its window
PastaPastaPasta Nov 6, 2026
f19536d
feat(interfaces): look up a Platform quorum at its signed Core height
PastaPastaPasta Nov 6, 2026
9ff9eaf
fix(evo): pass snapshot-data and mismatch errors through generic catches
PastaPastaPasta Oct 15, 2026
0a61891
refactor(evo): add EvoDB view tokens and scopes
PastaPastaPasta Oct 15, 2026
29dbd15
feat(evo): store snapshot-identity state in a generation namespace wi…
PastaPastaPasta Oct 15, 2026
40afe3c
fix(evo): partition Evo-derived caches by view
PastaPastaPasta Oct 15, 2026
ce52ee4
fix(evo): bind non-active paths and dispatcher boundaries explicitly
PastaPastaPasta Oct 15, 2026
117ec48
feat(evo): reject unbound Evo reads during dual operation
PastaPastaPasta Oct 15, 2026
cbbea8b
fix(instantsend): skip only the islocks whose quorum is below the sna…
PastaPastaPasta Oct 31, 2026
689ba7d
fix(llmq): treat short scans of types not enabled up to the snapshot …
PastaPastaPasta Oct 31, 2026
d6b41d7
fix(interfaces): raise the Evo-unavailable warning once per operation…
PastaPastaPasta Oct 31, 2026
3434059
fix(node): keep an earlier invalid snapshot dir when finishing a reje…
PastaPastaPasta Oct 31, 2026
3ba1d57
fix(validation): stop reading a rejected snapshot when its rejection …
PastaPastaPasta Oct 31, 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
39 changes: 39 additions & 0 deletions doc/release-notes-7887.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
Updated RPCs
------------

- An RPC that fails with an error because this node cannot read the
masternode, quorum or other Evo state it needs, for example because the
block data it is derived from was pruned, now returns error code
`RPC_INTERNAL_ERROR` (-32603) instead of `RPC_MISC_ERROR` (-1). The request
was not at fault. The package RPCs report such a failure as described
below. (#7887)

- `testmempoolaccept` with more than one transaction reports such a failure as
the `package-error` `evo-local-unavailable`, and `submitpackage` fails with
that reason. (#7887)

Other changes
-------------

- A block this node has stored but cannot read back while deriving the credit
pool or the EHF signals no longer makes the block being validated invalid.
The node shuts down with a "Failed to read block" error instead, as it does
when it cannot read a block it is connecting. (#7887)

- On a node without an assumeutxo snapshot, a warning about Evo state that
cannot be derived because block data was pruned now says so and suggests
restarting with `-reindex`. Startup still fails when the EHF signals at the
tip cannot be rebuilt. (#7887)

- On a pruned node, a block whose masternode, quorum or other Evo state
depends on block data that was pruned is no longer marked invalid, which
left the node on a local fork until `reconsiderblock`. The node now stops
advancing at that block, logs an error and shows a warning that block data
was pruned; restart with `-reindex` to download it again. (#7887)

- Developers: on regtest, every Evo database access must now be bound to a
chainstate's view (the hidden `-checkevobinding` option, on by default
there). A code path that reads Evo state without a binding fails with an
internal error, a dropped task or a stalled chainstate instead of reading
the default view. Local regtest networks run with it unless started with
`-checkevobinding=0`. (#7887)
5 changes: 5 additions & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -235,10 +235,13 @@ BITCOIN_CORE_H = \
evo/simplifiedmns.h \
evo/smldiff.h \
evo/snapshot.h \
evo/snapshot_config.h \
evo/snapshot_types.h \
evo/specialtx.h \
evo/specialtx_filter.h \
evo/specialtxman.h \
evo/types.h \
evo/viewcache.h \
external_signer.h \
flat-database.h \
flatfile.h \
Expand Down Expand Up @@ -570,6 +573,7 @@ libbitcoin_node_a_SOURCES = \
evo/simplifiedmns.cpp \
evo/smldiff.cpp \
evo/snapshot.cpp \
evo/snapshot_config.cpp \
evo/specialtx.cpp \
evo/specialtx_filter.cpp \
evo/specialtxman.cpp \
Expand Down Expand Up @@ -1312,6 +1316,7 @@ libdashkernel_la_SOURCES = \
evo/providertx_util.cpp \
evo/simplifiedmns.cpp \
evo/smldiff.cpp \
evo/snapshot_config.cpp \
evo/specialtx.cpp \
evo/specialtx_filter.cpp \
evo/specialtxman.cpp \
Expand Down
12 changes: 12 additions & 0 deletions src/active/dkgsessionhandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
#include <util/time.h>
#include <validation.h>

#include <utility>

namespace llmq {
ActiveDKGSessionHandler::ActiveDKGSessionHandler(
CBLSWorker& bls_worker, CDeterministicMNManager& dmnman, CMasternodeMetaMan& mn_metaman,
Expand Down Expand Up @@ -114,8 +116,18 @@ void ActiveDKGSessionHandler::WaitForNextPhase(std::optional<QuorumPhase> curPha
{
LogPrint(BCLog::LLMQ_DKG, "ActiveDKGSessionHandler::%s -- %s qi[%d] - starting, curPhase=%d, nextPhase=%d\n", __func__, params.name, quorumIndex, curPhase.has_value() ? std23::to_underlying(*curPhase) : -1, std23::to_underlying(nextPhase));

bool logged_snapshot_wait{false};
while (true) {
if (m_chainman.IsSnapshotActiveAndUnvalidated()) {
if (!curPhase.has_value() && !stopRequested) {
// No round can start before background validation completes, so there is nothing to abort
if (!std::exchange(logged_snapshot_wait, true)) {
LogPrint(BCLog::LLMQ_DKG, "%s -- %s qi[%d] - waiting for snapshot background validation\n",
__func__, params.name, quorumIndex);
}
UninterruptibleSleep(std::chrono::milliseconds{100});
continue;
}
LogPrint(BCLog::LLMQ_DKG, "ActiveDKGSessionHandler::%s -- %s qi[%d] - aborting because snapshot background validation is incomplete\n", __func__, params.name, quorumIndex);
throw AbortPhaseException();
}
Expand Down
60 changes: 51 additions & 9 deletions src/bls/bls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,40 @@
#include <cstring>

namespace bls {
std::atomic<bool> bls_legacy_scheme = std::atomic<bool>(true);
std::atomic<bool> bls_legacy_scheme = std::atomic<bool>(true);

namespace {
//! Trivially destructible, like util/threadnames.cpp's thread-local name.
thread_local std::optional<bool> g_thread_legacy_scheme;
} // namespace

bool EffectiveLegacyScheme() { return g_thread_legacy_scheme ? *g_thread_legacy_scheme : bls_legacy_scheme.load(); }

void SetEffectiveLegacyScheme(bool legacy)
{
if (g_thread_legacy_scheme) {
g_thread_legacy_scheme = legacy;
} else {
bls_legacy_scheme.store(legacy);
}
}

ScopedLegacySchemeOverride::ScopedLegacySchemeOverride(bool legacy) noexcept :
m_saved{g_thread_legacy_scheme}
{
g_thread_legacy_scheme = legacy;
}

ScopedLegacySchemeOverride::~ScopedLegacySchemeOverride() noexcept
{
if (!m_committed || !m_saved) {
g_thread_legacy_scheme = m_saved;
}
}

bool ScopedLegacySchemeOverride::Active() noexcept { return g_thread_legacy_scheme.has_value(); }
} // namespace bls

static const std::unique_ptr<bls::CoreMPL> pSchemeLegacy{std::make_unique<bls::LegacySchemeMPL>()};
static const std::unique_ptr<bls::CoreMPL> pScheme(std::make_unique<bls::BasicSchemeMPL>());

Expand Down Expand Up @@ -145,7 +176,7 @@ void CBLSPublicKey::AggregateInsecure(const CBLSPublicKey& o)
{
assert(IsValid() && o.IsValid());
try {
impl = Scheme(bls::bls_legacy_scheme.load())->Aggregate({impl, o.impl});
impl = Scheme(bls::EffectiveLegacyScheme())->Aggregate({impl, o.impl});
} catch (...) {
fValid = false;
}
Expand All @@ -166,7 +197,7 @@ CBLSPublicKey CBLSPublicKey::AggregateInsecure(Span<CBLSPublicKey> pks)

CBLSPublicKey ret;
try {
ret.impl = Scheme(bls::bls_legacy_scheme.load())->Aggregate(vecPublicKeys);
ret.impl = Scheme(bls::EffectiveLegacyScheme())->Aggregate(vecPublicKeys);
ret.fValid = true;
} catch (...) {
ret.fValid = false;
Expand Down Expand Up @@ -230,7 +261,7 @@ void CBLSSignature::AggregateInsecure(const CBLSSignature& o)
{
assert(IsValid() && o.IsValid());
try {
impl = Scheme(bls::bls_legacy_scheme.load())->Aggregate({impl, o.impl});
impl = Scheme(bls::EffectiveLegacyScheme())->Aggregate({impl, o.impl});
} catch (...) {
fValid = false;
}
Expand All @@ -251,7 +282,7 @@ CBLSSignature CBLSSignature::AggregateInsecure(Span<CBLSSignature> sigs)

CBLSSignature ret;
try {
ret.impl = Scheme(bls::bls_legacy_scheme.load())->Aggregate(v);
ret.impl = Scheme(bls::EffectiveLegacyScheme())->Aggregate(v);
ret.fValid = true;
} catch (...) {
ret.fValid = false;
Expand Down Expand Up @@ -283,7 +314,7 @@ CBLSSignature CBLSSignature::AggregateSecure(Span<CBLSSignature> sigs,

CBLSSignature ret;
try {
ret.impl = Scheme(bls::bls_legacy_scheme.load())->AggregateSecure(vecPublicKeys, vecSignatures, bls::Bytes(hash.begin(), hash.size()));
ret.impl = Scheme(bls::EffectiveLegacyScheme())->AggregateSecure(vecPublicKeys, vecSignatures, bls::Bytes(hash.begin(), hash.size()));
ret.fValid = true;
} catch (...) {
ret.fValid = false;
Expand Down Expand Up @@ -322,10 +353,16 @@ bool CBLSSignature::VerifyInsecure(const CBLSPublicKey& pubKey, const uint256& h

bool CBLSSignature::VerifyInsecure(const CBLSPublicKey& pubKey, const uint256& hash) const
{
return VerifyInsecure(pubKey, hash, bls::bls_legacy_scheme.load());
return VerifyInsecure(pubKey, hash, bls::EffectiveLegacyScheme());
}

bool CBLSSignature::VerifyInsecureAggregated(Span<CBLSPublicKey> pubKeys, Span<uint256> hashes) const
{
return VerifyInsecureAggregated(pubKeys, hashes, bls::EffectiveLegacyScheme());
}

bool CBLSSignature::VerifyInsecureAggregated(Span<CBLSPublicKey> pubKeys, Span<uint256> hashes,
const bool specificLegacyScheme) const
{
if (!IsValid()) {
return false;
Expand All @@ -346,13 +383,18 @@ bool CBLSSignature::VerifyInsecureAggregated(Span<CBLSPublicKey> pubKeys, Span<u
}

try {
return Scheme(bls::bls_legacy_scheme.load())->AggregateVerify(pubKeyVec, hashes2, impl);
return Scheme(specificLegacyScheme)->AggregateVerify(pubKeyVec, hashes2, impl);
} catch (...) {
return false;
}
}

bool CBLSSignature::VerifySecureAggregated(Span<CBLSPublicKey> pks, const uint256& hash) const
{
return VerifySecureAggregated(pks, hash, bls::EffectiveLegacyScheme());
}

bool CBLSSignature::VerifySecureAggregated(Span<CBLSPublicKey> pks, const uint256& hash, const bool specificLegacyScheme) const
{
if (pks.empty()) {
return false;
Expand All @@ -365,7 +407,7 @@ bool CBLSSignature::VerifySecureAggregated(Span<CBLSPublicKey> pks, const uint25
}

try {
return Scheme(bls::bls_legacy_scheme.load())->VerifySecure(vecPublicKeys, impl, bls::Bytes(hash.begin(), hash.size()));
return Scheme(specificLegacyScheme)->VerifySecure(vecPublicKeys, impl, bls::Bytes(hash.begin(), hash.size()));
} catch (...) {
return false;
}
Expand Down
52 changes: 46 additions & 6 deletions src/bls/bls.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,48 @@
#include <array>
#include <atomic>
#include <mutex>
#include <optional>
#include <ranges>
#include <type_traits>

namespace bls {
extern std::atomic<bool> bls_legacy_scheme;
}
//! Process-wide default scheme of BLS operations that are not given one. It
//! follows the active chainstate's tip only: other threads read it outside
//! cs_main, so work for any other chain context (a background chainstate, a dry
//! run) sets a ScopedLegacySchemeOverride instead.
extern std::atomic<bool> bls_legacy_scheme;

//! The scheme of BLS operations on this thread that are not given one: the
//! innermost ScopedLegacySchemeOverride if one is set, else bls_legacy_scheme.
bool EffectiveLegacyScheme();

//! Switches EffectiveLegacyScheme(): this thread's override if one is set,
//! else bls_legacy_scheme.
void SetEffectiveLegacyScheme(bool legacy);

//! Sets EffectiveLegacyScheme() on this thread only, for the object's lifetime.
//! Work handed to another thread does not see it and must capture the scheme.
class ScopedLegacySchemeOverride
{
public:
explicit ScopedLegacySchemeOverride(bool legacy) noexcept;
~ScopedLegacySchemeOverride() noexcept;

ScopedLegacySchemeOverride(const ScopedLegacySchemeOverride&) = delete;
ScopedLegacySchemeOverride& operator=(const ScopedLegacySchemeOverride&) = delete;

//! Leaves the scheme in force at scope exit to the enclosing override. The
//! outermost override always ends with its scope.
void Commit() noexcept { m_committed = true; }

//! Whether an override is set on this thread.
static bool Active() noexcept;

private:
const std::optional<bool> m_saved;
bool m_committed{false};
};
} // namespace bls

// reversed BLS12-381
constexpr int BLS_CURVE_ID_SIZE{32};
Expand Down Expand Up @@ -181,7 +217,7 @@ class CBLSWrapper
template <typename Stream>
inline void Serialize(Stream& s) const
{
Serialize(s, bls::bls_legacy_scheme.load());
Serialize(s, bls::EffectiveLegacyScheme());
}

template <typename Stream>
Expand Down Expand Up @@ -209,7 +245,7 @@ class CBLSWrapper
template <typename Stream>
inline void Unserialize(Stream& s)
{
Unserialize(s, bls::bls_legacy_scheme.load());
Unserialize(s, bls::EffectiveLegacyScheme());
}

inline bool CheckMalleable(Span<uint8_t> vecBytes, const bool specificLegacyScheme) const
Expand All @@ -232,7 +268,7 @@ class CBLSWrapper

inline std::string ToString() const
{
return ToString(bls::bls_legacy_scheme.load());
return ToString(bls::EffectiveLegacyScheme());
}
};

Expand Down Expand Up @@ -369,8 +405,12 @@ class CBLSSignature : public CBLSWrapper<bls::G2Element, BLS_CURVE_SIG_SIZE, CBL
void MulInsecure(const CBLSSecretKey& factor);
[[nodiscard]] bool VerifyInsecure(const CBLSPublicKey& pubKey, const uint256& hash, const bool specificLegacyScheme) const;
[[nodiscard]] bool VerifyInsecure(const CBLSPublicKey& pubKey, const uint256& hash) const;
[[nodiscard]] bool VerifyInsecureAggregated(Span<CBLSPublicKey> pubKeys, Span<uint256> hashes,
const bool specificLegacyScheme) const;
[[nodiscard]] bool VerifyInsecureAggregated(Span<CBLSPublicKey> pubKeys, Span<uint256> hashes) const;

[[nodiscard]] bool VerifySecureAggregated(Span<CBLSPublicKey> pks, const uint256& hash,
const bool specificLegacyScheme) const;
[[nodiscard]] bool VerifySecureAggregated(Span<CBLSPublicKey> pks, const uint256& hash) const;

bool Recover(Span<CBLSSignature> sigs, Span<CBLSId> ids);
Expand Down Expand Up @@ -414,7 +454,7 @@ class CBLSLazyWrapper

public:
CBLSLazyWrapper() :
bufLegacyScheme(bls::bls_legacy_scheme.load())
bufLegacyScheme(bls::EffectiveLegacyScheme())
{}

explicit CBLSLazyWrapper(const CBLSLazyWrapper& r)
Expand Down
11 changes: 7 additions & 4 deletions src/bls/bls_batchverifier.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ class CBLSBatchVerifier
bool secureVerification;
bool perMessageFallback;
size_t subBatchSize;
//! Taken from the constructing thread, so a batch verifies under one scheme wherever Verify() runs
const bool legacyScheme;

MessageMap messages;
MessagesBySourceMap messagesBySource;
Expand All @@ -61,7 +63,8 @@ class CBLSBatchVerifier
CBLSBatchVerifier(bool _secureVerification, bool _perMessageFallback, size_t _subBatchSize = 0) :
secureVerification(_secureVerification),
perMessageFallback(_perMessageFallback),
subBatchSize(_subBatchSize)
subBatchSize(_subBatchSize),
legacyScheme(bls::EffectiveLegacyScheme())
{
}

Expand Down Expand Up @@ -135,7 +138,7 @@ class CBLSBatchVerifier
}

const auto& msg = msgIt->second;
if (!msg.sig.VerifyInsecure(msg.pubKey, msg.msgHash)) {
if (!msg.sig.VerifyInsecure(msg.pubKey, msg.msgHash, legacyScheme)) {
badMessages.emplace(msg.msgId);
}
}
Expand Down Expand Up @@ -201,7 +204,7 @@ class CBLSBatchVerifier
}

CBLSSignature aggSig = CBLSSignature::AggregateInsecure(sigsToAggregate);
return aggSig.VerifyInsecureAggregated(pubKeys, msgHashes);
return aggSig.VerifyInsecureAggregated(pubKeys, msgHashes, legacyScheme);
}

bool VerifyBatchSecure(std::map<uint256, std::vector<MessageMapIterator>>& byMessageHash)
Expand Down Expand Up @@ -251,7 +254,7 @@ class CBLSBatchVerifier
assert(!msgHashes.empty());

CBLSSignature aggSig = CBLSSignature::AggregateInsecure(sigsToAggregate);
return aggSig.VerifyInsecureAggregated(pubKeys, msgHashes);
return aggSig.VerifyInsecureAggregated(pubKeys, msgHashes, legacyScheme);
}
};

Expand Down
Loading
Loading