mirror of
https://github.com/digital-asset/daml.git
synced 2024-09-20 01:07:18 +03:00
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
This commit is contained in:
parent
3113702dc0
commit
febca5d62d
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
@ -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(())
|
||||
|
@ -56,7 +56,6 @@ da_scala_library(
|
||||
"//ledger/ledger-api-client",
|
||||
"//ledger/ledger-api-common",
|
||||
"//libs-scala/auth-utils",
|
||||
"//libs-scala/scala-utils",
|
||||
],
|
||||
)
|
||||
|
||||
|
@ -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 =
|
||||
|
@ -324,5 +324,4 @@ class GrpcLedgerClient(val grpcClient: LedgerClient, val applicationId: Applicat
|
||||
}
|
||||
|
||||
override def tracelogIterator = Iterator.empty
|
||||
override def clearTracelog = ()
|
||||
}
|
||||
|
@ -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 var seed = crypto.Hash.hashPrivateKey(s"script-service")
|
||||
|
||||
private var _lastSubmission: Option[SubmissionCache] = None
|
||||
|
||||
private[this] def emptyPtx: PartialTransaction = PartialTransaction.initial(
|
||||
pkg2TxVersion,
|
||||
contractKeyUniqueness,
|
||||
ledger.currentTime,
|
||||
InitialSeeding.TransactionSeed(seed),
|
||||
)
|
||||
|
||||
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,
|
||||
)
|
||||
)
|
||||
|
||||
private[this] val preprocessor = new engine.preprocessing.CommandPreprocessor(compiledPackages)
|
||||
|
||||
private val seed = crypto.Hash.hashPrivateKey(s"script-service")
|
||||
|
||||
private val txSeeding =
|
||||
speedy.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,
|
||||
)
|
||||
val onLedger = machine.ledgerMode match {
|
||||
case OffLedger => throw SRequiresOnLedger("ScenarioRunner")
|
||||
case onLedger: OnLedger => onLedger
|
||||
}
|
||||
val scenarioRunner = ScenarioRunner(machine, seed)
|
||||
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],
|
||||
)
|
||||
}
|
||||
|
@ -453,7 +453,6 @@ class JsonLedgerClient(
|
||||
}
|
||||
|
||||
override def tracelogIterator = Iterator.empty
|
||||
override def clearTracelog = ()
|
||||
}
|
||||
|
||||
object JsonLedgerClient {
|
||||
|
@ -128,6 +128,4 @@ trait ScriptLedgerClient {
|
||||
)(implicit ec: ExecutionContext, esf: ExecutionSequencerFactory, mat: Materializer): Future[Unit]
|
||||
|
||||
def tracelogIterator: Iterator[(String, Option[Location])]
|
||||
|
||||
def clearTracelog: Unit
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user