fix(rng): add selfSeedReveal to fix CSF reveal counting

The CSF never self-seeded its own reveal into pendingReveals_ because
harvestRngData only processes peer proposals, not self.  The real code
handles this in decorateMessage, but the CSF has no equivalent.

Add selfSeedReveal() called from the tick at reveal transition.
Both the real ConsensusExtensions and the CSF Extensions implement it.
The real code now has belt-and-suspenders: tick + decorateMessage.

This fixes CSF peers having N-1 reveals instead of N, which caused
every peer to compute entropy from a different subset.
This commit is contained in:
Nicholas Dudfield
2026-04-09 17:23:53 +07:00
parent 383d9ec2e7
commit db302a0f78
4 changed files with 36 additions and 0 deletions

View File

@@ -462,6 +462,19 @@ struct Peer
return myEntropySecret_;
}
void
selfSeedReveal()
{
if (!enableRngConsensus_)
return;
// Self-seed our own reveal into pendingReveals_ so it
// counts toward reveal quorum. The real code does this
// in decorateMessage; the CSF does it here since it has
// no equivalent serialization hook.
if (myEntropySecret_ != uint256{})
pendingReveals_[peer.id] = myEntropySecret_;
}
void
setEntropyFailed()
{

View File

@@ -508,6 +508,17 @@ ConsensusExtensions::setEntropyFailed()
entropyFailed_ = true;
}
void
ConsensusExtensions::selfSeedReveal()
{
auto const& valKeys = app_.getValidatorKeys();
if (myEntropySecret_ != uint256{})
{
pendingReveals_[valKeys.nodeID] = myEntropySecret_;
nodeIdToKey_.insert_or_assign(valKeys.nodeID, valKeys.keys->publicKey);
}
}
//@@start clear-rng-state
void
ConsensusExtensions::clearRngState()

View File

@@ -195,6 +195,12 @@ public:
void
setEntropyFailed();
/// Self-seed our own reveal into pendingReveals_.
/// Called from extensionsTick at reveal transition.
/// In production, decorateMessage also self-seeds (belt + suspenders).
void
selfSeedReveal();
void
clearRngState();

View File

@@ -415,6 +415,12 @@ extensionsTick(Ext& ext, Ctx const& ctx)
auto newPos = ctx.getPosition();
newPos.myReveal = ext.getEntropySecret();
// Self-seed our own reveal into pendingReveals so it
// counts toward reveal quorum and appears in the
// entropy set. harvestRngData only sees peer proposals,
// not our own.
ext.selfSeedReveal();
ctx.updatePosition(newPos);
if (ctx.mode == ConsensusMode::proposing)