Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,7 @@ package free

import data.{ImmArray, Ref}
import speedy.{MachineLogger, Pretty, SError}
import ScriptEngine.{
ExtendedValue,
ExtendedValueClosureBlob,
ExtendedValueComputationMode,
runExtendedValueComputation,
}
import ScriptEngine.{ExtendedValue, ExtendedValueClosureBlob, ExtendedValueComputationMode}
import value.Value._
import scalaz.std.either._
import scalaz.std.vector._
Expand Down Expand Up @@ -116,39 +111,23 @@ private[lf] object Free {
def toErrOr: ErrOr[X] = e.left.map(ConversionError)
}

def getResult(
freeClosure: ExtendedValueClosureBlob, // LF Type: () -> Free ScriptF (a, ())
compiledPackages: CompiledPackages,
machineLogger: MachineLogger,
convertLegacyExceptions: Boolean,
): Result[ExtendedValue, Question, ExtendedValue] =
new Runner(
freeClosure,
compiledPackages: CompiledPackages,
machineLogger: MachineLogger,
convertLegacyExceptions,
).getResult()

def getResultF(
freeClosure: ExtendedValueClosureBlob, // LF Type: () -> Free ScriptF (a, ())
compiledPackages: CompiledPackages,
machineLogger: MachineLogger,
convertLegacyExceptions: Boolean,
cancelled: () => Option[RuntimeException],
)(implicit ec: ExecutionContext): Future[Result[ExtendedValue, Question, ExtendedValue]] =
new Runner(
freeClosure: ExtendedValueClosureBlob,
compiledPackages: CompiledPackages,
machineLogger: MachineLogger,
convertLegacyExceptions,
cancelled,
).getResultF()

private class Runner(
freeClosure: ExtendedValueClosureBlob, // LF Type: () -> Free ScriptF (a, ())
compiledPackages: CompiledPackages,
machineLogger: MachineLogger,
convertLegacyExceptions: Boolean,
cancelled: () => Option[RuntimeException] = () => None,
) {

Expand All @@ -168,17 +147,19 @@ private[lf] object Free {
closure: ExtendedValueClosureBlob,
args: List[ExtendedValue],
): Result.NoQuestion[ExtendedValue] =
runExtendedValueComputation(
computationMode = ExtendedValueComputationMode.ByClosure(closure, args),
cancelled = cancelled,
compiledPackages = compiledPackages,
iterationsBetweenInterruptions = 100000,
logger = machineLogger,
convertLegacyExceptions = convertLegacyExceptions,
).fold(
err => Result.failed(err.fold(identity, free.InterpretationError(_))),
Result.successful(_),
)
ScriptEngine
.runExtendedValueComputation(
computationMode = ExtendedValueComputationMode.ByClosure(closure, args),
cancelled = cancelled,
compiledPackages = compiledPackages,
iterationsBetweenInterruptions = 100000,
logger = machineLogger,
convertLegacyExceptions = false,
)
.fold(
err => Result.failed(err.fold(identity, free.InterpretationError(_))),
Result.successful(_),
)

def parseQuestion(
v: ExtendedValue
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,33 @@ package engine
package script
package v2

import org.apache.pekko.stream.Materializer
import com.daml.grpc.adapter.ExecutionSequencerFactory
import com.digitalasset.canton.logging.NamedLoggerFactory
import com.digitalasset.canton.tracing.TraceContext
import com.digitalasset.daml.lf.data.ImmArray
import com.digitalasset.daml.lf.data.{ImmArray, Ref}
import com.digitalasset.daml.lf.engine.ScriptEngine.{
ExtendedValue,
ExtendedValueClosureBlob,
ExtendedValueComputationMode,
runExtendedValueComputation,
}
import com.digitalasset.daml.lf.engine.free.Free
import com.digitalasset.daml.lf.engine.script.Runner.IdeLedgerContext
import com.digitalasset.daml.lf.engine.script.ledgerinteraction.{
ScriptLedgerClient => UnversionedScriptLedgerClient
}
import com.digitalasset.daml.lf.engine.script.v2.ledgerinteraction.ScriptLedgerClient
import com.digitalasset.daml.lf.language.Ast
import com.digitalasset.daml.lf.interpretation.{Error => IE}
import com.digitalasset.daml.lf.script.IdeLedger
import com.digitalasset.daml.lf.engine.ScriptEngine.{
ExtendedValue,
ExtendedValueClosureBlob,
ExtendedValueComputationMode,
runExtendedValueComputation,
}
import com.digitalasset.daml.lf.speedy.MachineLogger
import com.digitalasset.daml.lf.transaction.{NextGenContractStateMachine => ContractStateMachine}
import com.digitalasset.daml.lf.value.Value._
import com.digitalasset.daml.lf.script.converter.ConverterException
import com.digitalasset.canton.logging.NamedLoggerFactory
import com.digitalasset.daml.lf.speedy.{MachineLogger, SError}
import com.digitalasset.daml.lf.transaction.{NextGenContractStateMachine => ContractStateMachine}
import com.digitalasset.daml.lf.value.Value
import org.apache.pekko.stream.Materializer

import scala.concurrent.{ExecutionContext, Future}
import scala.util.{Failure, Success}

private[lf] class Runner(
unversionedRunner: script.Runner,
Expand Down Expand Up @@ -99,14 +102,14 @@ private[lf] class Runner(
}

// Takes a Script X and runs it
def runResolved(scriptValue: ExtendedValue, convertLegacyExceptions: Boolean = true)(implicit
def runResolved(scriptValue: ExtendedValue, convertLegacyExceptions: Boolean)(implicit
ec: ExecutionContext,
esf: ExecutionSequencerFactory,
mat: Materializer,
): Future[ExtendedValue] =
): Future[ExtendedValue] = handleLegacyExceptions(convertLegacyExceptions)(
for {
freeClosure <- scriptValue match {
case ValueRecord(_, ImmArray((_, freeClosure: ExtendedValueClosureBlob), _)) =>
case Value.ValueRecord(_, ImmArray((_, freeClosure: ExtendedValueClosureBlob), _)) =>
Future.successful(freeClosure)
case a => Future.failed(new RuntimeException(s"Expected Script a but got $a"))
}
Expand All @@ -115,7 +118,6 @@ private[lf] class Runner(
freeClosure,
unversionedRunner.extendedCompiledPackages,
machineLogger,
convertLegacyExceptions,
canceled,
)
result <-
Expand All @@ -125,21 +127,21 @@ private[lf] class Runner(
.recover { case err: RuntimeException => Result.failed(err) }
)
} yield result
)

// Takes something that resolves/computes to a Script X, then runs the script
def run(comp: ExtendedValueComputationMode, convertLegacyExceptions: Boolean = true)(implicit
def run(comp: ExtendedValueComputationMode, convertLegacyExceptions: Boolean)(implicit
ec: ExecutionContext,
esf: ExecutionSequencerFactory,
mat: Materializer,
): Future[ExtendedValue] =
for {
scriptValue <- runComputation(comp, convertLegacyExceptions)
scriptValue <- handleLegacyExceptions(convertLegacyExceptions)(runComputation(comp))
result <- runResolved(scriptValue, convertLegacyExceptions)
} yield result

def runComputation(
comp: ExtendedValueComputationMode,
convertLegacyExceptions: Boolean = true,
comp: ExtendedValueComputationMode
)(implicit ec: ExecutionContext): Future[ExtendedValue] =
Future {
runExtendedValueComputation(
Expand All @@ -148,7 +150,7 @@ private[lf] class Runner(
unversionedRunner.extendedCompiledPackages,
machineLogger,
iterationsBetweenInterruptions = 100000,
convertLegacyExceptions,
convertLegacyExceptions = false,
).fold(
err => throw err.fold(identity, free.InterpretationError(_)),
identity,
Expand All @@ -173,14 +175,67 @@ private[lf] class Runner(
(
unversionedRunner.script match {
case ScriptAction.NoParam(id, _) =>
run(ExtendedValueComputationMode.ByIdentifier(id))
run(ExtendedValueComputationMode.ByIdentifier(id), convertLegacyExceptions = true)
case ScriptAction.Param(id, paramType, Some(param), _) =>
run(ExtendedValueComputationMode.ByIdentifier(id, Some(List(param))))
run(
ExtendedValueComputationMode.ByIdentifier(id, Some(List(param))),
convertLegacyExceptions = true,
)
case _ =>
Future.failed(
new RuntimeException("impossible")
) // This case is caught by script.Runner, when a Param ScriptAction is called without a param
},
ideLedgerContext,
)

def makeFailureStatus(excpType: Ref.TypeConId, msg: String) =
free.InterpretationError(
SError.SErrorDamlException(
IE.FailureStatus(
"UNHANDLED_EXCEPTION/" + excpType.qualifiedName.toString,
Ast.FCInvalidGivenCurrentSystemStateOther.cantonCategoryId,
msg,
Map(),
)
)
)

def handleLegacyExceptions[X](
convertLegacyExceptions: Boolean
)(x: Future[X])(implicit ec: ExecutionContext) =
x.recoverWith {
case free.InterpretationError(
SError.SErrorDamlException(IE.UnhandledException(Ast.TTyCon(excpType), value))
) if convertLegacyExceptions =>
convertLegacyException(excpType, value)
}

def convertLegacyException(excpType: Ref.TypeConId, value: ExtendedValue)(implicit
ec: ExecutionContext
): Future[Nothing] = {
runComputation(
ExtendedValueComputationMode.ByExceptionMessage(excpType, value)
).transform { result =>
val error = result match {
case Success(Value.ValueText(msg)) =>
makeFailureStatus(excpType, msg)
case Success(_) =>
new RuntimeException(s"Message computation for exception $excpType did not give Text")
case Failure(
free.InterpretationError(
SError.SErrorDamlException(
IE.UnhandledException(Ast.TTyCon(messageExceptionName), _)
)
)
) =>
makeFailureStatus(
excpType,
s"<Failed to calculate message as ${messageExceptionName.qualifiedName.toString} was thrown during conversion>",
)
case Failure(error) => error
}
Failure(error)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -131,19 +131,10 @@ object ScriptF {
mat: Materializer,
esf: ExecutionSequencerFactory,
): Future[ExtendedValue] = {
def makeFailureStatus(name: Identifier, msg: String) =
Future.failed(
free.InterpretationError(
SError.SErrorDamlException(
IE.FailureStatus(
"UNHANDLED_EXCEPTION/" + name.qualifiedName.toString,
Ast.FCInvalidGivenCurrentSystemStateOther.cantonCategoryId,
msg,
Map(),
)
)
)
)

def raiseFailureStatus(excpType: TypeConId, msg: String): Future[Nothing] =
Future.failed(runner.makeFailureStatus(excpType, msg))

def userManagementDef(name: String) =
env.scriptIds.damlScriptModule("Daml.Script.Internal.Questions.UserManagement", name)
val invalidUserId = userManagementDef("InvalidUserId")
Expand All @@ -159,7 +150,7 @@ object ScriptF {
),
true,
) =>
makeFailureStatus(invalidUserId, msg)
raiseFailureStatus(invalidUserId, msg)
case (
ExtendedValueAny(
_,
Expand All @@ -170,7 +161,7 @@ object ScriptF {
),
true,
) =>
makeFailureStatus(userAlreadyExists, "User already exists: " + userId)
raiseFailureStatus(userAlreadyExists, "User already exists: " + userId)
case (
ExtendedValueAny(
_,
Expand All @@ -181,35 +172,11 @@ object ScriptF {
),
true,
) =>
makeFailureStatus(userNotFound, "User not found: " + userId)
case (ExtendedValueAny(Ast.TTyCon(name), v), true) =>
raiseFailureStatus(userNotFound, "User not found: " + userId)
case (ExtendedValueAny(Ast.TTyCon(excpType), value), true) =>
// Since we cannot call `SBThrow` from the engine, we must re-implement the legacy exception to FailureStatus conversion logic here
// This involves calculating the exception message by calling the engine again.
runner
.runComputation(
ExtendedValueComputationMode
.ByExceptionMessage(name, v),
false,
)
.transformWith {
case Success(ValueText(message)) => makeFailureStatus(name, message)
case Success(_) =>
Future.failed(
new RuntimeException(s"Message computation for exception $name did not give Text")
)
case Failure(
free.InterpretationError(
SError.SErrorDamlException(
IE.UnhandledException(Ast.TTyCon(messageExceptionName), _)
)
)
) =>
makeFailureStatus(
name,
s"<Failed to calculate message as ${messageExceptionName.qualifiedName.toString} was thrown during conversion>",
)
case Failure(e) => Future.failed(e)
}
runner.convertLegacyException(excpType, value)
case (ExtendedValueAny(ty, _), true) =>
Future.failed(
new RuntimeException(
Expand Down
Loading