Skip to content

Commit

Permalink
[Engine] push absolute contract ids inside the evaluation (digital-as…
Browse files Browse the repository at this point in the history
…set#4652)

* [Engine] push absolute contract inside the evaluation
* Remove discriminator fom relative contract id

CHANGELOG_BEGIN
CHANGELOG_END
  • Loading branch information
remyhaemmerle-da authored Feb 21, 2020
1 parent c68dd19 commit 0321a1b
Showing 9 changed files with 72 additions and 68 deletions.
Original file line number Diff line number Diff line change
@@ -310,7 +310,7 @@ case class Conversions(homePackageId: Ref.PackageId) {
.setContractId(coid)
.setTemplateId(convertIdentifier(templateId))
.build
case V.RelativeContractId(txnid, _) =>
case V.RelativeContractId(txnid) =>
ContractRef.newBuilder
.setRelative(true)
.setContractId(txnid.index.toString)
@@ -321,7 +321,7 @@ case class Conversions(homePackageId: Ref.PackageId) {
def convertContractId(coid: V.ContractId): String =
coid match {
case V.AbsoluteContractId(coid) => coid
case V.RelativeContractId(txnid, _) => txnid.index.toString
case V.RelativeContractId(txnid) => txnid.index.toString
}

def convertScenarioStep(
@@ -653,7 +653,7 @@ case class Conversions(homePackageId: Ref.PackageId) {
coid match {
case V.AbsoluteContractId(acoid) =>
builder.setContractId(acoid)
case V.RelativeContractId(txnid, _) =>
case V.RelativeContractId(txnid) =>
builder.setContractId(txnid.index.toString)
}
case V.ValueList(values) =>
Original file line number Diff line number Diff line change
@@ -339,7 +339,7 @@ object Pretty {
def prettyContractId(coid: ContractId): Doc =
coid match {
case AbsoluteContractId(acoid) => text(acoid)
case RelativeContractId(rcoid, _) => str(rcoid)
case RelativeContractId(rcoid) => str(rcoid)
}

def prettyActiveContracts(c: L.LedgerData): Doc = {
@@ -416,7 +416,7 @@ object Pretty {
text(constructor)
case ValueText(t) => char('"') + text(t) + char('"')
case ValueContractId(AbsoluteContractId(acoid)) => text(acoid)
case ValueContractId(RelativeContractId(rcoid, _)) =>
case ValueContractId(RelativeContractId(rcoid)) =>
char('~') + text(rcoid.toString)
case ValueUnit => text("<unit>")
case ValueBool(b) => str(b)
Original file line number Diff line number Diff line change
@@ -949,48 +949,50 @@ object SBuiltin {
case SContractId(coid) => coid
case v => crash(s"expected contract id, got: $v")
}
val arg = coid match {
case rcoid: V.RelativeContractId =>
machine.ptx.lookupLocalContract(rcoid) match {
case None =>
crash(s"Relative contract $rcoid ($templateId) not found from partial transaction")
case Some((_, Some(consumedBy))) =>
throw DamlELocalContractNotActive(coid, templateId, consumedBy)
case Some((coinst, None)) =>
// Here we crash hard rather than throwing a "nice" error
// ([[DamlEWronglyTypedContract]]) since if _relative_ contract
// id to be of the wrong template it means that the DAML-LF
// program that generated it is ill-typed.
//
// On the other hand absolute contract ids can come from outside
// (e.g. Ledger API) and thus we need to fail more gracefully
// (see below).
if (coinst.template != templateId) {
val coinst =
machine.ptx
.lookupLocalContract(coid)
.getOrElse(
coid match {
case acoid: V.AbsoluteContractId =>
throw SpeedyHungry(
SResultNeedContract(
acoid,
templateId,
machine.committers,
cbMissing = _ => machine.tryHandleException(),
cbPresent = {
coinst =>
// Note that we cannot throw in this continuation -- instead
// set the control appropriately which will crash the machine
// correctly later.
if (coinst.template != templateId) {
machine.ctrl =
CtrlWronglyTypeContractId(acoid, templateId, coinst.template)
} else {
machine.ctrl = CtrlValue(SValue.fromValue(coinst.arg.value))
}
},
),
)
case rcoid: V.RelativeContractId =>
crash(s"Relative contract $rcoid ($templateId) not found from partial transaction")
}
coinst.arg
}
case acoid: V.AbsoluteContractId =>
throw SpeedyHungry(
SResultNeedContract(
acoid,
templateId,
machine.committers,
cbMissing = _ => machine.tryHandleException(),
cbPresent = { coinst =>
// Note that we cannot throw in this continuation -- instead
// set the control appropriately which will crash the machine
// correctly later.
if (coinst.template != templateId) {
machine.ctrl = CtrlWronglyTypeContractId(acoid, templateId, coinst.template)
} else {
machine.ctrl = CtrlValue(SValue.fromValue(coinst.arg.value))
}
},
),
}
)

if (coinst.template != templateId) {
// Here we crash hard rather than throwing a "nice" error
// ([[DamlEWronglyTypedContract]]) since if _relative_ contract
// id to be of the wrong template it means that the DAML-LF
// program that generated it is ill-typed.
//
// On the other hand absolute contract ids can come from outside
// (e.g. Ledger API) and thus we need to fail more gracefully
// (see below).
crash(s"Relative contract $coid ($templateId) not found from partial transaction")
} else {
machine.ctrl = CtrlValue(SValue.fromValue(coinst.arg.value))
}
machine.ctrl = CtrlValue(SValue.fromValue(arg.value))
}
}

Original file line number Diff line number Diff line change
@@ -95,6 +95,7 @@ object PartialTransaction {
context = ContextRoot(seedWithTime.map(_._1)),
aborted = None,
keys = Map.empty,
localContracts = Map.empty,
)

}
@@ -131,6 +132,7 @@ case class PartialTransaction(
context: PartialTransaction.Context,
aborted: Option[Tx.TransactionError],
keys: Map[Node.GlobalKey, Option[Value.ContractId]],
localContracts: Map[Value.ContractId, Value.NodeId]
) {

import PartialTransaction._
@@ -179,10 +181,6 @@ case class PartialTransaction(
sb.toString
}

def resolveCidDiscriminator(node: Tx.Node) =
node.resolveRelCid(cid =>
Ref.ContractIdString.assertFromString("0" + cid.discriminator.get.toHexaString))

/** Finish building a transaction; i.e., try to extract a complete
* transaction from the given 'PartialTransaction'. This fails if
* the 'PartialTransaction' is not yet complete or has been
@@ -193,9 +191,7 @@ case class PartialTransaction(
case ContextRoot(transactionSeed, children) if aborted.isEmpty =>
Right(
GenTransaction(
nodes =
if (transactionSeed.isEmpty) nodes
else nodes.transform((_, v) => resolveCidDiscriminator(v)),
nodes = nodes,
roots = children.toImmArray,
optUsedPackages = None,
transactionSeed = transactionSeed,
@@ -210,13 +206,14 @@ case class PartialTransaction(
* consumed if any.
*/
def lookupLocalContract(
lcoid: Value.RelativeContractId,
): Option[(Value.ContractInst[Tx.Value[Value.ContractId]], Option[Value.NodeId])] =
lcoid: Value.ContractId,
): Option[Value.ContractInst[Tx.Value[Value.ContractId]]] =
for {
node <- nodes.get(lcoid.txnid)
nid <- localContracts.get(lcoid)
node <- nodes.get(nid)
coinst <- node match {
case create: Node.NodeCreate.WithTxValue[Value.ContractId] =>
Some((create.coinst, consumedBy.get(lcoid)))
Some(create.coinst)
case _: Node.NodeExercises[_, _, _] | _: Node.NodeFetch[_] |
_: Node.NodeLookupByKey[_, _] =>
None
@@ -241,12 +238,17 @@ case class PartialTransaction(
)
} else {
val nodeSeed = deriveChildSeed
val contractDiscriminator =
val discriminator =
for {
seed <- nodeSeed
time <- submissionTime
} yield crypto.Hash.deriveContractDiscriminator(seed, time, stakeholders)
val cid = Value.RelativeContractId(Value.NodeId(nextNodeIdx), contractDiscriminator)
val cid = discriminator.fold[Value.ContractId](
Value.RelativeContractId(Value.NodeId(nextNodeIdx))
)(
hash =>
Value.AbsoluteContractId(Ref.ContractIdString.assertFromString("0" + hash.toHexaString))
)
val createNode = Node.NodeCreate(
nodeSeed,
cid,
@@ -261,6 +263,7 @@ case class PartialTransaction(
nextNodeIdx = nextNodeIdx + 1,
context = context.addChild(nid),
nodes = nodes.updated(nid, createNode),
localContracts = localContracts.updated(cid, nid)
)

// if we have a contract key being added, include it in the list of
Original file line number Diff line number Diff line change
@@ -349,8 +349,7 @@ object Value extends CidContainer1WithDefaultCidResolver[Value] {
*/
sealed trait ContractId extends Product with Serializable
final case class AbsoluteContractId(coid: ContractIdString) extends ContractId
final case class RelativeContractId(txnid: NodeId, discriminator: Option[crypto.Hash] = None)
extends ContractId
final case class RelativeContractId(txnid: NodeId) extends ContractId

object ContractId {
implicit val equalInstance: Equal[ContractId] = Equal.equalA
Original file line number Diff line number Diff line change
@@ -60,13 +60,13 @@ object ValueCoder {
): Either[EncodeError, Either[String, (proto.ContractId)]] =
if (useOldStringField(sv))
cid match {
case RelativeContractId(nid, _) =>
case RelativeContractId(nid) =>
Right(Left("~" + nid.index.toString))
case AbsoluteContractId(s) =>
Right(Left(s))
} else
cid match {
case RelativeContractId(nid, _) =>
case RelativeContractId(nid) =>
Right(
Right(
proto.ContractId.newBuilder
@@ -105,7 +105,7 @@ object ValueCoder {
.bimap(
_ => //
DecodeError(s"""cannot parse relative contractId "$s""""),
idx => Some(RelativeContractId(NodeId(idx), None))
idx => Some(RelativeContractId(NodeId(idx)))
)

private def stringToCidString(s: String): Either[DecodeError, Ref.ContractIdString] =
@@ -151,7 +151,7 @@ object ValueCoder {
structForm: ValueOuterClass.ContractId,
): Either[DecodeError, Option[AbsoluteContractId]] =
CidDecoder.decodeOptional(sv, stringForm, structForm).flatMap {
case Some(RelativeContractId(_, _)) =>
case Some(RelativeContractId(_)) =>
Left(DecodeError("Unexpected relative contractId"))
case Some(AbsoluteContractId(coid)) =>
Right(Some(AbsoluteContractId(coid)))
Original file line number Diff line number Diff line change
@@ -73,11 +73,11 @@ class ValueSpec extends FreeSpec with Matchers with Checkers with GeneratorDrive
"resolveRelCidV0 is used" in {
val value = VersionedValue(
ValueVersions.minVersion,
ValueContractId(ValueContractId(RelativeContractId(NodeId(0), Some(randomHash())))),
ValueContractId(ValueContractId(RelativeContractId(NodeId(0)))),
)
val contract = ContractInst(tmplId, value, "agreed")
val resolver: RelativeContractId => Ref.ContractIdString = {
case RelativeContractId(NodeId(idx), _) =>
case RelativeContractId(NodeId(idx)) =>
Ref.ContractIdString.assertFromString(s"#0:$idx")
}
value.resolveRelCid(resolver).version shouldBe ValueVersions.minVersion
@@ -104,5 +104,4 @@ class ValueSpec extends FreeSpec with Matchers with Checkers with GeneratorDrive
}
}

private val randomHash = crypto.Hash.secureRandom
}
Original file line number Diff line number Diff line change
@@ -75,7 +75,8 @@ private[state] object Conversions {

def relativeContractIdToStateKey(
entryId: DamlLogEntryId,
rcoid: RelativeContractId): DamlStateKey =
rcoid: RelativeContractId,
): DamlStateKey =
DamlStateKey.newBuilder
.setContractId(encodeRelativeContractId(entryId, rcoid))
.build
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@ object EventIdFormatter {
def makeAbsCoid(transactionId: LedgerString)(coid: Lf.ContractId): Lf.AbsoluteContractId =
coid match {
case a @ Lf.AbsoluteContractId(_) => a
case Lf.RelativeContractId(txnid, _) =>
case Lf.RelativeContractId(txnid) =>
Lf.AbsoluteContractId(fromTransactionId(transactionId, txnid))
}

0 comments on commit 0321a1b

Please sign in to comment.