-
Notifications
You must be signed in to change notification settings - Fork 206
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Normalize transactions & values as a separate pass (#10524)
* Normalize transactions & values as a separate pass. Use for simpler defintiion of isReplayedBy. CHANGELOG_BEGIN CHANGELOG_END normalize transaction version * remove stray import from bad merge which breaks scala 2_12 build * change isReplayedBy to only norm its RIGHT (replay) argument * add forgotton normalization for ValueEmum * switch to use existing value normalization code (remove my newly coded duplicate code) * normalize submittedTransaction before calling engine.validate * dont call normalizeTx from Engine.validate * *do* call normalizeTx from Engine.validate
- Loading branch information
1 parent
99e1d78
commit 9db5ccf
Showing
9 changed files
with
193 additions
and
390 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
127 changes: 127 additions & 0 deletions
127
daml-lf/transaction/src/main/scala/com/digitalasset/daml/lf/transaction/Normalization.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
// Copyright (c) 2021 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package com.daml.lf | ||
package transaction | ||
|
||
import com.daml.lf.value.{Value => V} | ||
|
||
import com.daml.lf.transaction.Node.{ | ||
KeyWithMaintainers, | ||
GenNode, | ||
NodeCreate, | ||
NodeFetch, | ||
NodeLookupByKey, | ||
NodeExercises, | ||
NodeRollback, | ||
} | ||
|
||
class Normalization[Nid, Cid] { | ||
|
||
/** This class provides methods to normalize a transaction and embedded values. | ||
* | ||
* Informal spec: normalization is the result of serialization and deserialization. | ||
* | ||
* Here we take care of the following: | ||
* - type information is dropped from Variant and Record values | ||
* - field names are dropped from Records | ||
* - values are normalized recursively | ||
* - all values embedded in transaction nodes are normalized | ||
* - version-specific normalization is applied to the 'byKey' fields of 'NodeFetch' and 'NodeExercises' | ||
* | ||
* We do not normalize the node-ids in the transaction here, but rather assume that | ||
* aspect of normalization has already been performed (by the engine, or by | ||
* deserialization). | ||
* | ||
* Eventually we would like that all aspects of normalization are achieved directly by | ||
* the transaction which is constructed by the engine. When this is done, we will no | ||
* longer need this separate normalization pass. | ||
*/ | ||
|
||
private type Val = V[Cid] | ||
private type KWM = KeyWithMaintainers[Val] | ||
private type Node = GenNode[Nid, Cid] | ||
private type VTX = VersionedTransaction[Nid, Cid] | ||
|
||
def normalizeTx(vtx: VTX): VTX = { | ||
vtx match { | ||
case VersionedTransaction(_, nodes, roots) => | ||
// TODO: Normalized version calc should be shared with code in asVersionedTransaction | ||
val version = roots.iterator.foldLeft(TransactionVersion.minVersion) { (acc, nodeId) => | ||
import scala.Ordering.Implicits.infixOrderingOps | ||
nodes(nodeId).optVersion match { | ||
case Some(version) => acc max version | ||
case None => acc max TransactionVersion.minExceptions | ||
} | ||
} | ||
VersionedTransaction( | ||
version, | ||
nodes.map { case (k, v) => | ||
(k, normNode(v)) | ||
}, | ||
vtx.roots, | ||
) | ||
} | ||
} | ||
|
||
private def normNode( | ||
node: Node | ||
): Node = { | ||
import scala.Ordering.Implicits.infixOrderingOps | ||
node match { | ||
|
||
case old: NodeCreate[_] => | ||
old | ||
.copy(arg = normValue(old.version)(old.arg)) | ||
.copy(key = old.key.map(normKWM(old.version))) | ||
|
||
case old: NodeFetch[_] => | ||
(if (old.version >= TransactionVersion.minByKey) { | ||
old | ||
} else { | ||
old.copy(byKey = false) | ||
}) | ||
.copy( | ||
key = old.key.map(normKWM(old.version)) | ||
) | ||
|
||
case old: NodeExercises[_, _] => | ||
(if (old.version >= TransactionVersion.minByKey) { | ||
old | ||
} else { | ||
old.copy(byKey = false) | ||
}) | ||
.copy( | ||
chosenValue = normValue(old.version)(old.chosenValue), | ||
exerciseResult = old.exerciseResult.map(normValue(old.version)), | ||
key = old.key.map(normKWM(old.version)), | ||
) | ||
|
||
case old: NodeLookupByKey[_] => | ||
old.copy( | ||
key = normKWM(old.version)(old.key) | ||
) | ||
|
||
case old: NodeRollback[_] => old | ||
|
||
} | ||
} | ||
|
||
private def normValue(version: TransactionVersion)(x: Val): Val = { | ||
Util.assertNormalizeValue(x, version) | ||
} | ||
|
||
private def normKWM(version: TransactionVersion)(x: KWM): KWM = { | ||
x match { | ||
case KeyWithMaintainers(key, maintainers) => | ||
KeyWithMaintainers(normValue(version)(key), maintainers) | ||
} | ||
} | ||
|
||
} | ||
|
||
object Normalization { | ||
def normalizeTx[Nid, Cid](tx: VersionedTransaction[Nid, Cid]): VersionedTransaction[Nid, Cid] = { | ||
new Normalization().normalizeTx(tx) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.