mirror of
https://github.com/digital-asset/daml.git
synced 2024-09-20 09:17:43 +03:00
[DAML-LF] Version new contractId in Proto values (#4460)
* version new contractId CHANGELOG_BEGIN CHANGELOG_END
This commit is contained in:
parent
a440a3fabd
commit
8394f4ba7d
@ -251,10 +251,13 @@ private[data] final class IdStringImpl extends IdString {
|
||||
Either.cond(isA(s), s, s)
|
||||
|
||||
override def fromString(s: String): Either[String, ContractIdString] =
|
||||
toEither(s).fold(ContractIdStringV0.fromString, ContractIdStringV1.fromString)
|
||||
if (isA(s))
|
||||
ContractIdStringV0.fromString(s)
|
||||
else
|
||||
ContractIdStringV1.fromString(s)
|
||||
|
||||
override def isA(s: ContractIdString): Boolean =
|
||||
s.startsWith("$0")
|
||||
!isB(s)
|
||||
|
||||
override def toA(s: ContractIdString): Option[ContractIdStringV0] =
|
||||
Some(s).filter(isA)
|
||||
@ -263,7 +266,7 @@ private[data] final class IdStringImpl extends IdString {
|
||||
toA(s).getOrElse(throw new IllegalArgumentException("expect V0 ContractId get V1"))
|
||||
|
||||
override def isB(s: ContractIdString): Boolean =
|
||||
!isA(s)
|
||||
s.startsWith("$0")
|
||||
|
||||
override def toB(s: ContractIdString): Option[ContractIdStringV1] =
|
||||
Some(s).filter(isB)
|
||||
|
@ -9,7 +9,7 @@ class EngineInfoTest extends WordSpec with Matchers {
|
||||
EngineInfo.getClass.getSimpleName should {
|
||||
"show supported LF, Transaction and Value versions" in {
|
||||
EngineInfo.show shouldBe
|
||||
"DAML LF Engine supports LF versions: 0, 0.dev, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.dev; Transaction versions: 1, 2, 3, 4, 5, 6, 7, 8, 9; Value versions: 1, 2, 3, 4, 5, 6, 7"
|
||||
"DAML LF Engine supports LF versions: 0, 0.dev, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.dev; Transaction versions: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10; Value versions: 1, 2, 3, 4, 5, 6, 7"
|
||||
}
|
||||
|
||||
"toString returns the same value as show" in {
|
||||
|
@ -1048,7 +1048,9 @@ class EngineTest extends WordSpec with Matchers with EitherValues with BazelRunf
|
||||
(Some[Name]("fetcher"), ValueParty(party)),
|
||||
)
|
||||
|
||||
def makeContract[Cid](tid: Ref.QualifiedName, targs: ImmArray[(Option[Name], Value[Cid])]) =
|
||||
def makeContract[Cid <: ContractId](
|
||||
tid: Ref.QualifiedName,
|
||||
targs: ImmArray[(Option[Name], Value[Cid])]) =
|
||||
ContractInst(
|
||||
TypeConName(basicTestsPkgId, tid),
|
||||
assertAsVersionedValue(ValueRecord(Some(Identifier(basicTestsPkgId, tid)), targs)),
|
||||
|
@ -63,6 +63,7 @@ object LanguageVersion {
|
||||
val typeRep = v1_7
|
||||
val genMap = v1_dev
|
||||
val typeSynonyms = v1_dev
|
||||
val scenarioMustFailAtMsg = v1_dev
|
||||
|
||||
/** Unstable, experimental features. This should stay in 1.dev forever.
|
||||
* Features implemented with this flag should be moved to a separate
|
||||
|
@ -1,7 +1,8 @@
|
||||
// Copyright (c) 2020 The DAML Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.digitalasset.daml.lf.value
|
||||
package com.digitalasset.daml.lf
|
||||
package value
|
||||
|
||||
import com.digitalasset.daml.lf.data.Ref._
|
||||
import com.digitalasset.daml.lf.data._
|
||||
@ -151,19 +152,31 @@ object ValueGenerators {
|
||||
|
||||
def valueGenMapGen: Gen[ValueGenMap[ContractId]] = valueGenMapGen(0)
|
||||
|
||||
def coidGen: Gen[ContractId] = {
|
||||
val genRel: Gen[ContractId] =
|
||||
private val genRel: Gen[ContractId] =
|
||||
Arbitrary.arbInt.arbitrary.map(i => RelativeContractId(Tx.NodeId(i)))
|
||||
val genAbs: Gen[ContractId] =
|
||||
private val genAbsV0: Gen[ContractId] =
|
||||
Gen.zip(Gen.alphaChar, Gen.alphaStr) map {
|
||||
case (h, t) => AbsoluteContractId(toContractId(h +: t))
|
||||
}
|
||||
Gen.frequency((1, genRel), (3, genAbs))
|
||||
private val genAbsV1: Gen[ContractId] = {
|
||||
val random = crypto.Hash.secureRandom
|
||||
Gen.alphaNumStr.map(
|
||||
suffix =>
|
||||
AbsoluteContractId(
|
||||
Ref.ContractIdStringV1.assertFromString("$0" + random().toHexaString + suffix)))
|
||||
}
|
||||
|
||||
def coidValueGen: Gen[ValueContractId[ContractId]] = {
|
||||
def coidGenV0: Gen[ContractId] =
|
||||
Gen.frequency((1, genRel), (3, genAbsV0))
|
||||
|
||||
def coidValueGenV0: Gen[ValueContractId[ContractId]] =
|
||||
coidGenV0.map(ValueContractId(_))
|
||||
|
||||
def coidGen: Gen[ContractId] =
|
||||
Gen.frequency((2, genRel), (3, genAbsV0), (3, genAbsV1))
|
||||
|
||||
def coidValueGen: Gen[ValueContractId[ContractId]] =
|
||||
coidGen.map(ValueContractId(_))
|
||||
}
|
||||
|
||||
private def valueGen(nesting: Int): Gen[Value[ContractId]] = {
|
||||
Gen.sized(sz => {
|
||||
@ -214,7 +227,7 @@ object ValueGenerators {
|
||||
def versionedValueGen: Gen[VersionedValue[ContractId]] =
|
||||
for {
|
||||
value <- valueGen
|
||||
minVersion = ValueVersions.assertAssignVersion(value)
|
||||
minVersion = ValueVersions.assertAssignVersion(ValueVersions.VersionCid, value)
|
||||
version <- valueVersionGen(minVersion)
|
||||
} yield VersionedValue(version, value)
|
||||
|
||||
|
@ -9,7 +9,7 @@ import com.digitalasset.daml.lf.data.Ref.{Name, Party}
|
||||
import com.digitalasset.daml.lf.transaction.Node._
|
||||
import VersionTimeline.Implicits._
|
||||
import com.digitalasset.daml.lf.value.Value
|
||||
import com.digitalasset.daml.lf.value.Value.VersionedValue
|
||||
import com.digitalasset.daml.lf.value.Value.{ContractId, VersionedValue}
|
||||
import com.digitalasset.daml.lf.value.{ValueCoder, ValueOuterClass, ValueVersion}
|
||||
import com.digitalasset.daml.lf.value.ValueCoder.{DecodeError, EncodeError}
|
||||
import com.google.protobuf.ProtocolStringList
|
||||
@ -24,8 +24,6 @@ import scala.collection.immutable.HashMap
|
||||
|
||||
object TransactionCoder {
|
||||
|
||||
import ValueCoder.{DecodeCid, EncodeCid, codecContractId}
|
||||
|
||||
abstract class EncodeNid[-Nid] private[lf] {
|
||||
def asString(id: Nid): String
|
||||
}
|
||||
@ -77,7 +75,7 @@ object TransactionCoder {
|
||||
* @return protobuf wire format contract instance
|
||||
*/
|
||||
def encodeContractInstance[Cid](
|
||||
encodeCid: EncodeCid[Cid],
|
||||
encodeCid: ValueCoder.EncodeCid[Cid],
|
||||
coinst: Value.ContractInst[Value.VersionedValue[Cid]],
|
||||
): Either[EncodeError, TransactionOuterClass.ContractInstance] =
|
||||
encodeValue(encodeCid, coinst.arg).map {
|
||||
@ -98,7 +96,7 @@ object TransactionCoder {
|
||||
* @return contract instance value
|
||||
*/
|
||||
def decodeContractInstance[Cid](
|
||||
decodeCid: DecodeCid[Cid],
|
||||
decodeCid: ValueCoder.DecodeCid[Cid],
|
||||
protoCoinst: TransactionOuterClass.ContractInstance,
|
||||
): Either[DecodeError, Value.ContractInst[Value.VersionedValue[Cid]]] =
|
||||
for {
|
||||
@ -107,7 +105,7 @@ object TransactionCoder {
|
||||
} yield Value.ContractInst(id, value, (protoCoinst.getAgreement))
|
||||
|
||||
private def encodeKeyWithMaintainers[Cid](
|
||||
encodeCid: EncodeCid[Cid],
|
||||
encodeCid: ValueCoder.EncodeCid[Cid],
|
||||
key: KeyWithMaintainers[Value.VersionedValue[Cid]],
|
||||
): Either[EncodeError, (ValueVersion, TransactionOuterClass.KeyWithMaintainers)] =
|
||||
encodeValue(encodeCid, key.key).map {
|
||||
@ -134,7 +132,7 @@ object TransactionCoder {
|
||||
*/
|
||||
def encodeNode[Nid, Cid](
|
||||
encodeNid: EncodeNid[Nid],
|
||||
encodeCid: EncodeCid[Cid],
|
||||
encodeCid: ValueCoder.EncodeCid[Cid],
|
||||
transactionVersion: TransactionVersion,
|
||||
nodeId: Nid,
|
||||
node: GenNode[Nid, Cid, Value.VersionedValue[Cid]],
|
||||
@ -149,29 +147,32 @@ object TransactionCoder {
|
||||
}
|
||||
node match {
|
||||
case nc @ NodeCreate(_, _, _, _, _, _, _) =>
|
||||
encodeContractInstance(encodeCid, nc.coinst).flatMap { inst =>
|
||||
val createBuilder = TransactionOuterClass.NodeCreate
|
||||
val createBuilder =
|
||||
TransactionOuterClass.NodeCreate
|
||||
.newBuilder()
|
||||
.setContractIdOrStruct(encodeCid, transactionVersion, nc.coid)(
|
||||
_.setContractId(_),
|
||||
_.setContractIdStruct(_),
|
||||
)
|
||||
.setContractInstance(inst)
|
||||
.addAllStakeholders(nc.stakeholders.toSet[String].asJava)
|
||||
.addAllSignatories(nc.signatories.toSet[String].asJava)
|
||||
nc.key match {
|
||||
case None => Right(nodeBuilder.setCreate(createBuilder).build())
|
||||
|
||||
for {
|
||||
encodedCid <- encodeCid.encode(transactionVersion, nc.coid)
|
||||
inst <- encodeContractInstance(encodeCid, nc.coinst)
|
||||
optKey <- nc.key match {
|
||||
case None => Right(None)
|
||||
case Some(key) =>
|
||||
if (transactionVersion precedes minKeyOrLookupByKey)
|
||||
Left(EncodeError(transactionVersion, isTooOldFor = "NodeCreate's `key` field"))
|
||||
else
|
||||
encodeKeyWithMaintainers(encodeCid, key).map {
|
||||
case (_, encodedKey) =>
|
||||
createBuilder.setKeyWithMaintainers(encodedKey)
|
||||
encodeKeyWithMaintainers(encodeCid, key).map(Some(_))
|
||||
}
|
||||
|
||||
} yield {
|
||||
encodedCid.fold(createBuilder.setContractId, createBuilder.setContractIdStruct)
|
||||
createBuilder.setContractInstance(inst)
|
||||
optKey.foreach {
|
||||
case (_, encodedKey) => createBuilder.setKeyWithMaintainers(encodedKey)
|
||||
}
|
||||
nodeBuilder.setCreate(createBuilder).build()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case nf @ NodeFetch(_, _, _, _, _, _) =>
|
||||
val (vversion, etid) = ValueCoder.encodeIdentifier(
|
||||
@ -180,23 +181,22 @@ object TransactionCoder {
|
||||
)
|
||||
val fetchBuilder = TransactionOuterClass.NodeFetch
|
||||
.newBuilder()
|
||||
.setContractIdOrStruct(encodeCid, transactionVersion, nf.coid)(
|
||||
_.setContractId(_),
|
||||
_.setContractIdStruct(_),
|
||||
)
|
||||
.setTemplateId(etid)
|
||||
.setValueVersion(vversion.protoValue)
|
||||
.addAllStakeholders(nf.stakeholders.toSet[String].asJava)
|
||||
.addAllSignatories(nf.signatories.toSet[String].asJava)
|
||||
|
||||
if (transactionVersion precedes TransactionVersions.minFetchActors) {
|
||||
if (nf.actingParties.nonEmpty)
|
||||
Left(EncodeError(transactionVersion, isTooOldFor = "NodeFetch actors"))
|
||||
else Right(nodeBuilder.setFetch(fetchBuilder).build())
|
||||
} else {
|
||||
val fetchBuilderWithActors =
|
||||
fetchBuilder.addAllActors(nf.actingParties.getOrElse(Set.empty).toSet[String].asJava)
|
||||
Right(nodeBuilder.setFetch(fetchBuilderWithActors).build())
|
||||
for {
|
||||
encodedCid <- encodeCid.encode(transactionVersion, nf.coid)
|
||||
actors <- Either.cond(
|
||||
nf.actingParties.isEmpty || !(transactionVersion precedes TransactionVersions.minFetchActors),
|
||||
nf.actingParties.getOrElse(Set.empty),
|
||||
EncodeError(transactionVersion, isTooOldFor = "NodeFetch actors")
|
||||
)
|
||||
} yield {
|
||||
encodedCid.fold(fetchBuilder.setContractId, fetchBuilder.setContractIdStruct)
|
||||
actors.foreach(fetchBuilder.addActors)
|
||||
nodeBuilder.setFetch(fetchBuilder).build()
|
||||
}
|
||||
|
||||
case ne @ NodeExercises(_, _, _, _, _, _, _, _, _, _, _, _, _, _) =>
|
||||
@ -210,38 +210,31 @@ object TransactionCoder {
|
||||
.setTemplateId(ValueCoder.encodeIdentifier(ne.templateId, Some(vversion))._2)
|
||||
.setChosenValue(arg)
|
||||
.setConsuming(ne.consuming)
|
||||
.setContractIdOrStruct(encodeCid, transactionVersion, ne.targetCoid)(
|
||||
_.setContractId(_),
|
||||
_.setContractIdStruct(_),
|
||||
)
|
||||
.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)
|
||||
_ <- if (transactionVersion precedes minNoControllers) {
|
||||
if (ne.controllers == ne.actingParties) {
|
||||
exBuilder.addAllControllers(ne.controllers.toSet[String].asJava)
|
||||
Right(())
|
||||
} else {
|
||||
Left(
|
||||
encodedCid <- encodeCid.encode(transactionVersion, ne.targetCoid)
|
||||
controllers <- if (transactionVersion precedes minNoControllers)
|
||||
Either.cond(
|
||||
ne.controllers == ne.actingParties,
|
||||
ne.controllers,
|
||||
EncodeError(
|
||||
s"As of version $minNoControllers, the controllers and actingParties of an exercise node _must_ be the same, but I got ${ne.controllers} as controllers and ${ne.actingParties} as actingParties.",
|
||||
),
|
||||
)
|
||||
}
|
||||
} else Right(())
|
||||
_ <- (retValue, transactionVersion precedes minExerciseResult) match {
|
||||
case (Some(rv), false) =>
|
||||
exBuilder.setReturnValue(rv._2)
|
||||
Right(())
|
||||
case (None, false) =>
|
||||
Left(
|
||||
EncodeError(
|
||||
)
|
||||
else
|
||||
Right(Set.empty)
|
||||
result <- if (transactionVersion precedes minExerciseResult)
|
||||
Right(None)
|
||||
else
|
||||
Either.cond(
|
||||
test = retValue.nonEmpty,
|
||||
right = retValue,
|
||||
left = EncodeError(
|
||||
s"Trying to encode transaction of version $transactionVersion, which requires the exercise return value, but did not get exercise return value in node.",
|
||||
),
|
||||
)
|
||||
case (_, true) => Right(())
|
||||
}
|
||||
)
|
||||
_ <- Right(
|
||||
ne.key
|
||||
.map { kWithM =>
|
||||
@ -259,32 +252,36 @@ object TransactionCoder {
|
||||
}
|
||||
.getOrElse(()),
|
||||
)
|
||||
} yield nodeBuilder.setExercise(exBuilder).build()
|
||||
} yield {
|
||||
encodedCid.fold(exBuilder.setContractId, exBuilder.setContractIdStruct)
|
||||
controllers.foreach(exBuilder.addControllers)
|
||||
result.foreach { case (_, v) => exBuilder.setReturnValue(v) }
|
||||
nodeBuilder.setExercise(exBuilder).build()
|
||||
}
|
||||
|
||||
case nlbk @ NodeLookupByKey(_, _, _, _) =>
|
||||
if (transactionVersion precedes minKeyOrLookupByKey)
|
||||
Left(EncodeError(transactionVersion, isTooOldFor = "NodeLookupByKey transaction nodes"))
|
||||
else
|
||||
encodeKeyWithMaintainers(encodeCid, nlbk.key).map {
|
||||
case (vversion, key) =>
|
||||
val nlbkBuilder = TransactionOuterClass.NodeLookupByKey
|
||||
.newBuilder()
|
||||
val nlbkBuilder = TransactionOuterClass.NodeLookupByKey.newBuilder()
|
||||
for {
|
||||
_ <- Either.cond(
|
||||
test = !(transactionVersion precedes minKeyOrLookupByKey),
|
||||
right = (),
|
||||
left =
|
||||
EncodeError(transactionVersion, isTooOldFor = "NodeLookupByKey transaction nodes")
|
||||
)
|
||||
versionAndKey <- encodeKeyWithMaintainers(encodeCid, nlbk.key)
|
||||
encodedCid <- nlbk.result traverseU (cid => encodeCid.encode(transactionVersion, cid))
|
||||
} yield {
|
||||
val (vversion, key) = versionAndKey
|
||||
nlbkBuilder
|
||||
.setTemplateId(ValueCoder.encodeIdentifier(nlbk.templateId, Some(vversion))._2)
|
||||
.setKeyWithMaintainers(key)
|
||||
nlbk.result match {
|
||||
case None => ()
|
||||
case Some(result) =>
|
||||
nlbkBuilder.setContractIdOrStruct(encodeCid, transactionVersion, result)(
|
||||
_.setContractId(_),
|
||||
_.setContractIdStruct(_),
|
||||
)
|
||||
}
|
||||
encodedCid.foreach(_.fold(nlbkBuilder.setContractId, nlbkBuilder.setContractIdStruct))
|
||||
nodeBuilder.setLookupByKey(nlbkBuilder).build()
|
||||
}
|
||||
}
|
||||
}
|
||||
private def decodeKeyWithMaintainers[Cid](
|
||||
decodeCid: DecodeCid[Cid],
|
||||
decodeCid: ValueCoder.DecodeCid[Cid],
|
||||
keyWithMaintainers: TransactionOuterClass.KeyWithMaintainers,
|
||||
): Either[DecodeError, KeyWithMaintainers[Value.VersionedValue[Cid]]] =
|
||||
for {
|
||||
@ -303,7 +300,7 @@ object TransactionCoder {
|
||||
*/
|
||||
def decodeNode[Nid, Cid](
|
||||
decodeNid: DecodeNid[Nid],
|
||||
decodeCid: DecodeCid[Cid],
|
||||
decodeCid: ValueCoder.DecodeCid[Cid],
|
||||
txVersion: TransactionVersion,
|
||||
protoNode: TransactionOuterClass.Node,
|
||||
): Either[DecodeError, (Nid, GenNode[Nid, Cid, Value.VersionedValue[Cid]])] = {
|
||||
@ -321,9 +318,10 @@ object TransactionCoder {
|
||||
for {
|
||||
ni <- nodeId
|
||||
protoCreate = protoNode.getCreate
|
||||
c <- protoCreate.decodeContractIdOrStruct(decodeCid, txVersion)(
|
||||
_.getContractId,
|
||||
_.getContractIdStruct,
|
||||
c <- decodeCid.decode(
|
||||
txVersion,
|
||||
protoCreate.getContractId,
|
||||
protoCreate.getContractIdStruct,
|
||||
)
|
||||
ci <- decodeContractInstance(decodeCid, protoCreate.getContractInstance)
|
||||
stakeholders <- toPartySet(protoCreate.getStakeholdersList)
|
||||
@ -339,10 +337,7 @@ object TransactionCoder {
|
||||
for {
|
||||
ni <- nodeId
|
||||
templateId <- ValueCoder.decodeIdentifier(protoFetch.getTemplateId)
|
||||
c <- protoFetch.decodeContractIdOrStruct(decodeCid, txVersion)(
|
||||
_.getContractId,
|
||||
_.getContractIdStruct,
|
||||
)
|
||||
c <- decodeCid.decode(txVersion, protoFetch.getContractId, protoFetch.getContractIdStruct)
|
||||
actingPartiesSet <- toPartySet(protoFetch.getActorsList)
|
||||
_ <- if ((txVersion precedes TransactionVersions.minFetchActors) && actingPartiesSet.nonEmpty)
|
||||
Left(DecodeError(txVersion, isTooOldFor = "NodeFetch actors"))
|
||||
@ -395,9 +390,10 @@ object TransactionCoder {
|
||||
} else Right(None)
|
||||
|
||||
ni <- nodeId
|
||||
targetCoid <- protoExe.decodeContractIdOrStruct(decodeCid, txVersion)(
|
||||
_.getContractId,
|
||||
_.getContractIdStruct,
|
||||
targetCoid <- decodeCid.decode(
|
||||
txVersion,
|
||||
protoExe.getContractId,
|
||||
protoExe.getContractIdStruct,
|
||||
)
|
||||
children <- childrenOrError
|
||||
cv <- decodeValue(decodeCid, protoExe.getChosenValue)
|
||||
@ -445,9 +441,10 @@ object TransactionCoder {
|
||||
ni <- nodeId
|
||||
templateId <- ValueCoder.decodeIdentifier(protoLookupByKey.getTemplateId)
|
||||
key <- decodeKeyWithMaintainers(decodeCid, protoLookupByKey.getKeyWithMaintainers)
|
||||
cid <- protoLookupByKey.decodeOptionalContractIdOrStruct(decodeCid, txVersion)(
|
||||
_.getContractId,
|
||||
_.getContractIdStruct,
|
||||
cid <- decodeCid.decodeOptional(
|
||||
txVersion,
|
||||
protoLookupByKey.getContractId,
|
||||
protoLookupByKey.getContractIdStruct,
|
||||
)
|
||||
} yield (ni, NodeLookupByKey[Cid, Value.VersionedValue[Cid]](templateId, None, key, cid))
|
||||
case NodeTypeCase.NODETYPE_NOT_SET => Left(DecodeError("Unset Node type"))
|
||||
@ -465,9 +462,9 @@ object TransactionCoder {
|
||||
* @tparam Cid contract id type
|
||||
* @return protobuf encoded transaction
|
||||
*/
|
||||
def encodeTransaction[Nid, Cid](
|
||||
def encodeTransaction[Nid, Cid <: ContractId](
|
||||
encodeNid: EncodeNid[Nid],
|
||||
encodeCid: EncodeCid[Cid],
|
||||
encodeCid: ValueCoder.EncodeCid[Cid],
|
||||
tx: GenTransaction[Nid, Cid, VersionedValue[Cid]],
|
||||
): Either[EncodeError, TransactionOuterClass.Transaction] =
|
||||
encodeTransactionWithCustomVersion(
|
||||
@ -488,7 +485,7 @@ object TransactionCoder {
|
||||
*/
|
||||
private[transaction] def encodeTransactionWithCustomVersion[Nid, Cid](
|
||||
encodeNid: EncodeNid[Nid],
|
||||
encodeCid: EncodeCid[Cid],
|
||||
encodeCid: ValueCoder.EncodeCid[Cid],
|
||||
transaction: VersionedTransaction[Nid, Cid],
|
||||
): Either[EncodeError, TransactionOuterClass.Transaction] = {
|
||||
val tx = transaction.transaction
|
||||
@ -522,7 +519,7 @@ object TransactionCoder {
|
||||
})
|
||||
}
|
||||
|
||||
private def decodeVersion(vs: String): Either[DecodeError, TransactionVersion] =
|
||||
def decodeVersion(vs: String): Either[DecodeError, TransactionVersion] =
|
||||
TransactionVersions
|
||||
.isAcceptedVersion(vs)
|
||||
.fold[Either[DecodeError, TransactionVersion]](
|
||||
@ -544,7 +541,7 @@ object TransactionCoder {
|
||||
*/
|
||||
def decodeVersionedTransaction[Nid, Cid](
|
||||
decodeNid: DecodeNid[Nid],
|
||||
decodeCid: DecodeCid[Cid],
|
||||
decodeCid: ValueCoder.DecodeCid[Cid],
|
||||
protoTx: TransactionOuterClass.Transaction,
|
||||
): Either[DecodeError, VersionedTransaction[Nid, Cid]] =
|
||||
for {
|
||||
@ -571,7 +568,7 @@ object TransactionCoder {
|
||||
*/
|
||||
private def decodeTransaction[Nid, Cid](
|
||||
decodeNid: DecodeNid[Nid],
|
||||
decodeCid: DecodeCid[Cid],
|
||||
decodeCid: ValueCoder.DecodeCid[Cid],
|
||||
txVersion: TransactionVersion,
|
||||
protoTx: TransactionOuterClass.Transaction,
|
||||
): Either[DecodeError, GenTransaction[Nid, Cid, Value.VersionedValue[Cid]]] = {
|
||||
|
@ -4,9 +4,10 @@
|
||||
package com.digitalasset.daml.lf
|
||||
package transaction
|
||||
|
||||
import com.digitalasset.daml.lf.data.Ref
|
||||
import com.digitalasset.daml.lf.transaction.Node.KeyWithMaintainers
|
||||
import com.digitalasset.daml.lf.value.Value.VersionedValue
|
||||
import com.digitalasset.daml.lf.value.ValueVersion
|
||||
import com.digitalasset.daml.lf.value.{Value, ValueVersion}
|
||||
|
||||
final case class TransactionVersion(protoValue: String)
|
||||
|
||||
@ -25,8 +26,16 @@ object TransactionVersions
|
||||
private[transaction] val minExerciseResult = TransactionVersion("7")
|
||||
private[transaction] val minContractKeyInExercise = TransactionVersion("8")
|
||||
private[transaction] val minMaintainersInExercise = TransactionVersion("9")
|
||||
private[transaction] val minContractIdV1 = TransactionVersion("10")
|
||||
|
||||
def assignVersion(a: GenTransaction[_, _, _ <: VersionedValue[_]]): TransactionVersion = {
|
||||
private def isV1(cid: Value.ContractId) = cid match {
|
||||
case Value.AbsoluteContractId(coid) => Ref.ContractIdString.isB(coid)
|
||||
case _ => false
|
||||
}
|
||||
|
||||
def assignVersion(
|
||||
a: GenTransaction[_, Value.ContractId, VersionedValue[Value.ContractId]],
|
||||
): TransactionVersion = {
|
||||
require(a != null)
|
||||
import VersionTimeline.Implicits._
|
||||
|
||||
@ -43,26 +52,30 @@ object TransactionVersions
|
||||
case _: Node.NodeLookupByKey[_, _] => true
|
||||
case _: Node.NodeFetch[_] | _: Node.NodeExercises[_, _, _] => false
|
||||
}) minKeyOrLookupByKey
|
||||
else minVersion,
|
||||
else
|
||||
minVersion,
|
||||
// a NodeFetch with actingParties implies minimum version 5
|
||||
if (a.nodes.values
|
||||
.exists { case nf: Node.NodeFetch[_] => nf.actingParties.nonEmpty; case _ => false })
|
||||
minFetchActors
|
||||
else minVersion,
|
||||
else
|
||||
minVersion,
|
||||
if (a.nodes.values
|
||||
.exists {
|
||||
case ne: Node.NodeExercises[_, _, _] => ne.exerciseResult.isDefined
|
||||
case _ => false
|
||||
})
|
||||
minExerciseResult
|
||||
else minVersion,
|
||||
else
|
||||
minVersion,
|
||||
if (a.nodes.values
|
||||
.exists {
|
||||
case ne: Node.NodeExercises[_, _, _] => ne.key.isDefined
|
||||
case _ => false
|
||||
})
|
||||
minContractKeyInExercise
|
||||
else minVersion,
|
||||
else
|
||||
minVersion,
|
||||
if (a.nodes.values
|
||||
.exists {
|
||||
case ne: Node.NodeExercises[_, _, _] =>
|
||||
@ -73,7 +86,20 @@ object TransactionVersions
|
||||
case _ => false
|
||||
})
|
||||
minMaintainersInExercise
|
||||
else minVersion,
|
||||
else
|
||||
minVersion,
|
||||
if (a.transactionSeed.isDefined || a.nodes.values.exists {
|
||||
case Node.NodeCreate(nodeSeed, coid, _, _, _, _, _) =>
|
||||
nodeSeed.isDefined || isV1(coid)
|
||||
case Node.NodeExercises(nodeSeed, coid, _, _, _, _, _, _, _, _, _, _, _, _) =>
|
||||
nodeSeed.isDefined || isV1(coid)
|
||||
case Node.NodeFetch(cid, _, _, _, _, _) =>
|
||||
isV1(cid)
|
||||
case _ => false
|
||||
})
|
||||
minContractIdV1
|
||||
else
|
||||
minVersion
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ import scala.language.higherKinds
|
||||
* version picker uses the timeline in order to describe changes to
|
||||
* that same timeline.
|
||||
*/
|
||||
private[digitalasset] object VersionTimeline {
|
||||
object VersionTimeline {
|
||||
import LanguageVersion.Minor.Dev
|
||||
import \&/.{Both, That, This}
|
||||
|
||||
@ -37,7 +37,7 @@ private[digitalasset] object VersionTimeline {
|
||||
* it appeared in an earlier engine release. If two versions occur at the
|
||||
* same index, they were both added in the same engine release.
|
||||
*/
|
||||
private[transaction] val inAscendingOrder: NonEmptyList[Release] =
|
||||
private[lf] val inAscendingOrder: NonEmptyList[Release] =
|
||||
NonEmptyList(
|
||||
That(LanguageVersion(LMV.V0, "")),
|
||||
That(LanguageVersion(LMV.V0, Dev)),
|
||||
@ -59,7 +59,7 @@ private[digitalasset] object VersionTimeline {
|
||||
// FIXME https://github.com/digital-asset/daml/issues/2256
|
||||
// * change the following line when LF 1.8 is frozen.
|
||||
// * do not insert line after this once until 1.8 is frozen.
|
||||
This(This(ValueVersion("7"))),
|
||||
This(Both(ValueVersion("7"), TransactionVersion("10"))),
|
||||
// add new versions above this line (but see more notes below)
|
||||
That(LanguageVersion(LMV.V1, Dev)),
|
||||
// do *not* backfill to make more Boths, because such would
|
||||
@ -74,7 +74,7 @@ private[digitalasset] object VersionTimeline {
|
||||
// supported by this release".
|
||||
)
|
||||
|
||||
def foldRelease[Z: Semigroup](
|
||||
private[lf] def foldRelease[Z: Semigroup](
|
||||
av: Release,
|
||||
)(v: ValueVersion => Z, t: TransactionVersion => Z, l: LanguageVersion => Z): Z =
|
||||
av.bifoldMap(_.bifoldMap(v)(t))(l)
|
||||
@ -105,9 +105,9 @@ private[digitalasset] object VersionTimeline {
|
||||
): Z =
|
||||
sv fold (_ fold (v, t), l)
|
||||
|
||||
def showsVersion: String = foldVersion(_.toString, _.toString, _.toString)
|
||||
private[lf] def showsVersion: String = foldVersion(_.toString, _.toString, _.toString)
|
||||
|
||||
def precedes(ov: SpecifiedVersion): Boolean = releasePrecedes(sv, ov)
|
||||
private[lf] def precedes(ov: SpecifiedVersion): Boolean = releasePrecedes(sv, ov)
|
||||
}
|
||||
|
||||
implicit def `any to SVOps`[A: SubVersion](vv: A): SpecifiedVersionOps =
|
||||
@ -135,7 +135,9 @@ private[digitalasset] object VersionTimeline {
|
||||
* @note We do not know the relative ordering of unlisted versions; so
|
||||
* the meaning of "no index" is not "equal" but undefined.
|
||||
*/
|
||||
def compareReleaseTime(left: SpecifiedVersion, right: SpecifiedVersion): Option[Ordering] =
|
||||
private[lf] def compareReleaseTime(
|
||||
left: SpecifiedVersion,
|
||||
right: SpecifiedVersion): Option[Ordering] =
|
||||
(index get left, index get right) match {
|
||||
case (Some(ixl), Some(ixr)) =>
|
||||
import scalaz.std.anyVal._
|
||||
@ -158,10 +160,11 @@ private[digitalasset] object VersionTimeline {
|
||||
.getOrElse(sys.error("every SubVersion must have at least one entry in the timeline"))
|
||||
|
||||
// not antisymmetric, as unknown versions can't be compared
|
||||
def maxVersion[A](left: A, right: A)(implicit ev: SubVersion[A]): A =
|
||||
private[lf] def maxVersion[A](left: A, right: A)(implicit ev: SubVersion[A]): A =
|
||||
if (releasePrecedes(ev.inject(left), ev.inject(right))) right else left
|
||||
|
||||
def latestWhenAllPresent[A](minimum: A, as: SpecifiedVersion*)(implicit A: SubVersion[A]): A = {
|
||||
private[lf] def latestWhenAllPresent[A](minimum: A, as: SpecifiedVersion*)(
|
||||
implicit A: SubVersion[A]): A = {
|
||||
import scalaz.std.anyVal._
|
||||
import scalaz.std.iterable._
|
||||
// None means "after the end"
|
||||
|
@ -13,8 +13,6 @@ import com.google.protobuf
|
||||
|
||||
import scala.collection.JavaConverters._
|
||||
import scalaz.std.either._
|
||||
import scalaz.std.option._
|
||||
import scalaz.syntax.traverse._
|
||||
import scalaz.syntax.bifunctor._
|
||||
|
||||
/**
|
||||
@ -46,61 +44,139 @@ object ValueCoder {
|
||||
EncodeError(s"${version.showsVersion} is too old to support $isTooOldFor")
|
||||
}
|
||||
|
||||
abstract class EncodeCid[-Cid] private[lf] {
|
||||
def asString(cid: Cid): String
|
||||
def asStruct(cid: Cid): (String, Boolean)
|
||||
}
|
||||
private val defaultVersion: SpecifiedVersion = ValueVersions.acceptedVersions.last
|
||||
|
||||
abstract class DecodeCid[Cid] private[lf] {
|
||||
def fromString(s: String): Either[DecodeError, Cid]
|
||||
def fromStruct(s: String, isRelative: Boolean): Either[DecodeError, Cid]
|
||||
abstract class EncodeCid[-Cid] private[lf] {
|
||||
private[lf] def encode(
|
||||
version: SpecifiedVersion = defaultVersion,
|
||||
contractId: Cid,
|
||||
): Either[EncodeError, Either[String, (proto.ContractId)]]
|
||||
}
|
||||
|
||||
object CidEncoder extends EncodeCid[ContractId] {
|
||||
override def asString(cid: ContractId): String = cid match {
|
||||
case RelativeContractId(nid, _) => "~" + nid.index.toString
|
||||
case AbsoluteContractId(coid) => coid
|
||||
}
|
||||
override def asStruct(cid: ContractId): (String, Boolean) = cid match {
|
||||
case RelativeContractId(nid, _) => nid.index.toString -> true
|
||||
case AbsoluteContractId(coid) => coid -> false
|
||||
}
|
||||
}
|
||||
|
||||
object AbsCidDecoder extends DecodeCid[AbsoluteContractId] {
|
||||
|
||||
override def fromString(s: String): Either[DecodeError, AbsoluteContractId] =
|
||||
ContractIdString
|
||||
.fromString(s)
|
||||
.bimap(
|
||||
_ => DecodeError(s"cannot parse absolute contractId $s"),
|
||||
AbsoluteContractId
|
||||
)
|
||||
override def fromStruct(
|
||||
s: String,
|
||||
isRelative: Boolean): Either[DecodeError, AbsoluteContractId] =
|
||||
if (isRelative)
|
||||
Left(DecodeError(s"unexpected relative contractId $s"))
|
||||
private[lf] def encode(
|
||||
sv: SpecifiedVersion,
|
||||
cid: ContractId
|
||||
): Either[EncodeError, Either[String, (proto.ContractId)]] =
|
||||
if (useOldStringField(sv))
|
||||
cid match {
|
||||
case RelativeContractId(nid, _) =>
|
||||
Right(Left("~" + nid.index.toString))
|
||||
case AbsoluteContractId(s) if Ref.ContractIdString.isA(s) =>
|
||||
Right(Left(s))
|
||||
case AbsoluteContractId(_) =>
|
||||
Left(EncodeError(s"absolute contractId v1 not supported by ${sv.showsVersion}"))
|
||||
} else
|
||||
cid match {
|
||||
case RelativeContractId(nid, _) =>
|
||||
Right(
|
||||
Right(
|
||||
proto.ContractId.newBuilder
|
||||
.setRelative(true)
|
||||
.setContractId(nid.index.toString)
|
||||
.build))
|
||||
case AbsoluteContractId(s) =>
|
||||
if ((sv precedes ValueVersions.minContractIdV1) && (Ref.ContractIdString.isB(s)))
|
||||
Left(EncodeError(s"absolute contractId v1 not supported by ${sv.showsVersion}"))
|
||||
else
|
||||
fromString(s)
|
||||
Right(Right(proto.ContractId.newBuilder.setRelative(false).setContractId(s).build))
|
||||
}
|
||||
}
|
||||
|
||||
object CidDecoder extends DecodeCid[ContractId] {
|
||||
abstract class DecodeCid[Cid] private[lf] {
|
||||
def decodeOptional(
|
||||
sv: SpecifiedVersion,
|
||||
stringForm: String,
|
||||
structForm: proto.ContractId,
|
||||
): Either[DecodeError, Option[Cid]]
|
||||
|
||||
private def fromStringToRelCid(s: String): Either[DecodeError, RelativeContractId] =
|
||||
final def decode(
|
||||
sv: SpecifiedVersion,
|
||||
stringForm: String,
|
||||
structForm: proto.ContractId,
|
||||
): Either[DecodeError, Cid] =
|
||||
decodeOptional(sv, stringForm, structForm).flatMap {
|
||||
case Some(cid) => Right(cid)
|
||||
case None => Left(DecodeError("Missing required field contract_id"))
|
||||
}
|
||||
}
|
||||
|
||||
val CidDecoder: DecodeCid[ContractId] = new DecodeCid[ContractId] {
|
||||
|
||||
private def stringToRelativeCid(s: String) =
|
||||
scalaz.std.string
|
||||
.parseInt(s)
|
||||
.toEither
|
||||
.bimap(
|
||||
_ => DecodeError(s"cannot parse relative contractId $s"),
|
||||
idx => RelativeContractId(NodeId(idx), None)
|
||||
_ => //
|
||||
DecodeError(s"""cannot parse relative contractId "$s""""),
|
||||
idx => Some(RelativeContractId(NodeId(idx), None))
|
||||
)
|
||||
|
||||
override def fromString(s: String): Either[DecodeError, ContractId] =
|
||||
if (s.startsWith("~")) fromStringToRelCid(s.drop(1)) else AbsCidDecoder.fromString(s)
|
||||
private def stringToCidString(s: String): Either[DecodeError, Ref.ContractIdString] =
|
||||
Ref.ContractIdString
|
||||
.fromString(s)
|
||||
.left
|
||||
.map(_ => //
|
||||
DecodeError(s"""cannot parse absolute contractId "$s""""))
|
||||
|
||||
override def fromStruct(s: String, isRelative: Boolean): Either[DecodeError, ContractId] =
|
||||
if (isRelative) fromStringToRelCid(s) else AbsCidDecoder.fromStruct(s, isRelative)
|
||||
override def decodeOptional(
|
||||
sv: SpecifiedVersion,
|
||||
stringForm: String,
|
||||
structForm: proto.ContractId,
|
||||
): Either[DecodeError, Option[ContractId]] =
|
||||
if (useOldStringField(sv)) {
|
||||
if (structForm != proto.ContractId.getDefaultInstance) {
|
||||
Left(DecodeError(sv, isTooOldFor = "message ContractId"))
|
||||
} else {
|
||||
if (stringForm.isEmpty)
|
||||
Right(None)
|
||||
else if (stringForm.startsWith("~"))
|
||||
stringToRelativeCid(stringForm.drop(1))
|
||||
else
|
||||
for {
|
||||
coid <- stringToCidString(stringForm)
|
||||
_ <- Either.cond(
|
||||
test = Ref.ContractIdString.isA(coid),
|
||||
right = (),
|
||||
left = DecodeError(sv, s"absolute contractId V1"),
|
||||
)
|
||||
} yield Some(AbsoluteContractId(coid))
|
||||
}
|
||||
} else {
|
||||
if (stringForm.nonEmpty)
|
||||
Left(DecodeError(s"${sv.showsVersion} is too new to use string contract IDs"))
|
||||
else if (structForm.getContractId.isEmpty)
|
||||
Right(None)
|
||||
else if (structForm.getRelative)
|
||||
stringToRelativeCid(structForm.getContractId)
|
||||
else
|
||||
for {
|
||||
coid <- stringToCidString(structForm.getContractId)
|
||||
_ <- Either.cond(
|
||||
test = Ref.ContractIdString.isA(coid) || !(sv precedes ValueVersions.minContractIdV1),
|
||||
right = (),
|
||||
left = DecodeError(sv, s"absolute contractId V1")
|
||||
)
|
||||
} yield Some(AbsoluteContractId(coid))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
val AbsCidDecoder: DecodeCid[AbsoluteContractId] = new DecodeCid[AbsoluteContractId] {
|
||||
override def decodeOptional(
|
||||
sv: SpecifiedVersion,
|
||||
stringForm: String,
|
||||
structForm: ValueOuterClass.ContractId,
|
||||
): Either[DecodeError, Option[AbsoluteContractId]] =
|
||||
CidDecoder.decodeOptional(sv, stringForm, structForm).flatMap {
|
||||
case Some(RelativeContractId(_, _)) =>
|
||||
Left(DecodeError("Unexpected relative contractId"))
|
||||
case Some(AbsoluteContractId(coid)) =>
|
||||
Right(Some(AbsoluteContractId(coid)))
|
||||
case None =>
|
||||
Right(None)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -158,92 +234,6 @@ object ValueCoder {
|
||||
|
||||
} yield Identifier(pkgId, QualifiedName(module, name))
|
||||
|
||||
/** Codecs for ContractId that don't break the method chain style
|
||||
* of builders.
|
||||
*/
|
||||
private[lf] implicit final class codecContractId[A](private val self: A) extends AnyVal {
|
||||
def setContractIdOrStruct[Cid, Z](
|
||||
encodeCid: EncodeCid[Cid],
|
||||
version: SpecifiedVersion,
|
||||
contractId: Cid,
|
||||
)(stringly: (A, String) => Z, structly: (A, proto.ContractId) => Z): Z =
|
||||
if (useOldStringField(version)) stringly(self, encodeCid.asString(contractId))
|
||||
else {
|
||||
val (encCid, encRel) = encodeCid.asStruct(contractId)
|
||||
structly(
|
||||
self,
|
||||
proto.ContractId.newBuilder().setContractId(encCid).setRelative(encRel).build(),
|
||||
)
|
||||
}
|
||||
|
||||
import proto.ContractId.{getDefaultInstance => placeholderContractId}
|
||||
|
||||
private[this] def requireUnset(v: SpecifiedVersion, s: String): Either[DecodeError, Unit] =
|
||||
if (s == "") Right(())
|
||||
else Left(DecodeError(s"${v.showsVersion} is too new to use string contract IDs"))
|
||||
|
||||
private[this] def requireUnset(
|
||||
v: SpecifiedVersion,
|
||||
c: proto.ContractId,
|
||||
): Either[DecodeError, Unit] =
|
||||
if (c == null || c == placeholderContractId) Right(())
|
||||
else Left(DecodeError(v, isTooOldFor = "message ContractId"))
|
||||
|
||||
private[this] def decodeContractIdStruct[Cid](
|
||||
decodeCid: DecodeCid[Cid],
|
||||
p: proto.ContractId,
|
||||
): Either[DecodeError, Cid] =
|
||||
for {
|
||||
str <- Either.cond(
|
||||
p.getContractId.nonEmpty,
|
||||
p.getContractId,
|
||||
DecodeError("Missing required field contract_id"),
|
||||
)
|
||||
cid <- decodeCid.fromStruct(str, p.getRelative)
|
||||
} yield cid
|
||||
|
||||
def decodeContractIdOrStruct[Cid, Z](
|
||||
decodeCid: DecodeCid[Cid],
|
||||
version: SpecifiedVersion,
|
||||
)(stringly: A => String, structly: A => proto.ContractId): Either[DecodeError, Cid] = {
|
||||
val stringForm = stringly(self)
|
||||
val structForm = structly(self)
|
||||
if (useOldStringField(version))
|
||||
requireUnset(version, structForm) flatMap (
|
||||
_ =>
|
||||
if (stringForm == "")
|
||||
Left(DecodeError("Missing required field contract_id"))
|
||||
else decodeCid.fromString(stringForm),
|
||||
)
|
||||
else
|
||||
requireUnset(version, stringForm) flatMap (
|
||||
_ =>
|
||||
decodeContractIdStruct(decodeCid, structForm),
|
||||
)
|
||||
}
|
||||
|
||||
def decodeOptionalContractIdOrStruct[Cid, Z](
|
||||
decodeCid: DecodeCid[Cid],
|
||||
version: SpecifiedVersion,
|
||||
)(stringly: A => String, structly: A => proto.ContractId): Either[DecodeError, Option[Cid]] = {
|
||||
val stringForm = stringly(self)
|
||||
val structForm = structly(self)
|
||||
if (useOldStringField(version))
|
||||
requireUnset(version, structForm) flatMap (
|
||||
_ =>
|
||||
if (stringForm == "") Right(None)
|
||||
else decodeCid.fromString(stringForm) map (Some(_)),
|
||||
)
|
||||
else
|
||||
requireUnset(version, stringForm) flatMap (
|
||||
_ =>
|
||||
Option(structForm) filter (_ != placeholderContractId) traverseU (
|
||||
sf => decodeContractIdStruct(decodeCid, sf)
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private def decodeVersion(vs: String): Either[DecodeError, ValueVersion] =
|
||||
ValueVersions
|
||||
.isAcceptedVersion(vs)
|
||||
@ -290,11 +280,12 @@ object ValueCoder {
|
||||
* @return protocol buffer serialized values
|
||||
*/
|
||||
def encodeVersionedValue[Cid](
|
||||
versionCid: ValueVersions.VersionCid[Cid],
|
||||
encodeCid: EncodeCid[Cid],
|
||||
value: Value[Cid],
|
||||
): Either[EncodeError, proto.VersionedValue] =
|
||||
ValueVersions
|
||||
.assignVersion(value)
|
||||
.assignVersion(versionCid, value)
|
||||
.fold(
|
||||
err => Left(EncodeError(err)),
|
||||
version => encodeVersionedValueWithCustomVersion(encodeCid, VersionedValue(version, value)),
|
||||
@ -383,11 +374,14 @@ object ValueCoder {
|
||||
val party = Party.fromString(protoValue.getParty)
|
||||
party.fold(e => throw Err("error decoding party: " + e), ValueParty)
|
||||
case proto.Value.SumCase.CONTRACT_ID | proto.Value.SumCase.CONTRACT_ID_STRUCT =>
|
||||
val cid = protoValue.decodeContractIdOrStruct(decodeCid, valueVersion)(
|
||||
_.getContractId,
|
||||
_.getContractIdStruct,
|
||||
val cid = decodeCid.decode(
|
||||
valueVersion,
|
||||
protoValue.getContractId,
|
||||
protoValue.getContractIdStruct
|
||||
)
|
||||
cid.fold(err => throw Err(err.errorMessage), ValueContractId(_))
|
||||
cid.fold(
|
||||
e => throw Err("error decoding contractId: " + e.errorMessage),
|
||||
ValueContractId(_))
|
||||
case proto.Value.SumCase.LIST =>
|
||||
ValueList(
|
||||
FrontStack(
|
||||
@ -533,13 +527,11 @@ object ValueCoder {
|
||||
case ValueTimestamp(t) =>
|
||||
builder.setTimestamp(t.micros).build()
|
||||
case ValueContractId(coid) =>
|
||||
builder
|
||||
.setContractIdOrStruct(encodeCid, valueVersion, coid)(
|
||||
_.setContractId(_),
|
||||
_.setContractIdStruct(_),
|
||||
)
|
||||
.build()
|
||||
|
||||
encodeCid.encode(valueVersion, coid).map {
|
||||
case Left(string) => builder.setContractId(string)
|
||||
case Right(struct) => builder.setContractIdStruct(struct)
|
||||
}
|
||||
builder.build()
|
||||
case ValueList(elems) =>
|
||||
val listBuilder = proto.List.newBuilder()
|
||||
elems.foreach(elem => {
|
||||
@ -632,10 +624,11 @@ object ValueCoder {
|
||||
// general usage
|
||||
|
||||
private[value] def valueToBytes[Cid](
|
||||
versionCid: ValueVersions.VersionCid[Cid],
|
||||
encodeCid: EncodeCid[Cid],
|
||||
v: Value[Cid],
|
||||
): Either[EncodeError, Array[Byte]] = {
|
||||
encodeVersionedValue(encodeCid, v).map(_.toByteArray)
|
||||
encodeVersionedValue(versionCid, encodeCid, v).map(_.toByteArray)
|
||||
}
|
||||
|
||||
private[value] def valueFromBytes[Cid](
|
||||
|
@ -5,7 +5,7 @@ package com.digitalasset.daml.lf.value
|
||||
|
||||
import com.digitalasset.daml.lf.value.Value._
|
||||
import com.digitalasset.daml.lf.LfVersions
|
||||
import com.digitalasset.daml.lf.data.{Decimal, FrontStack, FrontStackCons, ImmArray}
|
||||
import com.digitalasset.daml.lf.data.{Decimal, FrontStack, FrontStackCons, ImmArray, Ref}
|
||||
import com.digitalasset.daml.lf.transaction.VersionTimeline
|
||||
|
||||
import scala.annotation.tailrec
|
||||
@ -29,7 +29,23 @@ object ValueVersions
|
||||
private[value] val minGenMap = ValueVersion("7")
|
||||
private[value] val minContractIdV1 = ValueVersion("7")
|
||||
|
||||
def assignVersion[Cid](v0: Value[Cid]): Either[String, ValueVersion] = {
|
||||
abstract class VersionCid[-Cid] private[lf] {
|
||||
private[lf] def assignVersion(cid: Cid): ValueVersion
|
||||
}
|
||||
|
||||
object VersionCid extends VersionCid[ContractId] {
|
||||
override private[lf] def assignVersion(cid: ContractId): ValueVersion =
|
||||
cid match {
|
||||
case AbsoluteContractId(cid) if Ref.ContractIdString.isB(cid) =>
|
||||
minContractIdV1
|
||||
case _ =>
|
||||
minVersion
|
||||
}
|
||||
}
|
||||
|
||||
def assignVersion[Cid](
|
||||
versionCid: VersionCid[Cid],
|
||||
v0: Value[Cid]): Either[String, ValueVersion] = {
|
||||
import VersionTimeline.{maxVersion => maxVV}
|
||||
|
||||
@tailrec
|
||||
@ -48,9 +64,11 @@ object ValueVersions
|
||||
case ValueRecord(_, fs) => go(currentVersion, fs.map(v => v._2) ++: values)
|
||||
case ValueVariant(_, _, arg) => go(currentVersion, arg +: values)
|
||||
case ValueList(vs) => go(currentVersion, vs.toImmArray ++: values)
|
||||
case ValueContractId(_) | ValueInt64(_) | ValueText(_) | ValueTimestamp(_) |
|
||||
ValueParty(_) | ValueBool(_) | ValueDate(_) | ValueUnit =>
|
||||
case ValueInt64(_) | ValueText(_) | ValueTimestamp(_) | ValueParty(_) | ValueBool(_) |
|
||||
ValueDate(_) | ValueUnit =>
|
||||
go(currentVersion, values)
|
||||
case ValueContractId(cid) =>
|
||||
go(maxVV(versionCid.assignVersion(cid), currentVersion), values)
|
||||
case ValueNumeric(x) if x.scale == Decimal.scale =>
|
||||
go(currentVersion, values)
|
||||
// for things added after version 1, we raise the minimum if present
|
||||
@ -79,17 +97,24 @@ object ValueVersions
|
||||
}
|
||||
|
||||
@throws[IllegalArgumentException]
|
||||
def assertAssignVersion[Cid](v0: Value[Cid]): ValueVersion =
|
||||
assignVersion(v0) match {
|
||||
def assertAssignVersion[Cid](versionCid: VersionCid[Cid], v0: Value[Cid]): ValueVersion =
|
||||
assignVersion(versionCid, v0) match {
|
||||
case Left(err) => throw new IllegalArgumentException(err)
|
||||
case Right(x) => x
|
||||
}
|
||||
|
||||
def asVersionedValue[Cid](value: Value[Cid]): Either[String, VersionedValue[Cid]] =
|
||||
assignVersion(value).map(version => VersionedValue(version = version, value = value))
|
||||
def asVersionedValue[Cid](
|
||||
versionCid: VersionCid[Cid],
|
||||
value: Value[Cid],
|
||||
): Either[String, VersionedValue[Cid]] =
|
||||
assignVersion(versionCid, value).map(version =>
|
||||
VersionedValue(version = version, value = value))
|
||||
|
||||
def asVersionedValue[Cid <: ContractId](value: Value[Cid]): Either[String, VersionedValue[Cid]] =
|
||||
asVersionedValue(VersionCid, value)
|
||||
|
||||
@throws[IllegalArgumentException]
|
||||
def assertAsVersionedValue[Cid](value: Value[Cid]): VersionedValue[Cid] =
|
||||
def assertAsVersionedValue[Cid <: ContractId](value: Value[Cid]): VersionedValue[Cid] =
|
||||
asVersionedValue(value) match {
|
||||
case Left(err) => throw new IllegalArgumentException(err)
|
||||
case Right(x) => x
|
||||
|
@ -114,20 +114,20 @@ class TransactionSpec extends FreeSpec with Matchers with GeneratorDrivenPropert
|
||||
}
|
||||
|
||||
object TransactionSpec {
|
||||
private[this] type Value[+Cid] = V[Cid]
|
||||
type StringTransaction = GenTransaction[String, String, Value[String]]
|
||||
private[this] type Value = V[V.AbsoluteContractId]
|
||||
type StringTransaction = GenTransaction[String, V.AbsoluteContractId, Value]
|
||||
def StringTransaction(
|
||||
nodes: HashMap[String, GenNode[String, String, Value[String]]],
|
||||
nodes: HashMap[String, GenNode[String, V.AbsoluteContractId, Value]],
|
||||
roots: ImmArray[String],
|
||||
): StringTransaction = GenTransaction(nodes, roots, None)
|
||||
|
||||
def dummyExerciseNode(
|
||||
children: ImmArray[String],
|
||||
hasExerciseResult: Boolean = true,
|
||||
): NodeExercises[String, String, Value[String]] =
|
||||
): NodeExercises[String, V.AbsoluteContractId, Value] =
|
||||
NodeExercises(
|
||||
nodeSeed = None,
|
||||
targetCoid = "dummyCoid",
|
||||
targetCoid = V.AbsoluteContractId(Ref.ContractIdString.assertFromString("dummyCoid")),
|
||||
templateId = Ref.Identifier(
|
||||
PackageId.assertFromString("-dummyPkg-"),
|
||||
QualifiedName.assertFromString("DummyModule:dummyName"),
|
||||
@ -145,10 +145,10 @@ object TransactionSpec {
|
||||
key = None,
|
||||
)
|
||||
|
||||
val dummyCreateNode: NodeCreate[String, Value[String]] =
|
||||
val dummyCreateNode: NodeCreate[V.AbsoluteContractId, Value] =
|
||||
NodeCreate(
|
||||
nodeSeed = None,
|
||||
coid = "dummyCoid",
|
||||
coid = V.AbsoluteContractId(Ref.ContractIdString.assertFromString("dummyCoid")),
|
||||
coinst = ContractInst(
|
||||
Ref.Identifier(
|
||||
PackageId.assertFromString("-dummyPkg-"),
|
||||
|
@ -6,7 +6,7 @@ package transaction
|
||||
|
||||
import data.ImmArray
|
||||
import value.Value
|
||||
import Value.{ValueOptional, VersionedValue}
|
||||
import Value.{ContractId, ValueOptional, VersionedValue}
|
||||
import value.ValueVersions.asVersionedValue
|
||||
import TransactionVersions.assignVersion
|
||||
import org.scalatest.{Matchers, WordSpec}
|
||||
@ -23,27 +23,27 @@ class TransactionVersionSpec extends WordSpec with Matchers {
|
||||
|
||||
"pick version 2 when confronted with newer data" in {
|
||||
val usingOptional = dummyCreateTransaction map3 (identity, identity, v =>
|
||||
ValueOptional(Some(v)): Value[String])
|
||||
ValueOptional(Some(v)): Value[Value.AbsoluteContractId])
|
||||
assignVersion(assignValueVersions(usingOptional)) shouldBe TransactionVersion("2")
|
||||
}
|
||||
|
||||
"pick version 7 when confronted with exercise result" in {
|
||||
val hasExerciseResult = dummyExerciseWithResultTransaction map3 (identity, identity, v =>
|
||||
ValueOptional(Some(v)): Value[String])
|
||||
ValueOptional(Some(v)): Value[Value.AbsoluteContractId])
|
||||
assignVersion(assignValueVersions(hasExerciseResult)) shouldBe TransactionVersion("7")
|
||||
}
|
||||
|
||||
"pick version 2 when confronted with exercise result" in {
|
||||
val hasExerciseResult = dummyExerciseTransaction map3 (identity, identity, v =>
|
||||
ValueOptional(Some(v)): Value[String])
|
||||
ValueOptional(Some(v)): Value[Value.AbsoluteContractId])
|
||||
assignVersion(assignValueVersions(hasExerciseResult)) shouldBe TransactionVersion("2")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private[this] def assignValueVersions[Nid, Cid, Cid2](
|
||||
t: GenTransaction[Nid, Cid, Value[Cid2]],
|
||||
): GenTransaction[Nid, Cid, VersionedValue[Cid2]] =
|
||||
private[this] def assignValueVersions[Nid, Cid <: ContractId](
|
||||
t: GenTransaction[Nid, Cid, Value[Cid]],
|
||||
): GenTransaction[Nid, Cid, VersionedValue[Cid]] =
|
||||
t map3 (identity, identity, v =>
|
||||
asVersionedValue(v) fold (e =>
|
||||
fail(s"We didn't write traverse for GenTransaction: $e"), identity))
|
||||
|
@ -128,8 +128,14 @@ class ValueCoderSpec extends WordSpec with Matchers with EitherAssertions with P
|
||||
}
|
||||
}
|
||||
|
||||
"do ContractId in any ValueVersion" in forAll(coidValueGen, valueVersionGen())(
|
||||
testRoundTripWithVersion,
|
||||
"do ContractId V0 in any ValueVersion" in forAll(coidValueGenV0, valueVersionGen())(
|
||||
testRoundTripWithVersion
|
||||
)
|
||||
|
||||
"do ContractId in any ValueVersion > 1.7" in forAll(
|
||||
coidValueGen,
|
||||
valueVersionGen(ValueVersions.minContractIdV1))(
|
||||
testRoundTripWithVersion
|
||||
)
|
||||
|
||||
"do lists" in {
|
||||
@ -185,7 +191,10 @@ class ValueCoderSpec extends WordSpec with Matchers with EitherAssertions with P
|
||||
)
|
||||
val fromToBytes = ValueCoder.valueFromBytes(
|
||||
ValueCoder.CidDecoder,
|
||||
ValueCoder.valueToBytes[ContractId](ValueCoder.CidEncoder, ValueUnit).toOption.get,
|
||||
ValueCoder
|
||||
.valueToBytes[ContractId](ValueVersions.VersionCid, ValueCoder.CidEncoder, ValueUnit)
|
||||
.toOption
|
||||
.get,
|
||||
)
|
||||
Right(ValueUnit) shouldEqual fromToBytes
|
||||
recovered shouldEqual Right(ValueUnit)
|
||||
@ -209,7 +218,7 @@ class ValueCoderSpec extends WordSpec with Matchers with EitherAssertions with P
|
||||
}
|
||||
|
||||
"do versioned value with assigned version" in forAll(valueGen) { v: Value[ContractId] =>
|
||||
testRoundTripWithVersion(v, ValueVersions.assertAssignVersion(v))
|
||||
testRoundTripWithVersion(v, ValueVersions.assertAssignVersion(ValueVersions.VersionCid, v))
|
||||
}
|
||||
|
||||
"versioned value should pass serialization if unsupported override version provided" in
|
||||
@ -258,7 +267,9 @@ class ValueCoderSpec extends WordSpec with Matchers with EitherAssertions with P
|
||||
)
|
||||
val fromToBytes = ValueCoder.valueFromBytes(
|
||||
ValueCoder.CidDecoder,
|
||||
assertRight(ValueCoder.valueToBytes[ContractId](ValueCoder.CidEncoder, value)),
|
||||
assertRight(
|
||||
ValueCoder
|
||||
.valueToBytes[ContractId](ValueVersions.VersionCid, ValueCoder.CidEncoder, value)),
|
||||
)
|
||||
Right(value) shouldEqual recovered
|
||||
Right(value) shouldEqual fromToBytes
|
||||
|
@ -139,7 +139,8 @@ private[validation] object Serializability {
|
||||
version: LanguageVersion,
|
||||
world: World,
|
||||
tyCon: TTyCon,
|
||||
template: Template): Unit = {
|
||||
template: Template,
|
||||
): Unit = {
|
||||
val context = ContextTemplate(tyCon.tycon)
|
||||
Env(version, world, context, SRTemplateArg, tyCon).checkType()
|
||||
template.choices.values.foreach { choice =>
|
||||
|
@ -45,10 +45,11 @@ object PlatformTypes {
|
||||
f: Cid => Cid2): GenTransaction[Nid, Cid2] =
|
||||
tx.mapContractIdAndValue(f, _.mapContractId(f))
|
||||
|
||||
def asVersionedValue[Cid](v: V[Cid]): scala.Either[String, V.VersionedValue[Cid]] =
|
||||
def asVersionedValue[Cid <: V.ContractId](
|
||||
v: V[Cid]): scala.Either[String, V.VersionedValue[Cid]] =
|
||||
ValueVersions.asVersionedValue(v)
|
||||
|
||||
def asVersionedValueOrThrow[Cid](v: V[Cid]): V.VersionedValue[Cid] = {
|
||||
def asVersionedValueOrThrow[Cid <: V.ContractId](v: V[Cid]): V.VersionedValue[Cid] = {
|
||||
asVersionedValue(v).fold(
|
||||
s => throw new IllegalArgumentException(s"Can't convert to versioned value: $s"),
|
||||
identity)
|
||||
|
@ -12,6 +12,7 @@ import com.digitalasset.daml.lf.data.Ref.{LedgerString, Party}
|
||||
import com.digitalasset.daml.lf.data.Time
|
||||
import com.digitalasset.daml.lf.transaction.Node.GlobalKey
|
||||
import com.digitalasset.daml.lf.transaction._
|
||||
import com.digitalasset.daml.lf.transaction.VersionTimeline.Implicits._
|
||||
import com.digitalasset.daml.lf.value.Value.{
|
||||
AbsoluteContractId,
|
||||
ContractId,
|
||||
@ -263,15 +264,18 @@ private[state] object Conversions {
|
||||
identity,
|
||||
)
|
||||
|
||||
def contractIdStructOrStringToStateKey(
|
||||
def contractIdStructOrStringToStateKey[A](
|
||||
transactionVersion: TransactionVersion,
|
||||
entryId: DamlLogEntryId,
|
||||
coidString: String,
|
||||
coidStruct: ValueOuterClass.ContractId): DamlStateKey = {
|
||||
val result =
|
||||
if (coidString.isEmpty)
|
||||
ValueCoder.CidDecoder.fromStruct(coidStruct.getContractId, coidStruct.getRelative)
|
||||
else
|
||||
ValueCoder.CidDecoder.fromString(coidString)
|
||||
coidStruct: ValueOuterClass.ContractId,
|
||||
): DamlStateKey = {
|
||||
|
||||
val result = ValueCoder.CidDecoder.decode(
|
||||
sv = transactionVersion,
|
||||
stringForm = coidString,
|
||||
structForm = coidStruct,
|
||||
)
|
||||
|
||||
result match {
|
||||
case Left(err) =>
|
||||
|
@ -9,13 +9,13 @@ import com.daml.ledger.participant.state.kvutils.DamlKvutils._
|
||||
import com.daml.ledger.participant.state.kvutils.committer.{
|
||||
ConfigCommitter,
|
||||
PackageCommitter,
|
||||
PartyAllocationCommitter,
|
||||
PartyAllocationCommitter
|
||||
}
|
||||
import com.daml.ledger.participant.state.kvutils.committing._
|
||||
import com.daml.ledger.participant.state.v1.{Configuration, ParticipantId}
|
||||
import com.digitalasset.daml.lf.data.Time.Timestamp
|
||||
import com.digitalasset.daml.lf.engine.Engine
|
||||
import com.digitalasset.daml.lf.transaction.TransactionOuterClass
|
||||
import com.digitalasset.daml.lf.transaction.{TransactionCoder, TransactionOuterClass}
|
||||
import com.digitalasset.daml_lf_dev.DamlLf
|
||||
import com.digitalasset.platform.common.metrics.VarGauge
|
||||
import com.google.protobuf.ByteString
|
||||
@ -219,9 +219,12 @@ object KeyValueCommitting {
|
||||
private def transactionOutputs(
|
||||
transactionEntry: DamlTransactionEntry,
|
||||
entryId: DamlLogEntryId,
|
||||
): Set[DamlStateKey] =
|
||||
transactionEntry.getTransaction.getNodesList.asScala.flatMap {
|
||||
node: TransactionOuterClass.Node =>
|
||||
): Set[DamlStateKey] = {
|
||||
val transaction = transactionEntry.getTransaction
|
||||
val transactionVersion = TransactionCoder
|
||||
.decodeVersion(transaction.getVersion)
|
||||
.fold(err => throw Err.InvalidSubmission(err.errorMessage), identity)
|
||||
transaction.getNodesList.asScala.flatMap { node: TransactionOuterClass.Node =>
|
||||
node.getNodeTypeCase match {
|
||||
case TransactionOuterClass.Node.NodeTypeCase.CREATE =>
|
||||
val create = node.getCreate
|
||||
@ -241,6 +244,7 @@ object KeyValueCommitting {
|
||||
|
||||
Conversions
|
||||
.contractIdStructOrStringToStateKey(
|
||||
transactionVersion,
|
||||
entryId,
|
||||
create.getContractId,
|
||||
create.getContractIdStruct,
|
||||
@ -264,6 +268,7 @@ object KeyValueCommitting {
|
||||
|
||||
Conversions
|
||||
.contractIdStructOrStringToStateKey(
|
||||
transactionVersion,
|
||||
entryId,
|
||||
exe.getContractId,
|
||||
exe.getContractIdStruct,
|
||||
@ -274,6 +279,7 @@ object KeyValueCommitting {
|
||||
List(
|
||||
Conversions
|
||||
.contractIdStructOrStringToStateKey(
|
||||
transactionVersion,
|
||||
entryId,
|
||||
node.getFetch.getContractId,
|
||||
node.getFetch.getContractIdStruct,
|
||||
@ -287,6 +293,7 @@ object KeyValueCommitting {
|
||||
throw Err.InvalidSubmission("submissionOutputs: NODETYPE_NOT_SET")
|
||||
}
|
||||
}.toSet
|
||||
}
|
||||
|
||||
private def verifyStateUpdatesAgainstPreDeclaredOutputs(
|
||||
actualStateUpdates: Map[DamlStateKey, DamlStateValue],
|
||||
|
@ -126,7 +126,8 @@ object KeyHasher extends KeyHasher {
|
||||
// Do not use directly. It is package visible for testing purpose.
|
||||
private[serialization] def putValue(
|
||||
digest: MessageDigest,
|
||||
value: Value[AbsoluteContractId]): MessageDigest = {
|
||||
value: Value[AbsoluteContractId],
|
||||
): MessageDigest = {
|
||||
// Then, write the value
|
||||
foldLeft[MessageDigest](
|
||||
value,
|
||||
|
Loading…
Reference in New Issue
Block a user