Engine: fork method to resolve contractIds (#4411)

* engine: fork method to resolve relative contractId

CHANGELOG_BEGIN
CHANGELOG_END
This commit is contained in:
Remy 2020-02-10 16:52:06 +01:00 committed by GitHub
parent 19063a7c4e
commit e02e5d61bc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 291 additions and 104 deletions

View File

@ -52,10 +52,14 @@ sealed trait UnionStringModule[T <: String, TA <: T, TB <: T] extends StringModu
def toEither(s: T): Either[TA, TB]
def isA(s: T): Boolean
def toA(s: T): Option[TA]
def assertToVA(s: T): TA
def isB(s: T): Boolean
def toB(s: T): Option[TB]
def assertToVB(s: T): TB
@ -228,7 +232,7 @@ private[data] final class IdStringImpl extends IdString {
// Prefixed with "$0" which is not a valid substring of ContractIdV0.
override type ContractIdStringV1 = String
override val ContractIdStringV1: StringModule[ContractIdStringV1] =
new MatchingStringModule("""\$0[0-9a-f]{64}[A-Za-z0-9:\-_]{189}""")
new MatchingStringModule("""\$0[0-9a-f]{64}[A-Za-z0-9:\-_]{0,189}""")
/** Identifier for a contractIs, union of `ContractIdStringV0` and `ContractIdStringV1` */
override type ContractIdString = String
@ -238,23 +242,28 @@ private[data] final class IdStringImpl extends IdString {
with UnionStringModule[ContractIdString, ContractIdStringV0, ContractIdStringV1] {
override def toEither(s: ContractIdString): Either[String, String] =
Either.cond(s.startsWith("$0"), s, s)
Either.cond(isA(s), s, s)
override def fromString(s: String): Either[String, ContractIdString] =
toEither(s).fold(ContractIdStringV0.fromString, ContractIdStringV1.fromString)
override def assertToVA(s: ContractIdString): ContractIdStringV0 =
toEither(s).left
.getOrElse(throw new IllegalArgumentException("expect V0 ContractId get V1"))
override def assertToVB(s: ContractIdString): ContractIdStringV1 =
toEither(s).right
.getOrElse(throw new IllegalArgumentException("expect V1 ContractId get V0"))
override def isA(s: ContractIdString): Boolean =
s.startsWith("$0")
override def toA(s: ContractIdString): Option[ContractIdStringV0] =
toEither(s).left.toOption
Some(s).filter(isA)
override def assertToVA(s: ContractIdString): ContractIdStringV0 =
toA(s).getOrElse(throw new IllegalArgumentException("expect V0 ContractId get V1"))
override def isB(s: ContractIdString): Boolean =
!isA(s)
override def toB(s: ContractIdString): Option[ContractIdStringV1] =
toEither(s).right.toOption
Some(s).filter(isB)
override def assertToVB(s: ContractIdString): ContractIdStringV1 =
toB(s).getOrElse(throw new IllegalArgumentException("expect V1 ContractId get V0"))
}
}

View File

@ -89,7 +89,7 @@ final case class ExerciseEvent[Nid, Cid, Val](
exerciseResult: Option[Val])
extends Event[Nid, Cid, Val]
object Event extends value.CidContainer3[Event] {
object Event extends value.CidContainer3WithDefaultCidResolver[Event] {
override private[lf] def map3[Nid, Cid, Val, Nid2, Cid2, Val2](
f1: Nid => Nid2,
@ -253,7 +253,7 @@ object Event extends value.CidContainer3[Event] {
Events(relevantRoots, Map() ++ evts)
}
object Events extends value.CidContainer3[Events] {
object Events extends value.CidContainer3WithDefaultCidResolver[Events] {
override private[lf] def map3[Nid, Cid, Val, Nid2, Cid2, Val2](
f1: Nid => Nid2,
f2: Cid => Cid2,

View File

@ -27,11 +27,11 @@ private[engine] class InMemoryPrivateLedgerData extends PrivateLedgerData {
private val txCounter: AtomicInteger = new AtomicInteger(0)
def update(tx: GenTransaction.WithTxValue[NodeId, ContractId]): Unit =
updateWithAbsoluteContractId(tx.resolveRelCid(toContractIdString(txCounter.get)))
updateWithAbsoluteContractId(tx.resolveRelCidV0(toContractIdString(txCounter.get)))
def toContractIdString(txCounter: Int)(r: RelativeContractId): Ref.ContractIdString =
def toContractIdString(txCounter: Int)(r: RelativeContractId): Ref.ContractIdStringV0 =
// It is safe to concatenate numbers and "-" to form a valid ContractId
Ref.ContractIdString.assertFromString(s"$txCounter-${r.txnid.index}")
Ref.ContractIdStringV0.assertFromString(s"$txCounter-${r.txnid.index}")
def updateWithAbsoluteContractId(
tx: GenTransaction.WithTxValue[NodeId, AbsoluteContractId]): Unit =

View File

@ -191,7 +191,7 @@ object Ledger {
nodes = enrichedTx.nodes.map {
case (nodeId, node) =>
ScenarioNodeId(commitPrefix, nodeId) -> node
.resolveRelCid(makeAbs)
.resolveRelCidV0(makeAbs)
.mapNodeId(ScenarioNodeId(commitPrefix, _))
}(breakOut),
explicitDisclosure = enrichedTx.explicitDisclosure.map {

View File

@ -5,7 +5,7 @@ package com.digitalasset.daml.lf
package crypto
import java.nio.ByteBuffer
import java.security.MessageDigest
import java.security.{MessageDigest, SecureRandom}
import java.util
import com.digitalasset.daml.lf.data.{ImmArray, Ref, Utf8}
@ -20,10 +20,10 @@ final class Hash private (private val bytes: Array[Byte]) {
def toByteArray: Array[Byte] = bytes.clone()
def toLedgerString: Ref.LedgerString =
Hash.toLedgerString(this)
def toHexaString: String =
bytes.map("%02x" format _).mkString
override def toString: String = s"Hash($toLedgerString)"
override def toString: String = s"Hash($toHexaString)"
override def equals(other: Any): Boolean =
other match {
@ -48,6 +48,16 @@ object Hash {
private val version = 0.toByte
private val underlyingHashLength = 32
val secureRandom: () => Hash = {
val random = new SecureRandom()
() =>
{
val a = Array.ofDim[Byte](underlyingHashLength)
random.nextBytes(a)
new Hash(a)
}
}
implicit val HashOrdering: Ordering[Hash] =
((hash1, hash2) => implicitly[Ordering[Iterable[Byte]]].compare(hash1.bytes, hash2.bytes))
@ -201,9 +211,6 @@ object Hash {
}
def toLedgerString(hash: Hash): Ref.LedgerString =
Ref.LedgerString.assertFromString(hash.bytes.map("%02x" format _).mkString)
def fromString(s: String): Either[String, Hash] = {
def error = s"Cannot parse hash $s"
try {

View File

@ -49,7 +49,9 @@ object Node {
def requiredAuthorizers: Set[Party]
}
object GenNode extends WithTxValue3[GenNode] with value.CidContainer3[GenNode] {
object GenNode
extends WithTxValue3[GenNode]
with value.CidContainer3WithDefaultCidResolver[GenNode] {
override private[lf] def map3[A1, A2, A3, B1, B2, B3](
f1: A1 => B1,
f2: A2 => B2,
@ -258,7 +260,7 @@ object Node {
KeyWithMaintainers.map1(f)(this)
}
object KeyWithMaintainers extends value.CidContainer1[KeyWithMaintainers] {
object KeyWithMaintainers extends value.CidContainer1WithDefaultCidResolver[KeyWithMaintainers] {
implicit def equalInstance[Val: Equal]: Equal[KeyWithMaintainers[Val]] =
ScalazEqual.withNatural(Equal[Val].equalIsNatural) { (a, b) =>
import a._

View File

@ -329,7 +329,7 @@ final case class GenTransaction[Nid, +Cid, +Val](
}
}
object GenTransaction extends value.CidContainer3[GenTransaction] {
object GenTransaction extends value.CidContainer3WithDefaultCidResolver[GenTransaction] {
type WithTxValue[Nid, +Cid] = GenTransaction[Nid, Cid, Transaction.Value[Cid]]
case class NotWellFormedError[Nid](nid: Nid, reason: NotWellFormedErrorReason)

View File

@ -5,35 +5,86 @@ package com.digitalasset.daml.lf
package value
import com.digitalasset.daml.lf.data.Ref
import com.digitalasset.daml.lf.transaction.VersionTimeline
import scala.language.higherKinds
import scala.util.control.NoStackTrace
sealed trait CidMapper[-A1, +A2, Fun] {
sealed abstract class CidMapper[-A1, +A2, In, Out] {
def map(f: Fun): A1 => A2
def map(f: In => Out): A1 => A2
// We cheat using exceptions, to get a cheap implementation of traverse using the `map` function above.
// In practice, we abort the traversal using an exception as soon as we find an input we cannot map.
def traverse[L](f: In => Either[L, Out]): A1 => Either[L, A2] = {
case class Ball(x: L) extends Throwable with NoStackTrace
a =>
try {
Right(map(x => f(x).fold(y => throw Ball(y), identity))(a))
} catch {
case Ball(x) => Left(x)
}
}
}
object CidMapper {
def trivialMapper[X, Fun]: CidMapper[X, X, Fun] =
new CidMapper[X, X, Fun] {
override def map(f: Fun): X => X = identity
type CidChecker[-A1, +A2, AllowCid] = CidMapper[A1, A2, Value.ContractId, AllowCid]
type NoCidChecker[-A1, +A2] = CidChecker[A1, A2, Nothing]
type NoRelCidChecker[-A1, +A2] = CidChecker[A1, A2, Value.AbsoluteContractId]
type RelCidResolver[-A1, +A2, Id] =
CidMapper[A1, A2, Value.RelativeContractId, Id]
type RelCidV0Resolver[-A1, +A2] =
CidMapper[A1, A2, Value.RelativeContractId, Ref.ContractIdStringV0]
type RelCidV1Resolver[-A1, +A2] =
CidMapper[A1, A2, Value.RelativeContractId, Ref.ContractIdStringV1]
def trivialMapper[X, In, Out]: CidMapper[X, X, In, Out] =
new CidMapper[X, X, In, Out] {
override def map(f: In => Out): X => X = identity
}
private[value] def basicInstance[Cid1, Cid2]: CidMapper[Cid1, Cid2, Cid1 => Cid2] =
new CidMapper[Cid1, Cid2, Cid1 => Cid2] {
private[value] def basicMapperInstance[Cid1, Cid2]: CidMapper[Cid1, Cid2, Cid1, Cid2] =
new CidMapper[Cid1, Cid2, Cid1, Cid2] {
override def map(f: Cid1 => Cid2): Cid1 => Cid2 = f
}
type RelCidResolverMapper[-A1, +A2] =
CidMapper[A1, A2, Value.ContractId => Value.AbsoluteContractId]
type NoCidMapper[-A1, +A2] = CidMapper[A1, A2, Value.ContractId => Nothing]
type NoRelCidMapper[-A1, +A2] = CidMapper[A1, A2, Value.ContractId => Value.AbsoluteContractId]
private[value] def basicCidResolverInstance[Id <: Ref.ContractIdString]
: RelCidResolver[Value.ContractId, Value.AbsoluteContractId, Id] =
new CidMapper[Value.ContractId, Value.AbsoluteContractId, Value.RelativeContractId, Id] {
override def map(
f: Value.RelativeContractId => Id,
): Value.ContractId => Value.AbsoluteContractId = {
case acoid: Value.AbsoluteContractId => acoid
case rcoid: Value.RelativeContractId => Value.AbsoluteContractId(f(rcoid))
}
}
private[value] def valueVersionCidV1Resolver[A1, A2](
implicit resolver: RelCidV1Resolver[A1, A2],
): RelCidV1Resolver[Value.VersionedValue[A1], Value.VersionedValue[A2]] =
new CidMapper[
Value.VersionedValue[A1],
Value.VersionedValue[A2],
Value.RelativeContractId,
Ref.ContractIdStringV1,
] {
override def map(
f: Value.RelativeContractId => Ref.ContractIdStringV1,
): Value.VersionedValue[A1] => Value.VersionedValue[A2] = {
case Value.VersionedValue(version, value) =>
Value.VersionedValue(
version = VersionTimeline.maxVersion(version, ValueVersions.minContractIdV1),
value = value.map1(resolver.map(f)),
)
}
}
}
trait CidContainer[+A] {
@ -42,46 +93,37 @@ trait CidContainer[+A] {
protected val self: A
def resolveRelCid[B](f: Value.RelativeContractId => Ref.ContractIdString)(
implicit mapper: RelCidResolverMapper[A, B],
final def resolveRelCidV0[B](f: Value.RelativeContractId => Ref.ContractIdStringV0)(
implicit resolver: RelCidV0Resolver[A, B]
): B =
mapper.map({
case acoid: Value.AbsoluteContractId => acoid
case rcoid: Value.RelativeContractId => Value.AbsoluteContractId(f(rcoid))
})(self)
resolver.map(f)(self)
def ensureNoCid[B](
implicit mapper: NoCidMapper[A, B]
): Either[Value.ContractId, B] = {
case class Ball(x: Value.ContractId) extends Throwable with NoStackTrace
try {
Right(mapper.map(coid => throw Ball(coid))(self))
} catch {
case Ball(coid) => Left(coid)
}
}
final def resolveRelCidV1[B](
f: Value.RelativeContractId => Either[String, Ref.ContractIdStringV1])(
implicit resolver: RelCidV1Resolver[A, B],
): Either[String, B] =
resolver.traverse[String](f)(self)
def assertNoCid[B](message: Value.ContractId => String)(
implicit mapper: NoCidMapper[A, B]
final def ensureNoCid[B](
implicit checker: NoCidChecker[A, B]
): Either[Value.ContractId, B] =
checker.traverse[Value.ContractId](Left(_))(self)
final def assertNoCid[B](message: Value.ContractId => String)(
implicit checker: NoCidChecker[A, B]
): B =
data.assertRight(ensureNoCid.left.map(message))
def ensureNoRelCid[B](
implicit mapper: NoRelCidMapper[A, B]
): Either[Value.RelativeContractId, B] = {
case class Ball(x: Value.RelativeContractId) extends Throwable with NoStackTrace
try {
Right(mapper.map({
case acoid: Value.AbsoluteContractId => acoid
case rcoid: Value.RelativeContractId => throw Ball(rcoid)
})(self))
} catch {
case Ball(coid) => Left(coid)
}
}
final def ensureNoRelCid[B](
implicit checker: NoRelCidChecker[A, B]
): Either[Value.RelativeContractId, B] =
checker.traverse[Value.RelativeContractId] {
case acoid: Value.AbsoluteContractId => Right(acoid)
case rcoid: Value.RelativeContractId => Left(rcoid)
}(self)
def assertNoRelCid[B](message: Value.ContractId => String)(
implicit mapper: NoRelCidMapper[A, B]
final def assertNoRelCid[B](message: Value.ContractId => String)(
implicit checker: NoRelCidChecker[A, B]
): B =
data.assertRight(ensureNoRelCid.left.map(message))
@ -89,35 +131,80 @@ trait CidContainer[+A] {
trait CidContainer1[F[_]] {
import CidMapper._
private[lf] def map1[A, B](f: A => B): F[A] => F[B]
final implicit def cidMapperInstance[A1, A2, Fun](
implicit mapper: CidMapper[A1, A2, Fun]
): CidMapper[F[A1], F[A2], Fun] =
new CidMapper[F[A1], F[A2], Fun] {
override def map(f: Fun): F[A1] => F[A2] =
protected final def cidMapperInstance[A1, A2, In, Out](
implicit mapper: CidMapper[A1, A2, In, Out]
): CidMapper[F[A1], F[A2], In, Out] =
new CidMapper[F[A1], F[A2], In, Out] {
override def map(f: In => Out): F[A1] => F[A2] =
map1[A1, A2](mapper.map(f))
}
final implicit def noCidCheckerInstance[A1, A2](
implicit checker1: NoCidChecker[A1, A2],
): NoCidChecker[F[A1], F[A2]] =
cidMapperInstance[A1, A2, Value.ContractId, Nothing]
final implicit def noRelCidCheckerInstance[A1, A2](
implicit checker1: NoRelCidChecker[A1, A2],
): NoRelCidChecker[F[A1], F[A2]] =
cidMapperInstance[A1, A2, Value.ContractId, Value.AbsoluteContractId]
}
trait CidContainer1WithDefaultCidResolver[F[_]] extends CidContainer1[F] {
import CidMapper._
final implicit def cidResolverInstance[A1, A2, OutputId](
implicit resolver1: RelCidResolver[A1, A2, OutputId],
): RelCidResolver[F[A1], F[A2], OutputId] =
cidMapperInstance(resolver1)
}
trait CidContainer3[F[_, _, _]] {
import CidMapper._
private[lf] def map3[A1, B1, C1, A2, B2, C2](
f1: A1 => A2,
f2: B1 => B2,
f3: C1 => C2,
): F[A1, B1, C1] => F[A2, B2, C2]
final implicit def cidMapperInstance[A1, B1, C1, A2, B2, C2, Fun](
implicit mapper1: CidMapper[A1, A2, Fun],
mapper2: CidMapper[B1, B2, Fun],
mapper3: CidMapper[C1, C2, Fun],
): CidMapper[F[A1, B1, C1], F[A2, B2, C2], Fun] =
new CidMapper[F[A1, B1, C1], F[A2, B2, C2], Fun] {
override def map(f: Fun): F[A1, B1, C1] => F[A2, B2, C2] = {
protected final def cidMapperInstance[A1, B1, C1, A2, B2, C2, In, Out](
implicit mapper1: CidMapper[A1, A2, In, Out],
mapper2: CidMapper[B1, B2, In, Out],
mapper3: CidMapper[C1, C2, In, Out],
): CidMapper[F[A1, B1, C1], F[A2, B2, C2], In, Out] =
new CidMapper[F[A1, B1, C1], F[A2, B2, C2], In, Out] {
override def map(f: In => Out): F[A1, B1, C1] => F[A2, B2, C2] = {
map3[A1, B1, C1, A2, B2, C2](mapper1.map(f), mapper2.map(f), mapper3.map(f))
}
}
final implicit def noRelCidCheckerInstance[A1, B1, C1, A2, B2, C2](
implicit checker1: NoRelCidChecker[A1, A2],
checker2: NoRelCidChecker[B1, B2],
checker3: NoRelCidChecker[C1, C2],
): NoRelCidChecker[F[A1, B1, C1], F[A2, B2, C2]] =
cidMapperInstance
}
trait CidContainer3WithDefaultCidResolver[F[_, _, _]] extends CidContainer3[F] {
import CidMapper._
final implicit def cidResolverInstance[A1, B1, C1, A2, B2, C2, OutputId](
implicit resolver1: RelCidResolver[A1, A2, OutputId],
resolver2: RelCidResolver[B1, B2, OutputId],
resolver3: RelCidResolver[C1, C2, OutputId],
): RelCidResolver[F[A1, B1, C1], F[A2, B2, C2], OutputId] =
cidMapperInstance
}

View File

@ -131,7 +131,7 @@ sealed abstract class Value[+Cid] extends CidContainer[Value[Cid]] with Product
}
object Value extends CidContainer1[Value] {
object Value extends CidContainer1WithDefaultCidResolver[Value] {
// TODO (FM) make this tail recursive
private[lf] override def map1[Cid, Cid2](f: Cid => Cid2): Value[Cid] => Value[Cid2] = {
@ -206,6 +206,16 @@ object Value extends CidContainer1[Value] {
override private[lf] def map1[A, B](f: A => B): VersionedValue[A] => VersionedValue[B] =
x => x.copy(value = Value.map1(f)(x.value))
final implicit def cidResolverV0Instance[A1, A2](
implicit mapper1: CidMapper.RelCidV0Resolver[A1, A2],
): CidMapper.RelCidV0Resolver[VersionedValue[A1], VersionedValue[A2]] =
cidMapperInstance
final implicit def cidResolverV1Instance[A1, A2](
implicit mapper1: CidMapper.RelCidV1Resolver[A1, A2],
): CidMapper.RelCidV1Resolver[VersionedValue[A1], VersionedValue[A2]] =
CidMapper.valueVersionCidV1Resolver
}
/** The parent of all [[Value]] cases that cannot possibly have a Cid.
@ -312,7 +322,7 @@ object Value extends CidContainer1[Value] {
}
object ContractInst extends CidContainer1[ContractInst] {
object ContractInst extends CidContainer1WithDefaultCidResolver[ContractInst] {
implicit def equalInstance[Val: Equal]: Equal[ContractInst[Val]] =
ScalazEqual.withNatural(Equal[Val].equalIsNatural) { (a, b) =>
import a._
@ -350,10 +360,14 @@ object Value extends CidContainer1[Value] {
object ContractId {
implicit val equalInstance: Equal[ContractId] = Equal.equalA
implicit val noCidMapper: CidMapper.NoCidMapper[ContractId, Nothing] =
CidMapper.basicInstance[ContractId, Nothing]
implicit val noRelCidMapper: CidMapper.NoRelCidMapper[ContractId, AbsoluteContractId] =
CidMapper.basicInstance[ContractId, AbsoluteContractId]
implicit val noCidMapper: CidMapper.NoCidChecker[ContractId, Nothing] =
CidMapper.basicMapperInstance[ContractId, Nothing]
implicit val noRelCidMapper: CidMapper.NoRelCidChecker[ContractId, AbsoluteContractId] =
CidMapper.basicMapperInstance[ContractId, AbsoluteContractId]
implicit val relCidV0esolver: CidMapper.RelCidV0Resolver[ContractId, AbsoluteContractId] =
CidMapper.basicCidResolverInstance
implicit val relCidV1Resolver: CidMapper.RelCidV1Resolver[ContractId, AbsoluteContractId] =
CidMapper.basicCidResolverInstance
}
/** The constructor is private so that we make sure that only this object constructs
@ -362,7 +376,8 @@ object Value extends CidContainer1[Value] {
final case class NodeId(index: Int)
object NodeId {
implicit def cidMapperInstance[Fun]: CidMapper[NodeId, NodeId, Fun] = CidMapper.trivialMapper
implicit def cidMapperInstance[In, Out]: CidMapper[NodeId, NodeId, In, Out] =
CidMapper.trivialMapper
}
/*** Keys cannot contain contract ids */

View File

@ -27,6 +27,7 @@ object ValueVersions
private[value] val minEnum = ValueVersion("5")
private[value] val minNumeric = ValueVersion("6")
private[value] val minGenMap = ValueVersion("7")
private[value] val minContractIdV1 = ValueVersion("7")
def assignVersion[Cid](v0: Value[Cid]): Either[String, ValueVersion] = {
import VersionTimeline.{maxVersion => maxVV}

View File

@ -90,7 +90,7 @@ class HashSpec extends WordSpec with Matchers {
val hash = "ea24627f5b014af67dbedb13d950e60be7f96a1a5bd9fb1a3b9a85b7fa9db4bc"
val value = complexRecordT.inj(complexRecordV)
val name = defRef("module", "name")
Hash.hashContractKey(GlobalKey(name, value)).toLedgerString shouldBe hash
Hash.hashContractKey(GlobalKey(name, value)).toHexaString shouldBe hash
}
"be deterministic and thread safe" in {
@ -542,7 +542,7 @@ class HashSpec extends WordSpec with Matchers {
.builder(Hash.Purpose.Testing)
.addTypedValue(value)
.build
.toLedgerString
.toHexaString
s"${value.toString}$sep $hash"
}
.mkString("", sep, sep)
@ -554,7 +554,7 @@ class HashSpec extends WordSpec with Matchers {
"Hash.fromString" should {
"convert properly string" in {
val s = "01cf85cfeb36d628ca2e6f583fa2331be029b6b28e877e1008fb3f862306c086"
Hash.assertFromString(s).toLedgerString shouldBe s
Hash.assertFromString(s).toHexaString shouldBe s
}
}

View File

@ -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.{FrontStack, ImmArray, Ref, Unnatural}
import com.digitalasset.daml.lf.value.Value._
@ -44,6 +45,68 @@ class ValueSpec extends FreeSpec with Matchers with Checkers with GeneratorDrive
}
}
"VersionedValue" - {
val pkgId = Ref.PackageId.assertFromString("pkgId")
val tmplId = Ref.Identifier(pkgId, Ref.QualifiedName.assertFromString("Mod:Template"))
"does not bump version when" - {
"ensureNoCid is used " in {
val value = VersionedValue[ContractId](ValueVersions.minVersion, ValueUnit)
val contract = ContractInst(tmplId, value, "agreed")
value.ensureNoCid.map(_.version) shouldBe Right(ValueVersions.minVersion)
contract.ensureNoCid.map(_.arg.version) shouldBe Right(ValueVersions.minVersion)
}
"ensureNoRelCid is used " in {
val value = VersionedValue(
ValueVersions.minVersion,
ValueContractId(AbsoluteContractId(Ref.ContractIdStringV0.assertFromString("#0:0"))),
)
val contract = ContractInst(tmplId, value, "agreed")
value.ensureNoRelCid.map(_.version) shouldBe Right(ValueVersions.minVersion)
contract.ensureNoRelCid.map(_.arg.version) shouldBe Right(ValueVersions.minVersion)
}
"resolveRelCidV0 is used" in {
val value = VersionedValue(
ValueVersions.minVersion,
ValueContractId(ValueContractId(RelativeContractId(NodeId(0), Some(randomHash())))),
)
val contract = ContractInst(tmplId, value, "agreed")
val resolver: RelativeContractId => Ref.ContractIdStringV0 = {
case RelativeContractId(NodeId(idx), _) =>
Ref.ContractIdStringV0.assertFromString(s"#0:$idx")
}
value.resolveRelCidV0(resolver).version shouldBe ValueVersions.minVersion
contract.resolveRelCidV0(resolver).arg.version shouldBe ValueVersions.minVersion
}
}
"does bump version when" - {
"resolveRelCidV1 is used" in {
val value = VersionedValue(
ValueVersions.minVersion,
ValueContractId(RelativeContractId(NodeId(0), Some(randomHash()))),
)
val contract = ContractInst(tmplId, value, "agreed")
val resolver: RelativeContractId => Either[String, Ref.ContractIdStringV1] = {
case RelativeContractId(_, Some(hash)) =>
Right(Ref.ContractIdStringV1.assertFromString("$0" + hash.toHexaString))
case RelativeContractId(_, _) =>
Left("unexpected relative contractId without discriminator")
}
value.resolveRelCidV1(resolver).map(_.version) shouldBe Right(ValueVersions.minContractIdV1)
contract.resolveRelCidV1(resolver).map(_.arg.version) shouldBe Right(
ValueVersions.minContractIdV1)
}
}
}
"Equal" - {
import com.digitalasset.daml.lf.value.ValueGenerators._
import org.scalacheck.Arbitrary
@ -59,4 +122,6 @@ class ValueSpec extends FreeSpec with Matchers with Checkers with GeneratorDrive
scalaz.Equal[T].equal(a, b) shouldBe (a == b)
}
}
private val randomHash = crypto.Hash.secureRandom
}

View File

@ -7,7 +7,8 @@ import java.time.{Duration, Instant}
import com.daml.ledger.participant.state.kvutils.DamlKvutils._
import com.daml.ledger.participant.state.v1.{PackageId, SubmittedTransaction, SubmitterInfo}
import com.digitalasset.daml.lf.data.Ref.{ContractIdString, LedgerString, Party}
import com.digitalasset.daml.lf.data.Ref
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._
@ -35,12 +36,12 @@ private[state] object Conversions {
def packageStateKey(packageId: PackageId): DamlStateKey =
DamlStateKey.newBuilder.setPackageId(packageId).build
def toAbsCoid(txId: DamlLogEntryId, coid: RelativeContractId): ContractIdString = {
def toAbsCoid(txId: DamlLogEntryId, coid: RelativeContractId): Ref.ContractIdStringV0 = {
val hexTxId =
BaseEncoding.base16.encode(txId.getEntryId.toByteArray)
// NOTE(JM): Must be in sync with [[absoluteContractIdToLogEntryId]] and
// [[absoluteContractIdToStateKey]].
ContractIdString.assertFromString(s"$hexTxId:${coid.txnid.index}")
Ref.ContractIdStringV0.assertFromString(s"$hexTxId:${coid.txnid.index}")
}
def absoluteContractIdToLogEntryId(acoid: AbsoluteContractId): (DamlLogEntryId, Int) =
@ -87,7 +88,7 @@ private[state] object Conversions {
def decodeContractId(coid: DamlContractId): AbsoluteContractId = {
val hexTxId =
BaseEncoding.base16.encode(coid.getEntryId.getEntryId.toByteArray)
AbsoluteContractId(ContractIdString.assertFromString(s"$hexTxId:${coid.getNodeId}"))
AbsoluteContractId(Ref.ContractIdString.assertFromString(s"$hexTxId:${coid.getNodeId}"))
}
def stateKeyToContractId(key: DamlStateKey): AbsoluteContractId = {

View File

@ -240,7 +240,7 @@ object KeyValueConsumption {
txId: DamlLogEntryId,
tx: SubmittedTransaction): CommittedTransaction =
/* Assign absolute contract ids */
tx.resolveRelCid(toAbsCoid(txId, _))
tx.resolveRelCidV0(toAbsCoid(txId, _))
@throws(classOf[Err])
private def parseLedgerString(what: String)(s: String): Ref.LedgerString =

View File

@ -231,7 +231,7 @@ private[kvutils] case class ProcessTransactionSubmission(
blindingInfo.localDisclosure(NodeId(key.getContractId.getNodeId.toInt))
cs.addAllLocallyDisclosedTo((localDisclosure: Iterable[String]).asJava)
val absCoInst =
createNode.coinst.resolveRelCid(Conversions.toAbsCoid(entryId, _))
createNode.coinst.resolveRelCidV0(Conversions.toAbsCoid(entryId, _))
cs.setContractInstance(
Conversions.encodeContractInstance(absCoInst)
)

View File

@ -67,7 +67,7 @@ object Ledger {
// First we "commit" the transaction by converting all relative contractIds to absolute ones
val committedTransaction: GenTransaction.WithTxValue[NodeId, AbsoluteContractId] =
transaction.resolveRelCid(EventIdFormatter.makeAbs(transactionId))
transaction.resolveRelCidV0(EventIdFormatter.makeAbs(transactionId))
// here we just need to align the type for blinding
val blindingInfo = Blinding.blind(committedTransaction)

View File

@ -139,7 +139,7 @@ class ActiveLedgerStateManager[ALS <: ActiveLedgerState[ALS]](initialState: => A
transactionId = transactionId,
eventId = nodeId,
workflowId = workflowId,
contract = nc.coinst.resolveRelCid(EventIdFormatter.makeAbs(transactionId)),
contract = nc.coinst.resolveRelCidV0(EventIdFormatter.makeAbs(transactionId)),
witnesses = disclosure(nodeId),
// The divulgences field used to be filled with data coming from the `localDivulgence` field of the blinding info.
// But this field is always empty in transactions with only absolute contract ids.