LF: retire ValueVersion (#8303)

use TransactionVersion instead.

This is part of #7788

CHANGELOG_BEGIN
CHANGELOG_END
This commit is contained in:
Remy 2020-12-17 17:35:43 +01:00 committed by GitHub
parent 403990dfd7
commit 649f740efd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 237 additions and 428 deletions

View File

@ -186,11 +186,6 @@ final class Conversions(
sys.error( 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.", 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 builder.build
} }

View File

@ -7,15 +7,13 @@ import scala.Ordering.Implicits.infixOrderingOps
/** /**
* [[VersionRange]] represents a range of versions of * [[VersionRange]] represents a range of versions of
* [[com.daml.lf.language.LanguageVersion]], * [[com.daml.lf.language.LanguageVersion]] or
* [[com.daml.lf.transaction.TransactionVersion]], or * [[com.daml.lf.transaction.TransactionVersion]].
* [[com.daml.lf.value.ValueVersion]].
* *
* @param min the minimal version included in the range. * @param min the minimal version included in the range.
* @param max the maximal version included in the range. * @param max the maximal version included in the range.
* @tparam V either [[com.daml.lf.language.LanguageVersion]], * @tparam V either [[com.daml.lf.language.LanguageVersion]] or
* [[com.daml.lf.transaction.TransactionVersion]], or * [[com.daml.lf.transaction.TransactionVersion]].
* [[com.daml.lf.value.ValueVersion]].
*/ */
final case class VersionRange[V]( final case class VersionRange[V](
min: V, min: V,

View File

@ -52,6 +52,7 @@ da_scala_test_suite(
"//daml-lf/language", "//daml-lf/language",
"//daml-lf/parser", "//daml-lf/parser",
"//daml-lf/transaction", "//daml-lf/transaction",
"//daml-lf/transaction-test-lib",
"@maven//:com_google_protobuf_protobuf_java", "@maven//:com_google_protobuf_protobuf_java",
"@maven//:com_storm_enroute_scalameter_core_2_12", "@maven//:com_storm_enroute_scalameter_core_2_12",
"@maven//:org_scalatest_scalatest_2_12", "@maven//:org_scalatest_scalatest_2_12",

View File

@ -7,8 +7,9 @@ import com.daml.lf.data._
import com.daml.lf.engine.Engine import com.daml.lf.engine.Engine
import com.daml.lf.testing.parser.Implicits._ import com.daml.lf.testing.parser.Implicits._
import com.daml.lf.transaction.GlobalKey import com.daml.lf.transaction.GlobalKey
import com.daml.lf.transaction.test.TransactionBuilder
import com.daml.lf.value.Value.ContractId import com.daml.lf.value.Value.ContractId
import com.daml.lf.value.{Value, ValueVersion} import com.daml.lf.value.Value
import org.scalatest.prop.TableDrivenPropertyChecks import org.scalatest.prop.TableDrivenPropertyChecks
import org.scalatest.matchers.should.Matchers import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec import org.scalatest.wordspec.AnyWordSpec
@ -98,7 +99,7 @@ class ContractDiscriminatorFreshnessCheckSpec
private def contractInstance(party: Ref.Party, idx: Int, cids: List[ContractId]) = private def contractInstance(party: Ref.Party, idx: Int, cids: List[ContractId]) =
Value.ContractInst( Value.ContractInst(
tmplId, tmplId,
ValueVersion.assertAsVersionedValue(contractRecord(party, idx, cids)), TransactionBuilder.assertAsVersionedValue(contractRecord(party, idx, cids)),
"Agreement", "Agreement",
) )

View File

@ -30,7 +30,7 @@ import com.daml.lf.speedy.{InitialSeeding, SValue, svalue}
import com.daml.lf.speedy.SValue._ import com.daml.lf.speedy.SValue._
import com.daml.lf.command._ import com.daml.lf.command._
import com.daml.lf.transaction.Node.GenNode import com.daml.lf.transaction.Node.GenNode
import com.daml.lf.value.ValueVersion.assertAsVersionedValue import com.daml.lf.transaction.test.TransactionBuilder.assertAsVersionedValue
import org.scalactic.Equality import org.scalactic.Equality
import org.scalatest.prop.TableDrivenPropertyChecks import org.scalatest.prop.TableDrivenPropertyChecks
import org.scalatest.EitherValues import org.scalatest.EitherValues

View File

@ -8,7 +8,6 @@ import org.typelevel.paiges.Doc._
import com.daml.lf.ledger.EventId import com.daml.lf.ledger.EventId
import com.daml.lf.value.Value import com.daml.lf.value.Value
import Value.{NodeId => _, _} import Value.{NodeId => _, _}
import com.daml.lf.VersionRange
import com.daml.lf.transaction.Node._ import com.daml.lf.transaction.Node._
import com.daml.lf.ledger._ import com.daml.lf.ledger._
import com.daml.lf.data.Ref._ import com.daml.lf.data.Ref._
@ -99,12 +98,6 @@ private[lf] object Pretty {
prettyTypeConName(tid) / prettyTypeConName(tid) /
text("The provided key is") & prettyValue(true)(key) text("The provided key is") & prettyValue(true)(key)
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.. // A minimal pretty-print of an update transaction node, without recursing into child nodes..

View File

@ -3,13 +3,12 @@
package com.daml.lf.speedy package com.daml.lf.speedy
import com.daml.lf.VersionRange
import com.daml.lf.data.Ref._ import com.daml.lf.data.Ref._
import com.daml.lf.data.Time import com.daml.lf.data.Time
import com.daml.lf.ledger.EventId import com.daml.lf.ledger.EventId
import com.daml.lf.ledger.FailedAuthorization import com.daml.lf.ledger.FailedAuthorization
import com.daml.lf.transaction.{GlobalKey, NodeId, Transaction => Tx} import com.daml.lf.transaction.{GlobalKey, NodeId, Transaction => Tx}
import com.daml.lf.value.{Value, ValueVersion} import com.daml.lf.value.Value
import com.daml.lf.value.Value.ContractId import com.daml.lf.value.Value.ContractId
import com.daml.lf.scenario.ScenarioLedger import com.daml.lf.scenario.ScenarioLedger
@ -110,14 +109,6 @@ object SError {
actual: TypeConName, actual: TypeConName,
) extends SErrorDamlException ) 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
/** There was an authorization failure during execution. */ /** There was an authorization failure during execution. */
final case class DamlEFailedAuthorization( final case class DamlEFailedAuthorization(
nid: NodeId, nid: NodeId,

View File

@ -194,17 +194,12 @@ object Repl {
private val seed = nextSeed() private val seed = nextSeed()
val (inputValueVersion, outputTransactionVersions) = val transactionVersions =
if (compilerConfig.allowedLanguageVersions.contains(LV.v1_dev)) if (compilerConfig.allowedLanguageVersions.contains(LV.v1_dev)) {
( transaction.TransactionVersion.DevVersions
value.ValueVersion.DevOutputVersions, } else {
transaction.TransactionVersion.DevVersions, transaction.TransactionVersion.StableVersions
) }
else
(
value.ValueVersion.StableOutputVersions,
transaction.TransactionVersion.StableVersions,
)
def run(expr: Expr): ( def run(expr: Expr): (
Speedy.Machine, Speedy.Machine,

View File

@ -5,12 +5,13 @@ package com.daml.lf
package transaction package transaction
package test package test
import com.daml.lf.data.{BackStack, ImmArray, Ref} import com.daml.lf.data.{BackStack, FrontStack, FrontStackCons, ImmArray, Ref}
import com.daml.lf.transaction.{Transaction => Tx} import com.daml.lf.transaction.{Transaction => Tx}
import com.daml.lf.value.Value.{ContractId, ContractInst} import com.daml.lf.value.Value.{ContractId, ContractInst, VersionedValue}
import com.daml.lf.value.{Value => LfValue} import com.daml.lf.value.{Value => LfValue}
import scala.Ordering.Implicits.infixOrderingOps import scala.Ordering.Implicits.infixOrderingOps
import scala.annotation.tailrec
import scala.collection.immutable.HashMap import scala.collection.immutable.HashMap
final class TransactionBuilder(pkgTxVersion: Ref.PackageId => TransactionVersion = _ => final class TransactionBuilder(pkgTxVersion: Ref.PackageId => TransactionVersion = _ =>
@ -74,14 +75,11 @@ final class TransactionBuilder(pkgTxVersion: Ref.PackageId => TransactionVersion
def newCid: ContractId = ContractId.V1(newHash()) def newCid: ContractId = ContractId.V1(newHash())
private[this] def pkgValVersion(pkgId: Ref.PackageId) =
TransactionVersion.assignValueVersion(pkgTxVersion(pkgId))
def versionContract(contract: ContractInst[Value]): ContractInst[TxValue] = def versionContract(contract: ContractInst[Value]): ContractInst[TxValue] =
ContractInst.map1[Value, TxValue](versionValue(contract.template))(contract) ContractInst.map1[Value, TxValue](transactionValue(contract.template))(contract)
private[this] def versionValue(templateId: Ref.TypeConName): Value => TxValue = private[this] def transactionValue(templateId: Ref.TypeConName): Value => TxValue =
value.Value.VersionedValue(pkgValVersion(templateId.packageId), _) value.Value.VersionedValue(pkgTxVersion(templateId.packageId), _)
def create( def create(
id: String, id: String,
@ -247,4 +245,73 @@ object TransactionBuilder {
val EmptySubmitted: SubmittedTransaction = SubmittedTransaction(Empty) val EmptySubmitted: SubmittedTransaction = SubmittedTransaction(Empty)
val EmptyCommitted: CommittedTransaction = CommittedTransaction(Empty) val EmptyCommitted: CommittedTransaction = CommittedTransaction(Empty)
def assignVersion[Cid](
v0: Value,
supportedVersions: VersionRange[TransactionVersion] = TransactionVersion.StableVersions
): Either[String, TransactionVersion] = {
@tailrec
def go(
currentVersion: TransactionVersion,
values0: FrontStack[Value],
): Either[String, TransactionVersion] = {
import LfValue._
if (currentVersion >= supportedVersions.max) {
Right(currentVersion)
} else {
values0 match {
case FrontStack() => Right(currentVersion)
case FrontStackCons(value, values) =>
value match {
// for things supported since version 1, we do not need to check
case ValueRecord(_, fs) => go(currentVersion, fs.map(v => v._2) ++: values)
case ValueVariant(_, _, arg) => go(currentVersion, arg +: values)
case ValueList(vs) => go(currentVersion, vs.toImmArray ++: values)
case ValueContractId(_) | ValueInt64(_) | ValueText(_) | ValueTimestamp(_) |
ValueParty(_) | ValueBool(_) | ValueDate(_) | ValueUnit | ValueNumeric(_) =>
go(currentVersion, values)
case ValueOptional(x) =>
go(currentVersion, x.fold(values)(_ +: values))
case ValueTextMap(map) =>
go(currentVersion, map.values ++: values)
case ValueEnum(_, _) =>
go(currentVersion, values)
// for things added after version 10, we raise the minimum if present
case ValueGenMap(entries) =>
val newValues = entries.iterator.foldLeft(values) {
case (acc, (key, value)) => key +: value +: acc
}
go(currentVersion max TransactionVersion.minGenMap, newValues)
}
}
}
}
go(supportedVersions.min, FrontStack(v0)) match {
case Right(inferredVersion) if supportedVersions.max < inferredVersion =>
Left(s"inferred version $inferredVersion is not supported")
case res =>
res
}
}
@throws[IllegalArgumentException]
def assertAssignVersion(
v0: Value,
supportedVersions: VersionRange[TransactionVersion] = TransactionVersion.DevVersions,
): TransactionVersion =
data.assertRight(assignVersion(v0, supportedVersions))
def asVersionedValue(
value: Value,
supportedVersions: VersionRange[TransactionVersion] = TransactionVersion.DevVersions,
): Either[String, TxValue] =
assignVersion(value, supportedVersions).map(VersionedValue(_, value))
@throws[IllegalArgumentException]
def assertAsVersionedValue(
value: Value,
supportedVersions: VersionRange[TransactionVersion] = TransactionVersion.DevVersions,
): TxValue =
data.assertRight(asVersionedValue(value, supportedVersions))
} }

View File

@ -22,6 +22,7 @@ import com.daml.lf.transaction.{
VersionedTransaction, VersionedTransaction,
Transaction => Tx Transaction => Tx
} }
import com.daml.lf.transaction.test.TransactionBuilder
import com.daml.lf.value.Value.{NodeId => _, _} import com.daml.lf.value.Value.{NodeId => _, _}
import org.scalacheck.{Arbitrary, Gen} import org.scalacheck.{Arbitrary, Gen}
import Arbitrary.arbitrary import Arbitrary.arbitrary
@ -249,8 +250,8 @@ object ValueGenerators {
def versionedValueGen: Gen[VersionedValue[ContractId]] = def versionedValueGen: Gen[VersionedValue[ContractId]] =
for { for {
value <- valueGen value <- valueGen
minVersion = ValueVersion.assertAssignVersion(value) minVersion = TransactionBuilder.assertAssignVersion(value)
version <- valueVersionGen(minVersion) version <- transactionVersionGen(minVersion)
} yield VersionedValue(version, value) } yield VersionedValue(version, value)
private[lf] val genMaybeEmptyParties: Gen[Set[Party]] = Gen.listOf(party).map(_.toSet) private[lf] val genMaybeEmptyParties: Gen[Set[Party]] = Gen.listOf(party).map(_.toSet)
@ -289,7 +290,7 @@ object ValueGenerators {
*/ */
val malformedCreateNodeGen: Gen[NodeCreate[Value.ContractId]] = { val malformedCreateNodeGen: Gen[NodeCreate[Value.ContractId]] = {
for { for {
version <- transactionVersionGen version <- transactionVersionGen()
coid <- coidGen coid <- coidGen
coinst <- contractInstanceGen coinst <- contractInstanceGen
signatories <- genNonEmptyParties signatories <- genNonEmptyParties
@ -300,7 +301,7 @@ object ValueGenerators {
val fetchNodeGen: Gen[NodeFetch[ContractId]] = { val fetchNodeGen: Gen[NodeFetch[ContractId]] = {
for { for {
version <- transactionVersionGen version <- transactionVersionGen()
coid <- coidGen coid <- coidGen
templateId <- idGen templateId <- idGen
actingParties <- genNonEmptyParties actingParties <- genNonEmptyParties
@ -324,7 +325,7 @@ object ValueGenerators {
/** Makes exercise nodes with some random child IDs. */ /** Makes exercise nodes with some random child IDs. */
val danglingRefExerciseNodeGen: Gen[NodeExercises[NodeId, Value.ContractId]] = { val danglingRefExerciseNodeGen: Gen[NodeExercises[NodeId, Value.ContractId]] = {
for { for {
version <- transactionVersionGen version <- transactionVersionGen()
targetCoid <- coidGen targetCoid <- coidGen
templateId <- idGen templateId <- idGen
choiceId <- nameGen choiceId <- nameGen
@ -465,8 +466,8 @@ object ValueGenerators {
def noDanglingRefGenVersionedTransaction: Gen[VersionedTransaction[NodeId, ContractId]] = { def noDanglingRefGenVersionedTransaction: Gen[VersionedTransaction[NodeId, ContractId]] = {
for { for {
tx <- noDanglingRefGenTransaction tx <- noDanglingRefGenTransaction
txVer <- transactionVersionGen txVer <- transactionVersionGen()
nodeVersionGen = transactionVersionGen.filterNot(_ < txVer) nodeVersionGen = transactionVersionGen().filterNot(_ < txVer)
nodes <- tx.fold(Gen.const(HashMap.empty[NodeId, GenNode[NodeId, ContractId]])) { nodes <- tx.fold(Gen.const(HashMap.empty[NodeId, GenNode[NodeId, ContractId]])) {
case (acc, (nodeId, node)) => case (acc, (nodeId, node)) =>
for { for {
@ -500,14 +501,10 @@ object ValueGenerators {
Gen.frequency((1, Gen.const("")), (10, g)) Gen.frequency((1, Gen.const("")), (10, g))
} }
def valueVersionGen(minVersion: ValueVersion = ValueVersion.minVersion): Gen[ValueVersion] = def transactionVersionGen(
Gen.oneOf(ValueVersion.acceptedVersions.filterNot(_ < minVersion).toSeq) minVersion: TransactionVersion = TransactionVersion.minVersion,
): Gen[TransactionVersion] =
def unsupportedValueVersionGen: Gen[ValueVersion] = Gen.oneOf(TransactionVersion.All.filterNot(_ < minVersion))
stringVersionGen.map(ValueVersion(_)).filterNot(ValueVersion.acceptedVersions.contains)
def transactionVersionGen: Gen[TransactionVersion] =
Gen.oneOf(TransactionVersion.Values)
object Implicits { object Implicits {
implicit val vdateArb: Arbitrary[Time.Date] = Arbitrary(dateGen) implicit val vdateArb: Arbitrary[Time.Date] = Arbitrary(dateGen)

View File

@ -49,7 +49,7 @@ object Node {
private[lf] def updateVersion(version: TransactionVersion): GenNode[Nid, Cid] private[lf] def updateVersion(version: TransactionVersion): GenNode[Nid, Cid]
protected def versionValue[Cid2 >: Cid](v: Value[Cid2]): VersionedValue[Cid2] = protected def versionValue[Cid2 >: Cid](v: Value[Cid2]): VersionedValue[Cid2] =
VersionedValue(TransactionVersion.assignValueVersion(version), v) VersionedValue(version, v)
} }
object GenNode extends CidContainer2[GenNode] { object GenNode extends CidContainer2[GenNode] {

View File

@ -63,7 +63,7 @@ object TransactionCoder {
cidEncoder: ValueCoder.EncodeCid[Cid], cidEncoder: ValueCoder.EncodeCid[Cid],
value: VersionedValue[Cid], value: VersionedValue[Cid],
): Either[EncodeError, ValueOuterClass.VersionedValue] = ): Either[EncodeError, ValueOuterClass.VersionedValue] =
ValueCoder.encodeVersionedValueWithCustomVersion(cidEncoder, value) ValueCoder.encodeVersionedValue(cidEncoder, value)
def decodeValue[Cid]( def decodeValue[Cid](
cidDecoder: ValueCoder.DecodeCid[Cid], cidDecoder: ValueCoder.DecodeCid[Cid],

View File

@ -6,7 +6,7 @@ package transaction
import com.daml.lf.data.ImmArray import com.daml.lf.data.ImmArray
import com.daml.lf.language.LanguageVersion import com.daml.lf.language.LanguageVersion
import com.daml.lf.value.{Value, ValueVersion} import com.daml.lf.value.Value
import scala.collection.immutable.HashMap import scala.collection.immutable.HashMap
@ -22,21 +22,28 @@ object TransactionVersion {
case object V10 extends TransactionVersion("10", 10) case object V10 extends TransactionVersion("10", 10)
case object VDev extends TransactionVersion("dev", Int.MaxValue) case object VDev extends TransactionVersion("dev", Int.MaxValue)
val Values = List(V10, VDev) val All = List(V10, VDev)
private[lf] implicit val Ordering: scala.Ordering[TransactionVersion] = scala.Ordering.by(_.index) private[lf] implicit val Ordering: scala.Ordering[TransactionVersion] = scala.Ordering.by(_.index)
private[this] val stringMapping = Values.iterator.map(v => v.protoValue -> v).toMap private[this] val stringMapping = All.iterator.map(v => v.protoValue -> v).toMap
def fromString(vs: String): Either[String, TransactionVersion] = def fromString(vs: String): Either[String, TransactionVersion] =
stringMapping.get(vs).toRight(s"Unsupported transaction version $vs") stringMapping.get(vs) match {
case Some(value) => Right(value)
case None =>
Left(s"Unsupported transaction version '$vs'")
}
def assertFromString(vs: String): TransactionVersion = def assertFromString(vs: String): TransactionVersion =
data.assertRight(fromString(vs)) data.assertRight(fromString(vs))
val minVersion = Values.min val minVersion: TransactionVersion = All.min
private[transaction] val minChoiceObservers = VDev def maxVersion: TransactionVersion = VDev
private[transaction] val minNodeVersion = VDev
private[lf] val minGenMap = VDev
private[lf] val minChoiceObservers = VDev
private[lf] val minNodeVersion = VDev
private[lf] val assignNodeVersion: LanguageVersion => TransactionVersion = { private[lf] val assignNodeVersion: LanguageVersion => TransactionVersion = {
import LanguageVersion._ import LanguageVersion._
@ -48,13 +55,6 @@ object TransactionVersion {
) )
} }
private[lf] val assignValueVersion: TransactionVersion => ValueVersion = {
Map(
V10 -> ValueVersion("6"),
VDev -> ValueVersion("dev"),
)
}
private[lf] def asVersionedTransaction( private[lf] def asVersionedTransaction(
roots: ImmArray[NodeId], roots: ImmArray[NodeId],
nodes: HashMap[NodeId, Node.GenNode[NodeId, Value.ContractId]], nodes: HashMap[NodeId, Node.GenNode[NodeId, Value.ContractId]],

View File

@ -7,6 +7,7 @@ package value
import com.daml.lf.crypto.Hash import com.daml.lf.crypto.Hash
import com.daml.lf.data.Ref.{Identifier, Name} import com.daml.lf.data.Ref.{Identifier, Name}
import com.daml.lf.data._ import com.daml.lf.data._
import com.daml.lf.transaction.TransactionVersion
import data.ScalazEqual._ import data.ScalazEqual._
import scala.annotation.tailrec import scala.annotation.tailrec
@ -202,7 +203,7 @@ object Value extends CidContainer1[Value] {
*/ */
val MAXIMUM_NESTING: Int = 100 val MAXIMUM_NESTING: Int = 100
final case class VersionedValue[+Cid](version: ValueVersion, value: Value[Cid]) final case class VersionedValue[+Cid](version: TransactionVersion, value: Value[Cid])
extends CidContainer[VersionedValue[Cid]] { extends CidContainer[VersionedValue[Cid]] {
override protected def self: this.type = this override protected def self: this.type = this

View File

@ -30,8 +30,6 @@ object ValueCoder {
object DecodeError extends (String => DecodeError) { object DecodeError extends (String => DecodeError) {
private[lf] def apply(version: TransactionVersion, isTooOldFor: String): DecodeError = private[lf] def apply(version: TransactionVersion, isTooOldFor: String): DecodeError =
DecodeError(s"transaction version ${version.protoValue} is too old to support $isTooOldFor") DecodeError(s"transaction version ${version.protoValue} is too old to support $isTooOldFor")
private[lf] def apply(version: ValueVersion, isTooOldFor: String): DecodeError =
DecodeError(s"value version ${version.protoValue} is too old to support $isTooOldFor")
} }
/** /**
@ -43,8 +41,6 @@ object ValueCoder {
object EncodeError extends (String => EncodeError) { object EncodeError extends (String => EncodeError) {
private[lf] def apply(version: TransactionVersion, isTooOldFor: String): EncodeError = private[lf] def apply(version: TransactionVersion, isTooOldFor: String): EncodeError =
EncodeError(s"transaction version ${version.protoValue} is too old to support $isTooOldFor") EncodeError(s"transaction version ${version.protoValue} is too old to support $isTooOldFor")
private[lf] def apply(version: ValueVersion, isTooOldFor: String): EncodeError =
EncodeError(s"value version ${version.protoValue} is too old to support $isTooOldFor")
} }
abstract class EncodeCid[-Cid] private[lf] { abstract class EncodeCid[-Cid] private[lf] {
@ -129,20 +125,27 @@ object ValueCoder {
} yield Identifier(pkgId, QualifiedName(module, name)) } yield Identifier(pkgId, QualifiedName(module, name))
private def decodeVersion(vs: String): Either[DecodeError, ValueVersion] = // For backward compatibility reasons, V10 is encoded as "6" when used inside a
ValueVersion // proto.VersionedValue
.isAcceptedVersion(vs) private[this] def encodeValueVersion(version: TransactionVersion): String =
.fold[Either[DecodeError, ValueVersion]](Left(DecodeError(s"Unsupported value version $vs")))( if (version == TransactionVersion.V10) {
v => Right(v), "6"
) } else {
version.protoValue
}
private[this] def decodeValueVersion(vs: String): Either[DecodeError, TransactionVersion] =
vs match {
case "6" => Right(TransactionVersion.V10)
case "10" => Left(DecodeError("Unsupported value version 10"))
case _ => TransactionVersion.fromString(vs).left.map(DecodeError)
}
/** /**
* Reads a serialized protobuf versioned value, * Reads a serialized protobuf versioned value,
* checks if the value version is currently supported and * checks if the value version is currently supported and
* converts the value to the type usable by engine/interpreter. * converts the value to the type usable by engine/interpreter.
* *
* Supported value versions configured in [[ValueVersions]].
*
* @param protoValue0 the value to be read * @param protoValue0 the value to be read
* @param decodeCid a function to decode stringified contract ids * @param decodeCid a function to decode stringified contract ids
* @tparam Cid ContractId type * @tparam Cid ContractId type
@ -153,7 +156,7 @@ object ValueCoder {
protoValue0: proto.VersionedValue, protoValue0: proto.VersionedValue,
): Either[DecodeError, VersionedValue[Cid]] = ): Either[DecodeError, VersionedValue[Cid]] =
for { for {
version <- decodeVersion(protoValue0.getVersion) version <- decodeValueVersion(protoValue0.getVersion)
value <- decodeValue(decodeCid, version, protoValue0.getValue) value <- decodeValue(decodeCid, version, protoValue0.getValue)
} yield VersionedValue(version, value) } yield VersionedValue(version, value)
@ -163,46 +166,6 @@ object ValueCoder {
): Either[DecodeError, Value[Cid]] = ): Either[DecodeError, Value[Cid]] =
decodeVersionedValue(decodeCid, protoValue0) map (_.value) decodeVersionedValue(decodeCid, protoValue0) map (_.value)
/**
* Serializes [[Value]] to protobuf, library decides which [[ValueVersion]] to assign.
* See [[ValueVersion.assignVersion]].
*
* @param value value to be written
* @param encodeCid a function to stringify contractIds (it's better to be invertible)
* @tparam Cid ContractId type
* @return protocol buffer serialized values
*/
def encodeVersionedValue[Cid](
encodeCid: EncodeCid[Cid],
value: Value[Cid],
supportedVersions: VersionRange[ValueVersion],
): Either[EncodeError, proto.VersionedValue] =
ValueVersion
.assignVersion(value, supportedVersions)
.fold(
err => Left(EncodeError(err)),
version => encodeVersionedValueWithCustomVersion(encodeCid, VersionedValue(version, value)),
)
/**
* Serializes [[VersionedValue]] to protobuf, caller provides the [[ValueVersion]].
*
* @param versionedValue value to be written
* @param encodeCid a function to stringify contractIds (it's better to be invertible)
* @tparam Cid ContractId type
* @return protocol buffer serialized values
*/
def encodeVersionedValueWithCustomVersion[Cid](
encodeCid: EncodeCid[Cid],
versionedValue: VersionedValue[Cid],
): Either[EncodeError, proto.VersionedValue] =
for {
value <- encodeValue(encodeCid, versionedValue.version, versionedValue.value)
} yield {
val builder = proto.VersionedValue.newBuilder()
builder.setVersion(versionedValue.version.protoValue).setValue(value).build()
}
/** /**
* Method to read a serialized protobuf value * Method to read a serialized protobuf value
* to engine/interpreter usable Value type * to engine/interpreter usable Value type
@ -214,7 +177,7 @@ object ValueCoder {
*/ */
def decodeValue[Cid]( def decodeValue[Cid](
decodeCid: DecodeCid[Cid], decodeCid: DecodeCid[Cid],
valueVersion: ValueVersion, version: TransactionVersion,
protoValue0: proto.Value, protoValue0: proto.Value,
): Either[DecodeError, Value[Cid]] = { ): Either[DecodeError, Value[Cid]] = {
case class Err(msg: String) extends Throwable(null, null, true, false) case class Err(msg: String) extends Throwable(null, null, true, false)
@ -227,9 +190,9 @@ object ValueCoder {
identity, identity,
) )
def assertSince(minVersion: ValueVersion, description: => String) = def assertSince(minVersion: TransactionVersion, description: => String) =
if (valueVersion < minVersion) if (version < minVersion)
throw Err(s"$description is not supported by value version $valueVersion") throw Err(s"$description is not supported by value version $version")
def go(nesting: Int, protoValue: proto.Value): Value[Cid] = { def go(nesting: Int, protoValue: proto.Value): Value[Cid] = {
if (nesting > MAXIMUM_NESTING) { if (nesting > MAXIMUM_NESTING) {
@ -343,7 +306,7 @@ object ValueCoder {
ValueTextMap(map) ValueTextMap(map)
case proto.Value.SumCase.GEN_MAP => case proto.Value.SumCase.GEN_MAP =>
assertSince(ValueVersion.minGenMap, "Value.SumCase.MAP") assertSince(TransactionVersion.minGenMap, "Value.SumCase.MAP")
val genMap = protoValue.getGenMap.getEntriesList.asScala.map(entry => val genMap = protoValue.getGenMap.getEntriesList.asScala.map(entry =>
go(newNesting, entry.getKey) -> go(newNesting, entry.getValue)) go(newNesting, entry.getKey) -> go(newNesting, entry.getValue))
ValueGenMap(ImmArray(genMap)) ValueGenMap(ImmArray(genMap))
@ -361,6 +324,32 @@ object ValueCoder {
} }
} }
/**
* Serializes [[VersionedValue]] to protobuf.
*
* @param versionedValue value to be written
* @param encodeCid a function to stringify contractIds (it's better to be invertible)
* @tparam Cid ContractId type
* @return protocol buffer serialized values
*/
def encodeVersionedValue[Cid](
encodeCid: EncodeCid[Cid],
versionedValue: VersionedValue[Cid],
): Either[EncodeError, proto.VersionedValue] =
encodeVersionedValue(encodeCid, versionedValue.version, versionedValue.value)
def encodeVersionedValue[Cid](
encodeCid: EncodeCid[Cid],
version: TransactionVersion,
value: Value[Cid],
): Either[EncodeError, proto.VersionedValue] =
for {
protoValue <- encodeValue(encodeCid, version, value)
} yield {
val builder = proto.VersionedValue.newBuilder()
builder.setVersion(encodeValueVersion(version)).setValue(protoValue).build()
}
/** /**
* Serialize a Value to protobuf * Serialize a Value to protobuf
* *
@ -372,12 +361,12 @@ object ValueCoder {
*/ */
def encodeValue[Cid]( def encodeValue[Cid](
encodeCid: EncodeCid[Cid], encodeCid: EncodeCid[Cid],
valueVersion: ValueVersion, valueVersion: TransactionVersion,
v0: Value[Cid], v0: Value[Cid],
): Either[EncodeError, proto.Value] = { ): Either[EncodeError, proto.Value] = {
case class Err(msg: String) extends Throwable(null, null, true, false) case class Err(msg: String) extends Throwable(null, null, true, false)
def assertSince(minVersion: ValueVersion, description: => String) = def assertSince(minVersion: TransactionVersion, description: => String) =
if (valueVersion < minVersion) if (valueVersion < minVersion)
throw Err(s"$description is not supported by value version $valueVersion") throw Err(s"$description is not supported by value version $valueVersion")
@ -469,7 +458,7 @@ object ValueCoder {
builder.setMap(protoMap).build() builder.setMap(protoMap).build()
case ValueGenMap(entries) => case ValueGenMap(entries) =>
assertSince(ValueVersion.minGenMap, "Value.SumCase.MAP") assertSince(TransactionVersion.minGenMap, "Value.SumCase.MAP")
val protoMap = proto.GenMap.newBuilder() val protoMap = proto.GenMap.newBuilder()
entries.foreach { entries.foreach {
case (key, value) => case (key, value) =>
@ -494,18 +483,6 @@ object ValueCoder {
} }
} }
// The codomain and domain of the below functions are subject to change
// without warning or type change; they are stable with respect to
// each other and nothing else. As such, they are unsafe for
// general usage
private[value] def valueToBytes[Cid](
encodeCid: EncodeCid[Cid],
v: Value[Cid],
supportedVersions: VersionRange[ValueVersion] = ValueVersion.DevOutputVersions,
): Either[EncodeError, Array[Byte]] =
encodeVersionedValue(encodeCid, v, supportedVersions).map(_.toByteArray)
private[value] def valueFromBytes[Cid]( private[value] def valueFromBytes[Cid](
decodeCid: DecodeCid[Cid], decodeCid: DecodeCid[Cid],
bytes: Array[Byte], bytes: Array[Byte],

View File

@ -1,108 +0,0 @@
// 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 value
import com.daml.lf.value.Value._
import com.daml.lf.data.{FrontStack, FrontStackCons, ImmArray}
import scalaz.NonEmptyList
import scala.Ordering.Implicits.infixOrderingOps
import scala.annotation.tailrec
final case class ValueVersion(protoValue: String)
/**
* Currently supported versions of the DAML-LF value specification.
*/
object ValueVersion
extends LfVersions(
versionsAscending = NonEmptyList(new ValueVersion("6"), new ValueVersion("dev")))(
_.protoValue,
) {
private[lf] implicit val Ordering: Ordering[ValueVersion] = mkOrdering
private[value] val minVersion = ValueVersion("6")
private[value] val minGenMap = ValueVersion("dev")
private[value] val minContractIdV1 = ValueVersion("dev")
// Older versions are deprecated https://github.com/digital-asset/daml/issues/5220
private[lf] val StableOutputVersions: VersionRange[ValueVersion] =
VersionRange(ValueVersion("6"), ValueVersion("6"))
private[lf] val DevOutputVersions: VersionRange[ValueVersion] =
StableOutputVersions.copy(max = acceptedVersions.last)
private[lf] def assignVersion[Cid](
v0: Value[Cid],
supportedVersions: VersionRange[ValueVersion] = StableOutputVersions,
): Either[String, ValueVersion] = {
@tailrec
def go(
currentVersion: ValueVersion,
values0: FrontStack[Value[Cid]],
): Either[String, ValueVersion] = {
if (currentVersion == maxVersion) {
Right(currentVersion)
} else {
values0 match {
case FrontStack() => Right(currentVersion)
case FrontStackCons(value, values) =>
value match {
// for things supported since version 1, we do not need to check
case ValueRecord(_, fs) => go(currentVersion, fs.map(v => v._2) ++: values)
case ValueVariant(_, _, arg) => go(currentVersion, arg +: values)
case ValueList(vs) => go(currentVersion, vs.toImmArray ++: values)
case ValueContractId(_) | ValueInt64(_) | ValueText(_) | ValueTimestamp(_) |
ValueParty(_) | ValueBool(_) | ValueDate(_) | ValueUnit =>
go(currentVersion, values)
case ValueNumeric(_) =>
go(currentVersion, values)
case ValueOptional(x) =>
go(currentVersion, ImmArray(x.toList) ++: values)
case ValueTextMap(map) =>
go(currentVersion, map.values ++: values)
// for things added after version 6, we raise the minimum if present
case ValueGenMap(entries) =>
val newValues = entries.iterator.foldLeft(values) {
case (acc, (key, value)) => key +: value +: acc
}
go(currentVersion max minGenMap, newValues)
case ValueEnum(_, _) =>
go(currentVersion, values)
}
}
}
}
go(supportedVersions.min, FrontStack(v0)) match {
case Right(inferredVersion) if supportedVersions.max < inferredVersion =>
Left(s"inferred version $inferredVersion is not supported")
case res =>
res
}
}
@throws[IllegalArgumentException]
private[lf] def assertAssignVersion[Cid](
v0: Value[Cid],
supportedVersions: VersionRange[ValueVersion] = DevOutputVersions,
): ValueVersion =
data.assertRight(assignVersion(v0, supportedVersions))
private[lf] def asVersionedValue[Cid](
value: Value[Cid],
supportedVersions: VersionRange[ValueVersion] = DevOutputVersions,
): Either[String, VersionedValue[Cid]] =
assignVersion(value, supportedVersions).map(VersionedValue(_, value))
@throws[IllegalArgumentException]
private[lf] def assertAsVersionedValue[Cid](
value: Value[Cid],
supportedVersions: VersionRange[ValueVersion] = DevOutputVersions,
): VersionedValue[Cid] =
data.assertRight(asVersionedValue(value, supportedVersions))
}

View File

@ -48,7 +48,7 @@ class TransactionCoderSpec
} }
"do NodeCreate" in { "do NodeCreate" in {
forAll(malformedCreateNodeGen, transactionVersionGen, transactionVersionGen) { forAll(malformedCreateNodeGen, transactionVersionGen(), transactionVersionGen()) {
(createNode, version1, version2) => (createNode, version1, version2) =>
val (nodeVersion, txVersion) = inIncreasingOrder(version1, version2) val (nodeVersion, txVersion) = inIncreasingOrder(version1, version2)
val versionedNode = createNode.updateVersion(nodeVersion) val versionedNode = createNode.updateVersion(nodeVersion)
@ -76,7 +76,7 @@ class TransactionCoderSpec
} }
"do NodeFetch" in { "do NodeFetch" in {
forAll(fetchNodeGen, transactionVersionGen, transactionVersionGen) { forAll(fetchNodeGen, transactionVersionGen(), transactionVersionGen()) {
(fetchNode, version1, version2) => (fetchNode, version1, version2) =>
val (nodeVersion, txVersion) = inIncreasingOrder(version1, version2) val (nodeVersion, txVersion) = inIncreasingOrder(version1, version2)
@ -107,7 +107,7 @@ class TransactionCoderSpec
} }
"do NodeExercises" in { "do NodeExercises" in {
forAll(danglingRefExerciseNodeGen, transactionVersionGen, transactionVersionGen) { forAll(danglingRefExerciseNodeGen, transactionVersionGen(), transactionVersionGen()) {
(exerciseNode, version1, version2) => (exerciseNode, version1, version2) =>
val (nodeVersion, txVersion) = inIncreasingOrder(version1, version2) val (nodeVersion, txVersion) = inIncreasingOrder(version1, version2)
@ -176,7 +176,7 @@ class TransactionCoderSpec
} }
forAll(noDanglingRefGenTransaction, minSuccessful(50)) { tx => forAll(noDanglingRefGenTransaction, minSuccessful(50)) { tx =>
forAll(transactionVersionGen, transactionVersionGen, minSuccessful(20)) { forAll(transactionVersionGen(), transactionVersionGen(), minSuccessful(20)) {
(txVer1, txVer2) => (txVer1, txVer2) =>
import scalaz.std.tuple._ import scalaz.std.tuple._
import scalaz.syntax.bifunctor._ import scalaz.syntax.bifunctor._
@ -242,7 +242,7 @@ class TransactionCoderSpec
ValueCoder.CidDecoder, ValueCoder.CidDecoder,
encodedTxWithBadTxVer, encodedTxWithBadTxVer,
) shouldEqual Left( ) shouldEqual Left(
DecodeError(s"Unsupported transaction version $badTxVer"), DecodeError(s"Unsupported transaction version '$badTxVer'"),
) )
} }
} }
@ -330,7 +330,11 @@ class TransactionCoderSpec
"fail if try to encode a node in a version newer than the transaction" in { "fail if try to encode a node in a version newer than the transaction" in {
forAll(danglingRefGenNode, transactionVersionGen, transactionVersionGen, minSuccessful(10)) { forAll(
danglingRefGenNode,
transactionVersionGen(),
transactionVersionGen(),
minSuccessful(10)) {
case ((nodeId, node), version1, version2) => case ((nodeId, node), version1, version2) =>
whenever(version1 != version2) { whenever(version1 != version2) {
val (txVersion, nodeVersion) = inIncreasingOrder(version1, version2) val (txVersion, nodeVersion) = inIncreasingOrder(version1, version2)
@ -386,8 +390,8 @@ class TransactionCoderSpec
forAll( forAll(
danglingRefGenNode, danglingRefGenNode,
transactionVersionGen.filter(_ != V10), transactionVersionGen().filter(_ != V10),
transactionVersionGen.filter(_ != V10), transactionVersionGen().filter(_ != V10),
minSuccessful(10)) { minSuccessful(10)) {
case ((nodeId, node), version1, version2) => case ((nodeId, node), version1, version2) =>
whenever(version1 != version2) { whenever(version1 != version2) {

View File

@ -158,7 +158,7 @@ class TransactionSpec extends AnyFreeSpec with Matchers with ScalaCheckDrivenPro
} }
"fail if version is different" in { "fail if version is different" in {
val versions = TransactionVersion.Values val versions = TransactionVersion.All
def diffVersion(v: TransactionVersion) = { def diffVersion(v: TransactionVersion) = {
val randomVersion = versions(Random.nextInt(versions.length - 1)) val randomVersion = versions(Random.nextInt(versions.length - 1))
if (randomVersion != v) randomVersion else versions.last if (randomVersion != v) randomVersion else versions.last

View File

@ -4,7 +4,6 @@
package com.daml.lf package com.daml.lf
package transaction package transaction
import value.ValueVersion
import com.daml.lf.language.LanguageVersion import com.daml.lf.language.LanguageVersion
import org.scalatest.prop.TableDrivenPropertyChecks import org.scalatest.prop.TableDrivenPropertyChecks
import org.scalatest.matchers.should.Matchers import org.scalatest.matchers.should.Matchers
@ -32,21 +31,4 @@ class TransactionVersionSpec extends AnyWordSpec with Matchers with TableDrivenP
} }
} }
"TransactionVersion.assignValueVersion" should {
"be stable" in {
val testCases = Table(
"input" -> "output",
V10 -> ValueVersion("6"),
VDev -> ValueVersion("dev")
)
forEvery(testCases) { (input, expectedOutput) =>
TransactionVersion.assignValueVersion(input) shouldBe expectedOutput
}
}
}
} }

View File

@ -6,8 +6,8 @@ package com.daml.lf.value
import com.daml.lf.EitherAssertions import com.daml.lf.EitherAssertions
import com.daml.lf.data.Ref.Party import com.daml.lf.data.Ref.Party
import com.daml.lf.data._ import com.daml.lf.data._
import com.daml.lf.transaction.TransactionVersion
import com.daml.lf.value.Value._ import com.daml.lf.value.Value._
import com.daml.lf.value.ValueCoder.DecodeError
import com.daml.lf.value.{ValueOuterClass => proto} import com.daml.lf.value.{ValueOuterClass => proto}
import org.scalacheck.Shrink import org.scalacheck.Shrink
import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks
@ -25,13 +25,6 @@ class ValueCoderSpec
implicit val noStringShrink: Shrink[String] = Shrink.shrinkAny[String] implicit val noStringShrink: Shrink[String] = Shrink.shrinkAny[String]
private[this] val lastDecimalVersion = ValueVersion("5")
private[this] val firstNumericVersion = ValueVersion("6")
private[this] val defaultValueVersion = ValueVersion.acceptedVersions.lastOption getOrElse sys
.error("there are no allowed versions! impossible! but could it be?")
implicit override val generatorDrivenConfig: PropertyCheckConfiguration = implicit override val generatorDrivenConfig: PropertyCheckConfiguration =
PropertyCheckConfiguration(minSuccessful = 1000) PropertyCheckConfiguration(minSuccessful = 1000)
@ -39,38 +32,14 @@ class ValueCoderSpec
"do Int" in { "do Int" in {
forAll("Int64 (Long) invariant") { i: Long => forAll("Int64 (Long) invariant") { i: Long =>
val value = ValueInt64(i) val value = ValueInt64(i)
testRoundTrip(value) testRoundTrip(TransactionVersion.minVersion, value)
} }
} }
"do Bool" in { "do Bool" in {
forAll("Bool invariant") { b: Boolean => forAll("Bool invariant") { b: Boolean =>
val value = ValueBool(b) val value = ValueBool(b)
testRoundTrip(value) testRoundTrip(TransactionVersion.minVersion, value)
}
}
"do Decimal" in {
forAll("Decimal (BigDecimal) invariant") { d: BigDecimal =>
// we are filtering on decimals invariant under string conversion
whenever(Decimal.fromBigDecimal(d).isRight) {
val Right(dec) = Decimal.fromBigDecimal(d)
val value = ValueNumeric(dec)
val recoveredDecimal = ValueCoder.decodeValue[ContractId](
ValueCoder.CidDecoder,
lastDecimalVersion,
assertRight(
ValueCoder
.encodeValue[ContractId](ValueCoder.CidEncoder, lastDecimalVersion, value),
),
) match {
case Right(ValueNumeric(d)) => d
case x => fail(s"should have got a decimal back, got $x")
}
Numeric.toUnscaledString(value.value) shouldEqual Numeric.toUnscaledString(
recoveredDecimal,
)
}
} }
} }
@ -85,10 +54,13 @@ class ValueCoderSpec
val value = ValueNumeric(dec) val value = ValueNumeric(dec)
val recoveredDecimal = ValueCoder.decodeValue[ContractId]( val recoveredDecimal = ValueCoder.decodeValue[ContractId](
ValueCoder.CidDecoder, ValueCoder.CidDecoder,
firstNumericVersion, TransactionVersion.minVersion,
assertRight( assertRight(
ValueCoder ValueCoder
.encodeValue[ContractId](ValueCoder.CidEncoder, firstNumericVersion, value), .encodeValue[ContractId](
ValueCoder.CidEncoder,
TransactionVersion.minVersion,
value),
), ),
) match { ) match {
case Right(ValueNumeric(x)) => x case Right(ValueNumeric(x)) => x
@ -104,98 +76,77 @@ class ValueCoderSpec
"do Text" in { "do Text" in {
forAll("Text (String) invariant") { t: String => forAll("Text (String) invariant") { t: String =>
val value = ValueText(t) val value = ValueText(t)
testRoundTrip(value) testRoundTrip(TransactionVersion.minVersion, value)
} }
} }
"do Party" in { "do Party" in {
forAll(party) { p: Party => forAll(party) { p: Party =>
val value = ValueParty(p) val value = ValueParty(p)
testRoundTrip(value) testRoundTrip(TransactionVersion.minVersion, value)
} }
} }
"do TimeStamp" in { "do TimeStamp" in {
forAll(timestampGen) { t: Time.Timestamp => // TODO: fails with Longs forAll(timestampGen) { t: Time.Timestamp => // TODO: fails with Longs
testRoundTrip(ValueTimestamp(t)) testRoundTrip(TransactionVersion.minVersion, ValueTimestamp(t))
} }
} }
"do Date" in { "do Date" in {
forAll(dateGen) { d: Time.Date => forAll(dateGen) { d: Time.Date =>
testRoundTrip(ValueDate(d)) testRoundTrip(TransactionVersion.minVersion, ValueDate(d))
} }
} }
"do ContractId" in { "do ContractId" in {
forAll(coidValueGen) { v: Value[ContractId] => forAll(coidValueGen) { v: Value[ContractId] =>
testRoundTrip(v) testRoundTrip(TransactionVersion.minVersion, v)
} }
} }
"do ContractId V0 in any ValueVersion" in forAll(coidValueGen, valueVersionGen())( "do ContractId V0 in any ValueVersion" in forAll(coidValueGen, transactionVersionGen())(
testRoundTripWithVersion
)
"do ContractId in any ValueVersion" in forAll(
coidValueGen,
valueVersionGen(ValueVersion.minContractIdV1))(
testRoundTripWithVersion testRoundTripWithVersion
) )
"do lists" in { "do lists" in {
forAll(valueListGen) { v: ValueList[ContractId] => forAll(valueListGen) { v: ValueList[ContractId] =>
testRoundTrip(v) testRoundTrip(TransactionVersion.minVersion, v)
} }
} }
"do optionals" in { "do optionals" in {
forAll(valueOptionalGen) { v: ValueOptional[ContractId] => forAll(valueOptionalGen) { v: ValueOptional[ContractId] =>
testRoundTrip(v) testRoundTrip(TransactionVersion.minVersion, v)
} }
} }
"do maps" in { "do maps" in {
forAll(valueMapGen) { v: ValueTextMap[ContractId] => forAll(valueMapGen) { v: ValueTextMap[ContractId] =>
testRoundTrip(v) testRoundTrip(TransactionVersion.minVersion, v)
} }
} }
"do genMaps" in { "do genMaps" in {
forAll(valueGenMapGen) { v: ValueGenMap[ContractId] => forAll(valueGenMapGen) { v: ValueGenMap[ContractId] =>
testRoundTrip(v) testRoundTrip(TransactionVersion.minGenMap, v)
} }
} }
"do variant" in { "do variant" in {
forAll(variantGen) { v: ValueVariant[ContractId] => forAll(variantGen) { v: ValueVariant[ContractId] =>
testRoundTrip(v) testRoundTrip(TransactionVersion.minVersion, v)
} }
} }
"do record" in { "do record" in {
forAll(recordGen) { v: ValueRecord[ContractId] => forAll(recordGen) { v: ValueRecord[ContractId] =>
testRoundTrip(v) testRoundTrip(TransactionVersion.minVersion, v)
} }
} }
"do unit" in { "do unit" in {
val recovered = ValueCoder.decodeValue( testRoundTrip(TransactionVersion.minVersion, ValueUnit)
ValueCoder.CidDecoder,
defaultValueVersion,
assertRight(
ValueCoder.encodeValue[ContractId](ValueCoder.CidEncoder, defaultValueVersion, ValueUnit),
),
)
val fromToBytes = ValueCoder.valueFromBytes(
ValueCoder.CidDecoder,
ValueCoder
.valueToBytes(ValueCoder.CidEncoder, ValueUnit)
.toOption
.get,
)
Right(ValueUnit) shouldEqual fromToBytes
recovered shouldEqual Right(ValueUnit)
} }
"do identifier" in { "do identifier" in {
@ -204,81 +155,45 @@ class ValueCoderSpec
} }
} }
"do identifier with supported override version" in forAll(idGen, valueVersionGen()) { (i, _) => "do identifier with supported override version" in forAll(idGen, transactionVersionGen()) {
val ei = ValueCoder.encodeIdentifier(i) (i, _) =>
ValueCoder.decodeIdentifier(ei) shouldEqual Right(i) val ei = ValueCoder.encodeIdentifier(i)
ValueCoder.decodeIdentifier(ei) shouldEqual Right(i)
} }
"do versioned value with supported override version" in forAll(versionedValueGen) { "do versioned value with supported override version" in forAll(versionedValueGen) {
case VersionedValue(version, value) => testRoundTripWithVersion(value, version) case VersionedValue(version, value) => testRoundTripWithVersion(value, version)
} }
"do versioned value with assigned version" in forAll(valueGen) { v: Value[ContractId] =>
testRoundTripWithVersion(v, ValueVersion.assertAssignVersion(v))
}
"versioned value should pass serialization if unsupported override version provided" in
forAll(valueGen, unsupportedValueVersionGen) {
(value: Value[ContractId], badVer: ValueVersion) =>
ValueVersion.acceptedVersions.contains(badVer) shouldBe false
val actual: proto.VersionedValue = assertRight(
ValueCoder
.encodeVersionedValueWithCustomVersion(
ValueCoder.CidEncoder,
VersionedValue(badVer, value),
),
)
actual.getVersion shouldEqual badVer.protoValue
}
"versioned value should fail deserialization if version is not supported" in
forAll(valueGen, unsupportedValueVersionGen) {
(value: Value[ContractId], badVer: ValueVersion) =>
ValueVersion.acceptedVersions.contains(badVer) shouldBe false
val protoWithUnsupportedVersion: proto.VersionedValue =
assertRight(
ValueCoder.encodeVersionedValueWithCustomVersion(
ValueCoder.CidEncoder,
VersionedValue(badVer, value),
),
)
protoWithUnsupportedVersion.getVersion shouldEqual badVer.protoValue
val actual: Either[DecodeError, VersionedValue[ContractId]] =
ValueCoder.decodeVersionedValue(ValueCoder.CidDecoder, protoWithUnsupportedVersion)
actual shouldEqual Left(DecodeError(s"Unsupported value version ${badVer.protoValue}"))
}
} }
def testRoundTrip(value: Value[ContractId]): Assertion = { def testRoundTrip(version: TransactionVersion, value: Value[ContractId]): Assertion = {
val recovered = ValueCoder.decodeValue( val recovered = ValueCoder.decodeValue(
ValueCoder.CidDecoder, ValueCoder.CidDecoder,
defaultValueVersion, version,
assertRight( assertRight(ValueCoder.encodeValue[ContractId](ValueCoder.CidEncoder, version, value)),
ValueCoder.encodeValue[ContractId](ValueCoder.CidEncoder, defaultValueVersion, value)),
) )
val bytes =
assertRight(
ValueCoder.encodeVersionedValue(
ValueCoder.CidEncoder,
VersionedValue(version, value),
)
).toByteArray
val fromToBytes = ValueCoder.valueFromBytes( val fromToBytes = ValueCoder.valueFromBytes(
ValueCoder.CidDecoder, ValueCoder.CidDecoder,
assertRight( bytes,
ValueCoder
.valueToBytes[ContractId](ValueCoder.CidEncoder, value)),
) )
Right(value) shouldEqual recovered Right(value) shouldEqual recovered
Right(value) shouldEqual fromToBytes Right(value) shouldEqual fromToBytes
} }
def testRoundTripWithVersion(value0: Value[ContractId], version: ValueVersion): Assertion = { def testRoundTripWithVersion(
ValueVersion.acceptedVersions.contains(version) shouldBe true value0: Value[ContractId],
version: TransactionVersion): Assertion = {
val encoded: proto.VersionedValue = assertRight( val encoded: proto.VersionedValue = assertRight(
ValueCoder ValueCoder
.encodeVersionedValueWithCustomVersion( .encodeVersionedValue(ValueCoder.CidEncoder, VersionedValue(version, value0)),
ValueCoder.CidEncoder,
VersionedValue(version, value0)),
) )
val decoded: VersionedValue[ContractId] = assertRight( val decoded: VersionedValue[ContractId] = assertRight(
ValueCoder.decodeVersionedValue(ValueCoder.CidDecoder, encoded), ValueCoder.decodeVersionedValue(ValueCoder.CidDecoder, encoded),

View File

@ -7,6 +7,7 @@ package value
import data.{Bytes, ImmArray, Ref} import data.{Bytes, ImmArray, Ref}
import Value._ import Value._
import Ref.{Identifier, Name} import Ref.{Identifier, Name}
import com.daml.lf.transaction.TransactionVersion
import test.ValueGenerators.{coidGen, idGen, nameGen} import test.ValueGenerators.{coidGen, idGen, nameGen}
import test.TypedValueGenerators.{RNil, genAddend, ValueAddend => VA} import test.TypedValueGenerators.{RNil, genAddend, ValueAddend => VA}
import com.daml.scalatest.Unnatural import com.daml.scalatest.Unnatural
@ -57,10 +58,10 @@ class ValueSpec
"does not bump version when" - { "does not bump version when" - {
"ensureNoCid is used " in { "ensureNoCid is used " in {
val value = VersionedValue[ContractId](ValueVersion.minVersion, ValueUnit) val value = VersionedValue[ContractId](TransactionVersion.minVersion, ValueUnit)
val contract = ContractInst(tmplId, value, "agreed") val contract = ContractInst(tmplId, value, "agreed")
value.ensureNoCid.map(_.version) shouldBe Right(ValueVersion.minVersion) value.ensureNoCid.map(_.version) shouldBe Right(TransactionVersion.minVersion)
contract.ensureNoCid.map(_.arg.version) shouldBe Right(ValueVersion.minVersion) contract.ensureNoCid.map(_.arg.version) shouldBe Right(TransactionVersion.minVersion)
} }

View File

@ -20,7 +20,7 @@ private[migration] final class V38__Update_value_versions extends BaseJavaMigrat
private[this] val batchSize = 1000 private[this] val batchSize = 1000
private[this] val stableValueVersion = lf.value.ValueVersion("6") private[this] val stableValueVersion = lf.transaction.TransactionVersion.V10
private[this] val stableValueVersionAsInt = stableValueVersion.protoValue.toInt private[this] val stableValueVersionAsInt = stableValueVersion.protoValue.toInt
private[this] val SELECT_EVENTS = private[this] val SELECT_EVENTS =

View File

@ -17,7 +17,7 @@ private[migration] object ValueSerializer {
private[translation] object DeprecatedValueVersionsError { private[translation] object DeprecatedValueVersionsError {
val DeprecatedValueVersions = Set("1", "2", "3", "4", "5") val DeprecatedValueVersions = Set("1", "2", "3", "4", "5")
val UnsupportedErrorMessage = """Unsupported value version (\d)""".r val UnsupportedErrorMessage = """Unsupported transaction version '(\d)'""".r
def unapply[X](arg: Either[ValueCoder.DecodeError, X]): Option[String] = def unapply[X](arg: Either[ValueCoder.DecodeError, X]): Option[String] =
arg match { arg match {

View File

@ -16,7 +16,7 @@ private[platform] object ValueSerializer {
errorContext: => String, errorContext: => String,
): Array[Byte] = ): Array[Byte] =
ValueCoder ValueCoder
.encodeVersionedValueWithCustomVersion(ValueCoder.CidEncoder, value) .encodeVersionedValue(ValueCoder.CidEncoder, value)
.fold(error => sys.error(s"$errorContext (${error.errorMessage})"), _.toByteArray) .fold(error => sys.error(s"$errorContext (${error.errorMessage})"), _.toByteArray)
private def deserializeValueHelper( private def deserializeValueHelper(

View File

@ -11,7 +11,6 @@ import com.daml.lf.transaction.Node.{KeyWithMaintainers, NodeCreate, NodeExercis
import com.daml.lf.transaction.TransactionVersion import com.daml.lf.transaction.TransactionVersion
import com.daml.lf.transaction.test.TransactionBuilder import com.daml.lf.transaction.test.TransactionBuilder
import com.daml.lf.value.Value.{ContractInst, ValueParty, VersionedValue} import com.daml.lf.value.Value.{ContractInst, ValueParty, VersionedValue}
import com.daml.lf.value.ValueVersion
import com.daml.platform.store.entries.LedgerEntry import com.daml.platform.store.entries.LedgerEntry
import org.scalatest.{Inside, LoneElement} import org.scalatest.{Inside, LoneElement}
import org.scalatest.flatspec.AsyncFlatSpec import org.scalatest.flatspec.AsyncFlatSpec
@ -138,7 +137,7 @@ private[dao] trait JdbcLedgerDaoDivulgenceSpec extends LoneElement with Inside {
template = someContractInstance.template, template = someContractInstance.template,
agreementText = someContractInstance.agreementText, agreementText = someContractInstance.agreementText,
arg = VersionedValue( arg = VersionedValue(
version = ValueVersion("6"), version = TransactionVersion.V10,
value = someContractInstance.arg value = someContractInstance.arg
) )
) )

View File

@ -10,11 +10,11 @@ import com.daml.lf.transaction.TransactionCoder.{
encodeTransaction encodeTransaction
} }
import com.daml.lf.transaction.TransactionOuterClass.Transaction import com.daml.lf.transaction.TransactionOuterClass.Transaction
import com.daml.lf.transaction.{NodeId, VersionedTransaction} import com.daml.lf.transaction.{NodeId, TransactionVersion, VersionedTransaction}
import com.daml.lf.value.Value.ContractId import com.daml.lf.value.Value.ContractId
import com.daml.lf.value.ValueCoder._ import com.daml.lf.value.ValueCoder._
import com.daml.lf.value.ValueOuterClass.VersionedValue import com.daml.lf.value.ValueOuterClass.VersionedValue
import com.daml.lf.value.{Value, ValueVersion} import com.daml.lf.value.Value
import com.google.protobuf.ByteString import com.google.protobuf.ByteString
package object benchmark { package object benchmark {
@ -32,7 +32,7 @@ package object benchmark {
/** /**
* This is the output of a successful call to * This is the output of a successful call to
* [[com.daml.lf.value.ValueCoder.encodeValue]]. * [[com.daml.lf.value.ValueCoder.encodeVersionedValue]].
* It's the in-memory representation of the Protobuf message that * It's the in-memory representation of the Protobuf message that
* describes a value, not its serialized form. * describes a value, not its serialized form.
*/ */
@ -89,6 +89,6 @@ package object benchmark {
encodeTransaction(NidEncoder, CidEncoder, transaction) encodeTransaction(NidEncoder, CidEncoder, transaction)
private def encode(value: DecodedValue): EncodeResult[EncodedValue] = private def encode(value: DecodedValue): EncodeResult[EncodedValue] =
encodeVersionedValue(CidEncoder, value, ValueVersion.DevOutputVersions) encodeVersionedValue(CidEncoder, TransactionVersion.VDev, value)
} }