LF: Add version directly in GenNode (#8154)

CHANGELOG_BEGIN
CHANGELOG_END
This commit is contained in:
Remy 2020-12-08 16:12:12 +01:00 committed by GitHub
parent 38455e8ca9
commit 8f3c6a4494
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 645 additions and 567 deletions

View File

@ -9,11 +9,11 @@ abstract class LfVersions[V](versionsAscending: NonEmptyList[V])(protoValue: V =
protected val maxVersion: V = versionsAscending.last protected val maxVersion: V = versionsAscending.last
val acceptedVersions: List[V] = versionsAscending.list.toList private[lf] val acceptedVersions: List[V] = versionsAscending.list.toList
private val acceptedVersionsMap: Map[String, V] = private val acceptedVersionsMap: Map[String, V] =
acceptedVersions.iterator.map(v => (protoValue(v), v)).toMap acceptedVersions.iterator.map(v => (protoValue(v), v)).toMap
def isAcceptedVersion(version: String): Option[V] = acceptedVersionsMap.get(version) private[lf] def isAcceptedVersion(version: String): Option[V] = acceptedVersionsMap.get(version)
} }

View File

@ -352,9 +352,7 @@ class Engine(val config: EngineConfig = EngineConfig.Stable) {
} }
} }
onLedger.ptx.finish( onLedger.ptx.finish match {
compiledPackages.packageLanguageVersion,
) match {
case PartialTransaction.CompleteTransaction(tx) => case PartialTransaction.CompleteTransaction(tx) =>
val meta = Tx.Metadata( val meta = Tx.Metadata(
submissionSeed = None, submissionSeed = None,

View File

@ -143,7 +143,14 @@ private[engine] final class Preprocessor(compiledPackages: MutableCompiledPackag
private def getTemplateId(node: Node.GenNode.WithTxValue[NodeId, _]) = private def getTemplateId(node: Node.GenNode.WithTxValue[NodeId, _]) =
node match { node match {
case Node.NodeCreate(coid @ _, coinst, optLoc @ _, sigs @ _, stks @ _, key @ _) => case Node.NodeCreate(
coid @ _,
coinst,
optLoc @ _,
sigs @ _,
stks @ _,
key @ _,
version @ _) =>
coinst.template coinst.template
case Node.NodeExercises( case Node.NodeExercises(
coid @ _, coid @ _,
@ -160,11 +167,12 @@ private[engine] final class Preprocessor(compiledPackages: MutableCompiledPackag
exerciseResult @ _, exerciseResult @ _,
key @ _, key @ _,
byKey @ _, byKey @ _,
version @ _,
) => ) =>
templateId templateId
case Node.NodeFetch(coid @ _, templateId, _, _, _, _, _, _) => case Node.NodeFetch(coid @ _, templateId, _, _, _, _, _, _, _) =>
templateId templateId
case Node.NodeLookupByKey(templateId, _, key @ _, _) => case Node.NodeLookupByKey(templateId, _, key @ _, _, _) =>
templateId templateId
} }

View File

@ -34,7 +34,14 @@ private[preprocessing] final class TransactionPreprocessor(
val (localCids, globalCids) = acc val (localCids, globalCids) = acc
node match { node match {
case Node.NodeCreate(coid @ _, coinst, optLoc @ _, sigs @ _, stks @ _, key @ _) => case Node.NodeCreate(
coid @ _,
coinst,
optLoc @ _,
sigs @ _,
stks @ _,
key @ _,
version @ _) =>
val identifier = coinst.template val identifier = coinst.template
if (globalCids(coid)) if (globalCids(coid))
fail("Conflicting discriminators between a global and local contract ID.") fail("Conflicting discriminators between a global and local contract ID.")
@ -59,15 +66,17 @@ private[preprocessing] final class TransactionPreprocessor(
children @ _, children @ _,
exerciseResult @ _, exerciseResult @ _,
key @ _, key @ _,
byKey @ _) => byKey @ _,
version @ _,
) =>
val templateId = template val templateId = template
val (cmd, newCids) = val (cmd, newCids) =
commandPreprocessor.unsafePreprocessExercise(templateId, coid, choice, chosenVal.value) commandPreprocessor.unsafePreprocessExercise(templateId, coid, choice, chosenVal.value)
(cmd, (localCids | newCids.filterNot(globalCids), globalCids)) (cmd, (localCids | newCids.filterNot(globalCids), globalCids))
case Node.NodeFetch(coid, templateId, _, _, _, _, _, _) => case Node.NodeFetch(coid, templateId, _, _, _, _, _, _, _) =>
val cmd = commandPreprocessor.unsafePreprocessFetch(templateId, coid) val cmd = commandPreprocessor.unsafePreprocessFetch(templateId, coid)
(cmd, acc) (cmd, acc)
case Node.NodeLookupByKey(templateId, _, key, _) => case Node.NodeLookupByKey(templateId, _, key, _, _) =>
val keyValue = unsafeAsValueWithNoContractIds(key.key.value) val keyValue = unsafeAsValueWithNoContractIds(key.key.value)
val cmd = commandPreprocessor.unsafePreprocessLookupByKey(templateId, keyValue) val cmd = commandPreprocessor.unsafePreprocessLookupByKey(templateId, keyValue)
(cmd, acc) (cmd, acc)
@ -89,9 +98,9 @@ private[preprocessing] final class TransactionPreprocessor(
fail(s"invalid transaction, root refers to non-existing node $id") fail(s"invalid transaction, root refers to non-existing node $id")
case Some(node) => case Some(node) =>
node match { node match {
case Node.NodeFetch(_, _, _, _, _, _, _, _) => case _: Node.NodeFetch[_, _] =>
fail(s"Transaction contains a fetch root node $id") fail(s"Transaction contains a fetch root node $id")
case Node.NodeLookupByKey(_, _, _, _) => case _: Node.NodeLookupByKey[_, _] =>
fail(s"Transaction contains a lookup by key root node $id") fail(s"Transaction contains a lookup by key root node $id")
case _ => case _ =>
val (cmd, acc) = unsafeTranslateNode(cids, node) val (cmd, acc) = unsafeTranslateNode(cids, node)

View File

@ -29,7 +29,6 @@ import Value._
import com.daml.lf.speedy.{InitialSeeding, SValue, svalue} 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.VersionedNode
import com.daml.lf.transaction.TransactionVersions.UnversionedNode import com.daml.lf.transaction.TransactionVersions.UnversionedNode
import com.daml.lf.value.ValueVersions.assertAsVersionedValue import com.daml.lf.value.ValueVersions.assertAsVersionedValue
import org.scalactic.Equality import org.scalactic.Equality
@ -1220,6 +1219,7 @@ class EngineTest
_, _,
_, _,
_, _,
_,
) => ) =>
coid shouldBe originalCoid coid shouldBe originalCoid
consuming shouldBe true consuming shouldBe true
@ -1230,7 +1230,7 @@ class EngineTest
} }
findNodeByIdx(bobView.nodes, 1).getOrElse(fail("node not found")) match { findNodeByIdx(bobView.nodes, 1).getOrElse(fail("node not found")) match {
case Node.NodeCreate(_, coins, _, _, stakeholders, _) => case Node.NodeCreate(_, coins, _, _, stakeholders, _, _) =>
coins.template shouldBe templateId coins.template shouldBe templateId
stakeholders shouldBe Set(alice, clara) stakeholders shouldBe Set(alice, clara)
case _ => fail("create event is expected") case _ => fail("create event is expected")
@ -1242,7 +1242,7 @@ class EngineTest
claraView.nodes.size shouldBe 1 claraView.nodes.size shouldBe 1
findNodeByIdx(claraView.nodes, 1).getOrElse(fail("node not found")) match { findNodeByIdx(claraView.nodes, 1).getOrElse(fail("node not found")) match {
case Node.NodeCreate(_, coins, _, _, stakeholders, _) => case Node.NodeCreate(_, coins, _, _, stakeholders, _, _) =>
coins.template shouldBe templateId coins.template shouldBe templateId
stakeholders shouldBe Set(alice, clara) stakeholders shouldBe Set(alice, clara)
case _ => fail("create event is expected") case _ => fail("create event is expected")
@ -1367,7 +1367,7 @@ class EngineTest
def actFetchActors[Nid, Cid, Val](n: Node.GenNode[Nid, Cid, Val]): Set[Party] = { def actFetchActors[Nid, Cid, Val](n: Node.GenNode[Nid, Cid, Val]): Set[Party] = {
n match { n match {
case Node.NodeFetch(_, _, _, actingParties, _, _, _, _) => actingParties case Node.NodeFetch(_, _, _, actingParties, _, _, _, _, _) => actingParties
case _ => Set() case _ => Set()
} }
} }
@ -1420,8 +1420,8 @@ class EngineTest
"be retained when reinterpreting single fetch nodes" in { "be retained when reinterpreting single fetch nodes" in {
val Right((tx, txMeta)) = runExample(fetcher1Cid, clara) val Right((tx, txMeta)) = runExample(fetcher1Cid, clara)
val fetchNodes = tx.versionedNodes.iterator.collect { val fetchNodes = tx.nodes.iterator.collect {
case entry @ (_, VersionedNode(_, Node.NodeFetch(_, _, _, _, _, _, _, _))) => entry case entry @ (_, Node.NodeFetch(_, _, _, _, _, _, _, _, _)) => entry
} }
fetchNodes.foreach { fetchNodes.foreach {
@ -1429,7 +1429,7 @@ class EngineTest
val fetchTx = VersionedTransaction(n.version, Map(nid -> n), ImmArray(nid)) val fetchTx = VersionedTransaction(n.version, Map(nid -> n), ImmArray(nid))
val Right((reinterpreted, _)) = val Right((reinterpreted, _)) =
engine engine
.reinterpret(n.node.requiredAuthorizers, n.node, txMeta.nodeSeeds.toSeq.collectFirst { .reinterpret(n.requiredAuthorizers, n, txMeta.nodeSeeds.toSeq.collectFirst {
case (`nid`, seed) => seed case (`nid`, seed) => seed
}, txMeta.submissionTime, let) }, txMeta.submissionTime, let)
.consume(lookupContract, lookupPackage, lookupKey) .consume(lookupContract, lookupPackage, lookupKey)
@ -1481,6 +1481,7 @@ class EngineTest
stakeholders = Set.empty, stakeholders = Set.empty,
key = None, key = None,
byKey = false, byKey = false,
version = TxVersions.minVersion,
) )
val let = Time.Timestamp.now() val let = Time.Timestamp.now()
@ -1531,7 +1532,7 @@ class EngineTest
tx: GenTx[Nid, Cid, Val], tx: GenTx[Nid, Cid, Val],
): Option[(Nid, Node.NodeLookupByKey[Cid, Val])] = ): Option[(Nid, Node.NodeLookupByKey[Cid, Val])] =
tx.nodes.collectFirst { tx.nodes.collectFirst {
case (nid, nl @ Node.NodeLookupByKey(_, _, _, _)) => nid -> nl case (nid, nl @ Node.NodeLookupByKey(_, _, _, _, _)) => nid -> nl
} }
val now = Time.Timestamp.now() val now = Time.Timestamp.now()
@ -1686,7 +1687,7 @@ class EngineTest
.consume(lookupContractMap.get, lookupPackage, lookupKey) .consume(lookupContractMap.get, lookupPackage, lookupKey)
tx.transaction.nodes.values.headOption match { tx.transaction.nodes.values.headOption match {
case Some(Node.NodeFetch(_, _, _, _, _, _, key, _)) => case Some(Node.NodeFetch(_, _, _, _, _, _, key, _, _)) =>
key match { key match {
// just test that the maintainers match here, getting the key out is a bit hairier // just test that the maintainers match here, getting the key out is a bit hairier
case Some(Node.KeyWithMaintainers(_, maintainers)) => case Some(Node.KeyWithMaintainers(_, maintainers)) =>
@ -1811,7 +1812,7 @@ class EngineTest
"be partially reinterpretable" in { "be partially reinterpretable" in {
val Right((tx, txMeta)) = run(3) val Right((tx, txMeta)) = run(3)
val ImmArray(_, exeNode1) = tx.transaction.roots val ImmArray(_, exeNode1) = tx.transaction.roots
val Node.NodeExercises(_, _, _, _, _, _, _, _, _, _, children, _, _, _) = val Node.NodeExercises(_, _, _, _, _, _, _, _, _, _, children, _, _, _, _) =
tx.transaction.nodes(exeNode1) tx.transaction.nodes(exeNode1)
val nids = children.toSeq.take(2).toImmArray val nids = children.toSeq.take(2).toImmArray
@ -2062,11 +2063,13 @@ object EngineTest {
_, _,
_, _,
_, _,
_))) => _,
_,
))) =>
(contracts - targetCoid, keys) (contracts - targetCoid, keys)
case ( case (
(contracts, keys), (contracts, keys),
(_, Node.NodeCreate(cid: ContractId, coinst, _, _, _, key))) => (_, Node.NodeCreate(cid: ContractId, coinst, _, _, _, key, _))) =>
( (
contracts.updated( contracts.updated(
cid, cid,
@ -2106,7 +2109,6 @@ object EngineTest {
case (nodes, roots, dependsOnTime, nodeSeeds, _, _) => case (nodes, roots, dependsOnTime, nodeSeeds, _, _) =>
( (
TxVersions.asVersionedTransaction( TxVersions.asVersionedTransaction(
engine.compiledPackages().packageLanguageVersion,
roots.toImmArray, roots.toImmArray,
nodes, nodes,
), ),

View File

@ -95,7 +95,7 @@ class LargeTransactionTest extends AnyWordSpec with Matchers with BazelRunfiles
cmdReference = "create RangeOfInts", cmdReference = "create RangeOfInts",
seed = hash("testLargeTransactionOneContract:create", txSize)) seed = hash("testLargeTransactionOneContract:create", txSize))
val contractId = firstRootNode(createCmdTx) match { val contractId = firstRootNode(createCmdTx) match {
case N.NodeCreate(coid, _, _, _, _, _) => coid case N.NodeCreate(coid, _, _, _, _, _, _) => coid
case n @ _ => fail(s"Expected NodeCreate, but got: $n") case n @ _ => fail(s"Expected NodeCreate, but got: $n")
} }
val exerciseCmd = toListContainerExerciseCmd(rangeOfIntsTemplateId, contractId) val exerciseCmd = toListContainerExerciseCmd(rangeOfIntsTemplateId, contractId)
@ -122,7 +122,7 @@ class LargeTransactionTest extends AnyWordSpec with Matchers with BazelRunfiles
cmdReference = "create RangeOfInts", cmdReference = "create RangeOfInts",
seed = hash("testLargeTransactionManySmallContracts:create", num)) seed = hash("testLargeTransactionManySmallContracts:create", num))
val contractId = firstRootNode(createCmdTx) match { val contractId = firstRootNode(createCmdTx) match {
case N.NodeCreate(coid, _, _, _, _, _) => coid case N.NodeCreate(coid, _, _, _, _, _, _) => coid
case n @ _ => fail(s"Expected NodeCreate, but got: $n") case n @ _ => fail(s"Expected NodeCreate, but got: $n")
} }
val exerciseCmd = toListOfIntContainers(rangeOfIntsTemplateId, contractId) val exerciseCmd = toListOfIntContainers(rangeOfIntsTemplateId, contractId)
@ -149,7 +149,7 @@ class LargeTransactionTest extends AnyWordSpec with Matchers with BazelRunfiles
cmdReference = "create ListUtil", cmdReference = "create ListUtil",
seed = hash("testLargeChoiceArgument:create", size)) seed = hash("testLargeChoiceArgument:create", size))
val contractId = firstRootNode(createCmdTx) match { val contractId = firstRootNode(createCmdTx) match {
case N.NodeCreate(coid, _, _, _, _, _) => coid case N.NodeCreate(coid, _, _, _, _, _, _) => coid
case n @ _ => fail(s"Expected NodeCreate, but got: $n") case n @ _ => fail(s"Expected NodeCreate, but got: $n")
} }
val exerciseCmd = sizeExerciseCmd(listUtilTemplateId, contractId)(size) val exerciseCmd = sizeExerciseCmd(listUtilTemplateId, contractId)(size)
@ -192,7 +192,7 @@ class LargeTransactionTest extends AnyWordSpec with Matchers with BazelRunfiles
} }
newContracts.count { newContracts.count {
case N.NodeCreate(_, _, _, _, _, _) => true case N.NodeCreate(_, _, _, _, _, _, _) => true
case n @ _ => fail(s"Unexpected match: $n") case n @ _ => fail(s"Unexpected match: $n")
} shouldBe expectedNumberOfContracts } shouldBe expectedNumberOfContracts
} }
@ -317,7 +317,7 @@ class LargeTransactionTest extends AnyWordSpec with Matchers with BazelRunfiles
} }
createNode match { createNode match {
case N.NodeCreate(_, x: ContractInst[_], _, _, _, _) => x case N.NodeCreate(_, x: ContractInst[_], _, _, _, _, _) => x
case n @ _ => fail(s"Unexpected match: $n") case n @ _ => fail(s"Unexpected match: $n")
} }
} }

View File

@ -448,7 +448,7 @@ object ScenarioLedger {
} }
processNodes(mbNewCache2, idsToProcess) processNodes(mbNewCache2, idsToProcess)
case NodeFetch(referencedCoid, templateId @ _, optLoc @ _, _, _, _, _, _) => case NodeFetch(referencedCoid, templateId @ _, optLoc @ _, _, _, _, _, _, _) =>
val newCacheP = val newCacheP =
newCache.updateLedgerNodeInfo(referencedCoid)(info => newCache.updateLedgerNodeInfo(referencedCoid)(info =>
info.copy(referencedBy = info.referencedBy + eventId)) info.copy(referencedBy = info.referencedBy + eventId))

View File

@ -15,7 +15,7 @@ import com.daml.lf.data.Ref._
import com.daml.lf.data.Time import com.daml.lf.data.Time
import com.daml.lf.scenario.ScenarioLedger.TransactionId import com.daml.lf.scenario.ScenarioLedger.TransactionId
import com.daml.lf.scenario._ import com.daml.lf.scenario._
import com.daml.lf.transaction.{NodeId, Transaction => Tx} import com.daml.lf.transaction.{NodeId, TransactionVersions, Transaction => Tx}
import com.daml.lf.speedy.SError._ import com.daml.lf.speedy.SError._
import com.daml.lf.speedy.SValue._ import com.daml.lf.speedy.SValue._
import com.daml.lf.speedy.SBuiltin._ import com.daml.lf.speedy.SBuiltin._
@ -41,6 +41,7 @@ private[lf] object Pretty {
def prettyError(err: SError): Doc = { def prettyError(err: SError): Doc = {
val ptx = PartialTransaction.initial( val ptx = PartialTransaction.initial(
(_ => TransactionVersions.minVersion),
submissionTime = Time.Timestamp.MinValue, submissionTime = Time.Timestamp.MinValue,
initialSeeds = InitialSeeding.NoSeed initialSeeds = InitialSeeding.NoSeed
) )

View File

@ -1266,9 +1266,7 @@ private[lf] object SBuiltin {
throw SpeedyHungry(SResultScenarioInsertMustFail(committerOld, commitLocationOld)) throw SpeedyHungry(SResultScenarioInsertMustFail(committerOld, commitLocationOld))
case SBool(false) => case SBool(false) =>
ptxOld.finish( ptxOld.finish match {
machine.compiledPackages.packageLanguageVersion,
) match {
case PartialTransaction.CompleteTransaction(tx) => case PartialTransaction.CompleteTransaction(tx) =>
// Transaction finished successfully. It might still // Transaction finished successfully. It might still
// fail when committed, so tell the scenario runner to // fail when committed, so tell the scenario runner to
@ -1289,10 +1287,7 @@ private[lf] object SBuiltin {
args: util.ArrayList[SValue], args: util.ArrayList[SValue],
machine: Machine, machine: Machine,
onLedger: OnLedger): Unit = onLedger: OnLedger): Unit =
onLedger.ptx onLedger.ptx.finish match {
.finish(
machine.compiledPackages.packageLanguageVersion,
) match {
case PartialTransaction.CompleteTransaction(tx) => case PartialTransaction.CompleteTransaction(tx) =>
throw SpeedyHungry( throw SpeedyHungry(
SResultScenarioCommit( SResultScenarioCommit(

View File

@ -15,6 +15,7 @@ import com.daml.lf.speedy.SError._
import com.daml.lf.speedy.SExpr._ import com.daml.lf.speedy.SExpr._
import com.daml.lf.speedy.SResult._ import com.daml.lf.speedy.SResult._
import com.daml.lf.speedy.SValue._ import com.daml.lf.speedy.SValue._
import com.daml.lf.transaction.TransactionVersions
import com.daml.lf.value.{Value => V} import com.daml.lf.value.{Value => V}
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
@ -629,7 +630,8 @@ private[lf] object Speedy {
onLedger.committers = Set.empty onLedger.committers = Set.empty
onLedger.commitLocation = None onLedger.commitLocation = None
onLedger.ptx = PartialTransaction.initial( onLedger.ptx = PartialTransaction.initial(
submissionTime = onLedger.ptx.submissionTime, onLedger.ptx.packageToTransactionVersion,
onLedger.ptx.submissionTime,
InitialSeeding.TransactionSeed(freshSeed), InitialSeeding.TransactionSeed(freshSeed),
) )
} }
@ -738,7 +740,9 @@ private[lf] object Speedy {
validating: Boolean = false, validating: Boolean = false,
checkAuthorization: CheckAuthorizationMode = CheckAuthorizationMode.On, checkAuthorization: CheckAuthorizationMode = CheckAuthorizationMode.On,
traceLog: TraceLog = RingBufferTraceLog(damlTraceLog, 100), traceLog: TraceLog = RingBufferTraceLog(damlTraceLog, 100),
): Machine = ): Machine = {
val pkg2TxVersion =
compiledPackages.packageLanguageVersion.andThen(TransactionVersions.assignNodeVersion)
new Machine( new Machine(
ctrl = expr, ctrl = expr,
returnValue = null, returnValue = null,
@ -751,7 +755,7 @@ private[lf] object Speedy {
ledgerMode = OnLedger( ledgerMode = OnLedger(
validating = validating, validating = validating,
checkAuthorization = checkAuthorization, checkAuthorization = checkAuthorization,
ptx = PartialTransaction.initial(submissionTime, initialSeeding), ptx = PartialTransaction.initial(pkg2TxVersion, submissionTime, initialSeeding),
committers = committers, committers = committers,
commitLocation = None, commitLocation = None,
dependsOnTime = false, dependsOnTime = false,
@ -766,6 +770,7 @@ private[lf] object Speedy {
track = Instrumentation(), track = Instrumentation(),
profile = new Profile(), profile = new Profile(),
) )
}
@throws[PackageNotFound] @throws[PackageNotFound]
@throws[CompilationError] @throws[CompilationError]

View File

@ -6,16 +6,15 @@ package speedy
import com.daml.lf.data.Ref.{ChoiceName, Location, Party, TypeConName} import com.daml.lf.data.Ref.{ChoiceName, Location, Party, TypeConName}
import com.daml.lf.data.{BackStack, ImmArray, Ref, Time} import com.daml.lf.data.{BackStack, ImmArray, Ref, Time}
import com.daml.lf.language.LanguageVersion
import com.daml.lf.ledger.Authorize import com.daml.lf.ledger.Authorize
import com.daml.lf.ledger.FailedAuthorization import com.daml.lf.ledger.FailedAuthorization
import com.daml.lf.transaction.{ import com.daml.lf.transaction.{
GenTransaction, GenTransaction,
GlobalKey, GlobalKey,
Node, Node,
NodeId, NodeId,
SubmittedTransaction, SubmittedTransaction,
TransactionVersion,
TransactionVersions, TransactionVersions,
Transaction => Tx Transaction => Tx
} }
@ -115,9 +114,11 @@ private[lf] object PartialTransaction {
) )
def initial( def initial(
pkg2TxVersion: Ref.PackageId => TransactionVersion,
submissionTime: Time.Timestamp, submissionTime: Time.Timestamp,
initialSeeds: InitialSeeding, initialSeeds: InitialSeeding,
) = PartialTransaction( ) = PartialTransaction(
pkg2TxVersion,
submissionTime = submissionTime, submissionTime = submissionTime,
nextNodeIdx = 0, nextNodeIdx = 0,
nodes = HashMap.empty, nodes = HashMap.empty,
@ -160,6 +161,7 @@ private[lf] object PartialTransaction {
* locally archived contract ids will succeed wrongly. * locally archived contract ids will succeed wrongly.
*/ */
private[lf] case class PartialTransaction( private[lf] case class PartialTransaction(
packageToTransactionVersion: Ref.PackageId => TransactionVersion,
submissionTime: Time.Timestamp, submissionTime: Time.Timestamp,
nextNodeIdx: Int, nextNodeIdx: Int,
nodes: HashMap[NodeId, PartialTransaction.Node], nodes: HashMap[NodeId, PartialTransaction.Node],
@ -224,21 +226,14 @@ private[lf] case class PartialTransaction(
* - an error in case the transaction cannot be serialized using * - an error in case the transaction cannot be serialized using
* the `outputTransactionVersions`. * the `outputTransactionVersions`.
*/ */
def finish( def finish: PartialTransaction.Result =
packageLanguageVersion: Ref.PackageId => LanguageVersion, if (context.exeContext.isEmpty && aborted.isEmpty) {
): PartialTransaction.Result =
if (context.exeContext.isEmpty && aborted.isEmpty)
CompleteTransaction( CompleteTransaction(
SubmittedTransaction( SubmittedTransaction(
TransactionVersions TransactionVersions.asVersionedTransaction(context.children.toImmArray, nodes)
.asVersionedTransaction(
packageLanguageVersion,
context.children.toImmArray,
nodes
)
) )
) )
else } else
IncompleteTransaction(this) IncompleteTransaction(this)
/** Extend the 'PartialTransaction' with a node for creating a /** Extend the 'PartialTransaction' with a node for creating a
@ -270,6 +265,7 @@ private[lf] case class PartialTransaction(
signatories, signatories,
stakeholders, stakeholders,
key, key,
packageToTransactionVersion(coinst.template.packageId)
) )
val nid = NodeId(nextNodeIdx) val nid = NodeId(nextNodeIdx)
val ptx = copy( val ptx = copy(
@ -312,7 +308,8 @@ private[lf] case class PartialTransaction(
signatories, signatories,
stakeholders, stakeholders,
key, key,
byKey byKey,
packageToTransactionVersion(templateId.packageId),
) )
mustBeActive( mustBeActive(
coid, coid,
@ -329,7 +326,13 @@ private[lf] case class PartialTransaction(
result: Option[Value.ContractId], result: Option[Value.ContractId],
): PartialTransaction = { ): PartialTransaction = {
val nid = NodeId(nextNodeIdx) val nid = NodeId(nextNodeIdx)
val node = Node.NodeLookupByKey(templateId, optLocation, key, result) val node = Node.NodeLookupByKey(
templateId,
optLocation,
key,
result,
packageToTransactionVersion(templateId.packageId),
)
insertLeafNode(node) insertLeafNode(node)
.noteAuthFails(nid, CheckAuthorization.authorizeLookupByKey(node), auth) .noteAuthFails(nid, CheckAuthorization.authorizeLookupByKey(node), auth)
} }
@ -414,6 +417,7 @@ private[lf] case class PartialTransaction(
exerciseResult = Some(value), exerciseResult = Some(value),
key = ec.contractKey, key = ec.contractKey,
byKey = ec.byKey, byKey = ec.byKey,
version = packageToTransactionVersion(ec.templateId.packageId),
) )
val nodeId = ec.nodeId val nodeId = ec.nodeId
val nodeSeed = ec.parent.nextChildrenSeed val nodeSeed = ec.parent.nextChildrenSeed

View File

@ -6,14 +6,15 @@ package transaction
package test package test
import com.daml.lf.data.{BackStack, ImmArray, Ref} import com.daml.lf.data.{BackStack, ImmArray, Ref}
import com.daml.lf.transaction.Node.{GenNode, VersionedNode} import com.daml.lf.transaction.Node.GenNode
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}
import com.daml.lf.value.{ValueVersions, Value => LfValue} import com.daml.lf.value.{ValueVersions, Value => LfValue}
import scala.collection.immutable.HashMap import scala.collection.immutable.HashMap
final class TransactionBuilder(pkgTxVersion: Ref.PackageId => TransactionVersion) { final class TransactionBuilder(pkgTxVersion: Ref.PackageId => TransactionVersion = _ =>
TransactionVersions.minVersion) {
import TransactionBuilder._ import TransactionBuilder._
@ -44,7 +45,7 @@ final class TransactionBuilder(pkgTxVersion: Ref.PackageId => TransactionVersion
def add(node: Node, parentId: NodeId): NodeId = ids.synchronized { def add(node: Node, parentId: NodeId): NodeId = ids.synchronized {
lazy val nodeId = newNode(node) // lazy to avoid getting the next id if the method later throws lazy val nodeId = newNode(node) // lazy to avoid getting the next id if the method later throws
nodes(parentId) match { nodes(parentId) match {
case VersionedNode(_, _: TxExercise) => case _: TxExercise =>
children += parentId -> (children(parentId) :+ nodeId) children += parentId -> (children(parentId) :+ nodeId)
case _ => case _ =>
throw new IllegalArgumentException( throw new IllegalArgumentException(
@ -57,9 +58,9 @@ final class TransactionBuilder(pkgTxVersion: Ref.PackageId => TransactionVersion
import VersionTimeline.Implicits._ import VersionTimeline.Implicits._
val finalNodes = nodes.transform { val finalNodes = nodes.transform {
case (nid, VersionedNode(version, exe: TxExercise)) => case (nid, exe: TxExercise) =>
VersionedNode[NodeId, ContractId](version, exe.copy(children = children(nid).toImmArray)) exe.copy(children = children(nid).toImmArray)
case (_, node @ VersionedNode(_, _: Node.LeafOnlyNode[_, _])) => case (_, node: Node.LeafOnlyNode[ContractId, TxValue]) =>
node node
} }
val finalRoots = roots.toImmArray val finalRoots = roots.toImmArray
@ -93,17 +94,100 @@ final class TransactionBuilder(pkgTxVersion: Ref.PackageId => TransactionVersion
value.Value.VersionedValue(pkgValVersion(templateId.packageId), _) value.Value.VersionedValue(pkgValVersion(templateId.packageId), _)
private[this] def versionN(node: Node): TxNode = private[this] def versionN(node: Node): TxNode =
VersionedNode( GenNode.map3(identity[NodeId], identity[ContractId], versionValue(node.templateId))(node)
pkgTxVersion(node.templateId.packageId),
GenNode.map3(identity[NodeId], identity[ContractId], versionValue(node.templateId))(node) def create(
id: String,
template: String,
argument: Value,
signatories: Seq[String],
observers: Seq[String],
key: Option[String],
): Create = {
val templateId = Ref.Identifier.assertFromString(template)
Create(
coid = ContractId.assertFromString(id),
coinst = ContractInst(
template = templateId,
arg = argument,
agreementText = "",
),
optLocation = None,
signatories = signatories.map(Ref.Party.assertFromString).toSet,
stakeholders = signatories.union(observers).map(Ref.Party.assertFromString).toSet,
key = key.map(keyWithMaintainers(maintainers = signatories, _)),
version = pkgTxVersion(templateId.packageId),
) )
}
def exercise(
contract: Create,
choice: String,
consuming: Boolean,
actingParties: Set[String],
argument: Value,
byKey: Boolean = true,
): Exercise =
Exercise(
choiceObservers = Set.empty, //FIXME #7709: take observers as argument (pref no default value)
targetCoid = contract.coid,
templateId = contract.coinst.template,
choiceId = Ref.ChoiceName.assertFromString(choice),
optLocation = None,
consuming = consuming,
actingParties = actingParties.map(Ref.Party.assertFromString),
chosenValue = argument,
stakeholders = contract.stakeholders,
signatories = contract.signatories,
children = ImmArray.empty,
exerciseResult = None,
key = contract.key,
byKey = byKey,
version = pkgTxVersion(contract.coinst.template.packageId),
)
def exerciseByKey(
contract: Create,
choice: String,
consuming: Boolean,
actingParties: Set[String],
argument: Value,
): Exercise =
exercise(contract, choice, consuming, actingParties, argument, byKey = true)
def fetch(contract: Create, byKey: Boolean = true): Fetch =
Fetch(
coid = contract.coid,
templateId = contract.coinst.template,
optLocation = None,
actingParties = contract.signatories.map(Ref.Party.assertFromString),
signatories = contract.signatories,
stakeholders = contract.stakeholders,
key = contract.key,
byKey = byKey,
version = pkgTxVersion(contract.coinst.template.packageId),
)
def fetchByKey(contract: Create): Fetch =
fetch(contract, byKey = true)
def lookupByKey(contract: Create, found: Boolean): LookupByKey =
LookupByKey(
templateId = contract.coinst.template,
optLocation = None,
key = contract.key.get,
result = if (found) Some(contract.coid) else None,
version = pkgTxVersion(contract.coinst.template.packageId),
)
} }
object TransactionBuilder { object TransactionBuilder {
type Value = value.Value[ContractId] type Value = value.Value[ContractId]
type TxValue = value.Value.VersionedValue[ContractId] type TxValue = value.Value.VersionedValue[ContractId]
type Node = Node.GenNode[NodeId, ContractId, Value] type Node = Node.GenNode[NodeId, ContractId, Value]
type TxNode = VersionedNode[NodeId, ContractId] type TxNode = Node.GenNode.WithTxValue[NodeId, ContractId]
type Create = Node.NodeCreate[ContractId, Value] type Create = Node.NodeCreate[ContractId, Value]
type Exercise = Node.NodeExercises[NodeId, ContractId, Value] type Exercise = Node.NodeExercises[NodeId, ContractId, Value]
@ -157,84 +241,6 @@ object TransactionBuilder {
maintainers = maintainers.map(Ref.Party.assertFromString).toSet, maintainers = maintainers.map(Ref.Party.assertFromString).toSet,
) )
def create(
id: String,
template: String,
argument: Value,
signatories: Seq[String],
observers: Seq[String],
key: Option[String],
): Create =
Create(
coid = ContractId.assertFromString(id),
coinst = ContractInst(
template = Ref.Identifier.assertFromString(template),
arg = argument,
agreementText = "",
),
optLocation = None,
signatories = signatories.map(Ref.Party.assertFromString).toSet,
stakeholders = signatories.union(observers).map(Ref.Party.assertFromString).toSet,
key = key.map(keyWithMaintainers(maintainers = signatories, _)),
)
def exercise(
contract: Create,
choice: String,
consuming: Boolean,
actingParties: Set[String],
argument: Value,
byKey: Boolean = true,
): Exercise =
Exercise(
choiceObservers = Set.empty, //FIXME #7709: take observers as argument (pref no default value)
targetCoid = contract.coid,
templateId = contract.coinst.template,
choiceId = Ref.ChoiceName.assertFromString(choice),
optLocation = None,
consuming = consuming,
actingParties = actingParties.map(Ref.Party.assertFromString),
chosenValue = argument,
stakeholders = contract.stakeholders,
signatories = contract.signatories,
children = ImmArray.empty,
exerciseResult = None,
key = contract.key,
byKey = byKey
)
def exerciseByKey(
contract: Create,
choice: String,
consuming: Boolean,
actingParties: Set[String],
argument: Value,
): Exercise =
exercise(contract, choice, consuming, actingParties, argument, byKey = true)
def fetch(contract: Create, byKey: Boolean = true): Fetch =
Fetch(
coid = contract.coid,
templateId = contract.coinst.template,
optLocation = None,
actingParties = contract.signatories.map(Ref.Party.assertFromString),
signatories = contract.signatories,
stakeholders = contract.stakeholders,
key = contract.key,
byKey = byKey,
)
def fetchByKey(contract: Create): Fetch =
fetch(contract, byKey = true)
def lookupByKey(contract: Create, found: Boolean): LookupByKey =
LookupByKey(
templateId = contract.coinst.template,
optLocation = None,
key = contract.key.get,
result = if (found) Some(contract.coid) else None
)
def just(node: Node, nodes: Node*): Tx.Transaction = { def just(node: Node, nodes: Node*): Tx.Transaction = {
val builder = TransactionBuilder() val builder = TransactionBuilder()
val _ = builder.add(node) val _ = builder.add(node)

View File

@ -8,11 +8,11 @@ package test
import com.daml.lf.data.Ref._ import com.daml.lf.data.Ref._
import com.daml.lf.data._ import com.daml.lf.data._
import com.daml.lf.transaction.Node.{ import com.daml.lf.transaction.Node.{
GenNode,
KeyWithMaintainers, KeyWithMaintainers,
NodeCreate, NodeCreate,
NodeExercises, NodeExercises,
NodeFetch, NodeFetch
VersionedNode
} }
import com.daml.lf.transaction.VersionTimeline.Implicits._ import com.daml.lf.transaction.VersionTimeline.Implicits._
import com.daml.lf.transaction.{ import com.daml.lf.transaction.{
@ -284,16 +284,18 @@ object ValueGenerators {
*/ */
val malformedCreateNodeGen: Gen[NodeCreate.WithTxValue[Value.ContractId]] = { val malformedCreateNodeGen: Gen[NodeCreate.WithTxValue[Value.ContractId]] = {
for { for {
version <- transactionVersionGen
coid <- coidGen coid <- coidGen
coinst <- contractInstanceGen coinst <- contractInstanceGen
signatories <- genNonEmptyParties signatories <- genNonEmptyParties
stakeholders <- genNonEmptyParties stakeholders <- genNonEmptyParties
key <- Gen.option(keyWithMaintainersGen) key <- Gen.option(keyWithMaintainersGen)
} yield NodeCreate(coid, coinst, None, signatories, stakeholders, key) } yield NodeCreate(coid, coinst, None, signatories, stakeholders, key, version)
} }
val fetchNodeGen: Gen[NodeFetch.WithTxValue[ContractId]] = { val fetchNodeGen: Gen[NodeFetch.WithTxValue[ContractId]] = {
for { for {
version <- transactionVersionGen
coid <- coidGen coid <- coidGen
templateId <- idGen templateId <- idGen
actingParties <- genNonEmptyParties actingParties <- genNonEmptyParties
@ -301,13 +303,24 @@ object ValueGenerators {
stakeholders <- genNonEmptyParties stakeholders <- genNonEmptyParties
key <- Gen.option(keyWithMaintainersGen) key <- Gen.option(keyWithMaintainersGen)
byKey <- Gen.oneOf(true, false) byKey <- Gen.oneOf(true, false)
} yield NodeFetch(coid, templateId, None, actingParties, signatories, stakeholders, key, byKey) } yield
NodeFetch(
coid,
templateId,
None,
actingParties,
signatories,
stakeholders,
key,
byKey,
version)
} }
/** Makes exercise nodes with some random child IDs. */ /** Makes exercise nodes with some random child IDs. */
val danglingRefExerciseNodeGen val danglingRefExerciseNodeGen
: Gen[NodeExercises[NodeId, Value.ContractId, Tx.Value[Value.ContractId]]] = { : Gen[NodeExercises[NodeId, Value.ContractId, Tx.Value[Value.ContractId]]] = {
for { for {
version <- transactionVersionGen
targetCoid <- coidGen targetCoid <- coidGen
templateId <- idGen templateId <- idGen
choiceId <- nameGen choiceId <- nameGen
@ -340,7 +353,8 @@ object ValueGenerators {
children, children,
Some(exerciseResultValue), Some(exerciseResultValue),
Some(KeyWithMaintainers(key, maintainers)), Some(KeyWithMaintainers(key, maintainers)),
byKey = byKey, byKey,
version,
) )
} }
@ -450,12 +464,12 @@ object ValueGenerators {
tx <- noDanglingRefGenTransaction tx <- noDanglingRefGenTransaction
txVer <- transactionVersionGen txVer <- transactionVersionGen
nodeVersionGen = transactionVersionGen.filter(v => !(txVer precedes v)) nodeVersionGen = transactionVersionGen.filter(v => !(txVer precedes v))
nodes <- tx.fold(Gen.const(HashMap.empty[NodeId, VersionedNode[NodeId, ContractId]])) { nodes <- tx.fold(Gen.const(HashMap.empty[NodeId, GenNode.WithTxValue[NodeId, ContractId]])) {
case (acc, (nodeId, node)) => case (acc, (nodeId, node)) =>
for { for {
hashMap <- acc hashMap <- acc
version <- nodeVersionGen version <- nodeVersionGen
} yield hashMap.updated(nodeId, VersionedNode(version, node)) } yield hashMap.updated(nodeId, node.updateVersion(version))
} }
} yield VersionedTransaction(txVer, nodes, tx.roots) } yield VersionedTransaction(txVer, nodes, tx.roots)

View File

@ -6,15 +6,7 @@ package transaction
import com.daml.lf.data.Ref._ import com.daml.lf.data.Ref._
import com.daml.lf.data.{ImmArray, ScalazEqual} import com.daml.lf.data.{ImmArray, ScalazEqual}
import com.daml.lf.value.Value.VersionedValue import com.daml.lf.value._
import com.daml.lf.value.{
CidContainer,
CidContainer1,
CidContainer2,
CidContainer3,
CidMapper,
Value
}
import scalaz.Equal import scalaz.Equal
import scalaz.std.option._ import scalaz.std.option._
import scalaz.syntax.equal._ import scalaz.syntax.equal._
@ -35,6 +27,7 @@ object Node {
with CidContainer[GenNode[Nid, Cid, Val]] { with CidContainer[GenNode[Nid, Cid, Val]] {
def templateId: TypeConName def templateId: TypeConName
def version: TransactionVersion
final override protected def self: this.type = this final override protected def self: this.type = this
@ -62,6 +55,8 @@ object Node {
def foreach3(fNid: Nid => Unit, fCid: Cid => Unit, fVal: Val => Unit) = def foreach3(fNid: Nid => Unit, fCid: Cid => Unit, fVal: Val => Unit) =
GenNode.foreach3(fNid, fCid, fVal)(this) GenNode.foreach3(fNid, fCid, fVal)(this)
private[lf] def updateVersion(version: TransactionVersion): GenNode[Nid, Cid, Val]
} }
object GenNode extends WithTxValue3[GenNode] with CidContainer3[GenNode] { object GenNode extends WithTxValue3[GenNode] with CidContainer3[GenNode] {
@ -78,6 +73,7 @@ object Node {
_, _,
_, _,
key, key,
_,
) => ) =>
self copy ( self copy (
coid = f2(coid), coid = f2(coid),
@ -93,6 +89,7 @@ object Node {
_, _,
key, key,
_, _,
_,
) => ) =>
self copy ( self copy (
coid = f2(coid), coid = f2(coid),
@ -113,6 +110,7 @@ object Node {
exerciseResult, exerciseResult,
key, key,
_, _,
_,
) => ) =>
self copy ( self copy (
targetCoid = f2(targetCoid), targetCoid = f2(targetCoid),
@ -126,6 +124,7 @@ object Node {
_, _,
key, key,
result, result,
_,
) => ) =>
self copy ( self copy (
key = KeyWithMaintainers.map1(f3)(key), key = KeyWithMaintainers.map1(f3)(key),
@ -145,6 +144,7 @@ object Node {
signatories @ _, signatories @ _,
stakeholders @ _, stakeholders @ _,
key, key,
_,
) => ) =>
f2(coid) f2(coid)
Value.ContractInst.foreach1(f3)(coinst) Value.ContractInst.foreach1(f3)(coinst)
@ -158,6 +158,7 @@ object Node {
stakeholdersd @ _, stakeholdersd @ _,
key, key,
_, _,
_,
) => ) =>
f2(coid) f2(coid)
key.foreach(KeyWithMaintainers.foreach1(f3)) key.foreach(KeyWithMaintainers.foreach1(f3))
@ -176,6 +177,7 @@ object Node {
exerciseResult, exerciseResult,
key, key,
_, _,
_,
) => ) =>
f2(targetCoid) f2(targetCoid)
f3(chosenValue) f3(chosenValue)
@ -187,6 +189,7 @@ object Node {
optLocation @ _, optLocation @ _,
key, key,
result, result,
_,
) => ) =>
KeyWithMaintainers.foreach1(f3)(key) KeyWithMaintainers.foreach1(f3)(key)
result.foreach(f2) result.foreach(f2)
@ -206,11 +209,16 @@ object Node {
signatories: Set[Party], signatories: Set[Party],
stakeholders: Set[Party], stakeholders: Set[Party],
key: Option[KeyWithMaintainers[Val]], key: Option[KeyWithMaintainers[Val]],
// For the sake of consistency between types with a version field, keep this field the last.
override val version: TransactionVersion,
) extends LeafOnlyNode[Cid, Val] ) extends LeafOnlyNode[Cid, Val]
with NodeInfo.Create { with NodeInfo.Create {
override def templateId: TypeConName = coinst.template override def templateId: TypeConName = coinst.template
override def byKey: Boolean = false override def byKey: Boolean = false
override private[lf] def updateVersion(version: TransactionVersion): NodeCreate[Cid, Val] =
copy(version = version)
} }
object NodeCreate extends WithTxValue2[NodeCreate] object NodeCreate extends WithTxValue2[NodeCreate]
@ -224,9 +232,15 @@ object Node {
signatories: Set[Party], signatories: Set[Party],
stakeholders: Set[Party], stakeholders: Set[Party],
key: Option[KeyWithMaintainers[Val]], key: Option[KeyWithMaintainers[Val]],
override val byKey: Boolean // invariant (!byKey || exerciseResult.isDefined) override val byKey: Boolean, // invariant (!byKey || exerciseResult.isDefined)
// For the sake of consistency between types with a version field, keep this field the last.
override val version: TransactionVersion,
) extends LeafOnlyNode[Cid, Val] ) extends LeafOnlyNode[Cid, Val]
with NodeInfo.Fetch with NodeInfo.Fetch {
override private[lf] def updateVersion(version: TransactionVersion): NodeFetch[Cid, Val] =
copy(version = version)
}
object NodeFetch extends WithTxValue2[NodeFetch] object NodeFetch extends WithTxValue2[NodeFetch]
@ -249,11 +263,18 @@ object Node {
children: ImmArray[Nid], children: ImmArray[Nid],
exerciseResult: Option[Val], exerciseResult: Option[Val],
key: Option[KeyWithMaintainers[Val]], key: Option[KeyWithMaintainers[Val]],
override val byKey: Boolean // invariant (!byKey || exerciseResult.isDefined) override val byKey: Boolean, // invariant (!byKey || exerciseResult.isDefined)
// For the sake of consistency between types with a version field, keep this field the last.
override val version: TransactionVersion,
) extends GenNode[Nid, Cid, Val] ) extends GenNode[Nid, Cid, Val]
with NodeInfo.Exercise { with NodeInfo.Exercise {
@deprecated("use actingParties instead", since = "1.1.2") @deprecated("use actingParties instead", since = "1.1.2")
private[daml] def controllers: actingParties.type = actingParties private[daml] def controllers: actingParties.type = actingParties
override private[lf] def updateVersion(
version: TransactionVersion,
): NodeExercises[Nid, Cid, Val] =
copy(version = version)
} }
object NodeExercises extends WithTxValue3[NodeExercises] object NodeExercises extends WithTxValue3[NodeExercises]
@ -263,12 +284,17 @@ object Node {
optLocation: Option[Location], optLocation: Option[Location],
key: KeyWithMaintainers[Val], key: KeyWithMaintainers[Val],
result: Option[Cid], result: Option[Cid],
// For the sake of consistency between types with a version field, keep this field the last.
override val version: TransactionVersion,
) extends LeafOnlyNode[Cid, Val] ) extends LeafOnlyNode[Cid, Val]
with NodeInfo.LookupByKey { with NodeInfo.LookupByKey {
override def keyMaintainers: Set[Party] = key.maintainers override def keyMaintainers: Set[Party] = key.maintainers
override def hasResult: Boolean = result.isDefined override def hasResult: Boolean = result.isDefined
override def byKey: Boolean = true override def byKey: Boolean = true
override private[lf] def updateVersion(version: TransactionVersion): NodeLookupByKey[Cid, Val] =
copy(version = version)
} }
object NodeLookupByKey extends WithTxValue2[NodeLookupByKey] object NodeLookupByKey extends WithTxValue2[NodeLookupByKey]
@ -311,10 +337,18 @@ object Node {
): Boolean = ): Boolean =
ScalazEqual.match2[recorded.type, isReplayedBy.type, Boolean](fallback = false) { ScalazEqual.match2[recorded.type, isReplayedBy.type, Boolean](fallback = false) {
case nc: NodeCreate[Cid, Val] => { case nc: NodeCreate[Cid, Val] => {
case NodeCreate(coid2, coinst2, optLocation2 @ _, signatories2, stakeholders2, key2) => case NodeCreate(
coid2,
coinst2,
optLocation2 @ _,
signatories2,
stakeholders2,
key2,
version2) =>
import nc._ import nc._
// NOTE(JM): Do not compare location annotations as they may differ due to // NOTE(JM): Do not compare location annotations as they may differ due to
// differing update expression constructed from the root node. // differing update expression constructed from the root node.
version == version2 &&
coid === coid2 && coinst === coinst2 && coid === coid2 && coinst === coinst2 &&
signatories == signatories2 && stakeholders == stakeholders2 && key === key2 signatories == signatories2 && stakeholders == stakeholders2 && key === key2
case _ => false case _ => false
@ -329,8 +363,10 @@ object Node {
stakeholders2, stakeholders2,
key2, key2,
_, _,
version2,
) => ) =>
import nf._ import nf._
version == version2 &&
coid === coid2 && templateId == templateId2 && coid === coid2 && templateId == templateId2 &&
actingParties.forall(_ => actingParties == actingParties2) && actingParties.forall(_ => actingParties == actingParties2) &&
signatories == signatories2 && stakeholders == stakeholders2 && signatories == signatories2 && stakeholders == stakeholders2 &&
@ -352,8 +388,10 @@ object Node {
exerciseResult2, exerciseResult2,
key2, key2,
_, _,
version2,
) => ) =>
import ne._ import ne._
version == version2 &&
targetCoid === targetCoid2 && templateId == templateId2 && choiceId == choiceId2 && targetCoid === targetCoid2 && templateId == templateId2 && choiceId == choiceId2 &&
consuming == consuming2 && actingParties == actingParties2 && chosenValue === chosenValue2 && consuming == consuming2 && actingParties == actingParties2 && chosenValue === chosenValue2 &&
stakeholders == stakeholders2 && signatories == signatories2 && choiceObservers == choiceObservers2 && stakeholders == stakeholders2 && signatories == signatories2 && choiceObservers == choiceObservers2 &&
@ -361,8 +399,9 @@ object Node {
key.fold(true)(_ => key === key2) key.fold(true)(_ => key === key2)
} }
case nl: NodeLookupByKey[Cid, Val] => { case nl: NodeLookupByKey[Cid, Val] => {
case NodeLookupByKey(templateId2, optLocation2 @ _, key2, result2) => case NodeLookupByKey(templateId2, optLocation2 @ _, key2, result2, version2) =>
import nl._ import nl._
version == version2 &&
templateId == templateId2 && templateId == templateId2 &&
key === key2 && result === result2 key === key2 && result === result2
} }
@ -376,31 +415,6 @@ object Node {
type WithTxValue[+Nid, +Cid] = F[Nid, Cid, Transaction.Value[Cid]] type WithTxValue[+Nid, +Cid] = F[Nid, Cid, Transaction.Value[Cid]]
} }
case class VersionedNode[+Nid, +Cid](
version: TransactionVersion,
node: Node.GenNode.WithTxValue[Nid, Cid],
) extends CidContainer[VersionedNode[Nid, Cid]] {
override protected def self: this.type = this
}
object VersionedNode extends CidContainer2[VersionedNode] {
override private[lf] def map2[A1, B1, A2, B2](
f1: A1 => A2,
f2: B1 => B2,
): VersionedNode[A1, B1] => VersionedNode[A2, B2] = {
case VersionedNode(version, node) =>
VersionedNode(version, GenNode.map3(f1, f2, VersionedValue.map1(f2))(node))
}
override private[lf] def foreach2[A, B](
f1: A => Unit,
f2: B => Unit,
): VersionedNode[A, B] => Unit = {
case VersionedNode(_, node) =>
GenNode.foreach3(f1, f2, VersionedValue.foreach1(f2))(node)
}
}
} }
final case class NodeId(index: Int) final case class NodeId(index: Int)

View File

@ -7,7 +7,7 @@ package transaction
import com.daml.lf.data.Ref._ import com.daml.lf.data.Ref._
import com.daml.lf.data._ import com.daml.lf.data._
import com.daml.lf.ledger.FailedAuthorization import com.daml.lf.ledger.FailedAuthorization
import com.daml.lf.transaction.Node.VersionedNode import com.daml.lf.transaction.Node.GenNode
import com.daml.lf.value.Value import com.daml.lf.value.Value
import scalaz.Equal import scalaz.Equal
@ -16,7 +16,7 @@ import scala.collection.immutable.HashMap
final case class VersionedTransaction[Nid, +Cid] private[lf] ( final case class VersionedTransaction[Nid, +Cid] private[lf] (
version: TransactionVersion, version: TransactionVersion,
versionedNodes: Map[Nid, Node.VersionedNode[Nid, Cid]], nodes: Map[Nid, GenNode.WithTxValue[Nid, Cid]],
override val roots: ImmArray[Nid], override val roots: ImmArray[Nid],
) extends HasTxNodes[Nid, Cid, Transaction.Value[Cid]] ) extends HasTxNodes[Nid, Cid, Transaction.Value[Cid]]
with value.CidContainer[VersionedTransaction[Nid, Cid]] with value.CidContainer[VersionedTransaction[Nid, Cid]]
@ -26,20 +26,14 @@ final case class VersionedTransaction[Nid, +Cid] private[lf] (
@deprecated("use resolveRelCid/ensureNoCid/ensureNoRelCid", since = "0.13.52") @deprecated("use resolveRelCid/ensureNoCid/ensureNoRelCid", since = "0.13.52")
def mapContractId[Cid2](f: Cid => Cid2): VersionedTransaction[Nid, Cid2] = { def mapContractId[Cid2](f: Cid => Cid2): VersionedTransaction[Nid, Cid2] = {
val versionNode = Node.VersionedNode.map2(identity[Nid], f) val versionNode = GenNode.map3(identity[Nid], f, Value.VersionedValue.map1(f))
VersionedTransaction( VersionedTransaction(
version, version,
versionedNodes = versionedNodes.transform((_, node) => versionNode(node)), nodes = nodes.transform((_, node) => versionNode(node)),
roots, roots,
) )
} }
// O(1)
override def nodes: Map[Nid, Node.GenNode.WithTxValue[Nid, Cid]] =
// Here we use intentionally `mapValues` to get the time/memory complexity of the method constant.
// It is fine since the function `_.node` is very cheap.
versionedNodes.mapValues(_.node)
// O(1) // O(1)
def transaction: GenTransaction[Nid, Cid, Transaction.Value[Cid]] = def transaction: GenTransaction[Nid, Cid, Transaction.Value[Cid]] =
GenTransaction(nodes, roots) GenTransaction(nodes, roots)
@ -53,7 +47,7 @@ object VersionedTransaction extends value.CidContainer2[VersionedTransaction] {
f2: B1 => B2, f2: B1 => B2,
): VersionedTransaction[A1, B1] => VersionedTransaction[A2, B2] = { ): VersionedTransaction[A1, B1] => VersionedTransaction[A2, B2] = {
case VersionedTransaction(version, versionedNodes, roots) => case VersionedTransaction(version, versionedNodes, roots) =>
val mapNode = Node.VersionedNode.map2(f1, f2) val mapNode = GenNode.map3(f1, f2, Value.VersionedValue.map1(f2))
VersionedTransaction( VersionedTransaction(
version, version,
versionedNodes.map { versionedNodes.map {
@ -68,7 +62,7 @@ object VersionedTransaction extends value.CidContainer2[VersionedTransaction] {
f2: B => Unit, f2: B => Unit,
): VersionedTransaction[A, B] => Unit = { ): VersionedTransaction[A, B] => Unit = {
case VersionedTransaction(_, versionedNodes, _) => case VersionedTransaction(_, versionedNodes, _) =>
val foreachNode = Node.VersionedNode.foreach2(f1, f2) val foreachNode = GenNode.foreach3(f1, f2, Value.VersionedValue.foreach1(f2))
versionedNodes.foreach { versionedNodes.foreach {
case (nid, node) => case (nid, node) =>
f1(nid) f1(nid)
@ -331,7 +325,7 @@ sealed abstract class HasTxNodes[Nid, +Cid, +Val] {
final def localContracts[Cid2 >: Cid]: Map[Cid2, Nid] = final def localContracts[Cid2 >: Cid]: Map[Cid2, Nid] =
fold(Map.empty[Cid2, Nid]) { fold(Map.empty[Cid2, Nid]) {
case (acc, (nid, create @ Node.NodeCreate(_, _, _, _, _, _))) => case (acc, (nid, create @ Node.NodeCreate(_, _, _, _, _, _, _))) =>
acc.updated(create.coid, nid) acc.updated(create.coid, nid)
case (acc, _) => acc case (acc, _) => acc
} }
@ -341,11 +335,11 @@ sealed abstract class HasTxNodes[Nid, +Cid, +Val] {
*/ */
final def inputContracts[Cid2 >: Cid]: Set[Cid2] = final def inputContracts[Cid2 >: Cid]: Set[Cid2] =
fold(Set.empty[Cid2]) { fold(Set.empty[Cid2]) {
case (acc, (_, Node.NodeExercises(coid, _, _, _, _, _, _, _, _, _, _, _, _, _))) => case (acc, (_, Node.NodeExercises(coid, _, _, _, _, _, _, _, _, _, _, _, _, _, _))) =>
acc + coid acc + coid
case (acc, (_, Node.NodeFetch(coid, _, _, _, _, _, _, _))) => case (acc, (_, Node.NodeFetch(coid, _, _, _, _, _, _, _, _))) =>
acc + coid acc + coid
case (acc, (_, Node.NodeLookupByKey(_, _, _, Some(coid)))) => case (acc, (_, Node.NodeLookupByKey(_, _, _, Some(coid), _))) =>
acc + coid acc + coid
case (acc, _) => acc case (acc, _) => acc
} -- localContracts.keySet } -- localContracts.keySet
@ -478,15 +472,15 @@ object GenTransaction extends value.CidContainer3[GenTransaction] {
tx.fold(State(Set.empty, Set.empty)) { tx.fold(State(Set.empty, Set.empty)) {
case (state, (_, node)) => case (state, (_, node)) =>
node match { node match {
case Node.NodeCreate(_, c, _, _, _, Some(key)) => case Node.NodeCreate(_, c, _, _, _, Some(key), _) =>
state.created(globalKey(c.template, key.key.value)) state.created(globalKey(c.template, key.key.value))
case Node.NodeExercises(_, tmplId, _, _, true, _, _, _, _, _, _, _, Some(key), _) => case Node.NodeExercises(_, tmplId, _, _, true, _, _, _, _, _, _, _, Some(key), _, _) =>
state.consumed(globalKey(tmplId, key.key.value)) state.consumed(globalKey(tmplId, key.key.value))
case Node.NodeExercises(_, tmplId, _, _, false, _, _, _, _, _, _, _, Some(key), _) => case Node.NodeExercises(_, tmplId, _, _, false, _, _, _, _, _, _, _, Some(key), _, _) =>
state.referenced(globalKey(tmplId, key.key.value)) state.referenced(globalKey(tmplId, key.key.value))
case Node.NodeFetch(_, tmplId, _, _, _, _, Some(key), _) => case Node.NodeFetch(_, tmplId, _, _, _, _, Some(key), _, _) =>
state.referenced(globalKey(tmplId, key.key.value)) state.referenced(globalKey(tmplId, key.key.value))
case Node.NodeLookupByKey(tmplId, _, key, Some(_)) => case Node.NodeLookupByKey(tmplId, _, key, Some(_), _) =>
state.referenced(globalKey(tmplId, key.key.value)) state.referenced(globalKey(tmplId, key.key.value))
case _ => case _ =>
state state
@ -598,123 +592,128 @@ object Transaction {
): Either[ReplayMismatch[Nid, Cid], Unit] = ): Either[ReplayMismatch[Nid, Cid], Unit] =
(nids1, nids2) match { (nids1, nids2) match {
case (nid1 #:: rest1, nid2 #:: rest2) => case (nid1 #:: rest1, nid2 #:: rest2) =>
val VersionedNode(version1, node1) = recorded.versionedNodes(nid1) (recorded.nodes(nid1), replayed.nodes(nid2)) match {
val VersionedNode(version2, node2) = replayed.versionedNodes(nid2) case (
if (version1 != version2) Node.NodeCreate(
Left(ReplayNodeMismatch(recorded, nid1, replayed, nid2)) coid1,
else coinst1,
(node1, node2) match { optLocation1 @ _,
case ( signatories1,
Node.NodeCreate( stakeholders1,
coid1, key1,
coinst1, version1,
optLocation1 @ _, ),
signatories1, Node.NodeCreate(
stakeholders1, coid2,
key1 coinst2,
), optLocation2 @ _,
Node.NodeCreate( signatories2,
coid2, stakeholders2,
coinst2, key2,
optLocation2 @ _, version2,
signatories2, ))
stakeholders2, if version1 == version2 &&
key2 coid1 === coid2 &&
)) coinst1 === coinst2 &&
if coid1 === coid2 && signatories1 == signatories2 &&
coinst1 === coinst2 && stakeholders1 == stakeholders2 &&
signatories1 == signatories2 && key1 === key2 =>
stakeholders1 == stakeholders2 && loop(rest1, rest2, stack)
key1 === key2 => case (
loop(rest1, rest2, stack) Node.NodeFetch(
case ( coid1,
Node.NodeFetch( templateId1,
coid1, optLocation1 @ _,
templateId1, actingParties1,
optLocation1 @ _, signatories1,
actingParties1, stakeholders1,
signatories1, key1,
stakeholders1, byKey1 @ _,
key1, version1,
byKey1 @ _, ),
), Node.NodeFetch(
Node.NodeFetch( coid2,
coid2, templateId2,
templateId2, optLocation2 @ _,
optLocation2 @ _, actingParties2,
actingParties2, signatories2,
signatories2, stakeholders2,
stakeholders2, key2,
key2, byKey2 @ _,
byKey2 @ _, version2,
)) ))
if coid1 === coid2 && if version1 == version2 &&
templateId1 == templateId2 && coid1 === coid2 &&
(actingParties1.isEmpty || actingParties1 == actingParties2) && templateId1 == templateId2 &&
signatories1 == signatories2 && (actingParties1.isEmpty || actingParties1 == actingParties2) &&
stakeholders1 == stakeholders2 && signatories1 == signatories2 &&
(key1.isEmpty || key1 === key2) => stakeholders1 == stakeholders2 &&
loop(rest1, rest2, stack) (key1.isEmpty || key1 === key2) =>
case ( loop(rest1, rest2, stack)
exe1 @ Node.NodeExercises( case (
targetCoid1, exe1 @ Node.NodeExercises(
templateId1, targetCoid1,
choiceId1, templateId1,
optLocation1 @ _, choiceId1,
consuming1, optLocation1 @ _,
actingParties1, consuming1,
chosenValue1, actingParties1,
stakeholders1, chosenValue1,
signatories1, stakeholders1,
choiceObservers1, signatories1,
children1 @ _, choiceObservers1,
exerciseResult1 @ _, children1 @ _,
key1, exerciseResult1 @ _,
byKey1 @ _, key1,
), byKey1 @ _,
exe2 @ Node.NodeExercises( version1,
targetCoid2, ),
templateId2, exe2 @ Node.NodeExercises(
choiceId2, targetCoid2,
optLocation2 @ _, templateId2,
consuming2, choiceId2,
actingParties2, optLocation2 @ _,
chosenValue2, consuming2,
stakeholders2, actingParties2,
signatories2, chosenValue2,
choiceObservers2, stakeholders2,
children2 @ _, signatories2,
exerciseResult2 @ _, choiceObservers2,
key2, children2 @ _,
byKey2 @ _, exerciseResult2 @ _,
)) key2,
// results are checked after the children byKey2 @ _,
if targetCoid1 === targetCoid2 && version2,
templateId1 == templateId2 && ))
choiceId1 == choiceId2 && // results are checked after the children
consuming1 == consuming2 && if version1 == version2 &&
actingParties1 == actingParties2 && targetCoid1 === targetCoid2 &&
chosenValue1 === chosenValue2 && templateId1 == templateId2 &&
stakeholders1 == stakeholders2 && choiceId1 == choiceId2 &&
signatories1 == signatories2 && consuming1 == consuming2 &&
choiceObservers1 == choiceObservers2 && actingParties1 == actingParties2 &&
(key1.isEmpty || key1 === key2) => chosenValue1 === chosenValue2 &&
loop( stakeholders1 == stakeholders2 &&
children1.iterator.toStream, signatories1 == signatories2 &&
children2.iterator.toStream, choiceObservers1 == choiceObservers2 &&
(nid1, exe1, rest1, nid2, exe2, rest2) :: stack (key1.isEmpty || key1 === key2) =>
loop(
children1.iterator.toStream,
children2.iterator.toStream,
(nid1, exe1, rest1, nid2, exe2, rest2) :: stack
)
case (
Node.NodeLookupByKey(templateId1, optLocation1 @ _, key1, result1, version1),
Node.NodeLookupByKey(templateId2, optLocation2 @ _, key2, result2, version2)
) )
case ( if version1 == version2 &&
Node.NodeLookupByKey(templateId1, optLocation1 @ _, key1, result1), templateId1 == templateId2 &&
Node.NodeLookupByKey(templateId2, optLocation2 @ _, key2, result2) key1 === key2 &&
) result1 === result2 =>
if templateId1 == templateId2 && loop(rest1, rest2, stack)
key1 === key2 && case _ =>
result1 === result2 => Left(ReplayNodeMismatch(recorded, nid1, replayed, nid2))
loop(rest1, rest2, stack) }
case _ =>
Left(ReplayNodeMismatch(recorded, nid1, replayed, nid2))
}
case (Stream.Empty, Stream.Empty) => case (Stream.Empty, Stream.Empty) =>
stack match { stack match {

View File

@ -118,19 +118,6 @@ object TransactionCoder {
.build() .build()
) )
private[lf] def encodeVersionedNode[Nid, Cid](
encodeNid: EncodeNid[Nid],
encodeCid: ValueCoder.EncodeCid[Cid],
enclosingVersion: TransactionVersion,
nodeId: Nid,
versionedNode: VersionedNode[Nid, Cid],
): Either[EncodeError, TransactionOuterClass.Node] =
if (enclosingVersion precedes versionedNode.version)
Left(EncodeError(
s"A transaction of version $enclosingVersion cannot contain nodes of newer version (${versionedNode.version}"))
else
encodeNode(encodeNid, encodeCid, versionedNode.version, nodeId, versionedNode.node)
/** /**
* encodes a [[GenNode[Nid, Cid]] to protocol buffer * encodes a [[GenNode[Nid, Cid]] to protocol buffer
* @param nodeId node id of the node to be encoded * @param nodeId node id of the node to be encoded
@ -144,109 +131,116 @@ object TransactionCoder {
private[lf] def encodeNode[Nid, Cid]( private[lf] def encodeNode[Nid, Cid](
encodeNid: EncodeNid[Nid], encodeNid: EncodeNid[Nid],
encodeCid: ValueCoder.EncodeCid[Cid], encodeCid: ValueCoder.EncodeCid[Cid],
version: TransactionVersion, enclosingVersion: TransactionVersion,
nodeId: Nid, nodeId: Nid,
node: GenNode[Nid, Cid, Value.VersionedValue[Cid]], node: GenNode[Nid, Cid, Value.VersionedValue[Cid]],
): Either[EncodeError, TransactionOuterClass.Node] = { ): Either[EncodeError, TransactionOuterClass.Node] =
val nodeBuilder = TransactionOuterClass.Node.newBuilder().setNodeId(encodeNid.asString(nodeId)) if (enclosingVersion precedes node.version)
nodeBuilder.setVersion(version.protoValue) Left(EncodeError(
s"A transaction of version $enclosingVersion cannot contain nodes of newer version (${node.version}"))
else {
node match { val nodeBuilder =
case nc @ NodeCreate(_, _, _, _, _, _) => TransactionOuterClass.Node.newBuilder().setNodeId(encodeNid.asString(nodeId))
val createBuilder = nodeBuilder.setVersion(node.version.protoValue)
TransactionOuterClass.NodeCreate
node match {
case nc @ NodeCreate(_, _, _, _, _, _, _) =>
val createBuilder =
TransactionOuterClass.NodeCreate
.newBuilder()
.addAllStakeholders(nc.stakeholders.toSet[String].asJava)
.addAllSignatories(nc.signatories.toSet[String].asJava)
for {
inst <- encodeContractInstance(encodeCid, nc.coinst)
optKey <- nc.key match {
case None => Right(None)
case Some(key) => encodeKeyWithMaintainers(encodeCid, key).map(Some(_))
}
encodedCid = encodeCid.encode(nc.coid)
} yield {
createBuilder.setContractIdStruct(encodedCid)
createBuilder.setContractInstance(inst)
optKey.foreach(createBuilder.setKeyWithMaintainers)
nodeBuilder.setCreate(createBuilder).build()
}
case nf @ NodeFetch(_, _, _, _, _, _, _, _, _) =>
val fetchBuilder = TransactionOuterClass.NodeFetch
.newBuilder() .newBuilder()
.addAllStakeholders(nc.stakeholders.toSet[String].asJava) .setTemplateId(ValueCoder.encodeIdentifier(nf.templateId))
.addAllSignatories(nc.signatories.toSet[String].asJava) .addAllStakeholders(nf.stakeholders.toSet[String].asJava)
for { .addAllSignatories(nf.signatories.toSet[String].asJava)
inst <- encodeContractInstance(encodeCid, nc.coinst)
optKey <- nc.key match {
case None => Right(None)
case Some(key) => encodeKeyWithMaintainers(encodeCid, key).map(Some(_))
}
encodedCid = encodeCid.encode(nc.coid)
} yield {
createBuilder.setContractIdStruct(encodedCid)
createBuilder.setContractInstance(inst)
optKey.foreach(createBuilder.setKeyWithMaintainers)
nodeBuilder.setCreate(createBuilder).build()
}
case nf @ NodeFetch(_, _, _, _, _, _, _, _) => for {
val fetchBuilder = TransactionOuterClass.NodeFetch optKey <- nf.key match {
.newBuilder() case None => Right(None)
.setTemplateId(ValueCoder.encodeIdentifier(nf.templateId)) case Some(key) => encodeKeyWithMaintainers(encodeCid, key).map(Some(_))
.addAllStakeholders(nf.stakeholders.toSet[String].asJava) }
.addAllSignatories(nf.signatories.toSet[String].asJava) encodedCid = encodeCid.encode(nf.coid)
} yield {
for { fetchBuilder.setContractIdStruct(encodedCid)
optKey <- nf.key match { nf.actingParties.foreach(fetchBuilder.addActors)
case None => Right(None) optKey.foreach(fetchBuilder.setKeyWithMaintainers)
case Some(key) => encodeKeyWithMaintainers(encodeCid, key).map(Some(_)) nodeBuilder.setFetch(fetchBuilder).build()
} }
encodedCid = encodeCid.encode(nf.coid)
} yield {
fetchBuilder.setContractIdStruct(encodedCid)
nf.actingParties.foreach(fetchBuilder.addActors)
optKey.foreach(fetchBuilder.setKeyWithMaintainers)
nodeBuilder.setFetch(fetchBuilder).build()
}
case ne @ NodeExercises(_, _, _, _, _, _, _, _, _, _, _, _, _, _) => case ne @ NodeExercises(_, _, _, _, _, _, _, _, _, _, _, _, _, _, _) =>
for { for {
_ <- Either.cond( _ <- Either.cond(
test = test =
!((version precedes TransactionVersions.minChoiceObservers) && ne.choiceObservers.nonEmpty), !((node.version precedes TransactionVersions.minChoiceObservers) && ne.choiceObservers.nonEmpty),
right = (), right = (),
left = EncodeError(version, isTooOldFor = "non-empty choice-observers") left = EncodeError(node.version, isTooOldFor = "non-empty choice-observers")
) )
argValue <- encodeValue(encodeCid, ne.chosenValue) argValue <- encodeValue(encodeCid, ne.chosenValue)
retValue <- ne.exerciseResult match { retValue <- ne.exerciseResult match {
case Some(value) => encodeValue(encodeCid, value) case Some(value) => encodeValue(encodeCid, value)
case None => Left(EncodeError("NodeExercises without result")) case None => Left(EncodeError("NodeExercises without result"))
}
exBuilder = TransactionOuterClass.NodeExercise
.newBuilder()
.setChoice(ne.choiceId)
.setTemplateId(ValueCoder.encodeIdentifier(ne.templateId))
.setChosenValue(argValue)
.setConsuming(ne.consuming)
.addAllActors(ne.actingParties.toSet[String].asJava)
.addAllChildren(ne.children.map(encodeNid.asString).toList.asJava)
.addAllSignatories(ne.signatories.toSet[String].asJava)
.addAllStakeholders(ne.stakeholders.toSet[String].asJava)
.addAllObservers(ne.choiceObservers.toSet[String].asJava)
encodedCid = encodeCid.encode(ne.targetCoid)
_ <- Right(
ne.key
.map { kWithM =>
encodeKeyWithMaintainers(encodeCid, kWithM).foreach(
exBuilder.setKeyWithMaintainers)
}
.getOrElse(()),
)
} yield {
exBuilder.setContractIdStruct(encodedCid)
exBuilder.setReturnValue(retValue)
nodeBuilder.setExercise(exBuilder).build()
} }
exBuilder = TransactionOuterClass.NodeExercise
.newBuilder()
.setChoice(ne.choiceId)
.setTemplateId(ValueCoder.encodeIdentifier(ne.templateId))
.setChosenValue(argValue)
.setConsuming(ne.consuming)
.addAllActors(ne.actingParties.toSet[String].asJava)
.addAllChildren(ne.children.map(encodeNid.asString).toList.asJava)
.addAllSignatories(ne.signatories.toSet[String].asJava)
.addAllStakeholders(ne.stakeholders.toSet[String].asJava)
.addAllObservers(ne.choiceObservers.toSet[String].asJava)
encodedCid = encodeCid.encode(ne.targetCoid)
_ <- Right(
ne.key
.map { kWithM =>
encodeKeyWithMaintainers(encodeCid, kWithM).foreach(exBuilder.setKeyWithMaintainers)
}
.getOrElse(()),
)
} yield {
exBuilder.setContractIdStruct(encodedCid)
exBuilder.setReturnValue(retValue)
nodeBuilder.setExercise(exBuilder).build()
}
case nlbk @ NodeLookupByKey(_, _, _, _) => case nlbk @ NodeLookupByKey(_, _, _, _, _) =>
val nlbkBuilder = TransactionOuterClass.NodeLookupByKey.newBuilder() val nlbkBuilder = TransactionOuterClass.NodeLookupByKey.newBuilder()
for { for {
encodedKey <- encodeKeyWithMaintainers(encodeCid, nlbk.key) encodedKey <- encodeKeyWithMaintainers(encodeCid, nlbk.key)
encodedCid = nlbk.result match { encodedCid = nlbk.result match {
case Some(cid) => Some(encodeCid.encode(cid)) case Some(cid) => Some(encodeCid.encode(cid))
case None => None case None => None
}
} yield {
nlbkBuilder
.setTemplateId(ValueCoder.encodeIdentifier(nlbk.templateId))
.setKeyWithMaintainers(encodedKey)
encodedCid.foreach(nlbkBuilder.setContractIdStruct)
nodeBuilder.setLookupByKey(nlbkBuilder).build()
} }
} yield { }
nlbkBuilder
.setTemplateId(ValueCoder.encodeIdentifier(nlbk.templateId))
.setKeyWithMaintainers(encodedKey)
encodedCid.foreach(nlbkBuilder.setContractIdStruct)
nodeBuilder.setLookupByKey(nlbkBuilder).build()
}
} }
}
private def decodeKeyWithMaintainers[Cid]( private def decodeKeyWithMaintainers[Cid](
decodeCid: ValueCoder.DecodeCid[Cid], decodeCid: ValueCoder.DecodeCid[Cid],
keyWithMaintainers: TransactionOuterClass.KeyWithMaintainers, keyWithMaintainers: TransactionOuterClass.KeyWithMaintainers,
@ -270,7 +264,7 @@ object TransactionCoder {
decodeCid: ValueCoder.DecodeCid[Cid], decodeCid: ValueCoder.DecodeCid[Cid],
enclosingVersion: TransactionVersion, enclosingVersion: TransactionVersion,
protoNode: TransactionOuterClass.Node, protoNode: TransactionOuterClass.Node,
): Either[DecodeError, (Nid, VersionedNode[Nid, Cid])] = ): Either[DecodeError, (Nid, GenNode.WithTxValue[Nid, Cid])] =
for { for {
version <- if (enclosingVersion precedes TransactionVersions.minNodeVersion) { version <- if (enclosingVersion precedes TransactionVersions.minNodeVersion) {
Right(enclosingVersion) Right(enclosingVersion)
@ -293,7 +287,7 @@ object TransactionCoder {
decodeCid: ValueCoder.DecodeCid[Cid], decodeCid: ValueCoder.DecodeCid[Cid],
version: TransactionVersion, version: TransactionVersion,
protoNode: TransactionOuterClass.Node, protoNode: TransactionOuterClass.Node,
): Either[DecodeError, (Nid, VersionedNode[Nid, Cid])] = { ): Either[DecodeError, (Nid, GenNode.WithTxValue[Nid, Cid])] = {
val nodeId = decodeNid.fromString(protoNode.getNodeId) val nodeId = decodeNid.fromString(protoNode.getNodeId)
protoNode.getNodeTypeCase match { protoNode.getNodeTypeCase match {
@ -308,8 +302,7 @@ object TransactionCoder {
key <- if (protoCreate.getKeyWithMaintainers == TransactionOuterClass.KeyWithMaintainers.getDefaultInstance) key <- if (protoCreate.getKeyWithMaintainers == TransactionOuterClass.KeyWithMaintainers.getDefaultInstance)
Right(None) Right(None)
else decodeKeyWithMaintainers(decodeCid, protoCreate.getKeyWithMaintainers).map(Some(_)) else decodeKeyWithMaintainers(decodeCid, protoCreate.getKeyWithMaintainers).map(Some(_))
} yield } yield ni -> NodeCreate(c, ci, None, signatories, stakeholders, key, version)
(ni, VersionedNode(version, NodeCreate(c, ci, None, signatories, stakeholders, key)))
case NodeTypeCase.FETCH => case NodeTypeCase.FETCH =>
val protoFetch = protoNode.getFetch val protoFetch = protoNode.getFetch
for { for {
@ -324,12 +317,16 @@ object TransactionCoder {
else else
decodeKeyWithMaintainers(decodeCid, protoFetch.getKeyWithMaintainers).map(Some(_)) decodeKeyWithMaintainers(decodeCid, protoFetch.getKeyWithMaintainers).map(Some(_))
} yield } yield
( ni -> NodeFetch(
ni, c,
VersionedNode( templateId,
version, None,
NodeFetch(c, templateId, None, actingParties, signatories, stakeholders, key, false), actingParties,
) signatories,
stakeholders,
key,
false,
version,
) )
case NodeTypeCase.EXERCISE => case NodeTypeCase.EXERCISE =>
@ -364,27 +361,22 @@ object TransactionCoder {
} }
choiceName <- toIdentifier(protoExe.getChoice) choiceName <- toIdentifier(protoExe.getChoice)
} yield } yield
( ni -> NodeExercises(
ni, targetCoid = targetCoid,
VersionedNode( templateId = templateId,
version, choiceId = choiceName,
NodeExercises( optLocation = None,
targetCoid = targetCoid, consuming = protoExe.getConsuming,
templateId = templateId, actingParties = actingParties,
choiceId = choiceName, chosenValue = cv,
optLocation = None, stakeholders = stakeholders,
consuming = protoExe.getConsuming, signatories = signatories,
actingParties = actingParties, choiceObservers = choiceObservers,
chosenValue = cv, children = children,
stakeholders = stakeholders, exerciseResult = Some(rv),
signatories = signatories, key = keyWithMaintainers,
choiceObservers = choiceObservers, byKey = false,
children = children, version = version,
exerciseResult = Some(rv),
key = keyWithMaintainers,
byKey = false,
),
)
) )
case NodeTypeCase.LOOKUP_BY_KEY => case NodeTypeCase.LOOKUP_BY_KEY =>
val protoLookupByKey = protoNode.getLookupByKey val protoLookupByKey = protoNode.getLookupByKey
@ -394,13 +386,7 @@ object TransactionCoder {
key <- decodeKeyWithMaintainers(decodeCid, protoLookupByKey.getKeyWithMaintainers) key <- decodeKeyWithMaintainers(decodeCid, protoLookupByKey.getKeyWithMaintainers)
cid <- decodeCid.decodeOptional(protoLookupByKey.getContractIdStruct) cid <- decodeCid.decodeOptional(protoLookupByKey.getContractIdStruct)
} yield } yield
( ni -> NodeLookupByKey[Cid, Value.VersionedValue[Cid]](templateId, None, key, cid, version)
ni,
VersionedNode(
version,
NodeLookupByKey[Cid, Value.VersionedValue[Cid]](templateId, None, key, cid)
)
)
case NodeTypeCase.NODETYPE_NOT_SET => Left(DecodeError("Unset Node type")) case NodeTypeCase.NODETYPE_NOT_SET => Left(DecodeError("Unset Node type"))
} }
} }
@ -456,12 +442,12 @@ object TransactionCoder {
case (builderOrError, (nid, _)) => case (builderOrError, (nid, _)) =>
for { for {
builder <- builderOrError builder <- builderOrError
encodedNode <- encodeVersionedNode( encodedNode <- encodeNode(
encodeNid, encodeNid,
encodeCid, encodeCid,
transaction.version, transaction.version,
nid, nid,
transaction.versionedNodes(nid), transaction.nodes(nid),
) )
} yield builder.addNodes(encodedNode) } yield builder.addNodes(encodedNode)
} }
@ -529,7 +515,8 @@ object TransactionCoder {
.map(_.toImmArray) .map(_.toImmArray)
val nodes = protoTx.getNodesList.asScala val nodes = protoTx.getNodesList.asScala
.foldLeft[Either[DecodeError, HashMap[Nid, VersionedNode[Nid, Cid]]]](Right(HashMap.empty)) { .foldLeft[Either[DecodeError, HashMap[Nid, GenNode.WithTxValue[Nid, Cid]]]](
Right(HashMap.empty)) {
case (Left(e), _) => Left(e) case (Left(e), _) => Left(e)
case (Right(acc), s) => case (Right(acc), s) =>
decodeVersionedNode(decodeNid, decodeCid, txVersion, s).map(acc + _) decodeVersionedNode(decodeNid, decodeCid, txVersion, s).map(acc + _)

View File

@ -4,7 +4,7 @@
package com.daml.lf package com.daml.lf
package transaction package transaction
import com.daml.lf.data.{ImmArray, Ref} import com.daml.lf.data.ImmArray
import com.daml.lf.language.LanguageVersion import com.daml.lf.language.LanguageVersion
import com.daml.lf.value.Value.{ContractId, VersionedValue} import com.daml.lf.value.Value.{ContractId, VersionedValue}
import com.daml.lf.value.{Value, ValueVersion, ValueVersions} import com.daml.lf.value.{Value, ValueVersion, ValueVersions}
@ -16,7 +16,7 @@ final case class TransactionVersion(protoValue: String)
/** /**
* Currently supported versions of the DAML-LF transaction specification. * Currently supported versions of the DAML-LF transaction specification.
*/ */
private[lf] object TransactionVersions object TransactionVersions
extends LfVersions(versionsAscending = VersionTimeline.ascendingVersions[TransactionVersion])( extends LfVersions(versionsAscending = VersionTimeline.ascendingVersions[TransactionVersion])(
_.protoValue, _.protoValue,
) { ) {
@ -24,18 +24,18 @@ private[lf] object TransactionVersions
import VersionTimeline._ import VersionTimeline._
import VersionTimeline.Implicits._ import VersionTimeline.Implicits._
private[transaction] val minVersion = TransactionVersion("10") val minVersion = TransactionVersion("10")
private[transaction] val minChoiceObservers = TransactionVersion("dev") private[transaction] val minChoiceObservers = TransactionVersion("dev")
private[transaction] val minNodeVersion = TransactionVersion("dev") private[transaction] val minNodeVersion = TransactionVersion("dev")
// Older versions are deprecated https://github.com/digital-asset/daml/issues/5220 // Older versions are deprecated https://github.com/digital-asset/daml/issues/5220
val StableOutputVersions: VersionRange[TransactionVersion] = private[lf] val StableOutputVersions: VersionRange[TransactionVersion] =
VersionRange(TransactionVersion("10"), TransactionVersion("10")) VersionRange(TransactionVersion("10"), TransactionVersion("10"))
val DevOutputVersions: VersionRange[TransactionVersion] = private[lf] val DevOutputVersions: VersionRange[TransactionVersion] =
StableOutputVersions.copy(max = acceptedVersions.last) StableOutputVersions.copy(max = acceptedVersions.last)
val Empty: VersionRange[TransactionVersion] = private[lf] val Empty: VersionRange[TransactionVersion] =
VersionRange(acceptedVersions.last, acceptedVersions.head) VersionRange(acceptedVersions.last, acceptedVersions.head)
private[lf] def assignValueVersion(nodeVersion: TransactionVersion): ValueVersion = private[lf] def assignValueVersion(nodeVersion: TransactionVersion): ValueVersion =
@ -44,25 +44,21 @@ private[lf] object TransactionVersions
private[lf] def assignNodeVersion(langVersion: LanguageVersion): TransactionVersion = private[lf] def assignNodeVersion(langVersion: LanguageVersion): TransactionVersion =
VersionTimeline.latestWhenAllPresent(TransactionVersions.minVersion, langVersion) VersionTimeline.latestWhenAllPresent(TransactionVersions.minVersion, langVersion)
type UnversionedNode = Node.GenNode[NodeId, Value.ContractId, Value[Value.ContractId]] private[lf] type UnversionedNode = Node.GenNode[NodeId, Value.ContractId, Value[Value.ContractId]]
type VersionedNode = Node.GenNode[NodeId, Value.ContractId, VersionedValue[Value.ContractId]] private[lf] type VersionedNode =
Node.GenNode[NodeId, Value.ContractId, VersionedValue[Value.ContractId]]
def asVersionedTransaction( private[lf] def asVersionedTransaction(
pkgLangVersions: Ref.PackageId => LanguageVersion,
roots: ImmArray[NodeId], roots: ImmArray[NodeId],
nodes: HashMap[NodeId, UnversionedNode], nodes: HashMap[NodeId, UnversionedNode],
): VersionedTransaction[NodeId, Value.ContractId] = { ): VersionedTransaction[NodeId, Value.ContractId] = {
val versionedNodes = nodes.transform { (_, node) => val versionedNodes = nodes.transform { (_, node) =>
val nodeVersion = assignNodeVersion(pkgLangVersions(node.templateId.packageId)) val valueVersion = assignValueVersion(node.version)
val valueVersion = assignValueVersion(nodeVersion) Node.GenNode.map3(
Node.VersionedNode( identity[NodeId],
nodeVersion, identity[ContractId],
Node.GenNode.map3( VersionedValue[ContractId](valueVersion, _))(node)
identity[NodeId],
identity[ContractId],
VersionedValue[ContractId](valueVersion, _))(node),
)
} }
val txVersion = roots.iterator.foldLeft(TransactionVersions.minVersion)((acc, nodeId) => val txVersion = roots.iterator.foldLeft(TransactionVersions.minVersion)((acc, nodeId) =>

View File

@ -57,9 +57,9 @@ class TransactionCoderSpec
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 = VersionedNode(nodeVersion, createNode) val versionedNode = createNode.updateVersion(nodeVersion)
val Right(encodedNode) = TransactionCoder val Right(encodedNode) = TransactionCoder
.encodeVersionedNode( .encodeNode(
TransactionCoder.NidEncoder, TransactionCoder.NidEncoder,
ValueCoder.CidEncoder, ValueCoder.CidEncoder,
txVersion, txVersion,
@ -86,10 +86,10 @@ class TransactionCoderSpec
(fetchNode, version1, version2) => (fetchNode, version1, version2) =>
val (nodeVersion, txVersion) = inIncreasingOrder(version1, version2) val (nodeVersion, txVersion) = inIncreasingOrder(version1, version2)
val versionedNode = VersionedNode(nodeVersion, withoutByKeyFlag(fetchNode)) val versionedNode = withoutByKeyFlag(fetchNode).updateVersion(nodeVersion)
val encodedNode = val encodedNode =
TransactionCoder TransactionCoder
.encodeVersionedNode( .encodeNode(
TransactionCoder.NidEncoder, TransactionCoder.NidEncoder,
ValueCoder.CidEncoder, ValueCoder.CidEncoder,
txVersion, txVersion,
@ -118,10 +118,10 @@ class TransactionCoderSpec
val (nodeVersion, txVersion) = inIncreasingOrder(version1, version2) val (nodeVersion, txVersion) = inIncreasingOrder(version1, version2)
val normalizedNode = minimalistNode(nodeVersion)(exerciseNode) val normalizedNode = minimalistNode(nodeVersion)(exerciseNode)
val versionedNode = VersionedNode(nodeVersion, normalizedNode) val versionedNode = normalizedNode.updateVersion(nodeVersion)
val Right(encodedNode) = val Right(encodedNode) =
TransactionCoder TransactionCoder
.encodeVersionedNode( .encodeNode(
TransactionCoder.NidEncoder, TransactionCoder.NidEncoder,
ValueCoder.CidEncoder, ValueCoder.CidEncoder,
txVersion, txVersion,
@ -147,8 +147,8 @@ class TransactionCoderSpec
forAll(noDanglingRefGenVersionedTransaction, minSuccessful(50)) { tx => forAll(noDanglingRefGenVersionedTransaction, minSuccessful(50)) { tx =>
val tx2 = VersionedTransaction( val tx2 = VersionedTransaction(
tx.version, tx.version,
tx.versionedNodes.transform((_, node) => tx.nodes.transform((_, node) =>
VersionedNode(node.version, minimalistNode(node.version)(node.node))), minimalistNode(node.version)(node.updateVersion(node.version))),
tx.roots, tx.roots,
) )
inside( inside(
@ -175,7 +175,12 @@ class TransactionCoderSpec
} }
} }
"succeed with encoding under later version if succeeded under earlier version" in "succeed with encoding under later version if succeeded under earlier version" in {
def overrideNodeVersions[Nid, Cid](tx: GenTransaction.WithTxValue[Nid, Cid]) = {
tx.copy(nodes = tx.nodes.transform((_, node) =>
node.updateVersion(TransactionVersions.minVersion)))
}
forAll(noDanglingRefGenTransaction, minSuccessful(50)) { tx => forAll(noDanglingRefGenTransaction, minSuccessful(50)) { tx =>
forAll(transactionVersionGen, transactionVersionGen, minSuccessful(20)) { forAll(transactionVersionGen, transactionVersionGen, minSuccessful(20)) {
(txVer1, txVer2) => (txVer1, txVer2) =>
@ -210,14 +215,16 @@ class TransactionCoderSpec
)), )),
) { ) {
case (Right(decWithMin), Right(decWithMax)) => case (Right(decWithMin), Right(decWithMax)) =>
decWithMin.transaction shouldBe minimalistTx(txvMin, tx) overrideNodeVersions(decWithMin.transaction) shouldBe
decWithMin.transaction shouldBe overrideNodeVersions(minimalistTx(txvMin, tx))
minimalistTx(txvMin, decWithMax.transaction) overrideNodeVersions(decWithMin.transaction) shouldBe
overrideNodeVersions(minimalistTx(txvMin, decWithMax.transaction))
} }
} }
} }
} }
} }
}
"transactions decoding should fail when unsupported value version received" in "transactions decoding should fail when unsupported value version received" in
forAll(noDanglingRefGenTransaction, minSuccessful(50)) { tx => forAll(noDanglingRefGenTransaction, minSuccessful(50)) { tx =>
@ -260,7 +267,7 @@ class TransactionCoderSpec
ValueCoder.CidEncoder, ValueCoder.CidEncoder,
VersionedTransaction( VersionedTransaction(
badTxVer, badTxVer,
tx.nodes.mapValues(VersionedNode(badTxVer, _)), tx.nodes.mapValues(_.updateVersion(badTxVer)),
tx.roots), tx.roots),
), ),
) )
@ -296,15 +303,16 @@ class TransactionCoderSpec
signatories = Set(Party.assertFromString("alice")), signatories = Set(Party.assertFromString("alice")),
stakeholders = Set(Party.assertFromString("alice"), Party.assertFromString("bob")), stakeholders = Set(Party.assertFromString("alice"), Party.assertFromString("bob")),
key = None, key = None,
version = TransactionVersions.minVersion,
) )
forEvery(transactionVersions) { version => forEvery(transactionVersions) { version =>
val versionedNode = VersionedNode(version, node) val versionedNode = node.updateVersion(version)
val roots = ImmArray.ImmArraySeq.range(0, 10000).map(NodeId(_)).toImmArray val roots = ImmArray.ImmArraySeq.range(0, 10000).map(NodeId(_)).toImmArray
val nodes = roots.iterator.map(nid => nid -> versionedNode).toMap val nodes = roots.iterator.map(nid => nid -> versionedNode).toMap
val tx = VersionedTransaction( val tx = VersionedTransaction(
version, version,
versionedNodes = nodes, nodes = nodes.mapValues(_.copy(version = version)),
roots = roots roots = roots
) )
@ -346,12 +354,12 @@ class TransactionCoderSpec
forEvery(transactionVersions) { txVersion => forEvery(transactionVersions) { txVersion =>
val result = TransactionCoder val result = TransactionCoder
.encodeVersionedNode( .encodeNode(
TransactionCoder.NidEncoder, TransactionCoder.NidEncoder,
ValueCoder.CidEncoder, ValueCoder.CidEncoder,
txVersion, txVersion,
NodeId(0), NodeId(0),
VersionedNode(v10, normalized) normalized.updateVersion(v10),
) )
result.isLeft shouldBe shouldFail result.isLeft shouldBe shouldFail
@ -369,12 +377,12 @@ class TransactionCoderSpec
val normalizedNode = minimalistNode(nodeVersion)(node) val normalizedNode = minimalistNode(nodeVersion)(node)
TransactionCoder TransactionCoder
.encodeVersionedNode( .encodeNode(
TransactionCoder.NidEncoder, TransactionCoder.NidEncoder,
ValueCoder.CidEncoder, ValueCoder.CidEncoder,
txVersion, txVersion,
nodeId, nodeId,
VersionedNode(nodeVersion, normalizedNode) normalizedNode.updateVersion(nodeVersion),
) shouldBe 'left ) shouldBe 'left
} }
} }
@ -389,11 +397,11 @@ class TransactionCoderSpec
forAll(danglingRefGenNode, Gen.asciiStr, minSuccessful(10)) { forAll(danglingRefGenNode, Gen.asciiStr, minSuccessful(10)) {
case ((nodeId, node), str) => case ((nodeId, node), str) =>
val normalizedNode = VersionedNode(v10, normalize(node)) val normalizedNode = normalize(node.updateVersion(v10))
val Right(encoded) = for { val Right(encoded) = for {
encoded <- TransactionCoder encoded <- TransactionCoder
.encodeVersionedNode( .encodeNode(
TransactionCoder.NidEncoder, TransactionCoder.NidEncoder,
ValueCoder.CidEncoder, ValueCoder.CidEncoder,
v10, v10,
@ -424,10 +432,10 @@ class TransactionCoderSpec
whenever(version1 != version2) { whenever(version1 != version2) {
val (txVersion, nodeVersion) = inIncreasingOrder(version1, version2) val (txVersion, nodeVersion) = inIncreasingOrder(version1, version2)
val normalizedNode = VersionedNode(nodeVersion, minimalistNode(nodeVersion)(node)) val normalizedNode = minimalistNode(nodeVersion)(node).updateVersion(nodeVersion)
val Right(encoded) = TransactionCoder val Right(encoded) = TransactionCoder
.encodeVersionedNode( .encodeNode(
TransactionCoder.NidEncoder, TransactionCoder.NidEncoder,
ValueCoder.CidEncoder, ValueCoder.CidEncoder,
nodeVersion, nodeVersion,
@ -457,12 +465,12 @@ class TransactionCoderSpec
minSuccessful(50), minSuccessful(50),
) { (nodeIdx, node, strings) => ) { (nodeIdx, node, strings) =>
val nodeId = NodeId(nodeIdx) val nodeId = NodeId(nodeIdx)
val normalizedNode = VersionedNode(v10, normalize(node)) val normalizedNode = normalize(node).updateVersion(v10)
val Right(encoded) = val Right(encoded) =
for { for {
encoded <- TransactionCoder encoded <- TransactionCoder
.encodeVersionedNode( .encodeNode(
TransactionCoder.NidEncoder, TransactionCoder.NidEncoder,
ValueCoder.CidEncoder, ValueCoder.CidEncoder,
v10, v10,
@ -607,8 +615,8 @@ class TransactionCoderSpec
private def versionNodes[Nid, Cid]( private def versionNodes[Nid, Cid](
version: TransactionVersion, version: TransactionVersion,
nodes: Map[Nid, GenNode[Nid, Cid, Tx.Value[Cid]]], nodes: Map[Nid, GenNode[Nid, Cid, Tx.Value[Cid]]],
): Map[Nid, VersionedNode[Nid, Cid]] = ): Map[Nid, GenNode.WithTxValue[Nid, Cid]] =
nodes.mapValues(VersionedNode(version, _)) nodes.mapValues(_.updateVersion(version))
private[this] def inIncreasingOrder(version1: TransactionVersion, version2: TransactionVersion) = private[this] def inIncreasingOrder(version1: TransactionVersion, version2: TransactionVersion) =
if (version1 precedes version2) if (version1 precedes version2)

View File

@ -12,10 +12,10 @@ import com.daml.lf.transaction.GenTransaction.{
NotWellFormedError, NotWellFormedError,
OrphanedNode OrphanedNode
} }
import com.daml.lf.transaction.Node.{GenNode, NodeCreate, NodeExercises, VersionedNode} import com.daml.lf.transaction.Node.{GenNode, NodeCreate, NodeExercises}
import com.daml.lf.value.Value.ContractId import com.daml.lf.value.Value.ContractId
import com.daml.lf.value.{Value => V} import com.daml.lf.value.{Value => V}
import com.daml.lf.value.test.ValueGenerators.{danglingRefGenNode, transactionVersionGen} import com.daml.lf.value.test.ValueGenerators.danglingRefGenNode
import org.scalacheck.Gen import org.scalacheck.Gen
import org.scalatestplus.scalacheck.ScalaCheckDrivenPropertyChecks import org.scalatestplus.scalacheck.ScalaCheckDrivenPropertyChecks
import org.scalatest.matchers.should.Matchers import org.scalatest.matchers.should.Matchers
@ -133,19 +133,19 @@ class TransactionSpec extends AnyFreeSpec with Matchers with ScalaCheckDrivenPro
*/ */
"isReplayedBy" - { "isReplayedBy" - {
def genTrans(node: VersionedNode[NodeId, ContractId]) = { def genTrans(node: GenNode.WithTxValue[NodeId, ContractId]) = {
val nid = NodeId(1) val nid = NodeId(1)
VersionedTransaction(node.version, HashMap(nid -> node), ImmArray(nid)) VersionedTransaction(node.version, HashMap(nid -> node), ImmArray(nid))
} }
def isReplayedBy( def isReplayedBy(
n1: VersionedNode[NodeId, ContractId], n1: GenNode.WithTxValue[NodeId, ContractId],
n2: VersionedNode[NodeId, ContractId], n2: GenNode.WithTxValue[NodeId, ContractId],
) = Transaction.isReplayedBy(genTrans(n1), genTrans(n2)) ) = Transaction.isReplayedBy(genTrans(n1), genTrans(n2))
// the whole-transaction-relevant parts are handled by equalForest testing // the whole-transaction-relevant parts are handled by equalForest testing
type CidVal[F[_, _]] = F[V.ContractId, V.VersionedValue[V.ContractId]] type CidVal[F[_, _]] = F[V.ContractId, V.VersionedValue[V.ContractId]]
val genEmptyNode: Gen[VersionedNode[Nothing, V.ContractId]] = val genEmptyNode: Gen[GenNode.WithTxValue[Nothing, V.ContractId]] =
for { for {
entry <- danglingRefGenNode entry <- danglingRefGenNode
node = entry match { node = entry match {
@ -153,8 +153,7 @@ class TransactionSpec extends AnyFreeSpec with Matchers with ScalaCheckDrivenPro
case (_, ne: Node.NodeExercises.WithTxValue[_, V.ContractId]) => case (_, ne: Node.NodeExercises.WithTxValue[_, V.ContractId]) =>
ne.copy(children = ImmArray.empty) ne.copy(children = ImmArray.empty)
} }
version <- transactionVersionGen } yield node
} yield VersionedNode(version, node)
"is reflexive" in forAll(genEmptyNode) { n => "is reflexive" in forAll(genEmptyNode) { n =>
isReplayedBy(n, n) shouldBe Right(()) isReplayedBy(n, n) shouldBe Right(())
@ -167,7 +166,7 @@ class TransactionSpec extends AnyFreeSpec with Matchers with ScalaCheckDrivenPro
if (randomVersion != v) randomVersion else versions.last if (randomVersion != v) randomVersion else versions.last
} }
forAll(genEmptyNode, minSuccessful(10)) { n => forAll(genEmptyNode, minSuccessful(10)) { n =>
val m = n.copy(version = diffVersion(n.version)) val m = n.updateVersion(diffVersion(n.version))
isReplayedBy(n, m) shouldBe 'left isReplayedBy(n, m) shouldBe 'left
} }
} }
@ -180,15 +179,14 @@ class TransactionSpec extends AnyFreeSpec with Matchers with ScalaCheckDrivenPro
"ignores location" in forAll(genEmptyNode) { n => "ignores location" in forAll(genEmptyNode) { n =>
val withoutLocation = { val withoutLocation = {
val VersionedNode(version, node) = n val nodeWithoutLocation = n match {
val nodeWithoutLocation = node match {
case nc: CidVal[Node.NodeCreate] => nc copy (optLocation = None) case nc: CidVal[Node.NodeCreate] => nc copy (optLocation = None)
case nf: Node.NodeFetch.WithTxValue[V.ContractId] => nf copy (optLocation = None) case nf: Node.NodeFetch.WithTxValue[V.ContractId] => nf copy (optLocation = None)
case ne: Node.NodeExercises.WithTxValue[Nothing, V.ContractId] => case ne: Node.NodeExercises.WithTxValue[Nothing, V.ContractId] =>
ne copy (optLocation = None) ne copy (optLocation = None)
case nl: CidVal[Node.NodeLookupByKey] => nl copy (optLocation = None) case nl: CidVal[Node.NodeLookupByKey] => nl copy (optLocation = None)
} }
VersionedNode(version, nodeWithoutLocation) nodeWithoutLocation
} }
isReplayedBy(withoutLocation, n) shouldBe Right(()) isReplayedBy(withoutLocation, n) shouldBe Right(())
isReplayedBy(n, withoutLocation) shouldBe Right(()) isReplayedBy(n, withoutLocation) shouldBe Right(())
@ -266,7 +264,8 @@ object TransactionSpec {
children = children, children = children,
exerciseResult = if (hasExerciseResult) Some(V.ValueUnit) else None, exerciseResult = if (hasExerciseResult) Some(V.ValueUnit) else None,
key = None, key = None,
byKey = false byKey = false,
version = TransactionVersions.minVersion,
) )
val dummyCid = V.ContractId.V1.assertBuild( val dummyCid = V.ContractId.V1.assertBuild(
@ -289,6 +288,7 @@ object TransactionSpec {
signatories = Set.empty, signatories = Set.empty,
stakeholders = Set.empty, stakeholders = Set.empty,
key = None, key = None,
version = TransactionVersions.minVersion,
) )
implicit def toChoiceName(s: String): Ref.Name = Ref.Name.assertFromString(s) implicit def toChoiceName(s: String): Ref.Name = Ref.Name.assertFromString(s)

View File

@ -488,7 +488,7 @@ class IdeClient(val compiledPackages: CompiledPackages) extends ScriptLedgerClie
case SResultNeedKey(keyWithMaintainers, committers, cb) => case SResultNeedKey(keyWithMaintainers, committers, cb) =>
scenarioRunner.lookupKey(keyWithMaintainers.globalKey, committers, cb).toTry.get scenarioRunner.lookupKey(keyWithMaintainers.globalKey, committers, cb).toTry.get
case SResultFinalValue(SUnit) => case SResultFinalValue(SUnit) =>
onLedger.ptx.finish(machine.compiledPackages.packageLanguageVersion) match { onLedger.ptx.finish match {
case PartialTransaction.CompleteTransaction(tx) => case PartialTransaction.CompleteTransaction(tx) =>
val results: ImmArray[ScriptLedgerClient.CommandResult] = tx.roots.map { n => val results: ImmArray[ScriptLedgerClient.CommandResult] = tx.roots.map { n =>
tx.nodes(n) match { tx.nodes(n) match {

View File

@ -53,7 +53,7 @@ private[migration] class V5_1__Populate_Event_Data extends BaseJavaMigration {
val data = txs.flatMap { val data = txs.flatMap {
case (txId, tx) => case (txId, tx) =>
tx.nodes.collect { tx.nodes.collect {
case (nodeId, NodeCreate(cid, _, _, signatories, stakeholders, _)) => case (nodeId, NodeCreate(cid, _, _, signatories, stakeholders, _, _)) =>
(cid, EventId(txId, nodeId), signatories, stakeholders -- signatories) (cid, EventId(txId, nodeId), signatories, stakeholders -- signatories)
} }
} }

View File

@ -53,7 +53,7 @@ private[migration] class V10_1__Populate_Event_Data extends BaseJavaMigration {
val data = txs.flatMap { val data = txs.flatMap {
case (txId, tx) => case (txId, tx) =>
tx.nodes.collect { tx.nodes.collect {
case (nodeId, NodeCreate(cid, _, _, signatories, stakeholders, _)) => case (nodeId, NodeCreate(cid, _, _, signatories, stakeholders, _, _)) =>
(cid, EventId(txId, nodeId), signatories, stakeholders -- signatories) (cid, EventId(txId, nodeId), signatories, stakeholders -- signatories)
} }
} }

View File

@ -8,6 +8,7 @@ import java.util.UUID
import com.daml.lf.data.{ImmArray, Ref} import com.daml.lf.data.{ImmArray, Ref}
import com.daml.lf.transaction.Node.{KeyWithMaintainers, NodeCreate, NodeExercises, NodeFetch} import com.daml.lf.transaction.Node.{KeyWithMaintainers, NodeCreate, NodeExercises, NodeFetch}
import com.daml.lf.transaction.TransactionVersions
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.lf.value.ValueVersion
@ -32,7 +33,8 @@ private[dao] trait JdbcLedgerDaoDivulgenceSpec extends LoneElement with Inside {
optLocation = None, optLocation = None,
signatories = Set(alice), signatories = Set(alice),
stakeholders = Set(alice), stakeholders = Set(alice),
key = None key = None,
version = TransactionVersions.minVersion,
) )
) )
contractId -> builder.buildCommitted() contractId -> builder.buildCommitted()
@ -49,7 +51,8 @@ private[dao] trait JdbcLedgerDaoDivulgenceSpec extends LoneElement with Inside {
stakeholders = Set(bob), stakeholders = Set(bob),
key = Some( key = Some(
KeyWithMaintainers(ValueParty(bob), Set(bob)) KeyWithMaintainers(ValueParty(bob), Set(bob))
) ),
version = TransactionVersions.minVersion,
) )
) )
contractId -> builder.buildCommitted() contractId -> builder.buildCommitted()
@ -72,6 +75,7 @@ private[dao] trait JdbcLedgerDaoDivulgenceSpec extends LoneElement with Inside {
exerciseResult = None, exerciseResult = None,
key = None, key = None,
byKey = false, byKey = false,
version = TransactionVersions.minVersion,
) )
) )
builder.add( builder.add(
@ -85,7 +89,8 @@ private[dao] trait JdbcLedgerDaoDivulgenceSpec extends LoneElement with Inside {
key = Some( key = Some(
KeyWithMaintainers(ValueParty(bob), Set(bob)) KeyWithMaintainers(ValueParty(bob), Set(bob))
), ),
byKey = false byKey = false,
version = TransactionVersions.minVersion,
), ),
parentId = rootExercise, parentId = rootExercise,
) )
@ -107,6 +112,7 @@ private[dao] trait JdbcLedgerDaoDivulgenceSpec extends LoneElement with Inside {
KeyWithMaintainers(ValueParty(bob), Set(bob)) KeyWithMaintainers(ValueParty(bob), Set(bob))
), ),
byKey = false, byKey = false,
version = TransactionVersions.minVersion,
), ),
parentId = rootExercise, parentId = rootExercise,
) )
@ -119,7 +125,8 @@ private[dao] trait JdbcLedgerDaoDivulgenceSpec extends LoneElement with Inside {
stakeholders = Set(alice, bob), stakeholders = Set(alice, bob),
key = Some( key = Some(
KeyWithMaintainers(ValueParty(bob), Set(bob)) KeyWithMaintainers(ValueParty(bob), Set(bob))
) ),
version = TransactionVersions.minVersion,
), ),
parentId = nestedExercise, parentId = nestedExercise,
) )

View File

@ -19,7 +19,13 @@ import com.daml.lf.data.Ref.{Identifier, Party}
import com.daml.lf.data.{ImmArray, Ref} import com.daml.lf.data.{ImmArray, Ref}
import com.daml.lf.transaction.Node._ import com.daml.lf.transaction.Node._
import com.daml.lf.transaction.test.TransactionBuilder import com.daml.lf.transaction.test.TransactionBuilder
import com.daml.lf.transaction.{BlindingInfo, CommittedTransaction, Node, NodeId} import com.daml.lf.transaction.{
BlindingInfo,
CommittedTransaction,
Node,
NodeId,
TransactionVersions
}
import com.daml.lf.value.Value import com.daml.lf.value.Value
import com.daml.lf.value.Value.{ContractId, ContractInst, ValueRecord, ValueText, ValueUnit} import com.daml.lf.value.Value.{ContractId, ContractInst, ValueRecord, ValueText, ValueUnit}
import com.daml.logging.LoggingContext import com.daml.logging.LoggingContext
@ -110,7 +116,8 @@ private[dao] trait JdbcLedgerDaoSuite extends JdbcLedgerDaoBackend {
optLocation = None, optLocation = None,
signatories = signatories, signatories = signatories,
stakeholders = signatories, stakeholders = signatories,
key = None key = None,
version = TransactionVersions.minVersion,
) )
protected final def createWithStakeholders( protected final def createWithStakeholders(
@ -124,7 +131,8 @@ private[dao] trait JdbcLedgerDaoSuite extends JdbcLedgerDaoBackend {
optLocation = None, optLocation = None,
signatories = signatories, signatories = signatories,
stakeholders = stakeholders, stakeholders = stakeholders,
key = None key = None,
version = TransactionVersions.minVersion,
) )
private def exercise( private def exercise(
@ -145,6 +153,7 @@ private[dao] trait JdbcLedgerDaoSuite extends JdbcLedgerDaoBackend {
exerciseResult = Some(ValueText("some exercise result")), exerciseResult = Some(ValueText("some exercise result")),
key = None, key = None,
byKey = false, byKey = false,
version = TransactionVersions.minVersion,
) )
// All non-transient contracts created in a transaction // All non-transient contracts created in a transaction
@ -232,6 +241,7 @@ private[dao] trait JdbcLedgerDaoSuite extends JdbcLedgerDaoBackend {
exerciseResult = Some(ValueUnit), exerciseResult = Some(ValueUnit),
key = None, key = None,
byKey = false, byKey = false,
version = TransactionVersions.minVersion,
) )
) )
txBuilder.add( txBuilder.add(
@ -244,6 +254,7 @@ private[dao] trait JdbcLedgerDaoSuite extends JdbcLedgerDaoBackend {
stakeholders = Set(alice), stakeholders = Set(alice),
None, None,
byKey = false, byKey = false,
version = TransactionVersions.minVersion,
), ),
exerciseId, exerciseId,
) )
@ -561,7 +572,8 @@ private[dao] trait JdbcLedgerDaoSuite extends JdbcLedgerDaoBackend {
optLocation = None, optLocation = None,
signatories = Set(party), signatories = Set(party),
stakeholders = Set(party), stakeholders = Set(party),
key = Some(KeyWithMaintainers(ValueText(key), Set(party))) key = Some(KeyWithMaintainers(ValueText(key), Set(party))),
version = TransactionVersions.minVersion,
)) ))
nextOffset() -> nextOffset() ->
LedgerEntry.Transaction( LedgerEntry.Transaction(
@ -600,6 +612,7 @@ private[dao] trait JdbcLedgerDaoSuite extends JdbcLedgerDaoBackend {
exerciseResult = Some(ValueUnit), exerciseResult = Some(ValueUnit),
key = maybeKey.map(k => KeyWithMaintainers(ValueText(k), Set(party))), key = maybeKey.map(k => KeyWithMaintainers(ValueText(k), Set(party))),
byKey = false, byKey = false,
version = TransactionVersions.minVersion,
)) ))
nextOffset() -> LedgerEntry.Transaction( nextOffset() -> LedgerEntry.Transaction(
commandId = Some(UUID.randomUUID().toString), commandId = Some(UUID.randomUUID().toString),
@ -627,6 +640,7 @@ private[dao] trait JdbcLedgerDaoSuite extends JdbcLedgerDaoBackend {
None, None,
KeyWithMaintainers(ValueText(key), Set(party)), KeyWithMaintainers(ValueText(key), Set(party)),
result, result,
version = TransactionVersions.minVersion,
)) ))
nextOffset() -> LedgerEntry.Transaction( nextOffset() -> LedgerEntry.Transaction(
commandId = Some(UUID.randomUUID().toString), commandId = Some(UUID.randomUUID().toString),
@ -656,6 +670,7 @@ private[dao] trait JdbcLedgerDaoSuite extends JdbcLedgerDaoBackend {
stakeholders = Set(party), stakeholders = Set(party),
None, None,
byKey = false, byKey = false,
version = TransactionVersions.minVersion,
)) ))
nextOffset() -> LedgerEntry.Transaction( nextOffset() -> LedgerEntry.Transaction(
commandId = Some(UUID.randomUUID().toString), commandId = Some(UUID.randomUUID().toString),

View File

@ -115,7 +115,7 @@ final class PostCommitValidationSpec extends AnyWordSpec with Matchers {
val error = val error =
store.validate( store.validate(
transaction = TxBuilder.justCommitted(createContract, TxBuilder.fetch(createContract)), transaction = TxBuilder.justCommitted(createContract, txBuilder.fetch(createContract)),
transactionLedgerEffectiveTime = Instant.now(), transactionLedgerEffectiveTime = Instant.now(),
divulged = Set.empty, divulged = Set.empty,
) )
@ -130,7 +130,7 @@ final class PostCommitValidationSpec extends AnyWordSpec with Matchers {
val error = val error =
store.validate( store.validate(
transaction = TxBuilder.justCommitted(TxBuilder.fetch(divulgedContract)), transaction = TxBuilder.justCommitted(txBuilder.fetch(divulgedContract)),
transactionLedgerEffectiveTime = Instant.now(), transactionLedgerEffectiveTime = Instant.now(),
divulged = Set(divulgedContract.coid), divulged = Set(divulgedContract.coid),
) )
@ -145,7 +145,7 @@ final class PostCommitValidationSpec extends AnyWordSpec with Matchers {
val error = val error =
store.validate( store.validate(
transaction = TxBuilder.justCommitted(TxBuilder.fetch(missingCreate)), transaction = TxBuilder.justCommitted(txBuilder.fetch(missingCreate)),
transactionLedgerEffectiveTime = Instant.now(), transactionLedgerEffectiveTime = Instant.now(),
divulged = Set.empty, divulged = Set.empty,
) )
@ -161,7 +161,7 @@ final class PostCommitValidationSpec extends AnyWordSpec with Matchers {
val error = val error =
store.validate( store.validate(
transaction = TxBuilder transaction = TxBuilder
.justCommitted(createContract, TxBuilder.lookupByKey(createContract, found = true)), .justCommitted(createContract, txBuilder.lookupByKey(createContract, found = true)),
transactionLedgerEffectiveTime = Instant.now(), transactionLedgerEffectiveTime = Instant.now(),
divulged = Set.empty, divulged = Set.empty,
) )
@ -177,7 +177,7 @@ final class PostCommitValidationSpec extends AnyWordSpec with Matchers {
val error = val error =
store.validate( store.validate(
transaction = transaction =
TxBuilder.justCommitted(TxBuilder.lookupByKey(missingCreate, found = true)), TxBuilder.justCommitted(txBuilder.lookupByKey(missingCreate, found = true)),
transactionLedgerEffectiveTime = Instant.now(), transactionLedgerEffectiveTime = Instant.now(),
divulged = Set.empty, divulged = Set.empty,
) )
@ -198,7 +198,7 @@ final class PostCommitValidationSpec extends AnyWordSpec with Matchers {
val error = val error =
store.validate( store.validate(
transaction = transaction =
TxBuilder.justCommitted(TxBuilder.lookupByKey(missingContract, found = false)), TxBuilder.justCommitted(txBuilder.lookupByKey(missingContract, found = false)),
transactionLedgerEffectiveTime = Instant.now(), transactionLedgerEffectiveTime = Instant.now(),
divulged = Set.empty, divulged = Set.empty,
) )
@ -276,7 +276,7 @@ final class PostCommitValidationSpec extends AnyWordSpec with Matchers {
val error = val error =
store.validate( store.validate(
transaction = TxBuilder.justCommitted(TxBuilder.fetch(committedContract)), transaction = TxBuilder.justCommitted(txBuilder.fetch(committedContract)),
transactionLedgerEffectiveTime = committedContractLedgerEffectiveTime, transactionLedgerEffectiveTime = committedContractLedgerEffectiveTime,
divulged = Set.empty, divulged = Set.empty,
) )
@ -289,7 +289,7 @@ final class PostCommitValidationSpec extends AnyWordSpec with Matchers {
val error = val error =
store.validate( store.validate(
transaction = TxBuilder.justCommitted(TxBuilder.fetch(committedContract)), transaction = TxBuilder.justCommitted(txBuilder.fetch(committedContract)),
transactionLedgerEffectiveTime = committedContractLedgerEffectiveTime.minusNanos(1), transactionLedgerEffectiveTime = committedContractLedgerEffectiveTime.minusNanos(1),
divulged = Set.empty, divulged = Set.empty,
) )
@ -308,7 +308,7 @@ final class PostCommitValidationSpec extends AnyWordSpec with Matchers {
val error = val error =
store.validate( store.validate(
transaction = transaction =
TxBuilder.justCommitted(TxBuilder.lookupByKey(committedContract, found = true)), TxBuilder.justCommitted(txBuilder.lookupByKey(committedContract, found = true)),
transactionLedgerEffectiveTime = committedContractLedgerEffectiveTime, transactionLedgerEffectiveTime = committedContractLedgerEffectiveTime,
divulged = Set.empty, divulged = Set.empty,
) )
@ -322,7 +322,7 @@ final class PostCommitValidationSpec extends AnyWordSpec with Matchers {
val error = val error =
store.validate( store.validate(
transaction = transaction =
TxBuilder.justCommitted(TxBuilder.lookupByKey(committedContract, found = false)), TxBuilder.justCommitted(txBuilder.lookupByKey(committedContract, found = false)),
transactionLedgerEffectiveTime = committedContractLedgerEffectiveTime, transactionLedgerEffectiveTime = committedContractLedgerEffectiveTime,
divulged = Set.empty, divulged = Set.empty,
) )
@ -368,7 +368,7 @@ final class PostCommitValidationSpec extends AnyWordSpec with Matchers {
val error = val error =
store.validate( store.validate(
transaction = TxBuilder.justCommitted(TxBuilder.fetch(divulgedContract)), transaction = TxBuilder.justCommitted(txBuilder.fetch(divulgedContract)),
transactionLedgerEffectiveTime = Instant.now(), transactionLedgerEffectiveTime = Instant.now(),
divulged = Set.empty, divulged = Set.empty,
) )
@ -402,8 +402,10 @@ final class PostCommitValidationSpec extends AnyWordSpec with Matchers {
object PostCommitValidationSpec { object PostCommitValidationSpec {
val txBuilder = new TxBuilder()
private def genTestCreate(): TxBuilder.Create = private def genTestCreate(): TxBuilder.Create =
TxBuilder.create( txBuilder.create(
id = s"#${UUID.randomUUID}", id = s"#${UUID.randomUUID}",
template = "foo:bar:baz", template = "foo:bar:baz",
argument = TxBuilder.record("field" -> "value"), argument = TxBuilder.record("field" -> "value"),
@ -413,7 +415,7 @@ object PostCommitValidationSpec {
) )
private def genTestExercise(create: TxBuilder.Create): TxBuilder.Exercise = private def genTestExercise(create: TxBuilder.Create): TxBuilder.Exercise =
TxBuilder.exercise( txBuilder.exercise(
contract = create, contract = create,
choice = "SomeChoice", choice = "SomeChoice",
consuming = true, consuming = true,

View File

@ -272,13 +272,18 @@ private[kvutils] class TransactionCommitter(
recordedTemplateId, recordedTemplateId,
recordedOptLocation @ _, recordedOptLocation @ _,
recordedKey, recordedKey,
recordedResult), recordedResult,
recordedVersion,
),
Node.NodeLookupByKey( Node.NodeLookupByKey(
replayedTemplateId, replayedTemplateId,
replayedOptLocation @ _, replayedOptLocation @ _,
replayedKey, replayedKey,
replayedResult)) replayedResult,
if recordedTemplateId == replayedTemplateId && recordedKey == replayedKey replayedVersion,
))
if recordedVersion == replayedVersion &&
recordedTemplateId == replayedTemplateId && recordedKey == replayedKey
&& !resultIsCreatedInTx(recordedTx, recordedResult) && !resultIsCreatedInTx(recordedTx, recordedResult)
&& !resultIsCreatedInTx(replayedTx, replayedResult) => && !resultIsCreatedInTx(replayedTx, replayedResult) =>
RejectionReason.Inconsistent(validationError.msg) RejectionReason.Inconsistent(validationError.msg)

View File

@ -8,7 +8,7 @@ import com.daml.lf.data.{BackStack, ImmArray}
import com.daml.lf.engine.Blinding import com.daml.lf.engine.Blinding
import com.daml.lf.transaction.Transaction.Transaction import com.daml.lf.transaction.Transaction.Transaction
import com.daml.lf.transaction.test.TransactionBuilder import com.daml.lf.transaction.test.TransactionBuilder
import com.daml.lf.transaction.Node import com.daml.lf.transaction.{Node, TransactionVersions}
import com.daml.lf.value.Value.{ContractId, ContractInst, ValueText} import com.daml.lf.value.Value.{ContractId, ContractInst, ValueText}
import org.scalatest.matchers.should.Matchers import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec import org.scalatest.wordspec.AnyWordSpec
@ -28,7 +28,8 @@ class ProjectionsSpec extends AnyWordSpec with Matchers {
optLocation = None, optLocation = None,
signatories = signatories, signatories = signatories,
stakeholders = stakeholders, stakeholders = stakeholders,
key = None key = None,
version = TransactionVersions.minVersion,
) )
def makeExeNode( def makeExeNode(
@ -54,6 +55,7 @@ class ProjectionsSpec extends AnyWordSpec with Matchers {
exerciseResult = None, exerciseResult = None,
key = None, key = None,
byKey = false, byKey = false,
version = TransactionVersions.minVersion,
) )
def project(tx: Transaction) = { def project(tx: Transaction) = {

View File

@ -36,6 +36,7 @@ import org.scalatest.Inspectors.forEvery
import scala.collection.JavaConverters._ import scala.collection.JavaConverters._
class TransactionCommitterSpec extends AnyWordSpec with Matchers with MockitoSugar { class TransactionCommitterSpec extends AnyWordSpec with Matchers with MockitoSugar {
private[this] val txBuilder = TransactionBuilder()
private val metrics = new Metrics(new MetricRegistry) private val metrics = new Metrics(new MetricRegistry)
private val aDamlTransactionEntry = DamlTransactionEntry.newBuilder private val aDamlTransactionEntry = DamlTransactionEntry.newBuilder
.setTransaction(Conversions.encodeTransaction(TransactionBuilder.Empty)) .setTransaction(Conversions.encodeTransaction(TransactionBuilder.Empty))
@ -352,7 +353,7 @@ class TransactionCommitterSpec extends AnyWordSpec with Matchers with MockitoSug
val dummyValue = TransactionBuilder.record("field" -> "value") val dummyValue = TransactionBuilder.record("field" -> "value")
def create(contractId: String, key: String = "key"): TransactionBuilder.Create = def create(contractId: String, key: String = "key"): TransactionBuilder.Create =
TransactionBuilder.create( txBuilder.create(
id = contractId, id = contractId,
template = "dummyPackage:DummyModule:DummyTemplate", template = "dummyPackage:DummyModule:DummyTemplate",
argument = dummyValue, argument = dummyValue,
@ -386,7 +387,7 @@ class TransactionCommitterSpec extends AnyWordSpec with Matchers with MockitoSug
val create1 = create("#someContractId") val create1 = create("#someContractId")
val create2 = create("#otherContractId") val create2 = create("#otherContractId")
val exercise = TransactionBuilder.exercise( val exercise = txBuilder.exercise(
contract = createInput, contract = createInput,
choice = "DummyChoice", choice = "DummyChoice",
consuming = false, consuming = false,
@ -398,7 +399,7 @@ class TransactionCommitterSpec extends AnyWordSpec with Matchers with MockitoSug
val lookupNodes @ Seq(lookup1, lookup2, lookupNone, lookupOther @ _) = val lookupNodes @ Seq(lookup1, lookup2, lookupNone, lookupOther @ _) =
Seq(create1 -> true, create2 -> true, create1 -> false, otherKeyCreate -> true) map { Seq(create1 -> true, create2 -> true, create1 -> false, otherKeyCreate -> true) map {
case (create, found) => TransactionBuilder.lookupByKey(create, found) case (create, found) => txBuilder.lookupByKey(create, found)
} }
val Seq(tx1, tx2, txNone, txOther) = lookupNodes map { node => val Seq(tx1, tx2, txNone, txOther) = lookupNodes map { node =>
val builder = TransactionBuilder() val builder = TransactionBuilder()