From febca5d62d431916f958b207e6115d0eb14c52e7 Mon Sep 17 00:00:00 2001 From: Moritz Kiefer Date: Thu, 17 Jun 2021 19:12:45 +0200 Subject: [PATCH] Use ScenarioRunner.submit in Daml Script (#10053) * Use ScenarioRunner.submit in Daml Script changelog_begin changelog_end * privatize ledger variable changelog_begin changelog_end * drop space changelog_begin changelog_end --- .../damlc/tests/src/DA/Test/ScriptService.hs | 6 +- .../daml/lf/ScenarioServiceMain.scala | 52 ++-- .../daml/lf/scenario/Context.scala | 8 +- .../daml/lf/speedy/SBuiltin.scala | 5 +- .../digitalasset/daml/lf/speedy/Speedy.scala | 23 -- .../daml/lf/speedy/ScenarioRunner.scala | 38 +-- .../lf/speedy/perf/CollectAuthority.scala | 7 +- daml-script/runner/BUILD.bazel | 1 - .../daml/lf/engine/script/ScriptF.scala | 1 - .../ledgerinteraction/GrpcLedgerClient.scala | 1 - .../ledgerinteraction/IdeLedgerClient.scala | 246 ++++++++---------- .../ledgerinteraction/JsonLedgerClient.scala | 1 - .../ScriptLedgerClient.scala | 2 - 13 files changed, 163 insertions(+), 228 deletions(-) diff --git a/compiler/damlc/tests/src/DA/Test/ScriptService.hs b/compiler/damlc/tests/src/DA/Test/ScriptService.hs index 1aa1227d4400..2a4970699188 100644 --- a/compiler/damlc/tests/src/DA/Test/ScriptService.hs +++ b/compiler/damlc/tests/src/DA/Test/ScriptService.hs @@ -367,7 +367,7 @@ main = " pure ()" ] expectScriptSuccess rs (vr "testAssertFail") $ \r -> - matchRegex r "Active contracts: #0:0, #1:0\n\nReturn value: {}\n\n$" + matchRegex r "Active contracts: #0:0, #2:0\n\nReturn value: {}\n\n$" pure (), testCase "contract keys" $ do rs <- @@ -667,7 +667,7 @@ main = , " submitMulti [p0] [p1] (createCmd (T p0 p1))" ] expectScriptSuccess rs (vr "testSucceed") $ \r -> - matchRegex r "Active contracts: #0:0, #1:0" + matchRegex r "Active contracts: #2:0, #3:0" expectScriptFailure rs (vr "testFail") $ \r -> matchRegex r "missing authorization from 'p1'", testCase "submitTree" $ do @@ -816,7 +816,7 @@ main = matchRegex :: T.Text -> T.Text -> Bool matchRegex s regex = matchTest (makeRegex regex :: Regex) s -expectScriptSuccess :: +expectScriptSuccess :: HasCallStack => -- | The list of script results. [(VirtualResource, Either T.Text T.Text)] -> -- | VR of the script diff --git a/compiler/scenario-service/server/src/main/scala/com/digitalasset/daml/lf/ScenarioServiceMain.scala b/compiler/scenario-service/server/src/main/scala/com/digitalasset/daml/lf/ScenarioServiceMain.scala index 5517bc65d842..ea62a0d3832d 100644 --- a/compiler/scenario-service/server/src/main/scala/com/digitalasset/daml/lf/ScenarioServiceMain.scala +++ b/compiler/scenario-service/server/src/main/scala/com/digitalasset/daml/lf/ScenarioServiceMain.scala @@ -13,6 +13,7 @@ import scalaz.std.scalaFuture._ import scalaz.syntax.traverse._ import com.daml.lf.archive.Decode.ParseError +import com.daml.lf.data.ImmArray import com.daml.lf.data.Ref import com.daml.lf.data.Ref.ModuleName import com.daml.lf.language.LanguageVersion @@ -176,35 +177,33 @@ class ScenarioService(implicit } context .interpretScript(packageId, scenarioId.getName) - .map(_.map { case (ledger, (clientMachine, ledgerMachine), errOrValue) => + .map(_.map { case (ledger, (clientMachine, submissionCache), errOrValue) => val builder = RunScenarioResponse.newBuilder - ledgerMachine.withOnLedger("runScript") { onLedger => - errOrValue match { - case Left(err) => - builder.setError( - new Conversions( - context.homePackageId, - ledger, - onLedger.incompleteTransaction(), - clientMachine.traceLog, - onLedger.commitLocation, - ledgerMachine.stackTrace(), - ) - .convertScenarioError(err) + errOrValue match { + case Left(err) => + builder.setError( + new Conversions( + context.homePackageId, + ledger, + submissionCache.ptx.finishIncomplete, + clientMachine.traceLog, + submissionCache.commitLocation, + ImmArray.empty, ) - case Right(value) => - builder.setResult( - new Conversions( - context.homePackageId, - ledger, - onLedger.incompleteTransaction(), - clientMachine.traceLog, - onLedger.commitLocation, - ledgerMachine.stackTrace(), - ) - .convertScenarioResult(value) + .convertScenarioError(err) + ) + case Right(value) => + builder.setResult( + new Conversions( + context.homePackageId, + ledger, + submissionCache.ptx.finishIncomplete, + clientMachine.traceLog, + submissionCache.commitLocation, + ImmArray.empty, ) - } + .convertScenarioResult(value) + ) } builder.build }) @@ -219,6 +218,7 @@ class ScenarioService(implicit respObs.onNext(resp) respObs.onCompleted() case Failure(err) => + System.err.println(err) respObs.onError(err) } } diff --git a/compiler/scenario-service/server/src/main/scala/com/digitalasset/daml/lf/scenario/Context.scala b/compiler/scenario-service/server/src/main/scala/com/digitalasset/daml/lf/scenario/Context.scala index 5258184d2d1b..c3e174b646a1 100644 --- a/compiler/scenario-service/server/src/main/scala/com/digitalasset/daml/lf/scenario/Context.scala +++ b/compiler/scenario-service/server/src/main/scala/com/digitalasset/daml/lf/scenario/Context.scala @@ -190,7 +190,9 @@ class Context(val contextId: Context.ContextId, languageVersion: LanguageVersion ec: ExecutionContext, esf: ExecutionSequencerFactory, mat: Materializer, - ): Future[Option[(ScenarioLedger, (Speedy.Machine, Speedy.Machine), Either[SError, SValue])]] = { + ): Future[Option[ + (ScenarioLedger, (Speedy.Machine, IdeLedgerClient.SubmissionCache), Either[SError, SValue]) + ]] = { val defns = this.defns val compiledPackages = PureCompiledPackages(allSignatures, defns, compilerConfig) val expectedScriptId = DottedName.assertFromString("Daml.Script") @@ -220,7 +222,7 @@ class Context(val contextId: Context.ContextId, languageVersion: LanguageVersion clientMachine.traceLog.add(msg, optLoc) } Success( - Some((ledgerClient.scenarioRunner.ledger, (clientMachine, ledgerClient.machine), Left(e))) + Some((ledgerClient.ledger, (clientMachine, ledgerClient.lastSubmission), Left(e))) ) } @@ -228,7 +230,7 @@ class Context(val contextId: Context.ContextId, languageVersion: LanguageVersion case Success(v) => Success( Some( - (ledgerClient.scenarioRunner.ledger, (clientMachine, ledgerClient.machine), Right(v)) + (ledgerClient.ledger, (clientMachine, ledgerClient.lastSubmission), Right(v)) ) ) case Failure(e: SError) => handleFailure(e) diff --git a/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala b/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala index 8140c0495941..73c62a132291 100644 --- a/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala +++ b/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala @@ -1310,10 +1310,7 @@ private[lf] object SBuiltin { commands = args.get(1), location = optLocation, mustFail = mustFail, - callback = newValue => { - machine.clearCommit - machine.returnValue = newValue - }, + callback = newValue => machine.returnValue = newValue, ) ) } diff --git a/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/Speedy.scala b/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/Speedy.scala index 657fa283c2ad..faae95e838e7 100644 --- a/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/Speedy.scala +++ b/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/Speedy.scala @@ -30,10 +30,6 @@ import scala.util.control.NoStackTrace private[lf] object Speedy { - // fake participant to generate a new transactionSeed when running scenarios - private[this] val scenarioServiceParticipant = - Ref.ParticipantId.assertFromString("scenario-service") - // Would like these to have zero cost when not enabled. Better still, to be switchable at runtime. private[this] val enableInstrumentation: Boolean = false private[this] val enableLightweightStepTracing: Boolean = false @@ -603,25 +599,6 @@ private[lf] object Speedy { println("============================================================") } - // reinitialize the state of the machine with a new fresh submission seed. - // Should be used only when running scenario - private[lf] def clearCommit: Unit = withOnLedger("clearCommit") { onLedger => - val freshSeed = - crypto.Hash.deriveTransactionSeed( - onLedger.ptx.context.nextActionChildSeed, - scenarioServiceParticipant, - onLedger.ptx.submissionTime, - ) - onLedger.committers = Set.empty - onLedger.commitLocation = None - onLedger.ptx = PartialTransaction.initial( - onLedger.ptx.packageToTransactionVersion, - onLedger.ptx.contractKeyUniqueness, - onLedger.ptx.submissionTime, - InitialSeeding.TransactionSeed(freshSeed), - ) - } - // This translates a well-typed LF value (typically coming from the ledger) // to speedy value and set the control of with the result. // Note the method does not check the value is well-typed as opposed as diff --git a/daml-lf/scenario-interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/ScenarioRunner.scala b/daml-lf/scenario-interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/ScenarioRunner.scala index 6fb3fd21425e..042a7a0ed963 100644 --- a/daml-lf/scenario-interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/ScenarioRunner.scala +++ b/daml-lf/scenario-interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/ScenarioRunner.scala @@ -52,15 +52,6 @@ final case class ScenarioRunner( case Right(t) => Right(t) } - private[this] def nextSeed(submissionSeed: crypto.Hash): crypto.Hash = - crypto.Hash.deriveTransactionSeed( - submissionSeed, - Ref.ParticipantId.assertFromString("scenario-service"), - // MinValue makes no sense here but this is what we did before so - // to avoid breaking all tests we keep it for now at least. - Time.Timestamp.MinValue, - ) - private def runUnsafe(): (Double, Int, ScenarioLedger, SValue) = { // NOTE(JM): Written with an imperative loop and exceptions for speed // and so that we don't need to worry about stack usage. @@ -92,7 +83,8 @@ final case class ScenarioRunner( machine.compiledPackages, ScenarioLedgerApi(ledger), committers, - commands, + Set.empty, + SExpr.SEValue(commands), location, seed, ) @@ -253,6 +245,7 @@ object ScenarioRunner { def currentTime: Time.Timestamp def commit( committers: Set[Party], + readAs: Set[Party], location: Option[Location], tx: SubmittedTransaction, ): Either[SError, R] @@ -368,12 +361,13 @@ object ScenarioRunner { override def currentTime = ledger.currentTime override def commit( committers: Set[Party], + readAs: Set[Party], location: Option[Location], tx: SubmittedTransaction, ): Either[SError, ScenarioLedger.CommitResult] = ScenarioLedger.commitTransaction( actAs = committers, - readAs = Set.empty, + readAs = readAs, effectiveAt = ledger.currentTime, optLocation = location, tx = tx, @@ -390,7 +384,8 @@ object ScenarioRunner { compiledPackages: CompiledPackages, ledger: LedgerApi[R], committers: Set[Party], - commands: SValue, + readAs: Set[Party], + commands: SExpr, location: Option[Location], seed: crypto.Hash, ): SubmissionResult[R] = { @@ -398,7 +393,7 @@ object ScenarioRunner { compiledPackages = compiledPackages, submissionTime = Time.Timestamp.MinValue, initialSeeding = InitialSeeding.TransactionSeed(seed), - expr = SExpr.SEApp(SExpr.SEValue(commands), Array(SExpr.SEValue(SValue.SToken))), + expr = SExpr.SEApp(commands, Array(SExpr.SEValue(SValue.SToken))), globalCids = Set.empty, committers = committers, ) @@ -412,7 +407,7 @@ object ScenarioRunner { case SResultFinalValue(resultValue) => onLedger.ptxInternal.finish match { case PartialTransaction.CompleteTransaction(tx) => - ledger.commit(committers, location, tx) match { + ledger.commit(committers, readAs, location, tx) match { case Left(err) => SubmissionError(err, onLedger.ptxInternal, ledgerMachine.traceLog) case Right(r) => Commit(r, resultValue, onLedger.ptxInternal, ledgerMachine.traceLog) @@ -423,17 +418,17 @@ object ScenarioRunner { case SResultError(err) => SubmissionError(err, onLedger.ptxInternal, ledgerMachine.traceLog) case SResultNeedContract(coid, tid @ _, committers, _, cbPresent) => - ledger.lookupContract(coid, committers, Set.empty, cbPresent) match { + ledger.lookupContract(coid, committers, readAs, cbPresent) match { case Left(err) => SubmissionError(err, onLedger.ptxInternal, ledgerMachine.traceLog) case Right(_) => go() } case SResultNeedKey(keyWithMaintainers, committers, cb) => - ledger.lookupKey(keyWithMaintainers.globalKey, committers, Set.empty, cb) match { + ledger.lookupKey(keyWithMaintainers.globalKey, committers, readAs, cb) match { case Left(err) => SubmissionError(err, onLedger.ptxInternal, ledgerMachine.traceLog) case Right(_) => go() } case SResultNeedLocalKeyVisible(stakeholders, committers, cb) => - val visible = SVisibleByKey.fromSubmitters(committers, Set.empty)(stakeholders) + val visible = SVisibleByKey.fromSubmitters(committers, readAs)(stakeholders) cb(visible) go() case SResultNeedTime(callback) => @@ -451,4 +446,13 @@ object ScenarioRunner { } go() } + + private[lf] def nextSeed(submissionSeed: crypto.Hash): crypto.Hash = + crypto.Hash.deriveTransactionSeed( + submissionSeed, + Ref.ParticipantId.assertFromString("scenario-service"), + // MinValue makes no sense here but this is what we did before so + // to avoid breaking all tests we keep it for now at least. + Time.Timestamp.MinValue, + ) } diff --git a/daml-lf/scenario-interpreter/src/perf/benches/scala/com/digitalasset/daml/lf/speedy/perf/CollectAuthority.scala b/daml-lf/scenario-interpreter/src/perf/benches/scala/com/digitalasset/daml/lf/speedy/perf/CollectAuthority.scala index ba4c452c7d24..d87c547496c5 100644 --- a/daml-lf/scenario-interpreter/src/perf/benches/scala/com/digitalasset/daml/lf/speedy/perf/CollectAuthority.scala +++ b/daml-lf/scenario-interpreter/src/perf/benches/scala/com/digitalasset/daml/lf/speedy/perf/CollectAuthority.scala @@ -90,7 +90,8 @@ class CollectAuthorityState { machine.compiledPackages, api, committers, - commands, + Set.empty, + SExpr.SEValue(commands), location, crypto.Hash.hashPrivateKey(step.toString), ) match { @@ -131,7 +132,8 @@ class CollectAuthorityState { machine.compiledPackages, api, committers, - commands, + Set.empty, + SExpr.SEValue(commands), location, crypto.Hash.hashPrivateKey(step.toString), ) match { @@ -203,6 +205,7 @@ class CannedLedgerApi( override def commit( committers: Set[Party], + readAs: Set[Party], location: Option[Location], tx: SubmittedTransaction, ) = Right(()) diff --git a/daml-script/runner/BUILD.bazel b/daml-script/runner/BUILD.bazel index 601eab4509c1..c2d67c79cd63 100644 --- a/daml-script/runner/BUILD.bazel +++ b/daml-script/runner/BUILD.bazel @@ -56,7 +56,6 @@ da_scala_library( "//ledger/ledger-api-client", "//ledger/ledger-api-common", "//libs-scala/auth-utils", - "//libs-scala/scala-utils", ], ) diff --git a/daml-script/runner/src/main/scala/com/digitalasset/daml/lf/engine/script/ScriptF.scala b/daml-script/runner/src/main/scala/com/digitalasset/daml/lf/engine/script/ScriptF.scala index 51f2a92b2729..4e21885dd32b 100644 --- a/daml-script/runner/src/main/scala/com/digitalasset/daml/lf/engine/script/ScriptF.scala +++ b/daml-script/runner/src/main/scala/com/digitalasset/daml/lf/engine/script/ScriptF.scala @@ -71,7 +71,6 @@ object ScriptF { for ((msg, optLoc) <- client.tracelogIterator) { machine.traceLog.add(msg, optLoc) } - client.clearTracelog } def addPartyParticipantMapping(party: Party, participant: Participant) = { _clients = diff --git a/daml-script/runner/src/main/scala/com/digitalasset/daml/lf/engine/script/ledgerinteraction/GrpcLedgerClient.scala b/daml-script/runner/src/main/scala/com/digitalasset/daml/lf/engine/script/ledgerinteraction/GrpcLedgerClient.scala index 16b1101b5540..01214d4f9f94 100644 --- a/daml-script/runner/src/main/scala/com/digitalasset/daml/lf/engine/script/ledgerinteraction/GrpcLedgerClient.scala +++ b/daml-script/runner/src/main/scala/com/digitalasset/daml/lf/engine/script/ledgerinteraction/GrpcLedgerClient.scala @@ -324,5 +324,4 @@ class GrpcLedgerClient(val grpcClient: LedgerClient, val applicationId: Applicat } override def tracelogIterator = Iterator.empty - override def clearTracelog = () } diff --git a/daml-script/runner/src/main/scala/com/digitalasset/daml/lf/engine/script/ledgerinteraction/IdeLedgerClient.scala b/daml-script/runner/src/main/scala/com/digitalasset/daml/lf/engine/script/ledgerinteraction/IdeLedgerClient.scala index e078312b511c..d6f511e81c54 100644 --- a/daml-script/runner/src/main/scala/com/digitalasset/daml/lf/engine/script/ledgerinteraction/IdeLedgerClient.scala +++ b/daml-script/runner/src/main/scala/com/digitalasset/daml/lf/engine/script/ledgerinteraction/IdeLedgerClient.scala @@ -9,13 +9,15 @@ import com.daml.ledger.api.domain.PartyDetails import com.daml.lf.data.Ref._ import com.daml.lf.data.{ImmArray, Ref, Time} import com.daml.lf.scenario.ScenarioLedger -import com.daml.lf.scenario.ScenarioLedger.RichTransaction import com.daml.lf.speedy.SError._ -import com.daml.lf.speedy.SExpr._ -import com.daml.lf.speedy.SResult._ -import com.daml.lf.speedy.SValue._ -import com.daml.lf.speedy.Speedy.{Machine, OffLedger, OnLedger} -import com.daml.lf.speedy.{PartialTransaction, SValue, ScenarioRunner, TraceLog} +import com.daml.lf.speedy.SValue +import com.daml.lf.speedy.{ + InitialSeeding, + PartialTransaction, + RingBufferTraceLog, + ScenarioRunner, + TraceLog, +} import com.daml.lf.transaction.Node.{ NodeRollback, NodeCreate, @@ -23,60 +25,63 @@ import com.daml.lf.transaction.Node.{ NodeFetch, NodeLookupByKey, } -import com.daml.lf.transaction.{GlobalKey, NodeId} +import com.daml.lf.transaction.{ContractKeyUniquenessMode, GlobalKey, NodeId, TransactionVersion} import com.daml.lf.value.Value import com.daml.lf.value.Value.ContractId import com.daml.lf._ -import com.daml.scalautil.Statement.discard import com.daml.script.converter.ConverterException import io.grpc.StatusRuntimeException +import org.slf4j.LoggerFactory import scalaz.OneAnd import scalaz.OneAnd._ import scalaz.std.set._ import scalaz.syntax.foldable._ import scala.collection.compat.immutable.LazyList -import scala.collection.mutable.ArrayBuffer import scala.concurrent.{ExecutionContext, Future} import scala.util.{Failure, Success} // Client for the script service. class IdeLedgerClient(val compiledPackages: CompiledPackages) extends ScriptLedgerClient { - class ArrayBufferTraceLog extends TraceLog { - val buffer = ArrayBuffer[(String, Option[Location])]() - override def add(message: String, optLocation: Option[Location]): Unit = { - discard { buffer.append((message, optLocation)) } - } - override def iterator: Iterator[(String, Option[Location])] = { - buffer.iterator - } - def clear: Unit = buffer.clear() - } + private val pkg2TxVersion = + compiledPackages.interface.packageLanguageVersion.andThen( + TransactionVersion.assignNodeVersion + ) + private val contractKeyUniqueness = ContractKeyUniquenessMode.On + private val damlTraceLog = LoggerFactory.getLogger("daml.tracelog") - val traceLog = new ArrayBufferTraceLog() + import IdeLedgerClient.SubmissionCache - private[this] val preprocessor = new engine.preprocessing.CommandPreprocessor(compiledPackages) + private var seed = crypto.Hash.hashPrivateKey(s"script-service") - private val seed = crypto.Hash.hashPrivateKey(s"script-service") + private var _lastSubmission: Option[SubmissionCache] = None - private val txSeeding = - speedy.InitialSeeding.TransactionSeed(seed) + private[this] def emptyPtx: PartialTransaction = PartialTransaction.initial( + pkg2TxVersion, + contractKeyUniqueness, + ledger.currentTime, + InitialSeeding.TransactionSeed(seed), + ) - // Machine for submissions. - val machine = Machine( - compiledPackages, - submissionTime = Time.Timestamp.Epoch, - initialSeeding = txSeeding, - expr = null, - globalCids = Set.empty, - committers = Set.empty, - traceLog = traceLog, + private[this] def clearPtx(): Unit = + _lastSubmission = _lastSubmission.map(cache => + cache.copy( + ptx = emptyPtx + ) + ) + + def lastSubmission: SubmissionCache = _lastSubmission.getOrElse( + SubmissionCache( + ptx = emptyPtx, + traceLog = RingBufferTraceLog(damlTraceLog, 100), + commitLocation = None, + ) ) - val onLedger = machine.ledgerMode match { - case OffLedger => throw SRequiresOnLedger("ScenarioRunner") - case onLedger: OnLedger => onLedger - } - val scenarioRunner = ScenarioRunner(machine, seed) + + private[this] val preprocessor = new engine.preprocessing.CommandPreprocessor(compiledPackages) + + private var _ledger: ScenarioLedger = ScenarioLedger.initialLedger(Time.Timestamp.Epoch) + def ledger: ScenarioLedger = _ledger private var allocatedParties: Map[String, PartyDetails] = Map() @@ -84,9 +89,9 @@ class IdeLedgerClient(val compiledPackages: CompiledPackages) extends ScriptLedg ec: ExecutionContext, mat: Materializer, ): Future[Seq[ScriptLedgerClient.ActiveContract]] = { - val acs = scenarioRunner.ledger.query( + val acs = ledger.query( view = ScenarioLedger.ParticipantView(Set(), Set(parties.toList: _*)), - effectiveAt = scenarioRunner.ledger.currentTime, + effectiveAt = ledger.currentTime, ) val filtered = acs.collect { case ScenarioLedger.LookupOk(cid, Value.ContractInst(tpl, arg, _), stakeholders) @@ -106,9 +111,9 @@ class IdeLedgerClient(val compiledPackages: CompiledPackages) extends ScriptLedg ec: ExecutionContext, mat: Materializer, ): Future[Option[ScriptLedgerClient.ActiveContract]] = { - scenarioRunner.ledger.lookupGlobalContract( + ledger.lookupGlobalContract( view = ScenarioLedger.ParticipantView(Set(), Set(parties.toList: _*)), - effectiveAt = scenarioRunner.ledger.currentTime, + effectiveAt = ledger.currentTime, cid, ) match { case ScenarioLedger.LookupOk(_, Value.ContractInst(_, arg, _), stakeholders) @@ -137,7 +142,7 @@ class IdeLedgerClient(val compiledPackages: CompiledPackages) extends ScriptLedg .build(templateId, key.toValue) .fold(err => Future.failed(new ConverterException(err)), Future.successful(_)) .flatMap { gkey => - scenarioRunner.ledger.ledgerData.activeKeys.get(gkey) match { + ledger.ledgerData.activeKeys.get(gkey) match { case None => Future.successful(None) case Some(cid) => queryContractId(parties, templateId, cid) } @@ -151,85 +156,24 @@ class IdeLedgerClient(val compiledPackages: CompiledPackages) extends ScriptLedg commands: List[command.ApiCommand], optLocation: Option[Location], )(implicit ec: ExecutionContext): Future[ - Either[StatusRuntimeException, RichTransaction] + ScenarioRunner.SubmissionResult[ScenarioLedger.CommitResult] ] = Future { - // Clear state at the beginning like in SBSBeginCommit for scenarios. - machine.returnValue = null - onLedger.commitLocation = optLocation - onLedger.globalDiscriminators = Set.empty - onLedger.cachedContracts = Map.empty val speedyCommands = preprocessor.unsafePreprocessCommands(commands.to(ImmArray))._1 val translated = compiledPackages.compiler.unsafeCompile(speedyCommands) - machine.setExpressionToEvaluate(SEApp(translated, Array(SEValue.Token))) - onLedger.committers = actAs.toSet - var result: RichTransaction = null - while (result == null) { - machine.run() match { - case SResultNeedContract(coid, tid @ _, committers @ _, _, cbPresent) => - discard { - ScenarioRunner - .ScenarioLedgerApi(scenarioRunner.ledger) - .lookupContract(coid, actAs.toSet, readAs, cbPresent) - .toTry - .get - } - case SResultNeedKey(keyWithMaintainers, committers @ _, cb) => - ScenarioRunner - .ScenarioLedgerApi(scenarioRunner.ledger) - .lookupKey(keyWithMaintainers.globalKey, actAs.toSet, readAs, cb) - .toTry - .get - case SResultNeedLocalKeyVisible(stakeholders, committers @ _, cb) => - val visible = SVisibleByKey.fromSubmitters(actAs.toSet, readAs)(stakeholders) - cb(visible) - case SResultFinalValue(SUnit) => - onLedger.ptxInternal.finish match { - case PartialTransaction.CompleteTransaction(tx) => - ScenarioLedger.commitTransaction( - actAs = actAs.toSet, - readAs = readAs, - effectiveAt = scenarioRunner.ledger.currentTime, - optLocation = onLedger.commitLocation, - tx = tx, - l = scenarioRunner.ledger, - ) match { - case Left(fas) => - // Capture the error and exit. - throw ScenarioErrorCommitError(fas) - case Right(commitResult) => - scenarioRunner.ledger = commitResult.newLedger - // Capture the result and exit. - result = commitResult.richTransaction - } - case PartialTransaction.IncompleteTransaction(ptx) => - throw new RuntimeException(s"Unexpected abort: $ptx") - } - case SResultFinalValue(v) => - // The final result should always be unit. - throw new RuntimeException(s"FATAL: Unexpected non-unit final result: $v") - case _: SResultScenarioSubmit => - throw new RuntimeException("FATAL: Encountered scenario submit in Daml Script") - case SResultError(err) => - // Capture the error and exit. - throw err - case SResultNeedTime(callback) => - callback(scenarioRunner.ledger.currentTime) - case SResultNeedPackage(pkg, callback @ _) => - throw new RuntimeException( - s"FATAL: Missing package $pkg should have been reported at Script compilation" - ) - case SResultScenarioPassTime(relTime @ _, callback @ _) => - throw new RuntimeException( - "FATAL: Encountered scenario instruction setTime in Daml Script" - ) - case SResultScenarioGetParty(partyText @ _, callback @ _) => - throw new RuntimeException( - "FATAL: Encountered scenario instruction getParty in Daml Script" - ) - } - } - Right(result) + + val ledgerApi = ScenarioRunner.ScenarioLedgerApi(ledger) + val result = ScenarioRunner.submit( + compiledPackages, + ledgerApi, + actAs.toSet, + readAs, + translated, + optLocation, + seed, + ) + _lastSubmission = Some(SubmissionCache(result.traceLog, result.ptx, optLocation)) + result } override def submit( @@ -242,10 +186,13 @@ class IdeLedgerClient(val compiledPackages: CompiledPackages) extends ScriptLedg mat: Materializer, ): Future[Either[StatusRuntimeException, Seq[ScriptLedgerClient.CommandResult]]] = unsafeSubmit(actAs, readAs, commands, optLocation).map { - case Right(richTransaction) => - val transaction = richTransaction.transaction - // Expected successful commit so clear. - machine.clearCommit + case ScenarioRunner.Commit(result, _, _, _) => + _ledger = result.newLedger + seed = ScenarioRunner.nextSeed( + crypto.Hash.deriveNodeSeed(seed, result.richTransaction.transaction.roots.length) + ) + clearPtx() + val transaction = result.richTransaction.transaction def convRootEvent(id: NodeId): ScriptLedgerClient.CommandResult = { val node = transaction.nodes.getOrElse( id, @@ -264,10 +211,10 @@ class IdeLedgerClient(val compiledPackages: CompiledPackages) extends ScriptLedg } } Right(transaction.roots.toSeq.map(convRootEvent(_))) - case Left(err) => + case ScenarioRunner.SubmissionError(err, _, _) => // Unexpected failure, do not clear so we can display the partial // transaction. - Left(err) + throw err } override def submitMustFail( @@ -278,15 +225,14 @@ class IdeLedgerClient(val compiledPackages: CompiledPackages) extends ScriptLedg )(implicit ec: ExecutionContext, mat: Materializer): Future[Either[Unit, Unit]] = { unsafeSubmit(actAs, readAs, commands, optLocation) .map({ - case Right(_) => Left(()) - // We don't expect to hit this case but list it for completeness. - case Left(_) => Right(()) - }) - .recoverWith({ case _: SError => - // Expected failed commit so clear, we do not clear on - // unexpected successes to keep the partial transaction. - machine.clearCommit - Future.successful(Right(())) + case _: ScenarioRunner.Commit[_] => Left(()) + case error: ScenarioRunner.SubmissionError => + _ledger = ledger.insertAssertMustFail(actAs.toSet, readAs, optLocation) + seed = ScenarioRunner.nextSeed( + error.ptx.unwind.context.nextActionChildSeed + ) + clearPtx() + Right(()) }) } @@ -300,10 +246,13 @@ class IdeLedgerClient(val compiledPackages: CompiledPackages) extends ScriptLedg mat: Materializer, ): Future[ScriptLedgerClient.TransactionTree] = { unsafeSubmit(actAs, readAs, commands, optLocation).map { - case Right(richTransaction) => - // Expected successful commit so clear. - machine.clearCommit - val transaction = richTransaction.transaction + case ScenarioRunner.Commit(result, _, _, _) => + _ledger = result.newLedger + seed = ScenarioRunner.nextSeed( + crypto.Hash.deriveNodeSeed(seed, result.richTransaction.transaction.roots.length) + ) + clearPtx() + val transaction = result.richTransaction.transaction def convEvent(id: NodeId): Option[ScriptLedgerClient.TreeEvent] = transaction.nodes(id) match { case create: NodeCreate[ContractId] => @@ -323,14 +272,14 @@ class IdeLedgerClient(val compiledPackages: CompiledPackages) extends ScriptLedg ScriptLedgerClient.TransactionTree( transaction.roots.collect(Function.unlift(convEvent(_))).toList ) - case Left(err) => throw new IllegalStateException(err) + case ScenarioRunner.SubmissionError(err, _, _) => throw new IllegalStateException(err) } } // All parties known to the ledger. This may include parties that were not // allocated explicitly, e.g. parties created by `partyFromText`. private def getLedgerParties(): Iterable[Ref.Party] = { - scenarioRunner.ledger.ledgerData.nodeInfos.values.flatMap(_.disclosures.keys) + ledger.ledgerData.nodeInfos.values.flatMap(_.disclosures.keys) } override def allocateParty(partyIdHint: String, displayName: String)(implicit @@ -374,7 +323,7 @@ class IdeLedgerClient(val compiledPackages: CompiledPackages) extends ScriptLedg esf: ExecutionSequencerFactory, mat: Materializer, ): Future[Time.Timestamp] = { - Future.successful(scenarioRunner.ledger.currentTime) + Future.successful(ledger.currentTime) } override def setStaticTime(time: Time.Timestamp)(implicit @@ -382,13 +331,22 @@ class IdeLedgerClient(val compiledPackages: CompiledPackages) extends ScriptLedg esf: ExecutionSequencerFactory, mat: Materializer, ): Future[Unit] = { - val diff = time.micros - scenarioRunner.ledger.currentTime.micros + val diff = time.micros - ledger.currentTime.micros // ScenarioLedger only provides pass, so we have to calculate the diff. // Note that ScenarioLedger supports going backwards in time. - scenarioRunner.ledger = scenarioRunner.ledger.passTime(diff) + _ledger = ledger.passTime(diff) Future.unit } - override def tracelogIterator = traceLog.iterator - override def clearTracelog = traceLog.clear + override def tracelogIterator = + _lastSubmission.map(_.traceLog.iterator).getOrElse(Iterator.empty) +} + +object IdeLedgerClient { + // Data cached from the last submission so we can pluck it out the client at the end. + case class SubmissionCache( + traceLog: TraceLog, + ptx: PartialTransaction, + commitLocation: Option[Location], + ) } diff --git a/daml-script/runner/src/main/scala/com/digitalasset/daml/lf/engine/script/ledgerinteraction/JsonLedgerClient.scala b/daml-script/runner/src/main/scala/com/digitalasset/daml/lf/engine/script/ledgerinteraction/JsonLedgerClient.scala index a80da7e7a9ad..0c199c5ca40a 100644 --- a/daml-script/runner/src/main/scala/com/digitalasset/daml/lf/engine/script/ledgerinteraction/JsonLedgerClient.scala +++ b/daml-script/runner/src/main/scala/com/digitalasset/daml/lf/engine/script/ledgerinteraction/JsonLedgerClient.scala @@ -453,7 +453,6 @@ class JsonLedgerClient( } override def tracelogIterator = Iterator.empty - override def clearTracelog = () } object JsonLedgerClient { diff --git a/daml-script/runner/src/main/scala/com/digitalasset/daml/lf/engine/script/ledgerinteraction/ScriptLedgerClient.scala b/daml-script/runner/src/main/scala/com/digitalasset/daml/lf/engine/script/ledgerinteraction/ScriptLedgerClient.scala index 203f1ce322e8..fbc204e56db1 100644 --- a/daml-script/runner/src/main/scala/com/digitalasset/daml/lf/engine/script/ledgerinteraction/ScriptLedgerClient.scala +++ b/daml-script/runner/src/main/scala/com/digitalasset/daml/lf/engine/script/ledgerinteraction/ScriptLedgerClient.scala @@ -128,6 +128,4 @@ trait ScriptLedgerClient { )(implicit ec: ExecutionContext, esf: ExecutionSequencerFactory, mat: Materializer): Future[Unit] def tracelogIterator: Iterator[(String, Option[Location])] - - def clearTracelog: Unit }