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(
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

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

View File

@ -52,6 +52,7 @@ da_scala_test_suite(
"//daml-lf/language",
"//daml-lf/parser",
"//daml-lf/transaction",
"//daml-lf/transaction-test-lib",
"@maven//:com_google_protobuf_protobuf_java",
"@maven//:com_storm_enroute_scalameter_core_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.testing.parser.Implicits._
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, ValueVersion}
import com.daml.lf.value.Value
import org.scalatest.prop.TableDrivenPropertyChecks
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec
@ -98,7 +99,7 @@ class ContractDiscriminatorFreshnessCheckSpec
private def contractInstance(party: Ref.Party, idx: Int, cids: List[ContractId]) =
Value.ContractInst(
tmplId,
ValueVersion.assertAsVersionedValue(contractRecord(party, idx, cids)),
TransactionBuilder.assertAsVersionedValue(contractRecord(party, idx, cids)),
"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.command._
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.scalatest.prop.TableDrivenPropertyChecks
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.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._
@ -99,12 +98,6 @@ private[lf] object Pretty {
prettyTypeConName(tid) /
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..

View File

@ -3,13 +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.ledger.FailedAuthorization
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.scenario.ScenarioLedger
@ -110,14 +109,6 @@ 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
/** There was an authorization failure during execution. */
final case class DamlEFailedAuthorization(
nid: NodeId,

View File

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

View File

@ -5,12 +5,13 @@ package com.daml.lf
package transaction
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.value.Value.{ContractId, ContractInst}
import com.daml.lf.value.Value.{ContractId, ContractInst, VersionedValue}
import com.daml.lf.value.{Value => LfValue}
import scala.Ordering.Implicits.infixOrderingOps
import scala.annotation.tailrec
import scala.collection.immutable.HashMap
final class TransactionBuilder(pkgTxVersion: Ref.PackageId => TransactionVersion = _ =>
@ -74,14 +75,11 @@ final class TransactionBuilder(pkgTxVersion: Ref.PackageId => TransactionVersion
def newCid: ContractId = ContractId.V1(newHash())
private[this] def pkgValVersion(pkgId: Ref.PackageId) =
TransactionVersion.assignValueVersion(pkgTxVersion(pkgId))
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 =
value.Value.VersionedValue(pkgValVersion(templateId.packageId), _)
private[this] def transactionValue(templateId: Ref.TypeConName): Value => TxValue =
value.Value.VersionedValue(pkgTxVersion(templateId.packageId), _)
def create(
id: String,
@ -247,4 +245,73 @@ object TransactionBuilder {
val EmptySubmitted: SubmittedTransaction = SubmittedTransaction(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,
Transaction => Tx
}
import com.daml.lf.transaction.test.TransactionBuilder
import com.daml.lf.value.Value.{NodeId => _, _}
import org.scalacheck.{Arbitrary, Gen}
import Arbitrary.arbitrary
@ -249,8 +250,8 @@ object ValueGenerators {
def versionedValueGen: Gen[VersionedValue[ContractId]] =
for {
value <- valueGen
minVersion = ValueVersion.assertAssignVersion(value)
version <- valueVersionGen(minVersion)
minVersion = TransactionBuilder.assertAssignVersion(value)
version <- transactionVersionGen(minVersion)
} yield VersionedValue(version, value)
private[lf] val genMaybeEmptyParties: Gen[Set[Party]] = Gen.listOf(party).map(_.toSet)
@ -289,7 +290,7 @@ object ValueGenerators {
*/
val malformedCreateNodeGen: Gen[NodeCreate[Value.ContractId]] = {
for {
version <- transactionVersionGen
version <- transactionVersionGen()
coid <- coidGen
coinst <- contractInstanceGen
signatories <- genNonEmptyParties
@ -300,7 +301,7 @@ object ValueGenerators {
val fetchNodeGen: Gen[NodeFetch[ContractId]] = {
for {
version <- transactionVersionGen
version <- transactionVersionGen()
coid <- coidGen
templateId <- idGen
actingParties <- genNonEmptyParties
@ -324,7 +325,7 @@ object ValueGenerators {
/** Makes exercise nodes with some random child IDs. */
val danglingRefExerciseNodeGen: Gen[NodeExercises[NodeId, Value.ContractId]] = {
for {
version <- transactionVersionGen
version <- transactionVersionGen()
targetCoid <- coidGen
templateId <- idGen
choiceId <- nameGen
@ -465,8 +466,8 @@ object ValueGenerators {
def noDanglingRefGenVersionedTransaction: Gen[VersionedTransaction[NodeId, ContractId]] = {
for {
tx <- noDanglingRefGenTransaction
txVer <- transactionVersionGen
nodeVersionGen = transactionVersionGen.filterNot(_ < txVer)
txVer <- transactionVersionGen()
nodeVersionGen = transactionVersionGen().filterNot(_ < txVer)
nodes <- tx.fold(Gen.const(HashMap.empty[NodeId, GenNode[NodeId, ContractId]])) {
case (acc, (nodeId, node)) =>
for {
@ -500,14 +501,10 @@ object ValueGenerators {
Gen.frequency((1, Gen.const("")), (10, g))
}
def valueVersionGen(minVersion: ValueVersion = ValueVersion.minVersion): Gen[ValueVersion] =
Gen.oneOf(ValueVersion.acceptedVersions.filterNot(_ < minVersion).toSeq)
def unsupportedValueVersionGen: Gen[ValueVersion] =
stringVersionGen.map(ValueVersion(_)).filterNot(ValueVersion.acceptedVersions.contains)
def transactionVersionGen: Gen[TransactionVersion] =
Gen.oneOf(TransactionVersion.Values)
def transactionVersionGen(
minVersion: TransactionVersion = TransactionVersion.minVersion,
): Gen[TransactionVersion] =
Gen.oneOf(TransactionVersion.All.filterNot(_ < minVersion))
object Implicits {
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]
protected def versionValue[Cid2 >: Cid](v: Value[Cid2]): VersionedValue[Cid2] =
VersionedValue(TransactionVersion.assignValueVersion(version), v)
VersionedValue(version, v)
}
object GenNode extends CidContainer2[GenNode] {

View File

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

View File

@ -6,7 +6,7 @@ package transaction
import com.daml.lf.data.ImmArray
import com.daml.lf.language.LanguageVersion
import com.daml.lf.value.{Value, ValueVersion}
import com.daml.lf.value.Value
import scala.collection.immutable.HashMap
@ -22,21 +22,28 @@ object TransactionVersion {
case object V10 extends TransactionVersion("10", 10)
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[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] =
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 =
data.assertRight(fromString(vs))
val minVersion = Values.min
private[transaction] val minChoiceObservers = VDev
private[transaction] val minNodeVersion = VDev
val minVersion: TransactionVersion = All.min
def maxVersion: TransactionVersion = VDev
private[lf] val minGenMap = VDev
private[lf] val minChoiceObservers = VDev
private[lf] val minNodeVersion = VDev
private[lf] val assignNodeVersion: LanguageVersion => TransactionVersion = {
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(
roots: ImmArray[NodeId],
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.data.Ref.{Identifier, Name}
import com.daml.lf.data._
import com.daml.lf.transaction.TransactionVersion
import data.ScalazEqual._
import scala.annotation.tailrec
@ -202,7 +203,7 @@ object Value extends CidContainer1[Value] {
*/
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]] {
override protected def self: this.type = this

View File

@ -30,8 +30,6 @@ object ValueCoder {
object DecodeError extends (String => DecodeError) {
private[lf] def apply(version: TransactionVersion, isTooOldFor: String): DecodeError =
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) {
private[lf] def apply(version: TransactionVersion, isTooOldFor: String): EncodeError =
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] {
@ -129,20 +125,27 @@ object ValueCoder {
} yield Identifier(pkgId, QualifiedName(module, name))
private def decodeVersion(vs: String): Either[DecodeError, ValueVersion] =
ValueVersion
.isAcceptedVersion(vs)
.fold[Either[DecodeError, ValueVersion]](Left(DecodeError(s"Unsupported value version $vs")))(
v => Right(v),
)
// For backward compatibility reasons, V10 is encoded as "6" when used inside a
// proto.VersionedValue
private[this] def encodeValueVersion(version: TransactionVersion): String =
if (version == TransactionVersion.V10) {
"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,
* checks if the value version is currently supported and
* converts the value to the type usable by engine/interpreter.
*
* Supported value versions configured in [[ValueVersions]].
*
* @param protoValue0 the value to be read
* @param decodeCid a function to decode stringified contract ids
* @tparam Cid ContractId type
@ -153,7 +156,7 @@ object ValueCoder {
protoValue0: proto.VersionedValue,
): Either[DecodeError, VersionedValue[Cid]] =
for {
version <- decodeVersion(protoValue0.getVersion)
version <- decodeValueVersion(protoValue0.getVersion)
value <- decodeValue(decodeCid, version, protoValue0.getValue)
} yield VersionedValue(version, value)
@ -163,46 +166,6 @@ object ValueCoder {
): Either[DecodeError, Value[Cid]] =
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
* to engine/interpreter usable Value type
@ -214,7 +177,7 @@ object ValueCoder {
*/
def decodeValue[Cid](
decodeCid: DecodeCid[Cid],
valueVersion: ValueVersion,
version: TransactionVersion,
protoValue0: proto.Value,
): Either[DecodeError, Value[Cid]] = {
case class Err(msg: String) extends Throwable(null, null, true, false)
@ -227,9 +190,9 @@ object ValueCoder {
identity,
)
def assertSince(minVersion: ValueVersion, description: => String) =
if (valueVersion < minVersion)
throw Err(s"$description is not supported by value version $valueVersion")
def assertSince(minVersion: TransactionVersion, description: => String) =
if (version < minVersion)
throw Err(s"$description is not supported by value version $version")
def go(nesting: Int, protoValue: proto.Value): Value[Cid] = {
if (nesting > MAXIMUM_NESTING) {
@ -343,7 +306,7 @@ object ValueCoder {
ValueTextMap(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 =>
go(newNesting, entry.getKey) -> go(newNesting, entry.getValue))
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
*
@ -372,12 +361,12 @@ object ValueCoder {
*/
def encodeValue[Cid](
encodeCid: EncodeCid[Cid],
valueVersion: ValueVersion,
valueVersion: TransactionVersion,
v0: Value[Cid],
): Either[EncodeError, proto.Value] = {
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)
throw Err(s"$description is not supported by value version $valueVersion")
@ -469,7 +458,7 @@ object ValueCoder {
builder.setMap(protoMap).build()
case ValueGenMap(entries) =>
assertSince(ValueVersion.minGenMap, "Value.SumCase.MAP")
assertSince(TransactionVersion.minGenMap, "Value.SumCase.MAP")
val protoMap = proto.GenMap.newBuilder()
entries.foreach {
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](
decodeCid: DecodeCid[Cid],
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 {
forAll(malformedCreateNodeGen, transactionVersionGen, transactionVersionGen) {
forAll(malformedCreateNodeGen, transactionVersionGen(), transactionVersionGen()) {
(createNode, version1, version2) =>
val (nodeVersion, txVersion) = inIncreasingOrder(version1, version2)
val versionedNode = createNode.updateVersion(nodeVersion)
@ -76,7 +76,7 @@ class TransactionCoderSpec
}
"do NodeFetch" in {
forAll(fetchNodeGen, transactionVersionGen, transactionVersionGen) {
forAll(fetchNodeGen, transactionVersionGen(), transactionVersionGen()) {
(fetchNode, version1, version2) =>
val (nodeVersion, txVersion) = inIncreasingOrder(version1, version2)
@ -107,7 +107,7 @@ class TransactionCoderSpec
}
"do NodeExercises" in {
forAll(danglingRefExerciseNodeGen, transactionVersionGen, transactionVersionGen) {
forAll(danglingRefExerciseNodeGen, transactionVersionGen(), transactionVersionGen()) {
(exerciseNode, version1, version2) =>
val (nodeVersion, txVersion) = inIncreasingOrder(version1, version2)
@ -176,7 +176,7 @@ class TransactionCoderSpec
}
forAll(noDanglingRefGenTransaction, minSuccessful(50)) { tx =>
forAll(transactionVersionGen, transactionVersionGen, minSuccessful(20)) {
forAll(transactionVersionGen(), transactionVersionGen(), minSuccessful(20)) {
(txVer1, txVer2) =>
import scalaz.std.tuple._
import scalaz.syntax.bifunctor._
@ -242,7 +242,7 @@ class TransactionCoderSpec
ValueCoder.CidDecoder,
encodedTxWithBadTxVer,
) 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 {
forAll(danglingRefGenNode, transactionVersionGen, transactionVersionGen, minSuccessful(10)) {
forAll(
danglingRefGenNode,
transactionVersionGen(),
transactionVersionGen(),
minSuccessful(10)) {
case ((nodeId, node), version1, version2) =>
whenever(version1 != version2) {
val (txVersion, nodeVersion) = inIncreasingOrder(version1, version2)
@ -386,8 +390,8 @@ class TransactionCoderSpec
forAll(
danglingRefGenNode,
transactionVersionGen.filter(_ != V10),
transactionVersionGen.filter(_ != V10),
transactionVersionGen().filter(_ != V10),
transactionVersionGen().filter(_ != V10),
minSuccessful(10)) {
case ((nodeId, node), 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 {
val versions = TransactionVersion.Values
val versions = TransactionVersion.All
def diffVersion(v: TransactionVersion) = {
val randomVersion = versions(Random.nextInt(versions.length - 1))
if (randomVersion != v) randomVersion else versions.last

View File

@ -4,7 +4,6 @@
package com.daml.lf
package transaction
import value.ValueVersion
import com.daml.lf.language.LanguageVersion
import org.scalatest.prop.TableDrivenPropertyChecks
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.data.Ref.Party
import com.daml.lf.data._
import com.daml.lf.transaction.TransactionVersion
import com.daml.lf.value.Value._
import com.daml.lf.value.ValueCoder.DecodeError
import com.daml.lf.value.{ValueOuterClass => proto}
import org.scalacheck.Shrink
import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks
@ -25,13 +25,6 @@ class ValueCoderSpec
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 =
PropertyCheckConfiguration(minSuccessful = 1000)
@ -39,38 +32,14 @@ class ValueCoderSpec
"do Int" in {
forAll("Int64 (Long) invariant") { i: Long =>
val value = ValueInt64(i)
testRoundTrip(value)
testRoundTrip(TransactionVersion.minVersion, value)
}
}
"do Bool" in {
forAll("Bool invariant") { b: Boolean =>
val value = ValueBool(b)
testRoundTrip(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,
)
}
testRoundTrip(TransactionVersion.minVersion, value)
}
}
@ -85,10 +54,13 @@ class ValueCoderSpec
val value = ValueNumeric(dec)
val recoveredDecimal = ValueCoder.decodeValue[ContractId](
ValueCoder.CidDecoder,
firstNumericVersion,
TransactionVersion.minVersion,
assertRight(
ValueCoder
.encodeValue[ContractId](ValueCoder.CidEncoder, firstNumericVersion, value),
.encodeValue[ContractId](
ValueCoder.CidEncoder,
TransactionVersion.minVersion,
value),
),
) match {
case Right(ValueNumeric(x)) => x
@ -104,98 +76,77 @@ class ValueCoderSpec
"do Text" in {
forAll("Text (String) invariant") { t: String =>
val value = ValueText(t)
testRoundTrip(value)
testRoundTrip(TransactionVersion.minVersion, value)
}
}
"do Party" in {
forAll(party) { p: Party =>
val value = ValueParty(p)
testRoundTrip(value)
testRoundTrip(TransactionVersion.minVersion, value)
}
}
"do TimeStamp" in {
forAll(timestampGen) { t: Time.Timestamp => // TODO: fails with Longs
testRoundTrip(ValueTimestamp(t))
testRoundTrip(TransactionVersion.minVersion, ValueTimestamp(t))
}
}
"do Date" in {
forAll(dateGen) { d: Time.Date =>
testRoundTrip(ValueDate(d))
testRoundTrip(TransactionVersion.minVersion, ValueDate(d))
}
}
"do ContractId" in {
forAll(coidValueGen) { v: Value[ContractId] =>
testRoundTrip(v)
testRoundTrip(TransactionVersion.minVersion, v)
}
}
"do ContractId V0 in any ValueVersion" in forAll(coidValueGen, valueVersionGen())(
testRoundTripWithVersion
)
"do ContractId in any ValueVersion" in forAll(
coidValueGen,
valueVersionGen(ValueVersion.minContractIdV1))(
"do ContractId V0 in any ValueVersion" in forAll(coidValueGen, transactionVersionGen())(
testRoundTripWithVersion
)
"do lists" in {
forAll(valueListGen) { v: ValueList[ContractId] =>
testRoundTrip(v)
testRoundTrip(TransactionVersion.minVersion, v)
}
}
"do optionals" in {
forAll(valueOptionalGen) { v: ValueOptional[ContractId] =>
testRoundTrip(v)
testRoundTrip(TransactionVersion.minVersion, v)
}
}
"do maps" in {
forAll(valueMapGen) { v: ValueTextMap[ContractId] =>
testRoundTrip(v)
testRoundTrip(TransactionVersion.minVersion, v)
}
}
"do genMaps" in {
forAll(valueGenMapGen) { v: ValueGenMap[ContractId] =>
testRoundTrip(v)
testRoundTrip(TransactionVersion.minGenMap, v)
}
}
"do variant" in {
forAll(variantGen) { v: ValueVariant[ContractId] =>
testRoundTrip(v)
testRoundTrip(TransactionVersion.minVersion, v)
}
}
"do record" in {
forAll(recordGen) { v: ValueRecord[ContractId] =>
testRoundTrip(v)
testRoundTrip(TransactionVersion.minVersion, v)
}
}
"do unit" in {
val recovered = ValueCoder.decodeValue(
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)
testRoundTrip(TransactionVersion.minVersion, ValueUnit)
}
"do identifier" in {
@ -204,81 +155,45 @@ class ValueCoderSpec
}
}
"do identifier with supported override version" in forAll(idGen, valueVersionGen()) { (i, _) =>
val ei = ValueCoder.encodeIdentifier(i)
ValueCoder.decodeIdentifier(ei) shouldEqual Right(i)
"do identifier with supported override version" in forAll(idGen, transactionVersionGen()) {
(i, _) =>
val ei = ValueCoder.encodeIdentifier(i)
ValueCoder.decodeIdentifier(ei) shouldEqual Right(i)
}
"do versioned value with supported override version" in forAll(versionedValueGen) {
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(
ValueCoder.CidDecoder,
defaultValueVersion,
assertRight(
ValueCoder.encodeValue[ContractId](ValueCoder.CidEncoder, defaultValueVersion, value)),
version,
assertRight(ValueCoder.encodeValue[ContractId](ValueCoder.CidEncoder, version, value)),
)
val bytes =
assertRight(
ValueCoder.encodeVersionedValue(
ValueCoder.CidEncoder,
VersionedValue(version, value),
)
).toByteArray
val fromToBytes = ValueCoder.valueFromBytes(
ValueCoder.CidDecoder,
assertRight(
ValueCoder
.valueToBytes[ContractId](ValueCoder.CidEncoder, value)),
bytes,
)
Right(value) shouldEqual recovered
Right(value) shouldEqual fromToBytes
}
def testRoundTripWithVersion(value0: Value[ContractId], version: ValueVersion): Assertion = {
ValueVersion.acceptedVersions.contains(version) shouldBe true
def testRoundTripWithVersion(
value0: Value[ContractId],
version: TransactionVersion): Assertion = {
val encoded: proto.VersionedValue = assertRight(
ValueCoder
.encodeVersionedValueWithCustomVersion(
ValueCoder.CidEncoder,
VersionedValue(version, value0)),
.encodeVersionedValue(ValueCoder.CidEncoder, VersionedValue(version, value0)),
)
val decoded: VersionedValue[ContractId] = assertRight(
ValueCoder.decodeVersionedValue(ValueCoder.CidDecoder, encoded),

View File

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

View File

@ -17,7 +17,7 @@ private[migration] object ValueSerializer {
private[translation] object DeprecatedValueVersionsError {
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] =
arg match {

View File

@ -16,7 +16,7 @@ private[platform] object ValueSerializer {
errorContext: => String,
): Array[Byte] =
ValueCoder
.encodeVersionedValueWithCustomVersion(ValueCoder.CidEncoder, value)
.encodeVersionedValue(ValueCoder.CidEncoder, value)
.fold(error => sys.error(s"$errorContext (${error.errorMessage})"), _.toByteArray)
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.test.TransactionBuilder
import com.daml.lf.value.Value.{ContractInst, ValueParty, VersionedValue}
import com.daml.lf.value.ValueVersion
import com.daml.platform.store.entries.LedgerEntry
import org.scalatest.{Inside, LoneElement}
import org.scalatest.flatspec.AsyncFlatSpec
@ -138,7 +137,7 @@ private[dao] trait JdbcLedgerDaoDivulgenceSpec extends LoneElement with Inside {
template = someContractInstance.template,
agreementText = someContractInstance.agreementText,
arg = VersionedValue(
version = ValueVersion("6"),
version = TransactionVersion.V10,
value = someContractInstance.arg
)
)

View File

@ -10,11 +10,11 @@ import com.daml.lf.transaction.TransactionCoder.{
encodeTransaction
}
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.ValueCoder._
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
package object benchmark {
@ -32,7 +32,7 @@ package object benchmark {
/**
* 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
* describes a value, not its serialized form.
*/
@ -89,6 +89,6 @@ package object benchmark {
encodeTransaction(NidEncoder, CidEncoder, transaction)
private def encode(value: DecodedValue): EncodeResult[EncodedValue] =
encodeVersionedValue(CidEncoder, value, ValueVersion.DevOutputVersions)
encodeVersionedValue(CidEncoder, TransactionVersion.VDev, value)
}