mirror of
https://github.com/Xahau/xahaud.git
synced 2026-10-22 11:47:09 +00:00
fix(rng): require UNLReport view for non-fallback entropy
This commit is contained in:
5 files changed
+45
-57
No files matched your search
@@ -1061,9 +1061,11 @@ class ConsensusExtensions_test : public beast::unit_test::suite
|
||||
auto const closeTime = NetClock::time_point{NetClock::duration{654}};
|
||||
auto const txSetHash = makeHash("explicit-final-nonzero-txset");
|
||||
auto const reveal = makeHash("explicit-final-nonzero-reveal");
|
||||
auto const viewLedger = makeUNLReportLedger(
|
||||
nonStandaloneEnv, std::vector<PublicKey>{publicKey});
|
||||
ConsensusExtensions revealCe{
|
||||
nonStandaloneEnv.app(), activeNoopJournal()};
|
||||
revealCe.cacheUNLReport(ledger);
|
||||
revealCe.cacheUNLReport(viewLedger);
|
||||
harvestCommitReveal(
|
||||
revealCe,
|
||||
nodeId,
|
||||
@@ -1163,7 +1165,6 @@ class ConsensusExtensions_test : public beast::unit_test::suite
|
||||
auto const ledger = env.app().getLedgerMaster().getClosedLedger();
|
||||
ce.onRoundStart(RCLCxLedger{ledger}, {});
|
||||
ce.setRngEnabledThisRound(true);
|
||||
BEAST_EXPECT(ce.belowValidatorQuorum());
|
||||
|
||||
CanonicalTXSet retriableTxs{makeHash("rng-zero-fallback-salt")};
|
||||
auto const seq = ledger->seq() + 1;
|
||||
@@ -1224,8 +1225,10 @@ class ConsensusExtensions_test : public beast::unit_test::suite
|
||||
auto const closeTime = NetClock::time_point{NetClock::duration{321}};
|
||||
auto const txSetHash = makeHash("prebuild-entropy-txset");
|
||||
auto const reveal = makeHash("prebuild-entropy-reveal");
|
||||
auto const viewLedger =
|
||||
makeUNLReportLedger(env, std::vector<PublicKey>{publicKey});
|
||||
ConsensusExtensions ce{env.app(), activeNoopJournal()};
|
||||
ce.cacheUNLReport(ledger);
|
||||
ce.cacheUNLReport(viewLedger);
|
||||
harvestCommitReveal(
|
||||
ce,
|
||||
nodeId,
|
||||
@@ -1241,7 +1244,6 @@ class ConsensusExtensions_test : public beast::unit_test::suite
|
||||
|
||||
auto const entropySetHash = ce.buildEntropySet(seq);
|
||||
BEAST_EXPECT(ce.isSidecarSet(entropySetHash));
|
||||
BEAST_EXPECT(!ce.belowValidatorQuorum());
|
||||
|
||||
CanonicalTXSet retriableTxs{makeHash("entropy-set-prebuild-salt")};
|
||||
ce.onPreBuild(retriableTxs, seq, txSetHash);
|
||||
@@ -2878,7 +2880,6 @@ class ConsensusExtensions_test : public beast::unit_test::suite
|
||||
BEAST_EXPECT(ce.exportSigConvergenceFailed());
|
||||
|
||||
ce.setEntropyFailed();
|
||||
BEAST_EXPECT(ce.belowValidatorQuorum());
|
||||
|
||||
ce.generateEntropySecret();
|
||||
BEAST_EXPECT(!ce.hasAnyReveals());
|
||||
|
||||
@@ -477,21 +477,6 @@ struct Peer
|
||||
return !pendingReveals_.empty();
|
||||
}
|
||||
|
||||
bool
|
||||
shouldZeroEntropy() const
|
||||
{
|
||||
if (entropyFailed_ || pendingReveals_.empty())
|
||||
return true;
|
||||
// Legacy validator-quorum-only predicate retained for old CSF
|
||||
// assertions. Current production tiering is handled in
|
||||
// finalizeRoundEntropy(), which can mint participant_aligned below
|
||||
// validator quorum when the advertised set reaches tier2Threshold().
|
||||
auto const threshold = unlNodes_.empty()
|
||||
? std::size_t{1}
|
||||
: calculateQuorumThreshold(unlNodes_.size());
|
||||
return pendingReveals_.size() < threshold;
|
||||
}
|
||||
|
||||
uint256
|
||||
buildCommitSet(Ledger::Seq seq)
|
||||
{
|
||||
|
||||
@@ -261,10 +261,10 @@ ConsensusExtensions::entropyGateThreshold() const
|
||||
// (0.8*effective), so this is the participant-alignment floor and
|
||||
// sub-quorum rounds reach injection; under heavy nUNL the band collapses
|
||||
// (tier2 >= quorum) and this is the 80% quorum, so only validator_quorum
|
||||
// survives. This governs
|
||||
// proceed-vs-fall-back ONLY — the selector still labels the agreed set's
|
||||
// tier from its participant count, so a node that proceeds here never mints
|
||||
// a different tier than its peers (divergent local views fall back).
|
||||
// survives. This governs proceed-vs-fall-back only. Non-fallback tier
|
||||
// labels are allowed only when the round view is anchored by UNLReport; the
|
||||
// trusted-fallback view is local configuration and selectEntropy() maps it
|
||||
// to consensus_fallback.
|
||||
return std::min(quorumThreshold(), tier2Threshold());
|
||||
}
|
||||
|
||||
@@ -416,17 +416,6 @@ ConsensusExtensions::hasAnyReveals() const
|
||||
return !pendingReveals_.empty();
|
||||
}
|
||||
|
||||
bool
|
||||
ConsensusExtensions::belowValidatorQuorum() const
|
||||
{
|
||||
if (entropyFailed_ || !entropySetMap_)
|
||||
return true;
|
||||
|
||||
auto const leafCount =
|
||||
std::distance(entropySetMap_->begin(), entropySetMap_->end());
|
||||
return leafCount == 0 || leafCount < quorumThreshold();
|
||||
}
|
||||
|
||||
ConsensusExtensions::EntropySelection
|
||||
ConsensusExtensions::selectEntropy(
|
||||
uint256 const& baseTxSetHash,
|
||||
@@ -454,11 +443,18 @@ ConsensusExtensions::selectEntropy(
|
||||
entropyTierValidatorQuorum,
|
||||
20};
|
||||
|
||||
// No agreed entropy set (round failed, or none was built) → fallback. We do
|
||||
// NOT fall back merely for being below the 80% quorum the way
|
||||
// belowValidatorQuorum() does: a sub-quorum-but-aligned set may still
|
||||
// qualify for participant_aligned (tier 2). The tier ladder below decides
|
||||
// from the agreed participant count.
|
||||
// Non-fallback entropy labels depend on validator-view thresholds. Without
|
||||
// an on-ledger UNLReport, that view is derived from local trusted config,
|
||||
// so two nodes can agree on the same entropy set but label it with
|
||||
// different tiers. A non-standalone node therefore mints only
|
||||
// consensus_fallback until the round view is ledger-anchored.
|
||||
if (!activeValidatorView()->fromUNLReport)
|
||||
return fallback();
|
||||
|
||||
// No agreed entropy set (round failed, or none was built) → fallback. A
|
||||
// sub-quorum-but-aligned set may still qualify for participant_aligned
|
||||
// (tier 2); the tier ladder below decides from the agreed participant
|
||||
// count.
|
||||
if (entropyFailed_ || !entropySetMap_)
|
||||
return fallback();
|
||||
|
||||
|
||||
@@ -189,13 +189,6 @@ public:
|
||||
bool
|
||||
hasAnyReveals() const;
|
||||
|
||||
/// True when the agreed entropy set would NOT reach validator_quorum
|
||||
/// (tier 3): absent/failed, or fewer leaves than quorumThreshold(). A
|
||||
/// tier-3 eligibility predicate only — it does NOT account for the tier-2
|
||||
/// floor, so never gate injection on it (selectEntropy() does the tiering).
|
||||
bool
|
||||
belowValidatorQuorum() const;
|
||||
|
||||
/// Result of the shared deterministic entropy selector: the digest to
|
||||
/// inject plus its tier/count labels. Both injection paths derive these
|
||||
/// identically from the AGREED entropySetMap_ so they cannot drift.
|
||||
@@ -209,7 +202,9 @@ public:
|
||||
/// Deterministically choose the entropy to inject for this round from the
|
||||
/// AGREED entropySetMap_ (never local pendingReveals_), labelled by agreed
|
||||
/// participant count: validator_quorum (>= quorumThreshold),
|
||||
/// participant_aligned (>= tier2Threshold) or consensus_fallback.
|
||||
/// participant_aligned (>= tier2Threshold) or consensus_fallback. In
|
||||
/// non-standalone mode, non-fallback labels require an UNLReport-backed
|
||||
/// active view; the trusted-fallback view is local config and mints Tier 1.
|
||||
/// baseTxSetHash is the BASE (pre-injection) tx set hash used for the
|
||||
/// fallback digest.
|
||||
EntropySelection
|
||||
@@ -342,8 +337,8 @@ public:
|
||||
clearRngState();
|
||||
|
||||
/// txSetHash is the BASE (pre-injection) consensus tx set hash —
|
||||
/// an input to the Tier 1 consensus_fallback digest. It must never be the hash
|
||||
/// of a set that could contain the entropy pseudo-tx itself.
|
||||
/// an input to the Tier 1 consensus_fallback digest. It must never be the
|
||||
/// hash of a set that could contain the entropy pseudo-tx itself.
|
||||
void
|
||||
onPreBuild(
|
||||
CanonicalTXSet& retriableTxs,
|
||||
|
||||
@@ -173,8 +173,8 @@ is the safe degradation path, not a consensus failure.
|
||||
|
||||
Examples with six active validators, validator_quorum threshold five, and
|
||||
participant_aligned threshold four (six is the smallest view with a non-empty
|
||||
Tier 2 band — at five validators quorum and participant_aligned coincide at
|
||||
four, leaving no band):
|
||||
Tier 2 band and non-zero tolerated Byzantine count; at five validators quorum
|
||||
and participant_aligned coincide at four, leaving no band):
|
||||
|
||||
- Five honest validators align on one entropy hash and one validator advertises
|
||||
a bogus hash: proceed with validator_quorum entropy for the honest quorum.
|
||||
@@ -194,11 +194,22 @@ seq)` — and labeled with `EntropyTier = consensus_fallback` and
|
||||
`min_tier`/`min_count` arguments to `dice()`/`random()`: a hook that demands
|
||||
validator-tier entropy fails closed with `TOO_LITTLE_ENTROPY` on fallback
|
||||
ledgers, while a hook that opts into fallback-grade randomness must do so
|
||||
explicitly at the call site. The fallback digest derives from the BASE
|
||||
(pre-injection) tx set hash to avoid circularity, and entropy pseudo-tx
|
||||
deduplication is value-based: if an explicit-final synthetic set already
|
||||
contains the exact pseudo-tx, injection skips it; a present-but-different
|
||||
pseudo-tx is logged as a determinism violation and left in the agreed set.
|
||||
explicitly at the call site. Valid `min_tier` values are the stored entropy
|
||||
tiers 1..3; `min_count` must fit the on-ledger `EntropyCount` UINT16 field.
|
||||
Invalid requirements return `INVALID_ARGUMENT`, while valid-but-unmet
|
||||
requirements return `TOO_LITTLE_ENTROPY`.
|
||||
|
||||
Open-ledger hook execution is provisional. During speculative open-ledger
|
||||
execution, `dice()`/`random()` can only use the previous ledger's finalized
|
||||
entropy; final buildLCL execution sees the current ledger's entropy pseudo-tx
|
||||
after it updates the SLE. Hooks that need final entropy must treat open-ledger
|
||||
RNG results as previews.
|
||||
|
||||
The fallback digest derives from the BASE (pre-injection) tx set hash to avoid
|
||||
circularity, and entropy pseudo-tx deduplication is value-based: if an
|
||||
explicit-final synthetic set already contains the exact pseudo-tx, injection
|
||||
skips it; a present-but-different pseudo-tx is logged as a determinism
|
||||
violation and left in the agreed set.
|
||||
|
||||
## Sidecar Convergence Rules
|
||||
|
||||
|
||||
Reference in new issue
Block a user