From f41ce171766231bf12e2804b9acb36cbe25d14ac Mon Sep 17 00:00:00 2001 From: Bengt Lofgren Date: Thu, 16 Jul 2026 05:19:08 -0700 Subject: [PATCH] canton: revert guardianGovernance fee-pin functional changes (re-review) #41 merged directly without going through PR review. Reverting the functional changes (the GetGuardianGovernance choice, the guardianGovernance field on Emitter/EmitterRegistry/ReplayRootRegistry, the pin assertions at the three fee-charging call sites, and the 3 regression tests proving them) so the fix can go through proper review this time. Deliberately does NOT touch #44 (comment cleanup) or anything else merged since -- only the functional pieces #41 introduced, hand-picked out of the current tree so #44's independent comment tightening survives untouched in the same files. Verified: dpm build clean, 41/41 tests passing (44 minus the 3 regression tests testing now-reverted functionality). A follow-up PR (the literal git revert of this commit) will re-propose adding this back, for review this time. --- canton/README.md | 22 +--- canton/core/daml/Wormhole/Core/Fees.daml | 5 +- canton/core/daml/Wormhole/Core/Setup.daml | 21 ++-- canton/core/daml/Wormhole/Core/State.daml | 88 ++++---------- canton/test/daml/Test/TestCore.daml | 8 +- canton/test/daml/Test/TestFees.daml | 114 +----------------- .../test/daml/Test/TestGuardianObserver.daml | 2 +- 7 files changed, 47 insertions(+), 213 deletions(-) diff --git a/canton/README.md b/canton/README.md index 55f78b98a6..e504f8462b 100644 --- a/canton/README.md +++ b/canton/README.md @@ -210,12 +210,9 @@ consumer's trust anchor, is stable across rotation). Two things follow: the real governance party, so any `CoreState` whose `guardianGovernance` field is the known anchor is **authentic by construction**, however its cid was obtained — consumers authenticate the payload, never the resolution path - (§4.7). **Pinning `operator` is weaker than pinning `guardianGovernance`:** a - compromised operator (a single hot key) can co-sign a `CoreState` whose - `operator` is genuine but whose `guardianGovernance`/`feeRecipient` it - controls, so an `operator`-only pin accepts a shadow `CoreState`. For the fee - path — where `guardianGovernance` _is_ the fee-custody anchor — only the - `guardianGovernance` pin suffices (see below). + (§4.7). An operator puppet could create a `CoreState` under a _different_ + governance party — but its payload names the wrong parties and no consumer + trusts it. - **Governance stays low-friction.** `guardianGovernance` actively signs only at **genesis**; each `SubmitGovernanceVAA` inherits its authority from the archived contract (as the `Emitter` owner does through `PublishMessage`), so @@ -401,19 +398,12 @@ with Canton Coin or any standard token, chosen at genesis an app publishes on a user's behalf (NTT §10); `payer` co-controls the publish, so it cannot be a third party who did not authorize spending its funds. Registries charge their `registrationFee`/`claimFee` the same way, skipping the - `CoreState` entirely at fee 0. Before charging, every call site pins the - `CoreState` by exercising `GetGuardianGovernance` against the - `guardianGovernance` committed on the emitter/registry — **the fee-custody - anchor, not merely the `operator`** — so a forged `CoreState` cannot redirect - the fee (see the custody bullet). + `CoreState` entirely at fee 0. - **Custody is governance-controlled**: `feeRecipient` is the `guardianGovernance` threshold party, so accrued fees move only via the k-of-n guardian ceremony — the operator never touches them (the Canton analog of "fees - sit in the bridge contract"). This holds **even against a compromised operator - hot key**: each charge pins `guardianGovernance` (unforgeable by a single - operator key) rather than `operator`, so an operator co-signing a shadow - `CoreState` under a throwaway `feeRecipient` is rejected before any funds move. - A `TransferFees` VAA mints an on-ledger `FeeWithdrawalAuthorization` (signed by + sit in the bridge contract"). A `TransferFees` VAA mints an on-ledger + `FeeWithdrawalAuthorization` (signed by guardianGovernance) recording the amount, the VAA's 32-byte recipient, and the VAA hash — the artifact the guardians' custody policy matches when threshold-signing the outbound transfer. "Only per authorization" is enforced diff --git a/canton/core/daml/Wormhole/Core/Fees.daml b/canton/core/daml/Wormhole/Core/Fees.daml index 3b3b0748b6..fdda2676e8 100644 --- a/canton/core/daml/Wormhole/Core/Fees.daml +++ b/canton/core/daml/Wormhole/Core/Fees.daml @@ -10,10 +10,7 @@ -- (guardianGovernance). The receiver's authority is inherited from -- 'CoreState' (a signatory), so every charge routes through -- 'ChargeMessageFee'/'ChargeFee' there, and a foreign sender can never be a --- controller — blocking theft structurally, PROVIDED 'CoreState' is --- authentic. Callers pin 'GetGuardianGovernance' against their own committed --- anchor before charging, so a compromised operator can't redirect fees via --- a shadow CoreState under a gg it controls. +-- controller — blocking theft structurally. -- -- Allocation views are forgeable; authenticity is the instrument admin's -- signature on the allocation, checked in 'chargeFee'. diff --git a/canton/core/daml/Wormhole/Core/Setup.daml b/canton/core/daml/Wormhole/Core/Setup.daml index 74eb5b7cda..86ab16d48c 100644 --- a/canton/core/daml/Wormhole/Core/Setup.daml +++ b/canton/core/daml/Wormhole/Core/Setup.daml @@ -58,18 +58,15 @@ mkInitialCoreState operator guardianGovernance govChain govContract initialGuard consumedGovernance = Set.empty -- | The emitter-id registry the operator creates alongside the 'CoreState' at --- setup (sequential ids). Carries @guardianGovernance@ (the fee-custody anchor, --- pinned by 'RegisterEmitter') and @guardianObserver@, both threaded into each --- 'Emitter'. Pass the same @guardianGovernance@ the 'CoreState' was created with. -mkInitialEmitterRegistry : Party -> Party -> Party -> Int -> EmitterRegistry -mkInitialEmitterRegistry operator guardianGovernance guardianObserver registrationFee = - EmitterRegistry with operator, guardianGovernance, guardianObserver, registrationFee, nextId = 0 +-- setup (sequential ids). Carries @guardianObserver@, threaded into each +-- 'Emitter'. +mkInitialEmitterRegistry : Party -> Party -> Int -> EmitterRegistry +mkInitialEmitterRegistry operator guardianObserver registrationFee = + EmitterRegistry with operator, guardianObserver, registrationFee, nextId = 0 -- | The replay-root registry (the stateless claim anchor) the operator creates -- alongside the 'CoreState' at setup; hands a 'ReplayNode' trie root to any --- consumer that claims one (see 'Wormhole.Core.Replay'). Carries --- @guardianGovernance@ (the fee-custody anchor, pinned by 'ClaimReplayRoot'); --- pass the same one the 'CoreState' was created with. -mkInitialReplayRootRegistry : Party -> Party -> Int -> ReplayRootRegistry -mkInitialReplayRootRegistry operator guardianGovernance claimFee = - ReplayRootRegistry with operator, guardianGovernance, claimFee +-- consumer that claims one (see 'Wormhole.Core.Replay'). +mkInitialReplayRootRegistry : Party -> Int -> ReplayRootRegistry +mkInitialReplayRootRegistry operator claimFee = + ReplayRootRegistry with operator, claimFee diff --git a/canton/core/daml/Wormhole/Core/State.daml b/canton/core/daml/Wormhole/Core/State.daml index dcbdff1392..8e70c1b0d6 100644 --- a/canton/core/daml/Wormhole/Core/State.daml +++ b/canton/core/daml/Wormhole/Core/State.daml @@ -15,11 +15,7 @@ -- (README §4.7). Authenticity is checked on the PAYLOAD, never the resolution -- path: signatories cannot be forged, so a 'CoreState' whose @guardianGovernance@ -- is the known anchor is authentic by construction — a compromised @operator@ --- cannot fabricate one. The fee-custody anchor is pinned this way: 'PublishMessage', --- 'RegisterEmitter', and 'ClaimReplayRoot' exercise 'GetGuardianGovernance' and --- compare it against the @guardianGovernance@ committed on the emitter/registry --- before charging. Pinning @guardianGovernance@ (not @operator@) is what holds --- against a compromised operator, which alone does not determine @feeRecipient@. +-- cannot fabricate one. -- -- Two invariants: -- @@ -166,21 +162,6 @@ template CoreState controller reader do pure operator - -- | Read the @guardianGovernance@ anchor off an authentic 'CoreState' — a - -- STRONGER pin than 'GetOperator' for consumers that must resist a compromised - -- operator, not just third-party forgery. A 'CoreState' needs only some - -- (operator, gg) signature pair, so a compromised operator key can co-sign a - -- forged one under a throwaway gg it controls and pass an operator-pin; - -- pinning @guardianGovernance@ (the guardians' k-of-n external party, - -- unforgeable by a single operator key) closes that. Used by - -- 'Wormhole.Ntt.Manager.Receive' and the fee-charging pins ('PublishMessage', - -- 'RegisterEmitter', 'ClaimReplayRoot'). - nonconsuming choice GetGuardianGovernance : Party - with - reader : Party - controller reader - do pure guardianGovernance - -- | Charge the governance-set per-publish fee ('messageFee'). Lives here, -- not on 'Emitter': the fee amount is only knowable from this contract, and -- 'Allocation_ExecuteTransfer' needs the fee recipient's authority (sender ∧ @@ -370,13 +351,11 @@ emitterAddressFor registrar owner emitterId = -- the owner, whose signature it lacks (see 'Emitter'). template EmitterRegistry with - operator : Party - guardianGovernance : Party -- CoreState fee-custody anchor; pinned (not a - -- signatory) in RegisterEmitter's fee charge - guardianObserver : Party -- threaded into each new Emitter by RegisterEmitter - registrationFee : Int -- onboarding fee, 10^-10 feeInstrument units; - -- operator-set at creation, 0 for free - nextId : Int + operator : Party + guardianObserver : Party -- threaded into each new Emitter by RegisterEmitter + registrationFee : Int -- onboarding fee, 10^-10 feeInstrument units; + -- operator-set at creation, 0 for free + nextId : Int where signatory operator @@ -396,16 +375,12 @@ template EmitterRegistry controller requester do -- Zero-fee registrations never touch the CoreState; fee'd ones charge - -- through it. The gg pin runs BEFORE the charge: pinning - -- @guardianGovernance@ (not just @operator@, which a forged CoreState - -- would match) rejects a shadow CoreState under a rogue gg, so the - -- registration fee cannot be redirected. + -- through it, pinning its operator so a fabricated CoreState cannot + -- waive or redirect the fee. case (registrationFee > 0, coreStateCid) of (False, _) -> pure () (True, None) -> abort "registration fee due: pass the CoreState" (True, Some csCid) -> do - csGg <- exercise csCid GetGuardianGovernance with reader = requester - assertMsg "core state guardianGovernance mismatch" (csGg == guardianGovernance) csOperator <- exercise csCid ChargeFee with payer = requester fee = registrationFee @@ -414,7 +389,6 @@ template EmitterRegistry reg' <- create this with nextId = nextId + 1 emitter <- create Emitter with operator - guardianGovernance owner = requester guardianObserver emitterId = nextId @@ -441,11 +415,9 @@ template EmitterRegistry -- grows. template ReplayRootRegistry with - operator : Party - guardianGovernance : Party -- CoreState fee-custody anchor; pinned (not a - -- signatory) in ClaimReplayRoot's fee charge - claimFee : Int -- onboarding fee, 10^-10 feeInstrument units; - -- operator-set at creation, 0 for free + operator : Party + claimFee : Int -- onboarding fee, 10^-10 feeInstrument units; + -- operator-set at creation, 0 for free where signatory operator @@ -457,17 +429,13 @@ template ReplayRootRegistry feeAllocation : Optional (ContractId Allocation, ExtraArgs) controller consumer do - -- Zero-fee claims never touch the CoreState; fee'd ones charge through - -- it. The gg pin runs BEFORE the charge: pinning @guardianGovernance@ - -- (not just @operator@, which a forged CoreState would match) rejects a - -- shadow CoreState under a rogue gg, so the claim fee cannot be - -- redirected (see README §4.2). + -- Zero-fee claims never touch the CoreState; fee'd ones charge + -- through it, pinning its operator so a fabricated CoreState cannot + -- waive or redirect the fee (see README §4.2). case (claimFee > 0, coreStateCid) of (False, _) -> pure () (True, None) -> abort "claim fee due: pass the CoreState" (True, Some csCid) -> do - csGg <- exercise csCid GetGuardianGovernance with reader = consumer - assertMsg "core state guardianGovernance mismatch" (csGg == guardianGovernance) csOperator <- exercise csCid ChargeFee with payer = consumer fee = claimFee @@ -486,14 +454,12 @@ template ReplayRootRegistry -- authority). Mirrors a Sui @EmitterCap@. template Emitter with - operator : Party - guardianGovernance : Party -- CoreState fee-custody anchor; pinned (not a - -- signatory) in PublishMessage's fee charge - owner : Party - guardianObserver : Party -- read-only guardian observer - emitterId : Int -- registry-allocated - emitterAddress : Bytes32 -- emitterAddressFor operator owner emitterId, set at creation - sequence : Int -- next sequence to assign + operator : Party + owner : Party + guardianObserver : Party -- read-only guardian observer + emitterId : Int -- registry-allocated + emitterAddress : Bytes32 -- emitterAddressFor operator owner emitterId, set at creation + sequence : Int -- next sequence to assign where -- Owner co-signs: only a transaction with the owner's authority can create a -- contract whose derived address is the owner's — the invariant guardians @@ -511,10 +477,10 @@ template Emitter -- directly. Mirrors @Implementation.publishMessage@. -- -- The 'messageFee' is charged through the referenced 'CoreState' (EVM parity: - -- the current fee is read on every call), whose @guardianGovernance@ and - -- operator are pinned so a fake CoreState cannot redirect or waive it. - -- Publishers attach the CoreState disclosure to every publish (cacheable - -- between governance actions, which invalidate the cid transiently — + -- the current fee is read on every call), whose operator is pinned so a fake + -- CoreState cannot waive the fee. Publishers attach the CoreState disclosure + -- to every publish (cacheable between governance actions, which invalidate + -- the cid transiently — -- re-resolve and retry); at fee 0 pass @feeAllocation = None@, but the -- CoreState reference is still required since the fee amount is only knowable -- from it. @@ -535,12 +501,6 @@ template Emitter feeAllocation : Optional (ContractId Allocation, ExtraArgs) controller owner, payer do - -- Pin the fee-custody anchor BEFORE charging: @guardianGovernance@ (not - -- just @operator@, which a forged CoreState would match) determines where - -- the fee lands, so this check rejects a shadow CoreState under a rogue - -- gg before the message fee can be redirected. - csGg <- exercise coreStateCid GetGuardianGovernance with reader = payer - assertMsg "core state guardianGovernance mismatch" (csGg == guardianGovernance) csOperator <- exercise coreStateCid ChargeMessageFee with payer feeAllocation diff --git a/canton/test/daml/Test/TestCore.daml b/canton/test/daml/Test/TestCore.daml index 6c7700b37c..744aa8a9aa 100644 --- a/canton/test/daml/Test/TestCore.daml +++ b/canton/test/daml/Test/TestCore.daml @@ -54,8 +54,8 @@ setupWithInstrument mInstrument = do createCmd (mkInitialCoreState operator guardianGovernance solanaGovernanceChainId defaultGovernanceContract [devnetGuardian] guardianObserver feeInstrument guardianGovernance) - _ <- submit operator do createCmd (mkInitialEmitterRegistry operator guardianGovernance guardianObserver 0) - _ <- submit operator do createCmd (mkInitialReplayRootRegistry operator guardianGovernance 0) + _ <- submit operator do createCmd (mkInitialEmitterRegistry operator guardianObserver 0) + _ <- submit operator do createCmd (mkInitialReplayRootRegistry operator 0) pure (operator, guardianObserver, csId) -- | Register an emitter for @owner@, the production way: the requester submits @@ -168,14 +168,14 @@ testImpersonationBlocked = do -- (a) Operator-alone forgery of Alice's address is impossible. submitMustFail operator do createCmd Emitter with - operator, guardianGovernance = a.guardianGovernance, owner = alice + operator, owner = alice guardianObserver, emitterId = 0 emitterAddress = a.emitterAddress, sequence = 0 -- (b) Operator + Mallory can create a same-id emitter, but its address differs. malloryEm <- submit (actAs operator <> actAs mallory) do createCmd Emitter with - operator, guardianGovernance = a.guardianGovernance, owner = mallory + operator, owner = mallory guardianObserver, emitterId = 0 emitterAddress = emitterAddressFor operator mallory 0, sequence = 0 Some m <- queryContractId mallory malloryEm diff --git a/canton/test/daml/Test/TestFees.daml b/canton/test/daml/Test/TestFees.daml index 3e64fd62a0..fd692b3f5b 100644 --- a/canton/test/daml/Test/TestFees.daml +++ b/canton/test/daml/Test/TestFees.daml @@ -8,7 +8,6 @@ module Test.TestFees where import Daml.Script import DA.Assert ((===)) import DA.Optional (fromSome) -import DA.Text (isInfixOf) import Splice.Api.Token.HoldingV1 (InstrumentId(..)) import Splice.Api.Token.AllocationV1 import Wormhole.Core.Fees (feeToDecimal, emptyExtraArgs) @@ -186,7 +185,7 @@ testRegistrationFee = do Some cs <- queryContractId operator csId bob <- allocateParty "RegFeeBob" regCid <- submit operator do - createCmd (mkInitialEmitterRegistry operator cs.guardianGovernance cs.guardianObserver 500) + createCmd (mkInitialEmitterRegistry operator cs.guardianObserver 500) regDisc <- fromSome <$> queryDisclosure operator regCid csDisc <- fromSome <$> queryDisclosure operator csId -- Fee due: no CoreState / no allocation both rejected. @@ -213,7 +212,7 @@ testClaimFee = do Some cs <- queryContractId operator csId carol <- allocateParty "ClaimFeeCarol" regCid <- submit operator do - createCmd (mkInitialReplayRootRegistry operator cs.guardianGovernance 300) + createCmd (mkInitialReplayRootRegistry operator 300) regDisc <- fromSome <$> queryDisclosure operator regCid csDisc <- fromSome <$> queryDisclosure operator csId submitMustFail (actAs carol <> disclose regDisc <> disclose csDisc) do @@ -245,112 +244,3 @@ testNoSpuriousWithdrawalAuthorization = do pubKeys = [(0, devnetGuardianPubKey)] auths <- query @FeeWithdrawalAuthorization operator auths === [] - ----------------------------------------------------------------------- --- guardianGovernance pin: the shadow-CoreState fee-redirect regression ----------------------------------------------------------------------- --- --- The call sites once pinned only the @operator@ returned by 'ChargeFee' / --- 'ChargeMessageFee', but @operator@ does NOT determine @feeRecipient@ (that is --- @guardianGovernance@). A compromised operator can co-sign a second, --- ledger-valid 'CoreState' under an attacker-controlled gg (= @feeRecipient@) — --- the operator field still matches, so an operator-only pin lets real fees flow --- to the attacker. Each call site now pins 'GetGuardianGovernance' against the --- gg committed on the emitter/registry before charging. Each test builds that --- forgery (real operator + rogue gg) and asserts the failure carries --- "guardianGovernance mismatch" — the operator pin would pass, so the substring --- match proves the fix rather than an incidental failure. - --- | 'PublishMessage' pins the Emitter's guardianGovernance anchor. The forged --- CoreState even has its (rogue) messageFee raised and a matching allocation --- attached, so the charge WOULD succeed against @rogueGov@ — the pin rejects it --- first. -testPublishPinsGuardianGovernance : Script () -testPublishPinsGuardianGovernance = do - (operator, mockAdmin, _realCsId, inst, _realRecipient) <- setupFees - alice <- allocateParty "PinPublishAlice" - rogueGov <- allocateParty "RoguePublishGov" - rogueObserver <- allocateParty "RoguePublishObserver" - emId <- registerEmitter operator alice - -- The shadow CoreState: co-signed by the REAL operator and a throwaway gg it - -- controls, with rogueGov as both guardianGovernance and feeRecipient. - fakeCsId <- submit (actAs operator <> actAs rogueGov) do - createCmd (mkInitialCoreState operator rogueGov solanaGovernanceChainId - defaultGovernanceContract [devnetGuardian] rogueObserver inst rogueGov) - fakeCsId' <- submit operator do - exerciseCmd fakeCsId SubmitGovernanceVAA with - encodedVAA = govSetFeeVAA - pubKeys = [(0, devnetGuardianPubKey)] - fakeDisc <- fromSome <$> queryDisclosure operator fakeCsId' - feePay <- mintAndAllocate mockAdmin alice rogueGov inst (feeToDecimal mockFee) - res <- trySubmit (actAs alice <> disclose fakeDisc) do - exerciseCmd emId PublishMessage with - nonce = 1, payload = "aa", consistencyLevel = 0 - coreStateCid = fakeCsId', payer = alice, feeAllocation = Some feePay - case res of - Left (FailureStatusError fs) -> - assertMsg ("expected a guardianGovernance pin failure, got: " <> fs.message) - ("guardianGovernance mismatch" `isInfixOf` fs.message) - Left err -> abort ("expected a guardianGovernance pin failure, got: " <> show err) - Right _ -> abort "PublishMessage accepted a shadow CoreState under a rogue guardianGovernance" - --- | 'RegisterEmitter' pins the registry's guardianGovernance anchor. The --- nonzero-fee registry is anchored to the real gg; the forged CoreState routes --- its @registrationFee@ to @rogueGov@, and a fully-funded allocation is --- attached — the pin rejects it before the charge. -testRegisterEmitterPinsGuardianGovernance : Script () -testRegisterEmitterPinsGuardianGovernance = do - (operator, mockAdmin, realCsId, inst, _realRecipient) <- setupFees - Some cs <- queryContractId operator realCsId - bob <- allocateParty "PinRegBob" - rogueGov <- allocateParty "RogueRegGov" - rogueObserver <- allocateParty "RogueRegObserver" - -- A real, nonzero-fee registry pinned to the REAL guardianGovernance. - regCid <- submit operator do - createCmd (mkInitialEmitterRegistry operator cs.guardianGovernance cs.guardianObserver 500) - regDisc <- fromSome <$> queryDisclosure operator regCid - fakeCsId <- submit (actAs operator <> actAs rogueGov) do - createCmd (mkInitialCoreState operator rogueGov solanaGovernanceChainId - defaultGovernanceContract [devnetGuardian] rogueObserver inst rogueGov) - fakeDisc <- fromSome <$> queryDisclosure operator fakeCsId - feePay <- mintAndAllocate mockAdmin bob rogueGov inst (feeToDecimal 500) - res <- trySubmit (actAs bob <> disclose regDisc <> disclose fakeDisc) do - exerciseCmd regCid RegisterEmitter with - requester = bob - coreStateCid = Some fakeCsId - feeAllocation = Some feePay - case res of - Left (FailureStatusError fs) -> - assertMsg ("expected a guardianGovernance pin failure, got: " <> fs.message) - ("guardianGovernance mismatch" `isInfixOf` fs.message) - Left err -> abort ("expected a guardianGovernance pin failure, got: " <> show err) - Right _ -> abort "RegisterEmitter accepted a shadow CoreState under a rogue guardianGovernance" - --- | 'ClaimReplayRoot' pins the registry's guardianGovernance anchor. Same --- forgery, routing the @claimFee@ to @rogueGov@; the pin rejects it before the --- charge. -testClaimReplayRootPinsGuardianGovernance : Script () -testClaimReplayRootPinsGuardianGovernance = do - (operator, mockAdmin, realCsId, inst, _realRecipient) <- setupFees - Some cs <- queryContractId operator realCsId - carol <- allocateParty "PinClaimCarol" - rogueGov <- allocateParty "RogueClaimGov" - rogueObserver <- allocateParty "RogueClaimObserver" - regCid <- submit operator do - createCmd (mkInitialReplayRootRegistry operator cs.guardianGovernance 300) - regDisc <- fromSome <$> queryDisclosure operator regCid - fakeCsId <- submit (actAs operator <> actAs rogueGov) do - createCmd (mkInitialCoreState operator rogueGov solanaGovernanceChainId - defaultGovernanceContract [devnetGuardian] rogueObserver inst rogueGov) - fakeDisc <- fromSome <$> queryDisclosure operator fakeCsId - feePay <- mintAndAllocate mockAdmin carol rogueGov inst (feeToDecimal 300) - res <- trySubmit (actAs carol <> disclose regDisc <> disclose fakeDisc) do - exerciseCmd regCid ClaimReplayRoot with - consumer = carol, splitThreshold = 4 - coreStateCid = Some fakeCsId, feeAllocation = Some feePay - case res of - Left (FailureStatusError fs) -> - assertMsg ("expected a guardianGovernance pin failure, got: " <> fs.message) - ("guardianGovernance mismatch" `isInfixOf` fs.message) - Left err -> abort ("expected a guardianGovernance pin failure, got: " <> show err) - Right _ -> abort "ClaimReplayRoot accepted a shadow CoreState under a rogue guardianGovernance" diff --git a/canton/test/daml/Test/TestGuardianObserver.daml b/canton/test/daml/Test/TestGuardianObserver.daml index a5fa48c7dc..f69fd5f5c5 100644 --- a/canton/test/daml/Test/TestGuardianObserver.daml +++ b/canton/test/daml/Test/TestGuardianObserver.daml @@ -88,7 +88,7 @@ testObserverHasNoAuthority = do -- Cannot create contracts bearing the real signatory parties (no authority). submitMustFail guardianObserver do createCmd Emitter with - operator, guardianGovernance = cs.guardianGovernance, owner = alice + operator, owner = alice emitterId = 0 emitterAddress = emitterAddressFor operator alice 0, sequence = 0 guardianObserver