From 5800a5efbcb078f21dc9b2a0829469c6655261e1 Mon Sep 17 00:00:00 2001 From: Paul Brauner Date: Mon, 11 Nov 2024 13:27:07 +0100 Subject: [PATCH 01/13] Add metadata test for local contracts, fix logic --- .../daml/lf/speedy/SBuiltin.scala | 103 +++- .../test/daml/upgrades/MetadataChanged.daml | 550 ++++++++++++++++++ 2 files changed, 650 insertions(+), 3 deletions(-) create mode 100644 sdk/daml-script/test/daml/upgrades/MetadataChanged.daml diff --git a/sdk/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala b/sdk/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala index f971d2b40b6e..0f7eb689c1c9 100644 --- a/sdk/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala +++ b/sdk/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala @@ -2394,7 +2394,104 @@ private[lf] object SBuiltin { coid: V.ContractId, )(f: (Option[Ref.PackageName], SValue) => Control[Question.Update]): Control[Question.Update] = { - def importContract(coinst: V.ContractInstance) = { + def checkContractUpgradable( + original: ContractInfo, + recomputed: ContractInfo, + )( + k: () => Control[Question.Update] + ): Control[Question.Update] = { + def check[T](recomputed: T, original: T)(desc: String): Either[String, Unit] = + Either.cond(recomputed == original, (), s"$desc mismatch: $original vs $recomputed") + + { + for { + _ <- check(recomputed.signatories, original.signatories)("signatories") + _ <- check(recomputed.observers, original.observers)("observers") + _ <- check(recomputed.keyOpt.map(_.maintainers), original.keyOpt.map(_.maintainers))( + "key maintainers" + ) + _ <- check(recomputed.keyOpt.map(_.globalKey.key), original.keyOpt.map(_.globalKey.key))( + "key value" + ) + } yield () + }.fold( + errorMsg => + Control.Error( + IE.Upgrade( + IE.Upgrade.ValidationFailed( + coid = coid, + srcTemplateId = original.templateId, + dstTemplateId = recomputed.templateId, + signatories = recomputed.signatories, + observers = recomputed.observers, + keyOpt = recomputed.keyOpt.map(_.globalKeyWithMaintainers), + msg = errorMsg, + ) + ) + ), + _ => k(), + ) + } + + def importLocalContract(contract: ContractInfo) = { + val coinstArg = contract.arg + val srcTmplId = contract.templateId + val (upgradingIsEnabled, dstTmplId) = optTargetTemplateId match { + case Some(tycon) + if V + .ContractInstance(contract.packageName, contract.templateId, contract.arg) + .upgradable => + (true, tycon) + case _ => + (false, srcTmplId) // upgrading not enabled; import at source type + } + if (srcTmplId.qualifiedName != dstTmplId.qualifiedName) { + Control.Error( + IE.WronglyTypedContract(coid, dstTmplId, srcTmplId) + ) + } else + machine.ensurePackageIsLoaded( + dstTmplId.packageId, + language.Reference.Template(dstTmplId), + ) { () => + importValue(machine, dstTmplId, coinstArg) { templateArg => + getContractInfo( + machine, + coid, + dstTmplId, + templateArg, + allowCatchingContractInfoErrors = false, + ) { upgradedContract => + ensureContractActive(machine, coid, upgradedContract.templateId) { + + machine.checkContractVisibility(coid, upgradedContract) + machine.enforceLimitAddInputContract() + machine.enforceLimitSignatoriesAndObservers(coid, upgradedContract) + + // In Validation mode, we always call validateContractInfo + // In Submission mode, we only call validateContractInfo when src != dest + val needValidationCall: Boolean = + if (machine.validating) { + upgradingIsEnabled + } else { + // we already check qualified names match + upgradingIsEnabled && (srcTmplId.packageId != dstTmplId.packageId) + } + if (needValidationCall) { + + checkContractUpgradable(contract, upgradedContract) { () => + f(upgradedContract.packageName, upgradedContract.any) + } + } else { + f(upgradedContract.packageName, upgradedContract.any) + } + } + } + } + } + } + + def importGlobalContract(coinst: V.ContractInstance) = { val V.ContractInstance(_, srcTmplId, coinstArg) = coinst val (upgradingIsEnabled, dstTmplId) = optTargetTemplateId match { case Some(tycon) if coinst.upgradable => @@ -2463,12 +2560,12 @@ private[lf] object SBuiltin { // import its value and validate its contract info again. f(contract.packageName, SValue.SAnyContract(templateId, templateArg)) } else { - importContract(V.ContractInstance(contract.packageName, templateId, contract.arg)) + importLocalContract(contract) } } } case None => - machine.lookupGlobalContract(coid)(importContract) + machine.lookupGlobalContract(coid)(importGlobalContract) } } diff --git a/sdk/daml-script/test/daml/upgrades/MetadataChanged.daml b/sdk/daml-script/test/daml/upgrades/MetadataChanged.daml new file mode 100644 index 000000000000..ad40f39b33c9 --- /dev/null +++ b/sdk/daml-script/test/daml/upgrades/MetadataChanged.daml @@ -0,0 +1,550 @@ +-- Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. +-- SPDX-License-Identifier: Apache-2.0 + +module MetadataChanged (main) where + +import DA.Text (isInfixOf) + +import UpgradeTestLib + +import qualified V1.MetadataChangedMod as V1 +import qualified V2.MetadataChangedMod as V2 +import qualified MetadataChangedClientMod as Client +import V1.Common +import DA.Exception + +main : TestTree +main = tests + [ subtree "local" + [ + subtree "exercise" + [ ("changed signatories", exerciseSignatoriesChangedLocal) + , ("changed observers", exerciseObserversChangedLocal) + , ("changed key", exerciseKeyChangedLocal) + , ("changed maintainer", exerciseMaintainerChangedLocal) + ] + , subtree "exercise by interface" + [ ("changed signatories", exerciseByInterfaceSignatoriesChangedLocal) + , ("changed observers", exerciseByInterfaceObserversChangedLocal) + , ("changed key", exerciseByInterfaceKeyChangedLocal) + , ("changed maintainer", exerciseByInterfaceMaintainerChangedLocal) + ] + , subtree "exercise by key" + [ ("changed signatories", exerciseByKeySignatoriesChangedLocal) + , ("changed observers", exerciseByKeyObserversChangedLocal) + , ("changed key", exerciseByKeyKeyChangedLocal) + , ("changed maintainer", exerciseByKeyMaintainerChangedLocal) + ] + ] + , subtree "global" + [ + subtree "exercise" + [ ("changed signatories", exerciseSignatoriesChangedGlobal) + , ("changed observers", exerciseObserversChangedGlobal) + , ("changed key", exerciseKeyChangedGlobal) + , ("changed maintainer", exerciseMaintainerChangedGlobal) + ] + , subtree "exercise by interface" + [ ("changed signatories", exerciseByInterfaceSignatoriesChangedGlobal) + , ("changed observers", exerciseByInterfaceObserversChangedGlobal) + , ("changed key", exerciseByInterfaceKeyChangedGlobal) + , ("changed maintainer", exerciseByInterfaceMaintainerChangedGlobal) + ] + , subtree "exercise by key" + [ ("changed signatories", exerciseByKeySignatoriesChangedGlobal) + , ("changed observers", exerciseByKeyObserversChangedGlobal) + , ("changed key", exerciseByKeyKeyChangedGlobal) + , ("changed maintainer", exerciseByKeyMaintainerChangedGlobal) + ] + ] + ] + +{- PACKAGE +name: metadata-changed-common +versions: 1 +-} + +{- MODULE +package: metadata-changed-common +contents: | + module Common where + + data IV = IV with + ctl : Party + + interface I where + viewtype IV + + getVersion : Text + nonconsuming choice DynamicCall: Text + controller (view this).ctl + do + pure $ getVersion this + + data MyKey = MyKey with + p1 : Party + p2 : Party + n : Int + deriving (Eq, Show) +-} + +{- PACKAGE +name: metadata-changed +versions: 2 +depends: metadata-changed-common-1.0.0 +-} + +{- MODULE +package: metadata-changed +contents: | + module MetadataChangedMod where + + import V1.Common + + template ChangedSignatories + with + party1 : Party + party2 : Party + where + signatory party1 -- @V 1 + signatory party2 -- @V 2 + key (MyKey party1 party2 0) : MyKey + maintainer key.p1 + + nonconsuming choice ChangedSignatoriesCall : Text + controller party1 + do pure "V1" -- @V 1 + do pure "V2" -- @V 2 + + interface instance I for ChangedSignatories where + view = IV party1 + getVersion = "V1" -- @V 1 + getVersion = "V2" -- @V 2 + + template ChangedObservers + with + party1 : Party + party2 : Party + where + signatory party1 + observer party1 -- @V 1 + observer party2 -- @V 2 + key (MyKey party1 party2 0) : MyKey + maintainer key.p1 + + nonconsuming choice ChangedObserversCall : Text + controller party1 + do pure "V1" -- @V 1 + do pure "V2" -- @V 2 + + interface instance I for ChangedObservers where + view = IV party1 + getVersion = "V1" -- @V 1 + getVersion = "V2" -- @V 2 + + template ChangedKey + with + party1 : Party + party2 : Party + where + signatory party1 + observer party1 + key (MyKey party1 party2 0) : MyKey -- @V 1 + key (MyKey party1 party2 1) : MyKey -- @V 2 + maintainer key.p1 + + nonconsuming choice ChangedKeyCall : Text + controller party1 + do pure "V1" -- @V 1 + do pure "V2" -- @V 2 + + interface instance I for ChangedKey where + view = IV party1 + getVersion = "V1" -- @V 1 + getVersion = "V2" -- @V 2 + + template ChangedMaintainer + with + party1 : Party + party2 : Party + where + signatory party1 + observer party1 + key (MyKey party1 party2 0) : MyKey + maintainer key.p1 -- @V 1 + maintainer key.p2 -- @V 2 + + nonconsuming choice ChangedMaintainerCall : Text + controller party1 + do pure "V1" -- @V 1 + do pure "V2" -- @V 2 + + interface instance I for ChangedMaintainer where + view = IV party1 + getVersion = "V1" -- @V 1 + getVersion = "V2" -- @V 2 +-} + +{- PACKAGE +name: metadata-changed-client +versions: 1 +depends: | + metadata-changed-1.0.0 + metadata-changed-2.0.0 + metadata-changed-common-1.0.0 +-} + +{- MODULE +package: metadata-changed-client +contents: | + module MetadataChangedClientMod where + + import DA.Exception + import qualified V1.MetadataChangedMod as V1 + import qualified V2.MetadataChangedMod as V2 + import V1.Common + + template MetadataChangedClient + with + party : Party + where + signatory party + + -- CHANGED SIGNATORIES + + nonconsuming choice ExerciseSignatoriesChanged : () + with + p1 : Party + p2 : Party + controller [p1, p2] + do + cid <- create (V1.ChangedSignatories p1 p2) + exercise @V2.ChangedSignatories (coerceContractId cid) V2.ChangedSignatoriesCall + pure () + + nonconsuming choice ExerciseByInterfaceSignatoriesChanged : () + with + p1 : Party + p2 : Party + controller [p1, p2] + do + cid <- create (V1.ChangedSignatories p1 p2) + exercise @I (coerceContractId cid) DynamicCall + pure () + + nonconsuming choice ExerciseByKeySignatoriesChanged : () + with + p1 : Party + p2 : Party + controller [p1, p2] + do + cid <- create (V1.ChangedSignatories p1 p2) + exerciseByKey @V2.ChangedSignatories (MyKey p1 p2 0) V2.ChangedSignatoriesCall + pure () + + -- CHANGED OBSERVERS + + nonconsuming choice ExerciseObserversChanged : () + with + p1 : Party + p2 : Party + controller [p1, p2] + do + cid <- create (V1.ChangedObservers p1 p2) + exercise @V2.ChangedObservers (coerceContractId cid) V2.ChangedObserversCall + pure () + + nonconsuming choice ExerciseByInterfaceObserversChanged : () + with + p1 : Party + p2 : Party + controller [p1, p2] + do + cid <- create (V1.ChangedObservers p1 p2) + exercise @I (coerceContractId cid) DynamicCall + pure () + + nonconsuming choice ExerciseByKeyObserversChanged : () + with + p1 : Party + p2 : Party + controller [p1, p2] + do + cid <- create (V1.ChangedObservers p1 p2) + exerciseByKey @V2.ChangedObservers (MyKey p1 p2 0) V2.ChangedObserversCall + pure () + + -- CHANGED KEY + + nonconsuming choice ExerciseKeyChanged : () + with + p1 : Party + p2 : Party + controller [p1, p2] + do + cid <- create (V1.ChangedKey p1 p2) + exercise @V2.ChangedKey (coerceContractId cid) V2.ChangedKeyCall + pure () + + nonconsuming choice ExerciseByInterfaceKeyChanged : () + with + p1 : Party + p2 : Party + controller [p1, p2] + do + cid <- create (V1.ChangedKey p1 p2) + exercise @I (coerceContractId cid) DynamicCall + pure () + + nonconsuming choice ExerciseByKeyKeyChanged : () + with + p1 : Party + p2 : Party + controller [p1, p2] + do + cid <- create (V1.ChangedKey p1 p2) + exerciseByKey @V2.ChangedKey (MyKey p1 p2 0) V2.ChangedKeyCall + pure () + + -- CHANGED MAINTAINER + + nonconsuming choice ExerciseMaintainerChanged : () + with + p1 : Party + p2 : Party + controller [p1, p2] + do + cid <- create (V1.ChangedMaintainer p1 p2) + exercise @V2.ChangedMaintainer (coerceContractId cid) V2.ChangedMaintainerCall + pure () + + nonconsuming choice ExerciseByInterfaceMaintainerChanged : () + with + p1 : Party + p2 : Party + controller [p1, p2] + do + cid <- create (V1.ChangedMaintainer p1 p2) + exercise @I (coerceContractId cid) DynamicCall + pure () + + nonconsuming choice ExerciseByKeyMaintainerChanged : () + with + p1 : Party + p2 : Party + controller [p1, p2] + do + cid <- create (V1.ChangedMaintainer p1 p2) + exerciseByKey @V2.ChangedMaintainer (MyKey p1 p2 0) V2.ChangedMaintainerCall + pure () +-} + +{- CHANGED SIGNATORIES -} + +exerciseSignatoriesChangedLocal : Test +exerciseSignatoriesChangedLocal = test $ do + a <- allocateParty "alice" + b <- allocateParty "bob" + cid <- a `submit` createExactCmd (Client.MetadataChangedClient a) + expectMetadataChangedError =<< + trySubmitMulti [a,b] [] (exerciseCmd cid (Client.ExerciseSignatoriesChanged a b)) + +exerciseByInterfaceSignatoriesChangedLocal : Test +exerciseByInterfaceSignatoriesChangedLocal = test $ do + a <- allocateParty "alice" + b <- allocateParty "bob" + cid <- a `submit` createExactCmd (Client.MetadataChangedClient a) + expectMetadataChangedError =<< + trySubmitMulti [a,b] [] (exerciseCmd cid (Client.ExerciseByInterfaceSignatoriesChanged a b)) + +exerciseByKeySignatoriesChangedLocal : Test +exerciseByKeySignatoriesChangedLocal = test $ do + a <- allocateParty "alice" + b <- allocateParty "bob" + cid <- a `submit` createExactCmd (Client.MetadataChangedClient a) + expectMetadataChangedError =<< + trySubmitMulti [a,b] [] (exerciseCmd cid (Client.ExerciseByKeySignatoriesChanged a b)) + +exerciseSignatoriesChangedGlobal : Test +exerciseSignatoriesChangedGlobal = test $ do + a <- allocateParty "alice" + b <- allocateParty "bob" + cid <- a `submit` createExactCmd (V1.ChangedSignatories a b) + expectMetadataChangedError =<< + trySubmitMulti [a,b] [] (exerciseExactCmd @V2.ChangedSignatories (coerceContractId cid) V2.ChangedSignatoriesCall) + +exerciseByInterfaceSignatoriesChangedGlobal : Test +exerciseByInterfaceSignatoriesChangedGlobal = test $ do + a <- allocateParty "alice" + b <- allocateParty "bob" + cid <- a `submit` createExactCmd (V1.ChangedSignatories a b) + expectMetadataChangedError =<< + trySubmitMulti [a,b] [] (exerciseExactCmd @I (coerceContractId cid) DynamicCall) + +exerciseByKeySignatoriesChangedGlobal : Test +exerciseByKeySignatoriesChangedGlobal = test $ do + a <- allocateParty "alice" + b <- allocateParty "bob" + cid <- a `submit` createExactCmd (V1.ChangedSignatories a b) + expectMetadataChangedError =<< + trySubmitMulti [a,b] [] (exerciseByKeyExactCmd @V2.ChangedSignatories (MyKey a b 0) V2.ChangedSignatoriesCall) + +{- CHANGED OBSERVERS -} + +exerciseObserversChangedLocal : Test +exerciseObserversChangedLocal = test $ do + a <- allocateParty "alice" + b <- allocateParty "bob" + cid <- a `submit` createExactCmd (Client.MetadataChangedClient a) + expectMetadataChangedError =<< + trySubmitMulti [a,b] [] (exerciseCmd cid (Client.ExerciseObserversChanged a b)) + +exerciseByInterfaceObserversChangedLocal : Test +exerciseByInterfaceObserversChangedLocal = test $ do + a <- allocateParty "alice" + b <- allocateParty "bob" + cid <- a `submit` createExactCmd (Client.MetadataChangedClient a) + expectMetadataChangedError =<< + trySubmitMulti [a,b] [] (exerciseCmd cid (Client.ExerciseByInterfaceObserversChanged a b)) + +exerciseByKeyObserversChangedLocal : Test +exerciseByKeyObserversChangedLocal = test $ do + a <- allocateParty "alice" + b <- allocateParty "bob" + cid <- a `submit` createExactCmd (Client.MetadataChangedClient a) + expectMetadataChangedError =<< + trySubmitMulti [a,b] [] (exerciseCmd cid (Client.ExerciseByKeyObserversChanged a b)) + +exerciseObserversChangedGlobal : Test +exerciseObserversChangedGlobal = test $ do + a <- allocateParty "alice" + b <- allocateParty "bob" + cid <- a `submit` createExactCmd (V1.ChangedObservers a b) + expectMetadataChangedError =<< + trySubmitMulti [a,b] [] (exerciseExactCmd @V2.ChangedObservers (coerceContractId cid) V2.ChangedObserversCall) + +exerciseByInterfaceObserversChangedGlobal : Test +exerciseByInterfaceObserversChangedGlobal = test $ do + a <- allocateParty "alice" + b <- allocateParty "bob" + cid <- a `submit` createExactCmd (V1.ChangedObservers a b) + expectMetadataChangedError =<< + trySubmitMulti [a,b] [] (exerciseExactCmd @I (coerceContractId cid) DynamicCall) + +exerciseByKeyObserversChangedGlobal : Test +exerciseByKeyObserversChangedGlobal = test $ do + a <- allocateParty "alice" + b <- allocateParty "bob" + cid <- a `submit` createExactCmd (V1.ChangedObservers a b) + expectMetadataChangedError =<< + trySubmitMulti [a,b] [] (exerciseByKeyExactCmd @V2.ChangedObservers (MyKey a b 0) V2.ChangedObserversCall) + +{- CHANGED KEY -} + +exerciseKeyChangedLocal : Test +exerciseKeyChangedLocal = test $ do + a <- allocateParty "alice" + b <- allocateParty "bob" + cid <- a `submit` createExactCmd (Client.MetadataChangedClient a) + expectMetadataChangedError =<< + trySubmitMulti [a,b] [] (exerciseCmd cid (Client.ExerciseKeyChanged a b)) + +exerciseByInterfaceKeyChangedLocal : Test +exerciseByInterfaceKeyChangedLocal = test $ do + a <- allocateParty "alice" + b <- allocateParty "bob" + cid <- a `submit` createExactCmd (Client.MetadataChangedClient a) + expectMetadataChangedError =<< + trySubmitMulti [a,b] [] (exerciseCmd cid (Client.ExerciseByInterfaceKeyChanged a b)) + +exerciseByKeyKeyChangedLocal : Test +exerciseByKeyKeyChangedLocal = test $ do + a <- allocateParty "alice" + b <- allocateParty "bob" + cid <- a `submit` createExactCmd (Client.MetadataChangedClient a) + expectMetadataChangedError =<< + trySubmitMulti [a,b] [] (exerciseCmd cid (Client.ExerciseByKeyKeyChanged a b)) + +exerciseKeyChangedGlobal : Test +exerciseKeyChangedGlobal = test $ do + a <- allocateParty "alice" + b <- allocateParty "bob" + cid <- a `submit` createExactCmd (V1.ChangedKey a b) + expectMetadataChangedError =<< + trySubmitMulti [a,b] [] (exerciseExactCmd @V2.ChangedKey (coerceContractId cid) V2.ChangedKeyCall) + +exerciseByInterfaceKeyChangedGlobal : Test +exerciseByInterfaceKeyChangedGlobal = test $ do + a <- allocateParty "alice" + b <- allocateParty "bob" + cid <- a `submit` createExactCmd (V1.ChangedKey a b) + expectMetadataChangedError =<< + trySubmitMulti [a,b] [] (exerciseExactCmd @I (coerceContractId cid) DynamicCall) + +exerciseByKeyKeyChangedGlobal : Test +exerciseByKeyKeyChangedGlobal = test $ do + a <- allocateParty "alice" + b <- allocateParty "bob" + cid <- a `submit` createExactCmd (V1.ChangedKey a b) + expectMetadataChangedError =<< + trySubmitMulti [a,b] [] (exerciseByKeyExactCmd @V2.ChangedKey (MyKey a b 0) V2.ChangedKeyCall) + +{- CHANGED MAINTAINER -} + +exerciseMaintainerChangedLocal : Test +exerciseMaintainerChangedLocal = test $ do + a <- allocateParty "alice" + b <- allocateParty "bob" + cid <- a `submit` createExactCmd (Client.MetadataChangedClient a) + expectMetadataChangedError =<< + trySubmitMulti [a,b] [] (exerciseCmd cid (Client.ExerciseMaintainerChanged a b)) + +exerciseByInterfaceMaintainerChangedLocal : Test +exerciseByInterfaceMaintainerChangedLocal = test $ do + a <- allocateParty "alice" + b <- allocateParty "bob" + cid <- a `submit` createExactCmd (Client.MetadataChangedClient a) + expectMetadataChangedError =<< + trySubmitMulti [a,b] [] (exerciseCmd cid (Client.ExerciseByInterfaceMaintainerChanged a b)) + +exerciseByKeyMaintainerChangedLocal : Test +exerciseByKeyMaintainerChangedLocal = test $ do + a <- allocateParty "alice" + b <- allocateParty "bob" + cid <- a `submit` createExactCmd (Client.MetadataChangedClient a) + expectMetadataChangedError =<< + trySubmitMulti [a,b] [] (exerciseCmd cid (Client.ExerciseByKeyMaintainerChanged a b)) + +exerciseMaintainerChangedGlobal : Test +exerciseMaintainerChangedGlobal = test $ do + a <- allocateParty "alice" + b <- allocateParty "bob" + cid <- a `submit` createExactCmd (V1.ChangedMaintainer a b) + expectMetadataChangedError =<< + trySubmitMulti [a,b] [] (exerciseExactCmd @V2.ChangedMaintainer (coerceContractId cid) V2.ChangedMaintainerCall) + +exerciseByInterfaceMaintainerChangedGlobal : Test +exerciseByInterfaceMaintainerChangedGlobal = test $ do + a <- allocateParty "alice" + b <- allocateParty "bob" + cid <- a `submit` createExactCmd (V1.ChangedMaintainer a b) + expectMetadataChangedError =<< + trySubmitMulti [a,b] [] (exerciseExactCmd @I (coerceContractId cid) DynamicCall) + +exerciseByKeyMaintainerChangedGlobal : Test +exerciseByKeyMaintainerChangedGlobal = test $ do + a <- allocateParty "alice" + b <- allocateParty "bob" + cid <- a `submit` createExactCmd (V1.ChangedMaintainer a b) + expectMetadataChangedError =<< + trySubmitMulti [a,b] [] (exerciseByKeyExactCmd @V2.ChangedMaintainer (MyKey a b 0) V2.ChangedMaintainerCall) + +------------------------------------------------------------------------------------------------------------------------ + +expectMetadataChangedError : Either SubmitError a -> Script () +expectMetadataChangedError r = case r of + Right _ -> assertFail "Expected failure but got success" + Left (UpgradeError msg) + | "Verify that neither the signatories, nor the observers, nor the contract key, nor the key's maintainers have changed" `isInfixOf` msg + -> pure () + Left e -> assertFail $ "Expected Upgrade error but got " <> show e From c95b04e4efdd0afaed2af20d32a8190200c07a78 Mon Sep 17 00:00:00 2001 From: Paul Brauner Date: Wed, 13 Nov 2024 14:42:58 +0100 Subject: [PATCH 02/13] factorize the code --- .../daml/lf/speedy/SBuiltin.scala | 96 ++++++------------- 1 file changed, 31 insertions(+), 65 deletions(-) diff --git a/sdk/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala b/sdk/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala index 0f7eb689c1c9..96ff073f256d 100644 --- a/sdk/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala +++ b/sdk/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala @@ -2394,6 +2394,7 @@ private[lf] object SBuiltin { coid: V.ContractId, )(f: (Option[Ref.PackageName], SValue) => Control[Question.Update]): Control[Question.Update] = { + // Checks that the metadata of [original] and [recomputed] are the same, fails with a [Control.Error] if not. def checkContractUpgradable( original: ContractInfo, recomputed: ContractInfo, @@ -2433,14 +2434,29 @@ private[lf] object SBuiltin { ) } - def importLocalContract(contract: ContractInfo) = { - val coinstArg = contract.arg - val srcTmplId = contract.templateId + type ContractInfoChecker = + ContractInfo => (() => Control[Question.Update]) => Control[Question.Update] + + def localContractInfoUpgradeChecker( + originalContractInfo: ContractInfo + ): ContractInfoChecker = + (upgradedContractInfo: ContractInfo) => + checkContractUpgradable(originalContractInfo, upgradedContractInfo) + + def globalContractInfoUpgradeChecker( + coid: V.ContractId, + srcTemplateId: Ref.Identifier, + ): ContractInfoChecker = + (upgradedContractInfo: ContractInfo) => + validateContractInfo(machine, coid, srcTemplateId, upgradedContractInfo) + + def importContract( + coinst: V.ContractInstance, + contractInfoUpgradeChecker: ContractInfoChecker, + ) = { + val V.ContractInstance(_, srcTmplId, coinstArg) = coinst val (upgradingIsEnabled, dstTmplId) = optTargetTemplateId match { - case Some(tycon) - if V - .ContractInstance(contract.packageName, contract.templateId, contract.arg) - .upgradable => + case Some(tycon) if coinst.upgradable => (true, tycon) case _ => (false, srcTmplId) // upgrading not enabled; import at source type @@ -2478,8 +2494,7 @@ private[lf] object SBuiltin { upgradingIsEnabled && (srcTmplId.packageId != dstTmplId.packageId) } if (needValidationCall) { - - checkContractUpgradable(contract, upgradedContract) { () => + contractInfoUpgradeChecker(upgradedContract) { () => f(upgradedContract.packageName, upgradedContract.any) } } else { @@ -2491,60 +2506,6 @@ private[lf] object SBuiltin { } } - def importGlobalContract(coinst: V.ContractInstance) = { - val V.ContractInstance(_, srcTmplId, coinstArg) = coinst - val (upgradingIsEnabled, dstTmplId) = optTargetTemplateId match { - case Some(tycon) if coinst.upgradable => - (true, tycon) - case _ => - (false, srcTmplId) // upgrading not enabled; import at source type - } - if (srcTmplId.qualifiedName != dstTmplId.qualifiedName) { - Control.Error( - IE.WronglyTypedContract(coid, dstTmplId, srcTmplId) - ) - } else - machine.ensurePackageIsLoaded( - dstTmplId.packageId, - language.Reference.Template(dstTmplId), - ) { () => - importValue(machine, dstTmplId, coinstArg) { templateArg => - getContractInfo( - machine, - coid, - dstTmplId, - templateArg, - allowCatchingContractInfoErrors = false, - ) { contract => - ensureContractActive(machine, coid, contract.templateId) { - - machine.checkContractVisibility(coid, contract) - machine.enforceLimitAddInputContract() - machine.enforceLimitSignatoriesAndObservers(coid, contract) - - // In Validation mode, we always call validateContractInfo - // In Submission mode, we only call validateContractInfo when src != dest - val needValidationCall: Boolean = - if (machine.validating) { - upgradingIsEnabled - } else { - // we already check qualified names match - upgradingIsEnabled && (srcTmplId.packageId != dstTmplId.packageId) - } - if (needValidationCall) { - - validateContractInfo(machine, coid, srcTmplId, contract) { () => - f(contract.packageName, contract.any) - } - } else { - f(contract.packageName, contract.any) - } - } - } - } - } - } - machine.getLocalContract(coid) match { case Some((templateId, templateArg)) => ensureContractActive(machine, coid, templateId) { @@ -2560,12 +2521,17 @@ private[lf] object SBuiltin { // import its value and validate its contract info again. f(contract.packageName, SValue.SAnyContract(templateId, templateArg)) } else { - importLocalContract(contract) + importContract( + V.ContractInstance(contract.packageName, templateId, contract.arg), + localContractInfoUpgradeChecker(contract), + ) } } } case None => - machine.lookupGlobalContract(coid)(importGlobalContract) + machine.lookupGlobalContract(coid)(coinst => + importContract(coinst, globalContractInfoUpgradeChecker(coid, coinst.template)) + ) } } From 34b8e56512ed53443dedf013a16c5133d458eabf Mon Sep 17 00:00:00 2001 From: Paul Brauner Date: Thu, 14 Nov 2024 08:57:50 +0100 Subject: [PATCH 03/13] fix tests but also uncovered a problem with interfaces --- .../daml/lf/speedy/UpgradeTest.scala | 35 ++++++------------- 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/sdk/daml-lf/interpreter/src/test/scala/com/digitalasset/daml/lf/speedy/UpgradeTest.scala b/sdk/daml-lf/interpreter/src/test/scala/com/digitalasset/daml/lf/speedy/UpgradeTest.scala index 646bcb5c193c..590b25733145 100644 --- a/sdk/daml-lf/interpreter/src/test/scala/com/digitalasset/daml/lf/speedy/UpgradeTest.scala +++ b/sdk/daml-lf/interpreter/src/test/scala/com/digitalasset/daml/lf/speedy/UpgradeTest.scala @@ -560,10 +560,8 @@ class UpgradeTest(majorLanguageVersion: LanguageMajorVersion) in upure @(ContractId '-pkg1-':M:T) cid """ ) - inside(res) { case Right((_, ValueContractId(cid), verificationRequests)) => - verificationRequests shouldBe List( - UpgradeVerificationRequest(cid, Set(alice), Set(bob), Some(v1_key)) - ) + inside(res) { case Right((_, ValueContractId(_), verificationRequests)) => + verificationRequests shouldBe empty } } @@ -576,11 +574,8 @@ class UpgradeTest(majorLanguageVersion: LanguageMajorVersion) in upure @(ContractId '-pkg1-':M:T) cid """ ) - inside(res) { case Right((_, ValueContractId(cid), verificationRequests)) => - verificationRequests shouldBe List( - UpgradeVerificationRequest(cid, Set(alice), Set(bob), Some(v2_key)), - UpgradeVerificationRequest(cid, Set(alice), Set(bob), Some(v2_key)), - ) + inside(res) { case Right((_, ValueContractId(_), verificationRequests)) => + verificationRequests shouldBe empty } } @@ -592,10 +587,8 @@ class UpgradeTest(majorLanguageVersion: LanguageMajorVersion) in upure @(ContractId '-pkg1-':M:T) cid """ ) - inside(res) { case Right((_, ValueContractId(cid), verificationRequests)) => - verificationRequests shouldBe List( - UpgradeVerificationRequest(cid, Set(alice), Set(bob), Some(v1_key)) - ) + inside(res) { case Right((_, ValueContractId(_), verificationRequests)) => + verificationRequests shouldBe empty } } @@ -608,16 +601,12 @@ class UpgradeTest(majorLanguageVersion: LanguageMajorVersion) in upure @(ContractId '-pkg1-':M:T) cid """ ) - inside(res) { case Right((_, ValueContractId(cid), verificationRequests)) => - verificationRequests shouldBe List( - UpgradeVerificationRequest(cid, Set(alice), Set(bob), Some(v2_key)), - UpgradeVerificationRequest(cid, Set(alice), Set(bob), Some(v2_key)), - ) + inside(res) { case Right((_, ValueContractId(_), verificationRequests)) => + verificationRequests shouldBe empty } } - // TODO(https://github.com/digital-asset/daml/issues/20099): re-enable this test once fixed - "be able to exercise by interface locally created contract using different versions" ignore { + "be able to exercise by interface locally created contract using different versions" in { val res = go( e"""let alice : Party = '-pkg1-':M:mkParty "alice" in ubind @@ -630,10 +619,8 @@ class UpgradeTest(majorLanguageVersion: LanguageMajorVersion) """, packageResolution = Map(Ref.PackageName.assertFromString("-upgrade-test-") -> pkgId2), ) - inside(res) { case Right((_, ValueContractId(cid), verificationRequests)) => - verificationRequests shouldBe List( - UpgradeVerificationRequest(cid, Set(alice), Set(bob), Some(v1_key)) - ) + inside(res) { case Right((_, ValueContractId(_), verificationRequests)) => + verificationRequests shouldBe empty } } From 11820f7bea04ca03ebe3a0e69795ea2030f56994 Mon Sep 17 00:00:00 2001 From: Paul Brauner Date: Thu, 14 Nov 2024 09:11:03 +0100 Subject: [PATCH 04/13] use toInterfaceContractId in daml-script tests --- .../test/daml/upgrades/MetadataChanged.daml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/sdk/daml-script/test/daml/upgrades/MetadataChanged.daml b/sdk/daml-script/test/daml/upgrades/MetadataChanged.daml index ad40f39b33c9..328e9f1dcf13 100644 --- a/sdk/daml-script/test/daml/upgrades/MetadataChanged.daml +++ b/sdk/daml-script/test/daml/upgrades/MetadataChanged.daml @@ -229,7 +229,7 @@ contents: | controller [p1, p2] do cid <- create (V1.ChangedSignatories p1 p2) - exercise @I (coerceContractId cid) DynamicCall + exercise @I (toInterfaceContractId cid) DynamicCall pure () nonconsuming choice ExerciseByKeySignatoriesChanged : () @@ -261,7 +261,7 @@ contents: | controller [p1, p2] do cid <- create (V1.ChangedObservers p1 p2) - exercise @I (coerceContractId cid) DynamicCall + exercise @I (toInterfaceContractId cid) DynamicCall pure () nonconsuming choice ExerciseByKeyObserversChanged : () @@ -293,7 +293,7 @@ contents: | controller [p1, p2] do cid <- create (V1.ChangedKey p1 p2) - exercise @I (coerceContractId cid) DynamicCall + exercise @I (toInterfaceContractId cid) DynamicCall pure () nonconsuming choice ExerciseByKeyKeyChanged : () @@ -325,7 +325,7 @@ contents: | controller [p1, p2] do cid <- create (V1.ChangedMaintainer p1 p2) - exercise @I (coerceContractId cid) DynamicCall + exercise @I (toInterfaceContractId cid) DynamicCall pure () nonconsuming choice ExerciseByKeyMaintainerChanged : () @@ -379,7 +379,7 @@ exerciseByInterfaceSignatoriesChangedGlobal = test $ do b <- allocateParty "bob" cid <- a `submit` createExactCmd (V1.ChangedSignatories a b) expectMetadataChangedError =<< - trySubmitMulti [a,b] [] (exerciseExactCmd @I (coerceContractId cid) DynamicCall) + trySubmitMulti [a,b] [] (exerciseExactCmd @I (toInterfaceContractId cid) DynamicCall) exerciseByKeySignatoriesChangedGlobal : Test exerciseByKeySignatoriesChangedGlobal = test $ do @@ -429,7 +429,7 @@ exerciseByInterfaceObserversChangedGlobal = test $ do b <- allocateParty "bob" cid <- a `submit` createExactCmd (V1.ChangedObservers a b) expectMetadataChangedError =<< - trySubmitMulti [a,b] [] (exerciseExactCmd @I (coerceContractId cid) DynamicCall) + trySubmitMulti [a,b] [] (exerciseExactCmd @I (toInterfaceContractId cid) DynamicCall) exerciseByKeyObserversChangedGlobal : Test exerciseByKeyObserversChangedGlobal = test $ do @@ -479,7 +479,7 @@ exerciseByInterfaceKeyChangedGlobal = test $ do b <- allocateParty "bob" cid <- a `submit` createExactCmd (V1.ChangedKey a b) expectMetadataChangedError =<< - trySubmitMulti [a,b] [] (exerciseExactCmd @I (coerceContractId cid) DynamicCall) + trySubmitMulti [a,b] [] (exerciseExactCmd @I (toInterfaceContractId cid) DynamicCall) exerciseByKeyKeyChangedGlobal : Test exerciseByKeyKeyChangedGlobal = test $ do @@ -529,7 +529,7 @@ exerciseByInterfaceMaintainerChangedGlobal = test $ do b <- allocateParty "bob" cid <- a `submit` createExactCmd (V1.ChangedMaintainer a b) expectMetadataChangedError =<< - trySubmitMulti [a,b] [] (exerciseExactCmd @I (coerceContractId cid) DynamicCall) + trySubmitMulti [a,b] [] (exerciseExactCmd @I (toInterfaceContractId cid) DynamicCall) exerciseByKeyMaintainerChangedGlobal : Test exerciseByKeyMaintainerChangedGlobal = test $ do From 2c97abbd123973951546b456500941e50e3a8722 Mon Sep 17 00:00:00 2001 From: Paul Brauner Date: Thu, 14 Nov 2024 09:46:56 +0100 Subject: [PATCH 05/13] fix typo in pretty-printer --- .../src/main/scala/com/digitalasset/daml/lf/speedy/Pretty.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/Pretty.scala b/sdk/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/Pretty.scala index 24a3edaa1c3a..76be32d09efb 100644 --- a/sdk/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/Pretty.scala +++ b/sdk/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/Pretty.scala @@ -43,7 +43,7 @@ private[lf] object Pretty { char('\'') + text(p) + char('\'') def prettyParties(p: Set[Party]): Doc = - char('{') & intercalate(char(','), p.map(prettyParty)) & char('{') + char('{') & intercalate(char(','), p.map(prettyParty)) & char('}') def prettyDamlException(error: interpretation.Error): Doc = { import interpretation.Error._ From 9b1b1a7a7df7cc656d663dac3b3a55560224bcfa Mon Sep 17 00:00:00 2001 From: Paul Brauner Date: Thu, 14 Nov 2024 11:34:45 +0100 Subject: [PATCH 06/13] add positive tests --- .../test/daml/upgrades/MetadataChanged.daml | 582 ++++++++++++++++-- 1 file changed, 534 insertions(+), 48 deletions(-) diff --git a/sdk/daml-script/test/daml/upgrades/MetadataChanged.daml b/sdk/daml-script/test/daml/upgrades/MetadataChanged.daml index 328e9f1dcf13..2f9f6642dead 100644 --- a/sdk/daml-script/test/daml/upgrades/MetadataChanged.daml +++ b/sdk/daml-script/test/daml/upgrades/MetadataChanged.daml @@ -9,52 +9,122 @@ import UpgradeTestLib import qualified V1.MetadataChangedMod as V1 import qualified V2.MetadataChangedMod as V2 -import qualified MetadataChangedClientMod as Client +import qualified ClientMod as Client import V1.Common import DA.Exception main : TestTree main = tests [ subtree "local" - [ - subtree "exercise" - [ ("changed signatories", exerciseSignatoriesChangedLocal) - , ("changed observers", exerciseObserversChangedLocal) - , ("changed key", exerciseKeyChangedLocal) - , ("changed maintainer", exerciseMaintainerChangedLocal) + [ subtree "exercise" + [ subtree "signatories" + [ ("changed", exerciseSignatoriesChangedLocal) + , ("unchanged", exerciseSignatoriesUnchangedLocal) + ] + , subtree "observers" + [ ("changed", exerciseObserversChangedLocal) + , ("unchanged", exerciseObserversUnchangedLocal) + ] + , subtree "key" + [ ("changed", exerciseKeyChangedLocal) + , ("unchanged", exerciseKeyUnchangedLocal) + ] + , subtree "maintainer" + [ ("changed", exerciseMaintainerChangedLocal) + , ("unchanged", exerciseMaintainerUnchangedLocal) + ] ] - , subtree "exercise by interface" - [ ("changed signatories", exerciseByInterfaceSignatoriesChangedLocal) - , ("changed observers", exerciseByInterfaceObserversChangedLocal) - , ("changed key", exerciseByInterfaceKeyChangedLocal) - , ("changed maintainer", exerciseByInterfaceMaintainerChangedLocal) + , subtree "exercise by interface" + [ subtree "signatories" + [ ("changed", exerciseByInterfaceSignatoriesChangedLocal) + , broken ("unchanged", exerciseByInterfaceSignatoriesUnchangedLocal) + ] + , subtree "observers" + [ ("changed", exerciseByInterfaceObserversChangedLocal) + , broken ("unchanged", exerciseByInterfaceObserversUnchangedLocal) + ] + , subtree "key" + [ ("changed", exerciseByInterfaceKeyChangedLocal) + , broken ("unchanged", exerciseByInterfaceKeyUnchangedLocal) + ] + , subtree "maintainer" + [ ("changed", exerciseByInterfaceMaintainerChangedLocal) + , broken ("unchanged", exerciseByInterfaceMaintainerUnchangedLocal) + ] ] - , subtree "exercise by key" - [ ("changed signatories", exerciseByKeySignatoriesChangedLocal) - , ("changed observers", exerciseByKeyObserversChangedLocal) - , ("changed key", exerciseByKeyKeyChangedLocal) - , ("changed maintainer", exerciseByKeyMaintainerChangedLocal) + , subtree "exercise by key" + [ subtree "signatories" + [ ("changed", exerciseByKeySignatoriesChangedLocal) + , ("unchanged", exerciseByKeySignatoriesUnchangedLocal) + ] + , subtree "observers" + [ ("changed", exerciseByKeyObserversChangedLocal) + , ("unchanged", exerciseByKeyObserversUnchangedLocal) + ] + , subtree "key" + [ ("changed", exerciseByKeyKeyChangedLocal) + , ("unchanged", exerciseByKeyKeyUnchangedLocal) + ] + , subtree "maintainer" + [ ("changed", exerciseByKeyMaintainerChangedLocal) + , ("unchanged", exerciseByKeyMaintainerUnchangedLocal) + ] ] ] , subtree "global" - [ - subtree "exercise" - [ ("changed signatories", exerciseSignatoriesChangedGlobal) - , ("changed observers", exerciseObserversChangedGlobal) - , ("changed key", exerciseKeyChangedGlobal) - , ("changed maintainer", exerciseMaintainerChangedGlobal) + [ subtree "exercise" + [ subtree "signatories" + [ ("changed", exerciseSignatoriesChangedGlobal) + , ("unchanged", exerciseSignatoriesUnchangedGlobal) + ] + , subtree "observers" + [ ("changed", exerciseObserversChangedGlobal) + , ("unchanged", exerciseObserversUnchangedGlobal) + ] + , subtree "key" + [ ("changed", exerciseKeyChangedGlobal) + , ("unchanged", exerciseKeyUnchangedGlobal) + ] + , subtree "maintainer" + [ ("changed", exerciseMaintainerChangedGlobal) + , ("unchanged", exerciseMaintainerUnchangedGlobal) + ] ] , subtree "exercise by interface" - [ ("changed signatories", exerciseByInterfaceSignatoriesChangedGlobal) - , ("changed observers", exerciseByInterfaceObserversChangedGlobal) - , ("changed key", exerciseByInterfaceKeyChangedGlobal) - , ("changed maintainer", exerciseByInterfaceMaintainerChangedGlobal) + [ subtree "signatories" + [ ("changed", exerciseByInterfaceSignatoriesChangedGlobal) + , ("unchanged", exerciseByInterfaceSignatoriesUnchangedGlobal) + ] + , subtree "observers" + [ ("changed", exerciseByInterfaceObserversChangedGlobal) + , ("unchanged", exerciseByInterfaceObserversUnchangedGlobal) + ] + , subtree "key" + [ ("changed", exerciseByInterfaceKeyChangedGlobal) + , ("unchanged", exerciseByInterfaceKeyUnchangedGlobal) + ] + , subtree "maintainer" + [ ("changed", exerciseByInterfaceMaintainerChangedGlobal) + , ("unchanged", exerciseByInterfaceMaintainerUnchangedGlobal) + ] ] - , subtree "exercise by key" - [ ("changed signatories", exerciseByKeySignatoriesChangedGlobal) - , ("changed observers", exerciseByKeyObserversChangedGlobal) - , ("changed key", exerciseByKeyKeyChangedGlobal) - , ("changed maintainer", exerciseByKeyMaintainerChangedGlobal) + , subtree "exercise by key" + [ subtree "signatories" + [ ("changed", exerciseByKeySignatoriesChangedGlobal) + , ("unchanged", exerciseByKeySignatoriesUnchangedGlobal) + ] + , subtree "observers" + [ ("changed", exerciseByKeyObserversChangedGlobal) + , ("unchanged", exerciseByKeyObserversUnchangedGlobal) + ] + , subtree "key" + [ ("changed", exerciseByKeyKeyChangedGlobal) + , ("unchanged", exerciseByKeyKeyUnchangedGlobal) + ] + , subtree "maintainer" + [ ("changed", exerciseByKeyMaintainerChangedGlobal) + , ("unchanged", exerciseByKeyMaintainerUnchangedGlobal) + ] ] ] ] @@ -121,6 +191,26 @@ contents: | getVersion = "V1" -- @V 1 getVersion = "V2" -- @V 2 + template UnchangedSignatories + with + party1 : Party + party2 : Party + where + signatory party1 -- @V 1 + signatory [] <> [party1] -- @V 2 + key (MyKey party1 party2 0) : MyKey + maintainer key.p1 + + nonconsuming choice UnchangedSignatoriesCall : Text + controller party1 + do pure "V1" -- @V 1 + do pure "V2" -- @V 2 + + interface instance I for UnchangedSignatories where + view = IV party1 + getVersion = "V1" -- @V 1 + getVersion = "V2" -- @V 2 + template ChangedObservers with party1 : Party @@ -135,13 +225,34 @@ contents: | nonconsuming choice ChangedObserversCall : Text controller party1 do pure "V1" -- @V 1 - do pure "V2" -- @V 2 + do pure "V2" -- @V 2 interface instance I for ChangedObservers where view = IV party1 getVersion = "V1" -- @V 1 getVersion = "V2" -- @V 2 + template UnchangedObservers + with + party1 : Party + party2 : Party + where + signatory party1 + observer party1 -- @V 1 + observer [] <> [party1] -- @V 2 + key (MyKey party1 party2 0) : MyKey + maintainer key.p1 + + nonconsuming choice UnchangedObserversCall : Text + controller party1 + do pure "V1" -- @V 1 + do pure "V2" -- @V 2 + + interface instance I for UnchangedObservers where + view = IV party1 + getVersion = "V1" -- @V 1 + getVersion = "V2" -- @V 2 + template ChangedKey with party1 : Party @@ -156,13 +267,34 @@ contents: | nonconsuming choice ChangedKeyCall : Text controller party1 do pure "V1" -- @V 1 - do pure "V2" -- @V 2 + do pure "V2" -- @V 2 interface instance I for ChangedKey where view = IV party1 getVersion = "V1" -- @V 1 getVersion = "V2" -- @V 2 + template UnchangedKey + with + party1 : Party + party2 : Party + where + signatory party1 + observer party1 + key (MyKey party1 party2 0) : MyKey -- @V 1 + key (MyKey party1 party2 (0+0)) : MyKey -- @V 2 + maintainer key.p1 + + nonconsuming choice UnchangedKeyCall : Text + controller party1 + do pure "V1" -- @V 1 + do pure "V2" -- @V 2 + + interface instance I for UnchangedKey where + view = IV party1 + getVersion = "V1" -- @V 1 + getVersion = "V2" -- @V 2 + template ChangedMaintainer with party1 : Party @@ -183,6 +315,27 @@ contents: | view = IV party1 getVersion = "V1" -- @V 1 getVersion = "V2" -- @V 2 + + template UnchangedMaintainer + with + party1 : Party + party2 : Party + where + signatory party1 + observer party1 + key (MyKey party1 party2 0) : MyKey + maintainer key.p1 -- @V 1 + maintainer [] <> [key.p1] -- @V 2 + + nonconsuming choice UnchangedMaintainerCall : Text + controller party1 + do pure "V1" -- @V 1 + do pure "V2" -- @V 2 + + interface instance I for UnchangedMaintainer where + view = IV party1 + getVersion = "V1" -- @V 1 + getVersion = "V2" -- @V 2 -} {- PACKAGE @@ -197,14 +350,14 @@ depends: | {- MODULE package: metadata-changed-client contents: | - module MetadataChangedClientMod where + module ClientMod where import DA.Exception import qualified V1.MetadataChangedMod as V1 import qualified V2.MetadataChangedMod as V2 import V1.Common - template MetadataChangedClient + template Client with party : Party where @@ -242,6 +395,38 @@ contents: | exerciseByKey @V2.ChangedSignatories (MyKey p1 p2 0) V2.ChangedSignatoriesCall pure () + -- UNCHANGED SIGNATORIES + + nonconsuming choice ExerciseSignatoriesUnchanged : () + with + p1 : Party + p2 : Party + controller [p1, p2] + do + cid <- create (V1.UnchangedSignatories p1 p2) + exercise @V2.UnchangedSignatories (coerceContractId cid) V2.UnchangedSignatoriesCall + pure () + + nonconsuming choice ExerciseByInterfaceSignatoriesUnchanged : () + with + p1 : Party + p2 : Party + controller [p1, p2] + do + cid <- create (V1.UnchangedSignatories p1 p2) + exercise @I (toInterfaceContractId cid) DynamicCall + pure () + + nonconsuming choice ExerciseByKeySignatoriesUnchanged : () + with + p1 : Party + p2 : Party + controller [p1, p2] + do + cid <- create (V1.UnchangedSignatories p1 p2) + exerciseByKey @V2.UnchangedSignatories (MyKey p1 p2 0) V2.UnchangedSignatoriesCall + pure () + -- CHANGED OBSERVERS nonconsuming choice ExerciseObserversChanged : () @@ -274,6 +459,38 @@ contents: | exerciseByKey @V2.ChangedObservers (MyKey p1 p2 0) V2.ChangedObserversCall pure () + -- UNCHANGED OBSERVERS + + nonconsuming choice ExerciseObserversUnchanged : () + with + p1 : Party + p2 : Party + controller [p1, p2] + do + cid <- create (V1.UnchangedObservers p1 p2) + exercise @V2.UnchangedObservers (coerceContractId cid) V2.UnchangedObserversCall + pure () + + nonconsuming choice ExerciseByInterfaceObserversUnchanged : () + with + p1 : Party + p2 : Party + controller [p1, p2] + do + cid <- create (V1.UnchangedObservers p1 p2) + exercise @I (toInterfaceContractId cid) DynamicCall + pure () + + nonconsuming choice ExerciseByKeyObserversUnchanged : () + with + p1 : Party + p2 : Party + controller [p1, p2] + do + cid <- create (V1.UnchangedObservers p1 p2) + exerciseByKey @V2.UnchangedObservers (MyKey p1 p2 0) V2.UnchangedObserversCall + pure () + -- CHANGED KEY nonconsuming choice ExerciseKeyChanged : () @@ -306,6 +523,38 @@ contents: | exerciseByKey @V2.ChangedKey (MyKey p1 p2 0) V2.ChangedKeyCall pure () + -- UNCHANGED KEY + + nonconsuming choice ExerciseKeyUnchanged : () + with + p1 : Party + p2 : Party + controller [p1, p2] + do + cid <- create (V1.UnchangedKey p1 p2) + exercise @V2.UnchangedKey (coerceContractId cid) V2.UnchangedKeyCall + pure () + + nonconsuming choice ExerciseByInterfaceKeyUnchanged : () + with + p1 : Party + p2 : Party + controller [p1, p2] + do + cid <- create (V1.UnchangedKey p1 p2) + exercise @I (toInterfaceContractId cid) DynamicCall + pure () + + nonconsuming choice ExerciseByKeyKeyUnchanged : () + with + p1 : Party + p2 : Party + controller [p1, p2] + do + cid <- create (V1.UnchangedKey p1 p2) + exerciseByKey @V2.UnchangedKey (MyKey p1 p2 0) V2.UnchangedKeyCall + pure () + -- CHANGED MAINTAINER nonconsuming choice ExerciseMaintainerChanged : () @@ -337,6 +586,38 @@ contents: | cid <- create (V1.ChangedMaintainer p1 p2) exerciseByKey @V2.ChangedMaintainer (MyKey p1 p2 0) V2.ChangedMaintainerCall pure () + + -- UNCHANGED MAINTAINER + + nonconsuming choice ExerciseMaintainerUnchanged : () + with + p1 : Party + p2 : Party + controller [p1, p2] + do + cid <- create (V1.UnchangedMaintainer p1 p2) + exercise @V2.UnchangedMaintainer (coerceContractId cid) V2.UnchangedMaintainerCall + pure () + + nonconsuming choice ExerciseByInterfaceMaintainerUnchanged : () + with + p1 : Party + p2 : Party + controller [p1, p2] + do + cid <- create (V1.UnchangedMaintainer p1 p2) + exercise @I (toInterfaceContractId cid) DynamicCall + pure () + + nonconsuming choice ExerciseByKeyMaintainerUnchanged : () + with + p1 : Party + p2 : Party + controller [p1, p2] + do + cid <- create (V1.UnchangedMaintainer p1 p2) + exerciseByKey @V2.UnchangedMaintainer (MyKey p1 p2 0) V2.UnchangedMaintainerCall + pure () -} {- CHANGED SIGNATORIES -} @@ -345,7 +626,7 @@ exerciseSignatoriesChangedLocal : Test exerciseSignatoriesChangedLocal = test $ do a <- allocateParty "alice" b <- allocateParty "bob" - cid <- a `submit` createExactCmd (Client.MetadataChangedClient a) + cid <- a `submit` createExactCmd (Client.Client a) expectMetadataChangedError =<< trySubmitMulti [a,b] [] (exerciseCmd cid (Client.ExerciseSignatoriesChanged a b)) @@ -353,7 +634,7 @@ exerciseByInterfaceSignatoriesChangedLocal : Test exerciseByInterfaceSignatoriesChangedLocal = test $ do a <- allocateParty "alice" b <- allocateParty "bob" - cid <- a `submit` createExactCmd (Client.MetadataChangedClient a) + cid <- a `submit` createExactCmd (Client.Client a) expectMetadataChangedError =<< trySubmitMulti [a,b] [] (exerciseCmd cid (Client.ExerciseByInterfaceSignatoriesChanged a b)) @@ -361,7 +642,7 @@ exerciseByKeySignatoriesChangedLocal : Test exerciseByKeySignatoriesChangedLocal = test $ do a <- allocateParty "alice" b <- allocateParty "bob" - cid <- a `submit` createExactCmd (Client.MetadataChangedClient a) + cid <- a `submit` createExactCmd (Client.Client a) expectMetadataChangedError =<< trySubmitMulti [a,b] [] (exerciseCmd cid (Client.ExerciseByKeySignatoriesChanged a b)) @@ -389,13 +670,63 @@ exerciseByKeySignatoriesChangedGlobal = test $ do expectMetadataChangedError =<< trySubmitMulti [a,b] [] (exerciseByKeyExactCmd @V2.ChangedSignatories (MyKey a b 0) V2.ChangedSignatoriesCall) +{- UNCHANGED SIGNATORIES -} + +exerciseSignatoriesUnchangedLocal : Test +exerciseSignatoriesUnchangedLocal = test $ do + a <- allocateParty "alice" + b <- allocateParty "bob" + cid <- a `submit` createExactCmd (Client.Client a) + expectSuccess =<< + trySubmitMulti [a,b] [] (exerciseCmd cid (Client.ExerciseSignatoriesUnchanged a b)) + +exerciseByInterfaceSignatoriesUnchangedLocal : Test +exerciseByInterfaceSignatoriesUnchangedLocal = test $ do + a <- allocateParty "alice" + b <- allocateParty "bob" + cid <- a `submit` createExactCmd (Client.Client a) + expectSuccess =<< + trySubmitMulti [a,b] [] (exerciseCmd cid (Client.ExerciseByInterfaceSignatoriesUnchanged a b)) + +exerciseByKeySignatoriesUnchangedLocal : Test +exerciseByKeySignatoriesUnchangedLocal = test $ do + a <- allocateParty "alice" + b <- allocateParty "bob" + cid <- a `submit` createExactCmd (Client.Client a) + expectSuccess =<< + trySubmitMulti [a,b] [] (exerciseCmd cid (Client.ExerciseByKeySignatoriesUnchanged a b)) + +exerciseSignatoriesUnchangedGlobal : Test +exerciseSignatoriesUnchangedGlobal = test $ do + a <- allocateParty "alice" + b <- allocateParty "bob" + cid <- a `submit` createExactCmd (V1.UnchangedSignatories a b) + expectSuccess =<< + trySubmitMulti [a,b] [] (exerciseExactCmd @V2.UnchangedSignatories (coerceContractId cid) V2.UnchangedSignatoriesCall) + +exerciseByInterfaceSignatoriesUnchangedGlobal : Test +exerciseByInterfaceSignatoriesUnchangedGlobal = test $ do + a <- allocateParty "alice" + b <- allocateParty "bob" + cid <- a `submit` createExactCmd (V1.UnchangedSignatories a b) + expectSuccess =<< + trySubmitMulti [a,b] [] (exerciseExactCmd @I (toInterfaceContractId cid) DynamicCall) + +exerciseByKeySignatoriesUnchangedGlobal : Test +exerciseByKeySignatoriesUnchangedGlobal = test $ do + a <- allocateParty "alice" + b <- allocateParty "bob" + cid <- a `submit` createExactCmd (V1.UnchangedSignatories a b) + expectSuccess =<< + trySubmitMulti [a,b] [] (exerciseByKeyExactCmd @V2.UnchangedSignatories (MyKey a b 0) V2.UnchangedSignatoriesCall) + {- CHANGED OBSERVERS -} exerciseObserversChangedLocal : Test exerciseObserversChangedLocal = test $ do a <- allocateParty "alice" b <- allocateParty "bob" - cid <- a `submit` createExactCmd (Client.MetadataChangedClient a) + cid <- a `submit` createExactCmd (Client.Client a) expectMetadataChangedError =<< trySubmitMulti [a,b] [] (exerciseCmd cid (Client.ExerciseObserversChanged a b)) @@ -403,7 +734,7 @@ exerciseByInterfaceObserversChangedLocal : Test exerciseByInterfaceObserversChangedLocal = test $ do a <- allocateParty "alice" b <- allocateParty "bob" - cid <- a `submit` createExactCmd (Client.MetadataChangedClient a) + cid <- a `submit` createExactCmd (Client.Client a) expectMetadataChangedError =<< trySubmitMulti [a,b] [] (exerciseCmd cid (Client.ExerciseByInterfaceObserversChanged a b)) @@ -411,7 +742,7 @@ exerciseByKeyObserversChangedLocal : Test exerciseByKeyObserversChangedLocal = test $ do a <- allocateParty "alice" b <- allocateParty "bob" - cid <- a `submit` createExactCmd (Client.MetadataChangedClient a) + cid <- a `submit` createExactCmd (Client.Client a) expectMetadataChangedError =<< trySubmitMulti [a,b] [] (exerciseCmd cid (Client.ExerciseByKeyObserversChanged a b)) @@ -439,13 +770,63 @@ exerciseByKeyObserversChangedGlobal = test $ do expectMetadataChangedError =<< trySubmitMulti [a,b] [] (exerciseByKeyExactCmd @V2.ChangedObservers (MyKey a b 0) V2.ChangedObserversCall) +{- UNCHANGED OBSERVERS -} + +exerciseObserversUnchangedLocal : Test +exerciseObserversUnchangedLocal = test $ do + a <- allocateParty "alice" + b <- allocateParty "bob" + cid <- a `submit` createExactCmd (Client.Client a) + expectSuccess =<< + trySubmitMulti [a,b] [] (exerciseCmd cid (Client.ExerciseObserversUnchanged a b)) + +exerciseByInterfaceObserversUnchangedLocal : Test +exerciseByInterfaceObserversUnchangedLocal = test $ do + a <- allocateParty "alice" + b <- allocateParty "bob" + cid <- a `submit` createExactCmd (Client.Client a) + expectSuccess =<< + trySubmitMulti [a,b] [] (exerciseCmd cid (Client.ExerciseByInterfaceObserversUnchanged a b)) + +exerciseByKeyObserversUnchangedLocal : Test +exerciseByKeyObserversUnchangedLocal = test $ do + a <- allocateParty "alice" + b <- allocateParty "bob" + cid <- a `submit` createExactCmd (Client.Client a) + expectSuccess =<< + trySubmitMulti [a,b] [] (exerciseCmd cid (Client.ExerciseByKeyObserversUnchanged a b)) + +exerciseObserversUnchangedGlobal : Test +exerciseObserversUnchangedGlobal = test $ do + a <- allocateParty "alice" + b <- allocateParty "bob" + cid <- a `submit` createExactCmd (V1.UnchangedObservers a b) + expectSuccess =<< + trySubmitMulti [a,b] [] (exerciseExactCmd @V2.UnchangedObservers (coerceContractId cid) V2.UnchangedObserversCall) + +exerciseByInterfaceObserversUnchangedGlobal : Test +exerciseByInterfaceObserversUnchangedGlobal = test $ do + a <- allocateParty "alice" + b <- allocateParty "bob" + cid <- a `submit` createExactCmd (V1.UnchangedObservers a b) + expectSuccess =<< + trySubmitMulti [a,b] [] (exerciseExactCmd @I (toInterfaceContractId cid) DynamicCall) + +exerciseByKeyObserversUnchangedGlobal : Test +exerciseByKeyObserversUnchangedGlobal = test $ do + a <- allocateParty "alice" + b <- allocateParty "bob" + cid <- a `submit` createExactCmd (V1.UnchangedObservers a b) + expectSuccess =<< + trySubmitMulti [a,b] [] (exerciseByKeyExactCmd @V2.UnchangedObservers (MyKey a b 0) V2.UnchangedObserversCall) + {- CHANGED KEY -} exerciseKeyChangedLocal : Test exerciseKeyChangedLocal = test $ do a <- allocateParty "alice" b <- allocateParty "bob" - cid <- a `submit` createExactCmd (Client.MetadataChangedClient a) + cid <- a `submit` createExactCmd (Client.Client a) expectMetadataChangedError =<< trySubmitMulti [a,b] [] (exerciseCmd cid (Client.ExerciseKeyChanged a b)) @@ -453,7 +834,7 @@ exerciseByInterfaceKeyChangedLocal : Test exerciseByInterfaceKeyChangedLocal = test $ do a <- allocateParty "alice" b <- allocateParty "bob" - cid <- a `submit` createExactCmd (Client.MetadataChangedClient a) + cid <- a `submit` createExactCmd (Client.Client a) expectMetadataChangedError =<< trySubmitMulti [a,b] [] (exerciseCmd cid (Client.ExerciseByInterfaceKeyChanged a b)) @@ -461,7 +842,7 @@ exerciseByKeyKeyChangedLocal : Test exerciseByKeyKeyChangedLocal = test $ do a <- allocateParty "alice" b <- allocateParty "bob" - cid <- a `submit` createExactCmd (Client.MetadataChangedClient a) + cid <- a `submit` createExactCmd (Client.Client a) expectMetadataChangedError =<< trySubmitMulti [a,b] [] (exerciseCmd cid (Client.ExerciseByKeyKeyChanged a b)) @@ -489,13 +870,63 @@ exerciseByKeyKeyChangedGlobal = test $ do expectMetadataChangedError =<< trySubmitMulti [a,b] [] (exerciseByKeyExactCmd @V2.ChangedKey (MyKey a b 0) V2.ChangedKeyCall) +{- UNCHANGED KEY -} + +exerciseKeyUnchangedLocal : Test +exerciseKeyUnchangedLocal = test $ do + a <- allocateParty "alice" + b <- allocateParty "bob" + cid <- a `submit` createExactCmd (Client.Client a) + expectSuccess =<< + trySubmitMulti [a,b] [] (exerciseCmd cid (Client.ExerciseKeyUnchanged a b)) + +exerciseByInterfaceKeyUnchangedLocal : Test +exerciseByInterfaceKeyUnchangedLocal = test $ do + a <- allocateParty "alice" + b <- allocateParty "bob" + cid <- a `submit` createExactCmd (Client.Client a) + expectSuccess =<< + trySubmitMulti [a,b] [] (exerciseCmd cid (Client.ExerciseByInterfaceKeyUnchanged a b)) + +exerciseByKeyKeyUnchangedLocal : Test +exerciseByKeyKeyUnchangedLocal = test $ do + a <- allocateParty "alice" + b <- allocateParty "bob" + cid <- a `submit` createExactCmd (Client.Client a) + expectSuccess =<< + trySubmitMulti [a,b] [] (exerciseCmd cid (Client.ExerciseByKeyKeyUnchanged a b)) + +exerciseKeyUnchangedGlobal : Test +exerciseKeyUnchangedGlobal = test $ do + a <- allocateParty "alice" + b <- allocateParty "bob" + cid <- a `submit` createExactCmd (V1.UnchangedKey a b) + expectSuccess =<< + trySubmitMulti [a,b] [] (exerciseExactCmd @V2.UnchangedKey (coerceContractId cid) V2.UnchangedKeyCall) + +exerciseByInterfaceKeyUnchangedGlobal : Test +exerciseByInterfaceKeyUnchangedGlobal = test $ do + a <- allocateParty "alice" + b <- allocateParty "bob" + cid <- a `submit` createExactCmd (V1.UnchangedKey a b) + expectSuccess =<< + trySubmitMulti [a,b] [] (exerciseExactCmd @I (toInterfaceContractId cid) DynamicCall) + +exerciseByKeyKeyUnchangedGlobal : Test +exerciseByKeyKeyUnchangedGlobal = test $ do + a <- allocateParty "alice" + b <- allocateParty "bob" + cid <- a `submit` createExactCmd (V1.UnchangedKey a b) + expectSuccess =<< + trySubmitMulti [a,b] [] (exerciseByKeyExactCmd @V2.UnchangedKey (MyKey a b 0) V2.UnchangedKeyCall) + {- CHANGED MAINTAINER -} exerciseMaintainerChangedLocal : Test exerciseMaintainerChangedLocal = test $ do a <- allocateParty "alice" b <- allocateParty "bob" - cid <- a `submit` createExactCmd (Client.MetadataChangedClient a) + cid <- a `submit` createExactCmd (Client.Client a) expectMetadataChangedError =<< trySubmitMulti [a,b] [] (exerciseCmd cid (Client.ExerciseMaintainerChanged a b)) @@ -503,7 +934,7 @@ exerciseByInterfaceMaintainerChangedLocal : Test exerciseByInterfaceMaintainerChangedLocal = test $ do a <- allocateParty "alice" b <- allocateParty "bob" - cid <- a `submit` createExactCmd (Client.MetadataChangedClient a) + cid <- a `submit` createExactCmd (Client.Client a) expectMetadataChangedError =<< trySubmitMulti [a,b] [] (exerciseCmd cid (Client.ExerciseByInterfaceMaintainerChanged a b)) @@ -511,7 +942,7 @@ exerciseByKeyMaintainerChangedLocal : Test exerciseByKeyMaintainerChangedLocal = test $ do a <- allocateParty "alice" b <- allocateParty "bob" - cid <- a `submit` createExactCmd (Client.MetadataChangedClient a) + cid <- a `submit` createExactCmd (Client.Client a) expectMetadataChangedError =<< trySubmitMulti [a,b] [] (exerciseCmd cid (Client.ExerciseByKeyMaintainerChanged a b)) @@ -539,8 +970,63 @@ exerciseByKeyMaintainerChangedGlobal = test $ do expectMetadataChangedError =<< trySubmitMulti [a,b] [] (exerciseByKeyExactCmd @V2.ChangedMaintainer (MyKey a b 0) V2.ChangedMaintainerCall) +{- UNCHANGED MAINTAINER -} + +exerciseMaintainerUnchangedLocal : Test +exerciseMaintainerUnchangedLocal = test $ do + a <- allocateParty "alice" + b <- allocateParty "bob" + cid <- a `submit` createExactCmd (Client.Client a) + expectSuccess =<< + trySubmitMulti [a,b] [] (exerciseCmd cid (Client.ExerciseMaintainerUnchanged a b)) + +exerciseByInterfaceMaintainerUnchangedLocal : Test +exerciseByInterfaceMaintainerUnchangedLocal = test $ do + a <- allocateParty "alice" + b <- allocateParty "bob" + cid <- a `submit` createExactCmd (Client.Client a) + expectSuccess =<< + trySubmitMulti [a,b] [] (exerciseCmd cid (Client.ExerciseByInterfaceMaintainerUnchanged a b)) + +exerciseByKeyMaintainerUnchangedLocal : Test +exerciseByKeyMaintainerUnchangedLocal = test $ do + a <- allocateParty "alice" + b <- allocateParty "bob" + cid <- a `submit` createExactCmd (Client.Client a) + expectSuccess =<< + trySubmitMulti [a,b] [] (exerciseCmd cid (Client.ExerciseByKeyMaintainerUnchanged a b)) + +exerciseMaintainerUnchangedGlobal : Test +exerciseMaintainerUnchangedGlobal = test $ do + a <- allocateParty "alice" + b <- allocateParty "bob" + cid <- a `submit` createExactCmd (V1.UnchangedMaintainer a b) + expectSuccess =<< + trySubmitMulti [a,b] [] (exerciseExactCmd @V2.UnchangedMaintainer (coerceContractId cid) V2.UnchangedMaintainerCall) + +exerciseByInterfaceMaintainerUnchangedGlobal : Test +exerciseByInterfaceMaintainerUnchangedGlobal = test $ do + a <- allocateParty "alice" + b <- allocateParty "bob" + cid <- a `submit` createExactCmd (V1.UnchangedMaintainer a b) + expectSuccess =<< + trySubmitMulti [a,b] [] (exerciseExactCmd @I (toInterfaceContractId cid) DynamicCall) + +exerciseByKeyMaintainerUnchangedGlobal : Test +exerciseByKeyMaintainerUnchangedGlobal = test $ do + a <- allocateParty "alice" + b <- allocateParty "bob" + cid <- a `submit` createExactCmd (V1.UnchangedMaintainer a b) + expectSuccess =<< + trySubmitMulti [a,b] [] (exerciseByKeyExactCmd @V2.UnchangedMaintainer (MyKey a b 0) V2.UnchangedMaintainerCall) + ------------------------------------------------------------------------------------------------------------------------ +expectSuccess : Either SubmitError a -> Script () +expectSuccess r = case r of + Right _ -> pure () + Left e -> assertFail $ "Expected success but got " <> show e + expectMetadataChangedError : Either SubmitError a -> Script () expectMetadataChangedError r = case r of Right _ -> assertFail "Expected failure but got success" From 2f0637dd809c7273d5733fc8b6111c60d651607e Mon Sep 17 00:00:00 2001 From: Paul Brauner Date: Thu, 14 Nov 2024 13:56:40 +0100 Subject: [PATCH 07/13] fix exercise by interface --- .../daml/lf/speedy/SBuiltin.scala | 108 +++++++++--------- .../test/daml/upgrades/MetadataChanged.daml | 8 +- 2 files changed, 60 insertions(+), 56 deletions(-) diff --git a/sdk/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala b/sdk/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala index 96ff073f256d..d9ab8be7ebd0 100644 --- a/sdk/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala +++ b/sdk/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala @@ -1197,7 +1197,7 @@ private[lf] object SBuiltin { coid: V.ContractId, ifaceId: TypeConName, )(k: SAny => Control[Question.Update]): Control[Question.Update] = { - fetchAny(machine, None, coid) { (_, srcContract) => + fetchAny(machine, None, coid) { (_, srcContract, _) => val (tplId, arg) = getSAnyContract(ArrayList.single(srcContract), 0) ensureTemplateImplementsInterface(machine, ifaceId, coid, tplId) { viewInterface(machine, ifaceId, tplId, arg) { srcView => @@ -1218,12 +1218,12 @@ private[lf] object SBuiltin { coid: V.ContractId, interfaceId: TypeConName, )(k: SAny => Control[Question.Update]): Control[Question.Update] = - fetchAny(machine, None, coid) { (maybePkgName, srcContract) => + fetchAny(machine, None, coid) { (maybePkgName, srcContractAny, srcContract) => maybePkgName match { case None => crash(s"unexpected contract instance without packageName") case Some(pkgName) => - val (srcTplId, srcArg) = getSAnyContract(ArrayList.single(srcContract), 0) + val (srcTplId, srcArg) = getSAnyContract(ArrayList.single(srcContractAny), 0) ensureTemplateImplementsInterface(machine, interfaceId, coid, srcTplId) { viewInterface(machine, interfaceId, srcTplId, srcArg) { srcView => resolvePackageName(machine, pkgName) { pkgId => @@ -1251,7 +1251,7 @@ private[lf] object SBuiltin { if (dstTplId == srcTplId) k(SAny(Ast.TTyCon(dstTplId), dstArg)) else { - validateContractInfo(machine, coid, dstTplId, contract) { () => + checkContractUpgradable(coid, srcContract, contract) { () => executeExpression(machine, SEPreventCatch(dstView)) { dstViewValue => if (srcViewValue != dstViewValue) { @@ -1306,7 +1306,7 @@ private[lf] object SBuiltin { machine: UpdateMachine, ): Control[Question.Update] = { val coid = getSContractId(args, 0) - fetchAny(machine, optTargetTemplateId, coid) { (_, sv) => + fetchAny(machine, optTargetTemplateId, coid) { (_, sv, _) => Control.Value(sv) } } @@ -2369,7 +2369,7 @@ private[lf] object SBuiltin { optTargetTemplateId: Option[TypeConName], coid: V.ContractId, )(f: SValue => Control[Question.Update]): Control[Question.Update] = { - fetchAny(machine, optTargetTemplateId, coid) { (_, fetched) => + fetchAny(machine, optTargetTemplateId, coid) { (_, fetched, _) => // The SBCastAnyContract check can never fail when the upgrading feature flag is enabled. // This is because the contract got up/down-graded when imported by importValue. @@ -2386,62 +2386,66 @@ private[lf] object SBuiltin { } } + /** Checks that the metadata of [original] and [recomputed] are the same, fails with a [Control.Error] if not. */ + private def checkContractUpgradable( + coid: V.ContractId, + original: ContractInfo, + recomputed: ContractInfo, + )( + k: () => Control[Question.Update] + ): Control[Question.Update] = { + def check[T](recomputed: T, original: T)(desc: String): Either[String, Unit] = + Either.cond(recomputed == original, (), s"$desc mismatch: $original vs $recomputed") + + { + for { + _ <- check(recomputed.signatories, original.signatories)("signatories") + _ <- check(recomputed.observers, original.observers)("observers") + _ <- check(recomputed.keyOpt.map(_.maintainers), original.keyOpt.map(_.maintainers))( + "key maintainers" + ) + _ <- check(recomputed.keyOpt.map(_.globalKey.key), original.keyOpt.map(_.globalKey.key))( + "key value" + ) + } yield () + }.fold( + errorMsg => + Control.Error( + IE.Upgrade( + IE.Upgrade.ValidationFailed( + coid = coid, + srcTemplateId = original.templateId, + dstTemplateId = recomputed.templateId, + signatories = recomputed.signatories, + observers = recomputed.observers, + keyOpt = recomputed.keyOpt.map(_.globalKeyWithMaintainers), + msg = errorMsg, + ) + ) + ), + _ => k(), + ) + } + // This is the core function which fetches a contract given its coid. // Regardless of it being a local, disclosed or global contract private def fetchAny( machine: UpdateMachine, optTargetTemplateId: Option[TypeConName], coid: V.ContractId, - )(f: (Option[Ref.PackageName], SValue) => Control[Question.Update]): Control[Question.Update] = { - - // Checks that the metadata of [original] and [recomputed] are the same, fails with a [Control.Error] if not. - def checkContractUpgradable( - original: ContractInfo, - recomputed: ContractInfo, - )( - k: () => Control[Question.Update] - ): Control[Question.Update] = { - def check[T](recomputed: T, original: T)(desc: String): Either[String, Unit] = - Either.cond(recomputed == original, (), s"$desc mismatch: $original vs $recomputed") - - { - for { - _ <- check(recomputed.signatories, original.signatories)("signatories") - _ <- check(recomputed.observers, original.observers)("observers") - _ <- check(recomputed.keyOpt.map(_.maintainers), original.keyOpt.map(_.maintainers))( - "key maintainers" - ) - _ <- check(recomputed.keyOpt.map(_.globalKey.key), original.keyOpt.map(_.globalKey.key))( - "key value" - ) - } yield () - }.fold( - errorMsg => - Control.Error( - IE.Upgrade( - IE.Upgrade.ValidationFailed( - coid = coid, - srcTemplateId = original.templateId, - dstTemplateId = recomputed.templateId, - signatories = recomputed.signatories, - observers = recomputed.observers, - keyOpt = recomputed.keyOpt.map(_.globalKeyWithMaintainers), - msg = errorMsg, - ) - ) - ), - _ => k(), - ) - } + )( + f: (Option[Ref.PackageName], SValue, ContractInfo) => Control[Question.Update] + ): Control[Question.Update] = { type ContractInfoChecker = ContractInfo => (() => Control[Question.Update]) => Control[Question.Update] def localContractInfoUpgradeChecker( + coid: V.ContractId, originalContractInfo: ContractInfo ): ContractInfoChecker = (upgradedContractInfo: ContractInfo) => - checkContractUpgradable(originalContractInfo, upgradedContractInfo) + checkContractUpgradable(coid, originalContractInfo, upgradedContractInfo) def globalContractInfoUpgradeChecker( coid: V.ContractId, @@ -2495,10 +2499,10 @@ private[lf] object SBuiltin { } if (needValidationCall) { contractInfoUpgradeChecker(upgradedContract) { () => - f(upgradedContract.packageName, upgradedContract.any) + f(upgradedContract.packageName, upgradedContract.any, upgradedContract) } } else { - f(upgradedContract.packageName, upgradedContract.any) + f(upgradedContract.packageName, upgradedContract.any, upgradedContract) } } } @@ -2519,11 +2523,11 @@ private[lf] object SBuiltin { if (optTargetTemplateId.forall(_ == templateId)) { // If the local contract has the same package ID as the target template ID, then we don't need to // import its value and validate its contract info again. - f(contract.packageName, SValue.SAnyContract(templateId, templateArg)) + f(contract.packageName, SValue.SAnyContract(templateId, templateArg), contract) } else { importContract( V.ContractInstance(contract.packageName, templateId, contract.arg), - localContractInfoUpgradeChecker(contract), + localContractInfoUpgradeChecker(coid, contract), ) } } diff --git a/sdk/daml-script/test/daml/upgrades/MetadataChanged.daml b/sdk/daml-script/test/daml/upgrades/MetadataChanged.daml index 2f9f6642dead..e5df29f554e4 100644 --- a/sdk/daml-script/test/daml/upgrades/MetadataChanged.daml +++ b/sdk/daml-script/test/daml/upgrades/MetadataChanged.daml @@ -37,19 +37,19 @@ main = tests , subtree "exercise by interface" [ subtree "signatories" [ ("changed", exerciseByInterfaceSignatoriesChangedLocal) - , broken ("unchanged", exerciseByInterfaceSignatoriesUnchangedLocal) + , ("unchanged", exerciseByInterfaceSignatoriesUnchangedLocal) ] , subtree "observers" [ ("changed", exerciseByInterfaceObserversChangedLocal) - , broken ("unchanged", exerciseByInterfaceObserversUnchangedLocal) + , ("unchanged", exerciseByInterfaceObserversUnchangedLocal) ] , subtree "key" [ ("changed", exerciseByInterfaceKeyChangedLocal) - , broken ("unchanged", exerciseByInterfaceKeyUnchangedLocal) + , ("unchanged", exerciseByInterfaceKeyUnchangedLocal) ] , subtree "maintainer" [ ("changed", exerciseByInterfaceMaintainerChangedLocal) - , broken ("unchanged", exerciseByInterfaceMaintainerUnchangedLocal) + , ("unchanged", exerciseByInterfaceMaintainerUnchangedLocal) ] ] , subtree "exercise by key" From 72a8c536ebd0b5828087ddbbc16aaca00d9e0be2 Mon Sep 17 00:00:00 2001 From: Paul Brauner Date: Thu, 14 Nov 2024 16:49:39 +0100 Subject: [PATCH 08/13] always perform the upgrade validation check in the engine --- .../daml/lf/speedy/SBuiltin.scala | 96 +++------ .../daml/lf/speedy/UpgradeTest.scala | 182 ++++-------------- 2 files changed, 56 insertions(+), 222 deletions(-) diff --git a/sdk/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala b/sdk/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala index d9ab8be7ebd0..464afbe098c6 100644 --- a/sdk/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala +++ b/sdk/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala @@ -2437,28 +2437,12 @@ private[lf] object SBuiltin { f: (Option[Ref.PackageName], SValue, ContractInfo) => Control[Question.Update] ): Control[Question.Update] = { - type ContractInfoChecker = - ContractInfo => (() => Control[Question.Update]) => Control[Question.Update] - - def localContractInfoUpgradeChecker( - coid: V.ContractId, - originalContractInfo: ContractInfo - ): ContractInfoChecker = - (upgradedContractInfo: ContractInfo) => - checkContractUpgradable(coid, originalContractInfo, upgradedContractInfo) - - def globalContractInfoUpgradeChecker( - coid: V.ContractId, - srcTemplateId: Ref.Identifier, - ): ContractInfoChecker = - (upgradedContractInfo: ContractInfo) => - validateContractInfo(machine, coid, srcTemplateId, upgradedContractInfo) - def importContract( - coinst: V.ContractInstance, - contractInfoUpgradeChecker: ContractInfoChecker, + srcContract: ContractInfo ) = { - val V.ContractInstance(_, srcTmplId, coinstArg) = coinst + val srcTmplId = srcContract.templateId + val coinst = + V.ContractInstance(srcContract.packageName, srcContract.templateId, srcContract.arg) val (upgradingIsEnabled, dstTmplId) = optTargetTemplateId match { case Some(tycon) if coinst.upgradable => (true, tycon) @@ -2474,19 +2458,19 @@ private[lf] object SBuiltin { dstTmplId.packageId, language.Reference.Template(dstTmplId), ) { () => - importValue(machine, dstTmplId, coinstArg) { templateArg => + importValue(machine, dstTmplId, srcContract.arg) { templateArg => getContractInfo( machine, coid, dstTmplId, templateArg, allowCatchingContractInfoErrors = false, - ) { upgradedContract => - ensureContractActive(machine, coid, upgradedContract.templateId) { + ) { dstContract => + ensureContractActive(machine, coid, dstContract.templateId) { - machine.checkContractVisibility(coid, upgradedContract) + machine.checkContractVisibility(coid, dstContract) machine.enforceLimitAddInputContract() - machine.enforceLimitSignatoriesAndObservers(coid, upgradedContract) + machine.enforceLimitSignatoriesAndObservers(coid, dstContract) // In Validation mode, we always call validateContractInfo // In Submission mode, we only call validateContractInfo when src != dest @@ -2498,11 +2482,11 @@ private[lf] object SBuiltin { upgradingIsEnabled && (srcTmplId.packageId != dstTmplId.packageId) } if (needValidationCall) { - contractInfoUpgradeChecker(upgradedContract) { () => - f(upgradedContract.packageName, upgradedContract.any, upgradedContract) + checkContractUpgradable(coid, srcContract, dstContract) { () => + f(dstContract.packageName, dstContract.any, dstContract) } } else { - f(upgradedContract.packageName, upgradedContract.any, upgradedContract) + f(dstContract.packageName, dstContract.any, dstContract) } } } @@ -2525,61 +2509,25 @@ private[lf] object SBuiltin { // import its value and validate its contract info again. f(contract.packageName, SValue.SAnyContract(templateId, templateArg), contract) } else { - importContract( - V.ContractInstance(contract.packageName, templateId, contract.arg), - localContractInfoUpgradeChecker(coid, contract), - ) + importContract(contract) } } } case None => machine.lookupGlobalContract(coid)(coinst => - importContract(coinst, globalContractInfoUpgradeChecker(coid, coinst.template)) + importValue(machine, coinst.template, coinst.arg) { templateArg => + getContractInfo( + machine, + coid, + coinst.template, + templateArg, + allowCatchingContractInfoErrors = false, + )(importContract) + } ) } } - private def validateContractInfo( - machine: UpdateMachine, - coid: V.ContractId, - srcTemplateId: Ref.Identifier, - contract: ContractInfo, - )( - continue: () => Control[Question.Update] - ): Control[Question.Update] = { - - val keyOpt: Option[GlobalKeyWithMaintainers] = contract.keyOpt match { - case None => None - case Some(cachedKey) => - Some(cachedKey.globalKeyWithMaintainers) - } - machine.needUpgradeVerification( - location = NameOf.qualifiedNameOfCurrentFunc, - coid = coid, - signatories = contract.signatories, - observers = contract.observers, - keyOpt = keyOpt, - continue = { - case None => - continue() - case Some(msg) => - Control.Error( - IE.Upgrade( - IE.Upgrade.ValidationFailed( - coid = coid, - srcTemplateId = srcTemplateId, - dstTemplateId = contract.templateId, - signatories = contract.signatories, - observers = contract.observers, - keyOpt = keyOpt, - msg = msg, - ) - ) - ) - }, - ) - } - private def importValue[Q](machine: Machine[Q], templateId: TypeConName, coinstArg: V)( f: SValue => Control[Q] ): Control[Q] = { diff --git a/sdk/daml-lf/interpreter/src/test/scala/com/digitalasset/daml/lf/speedy/UpgradeTest.scala b/sdk/daml-lf/interpreter/src/test/scala/com/digitalasset/daml/lf/speedy/UpgradeTest.scala index 590b25733145..56846e8c4ac9 100644 --- a/sdk/daml-lf/interpreter/src/test/scala/com/digitalasset/daml/lf/speedy/UpgradeTest.scala +++ b/sdk/daml-lf/interpreter/src/test/scala/com/digitalasset/daml/lf/speedy/UpgradeTest.scala @@ -165,6 +165,9 @@ class UpgradeTest(majorLanguageVersion: LanguageMajorVersion) val do_fetch: ContractId M:T -> Update M:T = \(cId: ContractId M:T) -> fetch_template @M:T cId; + + val do_exercise: ContractId M:T -> Update Unit = + \(cId: ContractId M:T) -> exercise @M:T NoOp cId (); } """ } @@ -193,6 +196,8 @@ class UpgradeTest(majorLanguageVersion: LanguageMajorVersion) \(cId: ContractId M:T) -> fetch_template @M:T cId; + val do_exercise: ContractId M:T -> Update Unit = + \(cId: ContractId M:T) -> exercise @M:T NoOp cId (); } """ } @@ -315,7 +320,11 @@ class UpgradeTest(majorLanguageVersion: LanguageMajorVersion) } // The given contractSValue is wrapped as a disclosedContract - def goDisclosed(e: Expr, contractSValue: SValue): Either[SError, Success] = { + def goDisclosed( + e: Expr, + templateId: Ref.TypeConName, + contractSValue: SValue, + ): Either[SError, Success] = { val se: SExpr = pkgs.compiler.unsafeCompile(e) val args = Array[SValue](SContractId(theCid)) @@ -330,7 +339,7 @@ class UpgradeTest(majorLanguageVersion: LanguageMajorVersion) Speedy.ContractInfo( version = V17, packageName = pkgName, - templateId = i"'-unknown-':M:T", + templateId = templateId, value = contractSValue, agreementText = "meh", signatories = Set.empty, @@ -425,43 +434,6 @@ class UpgradeTest(majorLanguageVersion: LanguageMajorVersion) v shouldBe v_missingField } } - - "missing non-optional field -- should be rejected" in { - // should be caught by package upgradability check - val v_missingField = makeRecord(ValueParty(alice)) - - inside( - go(e"'-pkg1-':M:do_fetch", ContractInstance(pkgName, i"'-unknow-':M:T", v_missingField)) - ) { case Left(SError.SErrorCrash(_, reason)) => - reason should include( - "Unexpected non-optional extra template field type encountered during upgrading" - ) - } - } - - "mismatching qualified name -- should be rejected" in { - val v = - makeRecord( - ValueParty(alice), - ValueParty(bob), - ValueInt64(100), - ValueOptional(None), - ) - - val expectedTyCon = i"'-pkg3-':M:T" - val negativeTestCase = i"'-pkg2-':M:T" - val positiveTestCases = Table("tyCon", i"'-pkg2-':M1:T", i"'-pkg2-':M2:T") - go(e"'-pkg3-':M:do_fetch", ContractInstance(pkgName, negativeTestCase, v)) shouldBe a[ - Right[_, _] - ] - - forEvery(positiveTestCases) { tyCon => - inside(go(e"'-pkg3-':M:do_fetch", ContractInstance(pkgName, tyCon, v))) { - case Left(SError.SErrorDamlException(e)) => - e shouldBe IE.WronglyTypedContract(theCid, expectedTyCon, tyCon) - } - } - } } "downgrade attempted" - { @@ -475,28 +447,6 @@ class UpgradeTest(majorLanguageVersion: LanguageMajorVersion) } } - "extra field (text) - something is very wrong" in { - // should be caught by package upgradability check - - val v1_extraText = - makeRecord( - ValueParty(alice), - ValueParty(bob), - ValueInt64(100), - ValueText("extra"), - ) - - val res = - go(e"'-pkg1-':M:do_fetch", ContractInstance(pkgName, i"'-unknown-':M:T", v1_extraText)) - - inside(res) { case Left(SError.SErrorCrash(_, reason)) => - reason should include( - "Unexpected non-optional extra contract field encountered during downgrading" - ) - } - - } - "extra field (Some) - cannot be dropped" in { val v1_extraSome = @@ -525,7 +475,7 @@ class UpgradeTest(majorLanguageVersion: LanguageMajorVersion) ) val res = - go(e"'-pkg2-':M:do_fetch", ContractInstance(pkgName, i"'-unknow-':M:T", v1_extraNone)) + go(e"'-pkg1-':M:do_fetch", ContractInstance(pkgName, i"'-pkg3-':M:T", v1_extraNone)) inside(res) { case Right((_, v, _)) => v shouldBe v1_base @@ -560,8 +510,8 @@ class UpgradeTest(majorLanguageVersion: LanguageMajorVersion) in upure @(ContractId '-pkg1-':M:T) cid """ ) - inside(res) { case Right((_, ValueContractId(_), verificationRequests)) => - verificationRequests shouldBe empty + inside(res) { case Right((_, v, List())) => + v shouldBe a[ValueContractId] } } @@ -574,8 +524,8 @@ class UpgradeTest(majorLanguageVersion: LanguageMajorVersion) in upure @(ContractId '-pkg1-':M:T) cid """ ) - inside(res) { case Right((_, ValueContractId(_), verificationRequests)) => - verificationRequests shouldBe empty + inside(res) { case Right((_, v, List())) => + v shouldBe a[ValueContractId] } } @@ -587,8 +537,8 @@ class UpgradeTest(majorLanguageVersion: LanguageMajorVersion) in upure @(ContractId '-pkg1-':M:T) cid """ ) - inside(res) { case Right((_, ValueContractId(_), verificationRequests)) => - verificationRequests shouldBe empty + inside(res) { case Right((_, v, List())) => + v shouldBe a[ValueContractId] } } @@ -601,8 +551,8 @@ class UpgradeTest(majorLanguageVersion: LanguageMajorVersion) in upure @(ContractId '-pkg1-':M:T) cid """ ) - inside(res) { case Right((_, ValueContractId(_), verificationRequests)) => - verificationRequests shouldBe empty + inside(res) { case Right((_, v, List())) => + v shouldBe a[ValueContractId] } } @@ -619,8 +569,8 @@ class UpgradeTest(majorLanguageVersion: LanguageMajorVersion) """, packageResolution = Map(Ref.PackageName.assertFromString("-upgrade-test-") -> pkgId2), ) - inside(res) { case Right((_, ValueContractId(_), verificationRequests)) => - verificationRequests shouldBe empty + inside(res) { case Right((_, v, List())) => + v shouldBe a[ValueContractId] } } @@ -630,79 +580,13 @@ class UpgradeTest(majorLanguageVersion: LanguageMajorVersion) e"""\(cid: ContractId '-pkg1-':M:T) -> ubind x1: Unit <- '-pkg2-':M:do_fetch cid; - x2: Unit <- '-pkg4-':M:do_fetch cid + x2: Unit <- '-pkg3-':M:do_fetch cid in upure @Unit () """, ContractInstance(pkgName, i"'-pkg1-':M:T", v1_base), ) - inside(res) { case Right((_, _, verificationRequests)) => - val v4_key = GlobalKeyWithMaintainers.assertBuild( - i"'-pkg1-':M:T", - ValueParty(bob), - Set(bob), - crypto.Hash.KeyPackageName.assertBuild(pkgName, V17), - ) - verificationRequests shouldBe List( - UpgradeVerificationRequest(theCid, Set(alice), Set(bob), Some(v1_key)), - UpgradeVerificationRequest(theCid, Set(bob), Set(alice), Some(v4_key)), - ) - } - } - } - - "Correct calls to ResultNeedUpgradeVerification" in { - - implicit val pkgId: Ref.PackageId = Ref.PackageId.assertFromString("-no-pkg-") - - val v_alice_none = - makeRecord( - ValueParty(alice), - ValueParty(bob), - ValueInt64(100), - ValueOptional(None), - ) - - val v_alice_no_none = - makeRecord( - ValueParty(alice), - ValueParty(bob), - ValueInt64(100), - ) - - val v_alice_some = - makeRecord( - ValueParty(alice), - ValueParty(bob), - ValueInt64(100), - ValueOptional(Some(ValueParty(bob))), - ) - - inside( - go( - e"'-pkg3-':M:do_fetch", - ContractInstance(pkgName, i"'-misspelled-pkg3-':M:T", v_alice_none), - ) - ) { case Right((_, v, List(uv))) => - v shouldBe v_alice_no_none - uv.coid shouldBe theCid - uv.signatories.toList shouldBe List(alice) - uv.observers.toList shouldBe List(bob) - uv.keyOpt shouldBe Some(v1_key) - } - - inside( - go( - e"'-pkg3-':M:do_fetch", - ContractInstance(pkgName, i"'-misspelled-pkg3-':M:T", v_alice_some), - ) - ) { case Right((_, v, List(uv))) => - v shouldBe v_alice_some - uv.coid shouldBe theCid - uv.signatories.toList shouldBe List(alice, bob) - uv.observers.toList shouldBe List(bob) - uv.keyOpt shouldBe Some(v1_key) + res shouldBe a[Right[_, _]] } - } "Disclosed contracts" - { @@ -727,8 +611,9 @@ class UpgradeTest(majorLanguageVersion: LanguageMajorVersion) SValue.SRecord(i"'-pkg1-':M:T", fields, values) } - inside(goDisclosed(e"'-pkg1-':M:do_fetch", sv1_base)) { case Right((_, v, _)) => - v shouldBe v1_base + inside(goDisclosed(e"'-pkg1-':M:do_fetch", i"'-pkg1-':M:T", sv1_base)) { + case Right((_, v, _)) => + v shouldBe v1_base } } @@ -751,8 +636,9 @@ class UpgradeTest(majorLanguageVersion: LanguageMajorVersion) SValue.SRecord(i"'-unknown-':M:T", fields, values) } - inside(goDisclosed(e"'-pkg1-':M:do_fetch", sv1_base)) { case Right((_, v, _)) => - v shouldBe v1_base + inside(goDisclosed(e"'-pkg1-':M:do_fetch", i"'-pkg1-':M:T", sv1_base)) { + case Right((_, v, _)) => + v shouldBe v1_base } } } @@ -768,7 +654,7 @@ class UpgradeTest(majorLanguageVersion: LanguageMajorVersion) ValueOptional(None), ) - val se: SExpr = pkgs.compiler.unsafeCompile(e"'-pkg4-':M:do_exercise") + val se: SExpr = pkgs.compiler.unsafeCompile(e"'-pkg3-':M:do_exercise") val args: Array[SValue] = Array(SContractId(theCid)) val sexprToEval: SExpr = SEApp(se, args) @@ -781,13 +667,13 @@ class UpgradeTest(majorLanguageVersion: LanguageMajorVersion) SpeedyTestLib.buildTransactionCollectRequests( machine, getContract = - Map(theCid -> Versioned(V17, ContractInstance(pkgName, i"'-pkg3-':M:T", v_alice_none))), + Map(theCid -> Versioned(V17, ContractInstance(pkgName, i"'-pkg2-':M:T", v_alice_none))), ) inside(res.map(_._1.nodes.values.toList)) { case Right(List(exe: Node.Exercise)) => exe.packageName shouldBe Some("-upgrade-test-") - exe.creationPackageId shouldBe Some("-pkg3-") - exe.templateId.packageId shouldBe "-pkg4-" + exe.creationPackageId shouldBe Some("-pkg2-") + exe.templateId.packageId shouldBe "-pkg3-" } } } From 62dafb0b48e851012525caeb4cd49818fc6f80fe Mon Sep 17 00:00:00 2001 From: Paul Brauner Date: Thu, 14 Nov 2024 16:55:29 +0100 Subject: [PATCH 09/13] simplify error reporting --- .../daml/lf/speedy/SBuiltin.scala | 42 ++++++++++--------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/sdk/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala b/sdk/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala index 464afbe098c6..eec405feb7f6 100644 --- a/sdk/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala +++ b/sdk/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala @@ -2394,22 +2394,25 @@ private[lf] object SBuiltin { )( k: () => Control[Question.Update] ): Control[Question.Update] = { - def check[T](recomputed: T, original: T)(desc: String): Either[String, Unit] = - Either.cond(recomputed == original, (), s"$desc mismatch: $original vs $recomputed") - - { - for { - _ <- check(recomputed.signatories, original.signatories)("signatories") - _ <- check(recomputed.observers, original.observers)("observers") - _ <- check(recomputed.keyOpt.map(_.maintainers), original.keyOpt.map(_.maintainers))( - "key maintainers" - ) - _ <- check(recomputed.keyOpt.map(_.globalKey.key), original.keyOpt.map(_.globalKey.key))( - "key value" - ) - } yield () - }.fold( - errorMsg => + def check[T](recomputed: T, original: T, desc: String): Option[String] = + Option.when(recomputed != original)(s"$desc mismatch: $original vs $recomputed") + + List( + check(recomputed.signatories, original.signatories, "signatories"), + check(recomputed.observers, original.observers, "observers"), + check( + recomputed.keyOpt.map(_.maintainers), + original.keyOpt.map(_.maintainers), + "key maintainers", + ), + check( + recomputed.keyOpt.map(_.globalKey.key), + original.keyOpt.map(_.globalKey.key), + "key value", + ), + ).flatten match { + case Nil => k() + case errors => Control.Error( IE.Upgrade( IE.Upgrade.ValidationFailed( @@ -2419,12 +2422,11 @@ private[lf] object SBuiltin { signatories = recomputed.signatories, observers = recomputed.observers, keyOpt = recomputed.keyOpt.map(_.globalKeyWithMaintainers), - msg = errorMsg, + msg = errors.mkString("['", "', '", "']"), ) ) - ), - _ => k(), - ) + ) + } } // This is the core function which fetches a contract given its coid. From b4549627de8855ba401037e792db4fce550eafa9 Mon Sep 17 00:00:00 2001 From: Paul Brauner Date: Thu, 14 Nov 2024 17:02:31 +0100 Subject: [PATCH 10/13] ensure package is loaded before importing global contract --- .../daml/lf/speedy/SBuiltin.scala | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/sdk/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala b/sdk/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala index eec405feb7f6..65b617c05b85 100644 --- a/sdk/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala +++ b/sdk/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala @@ -2517,14 +2517,19 @@ private[lf] object SBuiltin { } case None => machine.lookupGlobalContract(coid)(coinst => - importValue(machine, coinst.template, coinst.arg) { templateArg => - getContractInfo( - machine, - coid, - coinst.template, - templateArg, - allowCatchingContractInfoErrors = false, - )(importContract) + machine.ensurePackageIsLoaded( + coinst.template.packageId, + language.Reference.Template(coinst.template), + ) { () => + importValue(machine, coinst.template, coinst.arg) { templateArg => + getContractInfo( + machine, + coid, + coinst.template, + templateArg, + allowCatchingContractInfoErrors = false, + )(importContract) + } } ) } From 25c6530fc6a08b7c82a717631febf06154121d94 Mon Sep 17 00:00:00 2001 From: Paul Brauner Date: Thu, 14 Nov 2024 17:06:40 +0100 Subject: [PATCH 11/13] simplify check code even more --- .../daml/lf/speedy/SBuiltin.scala | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/sdk/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala b/sdk/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala index 65b617c05b85..3782ef76d056 100644 --- a/sdk/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala +++ b/sdk/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala @@ -2394,22 +2394,17 @@ private[lf] object SBuiltin { )( k: () => Control[Question.Update] ): Control[Question.Update] = { - def check[T](recomputed: T, original: T, desc: String): Option[String] = - Option.when(recomputed != original)(s"$desc mismatch: $original vs $recomputed") + + def check[T](getter: ContractInfo => T, desc: String): Option[String] = + Option.when(getter(recomputed) != getter(original))( + s"$desc mismatch: $original vs $recomputed" + ) List( - check(recomputed.signatories, original.signatories, "signatories"), - check(recomputed.observers, original.observers, "observers"), - check( - recomputed.keyOpt.map(_.maintainers), - original.keyOpt.map(_.maintainers), - "key maintainers", - ), - check( - recomputed.keyOpt.map(_.globalKey.key), - original.keyOpt.map(_.globalKey.key), - "key value", - ), + check(_.signatories, "signatories"), + check(_.observers, "observers"), + check(_.keyOpt.map(_.maintainers), "key maintainers"), + check(_.keyOpt.map(_.globalKey.key), "key value"), ).flatten match { case Nil => k() case errors => From e41c1e27f3f43c4d227cc323bbc30d14aacab408 Mon Sep 17 00:00:00 2001 From: Paul Brauner Date: Thu, 14 Nov 2024 18:27:55 +0100 Subject: [PATCH 12/13] allow losing observers that are also signatories --- .../main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sdk/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala b/sdk/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala index 3782ef76d056..618f8b11eb80 100644 --- a/sdk/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala +++ b/sdk/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala @@ -2402,7 +2402,8 @@ private[lf] object SBuiltin { List( check(_.signatories, "signatories"), - check(_.observers, "observers"), + // This definition of observers allows observers to lose parties that are signatories + check(c => c.stakeholders -- c.signatories, "observers"), check(_.keyOpt.map(_.maintainers), "key maintainers"), check(_.keyOpt.map(_.globalKey.key), "key value"), ).flatten match { From 67f14f32f3a2e7ebc2c051305aafc259b1143437 Mon Sep 17 00:00:00 2001 From: Paul Brauner <141240651+paulbrauner-da@users.noreply.github.com> Date: Fri, 15 Nov 2024 10:00:20 +0100 Subject: [PATCH 13/13] Apply suggestion Co-authored-by: Remy --- .../main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala | 3 ++- .../main/scala/com/digitalasset/daml/lf/speedy/Speedy.scala | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/sdk/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala b/sdk/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala index 618f8b11eb80..f768f3569a57 100644 --- a/sdk/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala +++ b/sdk/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala @@ -2403,7 +2403,7 @@ private[lf] object SBuiltin { List( check(_.signatories, "signatories"), // This definition of observers allows observers to lose parties that are signatories - check(c => c.stakeholders -- c.signatories, "observers"), + check(c => c.nonSignatoryStakeholders, "non signatory stakeholders"), check(_.keyOpt.map(_.maintainers), "key maintainers"), check(_.keyOpt.map(_.globalKey.key), "key value"), ).flatten match { @@ -2411,6 +2411,7 @@ private[lf] object SBuiltin { case errors => Control.Error( IE.Upgrade( + // TODO(https://github.com/digital-asset/daml/issues/20305): also include the original metadata IE.Upgrade.ValidationFailed( coid = coid, srcTemplateId = original.templateId, diff --git a/sdk/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/Speedy.scala b/sdk/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/Speedy.scala index 142639b6afbf..d3093d98e455 100644 --- a/sdk/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/Speedy.scala +++ b/sdk/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/Speedy.scala @@ -146,6 +146,7 @@ private[lf] object Speedy { keyOpt: Option[CachedKey], ) { val stakeholders: Set[Party] = signatories union observers + lazy val nonSignatoryStakeholders: Set[Party] = stakeholders -- signatories private[speedy] val any = SValue.SAnyContract(templateId, value) private[speedy] def arg = value.toNormalizedValue(version)