From 6c393cb8930e954c4d7a321835959fdc22d5ace1 Mon Sep 17 00:00:00 2001 From: Paul Brauner <141240651+paulbrauner-da@users.noreply.github.com> Date: Fri, 18 Oct 2024 19:09:17 +0200 Subject: [PATCH] [fwd port] Check that controller/observer exceptions are not catchable in dynamic choice exercises (#20124) * Check that controller/observer exceptions are not catchable in dynamic choice exercises (#19999) * also test choice controllers and observers * Check that controller/observer exceptions are not catchable in dynamic choice exercises * fix compilation --- .../daml/lf/speedy/ExceptionTest.scala | 94 ++++++++++++++++++- 1 file changed, 92 insertions(+), 2 deletions(-) diff --git a/sdk/daml-lf/interpreter/src/test/scala/com/digitalasset/daml/lf/speedy/ExceptionTest.scala b/sdk/daml-lf/interpreter/src/test/scala/com/digitalasset/daml/lf/speedy/ExceptionTest.scala index 9abecaf36577..014e6079a1a6 100644 --- a/sdk/daml-lf/interpreter/src/test/scala/com/digitalasset/daml/lf/speedy/ExceptionTest.scala +++ b/sdk/daml-lf/interpreter/src/test/scala/com/digitalasset/daml/lf/speedy/ExceptionTest.scala @@ -795,7 +795,17 @@ class ExceptionTest(majorLanguageVersion: LanguageMajorVersion) p"""metadata ( '-common-defs-' : '1.0.0' ) module Mod { record @serializable MyUnit = {}; - interface (this : Iface) = { viewtype Mod:MyUnit; }; + interface (this : Iface) = { + viewtype Mod:MyUnit; + + method myChoiceControllers : List Party; + method myChoiceObservers : List Party; + + choice @nonConsuming MyChoice (self) (u: Unit): Text + , controllers (call_method @Mod:Iface myChoiceControllers this) + , observers (call_method @Mod:Iface myChoiceObservers this) + to upure @Text "MyChoice was called"; + }; record @serializable Key = { label: Text, maintainers: List Party }; @@ -827,6 +837,8 @@ class ExceptionTest(majorLanguageVersion: LanguageMajorVersion) | label = "test-key", | maintainers = (Cons @Party [Mod:${templateName} {p} this] (Nil @Party)) | }""".stripMargin + def choiceControllers = s"""Cons @Party [Mod:${templateName} {p} this] (Nil @Party)""" + def choiceObservers = """Nil @Party""" def maintainers = s"""\\(key: '$commonDefsPkgId':Mod:Key) -> ('$commonDefsPkgId':Mod:Key {maintainers} key)""" @@ -844,7 +856,11 @@ class ExceptionTest(majorLanguageVersion: LanguageMajorVersion) | , observers (Nil @Party) | to upure @Text "SomeChoice was called"; | - | implements '$commonDefsPkgId':Mod:Iface { view = '$commonDefsPkgId':Mod:MyUnit {}; }; + | implements '$commonDefsPkgId':Mod:Iface { + | view = '$commonDefsPkgId':Mod:MyUnit {}; + | method myChoiceControllers = $choiceControllers; + | method myChoiceObservers = $choiceObservers; + | }; | | key @'$commonDefsPkgId':Mod:Key ($key) ($maintainers); | };""".stripMargin @@ -873,6 +889,14 @@ class ExceptionTest(majorLanguageVersion: LanguageMajorVersion) override def maintainers = s"""throw @('$commonDefsPkgId':Mod:Key -> List Party) @'$commonDefsPkgId':Mod:Ex ('$commonDefsPkgId':Mod:Ex {message = "Maintainers"})""" } + case object FailingChoiceControllers extends TemplateGenerator("ChoiceControllers") { + override def choiceControllers = + s"""throw @(List Party) @'$commonDefsPkgId':Mod:Ex ('$commonDefsPkgId':Mod:Ex {message = "ChoiceControllers"})""" + } + case object FailingChoiceObservers extends TemplateGenerator("ChoiceObservers") { + override def choiceObservers = + s"""throw @(List Party) @'$commonDefsPkgId':Mod:Ex ('$commonDefsPkgId':Mod:Ex {message = "ChoiceObservers"})""" + } val templateDefsPkgName = Ref.PackageName.assertFromString("-template-defs-") @@ -889,6 +913,8 @@ class ExceptionTest(majorLanguageVersion: LanguageMajorVersion) ${ValidMetadata("Observers").templateDefinition} ${ValidMetadata("Key").templateDefinition} ${ValidMetadata("Maintainers").templateDefinition} + ${ValidMetadata("ChoiceControllers").templateDefinition} + ${ValidMetadata("ChoiceObservers").templateDefinition} } """ (templateDefsV1ParserParams) @@ -907,6 +933,8 @@ class ExceptionTest(majorLanguageVersion: LanguageMajorVersion) ${FailingObservers.templateDefinition} ${FailingKey.templateDefinition} ${FailingMaintainers.templateDefinition} + ${FailingChoiceControllers.templateDefinition} + ${FailingChoiceObservers.templateDefinition} } """ (templateDefsV2ParserParams) @@ -982,6 +1010,19 @@ class ExceptionTest(majorLanguageVersion: LanguageMajorVersion) |""".stripMargin } + def dynamicChoiceTests(templateName: String): String = { + s""" + | // Tries to catch the error thrown by the dynamic exercise of a $templateName choice when fetching it + | // by interface, should fail to do so. + | val exercise${templateName}ByInterfaceAndCatchError: (ContractId '$commonDefsPkgId':Mod:Iface) -> Update Text = + | \\(cid: ContractId '$commonDefsPkgId':Mod:Iface) -> + | try @Text + | exercise_interface @'$commonDefsPkgId':Mod:Iface MyChoice cid () + | catch + | e -> Some @(Update Text) (upure @Text "unexpected: some exception was caught"); + """.stripMargin + } + val metadataTestsPkgId = Ref.PackageId.assertFromString("-metadata-tests-id-") val metadataTestsParserParams = parserParameters.copy(defaultPackageId = metadataTestsPkgId) val metadataTestsPkg = @@ -992,6 +1033,8 @@ class ExceptionTest(majorLanguageVersion: LanguageMajorVersion) ${tests(templateDefsV2PkgId, "Observers")} ${tests(templateDefsV2PkgId, "Key")} ${tests(templateDefsV2PkgId, "Maintainers")} + ${dynamicChoiceTests("ChoiceControllers")} + ${dynamicChoiceTests("ChoiceObservers")} } """ (metadataTestsParserParams) @@ -1113,5 +1156,52 @@ class ExceptionTest(majorLanguageVersion: LanguageMajorVersion) } } } + + for { + test <- List( + FailingChoiceControllers, + FailingChoiceObservers, + ) + } { + + s"exceptions thrown by ${test.templateName} cannot be caught when exercising a choice by interface" in { + val alice = Ref.Party.assertFromString("Alice") + val cid = Value.ContractId.V1(crypto.Hash.hashPrivateKey("abc")) + + inside { + runUpdateApp( + compiledPackages, + packageResolution = Map( + templateDefsPkgName -> templateDefsV2PkgId + ), + e"Mod:exercise${test.templateName}ByInterfaceAndCatchError" (metadataTestsParserParams), + Array(SContractId(cid)), + getContract = Map( + cid -> Versioned( + version = TransactionVersion.StableVersions.max, + Value.ContractInstance( + packageName = metadataTestsPkg.pkgName, + template = t"Mod:${test.templateName}" (templateDefsV1ParserParams) + .asInstanceOf[Ast.TTyCon] + .tycon, + arg = Value.ValueRecord(None, ImmArray(None -> Value.ValueParty(alice))), + ), + ) + ), + getKey = PartialFunction.empty, + ) + } { + case Left( + SError.SErrorDamlException( + IE.UnhandledException( + _, + Value.ValueRecord(_, ImmArray((_, Value.ValueText(msg)))), + ) + ) + ) => + msg shouldBe test.templateName + } + } + } } }