diff --git a/src/test/app/Export_test.cpp b/src/test/app/Export_test.cpp index ea87d3d39..779d4708f 100644 --- a/src/test/app/Export_test.cpp +++ b/src/test/app/Export_test.cpp @@ -911,6 +911,17 @@ struct Export_test : public beast::unit_test::suite BEAST_EXPECT( releasedLatch->getFieldH256(sfExportSignatureHash) == witness->getTransactionID()); + + // Witnessed latches still resolve their immutable committee during + // replay. The committee therefore remains undeletable until the latch + // itself reaches terminal cleanup. + Json::Value eraseCommittee; + eraseCommittee[jss::TransactionType] = jss::Export; + eraseCommittee[jss::Account] = alice.human(); + eraseCommittee[jss::Flags] = tfExportEraseCommittee; + eraseCommittee[sfExportCommitteeHash.jsonName] = + to_string(committeeHash); + env(eraseCommittee, fee(XRP(1)), ter(tecHAS_OBLIGATIONS)); } void diff --git a/src/xrpld/app/consensus/ConsensusExtensionsDesign.md b/src/xrpld/app/consensus/ConsensusExtensionsDesign.md index 3bec8b807..ee3780822 100644 --- a/src/xrpld/app/consensus/ConsensusExtensionsDesign.md +++ b/src/xrpld/app/consensus/ConsensusExtensionsDesign.md @@ -449,7 +449,9 @@ default committee and no implicit capped subset of the full UNL. Bare setup may pre-stage a roster, but every actual intent checks every roster master against the exact admitting parent ledger's pre-NegativeUNL `UNLReport`. Inline roster creation and intent admission perform that check atomically. Committee deletion -is blocked while the owner has any live Export latch. +is blocked while the owner has any live Export latch. That guard is coupled to +witness apply's parent-state committee lookup: weakening it to pending-only +would make a retained latch's accepted witness unreplayable. The destination account's SignerList remains the actual remote authority. Its keys, weights, optional operator signer, and rotation policy are client/operator diff --git a/src/xrpld/app/consensus/ExportIntent.md b/src/xrpld/app/consensus/ExportIntent.md index 099c66dcf..9f9d290f9 100644 --- a/src/xrpld/app/consensus/ExportIntent.md +++ b/src/xrpld/app/consensus/ExportIntent.md @@ -41,7 +41,10 @@ as an explicit template, but the protocol never silently selects it. Committee deletion is rejected while the owner has any live Export latch. This coarse rule keeps every accepted witness replayable without storing the roster -again in every latch or maintaining a metadata-expensive reference count. +again in every latch or maintaining a metadata-expensive reference count. The +deletion guard and witness apply's committee lookup are one load-bearing +invariant: do not weaken the guard to pending-only while latches retain only a +committee digest. **INV-3 - qC and qV answer different questions.** qC is the content threshold for one account-selected committee: