mirror of
https://github.com/Xahau/xahaud.git
synced 2026-07-24 15:40:08 +00:00
fix(export): require anchored view for finalization
This commit is contained in:
@@ -395,6 +395,7 @@ struct FakeExtensions
|
||||
bool localExportSigs{true};
|
||||
bool livePendingExportLatches{false};
|
||||
bool exportOn{true};
|
||||
bool exportViewAnchored{true};
|
||||
bool entropyFailed{false};
|
||||
bool commitFrozen{false};
|
||||
std::size_t sidecarQuorum{4};
|
||||
@@ -412,6 +413,7 @@ struct FakeExtensions
|
||||
std::deque<uint256> entropyHashSequence;
|
||||
int commitBuilds = 0;
|
||||
int exportBuilds = 0;
|
||||
int acceptedExportClears = 0;
|
||||
int entropyBuilds = 0;
|
||||
int participantDiagnostics = 0;
|
||||
int selfSeeds = 0;
|
||||
@@ -428,6 +430,12 @@ struct FakeExtensions
|
||||
return exportOn;
|
||||
}
|
||||
|
||||
bool
|
||||
exportFinalizationViewAnchored() const
|
||||
{
|
||||
return exportViewAnchored;
|
||||
}
|
||||
|
||||
bool
|
||||
testSuppressExportSigSetHash() const
|
||||
{
|
||||
@@ -611,6 +619,7 @@ struct FakeExtensions
|
||||
void
|
||||
clearAcceptedExportSigSet()
|
||||
{
|
||||
++acceptedExportClears;
|
||||
}
|
||||
|
||||
template <class PeerPositions>
|
||||
@@ -4096,6 +4105,25 @@ class ConsensusExtensions_test : public beast::unit_test::suite
|
||||
BEAST_EXPECT(ext.exportBuilds == 0);
|
||||
}
|
||||
|
||||
void
|
||||
testExportSigGateSkipsWithoutAnchoredView()
|
||||
{
|
||||
testcase("Export sig gate skips without ledger-anchored view");
|
||||
|
||||
FakeExtensions ext;
|
||||
ext.exportViewAnchored = false;
|
||||
ExtensionTickHarness harness;
|
||||
|
||||
harness.addPeer(1, ext.exportHash);
|
||||
|
||||
auto const result = harness.tick(ext);
|
||||
BEAST_EXPECT(result.readyForAccept);
|
||||
BEAST_EXPECT(!ext.exportSigGateStarted_);
|
||||
BEAST_EXPECT(!harness.position.exportSigSetHash);
|
||||
BEAST_EXPECT(ext.exportBuilds == 0);
|
||||
BEAST_EXPECT(ext.acceptedExportClears == 1);
|
||||
}
|
||||
|
||||
void
|
||||
testParticipantDiagnosticsOnlyWhenExtensionEnabled()
|
||||
{
|
||||
@@ -4886,6 +4914,7 @@ public:
|
||||
testExportSigGateRefreshesHashBeforeWaiting();
|
||||
testExportSigGateBoundsCandidateObservationWindow();
|
||||
testExportSigGateSkipsWhenExportDisabled();
|
||||
testExportSigGateSkipsWithoutAnchoredView();
|
||||
testParticipantDiagnosticsOnlyWhenExtensionEnabled();
|
||||
testExportDisabledRoundClearsCollector();
|
||||
testValidatorKeylessAuthoringNoops();
|
||||
|
||||
@@ -1713,6 +1713,12 @@ ConsensusExtensions::exportEnabled() const
|
||||
return exportEnabledThisRound_.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
bool
|
||||
ConsensusExtensions::exportFinalizationViewAnchored() const
|
||||
{
|
||||
return app_.config().standalone() || activeValidatorView()->fromUNLReport;
|
||||
}
|
||||
|
||||
bool
|
||||
ConsensusExtensions::testSuppressExportSigSetHash() const
|
||||
{
|
||||
@@ -2544,7 +2550,7 @@ ConsensusExtensions::onPreBuild(
|
||||
//@@end rng-inject-pseudotx
|
||||
}
|
||||
|
||||
if (exportEnabled())
|
||||
if (exportEnabled() && exportFinalizationViewAnchored())
|
||||
{
|
||||
//@@start export-later-ledger-witness-materialization
|
||||
// Standalone has no peer-position gate to build and accept a sidecar
|
||||
@@ -2656,6 +2662,16 @@ ConsensusExtensions::onPreBuild(
|
||||
}
|
||||
//@@end export-later-ledger-witness-materialization
|
||||
}
|
||||
else if (exportEnabled())
|
||||
{
|
||||
// Admission currently makes this unreachable for a live latch because
|
||||
// UNLReport state persists in descendants. Retain the materialization
|
||||
// guard so a future report-expiry or clearing rule cannot silently
|
||||
// turn node-local configured trust into ledger-defining authority.
|
||||
JLOG(j_.warn()) << "Export: skipping witness materialization"
|
||||
<< " reason=no-unl-report"
|
||||
<< " buildSeq=" << seq;
|
||||
}
|
||||
|
||||
//@@start accept-time-cleanup-success
|
||||
// Export's ledger-defining signature witnesses are now self-contained
|
||||
|
||||
@@ -388,6 +388,12 @@ public:
|
||||
bool
|
||||
exportEnabled() const;
|
||||
|
||||
/// Whether Export may use the current round's validator view to align and
|
||||
/// materialize a witness. Standalone is deterministic locally; networked
|
||||
/// operation requires the view to come from the parent UNLReport.
|
||||
bool
|
||||
exportFinalizationViewAnchored() const;
|
||||
|
||||
bool
|
||||
testSuppressExportSigSetHash() const;
|
||||
|
||||
|
||||
@@ -939,6 +939,21 @@ extensionsTick(Ext& ext, Ctx const& ctx)
|
||||
if (!ext.exportEnabled())
|
||||
return {.readyForAccept = true};
|
||||
|
||||
// Export admission already requires a nonempty parent UNLReport, and
|
||||
// current ledger rules never erase or empty that report afterward, so
|
||||
// this should be unreachable while an admitted latch is live. Keep the
|
||||
// finalization boundary fail-closed in case report expiry or clearing
|
||||
// semantics are introduced later: a config-derived view is node-local
|
||||
// and cannot safely align a ledger-defining Export witness.
|
||||
if (!ext.exportFinalizationViewAnchored())
|
||||
{
|
||||
ext.clearAcceptedExportSigSet();
|
||||
JLOG(ext.j_.warn()) << "Export: skipping signature-set alignment"
|
||||
<< " reason=no-unl-report"
|
||||
<< " buildSeq=" << ctx.buildSeq;
|
||||
return {.readyForAccept = true};
|
||||
}
|
||||
|
||||
auto startExportSigGate = [&]() -> bool {
|
||||
if (ext.exportSigGateStarted_)
|
||||
return false;
|
||||
|
||||
Reference in New Issue
Block a user