LF: control of input value version in the engine (#6828)

CHANGELOG_BEGIN
CHANGELOG_END
This commit is contained in:
Remy 2020-08-13 21:15:52 +02:00 committed by GitHub
parent a890618782
commit 66ae38da6c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 313 additions and 160 deletions

View File

@ -20,7 +20,6 @@ import com.daml.lf.speedy.Speedy
import com.daml.lf.speedy.SExpr
import com.daml.lf.speedy.SValue
import com.daml.lf.speedy.SExpr.{LfDefRef, SDefinitionRef}
import com.daml.lf.transaction.TransactionVersions
import com.daml.lf.validation.Validation
import com.google.protobuf.ByteString
@ -164,7 +163,8 @@ class Context(val contextId: Context.ContextId, languageVersion: LanguageVersion
compiledPackages,
txSeeding,
defn,
TransactionVersions.SupportedDevOutputVersions,
value.ValueVersions.DevOutputVersions,
transaction.TransactionVersions.DevOutputVersions,
)
}

View File

@ -155,6 +155,11 @@ final class Conversions(
sys.error(
s"Got unexpected DamlEWronglyTypedContract error in scenario service: $wtc. Note that in the scenario service this error should never surface since contract fetches are all type checked.",
)
case divv: SError.DamlEDisallowedInputValueVersion =>
sys.error(
s"Got unexpected DamlEDisallowedInputVersion error in scenario service: $divv. Note that in the scenario service this error should never surface since its accept all stable versions.",
)
}
builder.build
}

View File

@ -293,7 +293,8 @@ class Engine(private[lf] val config: EngineConfig = EngineConfig.Stable) {
expr = SExpr.SEApp(sexpr, Array(SExpr.SEValue.Token)),
globalCids = globalCids,
committers = submitters,
outputTransactionVersions = config.outputTransactionVersions,
inputValueVersions = config.allowedInputValueVersions,
outputTransactionVersions = config.allowedOutputTransactionVersions,
validating = validating,
)
interpretLoop(machine, ledgerTime)
@ -428,13 +429,13 @@ class Engine(private[lf] val config: EngineConfig = EngineConfig.Stable) {
}
private[engine] def addPackage(pkgId: PackageId, pkg: Package): Result[Unit] =
if (config.languageVersions.contains(pkg.languageVersion))
if (config.allowedLanguageVersions.contains(pkg.languageVersion))
compiledPackages.addPackage(pkgId, pkg)
else
ResultError(
Error(
s"Disallowed language version in package $pkgId: " +
s"Expected version between ${config.languageVersions.min} and ${config.languageVersions.max} but got ${pkg.languageVersion}"
s"Expected version between ${config.allowedLanguageVersions.min} and ${config.allowedLanguageVersions.max} but got ${pkg.languageVersion}"
)
)

View File

@ -11,45 +11,55 @@ import com.daml.lf.transaction.{TransactionVersions, TransactionVersion => TV}
// Currently only outputTransactionVersions is used.
// languageVersions and outputTransactionVersions should be plug
final case class EngineConfig(
// constrains the version of language accepted by the engine
languageVersions: VersionRange[LV],
// constrains the version of output transactions
inputTransactionVersions: VersionRange[TV],
// constrains the version of output transactions
outputTransactionVersions: VersionRange[TV],
)
// constrains the versions of language accepted by the engine
allowedLanguageVersions: VersionRange[LV],
// constrains the versions of input transactions
allowedInputTransactionVersions: VersionRange[TV],
// constrains the versions of output transactions
allowedOutputTransactionVersions: VersionRange[TV],
) {
private[lf] val allowedInputValueVersions =
VersionRange(
TransactionVersions.assignValueVersion(allowedInputTransactionVersions.min),
TransactionVersions.assignValueVersion(allowedInputTransactionVersions.max),
)
private[lf] val allowedOutputValueVersions =
VersionRange(
TransactionVersions.assignValueVersion(allowedOutputTransactionVersions.min),
TransactionVersions.assignValueVersion(allowedOutputTransactionVersions.max),
)
}
object EngineConfig {
// development configuration, should not be used in PROD.
// accept all language and transaction versions supported by SDK_1_x plus development versions.
// Development configuration, should not be used in PROD.
val Dev: EngineConfig = new EngineConfig(
languageVersions = VersionRange(
allowedLanguageVersions = VersionRange(
LV(LV.Major.V1, LV.Minor.Stable("6")),
LV(LV.Major.V1, LV.Minor.Dev),
),
inputTransactionVersions = VersionRange(
allowedInputTransactionVersions = VersionRange(
TV("10"),
TransactionVersions.acceptedVersions.last
),
outputTransactionVersions = VersionRange(
TV("10"),
TransactionVersions.acceptedVersions.last
)
allowedOutputTransactionVersions = TransactionVersions.DevOutputVersions
)
// Legacy configuration, to be used by sandbox classic only
@deprecated("Sandbox_Classic is to be used by sandbox classic only", since = "1.4.0")
val Sandbox_Classic: EngineConfig = new EngineConfig(
languageVersions = VersionRange(
LV(LV.Major.V1, LV.Minor.Stable("1")),
allowedLanguageVersions = VersionRange(
LV(LV.Major.V1, LV.Minor.Stable("0")),
LV(LV.Major.V1, LV.Minor.Dev),
),
inputTransactionVersions = VersionRange(
allowedInputTransactionVersions = VersionRange(
TransactionVersions.acceptedVersions.head,
TransactionVersions.acceptedVersions.last
),
outputTransactionVersions = VersionRange(
allowedOutputTransactionVersions = VersionRange(
TV("10"),
TransactionVersions.acceptedVersions.last
)
@ -57,12 +67,12 @@ object EngineConfig {
// recommended configuration
val Stable: EngineConfig = new EngineConfig(
languageVersions = VersionRange(
allowedLanguageVersions = VersionRange(
LV(LV.Major.V1, LV.Minor.Stable("6")),
LV(LV.Major.V1, LV.Minor.Stable("8")),
),
inputTransactionVersions = VersionRange(TV("10"), TV("10")),
outputTransactionVersions = VersionRange(TV("10"), TV("10"))
allowedInputTransactionVersions = VersionRange(TV("10"), TV("10")),
allowedOutputTransactionVersions = VersionRange(TV("10"), TV("10"))
)
}

View File

@ -1,53 +1,85 @@
// Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.lf.engine
import com.daml.lf.language.{LanguageVersion => LV}
import com.daml.lf.transaction.TransactionVersions
import com.daml.lf.value.ValueVersions
package com.daml.lf
package engine
class EngineInfo(config: EngineConfig) {
override lazy val toString: String = show
import language.{LanguageVersion => LV}
lazy val show: String =
s"DAML LF Engine supports LF versions: $formatLfVersions; Input Transaction versions: $formatInputTransactionVersions; Input Value versions: $formatInputValueVersions; Output Transaction versions: $formatOutputTransactionVersions; Output Value versions: $formatOutputValueVersions"
override def toString: String = show
def show: String = pretty.mkString(System.lineSeparator())
private[this] def formatInputTransactionVersions: String =
format(TransactionVersions.acceptedVersions.map(_.protoValue))
lazy val pretty: Iterable[String] = {
private[this] def formatOutputTransactionVersions: String =
format(
TransactionVersions.acceptedVersions
.collect {
case v if config.outputTransactionVersions.contains(v) =>
v.protoValue
}
val allLangVersions =
for {
major <- LV.Major.All
minor <- major.supportedMinorVersions
} yield LV(major, minor)
val allTransactionVersions =
transaction.TransactionVersions.acceptedVersions
val allValueVersions =
value.ValueVersions.acceptedVersions
val allOutputTransactionVersions =
allTransactionVersions.filter(transaction.TransactionVersions.DevOutputVersions.contains)
val allOutputValueVersions =
allOutputTransactionVersions.map(transaction.TransactionVersions.assignValueVersion)
val allowedLangVersions =
allLangVersions.filter(config.allowedLanguageVersions.contains)
val allowedInputTransactionVersions =
allTransactionVersions.filter(config.allowedInputTransactionVersions.contains)
val allowedInputValueVersions =
allValueVersions.filter(config.allowedInputValueVersions.contains)
val allowedOutputTransactionVersions =
allTransactionVersions.filter(config.allowedOutputTransactionVersions.contains)
val allowedOutputValueVersions =
allValueVersions.filter(config.allowedOutputValueVersions.contains)
List(
List(
formatLangVersions(allLangVersions),
formatTxVersions("input", allTransactionVersions),
formatValVersions("input", allValueVersions),
formatTxVersions("output", allOutputTransactionVersions),
formatValVersions("output", allOutputValueVersions)
).mkString("DAML LF Engine supports ", "; ", "."),
List(
formatLangVersions(allowedLangVersions),
formatTxVersions("input", allowedInputTransactionVersions),
formatValVersions("input", allowedInputValueVersions),
formatTxVersions("output", allowedOutputTransactionVersions),
formatValVersions("output", allowedOutputValueVersions)
).mkString("DAML LF Engine config allows ", "; ", ".")
)
private[this] def formatInputValueVersions: String =
format(ValueVersions.acceptedVersions.map(_.protoValue))
private[this] def formatOutputValueVersions: String = {
val outputValueVersions =
config.outputTransactionVersions.map(TransactionVersions.assignValueVersion)
format(ValueVersions.acceptedVersions.filter(outputValueVersions.contains).map(_.protoValue))
}
private def formatLfVersions: String = {
val allVersions: Iterable[String] =
LV.Major.All flatMap (mv => lfVersions(mv.pretty, mv.supportedMinorVersions))
format(allVersions)
}
private def lfVersions(
majorVersion: String,
minorVersions: Iterable[LV.Minor]): Iterable[String] =
minorVersions.map { a =>
val ap = a.toProtoIdentifier
s"$majorVersion${if (ap.isEmpty) "" else s".$ap"}"
private[this] def formatLangVersions(versions: Iterable[LV]) = {
val prettyVersions = versions.map {
case LV(major, minor) =>
val ap = minor.toProtoIdentifier
s"${major.pretty}${if (ap.isEmpty) "" else s".$ap"}"
}
s"LF versions: ${prettyVersions.mkString(", ")}"
}
private[this] def formatTxVersions(
prefix: String,
versions: List[transaction.TransactionVersion],
) =
s"$prefix transaction versions: ${versions.map(_.protoValue).mkString(", ")}"
private[this] def formatValVersions(prefix: String, versions: List[value.ValueVersion]) =
s"$prefix value versions: ${versions.map(_.protoValue).mkString(", ")}"
private def format(as: Iterable[String]): String = as.mkString(", ")
}

View File

@ -18,14 +18,22 @@ class EngineInfoTest extends WordSpec with Matchers {
"show supported LF, Transaction and Value versions" in {
engineInfoStable.show shouldBe
"DAML LF Engine supports LF versions: 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.dev; Input Transaction versions: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11; Input Value versions: 1, 2, 3, 4, 5, 6, 7; Output Transaction versions: 10; Output Value versions: 6"
infos.foreach(
_.pretty.toSeq(0) shouldBe
"DAML LF Engine supports LF versions: 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.dev; input transaction versions: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11; input value versions: 1, 2, 3, 4, 5, 6, 7; output transaction versions: 10, 11; output value versions: 6, 7."
)
}
engineInfoDev.show shouldBe
"DAML LF Engine supports LF versions: 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.dev; Input Transaction versions: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11; Input Value versions: 1, 2, 3, 4, 5, 6, 7; Output Transaction versions: 10, 11; Output Value versions: 6, 7"
"show allowed LF, Transaction and Value versions" in {
engineInfoLegacy.show shouldBe
"DAML LF Engine supports LF versions: 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.dev; Input Transaction versions: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11; Input Value versions: 1, 2, 3, 4, 5, 6, 7; Output Transaction versions: 10, 11; Output Value versions: 6, 7"
engineInfoStable.pretty.toSeq(1) shouldBe
"DAML LF Engine config allows LF versions: 1.6, 1.7, 1.8; input transaction versions: 10; input value versions: 6; output transaction versions: 10; output value versions: 6."
engineInfoDev.pretty.toSeq(1) shouldBe
"DAML LF Engine config allows LF versions: 1.6, 1.7, 1.8, 1.dev; input transaction versions: 10, 11; input value versions: 6, 7; output transaction versions: 10, 11; output value versions: 6, 7."
engineInfoLegacy.pretty.toSeq(1) shouldBe
"DAML LF Engine config allows LF versions: 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.dev; input transaction versions: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11; input value versions: 1, 2, 3, 4, 5, 6, 7; output transaction versions: 10, 11; output value versions: 6, 7."
}

View File

@ -23,7 +23,7 @@ import com.daml.lf.transaction.{
Transaction => Tx,
TransactionVersions => TxVersions
}
import com.daml.lf.value.Value
import com.daml.lf.value.{Value, ValueVersion}
import Value._
import com.daml.lf.speedy.{InitialSeeding, SValue, svalue}
import com.daml.lf.speedy.SValue._
@ -1628,6 +1628,48 @@ class EngineTest
}
}
"Engine#submit" should {
val cidV6 = toContractId("#cidV6")
val cidV7 = toContractId("#cidV7")
val contract = ValueRecord(
Some(Identifier(basicTestsPkgId, "BasicTests:Simple")),
ImmArray((Some[Name]("p"), ValueParty(party)))
)
val hello = Identifier(basicTestsPkgId, "BasicTests:Hello")
val templateId = TypeConName(basicTestsPkgId, "BasicTests:Simple")
val now = Time.Timestamp.now()
val submissionSeed = crypto.Hash.hashPrivateKey("engine check the version of input value")
def contracts = Map(
cidV6 -> ContractInst(templateId, VersionedValue(ValueVersion("6"), contract), ""),
cidV7 -> ContractInst(templateId, VersionedValue(ValueVersion("7"), contract), ""),
)
def run(cid: ContractId) = {
val engine = new Engine(EngineConfig.Stable)
val cmds = Commands(
submitter = party,
commands = ImmArray(
ExerciseCommand(templateId, cid, "Hello", ValueRecord(Some(hello), ImmArray.empty))),
ledgerEffectiveTime = now,
commandsReference = "",
)
engine
.submit(cmds, participant, submissionSeed)
.consume(contracts.get, lookupPackage, lookupKey)
}
"succeed if fed with allowed value version" in {
run(cidV6) shouldBe 'right
}
"fail if fed with disallowed value version" in {
val result = run(cidV7)
result shouldBe 'left
result.left.get.msg should include("Update failed due to disallowed value version")
}
}
"Engine.addPackage" should {
import com.daml.lf.language.{LanguageVersion => LV}
@ -1635,7 +1677,7 @@ class EngineTest
def engine(min: LV.Minor, max: LV.Minor) =
new Engine(
EngineConfig.Dev.copy(
languageVersions = VersionRange(LV(LV.Major.V1, min), LV(LV.Major.V1, max))
allowedLanguageVersions = VersionRange(LV(LV.Major.V1, min), LV(LV.Major.V1, max))
)
)

View File

@ -345,7 +345,7 @@ private[lf] object Anf {
}
case x: SEAbs => throw CompilationError(s"flatten: unexpected: $x")
case x: SEWronglyTypeContractId => throw CompilationError(s"flatten: unexpected: $x")
case x: SEDamlException => throw CompilationError(s"flatten: unexpected: $x")
case x: SEAppAtomicFun => throw CompilationError(s"flatten: unexpected: $x")
case x: SEAppAtomicGeneral => throw CompilationError(s"flatten: unexpected: $x")
case x: SEAppAtomicSaturatedBuiltin => throw CompilationError(s"flatten: unexpected: $x")

View File

@ -1126,8 +1126,8 @@ private[lf] final case class Compiler(
case SELabelClosure(label, expr) =>
SELabelClosure(label, closureConvert(remaps, expr))
case x: SEWronglyTypeContractId =>
throw CompilationError(s"unexpected SEWronglyTypeContractId: $x")
case x: SEDamlException =>
throw CompilationError(s"unexpected SEDamlException: $x")
case x: SEImportValue =>
throw CompilationError(s"unexpected SEImportValue: $x")
@ -1200,8 +1200,8 @@ private[lf] final case class Compiler(
go(body, bound, go(handler, bound, go(fin, bound, free)))
case SELabelClosure(_, expr) =>
go(expr, bound, free)
case x: SEWronglyTypeContractId =>
throw CompilationError(s"unexpected SEWronglyTypeContractId: $x")
case x: SEDamlException =>
throw CompilationError(s"unexpected SEDamlException: $x")
case x: SEImportValue =>
throw CompilationError(s"unexpected SEImportValue: $x")
@ -1302,8 +1302,8 @@ private[lf] final case class Compiler(
go(body)
case SELabelClosure(_, expr) =>
go(expr)
case x: SEWronglyTypeContractId =>
throw CompilationError(s"unexpected SEWronglyTypeContractId: $x")
case x: SEDamlException =>
throw CompilationError(s"unexpected SEDamlException: $x")
case x: SEImportValue =>
throw CompilationError(s"unexpected SEImportValue: $x")
}

View File

@ -8,6 +8,7 @@ import org.typelevel.paiges.Doc._
import com.daml.lf.ledger.EventId
import com.daml.lf.value.Value
import Value.{NodeId => _, _}
import com.daml.lf.VersionRange
import com.daml.lf.transaction.Node._
import com.daml.lf.ledger._
import com.daml.lf.data.Ref._
@ -72,6 +73,12 @@ private[lf] object Pretty {
text("Expected contract of type") & prettyTypeConName(expected) & text("but got") & prettyTypeConName(
actual,
)
case DamlEDisallowedInputValueVersion(VersionRange(expectedMin, expectedMax), actual) =>
text("Update failed due to disallowed value version") /
text("Expected value version between") & text(expectedMin.protoValue) &
text("and") & text(expectedMax.protoValue) & text("but got") &
text(actual.protoValue)
}
// A minimal pretty-print of an update transaction node, without recursing into child nodes..
@ -578,7 +585,7 @@ private[lf] object Pretty {
case x: SEBuiltinRecursiveDefinition => str(x)
case x: SEImportValue => str(x)
case x: SELabelClosure => str(x)
case x: SEWronglyTypeContractId => str(x)
case x: SEDamlException => str(x)
}
}

View File

@ -1019,14 +1019,20 @@ private[lf] object SBuiltin {
templateId,
machine.committers,
cbMissing = _ => machine.tryHandleException(),
cbPresent = { coinst =>
// Note that we cannot throw in this continuation -- instead
// set the control appropriately which will crash the machine
// correctly later.
if (coinst.template != templateId)
machine.ctrl = SEWronglyTypeContractId(coid, templateId, coinst.template)
else
machine.ctrl = SEImportValue(coinst.arg.value)
cbPresent = {
case V.ContractInst(actualTmplId, V.VersionedValue(version, arg), _) =>
// Note that we cannot throw in this continuation -- instead
// set the control appropriately which will crash the machine
// correctly later.
machine.ctrl =
if (actualTmplId != templateId)
SEDamlException(DamlEWronglyTypedContract(coid, templateId, actualTmplId))
else if (!machine.inputValueVersions.contains(version))
SEDamlException(
DamlEDisallowedInputValueVersion(machine.inputValueVersions, version),
)
else
SEImportValue(arg)
},
),
)

View File

@ -3,11 +3,12 @@
package com.daml.lf.speedy
import com.daml.lf.VersionRange
import com.daml.lf.data.Ref._
import com.daml.lf.data.Time
import com.daml.lf.ledger.EventId
import com.daml.lf.transaction.{GlobalKey, NodeId, Transaction => Tx}
import com.daml.lf.value.Value
import com.daml.lf.value.{Value, ValueVersion}
import com.daml.lf.scenario.ScenarioLedger
import com.daml.lf.value.Value.ContractId
@ -88,6 +89,14 @@ object SError {
actual: TypeConName,
) extends SErrorDamlException
/** We tried to fetch data with disallowed value version --
* see <https://github.com/digital-asset/daml/issues/5164>
*/
final case class DamlEDisallowedInputValueVersion(
allowed: VersionRange[ValueVersion],
actual: ValueVersion,
) extends SErrorDamlException
/** A fetch or exercise was being made against a contract that has not
* been disclosed to 'committer'. */
final case class ScenarioErrorContractNotVisible(

View File

@ -387,16 +387,12 @@ object SExpr {
}
}
/** When we fetch a contract id from upstream we cannot crash in the upstream
* calls. Rather, we set the control to this expression and then crash when executing.
/** We cannot crash in the engine call back.
* Rather, we set the control to this expression and then crash when executing.
*/
final case class SEWronglyTypeContractId(
acoid: V.ContractId,
expected: TypeConName,
actual: TypeConName,
) extends SExpr {
final case class SEDamlException(error: SErrorDamlException) extends SExpr {
def execute(machine: Machine): Unit = {
throw DamlEWronglyTypedContract(acoid, expected, actual)
throw error
}
}

View File

@ -15,7 +15,7 @@ import com.daml.lf.speedy.SExpr._
import com.daml.lf.speedy.SResult._
import com.daml.lf.speedy.SValue._
import com.daml.lf.transaction.{TransactionVersion, TransactionVersions}
import com.daml.lf.value.{Value => V}
import com.daml.lf.value.{ValueVersion, ValueVersions, Value => V}
import org.slf4j.LoggerFactory
import scala.collection.JavaConverters._
@ -98,8 +98,10 @@ private[lf] object Speedy {
/** The speedy CEK machine. */
final class Machine(
/* Transaction versions that the machine can read */
val inputValueVersions: VersionRange[ValueVersion],
/* Transaction versions that the machine can output */
val outputTransactionVersions: VersionRange[transaction.TransactionVersion],
val outputTransactionVersions: VersionRange[TransactionVersion],
/* Whether the current submission is validating the transaction, or interpreting
* it. If this is false, the committers must be a singleton set.
*/
@ -550,7 +552,6 @@ private[lf] object Speedy {
// to speedy value and set the control of with the result.
// All the contract IDs contained in the value are considered global.
// Raises an exception if missing a package.
private[speedy] def importValue(value: V[V.ContractId]): Unit = {
def go(value0: V[V.ContractId]): SValue =
value0 match {
@ -658,10 +659,12 @@ private[lf] object Speedy {
expr: SExpr,
globalCids: Set[V.ContractId],
committers: Set[Party],
outputTransactionVersions: VersionRange[transaction.TransactionVersion],
inputValueVersions: VersionRange[ValueVersion],
outputTransactionVersions: VersionRange[TransactionVersion],
validating: Boolean = false,
): Machine =
new Machine(
inputValueVersions = inputValueVersions,
outputTransactionVersions = outputTransactionVersions,
validating = validating,
ctrl = expr,
@ -693,6 +696,7 @@ private[lf] object Speedy {
compiledPackages: CompiledPackages,
transactionSeed: crypto.Hash,
scenario: SExpr,
inputValueVersions: VersionRange[ValueVersion],
outputTransactionVersions: VersionRange[TransactionVersion],
): Machine = Machine(
compiledPackages = compiledPackages,
@ -701,6 +705,7 @@ private[lf] object Speedy {
expr = SEApp(scenario, Array(SEValue.Token)),
globalCids = Set.empty,
committers = Set.empty,
inputValueVersions: VersionRange[ValueVersion],
outputTransactionVersions = outputTransactionVersions,
)
@ -711,12 +716,14 @@ private[lf] object Speedy {
compiledPackages: CompiledPackages,
transactionSeed: crypto.Hash,
scenario: Expr,
inputValueVersions: VersionRange[ValueVersion],
outputTransactionVersions: VersionRange[TransactionVersion],
): Machine =
fromScenarioSExpr(
compiledPackages = compiledPackages,
transactionSeed = transactionSeed,
scenario = compiledPackages.compiler.unsafeCompile(scenario),
inputValueVersions = inputValueVersions,
outputTransactionVersions = outputTransactionVersions,
)
@ -734,7 +741,8 @@ private[lf] object Speedy {
expr = expr,
globalCids = Set.empty,
committers = Set.empty,
outputTransactionVersions = TransactionVersions.SupportedDevOutputVersions,
inputValueVersions = ValueVersions.Empty,
outputTransactionVersions = TransactionVersions.Empty,
)
@throws[PackageNotFound]

View File

@ -22,7 +22,6 @@ import java.io.{File, PrintWriter, StringWriter}
import java.nio.file.{Path, Paths}
import java.io.PrintStream
import com.daml.lf.transaction.TransactionVersions
import org.jline.builtins.Completers
import org.jline.reader.{History, LineReader, LineReaderBuilder}
import org.jline.reader.impl.completer.{AggregateCompleter, ArgumentCompleter, StringsCompleter}
@ -185,11 +184,17 @@ object Repl {
private val seed = nextSeed()
val txVersions =
val (inputValueVersion, outputTransactionVersions) =
if (devMode)
TransactionVersions.SupportedDevOutputVersions
(
value.ValueVersions.DevOutputVersions,
transaction.TransactionVersions.DevOutputVersions
)
else
TransactionVersions.SupportedStableOutputVersions
(
value.ValueVersions.StableOutputVersions,
transaction.TransactionVersions.StableOutputVersions,
)
def run(expr: Expr): (
Speedy.Machine,
@ -199,7 +204,8 @@ object Repl {
compiledPackages,
seed,
expr,
txVersions,
inputValueVersion,
outputTransactionVersions,
)
(machine, ScenarioRunner(machine).run())
}

View File

@ -277,7 +277,8 @@ object ScenarioRunner {
engine.compiledPackages(),
transactionSeed,
scenarioExpr,
engine.config.outputTransactionVersions,
engine.config.allowedInputValueVersions,
engine.config.allowedOutputTransactionVersions,
)
ScenarioRunner(speedyMachine).run() match {
case Left(e) =>

View File

@ -18,7 +18,6 @@ import com.daml.lf.speedy.Speedy.Machine
import java.io.File
import java.util.concurrent.TimeUnit
import com.daml.lf.transaction.TransactionVersions
import org.openjdk.jmh.annotations._
class CollectAuthority {
@ -57,7 +56,8 @@ class CollectAuthorityState {
compiledPackages,
seeding(),
expr,
TransactionVersions.SupportedDevOutputVersions,
value.ValueVersions.DevOutputVersions,
transaction.TransactionVersions.DevOutputVersions,
)
the_sexpr = machine.ctrl

View File

@ -1,14 +1,13 @@
// Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.lf
package com.daml
package lf
package speedy
import com.daml.lf.data.Ref
import com.daml.lf.language.Ast
import com.daml.lf.language.Ast.ScenarioGetParty
import com.daml.lf.transaction.TransactionVersions
import org.scalatest._
import org.scalatest.{AsyncWordSpec, Matchers}
import org.scalatest.concurrent.ScalaFutures
class ScenarioRunnerTest extends AsyncWordSpec with Matchers with ScalaFutures {
@ -16,13 +15,14 @@ class ScenarioRunnerTest extends AsyncWordSpec with Matchers with ScalaFutures {
"ScenarioRunner" can {
"mangle party names correctly" in {
val compiledPackages = PureCompiledPackages(Map.empty).right.get
val e = Ast.EScenario(ScenarioGetParty(Ast.EPrimLit(Ast.PLText(("foo-bar")))))
val e = Ast.EScenario(Ast.ScenarioGetParty(Ast.EPrimLit(Ast.PLText("foo-bar"))))
val txSeed = crypto.Hash.hashPrivateKey("ScenarioRunnerTest")
val m = Speedy.Machine.fromScenarioExpr(
compiledPackages,
txSeed,
e,
TransactionVersions.SupportedDevOutputVersions,
lf.value.ValueVersions.DevOutputVersions,
transaction.TransactionVersions.DevOutputVersions,
)
val sr = ScenarioRunner(m, _ + "-XXX")
sr.run() match {

View File

@ -62,9 +62,7 @@ final class TransactionBuilder(pkgTxVersion: Ref.PackageId => TransactionVersion
.toSeq
val txVersion =
VersionTimeline
.latestWhenAllPresent(
TransactionVersions.SupportedStableOutputVersions.min,
nodesVersions: _*)
.latestWhenAllPresent(TransactionVersions.StableOutputVersions.min, nodesVersions: _*)
VersionedTransaction(txVersion, GenTransaction(nodes.result(), roots.result()))
}
@ -80,7 +78,7 @@ final class TransactionBuilder(pkgTxVersion: Ref.PackageId => TransactionVersion
private[this] def pkgValVersion(pkgId: Ref.PackageId) = {
import VersionTimeline.Implicits._
VersionTimeline.latestWhenAllPresent(
ValueVersions.SupportedStableVersions.min,
ValueVersions.StableOutputVersions.min,
pkgTxVersion(pkgId))
}
@ -124,7 +122,7 @@ object TransactionBuilder {
private val KeyWithMaintainers = transaction.Node.KeyWithMaintainers
def apply(): TransactionBuilder =
TransactionBuilder(TransactionVersions.SupportedStableOutputVersions.min)
TransactionBuilder(TransactionVersions.StableOutputVersions.min)
def apply(txVersion: TransactionVersion): TransactionBuilder =
new TransactionBuilder(_ => txVersion)
@ -133,7 +131,7 @@ object TransactionBuilder {
def pkgTxVersion(pkgId: Ref.PackageId) = {
import VersionTimeline.Implicits._
VersionTimeline.latestWhenAllPresent(
TransactionVersions.SupportedStableOutputVersions.min,
TransactionVersions.StableOutputVersions.min,
pkgLangVersion(pkgId)
)
}
@ -240,7 +238,7 @@ object TransactionBuilder {
// not valid transactions.
val Empty: Tx.Transaction =
VersionedTransaction(
TransactionVersions.SupportedStableOutputVersions.min,
TransactionVersions.StableOutputVersions.min,
GenTransaction(HashMap.empty, ImmArray.empty),
)
val EmptySubmitted: SubmittedTransaction = SubmittedTransaction(Empty)

View File

@ -34,16 +34,18 @@ private[lf] object TransactionVersions
private[transaction] val minContractKeyInFetch = TransactionVersion("10")
// Older versions are deprecated https://github.com/digital-asset/daml/issues/5220
// We force output of recent version, but keep reading older version as long as
// Sandbox is alive.
val SupportedStableOutputVersions =
val StableOutputVersions: VersionRange[TransactionVersion] =
VersionRange(TransactionVersion("10"), TransactionVersion("10"))
val SupportedDevOutputVersions = SupportedStableOutputVersions.copy(max = acceptedVersions.last)
val DevOutputVersions: VersionRange[TransactionVersion] =
StableOutputVersions.copy(max = acceptedVersions.last)
val Empty: VersionRange[TransactionVersion] =
VersionRange(acceptedVersions.last, acceptedVersions.head)
def assignVersion(
a: GenTransaction.WithTxValue[_, Value.ContractId],
supportedVersions: VersionRange[TransactionVersion] = SupportedDevOutputVersions,
supportedVersions: VersionRange[TransactionVersion] = DevOutputVersions,
): Either[String, TransactionVersion] = {
require(a != null)
import VersionTimeline.Implicits._
@ -118,7 +120,7 @@ private[lf] object TransactionVersions
def asVersionedTransaction(
tx: GenTransaction.WithTxValue[NodeId, Value.ContractId],
supportedVersions: VersionRange[TransactionVersion] = SupportedDevOutputVersions,
supportedVersions: VersionRange[TransactionVersion] = DevOutputVersions,
): Either[String, Transaction.Transaction] =
for {
v <- assignVersion(tx, supportedVersions)
@ -127,13 +129,13 @@ private[lf] object TransactionVersions
@throws[IllegalArgumentException]
def assertAsVersionedTransaction(
tx: GenTransaction.WithTxValue[NodeId, Value.ContractId],
supportedVersions: VersionRange[TransactionVersion] = SupportedDevOutputVersions,
supportedVersions: VersionRange[TransactionVersion] = DevOutputVersions,
): Transaction.Transaction =
data.assertRight(asVersionedTransaction(tx, supportedVersions))
private[lf] def assignValueVersion(transactionVersion: TransactionVersion): ValueVersion =
latestWhenAllPresent(
ValueVersions.SupportedStableVersions.min,
ValueVersions.acceptedVersions.head,
transactionVersion,
)
@ -145,7 +147,7 @@ private[lf] object TransactionVersions
val transactionVersion =
VersionTimeline.latestWhenAllPresent(
supportedTxVersions.min,
(SupportedStableOutputVersions.min: SpecifiedVersion) +: as: _*,
(DevOutputVersions.min: SpecifiedVersion) +: as: _*,
)
Either.cond(

View File

@ -8,7 +8,7 @@ import com.daml.lf.transaction.VersionTimeline
final case class VersionRange[V](
min: V,
max: V,
) {
)(implicit ev: VersionTimeline.SubVersion[V]) {
import VersionTimeline._
import VersionTimeline.Implicits._
@ -19,13 +19,10 @@ final case class VersionRange[V](
max = minVersion(this.max, that.max)
)
def nonEmpty(implicit ev: VersionTimeline.SubVersion[V]): Boolean =
def nonEmpty: Boolean =
!(max precedes min)
def contains(v: V)(implicit ev: VersionTimeline.SubVersion[V]): Boolean =
def contains(v: V): Boolean =
!((max precedes v) || (v precedes min))
def map[W](f: V => W): VersionRange[W] =
VersionRange(f(min), f(max))
}

View File

@ -546,7 +546,7 @@ object ValueCoder {
private[value] def valueToBytes[Cid](
encodeCid: EncodeCid[Cid],
v: Value[Cid],
supportedVersions: VersionRange[ValueVersion] = ValueVersions.SupportedDevVersions,
supportedVersions: VersionRange[ValueVersion] = ValueVersions.DevOutputVersions,
): Either[EncodeError, Array[Byte]] =
encodeVersionedValue(encodeCid, v, supportedVersions).map(_.toByteArray)

View File

@ -30,15 +30,19 @@ private[lf] object ValueVersions
private[value] val minContractIdV1 = ValueVersion("7")
// Older versions are deprecated https://github.com/digital-asset/daml/issues/5220
// We force output of recent version, but keep reading older version as long as
// Sandbox is alive.
val SupportedStableVersions = VersionRange(ValueVersion("6"), ValueVersion("6"))
val StableOutputVersions: VersionRange[ValueVersion] =
VersionRange(ValueVersion("6"), ValueVersion("6"))
val SupportedDevVersions = SupportedStableVersions.copy(max = acceptedVersions.last)
val DevOutputVersions: VersionRange[ValueVersion] =
StableOutputVersions.copy(max = acceptedVersions.last)
// Empty range
val Empty: VersionRange[ValueVersion] =
VersionRange(acceptedVersions.last, acceptedVersions.head)
def assignVersion[Cid](
v0: Value[Cid],
supportedVersions: VersionRange[ValueVersion] = SupportedDevVersions,
supportedVersions: VersionRange[ValueVersion] = StableOutputVersions,
): Either[String, ValueVersion] = {
import VersionTimeline.{maxVersion => maxVV}
import VersionTimeline.Implicits._
@ -98,20 +102,20 @@ private[lf] object ValueVersions
@throws[IllegalArgumentException]
def assertAssignVersion[Cid](
v0: Value[Cid],
supportedVersions: VersionRange[ValueVersion] = SupportedDevVersions,
supportedVersions: VersionRange[ValueVersion] = DevOutputVersions,
): ValueVersion =
data.assertRight(assignVersion(v0, supportedVersions))
def asVersionedValue[Cid](
value: Value[Cid],
supportedVersions: VersionRange[ValueVersion] = SupportedDevVersions,
supportedVersions: VersionRange[ValueVersion] = DevOutputVersions,
): Either[String, VersionedValue[Cid]] =
assignVersion(value, supportedVersions).map(VersionedValue(_, value))
@throws[IllegalArgumentException]
def assertAsVersionedValue[Cid](
value: Value[Cid],
supportedVersions: VersionRange[ValueVersion] = SupportedDevVersions,
supportedVersions: VersionRange[ValueVersion] = DevOutputVersions,
): VersionedValue[Cid] =
data.assertRight(asVersionedValue(value, supportedVersions))
}

View File

@ -19,9 +19,9 @@ class TransactionVersionSpec extends WordSpec with Matchers with TableDrivenProp
import VersionTimeline.maxVersion
private[this] val supportedValVersions =
ValueVersions.SupportedDevVersions.copy(min = ValueVersion("1"))
ValueVersions.DevOutputVersions.copy(min = ValueVersion("1"))
private[this] val supportedTxVersions =
TransactionVersions.SupportedDevOutputVersions.copy(min = TransactionVersion("1"))
TransactionVersions.DevOutputVersions.copy(min = TransactionVersion("1"))
"assignVersion" should {
"prefer picking an older version" in {
@ -139,6 +139,18 @@ class TransactionVersionSpec extends WordSpec with Matchers with TableDrivenProp
}
}
"ValueVersions.Empty" should {
"be empty" in {
ValueVersions.Empty.nonEmpty shouldBe false
}
}
"TransactionVersions.Empty" should {
"be empty" in {
TransactionVersions.Empty.nonEmpty shouldBe false
}
}
private[this] def assignValueVersions[Nid, Cid <: ContractId](
t: GenTransaction[Nid, Cid, Value[Cid]],
): GenTransaction[Nid, Cid, VersionedValue[Cid]] =

View File

@ -1,7 +1,9 @@
// Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.lf.engine.script
package com.daml.lf
package engine
package script
import akka.actor.ActorSystem
import akka.http.scaladsl.Http
@ -27,16 +29,12 @@ import spray.json._
import com.daml.api.util.TimestampConversion
import com.daml.grpc.adapter.ExecutionSequencerFactory
import com.daml.grpc.adapter.client.akka.ClientAdapter
import com.daml.lf.CompiledPackages
import com.daml.lf.scenario.ScenarioLedger
import com.daml.lf.crypto
import com.daml.lf.data.Ref._
import com.daml.lf.data.{Ref, ImmArray}
import com.daml.lf.data.{Time}
import com.daml.lf.iface.{EnvironmentInterface, InterfaceType}
import com.daml.lf.language.Ast._
import com.daml.lf.speedy
import com.daml.lf.speedy.{InitialSeeding, PartialTransaction}
import com.daml.lf.transaction.Node.{NodeCreate, NodeExercises}
import com.daml.lf.speedy.ScenarioRunner
import com.daml.lf.speedy.Speedy.Machine
@ -321,12 +319,23 @@ class GrpcLedgerClient(val grpcClient: LedgerClient, val applicationId: Applicat
// Client for the script service.
class IdeClient(val compiledPackages: CompiledPackages) extends ScriptLedgerClient {
private val txSeeding =
speedy.InitialSeeding.TransactionSeed(crypto.Hash.hashPrivateKey(s"script-service"))
// Machine for scenario expressions.
val machine = Machine.fromPureSExpr(compiledPackages, SEValue(SUnit))
val machine = Machine(
compiledPackages,
submissionTime = Time.Timestamp.Epoch,
initialSeeding = txSeeding,
expr = null,
globalCids = Set.empty,
committers = Set.empty,
inputValueVersions = value.ValueVersions.DevOutputVersions,
outputTransactionVersions = transaction.TransactionVersions.DevOutputVersions,
)
(compiledPackages, SEValue(SUnit))
val scenarioRunner = ScenarioRunner(machine)
private val txSeeding = crypto.Hash.hashPrivateKey(s"script-service")
machine.ptx =
PartialTransaction.initial(Time.Timestamp.MinValue, InitialSeeding.TransactionSeed(txSeeding))
override def query(party: SParty, templateId: Identifier)(
implicit ec: ExecutionContext,
mat: Materializer): Future[Seq[ScriptLedgerClient.ActiveContract]] = {

View File

@ -123,7 +123,7 @@ private[daml] object ApiServices {
private def createServices(ledgerId: LedgerId, ledgerConfigProvider: LedgerConfigProvider)(
implicit executionContext: ExecutionContext): List[BindableService] = {
logger.info(engine.info.show)
engine.info.pretty.foreach(logger.info(_))
val apiTransactionService =
ApiTransactionService.create(ledgerId, transactionsService)