From ea413873b23d05903182b28da3fb7ffb38421238 Mon Sep 17 00:00:00 2001 From: Nicholas Dudfield Date: Mon, 27 Apr 2026 20:34:58 +0700 Subject: [PATCH] fix(consensus): preserve export state without rng --- src/xrpld/app/consensus/ConsensusExtensionsDesign.md | 5 +++++ src/xrpld/app/consensus/RCLConsensus.cpp | 11 ++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/xrpld/app/consensus/ConsensusExtensionsDesign.md b/src/xrpld/app/consensus/ConsensusExtensionsDesign.md index 898d0a281..a56073e6d 100644 --- a/src/xrpld/app/consensus/ConsensusExtensionsDesign.md +++ b/src/xrpld/app/consensus/ConsensusExtensionsDesign.md @@ -192,6 +192,11 @@ Export sig convergence runs in parallel with RNG. An export-side convergence failure must not change RNG semantics; an RNG fallback must not make export unsafe. Each feature has its own gate and fallback. +Accept-time cleanup must preserve Export state through `buildLCL` whenever +`featureExport` is enabled. RNG-disabled does not mean extensions-disabled: +`ttEXPORT` still needs the round's export sidecar convergence state when it +applies. + CSF consensus tests model the export sidecar gate directly. Testnet scenarios under `.testnet/scenarios/export/` cover live-node Export+CE behavior and the Export-only unanimity mode. diff --git a/src/xrpld/app/consensus/RCLConsensus.cpp b/src/xrpld/app/consensus/RCLConsensus.cpp index 064e36aa7..3be828a70 100644 --- a/src/xrpld/app/consensus/RCLConsensus.cpp +++ b/src/xrpld/app/consensus/RCLConsensus.cpp @@ -587,13 +587,14 @@ RCLConsensus::Adaptor::doAccept( } //@@start auxiliary-pre-build-injection - // Inject consensus entropy pseudo-transaction (if amendment enabled) - // This must happen before buildLCL so the entropy tx is in the ledger + // Inject consensus entropy pseudo-transaction (if amendment enabled). + // Export-only rounds still need extension state preserved through buildLCL + // so ttEXPORT can observe exportSigSetHash convergence at apply time. //@@start accept-time-cleanup-disabled - if (prevLedger.ledger_->rules().enabled(featureConsensusEntropy)) + if (ce().rngEnabled()) ce().onPreBuild(retriableTxs, prevLedger.seq() + 1); - else - ce().clearRngState(); // CE disabled — clear extension state now + else if (!ce().exportEnabled()) + ce().clearRngState(); //@@end accept-time-cleanup-disabled auto built = buildLCL(