mirror of
https://github.com/digital-asset/daml.git
synced 2024-09-20 09:17:43 +03:00
Engine: fork method to resolve contractIds (#4411)
* engine: fork method to resolve relative contractId CHANGELOG_BEGIN CHANGELOG_END
This commit is contained in:
parent
19063a7c4e
commit
e02e5d61bc
@ -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"))
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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 =
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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._
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 = {
|
||||
|
@ -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 =
|
||||
|
@ -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)
|
||||
)
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user