Skip to content

Commit 408deed

Browse files
[Backport #19822] Pull interface package when replaying Exercice by interface (#19833)
* [Backport #19822] Pull interface package when replaying Exercice by interface (#19822) fix DACH-NY/canton#20645 * fmt * disable ReinterpretTestV2
1 parent 691e4cf commit 408deed

File tree

9 files changed

+196
-51
lines changed

9 files changed

+196
-51
lines changed

sdk/daml-lf/engine/src/main/scala/com/digitalasset/daml/lf/engine/preprocessing/Preprocessor.scala

+14-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package com.daml.lf
55
package engine
66
package preprocessing
77

8+
import com.daml.lf.command.ReplayCommand
89
import com.daml.lf.data.{ImmArray, Ref}
910
import com.daml.lf.language.{Ast, LookupError}
1011
import com.daml.lf.speedy.SValue
@@ -163,10 +164,21 @@ private[engine] final class Preprocessor(
163164

164165
private[engine] def preprocessReplayCommand(
165166
cmd: command.ReplayCommand
166-
): Result[speedy.Command] =
167-
safelyRun(pullTemplatePackage(List(cmd.templateId))) {
167+
): Result[speedy.Command] = {
168+
def templateAndInterfaceIds =
169+
cmd match {
170+
case ReplayCommand.Create(templateId, _) => List(templateId)
171+
case ReplayCommand.Exercise(templateId, interfaceId, _, _, _) =>
172+
templateId :: interfaceId.toList
173+
case ReplayCommand.ExerciseByKey(templateId, _, _, _) => List(templateId)
174+
case ReplayCommand.Fetch(templateId, _) => List(templateId)
175+
case ReplayCommand.FetchByKey(templateId, _) => List(templateId)
176+
case ReplayCommand.LookupByKey(templateId, _) => List(templateId)
177+
}
178+
safelyRun(pullTemplatePackage(templateAndInterfaceIds)) {
168179
commandPreprocessor.unsafePreprocessReplayCommand(cmd)
169180
}
181+
}
170182

171183
/** Translates a complete transaction. Assumes no contract ID suffixes are used */
172184
def translateTransactionRoots(

sdk/daml-lf/engine/src/test/scala/com/digitalasset/daml/lf/engine/ReinterpretTest.scala

+52-2
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import org.scalatest.matchers.should.Matchers
2424
import scala.language.implicitConversions
2525

2626
class ReinterpretTestV1 extends ReinterpretTest(LanguageMajorVersion.V1)
27-
class ReinterpretTestV2 extends ReinterpretTest(LanguageMajorVersion.V2)
27+
//class ReinterpretTestV2 extends ReinterpretTest(LanguageMajorVersion.V2)
2828

2929
class ReinterpretTest(majorLanguageVersion: LanguageMajorVersion)
3030
extends AnyWordSpec
@@ -62,13 +62,15 @@ class ReinterpretTest(majorLanguageVersion: LanguageMajorVersion)
6262
)
6363
)
6464

65-
private val engine = new Engine(
65+
private def freshEngine = new Engine(
6666
EngineConfig(
6767
allowedLanguageVersions = language.LanguageVersion.AllVersions(majorLanguageVersion),
6868
requireSuffixedGlobalContractId = true,
6969
)
7070
)
7171

72+
private val engine = freshEngine
73+
7274
def Top(xs: Shape*) = Shape.Top(xs.toList)
7375
def Exercise(xs: Shape*) = Shape.Exercise(xs.toList)
7476
def Rollback(xs: Shape*) = Shape.Rollback(xs.toList)
@@ -171,6 +173,54 @@ class ReinterpretTest(majorLanguageVersion: LanguageMajorVersion)
171173
Shape.ofTransaction(tx.transaction) shouldBe Top(Rollback(Exercise(Create())))
172174
}
173175
}
176+
177+
"exercise by interface pull interfaceId package " in {
178+
val templatePkgId = Ref.PackageId.assertFromString("-template-package-")
179+
val templateId = Ref.Identifier(templatePkgId, "A:T")
180+
val interfacePkgId = Ref.PackageId.assertFromString("-interface-package-")
181+
val interfaceId = Ref.Identifier(interfacePkgId, "B:I")
182+
183+
val testCases = Table(
184+
"cmd" -> "packgeIds",
185+
ReplayCommand.Create(templateId, ValueUnit) -> List(templatePkgId),
186+
ReplayCommand.Exercise(templateId, None, toContractId("cid"), "myChoice", ValueUnit) -> List(
187+
templatePkgId
188+
),
189+
ReplayCommand.Exercise(
190+
templateId,
191+
Some(interfaceId),
192+
toContractId("cid"),
193+
"myChoice",
194+
ValueUnit,
195+
) -> List(templatePkgId, interfacePkgId),
196+
ReplayCommand.ExerciseByKey(templateId, ValueUnit, "MyChoide", ValueUnit) -> List(
197+
templatePkgId
198+
),
199+
ReplayCommand.Fetch(templateId, toContractId("cid")) -> List(templatePkgId),
200+
ReplayCommand.FetchByKey(templateId, ValueUnit) -> List(templatePkgId),
201+
ReplayCommand.LookupByKey(templateId, ValueUnit) -> List(templatePkgId),
202+
)
203+
204+
forEvery(testCases) { (cmd, pkgIds) =>
205+
val emptyPackage =
206+
Package(Map.empty, Set.empty, LanguageMajorVersion.V1.maxStableVersion, None)
207+
var queriedPackageIds = Set.empty[Ref.PackageId]
208+
val trackPackageQueries: PartialFunction[Ref.PackageId, Package] = { pkgId =>
209+
queriedPackageIds = queriedPackageIds + pkgId
210+
emptyPackage
211+
}
212+
freshEngine
213+
.reinterpret(
214+
submitters,
215+
cmd,
216+
Some(seed),
217+
time,
218+
time,
219+
)
220+
.consume(pkgs = trackPackageQueries)
221+
pkgIds.toSet shouldBe queriedPackageIds
222+
}
223+
}
174224
}
175225

176226
object ReinterpretTest {

sdk/daml-script/test/BUILD.bazel

+26-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ load("//bazel_tools:haskell.bzl", "da_haskell_test")
1212
load("@build_environment//:configuration.bzl", "sdk_version")
1313
load("@os_info//:os_info.bzl", "is_windows")
1414
load("//rules_daml:daml.bzl", "daml_compile")
15-
load("//daml-lf/language:daml-lf.bzl", "LF_DEV_VERSIONS", "LF_MAJOR_VERSIONS", "lf_version_latest", "mangle_for_damlc")
15+
load("//daml-lf/language:daml-lf.bzl", "LF_DEV_VERSIONS", "lf_version_latest", "mangle_for_damlc")
16+
17+
# Test Only LF 1.x
18+
LF_MAJOR_VERSIONS = ["1"]
1619

1720
[
1821
genrule(
@@ -21,6 +24,8 @@ load("//daml-lf/language:daml-lf.bzl", "LF_DEV_VERSIONS", "LF_MAJOR_VERSIONS", "
2124
glob(["**/*.daml"]) + [
2225
"//daml-script/daml:daml-script-{}.dar".format(target),
2326
"//docs:source/daml-script/template-root/src/ScriptExample.daml",
27+
"//daml-script/test:template.dar",
28+
"//daml-script/test:retrointerface.dar",
2429
],
2530
outs = ["script-test-v{}.dar".format(major)],
2631
cmd = """
@@ -33,6 +38,8 @@ load("//daml-lf/language:daml-lf.bzl", "LF_DEV_VERSIONS", "LF_MAJOR_VERSIONS", "
3338
cp -L $(location :daml/TestExceptions.daml) $$TMP_DIR/daml
3439
cp -L $(location :daml/TestInterfaces.daml) $$TMP_DIR/daml
3540
cp -L $(location //docs:source/daml-script/template-root/src/ScriptExample.daml) $$TMP_DIR/daml
41+
cp -L $(location //daml-script/test:template.dar) $$TMP_DIR/
42+
cp -L $(location //daml-script/test:retrointerface.dar) $$TMP_DIR/
3643
cat << EOF >> $$TMP_DIR/daml/ScriptExample.daml
3744
initializeFixed : Script ()
3845
initializeFixed = do
@@ -52,6 +59,9 @@ dependencies:
5259
- daml-stdlib
5360
- daml-prim
5461
- daml-script-{target}.dar
62+
data-dependencies:
63+
- template.dar
64+
- retrointerface.dar
5565
build-options:
5666
- --target={target}
5767
EOF
@@ -330,6 +340,19 @@ daml_compile(
330340
version = "2.0.0",
331341
)
332342

343+
daml_compile(
344+
name = "template",
345+
srcs = [":daml/Template.daml"],
346+
target = "1.15",
347+
)
348+
349+
daml_compile(
350+
name = "retrointerface",
351+
srcs = [":daml/RetroInterface.daml"],
352+
data_dependencies = ["//daml-script/test:template.dar"],
353+
target = "1.15",
354+
)
355+
333356
da_scala_test_suite(
334357
name = "test",
335358
size = "large",
@@ -340,6 +363,8 @@ da_scala_test_suite(
340363
data = [
341364
":script-test-no-ledger.dar",
342365
"//daml-script/runner:daml-script-binary",
366+
"//daml-script/test:retrointerface.dar",
367+
"//daml-script/test:template.dar",
343368
] + [
344369
":script-test-v{}.dar".format(major)
345370
for major in LF_MAJOR_VERSIONS

sdk/daml-script/test/daml/MultiTest.daml

+13-43
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,8 @@ import DA.List
99
import DA.Optional(fromSome)
1010
import DA.Time
1111
import Daml.Script
12-
13-
template T
14-
with
15-
p1 : Party
16-
p2 : Party
17-
where
18-
signatory p1, p2
19-
20-
template TProposal
21-
with
22-
p1 : Party
23-
p2 : Party
24-
where
25-
signatory p1
26-
observer p2
27-
choice Accept : (ContractId T, Int)
28-
controller p2
29-
do cid <- create T { p1, p2 }
30-
pure (cid, 42)
12+
import Template
13+
import qualified RetroInterface as Retro
3114

3215
multiTest : Script Int
3316
multiTest = do
@@ -83,30 +66,6 @@ waitForCid tries p cid
8366
Some _ -> pure ()
8467
where delay = seconds 1
8568

86-
87-
template Box with
88-
s: Party
89-
content: Text
90-
where
91-
signatory s
92-
key s: Party
93-
maintainer key
94-
nonconsuming choice Open: Text with
95-
c: Party
96-
controller c
97-
do
98-
pure content
99-
100-
template Helper with
101-
s: Party
102-
where
103-
signatory s
104-
nonconsuming choice FailWith: () with
105-
msg: Text
106-
controller s
107-
do
108-
error msg
109-
11069
disclosuresTest : Script Text
11170
disclosuresTest = do
11271
-- init
@@ -160,3 +119,14 @@ inactiveDisclosureDoesNotFailDuringSubmission = do
160119
pure ()
161120

162121
pure ()
122+
123+
retroactiveExercise: Script Int
124+
retroactiveExercise = do
125+
alice <- allocatePartyOn "alice" (ParticipantName "participant0")
126+
bob <- allocatePartyOn "bob" (ParticipantName "participant1")
127+
proposalCid <- alice `submit` createCmd (TProposal alice bob)
128+
waitForCid tries bob proposalCid
129+
(cid, _) <- bob `submit` exerciseCmd proposalCid (Accept)
130+
waitForCid tries alice cid
131+
alice `submit` exerciseCmd (toInterfaceContractId @Retro.I cid) (Retro.Noop alice)
132+
pure 43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
RetroInterface.daml and Template.daml were backported and created in 2024
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
-- Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
2+
-- SPDX-License-Identifier: Apache-2.0
3+
4+
module RetroInterface where
5+
6+
import Template
7+
8+
data IView = IView {}
9+
10+
interface I where
11+
viewtype IView
12+
13+
nonconsuming choice Noop : Int with
14+
a : Party
15+
controller a
16+
do pure 42
17+
18+
interface instance I for T where
19+
view = IView {}
20+
+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
-- Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
2+
-- SPDX-License-Identifier: Apache-2.0
3+
4+
module Template where
5+
6+
template T
7+
with
8+
p1 : Party
9+
p2 : Party
10+
where
11+
signatory p1, p2
12+
13+
template TProposal
14+
with
15+
p1 : Party
16+
p2 : Party
17+
where
18+
signatory p1
19+
observer p2
20+
choice Accept : (ContractId T, Int)
21+
controller p2
22+
do cid <- create T { p1, p2 }
23+
pure (cid, 42)
24+
25+
template Box with
26+
s: Party
27+
content: Text
28+
where
29+
signatory s
30+
key s: Party
31+
maintainer key
32+
nonconsuming choice Open: Text with
33+
c: Party
34+
controller c
35+
do
36+
pure content
37+
38+
template Helper with
39+
s: Party
40+
where
41+
signatory s
42+
nonconsuming choice FailWith: () with
43+
msg: Text
44+
controller s
45+
do
46+
error msg

sdk/daml-script/test/src/com/digitalasset/daml/lf/engine/script/test/Daml2ScriptTestRunner.scala

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ class Daml2ScriptTestRunner extends DamlScriptTestRunner {
2323
|MultiTest:listKnownPartiesTest SUCCESS
2424
|MultiTest:multiTest SUCCESS
2525
|MultiTest:partyIdHintTest SUCCESS
26+
|MultiTest:retroactiveExercise SUCCESS
2627
|ScriptExample:allocateParties SUCCESS
2728
|ScriptExample:initializeFixed SUCCESS
2829
|ScriptExample:initializeUser SUCCESS

sdk/daml-script/test/src/com/digitalasset/daml/lf/engine/script/test/MultiParticipantIT.scala

+23-3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
package com.daml.lf.engine.script
55
package test
66

7+
import com.daml.bazeltools.BazelRunfiles.rlocation
78
import com.daml.lf.data.FrontStack
89
import com.daml.lf.data.Ref._
910
import com.daml.lf.engine.script.ScriptTimeMode
@@ -13,6 +14,7 @@ import org.scalatest.Inside
1314
import org.scalatest.matchers.should.Matchers
1415
import org.scalatest.wordspec.AsyncWordSpec
1516

17+
import java.nio.file.Paths
1618
import scala.util.{Failure, Success}
1719

1820
class MultiParticipantITV1 extends MultiParticipantIT(LanguageMajorVersion.V1)
@@ -30,8 +32,10 @@ class MultiParticipantIT(override val majorLanguageVersion: LanguageMajorVersion
3032
final override protected lazy val nParticipants = 2
3133
final override protected lazy val timeMode = ScriptTimeMode.WallClock
3234

33-
// TODO(#17366): Delete once 2.0 is introduced and Canton supports LF v2 in non-dev mode.
34-
final override protected lazy val devMode = (majorLanguageVersion == LanguageMajorVersion.V2)
35+
final override protected lazy val darFiles = List(
36+
rlocation(Paths.get(s"daml-script/test/template.dar")),
37+
rlocation(Paths.get(s"daml-script/test/retrointerface.dar")),
38+
)
3539

3640
"Multi-participant Daml Script" can {
3741
"multiTest" should {
@@ -90,7 +94,6 @@ class MultiParticipantIT(override val majorLanguageVersion: LanguageMajorVersion
9094
)
9195
assert(vals.get(1) == second)
9296
}
93-
9497
}
9598
}
9699

@@ -129,5 +132,22 @@ class MultiParticipantIT(override val majorLanguageVersion: LanguageMajorVersion
129132
} yield error.getMessage should include regex """Unhandled Daml exception\: DA\.Exception\.GeneralError\:GeneralError\@[a-f0-9]{8}\{ message \= \"Here\" \}"""
130133
}
131134
}
135+
136+
"exercise retroactive instance" in {
137+
// Regression test for https://github.com/DACH-NY/canton/issues/20645
138+
// To reproduce the bug in 2.8.10 or 2.9.4 the package retrointerface.dar
139+
// should not be loaded in the participant1 before starting the test.
140+
// Unfortunately there is n simple way to ensure that, so here are some guardrail:
141+
// - do not write in this file other tests that use retroactiveExercise
142+
// - do not load the script in the ledger, but only the template code.
143+
for {
144+
clients <- scriptClients()
145+
r <- run(
146+
clients,
147+
QualifiedName.assertFromString("MultiTest:retroactiveExercise"),
148+
dar = dar,
149+
)
150+
} yield assert(r == SInt64(43))
151+
}
132152
}
133153
}

0 commit comments

Comments
 (0)