Skip to content

Commit

Permalink
LF: Simplify transaction versionning for interface (#11744)
Browse files Browse the repository at this point in the history
We revert #11626, and just change the way transaction version is
computed:

- As before, Node version is calculated from the
  package of the template ID action.

- Transaction version is the max of the version of all the nodes,
   instead of the root nodes.

CHANGELOG_BEGIN
CHANGELOG_END
  • Loading branch information
remyhaemmerle-da authored Nov 18, 2021
1 parent 4b59c57 commit 1bb2fc2
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 187 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,40 +14,17 @@ import scala.Ordering.Implicits.infixOrderingOps
* @tparam V either [[com.daml.lf.language.LanguageVersion]] or
* [[com.daml.lf.transaction.TransactionVersion]].
*/
final case class VersionRange[V] private (
final case class VersionRange[V](
min: V,
max: V,
)(implicit ordering: Ordering[V])
extends data.NoCopy {
)(implicit ordering: Ordering[V]) {

def join(that: VersionRange[V]): VersionRange[V] =
new VersionRange(
min = this.min min that.min,
max = this.max max that.max,
)
require(min <= max)

def join(version: V): VersionRange[V] = join(VersionRange(version))

private[lf] def map[W](f: V => W)(implicit ordering: Ordering[W]): VersionRange[W] =
private[lf] def map[W](f: V => W)(implicit ordering: Ordering[W]) =
VersionRange(f(min), f(max))

def contains(v: V): Boolean =
min <= v && v <= max
}

object VersionRange {

def apply[V](min: V, max: V)(implicit ordering: Ordering[V]): VersionRange[V] = {
assert(min <= max)
new VersionRange(min, max)
}

def apply[V](version: V)(implicit ordering: Ordering[V]): VersionRange[V] =
new VersionRange(version, version)

// We represent an empty Range by setting min and max to the max/min possible value
// O(n)
private[lf] def slowEmpty[V](allValues: Seq[V])(implicit ordering: Ordering[V]): VersionRange[V] =
new VersionRange(allValues.max, allValues.min)

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import com.daml.lf.transaction.{
SubmittedTransaction,
Transaction => Tx,
TransactionVersion => TxVersion,
VersionedTransaction => VersionedTx,
}
import com.daml.lf.value.Value
import com.daml.nameof.NameOf
Expand Down Expand Up @@ -85,28 +84,25 @@ private[lf] object PartialTransaction {
*/
final case class Context(
info: ContextInfo,
minChildVersion: TxVersion, // tracks the minimum version of any child within `children`
children: BackStack[NodeId],
// tracks the min and max version of any child within `children`
childrenVersions: VersionRange[TxVersion],
nextActionChildIdx: Int,
) {
// when we add a child node we must pass the minimum-version contained in that child
def addActionChild(child: NodeId, childVersions: VersionRange[TxVersion]): Context =
Context(info, children :+ child, childrenVersions join childVersions, nextActionChildIdx + 1)
def addRollbackChild(
child: NodeId,
childVersions: VersionRange[TxVersion],
nextActionChildIdx: Int,
): Context =
Context(info, children :+ child, childrenVersions join childVersions, nextActionChildIdx)
def addActionChild(child: NodeId, version: TxVersion): Context = {
Context(info, minChildVersion min version, children :+ child, nextActionChildIdx + 1)
}
def addRollbackChild(child: NodeId, version: TxVersion, nextActionChildIdx: Int): Context =
Context(info, minChildVersion min version, children :+ child, nextActionChildIdx)
// This function may be costly, it must be call at most once for each node.
def nextActionChildSeed: crypto.Hash = info.actionChildSeed(nextActionChildIdx)
}

object Context {

def apply(info: ContextInfo, nextActionChildIdx: Int = 0): Context =
Context(info, BackStack.empty, TxVersion.NoVersions, nextActionChildIdx)
def apply(info: ContextInfo): Context =
// An empty context, with no children; minChildVersion is set to the max-int.
Context(info, TxVersion.VDev, BackStack.empty, 0)

def apply(initialSeeds: InitialSeeding, committers: Set[Party]): Context =
initialSeeds match {
Expand Down Expand Up @@ -376,7 +372,9 @@ private[speedy] case class PartialTransaction(
val tx0 = Tx(nodes, roots)
val (tx, seeds) = NormalizeRollbacks.normalizeTx(tx0)
CompleteTransaction(
SubmittedTransaction(VersionedTx(context.childrenVersions.max, tx.nodes, tx.roots)),
SubmittedTransaction(
TxVersion.asVersionedTransaction(tx)
),
locationInfo(),
seeds.zip(actionNodeSeeds.toImmArray),
)
Expand Down Expand Up @@ -432,7 +430,7 @@ private[speedy] case class PartialTransaction(
val ptx = copy(
actionNodeLocations = actionNodeLocations :+ optLocation,
nextNodeIdx = nextNodeIdx + 1,
context = context.addActionChild(nid, VersionRange(version)),
context = context.addActionChild(nid, version),
nodes = nodes.updated(nid, createNode),
actionNodeSeeds = actionNodeSeeds :+ actionNodeSeed,
localContracts = localContracts + cid,
Expand Down Expand Up @@ -609,14 +607,7 @@ private[speedy] case class PartialTransaction(
case _ => keys
},
),
).noteAuthFails(
nid,
CheckAuthorization.authorizeExercise(
optLocation,
makeExNode(ec, packageToTransactionVersion(templateId.packageId)),
),
auth,
)
).noteAuthFails(nid, CheckAuthorization.authorizeExercise(optLocation, makeExNode(ec)), auth)
}

/** Close normally an exercise context.
Expand All @@ -626,14 +617,12 @@ private[speedy] case class PartialTransaction(
context.info match {
case ec: ExercisesContextInfo =>
val result = normValue(ec.templateId, value)
val exeVersions =
context.childrenVersions join packageToTransactionVersion(ec.templateId.packageId)
val exerciseNode =
makeExNode(ec, exeVersions.max)
.copy(children = context.children.toImmArray, exerciseResult = Some(result))
makeExNode(ec).copy(children = context.children.toImmArray, exerciseResult = Some(result))
val nodeId = ec.nodeId
copy(
context = ec.parent.addActionChild(nodeId, exeVersions),
context =
ec.parent.addActionChild(nodeId, exerciseNode.version min context.minChildVersion),
nodes = nodes.updated(nodeId, exerciseNode),
)
case _ =>
Expand All @@ -649,16 +638,15 @@ private[speedy] case class PartialTransaction(
def abortExercises: PartialTransaction =
context.info match {
case ec: ExercisesContextInfo =>
val exeVersions =
context.childrenVersions join packageToTransactionVersion(ec.templateId.packageId)
val exerciseNode =
makeExNode(ec, exeVersions.max).copy(children = context.children.toImmArray)
val exerciseNode = makeExNode(ec).copy(children = context.children.toImmArray)
val nodeId = ec.nodeId
val actionNodeSeed = context.nextActionChildSeed
copy(
context = ec.parent.addActionChild(nodeId, exeVersions),
context =
ec.parent.addActionChild(nodeId, exerciseNode.version min context.minChildVersion),
nodes = nodes.updated(nodeId, exerciseNode),
actionNodeSeeds = actionNodeSeeds :+ actionNodeSeed,
actionNodeSeeds =
actionNodeSeeds :+ actionNodeSeed, //(NC) pushed by 'beginExercises'; why push again?
)
case _ =>
InternalError.runtimeException(
Expand All @@ -667,7 +655,8 @@ private[speedy] case class PartialTransaction(
)
}

private[this] def makeExNode(ec: ExercisesContextInfo, version: TxVersion): Node.Exercise =
private[this] def makeExNode(ec: ExercisesContextInfo): Node.Exercise = {
val version = packageToTransactionVersion(ec.templateId.packageId)
Node.Exercise(
targetCoid = ec.targetId,
templateId = ec.templateId,
Expand All @@ -685,6 +674,7 @@ private[speedy] case class PartialTransaction(
byInterface = ec.byInterface,
version = version,
)
}

/** Open a Try context.
* Must be closed by `endTry`, `abortTry`, or `rollbackTry`.
Expand All @@ -694,7 +684,7 @@ private[speedy] case class PartialTransaction(
val info = TryContextInfo(nid, context, activeState, authorizers = context.info.authorizers)
copy(
nextNodeIdx = nextNodeIdx + 1,
context = Context(info, context.nextActionChildIdx),
context = Context(info).copy(nextActionChildIdx = context.nextActionChildIdx),
)
}

Expand All @@ -707,7 +697,6 @@ private[speedy] case class PartialTransaction(
copy(
context = info.parent.copy(
children = info.parent.children :++ context.children.toImmArray,
childrenVersions = context.childrenVersions,
nextActionChildIdx = context.nextActionChildIdx,
)
)
Expand All @@ -730,18 +719,19 @@ private[speedy] case class PartialTransaction(
*/
def rollbackTry(ex: SValue.SAny): PartialTransaction = {
// we must never create a rollback containing a node with a version pre-dating exceptions
if (context.childrenVersions.min < TxVersion.minExceptions)
if (context.minChildVersion < TxVersion.minExceptions) {
throw SError.SErrorDamlException(
interpretation.Error.UnhandledException(ex.ty, ex.value.toUnnormalizedValue)
)
}
context.info match {
case info: TryContextInfo =>
// In the case of there being no children we could drop the entire rollback node.
// But we do that in a later normalization phase, not here.
val rollbackNode = Node.Rollback(context.children.toImmArray)
copy(
context = info.parent
.addRollbackChild(info.nodeId, context.childrenVersions, context.nextActionChildIdx),
.addRollbackChild(info.nodeId, context.minChildVersion, context.nextActionChildIdx),
nodes = nodes.updated(info.nodeId, rollbackNode),
).resetActiveState(info.beginState)
case _ =>
Expand Down Expand Up @@ -796,7 +786,7 @@ private[speedy] case class PartialTransaction(
copy(
actionNodeLocations = actionNodeLocations :+ optLocation,
nextNodeIdx = nextNodeIdx + 1,
context = context.addActionChild(nid, VersionRange(version)),
context = context.addActionChild(nid, version),
nodes = nodes.updated(nid, node),
)
}
Expand Down
Loading

0 comments on commit 1bb2fc2

Please sign in to comment.