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