LF: Refactor PreprocessorSpec test (#10909)

Just moving stuff arround

CHANGELOG_BEGIN
CHANGELOG_END
This commit is contained in:
Remy 2021-09-16 15:58:47 +02:00 committed by GitHub
parent 9b0fa29aec
commit 9582e019ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 460 additions and 426 deletions

View File

@ -0,0 +1,170 @@
// Copyright (c) 2021 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.lf
package engine
package preprocessing
import com.daml.lf.command._
import com.daml.lf.data._
import com.daml.lf.value.Value._
import org.scalatest.matchers.should.Matchers
import org.scalatest.prop.TableDrivenPropertyChecks
import org.scalatest.wordspec.AnyWordSpec
import scala.util.{Failure, Success, Try}
class CommandPreprocessorSpec extends AnyWordSpec with Matchers with TableDrivenPropertyChecks {
import com.daml.lf.testing.parser.Implicits._
import com.daml.lf.transaction.test.TransactionBuilder.Implicits.{defaultPackageId => _, _}
private implicit val defaultPackageId = defaultParserParameters.defaultPackageId
lazy val pkg =
p"""
module Mod {
record @serializable Record = { field : Int64 };
record @serializable RecordRef = { owner: Party, cid: (ContractId Mod:Record) };
val @noPartyLiterals toParties: Mod:RecordRef -> List Party =
\ (ref: Mod:RecordRef) -> Cons @Party [Mod:RecordRef {owner} ref] (Nil @Party);
template (this : RecordRef) = {
precondition True,
signatories Mod:toParties this,
observers Mod:toParties this,
agreement "Agreement",
choices {
choice Change (self) (newCid: ContractId Mod:Record) : ContractId Mod:RecordRef,
controllers Mod:toParties this,
observers Nil @Party
to create @Mod:RecordRef Mod:RecordRef { owner = Mod:RecordRef {owner} this, cid = newCid }
},
key @Party (Mod:RecordRef {owner} this) (\ (p: Party) -> Cons @Party [p] (Nil @Party))
};
}
"""
private[this] val compiledPackage = ConcurrentCompiledPackages()
assert(compiledPackage.addPackage(defaultPackageId, pkg) == ResultDone.Unit)
"preprocessCommand" should {
def contractIdTestCases(culpritCid: ContractId, innocentCid: ContractId) = Table[ApiCommand](
"command",
CreateCommand(
"Mod:RecordRef",
ValueRecord("", ImmArray("" -> ValueParty("Alice"), "" -> ValueContractId(culpritCid))),
),
ExerciseCommand(
"Mod:RecordRef",
innocentCid,
"Change",
ValueContractId(culpritCid),
),
ExerciseCommand(
"Mod:RecordRef",
culpritCid,
"Change",
ValueContractId(innocentCid),
),
CreateAndExerciseCommand(
"Mod:RecordRef",
ValueRecord("", ImmArray("" -> ValueParty("Alice"), "" -> ValueContractId(culpritCid))),
"Change",
ValueContractId(innocentCid),
),
CreateAndExerciseCommand(
"Mod:RecordRef",
ValueRecord("", ImmArray("" -> ValueParty("Alice"), "" -> ValueContractId(innocentCid))),
"Change",
ValueContractId(culpritCid),
),
ExerciseByKeyCommand(
"Mod:RecordRef",
ValueParty("Alice"),
"Change",
ValueContractId(culpritCid),
),
)
"accept all contract IDs when require flags are false" in {
val cmdPreprocessor = new CommandPreprocessor(
compiledPackage.interface,
forbidV0ContractId = false,
requireV1ContractIdSuffix = false,
)
val cids = List(
ContractId.V1
.assertBuild(
crypto.Hash.hashPrivateKey("a suffixed V1 Contract ID"),
Bytes.assertFromString("00"),
),
ContractId.V1
.assertBuild(crypto.Hash.hashPrivateKey("a non-suffixed V1 Contract ID"), Bytes.Empty),
ContractId.V0.assertFromString("#a V0 Contract ID"),
)
cids.foreach(cid =>
forEvery(contractIdTestCases(cids.head, cid))(cmd =>
Try(cmdPreprocessor.unsafePreprocessCommand(cmd)) shouldBe a[Success[_]]
)
)
}
"reject V0 Contract IDs when requireV1ContractId flag is true" in {
val cmdPreprocessor = new CommandPreprocessor(
compiledPackage.interface,
forbidV0ContractId = true,
requireV1ContractIdSuffix = false,
)
val List(aLegalCid, anotherLegalCid) =
List("a legal Contract ID", "another legal Contract ID").map(s =>
ContractId.V1.assertBuild(crypto.Hash.hashPrivateKey(s), Bytes.assertFromString("00"))
)
val illegalCid = ContractId.V0.assertFromString("#illegal Contract ID")
val failure = Failure(Error.Preprocessing.IllegalContractId.V0ContractId(illegalCid))
forEvery(contractIdTestCases(aLegalCid, anotherLegalCid))(cmd =>
Try(cmdPreprocessor.unsafePreprocessCommand(cmd)) shouldBe a[Success[_]]
)
forEvery(contractIdTestCases(illegalCid, aLegalCid))(cmd =>
Try(cmdPreprocessor.unsafePreprocessCommand(cmd)) shouldBe failure
)
}
"reject non suffixed V1 Contract IDs when requireV1ContractIdSuffix is true" in {
val cmdPreprocessor = new CommandPreprocessor(
compiledPackage.interface,
forbidV0ContractId = true,
requireV1ContractIdSuffix = true,
)
val List(aLegalCid, anotherLegalCid) =
List("a legal Contract ID", "another legal Contract ID").map(s =>
ContractId.V1.assertBuild(crypto.Hash.hashPrivateKey(s), Bytes.assertFromString("00"))
)
val illegalCid =
ContractId.V1.assertBuild(crypto.Hash.hashPrivateKey("an illegal Contract ID"), Bytes.Empty)
val failure = Failure(Error.Preprocessing.IllegalContractId.NonSuffixV1ContractId(illegalCid))
forEvery(contractIdTestCases(aLegalCid, anotherLegalCid)) { cmd =>
Try(cmdPreprocessor.unsafePreprocessCommand(cmd)) shouldBe a[Success[_]]
}
forEvery(contractIdTestCases(illegalCid, aLegalCid)) { cmd =>
Try(cmdPreprocessor.unsafePreprocessCommand(cmd)) shouldBe failure
}
}
}
}

View File

@ -1,407 +0,0 @@
// Copyright (c) 2021 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.lf
package engine
package preprocessing
import com.daml.lf.data._
import com.daml.lf.language.Ast
import com.daml.lf.language.Util._
import com.daml.lf.speedy.SValue._
import com.daml.lf.testing.parser.Implicits._
import com.daml.lf.value.Value
import com.daml.lf.value.Value._
import org.scalatest.Inside
import org.scalatest.matchers.should.Matchers
import org.scalatest.prop.TableDrivenPropertyChecks
import org.scalatest.wordspec.AnyWordSpec
import scala.language.implicitConversions
import scala.util.{Failure, Success, Try}
class PreprocessorSpec
extends AnyWordSpec
with Matchers
with TableDrivenPropertyChecks
with Inside {
import Preprocessor.ArrayList
import defaultParserParameters.{defaultPackageId => pkgId}
private implicit def toName(s: String): Ref.Name = Ref.Name.assertFromString(s)
private[this] val recordCon =
Ref.Identifier(pkgId, Ref.QualifiedName.assertFromString("Mod:Record"))
private[this] val recordRefCon =
Ref.Identifier(pkgId, Ref.QualifiedName.assertFromString("Mod:RecordRef"))
private[this] val variantCon =
Ref.Identifier(pkgId, Ref.QualifiedName.assertFromString("Mod:Either"))
private[this] val enumCon =
Ref.Identifier(pkgId, Ref.QualifiedName.assertFromString("Mod:Enum"))
private[this] val tricky =
Ref.Identifier(pkgId, Ref.QualifiedName.assertFromString("Mod:Tricky"))
private[this] val myListTyCons =
Ref.Identifier(pkgId, Ref.QualifiedName.assertFromString("Mod:MyList"))
private[this] val myNilCons = Ref.Name.assertFromString("MyNil")
private[this] val myConsCons = Ref.Name.assertFromString("MyCons")
private[this] val alice = Ref.Party.assertFromString("Alice")
private[this] val dummySuffix = Bytes.assertFromString("00")
private[this] val typ = t"ContractId Mod:Record"
private[this] val aCid =
ContractId.V1.assertBuild(crypto.Hash.hashPrivateKey("a Contract ID"), dummySuffix)
lazy val pkg =
p"""
module Mod {
record @serializable Record = { field : Int64 };
variant @serializable Either (a: *) (b: *) = Left : a | Right : b;
enum Enum = value1 | value2;
record Tricky (b: * -> *) = { x : b Unit };
record MyCons = { head : Int64, tail: Mod:MyList };
variant MyList = MyNil : Unit | MyCons: Mod:MyCons ;
record @serializable RecordRef = { owner: Party, cid: (ContractId Mod:Record) };
val @noPartyLiterals toParties: Mod:RecordRef -> List Party =
\ (ref: Mod:RecordRef) -> Cons @Party [Mod:RecordRef {owner} ref] (Nil @Party);
template (this : RecordRef) = {
precondition True,
signatories Mod:toParties this,
observers Mod:toParties this,
agreement "Agreement",
choices {
choice Change (self) (newCid: ContractId Mod:Record) : ContractId Mod:RecordRef,
controllers Mod:toParties this,
observers Nil @Party
to create @Mod:RecordRef Mod:RecordRef { owner = Mod:RecordRef {owner} this, cid = newCid }
},
key @Party (Mod:RecordRef {owner} this) (\ (p: Party) -> Cons @Party [p] (Nil @Party))
};
}
"""
private[this] val compiledPackage = ConcurrentCompiledPackages()
assert(compiledPackage.addPackage(pkgId, pkg) == ResultDone.Unit)
"translateValue" should {
val valueTranslator = new ValueTranslator(
compiledPackage.interface,
forbidV0ContractId = true,
requireV1ContractIdSuffix = false,
)
import valueTranslator.unsafeTranslateValue
val testCases = Table[Ast.Type, Value, speedy.SValue](
("type", "value", "svalue"),
(TUnit, ValueUnit, SValue.Unit),
(TBool, ValueTrue, SValue.True),
(TInt64, ValueInt64(42), SInt64(42)),
(
TTimestamp,
ValueTimestamp(Time.Timestamp.assertFromString("1969-07-20T20:17:00Z")),
STimestamp(Time.Timestamp.assertFromString("1969-07-20T20:17:00Z")),
),
(
TDate,
ValueDate(Time.Date.assertFromString("1879-03-14")),
SDate(Time.Date.assertFromString("1879-03-14")),
),
(TText, ValueText("daml"), SText("daml")),
(
TNumeric(Ast.TNat(Decimal.scale)),
ValueNumeric(Numeric.assertFromString("10.")),
SNumeric(Numeric.assertFromString("10.0000000000")),
),
// TNumeric(TNat(9)) ,
// ValueNumeric(Numeric.assertFromString("9.000000000")),
(TParty, ValueParty(alice), SParty(alice)),
(
TContractId(Ast.TTyCon(recordCon)),
ValueContractId(aCid),
SContractId(aCid),
),
(
TList(TText),
ValueList(FrontStack(ValueText("a"), ValueText("b"))),
SList(FrontStack(SText("a"), SText("b"))),
),
(
TTextMap(TBool),
ValueTextMap(SortedLookupList(Map("0" -> ValueTrue, "1" -> ValueFalse))),
SMap(true, Iterator(SText("0") -> SValue.True, SText("1") -> SValue.False)),
),
(
TGenMap(TInt64, TText),
ValueGenMap(ImmArray(ValueInt64(1) -> ValueText("1"), ValueInt64(42) -> ValueText("42"))),
SMap(false, Iterator(SInt64(1) -> SText("1"), SInt64(42) -> SText("42"))),
),
(TOptional(TText), ValueOptional(Some(ValueText("text"))), SOptional(Some(SText("text")))),
(
Ast.TTyCon(recordCon),
ValueRecord(None, ImmArray(Some[Ref.Name]("field") -> ValueInt64(33))),
SRecord(recordCon, ImmArray("field"), ArrayList(SInt64(33))),
),
(
TTyConApp(variantCon, ImmArray(TText, TInt64)),
ValueVariant(None, "Left", ValueText("some test")),
SVariant(variantCon, "Left", 0, SText("some test")),
),
(Ast.TTyCon(enumCon), ValueEnum(None, "value1"), SEnum(enumCon, "value1", 0)),
(
Ast.TApp(Ast.TTyCon(tricky), Ast.TBuiltin(Ast.BTList)),
ValueRecord(None, ImmArray(None -> ValueNil)),
SRecord(tricky, ImmArray("x"), ArrayList(SValue.EmptyList)),
),
)
"succeeds on well type values" in {
forAll(testCases) { (typ, value, svalue) =>
unsafeTranslateValue(typ, value) shouldBe svalue
}
}
"fails on non-well type values" in {
forAll(testCases) { (typ1, value1, _) =>
forAll(testCases) { (_, value2, _) =>
if (value1 != value2) {
a[Error.Preprocessing.Error] shouldBe thrownBy(unsafeTranslateValue(typ1, value2))
}
}
}
}
"fails on too deep values" in {
def mkMyList(n: Int) =
Iterator.range(0, n).foldLeft[Value](ValueVariant(None, myNilCons, ValueUnit)) {
case (v, n) =>
ValueVariant(
None,
myConsCons,
ValueRecord(None, ImmArray(None -> ValueInt64(n.toLong), None -> v)),
)
}
val notTooBig = mkMyList(49)
val tooBig = mkMyList(50)
val failure = Failure(Error.Preprocessing.ValueNesting(tooBig))
Try(unsafeTranslateValue(Ast.TTyCon(myListTyCons), notTooBig)) shouldBe a[Success[_]]
Try(unsafeTranslateValue(Ast.TTyCon(myListTyCons), tooBig)) shouldBe failure
}
def testCasesForCid(culprit: ContractId) = {
val cid = ValueContractId(culprit)
Table[Ast.Type, Value](
("type" -> "value"),
t"ContractId Mod:Record" -> cid,
TList(typ) -> ValueList(FrontStack(cid)),
TTextMap(typ) -> ValueTextMap(SortedLookupList(Map("0" -> cid))),
TGenMap(TInt64, typ) -> ValueGenMap(ImmArray(ValueInt64(1) -> cid)),
TGenMap(typ, TInt64) -> ValueGenMap(ImmArray(cid -> ValueInt64(0))),
TOptional(typ) -> ValueOptional(Some(cid)),
Ast.TTyCon(recordRefCon) -> ValueRecord(
None,
ImmArray(None -> ValueParty(alice), None -> cid),
),
TTyConApp(variantCon, ImmArray(typ, TInt64)) -> ValueVariant(
None,
"Left",
cid,
),
)
}
"accept all contract IDs when require flags are false" in {
val valueTranslator = new ValueTranslator(
compiledPackage.interface,
forbidV0ContractId = false,
requireV1ContractIdSuffix = false,
)
val cids = List(
ContractId.V1
.assertBuild(crypto.Hash.hashPrivateKey("a suffixed V1 Contract ID"), dummySuffix),
ContractId.V1
.assertBuild(crypto.Hash.hashPrivateKey("a non-suffixed V1 Contract ID"), Bytes.Empty),
ContractId.V0.assertFromString("#a V0 Contract ID"),
)
cids.foreach(cid =>
forEvery(testCasesForCid(cid))((typ, value) =>
Try(valueTranslator.unsafeTranslateValue(typ, value)) shouldBe a[Success[_]]
)
)
}
"reject V0 Contract IDs when requireV1ContractId flag is true" in {
val valueTranslator = new ValueTranslator(
compiledPackage.interface,
forbidV0ContractId = true,
requireV1ContractIdSuffix = false,
)
val legalCid =
ContractId.V1.assertBuild(crypto.Hash.hashPrivateKey("a legal Contract ID"), dummySuffix)
val illegalCid =
ContractId.V0.assertFromString("#illegal Contract ID")
val failure = Failure(Error.Preprocessing.IllegalContractId.V0ContractId(illegalCid))
forEvery(testCasesForCid(legalCid))((typ, value) =>
Try(valueTranslator.unsafeTranslateValue(typ, value)) shouldBe a[Success[_]]
)
forEvery(testCasesForCid(illegalCid))((typ, value) =>
Try(valueTranslator.unsafeTranslateValue(typ, value)) shouldBe failure
)
}
"reject non suffixed V1 Contract IDs when requireV1ContractIdSuffix is true" in {
val valueTranslator = new ValueTranslator(
compiledPackage.interface,
forbidV0ContractId = true,
requireV1ContractIdSuffix = true,
)
val legalCid =
ContractId.V1.assertBuild(crypto.Hash.hashPrivateKey("a legal Contract ID"), dummySuffix)
val illegalCid =
ContractId.V1.assertBuild(crypto.Hash.hashPrivateKey("an illegal Contract ID"), Bytes.Empty)
val failure = Failure(Error.Preprocessing.IllegalContractId.NonSuffixV1ContractId(illegalCid))
forEvery(testCasesForCid(legalCid))((typ, value) =>
Try(valueTranslator.unsafeTranslateValue(typ, value)) shouldBe a[Success[_]]
)
forEvery(testCasesForCid(illegalCid))((typ, value) =>
Try(valueTranslator.unsafeTranslateValue(typ, value)) shouldBe failure
)
}
}
"preprocessCommand" should {
import command._
def payload(cid: ContractId) = ValueRecord(
None,
ImmArray(None -> ValueParty(alice), None -> ValueContractId(cid)),
)
def testCases(culpritCid: ContractId, innocentCid: ContractId) = Table[ApiCommand](
"command",
CreateCommand(
recordRefCon,
payload(culpritCid),
),
ExerciseCommand(
recordRefCon,
innocentCid,
"Change",
ValueContractId(culpritCid),
),
ExerciseCommand(
recordRefCon,
culpritCid,
"Change",
ValueContractId(innocentCid),
),
CreateAndExerciseCommand(
recordRefCon,
payload(culpritCid),
"Change",
ValueContractId(innocentCid),
),
CreateAndExerciseCommand(
recordRefCon,
payload(innocentCid),
"Change",
ValueContractId(culpritCid),
),
ExerciseByKeyCommand(
recordRefCon,
ValueParty(alice),
"Change",
ValueContractId(culpritCid),
),
)
"accept all contract IDs when require flags are false" in {
val cmdPreprocessor = new CommandPreprocessor(
compiledPackage.interface,
forbidV0ContractId = false,
requireV1ContractIdSuffix = false,
)
val cids = List(
ContractId.V1
.assertBuild(crypto.Hash.hashPrivateKey("a suffixed V1 Contract ID"), dummySuffix),
ContractId.V1
.assertBuild(crypto.Hash.hashPrivateKey("a non-suffixed V1 Contract ID"), Bytes.Empty),
ContractId.V0.assertFromString("#a V0 Contract ID"),
)
cids.foreach(cid =>
forEvery(testCases(cids.head, cid))(cmd =>
Try(cmdPreprocessor.unsafePreprocessCommand(cmd)) shouldBe a[Success[_]]
)
)
}
"reject V0 Contract IDs when requireV1ContractId flag is true" in {
val cmdPreprocessor = new CommandPreprocessor(
compiledPackage.interface,
forbidV0ContractId = true,
requireV1ContractIdSuffix = false,
)
val List(aLegalCid, anotherLegalCid) =
List("a legal Contract ID", "another legal Contract ID").map(s =>
ContractId.V1.assertBuild(crypto.Hash.hashPrivateKey(s), dummySuffix)
)
val illegalCid = ContractId.V0.assertFromString("#illegal Contract ID")
val failure = Failure(Error.Preprocessing.IllegalContractId.V0ContractId(illegalCid))
forEvery(testCases(aLegalCid, anotherLegalCid))(cmd =>
Try(cmdPreprocessor.unsafePreprocessCommand(cmd)) shouldBe a[Success[_]]
)
forEvery(testCases(illegalCid, aLegalCid))(cmd =>
Try(cmdPreprocessor.unsafePreprocessCommand(cmd)) shouldBe failure
)
}
"reject non suffixed V1 Contract IDs when requireV1ContractIdSuffix is true" in {
val cmdPreprocessor = new CommandPreprocessor(
compiledPackage.interface,
forbidV0ContractId = true,
requireV1ContractIdSuffix = true,
)
val List(aLegalCid, anotherLegalCid) =
List("a legal Contract ID", "another legal Contract ID").map(s =>
ContractId.V1.assertBuild(crypto.Hash.hashPrivateKey(s), dummySuffix)
)
val illegalCid =
ContractId.V1.assertBuild(crypto.Hash.hashPrivateKey("an illegal Contract ID"), Bytes.Empty)
val failure = Failure(Error.Preprocessing.IllegalContractId.NonSuffixV1ContractId(illegalCid))
forEvery(testCases(aLegalCid, anotherLegalCid)) { cmd =>
Try(cmdPreprocessor.unsafePreprocessCommand(cmd)) shouldBe a[Success[_]]
}
forEvery(testCases(illegalCid, aLegalCid)) { cmd =>
Try(cmdPreprocessor.unsafePreprocessCommand(cmd)) shouldBe failure
}
}
}
}

View File

@ -164,8 +164,8 @@ class ValueEnricherSpec extends AnyWordSpec with Matchers with TableDrivenProper
"enrich transaction as expected" in {
val inputKey = ValueRecord(
None,
ImmArray[(String, Value)](
"",
ImmArray(
"" -> ValueParty("Alice"),
"" -> Value.ValueInt64(0),
),
@ -173,8 +173,8 @@ class ValueEnricherSpec extends AnyWordSpec with Matchers with TableDrivenProper
val inputContract =
ValueRecord(
None,
ImmArray[(String, Value)](
"",
ImmArray(
"" -> inputKey,
"" -> Value.ValueNil,
),
@ -190,8 +190,8 @@ class ValueEnricherSpec extends AnyWordSpec with Matchers with TableDrivenProper
)
val outputKey = ValueRecord(
Some("Mod:Key"),
ImmArray[(String, Value)](
"Mod:Key",
ImmArray(
"party" -> ValueParty("Alice"),
"idx" -> Value.ValueInt64(0),
),
@ -199,15 +199,15 @@ class ValueEnricherSpec extends AnyWordSpec with Matchers with TableDrivenProper
val outputContract =
ValueRecord(
Some("Mod:Contract"),
ImmArray[(String, Value)](
"Mod:Contract",
ImmArray(
"key" -> outputKey,
"cids" -> Value.ValueNil,
),
)
val outputRecord =
ValueRecord(Some("Mod:Record"), ImmArray[(String, Value)]("field" -> ValueInt64(33)))
ValueRecord("Mod:Record", ImmArray("field" -> ValueInt64(33)))
val outputTransaction = buildTransaction(
outputContract,

View File

@ -0,0 +1,257 @@
// Copyright (c) 2021 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.lf
package engine
package preprocessing
import com.daml.lf.data._
import com.daml.lf.language.Ast
import com.daml.lf.language.Util._
import com.daml.lf.speedy.SValue._
import com.daml.lf.value.Value
import com.daml.lf.value.Value._
import org.scalatest.matchers.should.Matchers
import org.scalatest.prop.TableDrivenPropertyChecks
import org.scalatest.wordspec.AnyWordSpec
import scala.util.{Failure, Success, Try}
class ValueTranslatorSpec extends AnyWordSpec with Matchers with TableDrivenPropertyChecks {
import Preprocessor.ArrayList
import com.daml.lf.testing.parser.Implicits._
import com.daml.lf.transaction.test.TransactionBuilder.Implicits.{defaultPackageId => _, _}
private implicit val defaultPackageId = defaultParserParameters.defaultPackageId
val aCid =
ContractId.V1.assertBuild(
crypto.Hash.hashPrivateKey("a Contract ID"),
Bytes.assertFromString("00"),
)
lazy val pkg =
p"""
module Mod {
record @serializable Record = { field : Int64 };
variant @serializable Either (a: *) (b: *) = Left : a | Right : b;
enum Enum = value1 | value2;
record Tricky (b: * -> *) = { x : b Unit };
record MyCons = { head : Int64, tail: Mod:MyList };
variant MyList = MyNil : Unit | MyCons: Mod:MyCons ;
record @serializable RecordRef = { owner: Party, cid: (ContractId Mod:Record) };
}
"""
private[this] val compiledPackage = ConcurrentCompiledPackages()
assert(compiledPackage.addPackage(defaultPackageId, pkg) == ResultDone.Unit)
"translateValue" should {
val valueTranslator = new ValueTranslator(
compiledPackage.interface,
forbidV0ContractId = true,
requireV1ContractIdSuffix = false,
)
import valueTranslator.unsafeTranslateValue
val testCases = Table[Ast.Type, Value, speedy.SValue](
("type", "value", "svalue"),
(TUnit, ValueUnit, SValue.Unit),
(TBool, ValueTrue, SValue.True),
(TInt64, ValueInt64(42), SInt64(42)),
(
TTimestamp,
ValueTimestamp(Time.Timestamp.assertFromString("1969-07-20T20:17:00Z")),
STimestamp(Time.Timestamp.assertFromString("1969-07-20T20:17:00Z")),
),
(
TDate,
ValueDate(Time.Date.assertFromString("1879-03-14")),
SDate(Time.Date.assertFromString("1879-03-14")),
),
(TText, ValueText("daml"), SText("daml")),
(
TNumeric(Ast.TNat(Decimal.scale)),
ValueNumeric(Numeric.assertFromString("10.")),
SNumeric(Numeric.assertFromString("10.0000000000")),
),
// TNumeric(TNat(9)) ,
// ValueNumeric(Numeric.assertFromString("9.000000000")),
(TParty, ValueParty("Alice"), SParty("Alice")),
(
TContractId(t"Mod:Record"),
ValueContractId(aCid),
SContractId(aCid),
),
(
TList(TText),
ValueList(FrontStack(ValueText("a"), ValueText("b"))),
SList(FrontStack(SText("a"), SText("b"))),
),
(
TTextMap(TBool),
ValueTextMap(SortedLookupList(Map("0" -> ValueTrue, "1" -> ValueFalse))),
SMap(true, Iterator(SText("0") -> SValue.True, SText("1") -> SValue.False)),
),
(
TGenMap(TInt64, TText),
ValueGenMap(ImmArray(ValueInt64(1) -> ValueText("1"), ValueInt64(42) -> ValueText("42"))),
SMap(false, Iterator(SInt64(1) -> SText("1"), SInt64(42) -> SText("42"))),
),
(TOptional(TText), ValueOptional(Some(ValueText("text"))), SOptional(Some(SText("text")))),
(
t"Mod:Record",
ValueRecord("", ImmArray("field" -> ValueInt64(33))),
SRecord("Mod:Record", ImmArray("field"), ArrayList(SInt64(33))),
),
(
t"Mod:Either Text Int64",
ValueVariant("", "Left", ValueText("some test")),
SVariant("Mod:Either", "Left", 0, SText("some test")),
),
(Ast.TTyCon("Mod:Enum"), ValueEnum("", "value1"), SEnum("Mod:Enum", "value1", 0)),
(
Ast.TApp(Ast.TTyCon("Mod:Tricky"), Ast.TBuiltin(Ast.BTList)),
ValueRecord("", ImmArray("" -> ValueNil)),
SRecord("Mod:Tricky", ImmArray("x"), ArrayList(SValue.EmptyList)),
),
)
"succeeds on well type values" in {
forAll(testCases) { (typ, value, svalue) =>
unsafeTranslateValue(typ, value) shouldBe svalue
}
}
"fails on non-well type values" in {
forAll(testCases) { (typ1, value1, _) =>
forAll(testCases) { (_, value2, _) =>
if (value1 != value2) {
a[Error.Preprocessing.Error] shouldBe thrownBy(unsafeTranslateValue(typ1, value2))
}
}
}
}
"fails on too deep values" in {
def mkMyList(n: Int) =
Iterator.range(0, n).foldLeft[Value](ValueVariant("", "MyNil", ValueUnit)) { case (v, n) =>
ValueVariant(
"",
"MyCons",
ValueRecord("", ImmArray("" -> ValueInt64(n.toLong), "" -> v)),
)
}
val notTooBig = mkMyList(49)
val tooBig = mkMyList(50)
val failure = Failure(Error.Preprocessing.ValueNesting(tooBig))
Try(unsafeTranslateValue(t"Mod:MyList", notTooBig)) shouldBe a[Success[_]]
Try(unsafeTranslateValue(t"Mod:MyList", tooBig)) shouldBe failure
}
def testCasesForCid(culprit: ContractId) = {
val cid = ValueContractId(culprit)
Table[Ast.Type, Value](
("type" -> "value"),
t"ContractId Mod:Record" -> cid,
TList(t"ContractId Mod:Record") -> ValueList(FrontStack(cid)),
TTextMap(t"ContractId Mod:Record") -> ValueTextMap(SortedLookupList(Map("0" -> cid))),
TGenMap(TInt64, t"ContractId Mod:Record") -> ValueGenMap(ImmArray(ValueInt64(1) -> cid)),
TGenMap(t"ContractId Mod:Record", TInt64) -> ValueGenMap(ImmArray(cid -> ValueInt64(0))),
TOptional(t"ContractId Mod:Record") -> ValueOptional(Some(cid)),
Ast.TTyCon("Mod:RecordRef") -> ValueRecord(
"",
ImmArray("" -> ValueParty("Alice"), "" -> cid),
),
TTyConApp("Mod:Either", ImmArray(t"ContractId Mod:Record", TInt64)) -> ValueVariant(
"",
"Left",
cid,
),
)
}
"accept all contract IDs when require flags are false" in {
val valueTranslator = new ValueTranslator(
compiledPackage.interface,
forbidV0ContractId = false,
requireV1ContractIdSuffix = false,
)
val cids = List(
ContractId.V1
.assertBuild(
crypto.Hash.hashPrivateKey("a suffixed V1 Contract ID"),
Bytes.assertFromString("00"),
),
ContractId.V1
.assertBuild(crypto.Hash.hashPrivateKey("a non-suffixed V1 Contract ID"), Bytes.Empty),
ContractId.V0.assertFromString("#a V0 Contract ID"),
)
cids.foreach(cid =>
forEvery(testCasesForCid(cid))((typ, value) =>
Try(valueTranslator.unsafeTranslateValue(typ, value)) shouldBe a[Success[_]]
)
)
}
"reject V0 Contract IDs when requireV1ContractId flag is true" in {
val valueTranslator = new ValueTranslator(
compiledPackage.interface,
forbidV0ContractId = true,
requireV1ContractIdSuffix = false,
)
val legalCid =
ContractId.V1.assertBuild(
crypto.Hash.hashPrivateKey("a legal Contract ID"),
Bytes.assertFromString("00"),
)
val illegalCid =
ContractId.V0.assertFromString("#illegal Contract ID")
val failure = Failure(Error.Preprocessing.IllegalContractId.V0ContractId(illegalCid))
forEvery(testCasesForCid(legalCid))((typ, value) =>
Try(valueTranslator.unsafeTranslateValue(typ, value)) shouldBe a[Success[_]]
)
forEvery(testCasesForCid(illegalCid))((typ, value) =>
Try(valueTranslator.unsafeTranslateValue(typ, value)) shouldBe failure
)
}
"reject non suffixed V1 Contract IDs when requireV1ContractIdSuffix is true" in {
val valueTranslator = new ValueTranslator(
compiledPackage.interface,
forbidV0ContractId = true,
requireV1ContractIdSuffix = true,
)
val legalCid =
ContractId.V1.assertBuild(
crypto.Hash.hashPrivateKey("a legal Contract ID"),
Bytes.assertFromString("00"),
)
val illegalCid =
ContractId.V1.assertBuild(crypto.Hash.hashPrivateKey("an illegal Contract ID"), Bytes.Empty)
val failure = Failure(Error.Preprocessing.IllegalContractId.NonSuffixV1ContractId(illegalCid))
forEvery(testCasesForCid(legalCid))((typ, value) =>
Try(valueTranslator.unsafeTranslateValue(typ, value)) shouldBe a[Success[_]]
)
forEvery(testCasesForCid(illegalCid))((typ, value) =>
Try(valueTranslator.unsafeTranslateValue(typ, value)) shouldBe failure
)
}
}
}

View File

@ -8,6 +8,7 @@ package test
import com.daml.lf.data._
import com.daml.lf.language.LanguageVersion
import com.daml.lf.transaction.{Transaction => Tx}
import com.daml.lf.value.Value
import com.daml.lf.value.Value.ContractId
import scala.Ordering.Implicits.infixOrderingOps
@ -180,7 +181,6 @@ final class TransactionBuilder(pkgTxVersion: Ref.PackageId => TransactionVersion
object TransactionBuilder {
type Value = value.Value
type TxValue = value.Value.VersionedValue
type Node = Node.GenNode[NodeId]
type TxNode = Node.GenNode[NodeId]
@ -197,7 +197,6 @@ object TransactionBuilder {
type TxKeyWithMaintainers = Node.KeyWithMaintainers[TxValue]
type TxRollBack = Node.NodeRollback[NodeId]
val Value = value.Value
val Create = Node.NodeCreate
val Exercise = Node.NodeExercises
val Fetch = Node.NodeFetch
@ -357,12 +356,25 @@ object TransactionBuilder {
implicit def toNumeric(s: String): Numeric =
Numeric.assertFromString(s)
implicit def toOption[X](s: String)(implicit toX: String => X): Option[X] =
private def toOption[X](s: String)(implicit toX: String => X): Option[X] =
if (s.isEmpty) None else Some(toX(s))
implicit def toFields(list: ImmArray[(String, Value)]): ImmArray[(Option[Ref.Name], Value)] =
list.map { case (name, value) => toOption(name)(toName) -> value }
private def toTuple[X1, Y1, X2, Y2](
tuple: (X1, Y1)
)(implicit toX2: X1 => X2, toY2: Y1 => Y2): (X2, Y2) =
(toX2(tuple._1), toY2(tuple._2))
implicit def toOptionIdentifier(s: String)(implicit
defaultPackageId: Ref.PackageId
): Option[Ref.Identifier] = toOption(s)
implicit def toOptionName(s: String): Option[Ref.Name] = toOption(s)
implicit def toField(t: (String, Value)): (Option[Ref.Name], Value) = toTuple(t)
}
def valueRecord(id: Option[Ref.Identifier], fields: (Option[Ref.Name], Value)*) =
Value.ValueRecord(id, fields.to(ImmArray))
}

View File

@ -20,6 +20,7 @@ import com.daml.lf.data.Ref
import com.daml.lf.data.Time.Timestamp
import com.daml.lf.transaction.SubmittedTransaction
import com.daml.lf.transaction.test.TransactionBuilder
import com.daml.lf.value.Value
import com.google.protobuf.ByteString
import scala.jdk.CollectionConverters._
@ -84,7 +85,7 @@ object TestHelpers {
.logEntry
.getTransactionRejectionEntry
def lfTuple(values: String*): TransactionBuilder.Value =
def lfTuple(values: String*): Value =
TransactionBuilder.record(values.zipWithIndex.map { case (v, i) =>
s"_$i" -> v
}: _*)

View File

@ -413,7 +413,7 @@ class TransactionCommitterSpec
private def create(
contractId: ContractId,
signatories: Set[Ref.Party] = Set(aKeyMaintainer),
argument: TransactionBuilder.Value = aDummyValue,
argument: Value = aDummyValue,
keyAndMaintainer: Option[(String, String)] = Some(aKey -> aKeyMaintainer),
): TransactionBuilder.Create =
txBuilder.create(

View File

@ -372,7 +372,7 @@ class ModelConformanceValidatorSpec
private def create(
contractId: ContractId,
signatories: Set[Ref.Party] = Set(aKeyMaintainer),
argument: TransactionBuilder.Value = aDummyValue,
argument: Value = aDummyValue,
keyAndMaintainer: Option[(String, String)] = Some(aKey -> aKeyMaintainer),
): TransactionBuilder.Create = {
txBuilder.create(

View File

@ -278,7 +278,7 @@ class TransactionConsistencyValidatorSpec extends AnyWordSpec with Matchers {
private def create(
contractId: Value.ContractId,
signatories: Set[Ref.Party] = Set(aKeyMaintainer),
argument: TransactionBuilder.Value = aDummyValue,
argument: Value = aDummyValue,
keyAndMaintainer: Option[(String, String)] = Some(aKey -> aKeyMaintainer),
): TransactionBuilder.Create =
txBuilder.create(

View File

@ -16,6 +16,7 @@ import com.daml.lf.data.Relation.Relation
import com.daml.lf.data.{Ref, Time}
import com.daml.lf.transaction.test.TransactionBuilder
import com.daml.lf.transaction.{BlindingInfo, CommittedTransaction, NodeId}
import com.daml.lf.value.Value
import com.daml.lf.value.Value.ContractId
import org.scalatest.matchers.should.Matchers
import org.scalatest.prop.TableDrivenPropertyChecks
@ -133,7 +134,7 @@ final class StateUpdateComparisonSpec
private def create(
contractId: ContractId,
signatories: Set[Ref.Party] = Set(aKeyMaintainer),
argument: TransactionBuilder.Value = aDummyValue,
argument: Value = aDummyValue,
keyAndMaintainer: Option[(String, String)] = Some("key" -> aKeyMaintainer),
): TransactionBuilder.Create =
TransactionBuilder().create(