LF: drop legacy logic to infer transaction versions. (#7856)

This advances the state of #7788

CHANGELOG_BEGIN
CHANGELOG_END
This commit is contained in:
Remy 2020-11-02 19:15:55 +01:00 committed by GitHub
parent 7325499ff9
commit 23ba79ca6b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 44 additions and 191 deletions

View File

@ -19,10 +19,10 @@ import com.daml.lf.transaction.{
Node,
NodeId,
SubmittedTransaction,
TransactionVersion,
VersionedTransaction,
GenTransaction => GenTx,
Transaction => Tx,
TransactionVersion => TxVersion,
TransactionVersions => TxVersions
}
import com.daml.lf.value.{Value, ValueVersion}
@ -30,6 +30,7 @@ import Value._
import com.daml.lf.speedy.{InitialSeeding, SValue, svalue}
import com.daml.lf.speedy.SValue._
import com.daml.lf.command._
import com.daml.lf.transaction.TransactionVersions.UnversionedNode
import com.daml.lf.value.ValueVersions.assertAsVersionedValue
import org.scalactic.Equality
import org.scalatest.prop.TableDrivenPropertyChecks
@ -1857,8 +1858,7 @@ class EngineTest
val result = run(
cidV6,
EngineConfig.Stable.copy(
allowedOutputTransactionVersions =
VersionRange(TransactionVersion("9"), TransactionVersion("9"))))
allowedOutputTransactionVersions = VersionRange(TxVersion("9"), TxVersion("9"))))
result shouldBe 'left
result.left.get.msg should include("inferred transaction version 10 is not allowed")
}
@ -1955,7 +1955,7 @@ object EngineTest {
): Either[Error, (Tx.Transaction, Tx.Metadata)] = {
type Acc =
(
HashMap[NodeId, Tx.Node],
HashMap[NodeId, UnversionedNode],
BackStack[NodeId],
Boolean,
BackStack[(NodeId, crypto.Hash)],
@ -2025,7 +2025,13 @@ object EngineTest {
tr = tr1.transaction.mapNodeId(nodeRenaming)
} yield
(
nodes ++ tr.nodes,
nodes ++ tr.nodes.mapValues(
Node.GenNode.map3(
identity[NodeId],
identity[ContractId],
(v: VersionedValue[ContractId]) => v.value,
)
),
roots :++ tr.roots,
dependsOnTime || meta1.dependsOnTime,
nodeSeeds :++ meta1.nodeSeeds.map { case (nid, seed) => nodeRenaming(nid) -> seed },
@ -2037,7 +2043,14 @@ object EngineTest {
iterate.map {
case (nodes, roots, dependsOnTime, nodeSeeds, _, _) =>
(
TxVersions.assertAsVersionedTransaction(GenTx(nodes, roots.toImmArray)),
data.assertRight(
TxVersions.asVersionedTransaction(
TxVersions.DevOutputVersions,
engine.compiledPackages().packageLanguageVersion,
roots.toImmArray,
nodes,
)
),
Tx.Metadata(
submissionSeed = None,
submissionTime = txMeta.submissionTime,

View File

@ -24,7 +24,7 @@ private[lf] object TransactionVersions
import VersionTimeline._
import VersionTimeline.Implicits._
private[this] val minVersion = TransactionVersion("1")
private[transaction] val minVersion = TransactionVersion("1")
private[transaction] val minKeyOrLookupByKey = TransactionVersion("3")
private[transaction] val minFetchActors = TransactionVersion("5")
private[transaction] val minNoControllers = TransactionVersion("6")
@ -44,96 +44,6 @@ private[lf] object TransactionVersions
val Empty: VersionRange[TransactionVersion] =
VersionRange(acceptedVersions.last, acceptedVersions.head)
def assignVersion(
a: GenTransaction.WithTxValue[_, Value.ContractId],
supportedVersions: VersionRange[TransactionVersion] = DevOutputVersions,
): Either[String, TransactionVersion] = {
require(a != null)
import VersionTimeline.Implicits._
val inferredVersion =
VersionTimeline.latestWhenAllPresent(
supportedVersions.min,
// latest version used by any value
a.foldValues(ValueVersion("1")) { (z, vv) =>
VersionTimeline.maxVersion(z, vv.version)
},
// a NodeCreate with defined `key` or a NodeLookupByKey
// implies minimum version 3
if (a.nodes.values.exists {
case nc: Node.NodeCreate[_, _] => nc.key.isDefined
case _: Node.NodeLookupByKey[_, _] => true
case _: Node.NodeFetch[_, _] | _: Node.NodeExercises[_, _, _] => false
}) minKeyOrLookupByKey
else
minVersion,
// a NodeFetch with actingParties implies minimum version 5
if (a.nodes.values
.exists { case nf: Node.NodeFetch[_, _] => nf.actingParties.nonEmpty; case _ => false })
minFetchActors
else
minVersion,
if (a.nodes.values
.exists {
case ne: Node.NodeExercises[_, _, _] => ne.exerciseResult.isDefined
case _ => false
})
minExerciseResult
else
minVersion,
if (a.nodes.values
.exists {
case ne: Node.NodeExercises[_, _, _] => ne.key.isDefined
case _ => false
})
minContractKeyInExercise
else
minVersion,
if (a.nodes.values
.exists {
case ne: Node.NodeExercises[_, _, _] =>
ne.key match {
case Some(Node.KeyWithMaintainers(key @ _, maintainers)) => maintainers.nonEmpty
case _ => false
}
case _ => false
})
minMaintainersInExercise
else
minVersion,
if (a.nodes.values
.exists {
case nf: Node.NodeFetch[_, _] => nf.key.isDefined
case _ => false
})
minContractKeyInFetch
else
minVersion,
)
Either.cond(
!(supportedVersions.max precedes inferredVersion),
inferredVersion,
s"inferred version $inferredVersion is not supported"
)
}
def asVersionedTransaction(
tx: GenTransaction.WithTxValue[NodeId, Value.ContractId],
supportedVersions: VersionRange[TransactionVersion] = DevOutputVersions,
): Either[String, Transaction.Transaction] =
for {
v <- assignVersion(tx, supportedVersions)
} yield VersionedTransaction(v, tx)
@throws[IllegalArgumentException]
def assertAsVersionedTransaction(
tx: GenTransaction.WithTxValue[NodeId, Value.ContractId],
supportedVersions: VersionRange[TransactionVersion] = DevOutputVersions,
): Transaction.Transaction =
data.assertRight(asVersionedTransaction(tx, supportedVersions))
private[lf] def assignValueVersion(transactionVersion: TransactionVersion): ValueVersion =
latestWhenAllPresent(
ValueVersions.acceptedVersions.head,

View File

@ -294,24 +294,31 @@ class TransactionCoderSpec
val nodes = ImmArray((1 to 10000).map { nid =>
NodeId(nid) -> node
})
val tx = TransactionVersions.assertAsVersionedTransaction(
GenTransaction(nodes = HashMap(nodes.toSeq: _*), roots = nodes.map(_._1)))
tx shouldEqual TransactionCoder
.decodeTransaction(
TransactionCoder.NidDecoder,
ValueCoder.CidDecoder,
TransactionCoder
.encodeTransaction(
TransactionCoder.NidEncoder,
ValueCoder.CidEncoder,
tx,
)
.right
.get,
val versions = Table("version", TransactionVersion("10"), TransactionVersion("dev"))
forEvery(versions) { version =>
val tx = VersionedTransaction(
version,
GenTransaction(nodes = HashMap(nodes.toSeq: _*), roots = nodes.map(_._1))
)
.right
.get
tx shouldEqual TransactionCoder
.decodeTransaction(
TransactionCoder.NidDecoder,
ValueCoder.CidDecoder,
TransactionCoder
.encodeTransaction(
TransactionCoder.NidEncoder,
ValueCoder.CidEncoder,
tx,
)
.right
.get,
)
.right
.get
}
}
}

View File

@ -4,60 +4,12 @@
package com.daml.lf
package transaction
import data.ImmArray
import value.{Value, ValueVersion, ValueVersions}
import Value.{ContractId, ValueOptional, VersionedValue}
import value.{ValueVersion, ValueVersions}
import com.daml.lf.language.LanguageVersion
import org.scalatest.prop.TableDrivenPropertyChecks
import org.scalatest.{Matchers, WordSpec}
import scala.collection.immutable.HashMap
class TransactionVersionSpec extends WordSpec with Matchers with TableDrivenPropertyChecks {
import TransactionVersionSpec._
import VersionTimeline.maxVersion
private[this] val supportedValVersions =
ValueVersions.DevOutputVersions.copy(min = ValueVersion("1"))
private[this] val supportedTxVersions =
TransactionVersions.DevOutputVersions.copy(min = TransactionVersion("1"))
"assignVersion" should {
"prefer picking an older version" in {
assignTxVersion(assignValueVersions(dummyCreateTransaction)) shouldBe Right(
maxVersion(supportedTxVersions.min, TransactionVersion("1")))
}
"pick version 2 when confronted with newer data" in {
val usingOptional =
dummyCreateTransaction map3 (identity, identity, v => ValueOptional(Some(v)))
assignTxVersion(assignValueVersions(usingOptional)) shouldBe Right(
maxVersion(supportedTxVersions.min, TransactionVersion("2")))
}
"pick version 7 when confronted with exercise result" in {
val hasExerciseResult =
dummyExerciseWithResultTransaction map3 (identity, identity, v => ValueOptional(Some(v)))
assignTxVersion(assignValueVersions(hasExerciseResult)) shouldBe Right(
maxVersion(supportedTxVersions.min, TransactionVersion("7")))
}
"pick version 2 when confronted with exercise result" in {
val hasExerciseResult =
dummyExerciseTransaction map3 (identity, identity, v => ValueOptional(Some(v)))
assignTxVersion(assignValueVersions(hasExerciseResult)) shouldBe Right(
maxVersion(supportedTxVersions.min, TransactionVersion("2")))
}
"crash the picked version is more recent that the maximal supported version" in {
val supportedVersions = VersionRange(TransactionVersion("1"), TransactionVersion("5"))
val hasExerciseResult =
dummyExerciseWithResultTransaction map3 (identity, identity, v => ValueOptional(Some(v)))
TransactionVersions.assignVersion(assignValueVersions(hasExerciseResult), supportedVersions) shouldBe 'left
}
}
"TransactionVersions.assignVersions" should {
@ -151,33 +103,4 @@ class TransactionVersionSpec extends WordSpec with Matchers with TableDrivenProp
}
}
private[this] def assignValueVersions[Nid, Cid <: ContractId](
t: GenTransaction[Nid, Cid, Value[Cid]],
): GenTransaction[Nid, Cid, VersionedValue[Cid]] =
t map3 (identity, identity, ValueVersions.assertAsVersionedValue(_, supportedValVersions))
private[this] def assignTxVersion[Nid, Cid <: ContractId](
t: GenTransaction[Nid, Cid, VersionedValue[Cid]],
): Either[String, TransactionVersion] =
TransactionVersions.assignVersion(t, supportedTxVersions)
}
object TransactionVersionSpec {
import TransactionSpec._
private[this] val singleId = NodeId(0)
private val dummyCreateTransaction =
mkTransaction(HashMap(singleId -> dummyCreateNode("cid1")), ImmArray(singleId))
private val dummyExerciseWithResultTransaction =
mkTransaction(
HashMap(singleId -> dummyExerciseNode("cid2", ImmArray.empty)),
ImmArray(singleId))
private val dummyExerciseTransaction =
mkTransaction(
HashMap(singleId -> dummyExerciseNode("cid3", ImmArray.empty, false)),
ImmArray(singleId),
)
}