mirror of
https://github.com/Xahau/xahaud.git
synced 2026-07-27 00:50:20 +00:00
feat: record consensus entropy denominator
This commit is contained in:
@@ -26,6 +26,7 @@
|
||||
#define sfLedgerFixType ((1U << 16U) + 22U)
|
||||
#define sfHookExportCount ((1U << 16U) + 98U)
|
||||
#define sfEntropyCount ((1U << 16U) + 99U)
|
||||
#define sfEntropyDenominator ((1U << 16U) + 100U)
|
||||
#define sfNetworkID ((2U << 16U) + 1U)
|
||||
#define sfFlags ((2U << 16U) + 2U)
|
||||
#define sfSourceTag ((2U << 16U) + 3U)
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#define sfLedgerFixType ((1U << 16U) + 22U)
|
||||
#define sfHookExportCount ((1U << 16U) + 98U)
|
||||
#define sfEntropyCount ((1U << 16U) + 99U)
|
||||
#define sfEntropyDenominator ((1U << 16U) + 100U)
|
||||
#define sfNetworkID ((2U << 16U) + 1U)
|
||||
#define sfFlags ((2U << 16U) + 2U)
|
||||
#define sfSourceTag ((2U << 16U) + 3U)
|
||||
|
||||
@@ -9,9 +9,12 @@ namespace ripple {
|
||||
/// on the ttCONSENSUS_ENTROPY pseudo-transaction and the ConsensusEntropy
|
||||
/// ledger entry.
|
||||
///
|
||||
/// EntropyCount says how many validators contributed; EntropyTier says which
|
||||
/// gate the result passed. Values are strength-ordered so consumers can gate
|
||||
/// with a numeric comparison (tier >= required).
|
||||
/// EntropyCount says how many validators contributed; EntropyDenominator says
|
||||
/// how many active validators were in the ledger-anchored view for that
|
||||
/// non-fallback result; EntropyTier says which gate the result passed. Fallback
|
||||
/// entropy carries count=0/denominator=0 because no validator-derived
|
||||
/// denominator was accepted. Tier values are strength-ordered so consumers can
|
||||
/// gate with a numeric comparison (tier >= required).
|
||||
///
|
||||
/// RESIDUAL BIAS — applies to EVERY tier, including participant_aligned and
|
||||
/// validator_quorum, not just the fallback. This is a commit/reveal scheme: a
|
||||
|
||||
@@ -232,6 +232,7 @@ LEDGER_ENTRY(ltURI_TOKEN, 0x0055, URIToken, uri_token, ({
|
||||
LEDGER_ENTRY_DUPLICATE(ltCONSENSUS_ENTROPY, 0x0058, ConsensusEntropy, consensus_entropy, ({
|
||||
{sfDigest, soeREQUIRED},
|
||||
{sfEntropyCount, soeREQUIRED},
|
||||
{sfEntropyDenominator, soeREQUIRED},
|
||||
{sfEntropyTier, soeREQUIRED},
|
||||
{sfLedgerSequence, soeREQUIRED},
|
||||
{sfPreviousTxnID, soeREQUIRED},
|
||||
|
||||
@@ -63,6 +63,7 @@ TYPED_SFIELD(sfHookStateScale, UINT16, 21)
|
||||
TYPED_SFIELD(sfLedgerFixType, UINT16, 22)
|
||||
TYPED_SFIELD(sfHookExportCount, UINT16, 98)
|
||||
TYPED_SFIELD(sfEntropyCount, UINT16, 99)
|
||||
TYPED_SFIELD(sfEntropyDenominator, UINT16, 100)
|
||||
|
||||
// 32-bit integers (common)
|
||||
TYPED_SFIELD(sfNetworkID, UINT32, 1)
|
||||
|
||||
@@ -622,6 +622,7 @@ TRANSACTION(ttCONSENSUS_ENTROPY, 105, ConsensusEntropy, ({
|
||||
{sfLedgerSequence, soeREQUIRED},
|
||||
{sfDigest, soeREQUIRED},
|
||||
{sfEntropyCount, soeREQUIRED},
|
||||
{sfEntropyDenominator, soeREQUIRED},
|
||||
{sfEntropyTier, soeREQUIRED},
|
||||
}))
|
||||
|
||||
|
||||
@@ -84,6 +84,7 @@ class ConsensusEntropy_test : public beast::unit_test::suite
|
||||
|
||||
auto const count = sle->getFieldU16(sfEntropyCount);
|
||||
BEAST_EXPECT(count >= 5);
|
||||
BEAST_EXPECT(sle->getFieldU16(sfEntropyDenominator) >= count);
|
||||
|
||||
auto const sleSeq = sle->getFieldU32(sfLedgerSequence);
|
||||
BEAST_EXPECT(sleSeq == env.closed()->seq());
|
||||
@@ -445,7 +446,8 @@ class ConsensusEntropy_test : public beast::unit_test::suite
|
||||
|
||||
BEAST_REQUIRE(env.le(keylet::consensusEntropy()));
|
||||
|
||||
// Standalone entropy carries EntropyCount=20 / tier validator_quorum.
|
||||
// Standalone entropy carries EntropyCount=20,
|
||||
// EntropyDenominator=20, and tier validator_quorum.
|
||||
// A hook demanding min_count=21 states a requirement this ledger
|
||||
// cannot meet, so dice must fail closed with TOO_LITTLE_ENTROPY (-48)
|
||||
// rather than silently serving weaker entropy.
|
||||
|
||||
@@ -65,6 +65,7 @@ struct PseudoTx_test : public beast::unit_test::suite
|
||||
obj.setFieldAmount(sfFee, STAmount{});
|
||||
obj.setFieldH256(sfDigest, uint256(3));
|
||||
obj.setFieldU16(sfEntropyCount, 1);
|
||||
obj.setFieldU16(sfEntropyDenominator, 1);
|
||||
obj.setFieldU8(sfEntropyTier, entropyTierValidatorQuorum);
|
||||
}));
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
#include <deque>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
@@ -1238,6 +1239,7 @@ class ConsensusExtensions_test : public beast::unit_test::suite
|
||||
BEAST_EXPECT(tx->getFieldH256(sfDigest) == expected);
|
||||
BEAST_EXPECT(tx->getFieldH256(sfDigest) != uint256{});
|
||||
BEAST_EXPECT(tx->getFieldU16(sfEntropyCount) == 0);
|
||||
BEAST_EXPECT(tx->getFieldU16(sfEntropyDenominator) == 0);
|
||||
BEAST_EXPECT(
|
||||
tx->getFieldU8(sfEntropyTier) == entropyTierConsensusFallback);
|
||||
|
||||
@@ -1283,6 +1285,7 @@ class ConsensusExtensions_test : public beast::unit_test::suite
|
||||
txSetHash,
|
||||
fallbackDigest,
|
||||
static_cast<std::uint8_t>(entropyTierConsensusFallback),
|
||||
static_cast<std::uint16_t>(0),
|
||||
static_cast<std::uint16_t>(0));
|
||||
|
||||
auto const salt = ce.txnOrderingSalt(txSetHash, seq);
|
||||
@@ -1351,6 +1354,7 @@ class ConsensusExtensions_test : public beast::unit_test::suite
|
||||
BEAST_EXPECT(
|
||||
tx->getFieldH256(sfDigest) == expectedEntropy(publicKey, reveal));
|
||||
BEAST_EXPECT(tx->getFieldU16(sfEntropyCount) == 1);
|
||||
BEAST_EXPECT(tx->getFieldU16(sfEntropyDenominator) == 1);
|
||||
BEAST_EXPECT(
|
||||
tx->getFieldU8(sfEntropyTier) == entropyTierValidatorQuorum);
|
||||
}
|
||||
@@ -1420,6 +1424,7 @@ class ConsensusExtensions_test : public beast::unit_test::suite
|
||||
BEAST_EXPECT(
|
||||
tx->getFieldH256(sfDigest) == expectedEntropy(publicKey, reveal));
|
||||
BEAST_EXPECT(tx->getFieldU16(sfEntropyCount) == 1);
|
||||
BEAST_EXPECT(tx->getFieldU16(sfEntropyDenominator) == 1);
|
||||
BEAST_EXPECT(
|
||||
tx->getFieldU8(sfEntropyTier) == entropyTierValidatorQuorum);
|
||||
}
|
||||
@@ -1476,7 +1481,8 @@ class ConsensusExtensions_test : public beast::unit_test::suite
|
||||
auto const txSetHash = makeHash("tier2-txset");
|
||||
|
||||
// Harvest commit+reveal from `revealers` of the 6 validators, build the
|
||||
// agreed entropy set, inject, and return the labelled (tier, count).
|
||||
// agreed entropy set, inject, and return the labelled
|
||||
// (tier, count, denominator).
|
||||
auto runWith = [&](std::size_t revealers) {
|
||||
ConsensusExtensions ce{env.app(), activeNoopJournal()};
|
||||
ce.cacheUNLReport(viewLedger);
|
||||
@@ -1501,28 +1507,32 @@ class ConsensusExtensions_test : public beast::unit_test::suite
|
||||
CanonicalTXSet txs{makeHash("tier2-salt")};
|
||||
ce.onPreBuild(txs, seq, txSetHash);
|
||||
auto const tx = singleCanonicalTx(txs);
|
||||
std::pair<int, std::uint16_t> out{-1, 0};
|
||||
std::tuple<int, std::uint16_t, std::uint16_t> out{-1, 0, 0};
|
||||
if (tx)
|
||||
out = {
|
||||
tx->getFieldU8(sfEntropyTier),
|
||||
tx->getFieldU16(sfEntropyCount)};
|
||||
tx->getFieldU16(sfEntropyCount),
|
||||
tx->getFieldU16(sfEntropyDenominator)};
|
||||
return out;
|
||||
};
|
||||
|
||||
// 5 of 6 aligned -> validator_quorum (count >= quorum 5).
|
||||
auto const q = runWith(5);
|
||||
BEAST_EXPECT(q.first == entropyTierValidatorQuorum);
|
||||
BEAST_EXPECT(q.second == 5);
|
||||
BEAST_EXPECT(std::get<0>(q) == entropyTierValidatorQuorum);
|
||||
BEAST_EXPECT(std::get<1>(q) == 5);
|
||||
BEAST_EXPECT(std::get<2>(q) == kValidators);
|
||||
|
||||
// 4 of 6 aligned -> participant_aligned (count >= tier2 4, < quorum 5).
|
||||
auto const p = runWith(4);
|
||||
BEAST_EXPECT(p.first == entropyTierParticipantAligned);
|
||||
BEAST_EXPECT(p.second == 4);
|
||||
BEAST_EXPECT(std::get<0>(p) == entropyTierParticipantAligned);
|
||||
BEAST_EXPECT(std::get<1>(p) == 4);
|
||||
BEAST_EXPECT(std::get<2>(p) == kValidators);
|
||||
|
||||
// 3 of 6 aligned -> below the tier-2 floor -> consensus_fallback.
|
||||
auto const f = runWith(3);
|
||||
BEAST_EXPECT(f.first == entropyTierConsensusFallback);
|
||||
BEAST_EXPECT(f.second == 0);
|
||||
BEAST_EXPECT(std::get<0>(f) == entropyTierConsensusFallback);
|
||||
BEAST_EXPECT(std::get<1>(f) == 0);
|
||||
BEAST_EXPECT(std::get<2>(f) == 0);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1669,25 +1679,29 @@ class ConsensusExtensions_test : public beast::unit_test::suite
|
||||
CanonicalTXSet txs{makeHash("tier2-nunl-salt")};
|
||||
ce.onPreBuild(txs, seq, txSetHash);
|
||||
auto const tx = singleCanonicalTx(txs);
|
||||
std::pair<int, std::uint16_t> out{-1, 0};
|
||||
std::tuple<int, std::uint16_t, std::uint16_t> out{-1, 0, 0};
|
||||
if (tx)
|
||||
out = {
|
||||
tx->getFieldU8(sfEntropyTier),
|
||||
tx->getFieldU16(sfEntropyCount)};
|
||||
tx->getFieldU16(sfEntropyCount),
|
||||
tx->getFieldU16(sfEntropyDenominator)};
|
||||
return out;
|
||||
};
|
||||
|
||||
auto const q = runWith(16);
|
||||
BEAST_EXPECT(q.first == entropyTierValidatorQuorum);
|
||||
BEAST_EXPECT(q.second == 16);
|
||||
BEAST_EXPECT(std::get<0>(q) == entropyTierValidatorQuorum);
|
||||
BEAST_EXPECT(std::get<1>(q) == 16);
|
||||
BEAST_EXPECT(std::get<2>(q) == kOriginal - kDisabled);
|
||||
|
||||
auto const p = runWith(15);
|
||||
BEAST_EXPECT(p.first == entropyTierParticipantAligned);
|
||||
BEAST_EXPECT(p.second == 15);
|
||||
BEAST_EXPECT(std::get<0>(p) == entropyTierParticipantAligned);
|
||||
BEAST_EXPECT(std::get<1>(p) == 15);
|
||||
BEAST_EXPECT(std::get<2>(p) == kOriginal - kDisabled);
|
||||
|
||||
auto const f = runWith(12);
|
||||
BEAST_EXPECT(f.first == entropyTierConsensusFallback);
|
||||
BEAST_EXPECT(f.second == 0);
|
||||
BEAST_EXPECT(std::get<0>(f) == entropyTierConsensusFallback);
|
||||
BEAST_EXPECT(std::get<1>(f) == 0);
|
||||
BEAST_EXPECT(std::get<2>(f) == 0);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -2321,6 +2335,7 @@ class ConsensusExtensions_test : public beast::unit_test::suite
|
||||
tx->getFieldH256(sfDigest) ==
|
||||
sha512Half(std::string("standalone-entropy"), seq));
|
||||
BEAST_EXPECT(tx->getFieldU16(sfEntropyCount) == 20);
|
||||
BEAST_EXPECT(tx->getFieldU16(sfEntropyDenominator) == 20);
|
||||
BEAST_EXPECT(
|
||||
tx->getFieldU8(sfEntropyTier) == entropyTierValidatorQuorum);
|
||||
|
||||
@@ -2353,6 +2368,7 @@ class ConsensusExtensions_test : public beast::unit_test::suite
|
||||
obj.setFieldVL(sfSigningPubKey, Slice{}); // pseudo-tx convention
|
||||
obj.setFieldH256(sfDigest, digest);
|
||||
obj.setFieldU16(sfEntropyCount, count);
|
||||
obj.setFieldU16(sfEntropyDenominator, count);
|
||||
obj.setFieldU8(sfEntropyTier, entropyTierConsensusFallback);
|
||||
return std::make_shared<STTx const>(makeSTTx(obj));
|
||||
};
|
||||
|
||||
@@ -66,6 +66,8 @@ public:
|
||||
{
|
||||
BEAST_EXPECT(!peer->ce().lastEntropyWasFallback_);
|
||||
BEAST_EXPECT(peer->ce().lastEntropyCount_ > 0);
|
||||
BEAST_EXPECT(
|
||||
peer->ce().lastEntropyDenominator_ == peers.size());
|
||||
BEAST_EXPECT(peer->ce().lastEntropyDigest_ != uint256{});
|
||||
}
|
||||
}
|
||||
@@ -908,6 +910,7 @@ public:
|
||||
peer->ce().lastEntropyDigest_ ==
|
||||
peers[0]->ce().lastEntropyDigest_);
|
||||
BEAST_EXPECT(peer->ce().lastEntropyCount_ == 0);
|
||||
BEAST_EXPECT(peer->ce().lastEntropyDenominator_ == 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -981,6 +984,7 @@ public:
|
||||
BEAST_EXPECT(
|
||||
peer->ce().lastEntropyTier_ == entropyTierConsensusFallback);
|
||||
BEAST_EXPECT(peer->ce().lastEntropyCount_ == 0);
|
||||
BEAST_EXPECT(peer->ce().lastEntropyDenominator_ == 0);
|
||||
BEAST_EXPECT(peer->ce().lastEntropyDigest_ != uint256{});
|
||||
}
|
||||
BEAST_EXPECT(
|
||||
@@ -1038,12 +1042,14 @@ public:
|
||||
BEAST_EXPECT(
|
||||
peers[i]->ce().lastEntropyTier_ == entropyTierValidatorQuorum);
|
||||
BEAST_EXPECT(peers[i]->ce().lastEntropyCount_ == 4);
|
||||
BEAST_EXPECT(peers[i]->ce().lastEntropyDenominator_ == 5);
|
||||
}
|
||||
|
||||
BEAST_EXPECT(peers[0]->ce().lastEntropyWasFallback_);
|
||||
BEAST_EXPECT(
|
||||
peers[0]->ce().lastEntropyTier_ == entropyTierConsensusFallback);
|
||||
BEAST_EXPECT(peers[0]->ce().lastEntropyCount_ == 0);
|
||||
BEAST_EXPECT(peers[0]->ce().lastEntropyDenominator_ == 0);
|
||||
BEAST_EXPECT(peers[0]->ce().lastEntropyDigest_ != cohortDigest);
|
||||
}
|
||||
|
||||
|
||||
@@ -352,6 +352,7 @@ struct Peer
|
||||
// Last round summary (for test assertions)
|
||||
uint256 lastEntropyDigest_;
|
||||
std::uint16_t lastEntropyCount_ = 0;
|
||||
std::uint16_t lastEntropyDenominator_ = 0;
|
||||
bool lastEntropyWasFallback_ = true;
|
||||
EntropyTier lastEntropyTier_ = entropyTierNone;
|
||||
bool lastExportSucceeded_ = false;
|
||||
@@ -819,6 +820,7 @@ struct Peer
|
||||
// zero digest as the "none" marker.
|
||||
lastEntropyDigest_.zero();
|
||||
lastEntropyCount_ = 0;
|
||||
lastEntropyDenominator_ = 0;
|
||||
lastEntropyWasFallback_ = true;
|
||||
lastEntropyTier_ = entropyTierNone;
|
||||
return;
|
||||
@@ -829,6 +831,7 @@ struct Peer
|
||||
auto const fallback = [&] {
|
||||
lastEntropyDigest_ = fallbackEntropy();
|
||||
lastEntropyCount_ = 0;
|
||||
lastEntropyDenominator_ = 0;
|
||||
lastEntropyWasFallback_ = true;
|
||||
lastEntropyTier_ = entropyTierConsensusFallback;
|
||||
};
|
||||
@@ -902,6 +905,8 @@ struct Peer
|
||||
}
|
||||
lastEntropyDigest_ = digest;
|
||||
lastEntropyCount_ = static_cast<std::uint16_t>(count);
|
||||
lastEntropyDenominator_ =
|
||||
static_cast<std::uint16_t>(unlNodes_.size());
|
||||
lastEntropyWasFallback_ = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,11 +25,12 @@ determinism or liveness.**
|
||||
|
||||
**INV-1 — Determinism of the injected object.**
|
||||
Given the same parent ledger and the same *agreed* entropy sidecar, every honest
|
||||
node injects the byte-identical `ttCONSENSUS_ENTROPY` (digest, tier, count). That
|
||||
node injects the byte-identical `ttCONSENSUS_ENTROPY` (digest, tier, count,
|
||||
denominator). That
|
||||
object is ledger state. Therefore **non-fallback entropy must not read mutable
|
||||
local collector state or timing-derived state.** The selector derives non-fallback
|
||||
`(digest, tier, count)` only from the accepted `entropySetMap_` (matched to the
|
||||
hash the gate accepted) plus the parent-ledger active view. Local timeout or
|
||||
`(digest, tier, count, denominator)` only from the accepted `entropySetMap_`
|
||||
(matched to the hash the gate accepted) plus the parent-ledger active view. Local timeout or
|
||||
diagnostic state such as `entropyFailed_` must not override an accepted root at
|
||||
injection time; a node that never accepts a root falls back through the normal
|
||||
missing-accepted-root path.
|
||||
|
||||
@@ -782,6 +782,7 @@ ConsensusExtensions::selectEntropy(
|
||||
agreedTxSetHash,
|
||||
seq),
|
||||
entropyTierConsensusFallback,
|
||||
0,
|
||||
0};
|
||||
};
|
||||
//@@end entropy-selector-fallback
|
||||
@@ -792,6 +793,7 @@ ConsensusExtensions::selectEntropy(
|
||||
return {
|
||||
sha512Half(std::string("standalone-entropy"), seq),
|
||||
entropyTierValidatorQuorum,
|
||||
20,
|
||||
20};
|
||||
//@@end entropy-selector-standalone
|
||||
|
||||
@@ -836,9 +838,9 @@ ConsensusExtensions::selectEntropy(
|
||||
// Derive from the AGREED entropySetMap_ — NOT local pendingReveals_. The
|
||||
// map's hash was published in proposals and accepted by the gate, so every
|
||||
// node holding the same entropySetHash produces byte-identical entropy and
|
||||
// the same tier/count. Read leaves through the shared sidecar admission
|
||||
// helper so accepted-map consumption enforces the same content-address/type
|
||||
// contract as snapshot construction.
|
||||
// the same tier/count/denominator labels. Read leaves through the shared
|
||||
// sidecar admission helper so accepted-map consumption enforces the same
|
||||
// content-address/type contract as snapshot construction.
|
||||
std::vector<std::pair<PublicKey, uint256>> sorted;
|
||||
entropySetMap_->visitLeaves(
|
||||
[&](boost::intrusive_ptr<SHAMapItem const> const& item) {
|
||||
@@ -887,6 +889,7 @@ ConsensusExtensions::selectEntropy(
|
||||
}
|
||||
auto const digest = sha512Half(s.slice());
|
||||
auto const count = static_cast<std::uint16_t>(sorted.size());
|
||||
auto const denominator = static_cast<std::uint16_t>(validatorView->size());
|
||||
|
||||
//@@start entropy-selector-tier-ladder
|
||||
// Tier ladder over the AGREED participant count — deterministic on every
|
||||
@@ -901,7 +904,7 @@ ConsensusExtensions::selectEntropy(
|
||||
validatorView->size(),
|
||||
validatorView->originalViewSize);
|
||||
if (tier != entropyTierConsensusFallback)
|
||||
return {digest, static_cast<std::uint8_t>(tier), count};
|
||||
return {digest, static_cast<std::uint8_t>(tier), count, denominator};
|
||||
return fallback();
|
||||
//@@end entropy-selector-tier-ladder
|
||||
}
|
||||
@@ -926,7 +929,8 @@ ConsensusExtensions::txnOrderingSalt(
|
||||
agreedTxSetHash,
|
||||
selection.digest,
|
||||
selection.tier,
|
||||
selection.count);
|
||||
selection.count,
|
||||
selection.denominator);
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -1711,19 +1715,21 @@ ConsensusExtensions::onPreBuild(
|
||||
|
||||
//@@start rng-inject-entropy-selection
|
||||
// One deterministic selector over the AGREED entropySetMap_ chooses the
|
||||
// digest and its tier/count. Every node derives the same entropy for
|
||||
// the same agreed round inputs. txSetHash is the agreed pre-injection
|
||||
// consensus tx set hash.
|
||||
// digest and its tier/count/denominator labels. Every node derives the
|
||||
// same entropy for the same agreed round inputs. txSetHash is the
|
||||
// agreed pre-injection consensus tx set hash.
|
||||
auto const selection = selectEntropy(txSetHash, seq);
|
||||
uint256 const finalEntropy = selection.digest;
|
||||
std::uint8_t const entropyTier = selection.tier;
|
||||
std::uint16_t const entropyCount = selection.count;
|
||||
std::uint16_t const entropyDenominator = selection.denominator;
|
||||
//@@end rng-inject-entropy-selection
|
||||
|
||||
JLOG(j_.info()) << "RNG: entropy selected"
|
||||
<< " seq=" << seq
|
||||
<< " tier=" << static_cast<int>(entropyTier)
|
||||
<< " count=" << entropyCount
|
||||
<< " denominator=" << entropyDenominator
|
||||
<< " digest=" << finalEntropy;
|
||||
|
||||
//@@start rng-inject-pseudotx
|
||||
@@ -1755,6 +1761,7 @@ ConsensusExtensions::onPreBuild(
|
||||
obj.setFieldAmount(sfFee, STAmount{});
|
||||
obj.setFieldH256(sfDigest, finalEntropy);
|
||||
obj.setFieldU16(sfEntropyCount, entropyCount);
|
||||
obj.setFieldU16(sfEntropyDenominator, entropyDenominator);
|
||||
obj.setFieldU8(sfEntropyTier, entropyTier);
|
||||
});
|
||||
|
||||
@@ -1820,6 +1827,11 @@ ConsensusExtensions::onPreBuild(
|
||||
<< (pres.isFieldPresent(sfEntropyCount)
|
||||
? std::to_string(
|
||||
pres.getFieldU16(sfEntropyCount))
|
||||
: std::string{"<missing>"})
|
||||
<< " presentDenominator="
|
||||
<< (pres.isFieldPresent(sfEntropyDenominator)
|
||||
? std::to_string(
|
||||
pres.getFieldU16(sfEntropyDenominator))
|
||||
: std::string{"<missing>"});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,13 +257,15 @@ public:
|
||||
clearAcceptedEntropySet();
|
||||
|
||||
/// 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.
|
||||
/// inject plus its tier/count/denominator labels. Both injection paths
|
||||
/// derive these identically from the AGREED entropySetMap_ so they cannot
|
||||
/// drift.
|
||||
struct EntropySelection
|
||||
{
|
||||
uint256 digest;
|
||||
std::uint8_t tier = 0; // EntropyTier; the selector always sets this
|
||||
std::uint16_t count = 0;
|
||||
std::uint16_t denominator = 0;
|
||||
};
|
||||
|
||||
/// Deterministically choose the entropy to inject for this round from the
|
||||
|
||||
@@ -347,8 +347,11 @@ non-UNLReport (config-fallback) view, every case below instead mints
|
||||
|
||||
The fallback pseudo-transaction is deterministic — every node derives the same
|
||||
digest from `(HashPrefix::entropyFallback, parentLedgerHash, agreedTxSetHash,
|
||||
seq)` — and labeled with `EntropyTier = consensus_fallback` and
|
||||
`EntropyCount = 0`. Hooks state their own requirements via the required
|
||||
seq)` — and labeled with `EntropyTier = consensus_fallback`,
|
||||
`EntropyCount = 0`, and `EntropyDenominator = 0`. Non-fallback entropy records
|
||||
both the contributor count and the active-validator denominator used for the
|
||||
validator-quorum threshold; the participant-aligned floor still uses the
|
||||
original pre-NegativeUNL view internally. Hooks state their own requirements via the required
|
||||
`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
|
||||
|
||||
@@ -309,6 +309,8 @@ Change::applyConsensusEntropy()
|
||||
|
||||
sle->setFieldH256(sfDigest, entropy);
|
||||
sle->setFieldU16(sfEntropyCount, ctx_.tx.getFieldU16(sfEntropyCount));
|
||||
sle->setFieldU16(
|
||||
sfEntropyDenominator, ctx_.tx.getFieldU16(sfEntropyDenominator));
|
||||
sle->setFieldU8(sfEntropyTier, ctx_.tx.getFieldU8(sfEntropyTier));
|
||||
sle->setFieldU32(sfLedgerSequence, view().info().seq);
|
||||
// Note: sfPreviousTxnID and sfPreviousTxnLgrSeq are set automatically
|
||||
|
||||
Reference in New Issue
Block a user