Use the appropriate daml-script for each Script (#14321)

* Add failing test

* Overload daml-script 'Runner.run' to work directly with 'PureCompiledPackages'

* Use new daml-script 'Runner.run' in scenario-service

changelog_begin
* data-dependencies: fixed an issue with the handling of multiple versions of the daml-script package (#14291)
changelog_end

Co-authored-by: Remy <remy.haemmerle@daml.com>
This commit is contained in:
Moisés Ackerman 2022-07-04 10:26:12 +02:00 committed by GitHub
parent 89b068ecde
commit ba9abf3c5f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 139 additions and 31 deletions

View File

@ -453,8 +453,11 @@ da_haskell_test(
"//compiler/damlc",
"@damlc_legacy",
"//compiler/damlc/tests:generate-simple-dalf",
"//daml-script/daml:daml-script.dar",
# Feel free to update this to 0.13.55 once that is frozen.
":dars/old-proj-0.13.55-snapshot.20200309.3401.0.6f8c3ad8-1.8.dar",
# Used in regression test for https://github.com/digital-asset/daml/issues/14291
":dars/lib-with-script-0.0.1-sdk-2.2.0-lf-1.14.dar",
],
hackage_deps = [
"base",

View File

@ -31,7 +31,9 @@ main = do
setEnv "TASTY_NUM_THREADS" "3" True
damlc <- locateRunfiles (mainWorkspace </> "compiler" </> "damlc" </> exe "damlc")
damlcLegacy <- locateRunfiles ("damlc_legacy" </> exe "damlc_legacy")
damlScriptDar <- locateRunfiles (mainWorkspace </> "daml-script" </> "daml" </> "daml-script.dar")
oldProjDar <- locateRunfiles (mainWorkspace </> "compiler" </> "damlc" </> "tests" </> "dars" </> "old-proj-0.13.55-snapshot.20200309.3401.0.6f8c3ad8-1.8.dar")
libWithScriptDar <- locateRunfiles (mainWorkspace </> "compiler" </> "damlc" </> "tests" </> "dars" </> "lib-with-script-0.0.1-sdk-2.2.0-lf-1.14.dar")
let validate dar = callProcessSilent damlc ["validate-dar", dar]
defaultMain $ tests Tools{..}
@ -39,7 +41,9 @@ data Tools = Tools -- and places
{ damlc :: FilePath
, damlcLegacy :: FilePath
, validate :: FilePath -> IO ()
, damlScriptDar :: FilePath
, oldProjDar :: FilePath
, libWithScriptDar :: FilePath
}
damlcForTarget :: Tools -> LF.Version -> FilePath
@ -65,7 +69,7 @@ lfVersionTestPairs =
in legacyPairs ++ zip versions (tail versions)
tests :: Tools -> TestTree
tests tools@Tools{damlc,validate,oldProjDar} = testGroup "Data Dependencies" $
tests tools = testGroup "Data Dependencies" $
[ testCaseSteps ("Cross Daml-LF version: " <> LF.renderVersion depLfVer <> " -> " <> LF.renderVersion targetLfVer) $ \step -> withTempDir $ \tmpDir -> do
let proja = tmpDir </> "proja"
let projb = tmpDir </> "projb"
@ -2329,6 +2333,63 @@ tests tools@Tools{damlc,validate,oldProjDar} = testGroup "Data Dependencies" $
, "--project-root", path mainProj
]
, testCaseSteps "Cross-SDK data-dependency with daml-script" $ \step' -> withTempDir $ \tmpDir -> do
-- regression test for https://github.com/digital-asset/daml/issues/14291
let
mainProj = "main"
path proj = tmpDir </> proj
damlYaml proj = path proj </> "daml.yaml"
damlMod proj mod = path proj </> mod <.> "daml"
step proj = step' ("building '" <> proj <> "' project")
step mainProj >> do
createDirectoryIfMissing True (path mainProj)
writeFileUTF8 (damlYaml mainProj) $ unlines
[ "sdk-version: " <> sdkVersion
, "name: " <> mainProj
, "source: ."
, "version: 0.1.0"
, "dependencies: [daml-prim, daml-stdlib, " <> damlScriptDar <> "]"
, "data-dependencies: [" <> libWithScriptDar <> "]"
]
writeFileUTF8 (damlMod mainProj "Main") $ unlines
[ "module Main where"
, "import Daml.Script"
, "import Lib qualified"
, "template T1"
, " with"
, " party : Party"
, " where"
, " signatory party"
, " nonconsuming choice C1 : Bool"
, " controller party"
, " do pure False"
, "run1 : Lib.Script ()"
, "run1 = Lib.run"
, "run2 : Script ()"
, "run2 = script do"
, " alice <- allocateParty \"alice\""
, " t <- alice `submit` createCmd Lib.T0 with party = alice"
, " b <- alice `submit` exerciseCmd t Lib.C0"
, " debug b"
, " alice `submit` archiveCmd t"
, " t <- alice `submit` createCmd T1 with party = alice"
, " b <- alice `submit` exerciseCmd t C1"
, " debug b"
, " alice `submit` archiveCmd t"
]
callProcessSilent damlc
[ "test"
, "--project-root", path mainProj
]
, testCaseSteps "User-defined exceptions" $ \step -> withTempDir $ \tmpDir -> do
step "building project to be imported via data-dependencies"
createDirectoryIfMissing True (tmpDir </> "lib")
@ -2506,6 +2567,14 @@ tests tools@Tools{damlc,validate,oldProjDar} = testGroup "Data Dependencies" $
, "--target", LF.renderVersion LF.versionDev ]
]
where
Tools
{ damlc
, validate
, damlScriptDar
, oldProjDar
, libWithScriptDar
} = tools
simpleImportTest :: String -> [String] -> [String] -> TestTree
simpleImportTest title = simpleImportTestOptions title []

View File

@ -10,15 +10,15 @@ import akka.stream.Materializer
import com.daml.grpc.adapter.ExecutionSequencerFactory
import com.daml.lf.archive
import com.daml.lf.data.{assertRight, ImmArray}
import com.daml.lf.data.Ref.{DottedName, Identifier, ModuleName, PackageId, QualifiedName}
import com.daml.lf.data.Ref.{Identifier, ModuleName, PackageId, QualifiedName}
import com.daml.lf.engine.script.ledgerinteraction.{IdeLedgerClient, ScriptTimeMode}
import com.daml.lf.language.{Ast, LanguageVersion, Util => AstUtil}
import com.daml.lf.scenario.api.v1.{ScenarioModule => ProtoScenarioModule}
import com.daml.lf.speedy.{Compiler, SDefinition, SExpr, Speedy}
import com.daml.lf.speedy.{Compiler, SDefinition, Speedy}
import com.daml.lf.speedy.SExpr.{LfDefRef, SDefinitionRef}
import com.daml.lf.validation.Validation
import com.google.protobuf.ByteString
import com.daml.lf.engine.script.{Participants, Runner, Script, ScriptF, ScriptIds}
import com.daml.lf.engine.script.{Participants, Runner, ScriptF}
import com.daml.logging.LoggingContext
import scala.concurrent.ExecutionContext
@ -164,23 +164,22 @@ class Context(val contextId: Context.ContextId, languageVersion: LanguageVersion
): Future[Option[ScenarioRunner.ScenarioResult]] = {
val defns = this.defns
val compiledPackages = PureCompiledPackages(allSignatures, defns, compilerConfig)
val expectedScriptId = DottedName.assertFromString("Daml.Script")
val Some(scriptPackageId) = allSignatures.collectFirst {
case (pkgId, pkg) if pkg.modules contains expectedScriptId => pkgId
}
val scriptExpr = SExpr.SEVal(
LfDefRef(Identifier(PackageId.assertFromString(pkgId), QualifiedName.assertFromString(name)))
)
val runner = new Runner(
compiledPackages,
Script.Action(scriptExpr, ScriptIds(scriptPackageId)),
ScriptTimeMode.Static,
)
val scriptId =
Identifier(PackageId.assertFromString(pkgId), QualifiedName.assertFromString(name))
val traceLog = Speedy.Machine.newTraceLog
val warningLog = Speedy.Machine.newWarningLog
val ledgerClient: IdeLedgerClient = new IdeLedgerClient(compiledPackages, traceLog, warningLog)
val participants = Participants(Some(ledgerClient), Map.empty, Map.empty)
val (clientMachine, resultF) = runner.runWithClients(participants, traceLog, warningLog)
val (clientMachine, resultF) = Runner.run(
compiledPackages = compiledPackages,
scriptId = scriptId,
convertInputValue = None,
inputValue = None,
initialClients = participants,
timeMode = ScriptTimeMode.Static,
traceLog = traceLog,
warningLog = warningLog,
)
def handleFailure(e: Error) =
// SError are the errors that should be handled and displayed as

View File

@ -320,31 +320,68 @@ object Runner {
): Future[SValue] = {
val darMap = dar.all.toMap
val compiledPackages = PureCompiledPackages.assertBuild(darMap, Runner.compilerConfig)
def converter(json: JsValue, typ: Type) = {
val ifaceDar = dar.map(pkg => InterfaceReader.readInterface(() => \/-(pkg))._2)
val envIface = EnvironmentInterface.fromReaderInterfaces(ifaceDar)
Converter.fromJsonValue(
scriptId.qualifiedName,
envIface,
compiledPackages,
typ,
json,
)
}
run(
compiledPackages,
scriptId,
Some(converter),
inputValue,
initialClients,
timeMode,
)._2
}
// Executes a Daml script
//
// Looks for the script in the given compiledPackages, applies the input
// value as an argument if provided together with a conversion function,
// and runs the script with the given participants.
def run(
compiledPackages: PureCompiledPackages,
scriptId: Identifier,
convertInputValue: Option[(JsValue, Type) => Either[String, SValue]],
inputValue: Option[JsValue],
initialClients: Participants[ScriptLedgerClient],
timeMode: ScriptTimeMode,
traceLog: TraceLog = Speedy.Machine.newTraceLog,
warningLog: WarningLog = Speedy.Machine.newWarningLog,
)(implicit
ec: ExecutionContext,
esf: ExecutionSequencerFactory,
mat: Materializer,
): (Speedy.Machine, Future[SValue]) = {
val script = data.assertRight(Script.fromIdentifier(compiledPackages, scriptId))
val scriptAction: Script.Action = (script, inputValue) match {
case (script: Script.Action, None) => script
case (script: Script.Function, Some(inputJson)) =>
val ifaceDar = dar.map(pkg => InterfaceReader.readInterface(() => \/-(pkg))._2)
val envIface = EnvironmentInterface.fromReaderInterfaces(ifaceDar)
val arg = Converter
.fromJsonValue(
scriptId.qualifiedName,
envIface,
compiledPackages,
script.param,
inputJson,
) match {
case Left(msg) => throw new ConverterException(msg)
case Right(x) => x
convertInputValue match {
case Some(f) =>
f(inputJson, script.param) match {
case Left(msg) => throw new ConverterException(msg)
case Right(arg) => script.apply(SEValue(arg))
}
case None =>
throw new RuntimeException(
s"The script ${scriptId} requires an argument, but a converter was not provided"
)
}
script.apply(SEValue(arg))
case (_: Script.Action, Some(_)) =>
throw new RuntimeException(s"The script ${scriptId} does not take arguments.")
case (_: Script.Function, None) =>
throw new RuntimeException(s"The script ${scriptId} requires an argument.")
}
val runner = new Runner(compiledPackages, scriptAction, timeMode)
runner.runWithClients(initialClients)._2
runner.runWithClients(initialClients, traceLog, warningLog)
}
}