mirror of
https://github.com/digital-asset/daml.git
synced 2024-09-20 01:07:18 +03:00
LF: Refactor PartialTransaction Context (#8804)
This prepares the introduction of rollback node. This is part of #8020. CHANGELOG_BEGIN CHANGELOG_END
This commit is contained in:
parent
8c45fd6afd
commit
c5f0b3636c
@ -392,15 +392,15 @@ final class Conversions(
|
||||
ptx.context.children.toImmArray.toSeq.sortBy(_.index).map(convertTxNodeId).asJava
|
||||
)
|
||||
|
||||
ptx.context.exeContext match {
|
||||
case None =>
|
||||
case Some(ctx) =>
|
||||
ptx.context.info match {
|
||||
case ctx: SPartialTransaction.ExercisesContextInfo =>
|
||||
val ecBuilder = proto.ExerciseContext.newBuilder
|
||||
.setTargetId(mkContractRef(ctx.targetId, ctx.templateId))
|
||||
.setChoiceId(ctx.choiceId)
|
||||
.setChosenValue(convertValue(ctx.chosenValue))
|
||||
ctx.optLocation.map(loc => ecBuilder.setExerciseLocation(convertLocation(loc)))
|
||||
builder.setExerciseContext(ecBuilder.build)
|
||||
case _: SPartialTransaction.RootContextInfo =>
|
||||
}
|
||||
builder.build
|
||||
}
|
||||
|
@ -65,6 +65,14 @@ final class ImmArray[+A] private (
|
||||
uncheckedGet(idx)
|
||||
}
|
||||
|
||||
/** O(1) */
|
||||
def get(idx: Int): Option[A] =
|
||||
if (idx >= length) {
|
||||
None
|
||||
} else {
|
||||
Some(uncheckedGet(idx))
|
||||
}
|
||||
|
||||
private def uncheckedGet(idx: Int): A = array(start + idx)
|
||||
|
||||
/** O(n) */
|
||||
|
@ -12,6 +12,7 @@ import com.daml.lf.language.Ast._
|
||||
import com.daml.lf.language.{Util => AstUtil}
|
||||
import com.daml.lf.ledger.{Authorize, CheckAuthorizationMode}
|
||||
import com.daml.lf.speedy.Compiler.{CompilationError, PackageNotFound}
|
||||
import com.daml.lf.speedy.PartialTransaction.ExercisesContextInfo
|
||||
import com.daml.lf.speedy.SError._
|
||||
import com.daml.lf.speedy.SExpr._
|
||||
import com.daml.lf.speedy.SResult._
|
||||
@ -310,10 +311,10 @@ private[lf] object Speedy {
|
||||
|
||||
private[lf] def contextActors: Set[Party] =
|
||||
withOnLedger("ptx") { onLedger =>
|
||||
onLedger.ptx.context.exeContext match {
|
||||
case Some(ctx) =>
|
||||
onLedger.ptx.context.info match {
|
||||
case ctx: ExercisesContextInfo =>
|
||||
ctx.actingParties union ctx.signatories
|
||||
case None =>
|
||||
case _ =>
|
||||
onLedger.committers
|
||||
}
|
||||
}
|
||||
@ -629,7 +630,7 @@ private[lf] object Speedy {
|
||||
private[lf] def clearCommit: Unit = withOnLedger("clearCommit") { onLedger =>
|
||||
val freshSeed =
|
||||
crypto.Hash.deriveTransactionSeed(
|
||||
onLedger.ptx.context.nextChildrenSeed,
|
||||
onLedger.ptx.context.nextChildSeed,
|
||||
scenarioServiceParticipant,
|
||||
onLedger.ptx.submissionTime,
|
||||
)
|
||||
|
@ -8,7 +8,7 @@ import com.daml.lf.ledger.Authorize
|
||||
import com.daml.lf.ledger.FailedAuthorization
|
||||
import com.daml.lf.transaction.Node.{NodeCreate, NodeFetch, NodeLookupByKey}
|
||||
|
||||
import PartialTransaction.ExercisesContext
|
||||
import PartialTransaction.ExercisesContextInfo
|
||||
|
||||
private[lf] object CheckAuthorization {
|
||||
|
||||
@ -83,7 +83,7 @@ private[lf] object CheckAuthorization {
|
||||
)
|
||||
}
|
||||
|
||||
private[lf] def authorizeExercise(ex: ExercisesContext)(
|
||||
private[lf] def authorizeExercise(ex: ExercisesContextInfo)(
|
||||
auth: Authorize
|
||||
): List[FailedAuthorization] = {
|
||||
authorize(
|
||||
|
@ -27,18 +27,43 @@ private[lf] object PartialTransaction {
|
||||
type Node = Node.GenNode[NodeId, Value.ContractId]
|
||||
type LeafNode = Node.LeafOnlyNode[Value.ContractId]
|
||||
|
||||
sealed abstract class ContextInfo {
|
||||
val childSeed: Int => crypto.Hash
|
||||
}
|
||||
|
||||
sealed abstract class RootContextInfo extends ContextInfo
|
||||
|
||||
private[PartialTransaction] final class SeededTransactionRootContext(seed: crypto.Hash)
|
||||
extends RootContextInfo {
|
||||
val childSeed = crypto.Hash.deriveNodeSeed(seed, _)
|
||||
}
|
||||
|
||||
private[PartialTransaction] final class SeededPartialTransactionRootContext(
|
||||
seeds: ImmArray[Option[crypto.Hash]]
|
||||
) extends RootContextInfo {
|
||||
override val childSeed: Int => crypto.Hash = { idx =>
|
||||
seeds.get(idx) match {
|
||||
case Some(Some(value)) =>
|
||||
value
|
||||
case _ =>
|
||||
throw new RuntimeException(s"seed for ${idx}th root node not provided")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private[PartialTransaction] object NoneSeededTransactionRootContext extends RootContextInfo {
|
||||
val childSeed: Any => Nothing = { _ =>
|
||||
throw new IllegalStateException(s"the machine is not configure to create transaction")
|
||||
}
|
||||
}
|
||||
|
||||
/** Contexts of the transaction graph builder, which we use to record
|
||||
* the sub-transaction structure due to 'exercises' statements.
|
||||
*/
|
||||
final class Context private (
|
||||
val exeContext: Option[ExercisesContext], // empty if root context
|
||||
val children: BackStack[NodeId],
|
||||
protected val childrenSeeds: Int => crypto.Hash,
|
||||
) {
|
||||
def addChild(child: NodeId): Context =
|
||||
new Context(exeContext, children :+ child, childrenSeeds)
|
||||
def nextChildrenSeed: crypto.Hash =
|
||||
childrenSeeds(children.length)
|
||||
final case class Context(info: ContextInfo, children: BackStack[NodeId]) {
|
||||
def addChild(child: NodeId): Context = Context(info, children :+ child)
|
||||
// needs to be lazy as info.childrenSeeds may be undefined on children.length
|
||||
def nextChildSeed: crypto.Hash = info.childSeed(children.length)
|
||||
}
|
||||
|
||||
object Context {
|
||||
@ -46,33 +71,12 @@ private[lf] object PartialTransaction {
|
||||
def apply(initialSeeds: InitialSeeding): Context =
|
||||
initialSeeds match {
|
||||
case InitialSeeding.TransactionSeed(seed) =>
|
||||
new Context(None, BackStack.empty, childrenSeeds(seed))
|
||||
Context(new SeededTransactionRootContext(seed), BackStack.empty)
|
||||
case InitialSeeding.RootNodeSeeds(seeds) =>
|
||||
new Context(
|
||||
None,
|
||||
BackStack.empty,
|
||||
i =>
|
||||
(if (0 <= i && i < seeds.length) seeds(i) else None)
|
||||
.getOrElse(throw new RuntimeException(s"seed for ${i}th root node not provided")),
|
||||
)
|
||||
Context(new SeededPartialTransactionRootContext(seeds), BackStack.empty)
|
||||
case InitialSeeding.NoSeed =>
|
||||
new Context(
|
||||
None,
|
||||
BackStack.empty,
|
||||
_ => throw new RuntimeException(s"the machine is not configure to create transaction"),
|
||||
)
|
||||
Context(NoneSeededTransactionRootContext, BackStack.empty)
|
||||
}
|
||||
|
||||
private[PartialTransaction] def apply(exeContext: ExercisesContext) =
|
||||
new Context(
|
||||
Some(exeContext),
|
||||
BackStack.empty,
|
||||
childrenSeeds(exeContext.parent.nextChildrenSeed),
|
||||
)
|
||||
|
||||
private[this] def childrenSeeds(seed: crypto.Hash)(i: Int) =
|
||||
crypto.Hash.deriveNodeSeed(seed, i)
|
||||
|
||||
}
|
||||
|
||||
/** Context information to remember when building a sub-transaction
|
||||
@ -96,7 +100,7 @@ private[lf] object PartialTransaction {
|
||||
* happening.
|
||||
* @param byKey True if the exercise is done "by key"
|
||||
*/
|
||||
case class ExercisesContext(
|
||||
final case class ExercisesContextInfo(
|
||||
targetId: Value.ContractId,
|
||||
templateId: TypeConName,
|
||||
contractKey: Option[Node.KeyWithMaintainers[Value[Nothing]]],
|
||||
@ -111,7 +115,10 @@ private[lf] object PartialTransaction {
|
||||
nodeId: NodeId,
|
||||
parent: Context,
|
||||
byKey: Boolean,
|
||||
)
|
||||
) extends ContextInfo {
|
||||
val childSeed =
|
||||
crypto.Hash.deriveNodeSeed(parent.nextChildSeed, _)
|
||||
}
|
||||
|
||||
def initial(
|
||||
pkg2TxVersion: Ref.PackageId => TxVersion,
|
||||
@ -227,14 +234,16 @@ private[lf] case class PartialTransaction(
|
||||
* the `outputTransactionVersions`.
|
||||
*/
|
||||
def finish: PartialTransaction.Result =
|
||||
if (context.exeContext.isEmpty && aborted.isEmpty) {
|
||||
CompleteTransaction(
|
||||
SubmittedTransaction(
|
||||
TxVersion.asVersionedTransaction(context.children.toImmArray, nodes)
|
||||
context.info match {
|
||||
case _: RootContextInfo if aborted.isEmpty =>
|
||||
CompleteTransaction(
|
||||
SubmittedTransaction(
|
||||
TxVersion.asVersionedTransaction(context.children.toImmArray, nodes)
|
||||
)
|
||||
)
|
||||
)
|
||||
} else
|
||||
IncompleteTransaction(this)
|
||||
case _ =>
|
||||
IncompleteTransaction(this)
|
||||
}
|
||||
|
||||
/** Extend the 'PartialTransaction' with a node for creating a
|
||||
* contract instance.
|
||||
@ -256,7 +265,7 @@ private[lf] case class PartialTransaction(
|
||||
.mkString(",")}"""
|
||||
)
|
||||
} else {
|
||||
val nodeSeed = context.nextChildrenSeed
|
||||
val nodeSeed = context.nextChildSeed
|
||||
val discriminator =
|
||||
crypto.Hash.deriveContractDiscriminator(nodeSeed, submissionTime, stakeholders)
|
||||
val cid = Value.ContractId.V1(discriminator)
|
||||
@ -365,7 +374,7 @@ private[lf] case class PartialTransaction(
|
||||
} else {
|
||||
val nid = NodeId(nextNodeIdx)
|
||||
val ec =
|
||||
ExercisesContext(
|
||||
ExercisesContextInfo(
|
||||
targetId = targetId,
|
||||
templateId = templateId,
|
||||
contractKey = mbKey,
|
||||
@ -388,7 +397,7 @@ private[lf] case class PartialTransaction(
|
||||
templateId,
|
||||
copy(
|
||||
nextNodeIdx = nextNodeIdx + 1,
|
||||
context = Context(ec),
|
||||
context = Context(ec, BackStack.empty),
|
||||
// important: the semantics of DAML dictate that contracts are immediately
|
||||
// inactive as soon as you exercise it. therefore, mark it as consumed now.
|
||||
consumedBy = if (consuming) consumedBy.updated(targetId, nid) else consumedBy,
|
||||
@ -404,8 +413,8 @@ private[lf] case class PartialTransaction(
|
||||
}
|
||||
|
||||
def endExercises(value: Value[Value.ContractId]): PartialTransaction =
|
||||
context.exeContext match {
|
||||
case Some(ec) =>
|
||||
context.info match {
|
||||
case ec: ExercisesContextInfo =>
|
||||
val exerciseNode = Node.NodeExercises(
|
||||
targetCoid = ec.targetId,
|
||||
templateId = ec.templateId,
|
||||
@ -424,13 +433,13 @@ private[lf] case class PartialTransaction(
|
||||
version = packageToTransactionVersion(ec.templateId.packageId),
|
||||
)
|
||||
val nodeId = ec.nodeId
|
||||
val nodeSeed = ec.parent.nextChildrenSeed
|
||||
val nodeSeed = ec.parent.nextChildSeed
|
||||
copy(
|
||||
context = ec.parent.addChild(nodeId),
|
||||
nodes = nodes.updated(nodeId, exerciseNode),
|
||||
nodeSeeds = nodeSeeds :+ (nodeId -> nodeSeed),
|
||||
)
|
||||
case None =>
|
||||
case _ =>
|
||||
noteAbort(Tx.EndExerciseInRootContext)
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user