mirror of
https://github.com/digital-asset/daml.git
synced 2024-09-20 01:07:18 +03:00
DAML-LF: remove seed from node, move it to transaction meta data. (#5570)
* DAML-LF: remove seed from node, move it to transaction meta data. + redesign seeding to validate partial transaction. CHANGELOG_BEGIN CHANGELOG_END
This commit is contained in:
parent
7067fa432e
commit
a178e3e3e9
@ -8,14 +8,13 @@ import com.daml.lf.command._
|
||||
import com.daml.lf.data._
|
||||
import com.daml.lf.data.Ref.{PackageId, ParticipantId, Party}
|
||||
import com.daml.lf.language.Ast._
|
||||
import com.daml.lf.speedy.Compiler
|
||||
import com.daml.lf.speedy.Pretty
|
||||
import com.daml.lf.speedy.{Compiler, InitialSeeding, Pretty, Command => SpeedyCommand}
|
||||
import com.daml.lf.speedy.Speedy.Machine
|
||||
import com.daml.lf.speedy.SResult._
|
||||
import com.daml.lf.transaction.Transaction
|
||||
import com.daml.lf.transaction.{Transaction => Tx}
|
||||
import com.daml.lf.transaction.Node._
|
||||
import com.daml.lf.transaction.Transaction.{NodeId, Transaction}
|
||||
import com.daml.lf.value.Value
|
||||
import com.daml.lf.speedy.{Command => SpeedyCommand}
|
||||
|
||||
/**
|
||||
* Allows for evaluating [[Commands]] and validating [[Transaction]]s.
|
||||
@ -48,8 +47,8 @@ import com.daml.lf.speedy.{Command => SpeedyCommand}
|
||||
* This class is thread safe as long `nextRandomInt` is.
|
||||
*/
|
||||
final class Engine {
|
||||
private[this] val _compiledPackages = ConcurrentCompiledPackages()
|
||||
private[this] val _preprocessor = new preprocessing.Preprocessor(_compiledPackages)
|
||||
private[this] val compiledPackages = ConcurrentCompiledPackages()
|
||||
private[this] val preprocessor = new preprocessing.Preprocessor(compiledPackages)
|
||||
|
||||
/**
|
||||
* Executes commands `cmds` under the authority of `cmds.submitter` and returns one of the following:
|
||||
@ -82,12 +81,12 @@ final class Engine {
|
||||
cmds: Commands,
|
||||
participantId: ParticipantId,
|
||||
submissionSeed: Option[crypto.Hash],
|
||||
): Result[(Transaction.Transaction, Transaction.Metadata)] = {
|
||||
): Result[(Tx.Transaction, Tx.Metadata)] = {
|
||||
val submissionTime = cmds.ledgerEffectiveTime
|
||||
_preprocessor
|
||||
preprocessor
|
||||
.preprocessCommands(cmds.commands)
|
||||
.flatMap { processedCmds =>
|
||||
ShouldCheckSubmitterInMaintainers(_compiledPackages, cmds).flatMap {
|
||||
ShouldCheckSubmitterInMaintainers(compiledPackages, cmds).flatMap {
|
||||
checkSubmitterInMaintainers =>
|
||||
interpretCommands(
|
||||
validating = false,
|
||||
@ -96,10 +95,9 @@ final class Engine {
|
||||
commands = processedCmds,
|
||||
ledgerTime = cmds.ledgerEffectiveTime,
|
||||
submissionTime = submissionTime,
|
||||
transactionSeed = submissionSeed.map(
|
||||
crypto.Hash.deriveTransactionSeed(_, participantId, submissionTime)),
|
||||
seeding = Engine.initialSeeding(submissionSeed, participantId, submissionTime),
|
||||
) map {
|
||||
case (tx, dependsOnTime) =>
|
||||
case (tx, dependsOnTime, nodeSeeds) =>
|
||||
// Annotate the transaction with the package dependencies. Since
|
||||
// all commands are actions on a contract template, with a fully typed
|
||||
// argument, we only need to consider the templates mentioned in the command
|
||||
@ -107,16 +105,18 @@ final class Engine {
|
||||
val deps = processedCmds.foldLeft(Set.empty[PackageId]) { (pkgIds, cmd) =>
|
||||
val pkgId = cmd.templateId.packageId
|
||||
val transitiveDeps =
|
||||
_compiledPackages
|
||||
compiledPackages
|
||||
.getPackageDependencies(pkgId)
|
||||
.getOrElse(
|
||||
sys.error(s"INTERNAL ERROR: Missing dependencies of package $pkgId"))
|
||||
(pkgIds + pkgId) union transitiveDeps
|
||||
}
|
||||
tx -> Transaction.Metadata(
|
||||
submissionTime = submissionTime,
|
||||
usedPackages = deps,
|
||||
dependsOnTime = dependsOnTime,
|
||||
tx -> Tx.Metadata(
|
||||
submissionSeed,
|
||||
submissionTime,
|
||||
deps,
|
||||
dependsOnTime,
|
||||
nodeSeeds,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -124,47 +124,35 @@ final class Engine {
|
||||
}
|
||||
|
||||
/**
|
||||
* Behaves like `submit`, but it takes GenNode arguments instead of a Commands argument.
|
||||
* That is, it can be used to reinterpret an already interpreted transaction (since it consists of GenNodes).
|
||||
* Formally, the following is guaranteed to hold for all pcs, pkgs, and keys, when evaluated on the same Engine:
|
||||
* evaluate(submit(cmds)) = ResultDone(tx) ==> evaluate(reinterpret(cmds.submitters, txRoots, cmds.ledgerEffectiveTime)) === ResultDone(tx)
|
||||
* where:
|
||||
* evaluate(result) = result.consume(pcs, pkgs, keys)
|
||||
* txRoots = tx.roots.map(id => tx.nodes.get(id).get).toSeq
|
||||
* tx === tx' if tx and tx' are equivalent modulo a renaming of node and relative contract IDs
|
||||
* Behaves like `submit`, but it takes a GenNode argument instead of a Commands argument.
|
||||
* That is, it can be used to reinterpret partially an already interpreted transaction (since it consists of GenNodes).
|
||||
*
|
||||
* Moreover, if the transaction tx is valid at time leTime, n belongs to tx.nodes, and subtx is the subtransaction of
|
||||
* tx rooted at n, the following holds:
|
||||
* evaluate(reinterpret(n.requiredAuthorizers, Seq(n), leTime) === subtx
|
||||
*
|
||||
* In addition to the errors returned by `submit`, reinterpretation fails with a `ValidationError` whenever `nodes`
|
||||
* contain a relative contract ID, either as the target contract of a fetch, or as an argument to a
|
||||
* create or an exercise choice.
|
||||
*
|
||||
* [[transactionSeed]] is the master hash te be used to derive node and contractId discriminator.
|
||||
* If let undefined, no discriminator will be generated.
|
||||
* [[nodeSeed]] is the seed of the Create and Exercise node as generated during submission.
|
||||
* If undefined the contract IDs are derive using V0 scheme.
|
||||
* The value of [[nodeSeed]] does not matter for other kind of nodes.
|
||||
*/
|
||||
def reinterpret(
|
||||
submissionTime: Time.Timestamp,
|
||||
transactionSeed: Option[crypto.Hash],
|
||||
submitters: Set[Party],
|
||||
nodes: Seq[GenNode.WithTxValue[Value.NodeId, Value.ContractId]],
|
||||
node: GenNode.WithTxValue[Value.NodeId, Value.ContractId],
|
||||
nodeSeed: Option[crypto.Hash],
|
||||
submissionTime: Time.Timestamp,
|
||||
ledgerEffectiveTime: Time.Timestamp,
|
||||
): Result[(Transaction.Transaction, Boolean)] =
|
||||
): Result[(Tx.Transaction, Boolean, ImmArray[(NodeId, crypto.Hash)])] =
|
||||
for {
|
||||
commands <- Result.sequence(ImmArray(nodes).map(_preprocessor.translateNode))
|
||||
command <- preprocessor.translateNode(node)
|
||||
checkSubmitterInMaintainers <- ShouldCheckSubmitterInMaintainers(
|
||||
_compiledPackages,
|
||||
commands.map(_.templateId))
|
||||
compiledPackages,
|
||||
ImmArray(command.templateId))
|
||||
// reinterpret is never used for submission, only for validation.
|
||||
result <- interpretCommands(
|
||||
validating = true,
|
||||
checkSubmitterInMaintainers = checkSubmitterInMaintainers,
|
||||
submitters = submitters,
|
||||
commands = commands,
|
||||
commands = ImmArray(command),
|
||||
ledgerTime = ledgerEffectiveTime,
|
||||
submissionTime,
|
||||
transactionSeed,
|
||||
submissionTime = submissionTime,
|
||||
seeding = InitialSeeding.RootNodeSeeds(ImmArray(nodeSeed)),
|
||||
)
|
||||
} yield result
|
||||
|
||||
@ -184,7 +172,7 @@ final class Engine {
|
||||
* @param ledgerEffectiveTime time when the transaction is claimed to be submitted
|
||||
*/
|
||||
def validate(
|
||||
tx: Transaction.Transaction,
|
||||
tx: Tx.Transaction,
|
||||
ledgerEffectiveTime: Time.Timestamp,
|
||||
participantId: Ref.ParticipantId,
|
||||
submissionTime: Time.Timestamp,
|
||||
@ -217,9 +205,9 @@ final class Engine {
|
||||
// For empty transactions, use an empty set of submitters
|
||||
submitters = submittersOpt.getOrElse(Set.empty)
|
||||
|
||||
commands <- _preprocessor.translateTransactionRoots(tx)
|
||||
commands <- preprocessor.translateTransactionRoots(tx)
|
||||
checkSubmitterInMaintainers <- ShouldCheckSubmitterInMaintainers(
|
||||
_compiledPackages,
|
||||
compiledPackages,
|
||||
commands.map(_._2.templateId))
|
||||
result <- interpretCommands(
|
||||
validating = true,
|
||||
@ -227,10 +215,10 @@ final class Engine {
|
||||
submitters = submitters,
|
||||
commands = commands.map(_._2),
|
||||
ledgerTime = ledgerEffectiveTime,
|
||||
submissionTime,
|
||||
submissionSeed.map(crypto.Hash.deriveTransactionSeed(_, participantId, submissionTime))
|
||||
submissionTime = submissionTime,
|
||||
seeding = Engine.initialSeeding(submissionSeed, participantId, submissionTime),
|
||||
)
|
||||
(rtx, _) = result
|
||||
(rtx, _, _) = result
|
||||
validationResult <- if (tx isReplayedBy rtx) {
|
||||
ResultDone(())
|
||||
} else {
|
||||
@ -246,8 +234,7 @@ final class Engine {
|
||||
* Submitters are a set, in order to support interpreting subtransactions
|
||||
* (a subtransaction can be authorized by multiple parties).
|
||||
*
|
||||
* [[transactionSeed]] is the master hash used to derive node and contractId discriminator.
|
||||
* If let undefined, no discriminator will be generated.
|
||||
* [[seeding]] is seeding used to derive node seed and contractId discriminator.
|
||||
*/
|
||||
private[engine] def interpretCommands(
|
||||
validating: Boolean,
|
||||
@ -257,15 +244,15 @@ final class Engine {
|
||||
commands: ImmArray[SpeedyCommand],
|
||||
ledgerTime: Time.Timestamp,
|
||||
submissionTime: Time.Timestamp,
|
||||
transactionSeed: Option[crypto.Hash],
|
||||
): Result[(Transaction.Transaction, Boolean)] = {
|
||||
seeding: speedy.InitialSeeding,
|
||||
): Result[(Transaction, Boolean, ImmArray[(Tx.NodeId, crypto.Hash)])] = {
|
||||
val machine = Machine
|
||||
.build(
|
||||
checkSubmitterInMaintainers = checkSubmitterInMaintainers,
|
||||
sexpr = Compiler(compiledPackages.packages).compile(commands),
|
||||
compiledPackages = _compiledPackages,
|
||||
submissionTime,
|
||||
speedy.InitialSeeding(transactionSeed)
|
||||
compiledPackages = compiledPackages,
|
||||
submissionTime = submissionTime,
|
||||
seeds = seeding,
|
||||
)
|
||||
.copy(validating = validating, committers = submitters)
|
||||
interpretLoop(machine, ledgerTime)
|
||||
@ -276,7 +263,7 @@ final class Engine {
|
||||
private[engine] def interpretLoop(
|
||||
machine: Machine,
|
||||
time: Time.Timestamp
|
||||
): Result[(Transaction.Transaction, Boolean)] = {
|
||||
): Result[(Tx.Transaction, Boolean, ImmArray[(Tx.NodeId, crypto.Hash)])] = {
|
||||
while (!machine.isFinal) {
|
||||
machine.step() match {
|
||||
case SResultContinue =>
|
||||
@ -293,9 +280,9 @@ final class Engine {
|
||||
return Result.needPackage(
|
||||
pkgId,
|
||||
pkg => {
|
||||
_compiledPackages.addPackage(pkgId, pkg).flatMap {
|
||||
compiledPackages.addPackage(pkgId, pkg).flatMap {
|
||||
case _ =>
|
||||
callback(_compiledPackages)
|
||||
callback(compiledPackages)
|
||||
interpretLoop(machine, time)
|
||||
}
|
||||
}
|
||||
@ -345,17 +332,17 @@ final class Engine {
|
||||
machine.ptx.finish match {
|
||||
case Left(p) =>
|
||||
ResultError(Error(s"Interpretation error: ended with partial result: $p"))
|
||||
case Right(t) => ResultDone(t -> machine.dependsOnTime)
|
||||
case Right(t) => ResultDone((t, machine.dependsOnTime, machine.ptx.nodeSeeds.toImmArray))
|
||||
}
|
||||
}
|
||||
|
||||
def clearPackages(): Unit = _compiledPackages.clear()
|
||||
def clearPackages(): Unit = compiledPackages.clear()
|
||||
|
||||
/** Note: it's important we return a [[com.daml.lf.CompiledPackages]],
|
||||
* and not a [[ConcurrentCompiledPackages]], otherwise people would be able
|
||||
* to modify them.
|
||||
*/
|
||||
def compiledPackages(): CompiledPackages = _compiledPackages
|
||||
def compiledPackages(): CompiledPackages = compiledPackages
|
||||
|
||||
/** This function can be used to give a package to the engine pre-emptively,
|
||||
* rather than having the engine to ask about it through
|
||||
@ -365,9 +352,22 @@ final class Engine {
|
||||
* be loaded.
|
||||
*/
|
||||
def preloadPackage(pkgId: PackageId, pkg: Package): Result[Unit] =
|
||||
_compiledPackages.addPackage(pkgId, pkg)
|
||||
compiledPackages.addPackage(pkgId, pkg)
|
||||
}
|
||||
|
||||
object Engine {
|
||||
def apply(): Engine = new Engine()
|
||||
|
||||
def initialSeeding(
|
||||
submissionSeed: Option[crypto.Hash],
|
||||
participant: Ref.ParticipantId,
|
||||
submissionTime: Time.Timestamp,
|
||||
): InitialSeeding =
|
||||
submissionSeed match {
|
||||
case None =>
|
||||
InitialSeeding.NoSeed
|
||||
case Some(seed) =>
|
||||
InitialSeeding.TransactionSeed(
|
||||
crypto.Hash.deriveTransactionSeed(seed, participant, submissionTime))
|
||||
}
|
||||
}
|
||||
|
@ -145,17 +145,9 @@ private[engine] final class Preprocessor(compiledPackages: MutableCompiledPackag
|
||||
|
||||
private def getTemplateId(node: Node.GenNode.WithTxValue[Transaction.NodeId, _]) =
|
||||
node match {
|
||||
case Node.NodeCreate(
|
||||
nodeSeed @ _,
|
||||
coid @ _,
|
||||
coinst,
|
||||
optLoc @ _,
|
||||
sigs @ _,
|
||||
stks @ _,
|
||||
key @ _) =>
|
||||
case Node.NodeCreate(coid @ _, coinst, optLoc @ _, sigs @ _, stks @ _, key @ _) =>
|
||||
coinst.template
|
||||
case Node.NodeExercises(
|
||||
nodeSeed @ _,
|
||||
coid @ _,
|
||||
templateId,
|
||||
choice @ _,
|
||||
|
@ -48,20 +48,12 @@ private[preprocessing] final class TransactionPreprocessor(
|
||||
): speedy.Command = {
|
||||
|
||||
node match {
|
||||
case Node.NodeCreate(
|
||||
nodeSeed @ _,
|
||||
coid @ _,
|
||||
coinst,
|
||||
optLoc @ _,
|
||||
sigs @ _,
|
||||
stks @ _,
|
||||
key @ _) =>
|
||||
case Node.NodeCreate(coid @ _, coinst, optLoc @ _, sigs @ _, stks @ _, key @ _) =>
|
||||
val identifier = coinst.template
|
||||
val arg = unsafeAsValueWithAbsoluteContractIds(coinst.arg.value)
|
||||
commandPreprocessor.unsafePreprocessCreate(identifier, arg)
|
||||
|
||||
case Node.NodeExercises(
|
||||
nodeSeed @ _,
|
||||
coid,
|
||||
template,
|
||||
choice,
|
||||
|
@ -17,7 +17,7 @@ import com.daml.lf.transaction.Node._
|
||||
import com.daml.lf.transaction.{GenTransaction => GenTx, Transaction => Tx}
|
||||
import com.daml.lf.value.Value
|
||||
import Value._
|
||||
import com.daml.lf.speedy.{SValue, svalue}
|
||||
import com.daml.lf.speedy.{InitialSeeding, SValue, svalue}
|
||||
import com.daml.lf.speedy.SValue._
|
||||
import com.daml.lf.command._
|
||||
import com.daml.lf.value.ValueVersions.assertAsVersionedValue
|
||||
@ -61,36 +61,6 @@ class EngineTest extends WordSpec with Matchers with EitherValues with BazelRunf
|
||||
private val (basicTestsPkgId, basicTestsPkg, allPackages) = loadPackage(
|
||||
"daml-lf/tests/BasicTests.dar")
|
||||
|
||||
def lookupContract(@deprecated("shut up unused arguments warning", "blah") id: AbsoluteContractId)
|
||||
: Option[ContractInst[Tx.Value[AbsoluteContractId]]] = {
|
||||
Some(
|
||||
ContractInst(
|
||||
TypeConName(basicTestsPkgId, "BasicTests:Simple"),
|
||||
assertAsVersionedValue(
|
||||
ValueRecord(
|
||||
Some(Identifier(basicTestsPkgId, "BasicTests:Simple")),
|
||||
ImmArray((Some[Name]("p"), ValueParty(party))))),
|
||||
""
|
||||
))
|
||||
}
|
||||
|
||||
def lookupContractForPayout(
|
||||
@deprecated("shut up unused arguments warning", "blah") id: AbsoluteContractId)
|
||||
: Option[ContractInst[Tx.Value[AbsoluteContractId]]] = {
|
||||
Some(
|
||||
ContractInst(
|
||||
TypeConName(basicTestsPkgId, "BasicTests:CallablePayout"),
|
||||
assertAsVersionedValue(
|
||||
ValueRecord(
|
||||
Some(Identifier(basicTestsPkgId, "BasicTests:CallablePayout")),
|
||||
ImmArray(
|
||||
(Some("giver"), ValueParty(alice)),
|
||||
(Some("receiver"), ValueParty(bob))
|
||||
))),
|
||||
""
|
||||
))
|
||||
}
|
||||
|
||||
val withKeyTemplate = "BasicTests:WithKey"
|
||||
val BasicTests_WithKey = Identifier(basicTestsPkgId, withKeyTemplate)
|
||||
val withKeyContractInst: ContractInst[Tx.Value[AbsoluteContractId]] =
|
||||
@ -106,11 +76,35 @@ class EngineTest extends WordSpec with Matchers with EitherValues with BazelRunf
|
||||
""
|
||||
)
|
||||
|
||||
def lookupContractWithKey(
|
||||
@deprecated("shut up unused arguments warning", "blah") id: AbsoluteContractId)
|
||||
: Option[ContractInst[Tx.Value[AbsoluteContractId]]] = {
|
||||
Some(withKeyContractInst)
|
||||
}
|
||||
val defaultContracts =
|
||||
Map(
|
||||
toContractId("#BasicTests:Simple:1") ->
|
||||
ContractInst(
|
||||
TypeConName(basicTestsPkgId, "BasicTests:Simple"),
|
||||
assertAsVersionedValue(
|
||||
ValueRecord(
|
||||
Some(Identifier(basicTestsPkgId, "BasicTests:Simple")),
|
||||
ImmArray((Some[Name]("p"), ValueParty(party))))),
|
||||
""
|
||||
),
|
||||
toContractId("#BasicTests:CallablePayout:1") ->
|
||||
ContractInst(
|
||||
TypeConName(basicTestsPkgId, "BasicTests:CallablePayout"),
|
||||
assertAsVersionedValue(
|
||||
ValueRecord(
|
||||
Some(Identifier(basicTestsPkgId, "BasicTests:CallablePayout")),
|
||||
ImmArray(
|
||||
(Some[Ref.Name]("giver"), ValueParty(alice)),
|
||||
(Some[Ref.Name]("receiver"), ValueParty(bob))
|
||||
)
|
||||
)),
|
||||
""
|
||||
),
|
||||
toContractId("#BasicTests:WithKey:1") ->
|
||||
withKeyContractInst
|
||||
)
|
||||
|
||||
val lookupContract = defaultContracts.get(_)
|
||||
|
||||
def lookupPackage(pkgId: PackageId): Option[Package] = {
|
||||
allPackages.get(pkgId)
|
||||
@ -122,7 +116,7 @@ class EngineTest extends WordSpec with Matchers with EitherValues with BazelRunf
|
||||
BasicTests_WithKey,
|
||||
ValueRecord(_, ImmArray((_, ValueParty(`alice`)), (_, ValueInt64(42)))),
|
||||
) =>
|
||||
Some(toContractId("#1"))
|
||||
Some(toContractId("#BasicTests:WithKey:1"))
|
||||
case _ =>
|
||||
None
|
||||
}
|
||||
@ -198,7 +192,7 @@ class EngineTest extends WordSpec with Matchers with EitherValues with BazelRunf
|
||||
}
|
||||
|
||||
"translate exercise commands argument including labels" in {
|
||||
val originalCoid = toContractId("#1")
|
||||
val originalCoid = toContractId("#BasicTests:CallablePayout:1")
|
||||
val templateId = Identifier(basicTestsPkgId, "BasicTests:CallablePayout")
|
||||
val command = ExerciseCommand(
|
||||
templateId,
|
||||
@ -208,12 +202,12 @@ class EngineTest extends WordSpec with Matchers with EitherValues with BazelRunf
|
||||
|
||||
val res = preprocessor
|
||||
.preprocessCommands(ImmArray(command))
|
||||
.consume(lookupContractForPayout, lookupPackage, lookupKey)
|
||||
.consume(lookupContract, lookupPackage, lookupKey)
|
||||
res shouldBe 'right
|
||||
}
|
||||
|
||||
"translate exercise commands argument without labels" in {
|
||||
val originalCoid = toContractId("#1")
|
||||
val originalCoid = toContractId("#BasicTests:CallablePayout:1")
|
||||
val templateId = Identifier(basicTestsPkgId, "BasicTests:CallablePayout")
|
||||
val command = ExerciseCommand(
|
||||
templateId,
|
||||
@ -223,7 +217,7 @@ class EngineTest extends WordSpec with Matchers with EitherValues with BazelRunf
|
||||
|
||||
val res = preprocessor
|
||||
.preprocessCommands(ImmArray(command))
|
||||
.consume(lookupContractForPayout, lookupPackage, lookupKey)
|
||||
.consume(lookupContract, lookupPackage, lookupKey)
|
||||
res shouldBe 'right
|
||||
}
|
||||
|
||||
@ -238,7 +232,7 @@ class EngineTest extends WordSpec with Matchers with EitherValues with BazelRunf
|
||||
|
||||
val res = preprocessor
|
||||
.preprocessCommands(ImmArray(command))
|
||||
.consume(lookupContractForPayout, lookupPackage, lookupKey)
|
||||
.consume(lookupContract, lookupPackage, lookupKey)
|
||||
res shouldBe 'right
|
||||
}
|
||||
|
||||
@ -253,7 +247,7 @@ class EngineTest extends WordSpec with Matchers with EitherValues with BazelRunf
|
||||
|
||||
val res = preprocessor
|
||||
.preprocessCommands(ImmArray(command))
|
||||
.consume(lookupContractForPayout, lookupPackage, lookupKey)
|
||||
.consume(lookupContract, lookupPackage, lookupKey)
|
||||
res shouldBe 'right
|
||||
}
|
||||
|
||||
@ -268,7 +262,7 @@ class EngineTest extends WordSpec with Matchers with EitherValues with BazelRunf
|
||||
|
||||
val res = preprocessor
|
||||
.preprocessCommands(ImmArray(command))
|
||||
.consume(lookupContractForPayout, lookupPackage, lookupKey)
|
||||
.consume(lookupContract, lookupPackage, lookupKey)
|
||||
res.left.value.msg should startWith("Missing record label n for record")
|
||||
}
|
||||
|
||||
@ -283,7 +277,7 @@ class EngineTest extends WordSpec with Matchers with EitherValues with BazelRunf
|
||||
|
||||
val res = preprocessor
|
||||
.preprocessCommands(ImmArray(command))
|
||||
.consume(lookupContractForPayout, lookupPackage, lookupKey)
|
||||
.consume(lookupContract, lookupPackage, lookupKey)
|
||||
res.left.value.msg should startWith(
|
||||
"Impossible to exercise by key, no key is defined for template")
|
||||
}
|
||||
@ -299,7 +293,7 @@ class EngineTest extends WordSpec with Matchers with EitherValues with BazelRunf
|
||||
|
||||
val res = preprocessor
|
||||
.preprocessCommands(ImmArray(command))
|
||||
.consume(lookupContractForPayout, lookupPackage, lookupKey)
|
||||
.consume(lookupContract, lookupPackage, lookupKey)
|
||||
res.left.value.msg should startWith("mismatching type")
|
||||
}
|
||||
|
||||
@ -318,7 +312,7 @@ class EngineTest extends WordSpec with Matchers with EitherValues with BazelRunf
|
||||
|
||||
val res = preprocessor
|
||||
.preprocessCommands(ImmArray(command))
|
||||
.consume(lookupContractForPayout, lookupPackage, lookupKey)
|
||||
.consume(lookupContract, lookupPackage, lookupKey)
|
||||
res shouldBe 'right
|
||||
|
||||
}
|
||||
@ -439,14 +433,19 @@ class EngineTest extends WordSpec with Matchers with EitherValues with BazelRunf
|
||||
|
||||
"reinterpret to the same result" in {
|
||||
val Right((tx, txMeta)) = interpretResult
|
||||
val txRoots = tx.roots.map(id => tx.nodes(id)).toSeq
|
||||
val txSeed =
|
||||
Some(crypto.Hash.deriveTransactionSeed(submissionSeed, participant, txMeta.submissionTime))
|
||||
val txRoots = tx.roots.map(id => tx.nodes(id))
|
||||
val nodeSeedMap = txMeta.nodeSeeds.toSeq.toMap
|
||||
|
||||
val Right((rtx, _)) =
|
||||
engine
|
||||
.reinterpret(txMeta.submissionTime, txSeed, Set(party), txRoots, let)
|
||||
.consume(lookupContract, lookupPackage, lookupKey)
|
||||
val Right((rtx, _, _)) =
|
||||
reinterpret(
|
||||
engine,
|
||||
Set(party),
|
||||
txRoots,
|
||||
tx.roots.map(nodeSeedMap.get),
|
||||
txMeta.submissionTime,
|
||||
let,
|
||||
lookupPackage
|
||||
)
|
||||
(tx isReplayedBy rtx) shouldBe true
|
||||
}
|
||||
|
||||
@ -474,13 +473,10 @@ class EngineTest extends WordSpec with Matchers with EitherValues with BazelRunf
|
||||
val templateId = Identifier(basicTestsPkgId, "BasicTests:Simple")
|
||||
val hello = Identifier(basicTestsPkgId, "BasicTests:Hello")
|
||||
val let = Time.Timestamp.now()
|
||||
val txSeed = crypto.Hash.deriveTransactionSeed(submissionSeed, participant, let)
|
||||
val seeding = Engine.initialSeeding(Some(submissionSeed), participant, let)
|
||||
val cid = toContractId("#BasicTests:Simple:1")
|
||||
val command =
|
||||
ExerciseCommand(
|
||||
templateId,
|
||||
toContractId("#1"),
|
||||
"Hello",
|
||||
ValueRecord(Some(hello), ImmArray.empty))
|
||||
ExerciseCommand(templateId, cid, "Hello", ValueRecord(Some(hello), ImmArray.empty))
|
||||
|
||||
val res = preprocessor
|
||||
.preprocessCommands(ImmArray(command))
|
||||
@ -498,10 +494,10 @@ class EngineTest extends WordSpec with Matchers with EitherValues with BazelRunf
|
||||
commands = r,
|
||||
ledgerTime = let,
|
||||
submissionTime = let,
|
||||
transactionSeed = Some(txSeed)
|
||||
seeding = seeding,
|
||||
)
|
||||
.consume(lookupContract, lookupPackage, lookupKey))
|
||||
val Right((tx, _)) = interpretResult
|
||||
val Right((tx, _, nodeSeeds)) = interpretResult
|
||||
|
||||
"be translated" in {
|
||||
val Right((rtx, _)) = engine
|
||||
@ -511,11 +507,21 @@ class EngineTest extends WordSpec with Matchers with EitherValues with BazelRunf
|
||||
}
|
||||
|
||||
"reinterpret to the same result" in {
|
||||
val txRoots = tx.roots.map(id => tx.nodes(id)).toSeq
|
||||
val nodeSeedMap = HashMap(nodeSeeds.toSeq: _*)
|
||||
val roots = tx.roots.map(tx.nodes)
|
||||
val rootSeeds = tx.roots.map(nodeSeedMap.get)
|
||||
|
||||
val Right((rtx, _)) = engine
|
||||
.reinterpret(let, Some(txSeed), Set(party), txRoots, let)
|
||||
.consume(lookupContract, lookupPackage, lookupKey)
|
||||
val Right((rtx, _, _)) =
|
||||
reinterpret(
|
||||
engine,
|
||||
Set(party),
|
||||
roots,
|
||||
rootSeeds,
|
||||
let,
|
||||
let,
|
||||
lookupPackage,
|
||||
defaultContracts
|
||||
)
|
||||
(tx isReplayedBy rtx) shouldBe true
|
||||
}
|
||||
|
||||
@ -556,13 +562,13 @@ class EngineTest extends WordSpec with Matchers with EitherValues with BazelRunf
|
||||
|
||||
val res = preprocessor
|
||||
.preprocessCommands(ImmArray(command))
|
||||
.consume(lookupContractWithKey, lookupPackage, lookupKey)
|
||||
.consume(lookupContract, lookupPackage, lookupKey)
|
||||
res shouldBe 'right
|
||||
|
||||
"fail at submission" in {
|
||||
val submitResult = engine
|
||||
.submit(Commands(alice, ImmArray(command), let, "test"), participant, Some(submissionSeed))
|
||||
.consume(lookupContractWithKey, lookupPackage, lookupKey)
|
||||
.consume(lookupContract, lookupPackage, lookupKey)
|
||||
submitResult.left.value.msg should startWith("dependency error: couldn't find key")
|
||||
}
|
||||
}
|
||||
@ -571,7 +577,7 @@ class EngineTest extends WordSpec with Matchers with EitherValues with BazelRunf
|
||||
val submissionSeed = hash("exercise-by-key command with existing key")
|
||||
val templateId = Identifier(basicTestsPkgId, "BasicTests:WithKey")
|
||||
val let = Time.Timestamp.now()
|
||||
val txSeed = crypto.Hash.deriveTransactionSeed(submissionSeed, participant, let)
|
||||
val seeding = Engine.initialSeeding(Some(submissionSeed), participant, let)
|
||||
val command = ExerciseByKeyCommand(
|
||||
templateId,
|
||||
ValueRecord(None, ImmArray((None, ValueParty(alice)), (None, ValueInt64(42)))),
|
||||
@ -581,7 +587,7 @@ class EngineTest extends WordSpec with Matchers with EitherValues with BazelRunf
|
||||
|
||||
val res = preprocessor
|
||||
.preprocessCommands(ImmArray(command))
|
||||
.consume(lookupContractWithKey, lookupPackage, lookupKey)
|
||||
.consume(lookupContract, lookupPackage, lookupKey)
|
||||
res shouldBe 'right
|
||||
val result =
|
||||
res
|
||||
@ -595,34 +601,34 @@ class EngineTest extends WordSpec with Matchers with EitherValues with BazelRunf
|
||||
commands = r,
|
||||
ledgerTime = let,
|
||||
submissionTime = let,
|
||||
transactionSeed = Some(txSeed)
|
||||
seeding = seeding,
|
||||
)
|
||||
.consume(lookupContractWithKey, lookupPackage, lookupKey))
|
||||
.map(_._1)
|
||||
val tx = result.right.value
|
||||
.consume(lookupContract, lookupPackage, lookupKey))
|
||||
val Right((tx, _, nodeSeeds)) = result
|
||||
|
||||
"be translated" in {
|
||||
val submitResult = engine
|
||||
.submit(Commands(alice, ImmArray(command), let, "test"), participant, Some(submissionSeed))
|
||||
.consume(lookupContractWithKey, lookupPackage, lookupKey)
|
||||
.consume(lookupContract, lookupPackage, lookupKey)
|
||||
.map(_._1)
|
||||
(result |@| submitResult)(_ isReplayedBy _) shouldBe Right(true)
|
||||
(result.map(_._1) |@| submitResult)(_ isReplayedBy _) shouldBe Right(true)
|
||||
}
|
||||
|
||||
"reinterpret to the same result" in {
|
||||
val txRoots = tx.roots.map(id => tx.nodes(id)).toSeq
|
||||
val roots = tx.roots.map(id => tx.nodes(id))
|
||||
val nodeSeedMap = HashMap(nodeSeeds.toSeq: _*)
|
||||
val rootSeeds = tx.roots.map(nodeSeedMap.get)
|
||||
|
||||
val reinterpretResult =
|
||||
engine
|
||||
.reinterpret(let, Some(txSeed), Set(alice), txRoots, let)
|
||||
.consume(lookupContractWithKey, lookupPackage, lookupKey)
|
||||
reinterpret(engine, Set(alice), roots, rootSeeds, let, let, lookupPackage, defaultContracts)
|
||||
.map(_._1)
|
||||
(result |@| reinterpretResult)(_ isReplayedBy _) shouldBe Right(true)
|
||||
(result.map(_._1) |@| reinterpretResult)(_ isReplayedBy _) shouldBe Right(true)
|
||||
}
|
||||
|
||||
"be validated" in {
|
||||
val validated = engine
|
||||
.validate(tx, let, participant, let, Some(submissionSeed))
|
||||
.consume(lookupContractWithKey, lookupPackage, lookupKey)
|
||||
.consume(lookupContract, lookupPackage, lookupKey)
|
||||
validated match {
|
||||
case Left(e) =>
|
||||
fail(e.msg)
|
||||
@ -673,11 +679,11 @@ class EngineTest extends WordSpec with Matchers with EitherValues with BazelRunf
|
||||
commands = r,
|
||||
ledgerTime = let,
|
||||
submissionTime = let,
|
||||
transactionSeed = Some(txSeed),
|
||||
seeding = InitialSeeding.TransactionSeed(txSeed),
|
||||
)
|
||||
.consume(lookupContract, lookupPackage, lookupKey))
|
||||
.map(_._1)
|
||||
val Right(tx) = interpretResult
|
||||
|
||||
val Right((tx, _, nodeSeeds)) = interpretResult
|
||||
|
||||
"be translated" in {
|
||||
tx.roots should have length 2
|
||||
@ -688,13 +694,14 @@ class EngineTest extends WordSpec with Matchers with EitherValues with BazelRunf
|
||||
}
|
||||
|
||||
"reinterpret to the same result" in {
|
||||
val txRoots = tx.roots.map(id => tx.nodes(id)).toSeq
|
||||
val nodeSeedMap = HashMap(nodeSeeds.toSeq: _*)
|
||||
val roots = tx.roots.map(tx.nodes)
|
||||
val rootSeeds = tx.roots.map(nodeSeedMap.get)
|
||||
|
||||
val reinterpretResult =
|
||||
engine
|
||||
.reinterpret(let, Some(txSeed), Set(party), txRoots, let)
|
||||
.consume(lookupContract, lookupPackage, lookupKey)
|
||||
reinterpret(engine, Set(party), roots, rootSeeds, let, let, lookupPackage)
|
||||
.map(_._1)
|
||||
(interpretResult |@| reinterpretResult)(_ isReplayedBy _) shouldBe Right(true)
|
||||
(interpretResult.map(_._1) |@| reinterpretResult)(_ isReplayedBy _) shouldBe Right(true)
|
||||
}
|
||||
|
||||
"be validated" in {
|
||||
@ -873,7 +880,7 @@ class EngineTest extends WordSpec with Matchers with EitherValues with BazelRunf
|
||||
|
||||
"exercise callable command" should {
|
||||
val submissionSeed = hash("exercise callable command")
|
||||
val originalCoid = toContractId("#1")
|
||||
val originalCoid = toContractId("#BasicTests:CallablePayout:1")
|
||||
val templateId = Identifier(basicTestsPkgId, "BasicTests:CallablePayout")
|
||||
// we need to fix time as cid are depending on it
|
||||
val let = Time.Timestamp.assertFromString("1969-07-20T20:17:00Z")
|
||||
@ -883,18 +890,18 @@ class EngineTest extends WordSpec with Matchers with EitherValues with BazelRunf
|
||||
"Transfer",
|
||||
ValueRecord(None, ImmArray((Some[Name]("newReceiver"), ValueParty(clara)))))
|
||||
|
||||
val Right((tx, meta)) = engine
|
||||
val Right((tx, txMeta)) = engine
|
||||
.submit(Commands(bob, ImmArray(command), let, "test"), participant, Some(submissionSeed))
|
||||
.consume(lookupContractForPayout, lookupPackage, lookupKey)
|
||||
.consume(lookupContract, lookupPackage, lookupKey)
|
||||
|
||||
val submissionTime = meta.submissionTime
|
||||
val submissionTime = txMeta.submissionTime
|
||||
|
||||
val txSeed =
|
||||
crypto.Hash.deriveTransactionSeed(submissionSeed, participant, submissionTime)
|
||||
val Right(cmds) = preprocessor
|
||||
.preprocessCommands(ImmArray(command))
|
||||
.consume(lookupContractForPayout, lookupPackage, lookupKey)
|
||||
val Right((rtx, _)) = engine
|
||||
.consume(lookupContract, lookupPackage, lookupKey)
|
||||
val Right((rtx, _, _)) = engine
|
||||
.interpretCommands(
|
||||
validating = false,
|
||||
checkSubmitterInMaintainers = true,
|
||||
@ -902,9 +909,9 @@ class EngineTest extends WordSpec with Matchers with EitherValues with BazelRunf
|
||||
commands = cmds,
|
||||
ledgerTime = let,
|
||||
submissionTime = submissionTime,
|
||||
transactionSeed = Some(txSeed),
|
||||
seeding = InitialSeeding.TransactionSeed(txSeed)
|
||||
)
|
||||
.consume(lookupContractForPayout, lookupPackage, lookupKey)
|
||||
.consume(lookupContract, lookupPackage, lookupKey)
|
||||
|
||||
"be translated" in {
|
||||
(rtx isReplayedBy tx) shouldBe true
|
||||
@ -914,11 +921,20 @@ class EngineTest extends WordSpec with Matchers with EitherValues with BazelRunf
|
||||
Blinding.checkAuthorizationAndBlind(tx, Set(bob))
|
||||
|
||||
"reinterpret to the same result" in {
|
||||
val txRoots = tx.roots.map(id => tx.nodes(id)).toSeq
|
||||
val Right((rtx, _)) =
|
||||
engine
|
||||
.reinterpret(submissionTime, Some(txSeed), Set(bob), txRoots, let)
|
||||
.consume(lookupContractForPayout, lookupPackage, lookupKey)
|
||||
val roots = tx.roots.map(id => tx.nodes(id))
|
||||
val nodeSeedMap = HashMap(txMeta.nodeSeeds.toSeq: _*)
|
||||
val rootSeeds = tx.roots.map(nodeSeedMap.get)
|
||||
|
||||
val Right((rtx, _, _)) =
|
||||
reinterpret(
|
||||
engine,
|
||||
Set(bob),
|
||||
roots,
|
||||
rootSeeds,
|
||||
submissionTime,
|
||||
let,
|
||||
lookupPackage,
|
||||
defaultContracts)
|
||||
(rtx isReplayedBy tx) shouldBe true
|
||||
}
|
||||
|
||||
@ -929,7 +945,6 @@ class EngineTest extends WordSpec with Matchers with EitherValues with BazelRunf
|
||||
bobView.nodes.size shouldBe 2
|
||||
findNodeByIdx(bobView.nodes, 0).getOrElse(fail("node not found")) match {
|
||||
case NodeExercises(
|
||||
nodeSeed @ _,
|
||||
coid,
|
||||
_,
|
||||
choice,
|
||||
@ -952,7 +967,7 @@ class EngineTest extends WordSpec with Matchers with EitherValues with BazelRunf
|
||||
}
|
||||
|
||||
findNodeByIdx(bobView.nodes, 1).getOrElse(fail("node not found")) match {
|
||||
case NodeCreate(nodeSeed @ _, _, coins, _, _, stakeholders, _) =>
|
||||
case NodeCreate(_, coins, _, _, stakeholders, _) =>
|
||||
coins.template shouldBe templateId
|
||||
stakeholders shouldBe Set(alice, clara)
|
||||
case _ => fail("create event is expected")
|
||||
@ -963,7 +978,7 @@ class EngineTest extends WordSpec with Matchers with EitherValues with BazelRunf
|
||||
|
||||
claraView.nodes.size shouldBe 1
|
||||
findNodeByIdx(claraView.nodes, 1).getOrElse(fail("node not found")) match {
|
||||
case NodeCreate(nodeSeed @ _, _, coins, _, _, stakeholders, _) =>
|
||||
case NodeCreate(_, coins, _, _, stakeholders, _) =>
|
||||
coins.template shouldBe templateId
|
||||
stakeholders shouldBe Set(alice, clara)
|
||||
case _ => fail("create event is expected")
|
||||
@ -976,7 +991,7 @@ class EngineTest extends WordSpec with Matchers with EitherValues with BazelRunf
|
||||
val transactionSeed =
|
||||
crypto.Hash.deriveTransactionSeed(submissionSeed, participant, submissionTime)
|
||||
|
||||
val Right((tx, _)) =
|
||||
val Right((tx, _, _)) =
|
||||
engine
|
||||
.interpretCommands(
|
||||
validating = false,
|
||||
@ -985,9 +1000,9 @@ class EngineTest extends WordSpec with Matchers with EitherValues with BazelRunf
|
||||
commands = cmds,
|
||||
ledgerTime = let,
|
||||
submissionTime = submissionTime,
|
||||
transactionSeed = Some(transactionSeed)
|
||||
seeding = InitialSeeding.TransactionSeed(transactionSeed),
|
||||
)
|
||||
.consume(lookupContractForPayout, lookupPackage, lookupKey)
|
||||
.consume(lookupContract, lookupPackage, lookupKey)
|
||||
val Seq(_, noid1) = tx.nodes.keys.toSeq.sortBy(_.index)
|
||||
val Right(blindingInfo) =
|
||||
Blinding.checkAuthorizationAndBlind(tx, Set(bob))
|
||||
@ -1086,7 +1101,7 @@ class EngineTest extends WordSpec with Matchers with EitherValues with BazelRunf
|
||||
}
|
||||
|
||||
val let = Time.Timestamp.now()
|
||||
val transactionSeed = crypto.Hash.deriveTransactionSeed(submissionSeed, participant, let)
|
||||
val seeding = Engine.initialSeeding(Some(submissionSeed), participant, let)
|
||||
|
||||
def actFetchActors[Nid, Cid, Val](n: GenNode[Nid, Cid, Val]): Set[Party] = {
|
||||
n match {
|
||||
@ -1122,38 +1137,40 @@ class EngineTest extends WordSpec with Matchers with EitherValues with BazelRunf
|
||||
commands = r,
|
||||
ledgerTime = let,
|
||||
submissionTime = let,
|
||||
transactionSeed = Some(transactionSeed),
|
||||
seeding = seeding,
|
||||
)
|
||||
.consume(lookupContract, lookupPackage, lookupKey))
|
||||
.map(_._1)
|
||||
|
||||
}
|
||||
|
||||
"propagate the parent's signatories and actors (but not observers) when stakeholders" in {
|
||||
|
||||
val Right(tx) = runExample(fetcher1Cid, clara)
|
||||
val Right((tx, _, _)) = runExample(fetcher1Cid, clara)
|
||||
txFetchActors(tx) shouldBe Set(alice, clara)
|
||||
}
|
||||
|
||||
"not propagate the parent's signatories nor actors when not stakeholders" in {
|
||||
|
||||
val Right(tx) = runExample(fetcher2Cid, party)
|
||||
val Right((tx, _, _)) = runExample(fetcher2Cid, party)
|
||||
txFetchActors(tx) shouldBe Set()
|
||||
}
|
||||
|
||||
"be retained when reinterpreting single fetch nodes" in {
|
||||
val Right(tx) = runExample(fetcher1Cid, clara)
|
||||
val Right((tx, _, nodeSeeds)) = runExample(fetcher1Cid, clara)
|
||||
val nodeSeedMap = HashMap(nodeSeeds.toSeq: _*)
|
||||
val fetchNodes =
|
||||
tx.fold(Seq[(NodeId, GenNode.WithTxValue[NodeId, ContractId])]()) {
|
||||
case (ns, (nid, n @ NodeFetch(_, _, _, _, _, _, _))) => ns :+ ((nid, n))
|
||||
case (ns, _) => ns
|
||||
}
|
||||
|
||||
fetchNodes.foreach {
|
||||
case (nid, n) =>
|
||||
val fetchTx = GenTx(HashMap(nid -> n), ImmArray(nid))
|
||||
val Right((reinterpreted, _)) = engine
|
||||
.reinterpret(let, None, n.requiredAuthorizers, Seq(n), let)
|
||||
.consume(lookupContract, lookupPackage, lookupKey)
|
||||
val Right((reinterpreted, _, _)) =
|
||||
engine
|
||||
.reinterpret(n.requiredAuthorizers, n, nodeSeedMap.get(nid), let, let)
|
||||
.consume(lookupContract, lookupPackage, lookupKey)
|
||||
(fetchTx isReplayedBy reinterpreted) shouldBe true
|
||||
}
|
||||
}
|
||||
@ -1161,8 +1178,6 @@ class EngineTest extends WordSpec with Matchers with EitherValues with BazelRunf
|
||||
|
||||
"reinterpreting fetch nodes" should {
|
||||
|
||||
val submissionSeed = hash("reinterpreting fetch nodes")
|
||||
|
||||
val fetchedCid = toContractId("#1")
|
||||
val fetchedStrTid = "BasicTests:Fetched"
|
||||
val fetchedTid = Identifier(basicTestsPkgId, fetchedStrTid)
|
||||
@ -1203,11 +1218,11 @@ class EngineTest extends WordSpec with Matchers with EitherValues with BazelRunf
|
||||
)
|
||||
|
||||
val let = Time.Timestamp.now()
|
||||
val txSeed = crypto.Hash.deriveTransactionSeed(submissionSeed, participant, let)
|
||||
|
||||
val reinterpreted = engine
|
||||
.reinterpret(let, Some(txSeed), Set.empty, Seq(fetchNode), let)
|
||||
.consume(lookupContract, lookupPackage, lookupKey)
|
||||
val reinterpreted =
|
||||
engine
|
||||
.reinterpret(Set.empty, fetchNode, None, let, let)
|
||||
.consume(lookupContract, lookupPackage, lookupKey)
|
||||
|
||||
reinterpreted shouldBe 'right
|
||||
}
|
||||
@ -1247,9 +1262,10 @@ class EngineTest extends WordSpec with Matchers with EitherValues with BazelRunf
|
||||
)
|
||||
|
||||
def firstLookupNode[Nid, Cid, Val](
|
||||
tx: GenTx[Nid, Cid, Val]): Option[NodeLookupByKey[Cid, Val]] =
|
||||
tx.nodes.values.collectFirst {
|
||||
case nl @ NodeLookupByKey(_, _, _, _) => nl
|
||||
tx: GenTx[Nid, Cid, Val],
|
||||
): Option[(Nid, NodeLookupByKey[Cid, Val])] =
|
||||
tx.nodes.collectFirst {
|
||||
case (nid, nl @ NodeLookupByKey(_, _, _, _)) => nid -> nl
|
||||
}
|
||||
|
||||
val now = Time.Timestamp.now()
|
||||
@ -1266,25 +1282,23 @@ class EngineTest extends WordSpec with Matchers with EitherValues with BazelRunf
|
||||
participant,
|
||||
Some(submissionSeed))
|
||||
.consume(lookupContractMap.get, lookupPackage, lookupKey)
|
||||
val nodeSeedMap = HashMap(txMeta.nodeSeeds.toSeq: _*)
|
||||
|
||||
val txSeed =
|
||||
crypto.Hash.deriveTransactionSeed(submissionSeed, participant, txMeta.submissionTime)
|
||||
|
||||
val Some(lookupNode) = firstLookupNode(tx)
|
||||
val Some((nid, lookupNode)) = firstLookupNode(tx)
|
||||
lookupNode.result shouldBe Some(lookedUpCid)
|
||||
|
||||
val freshEngine = Engine()
|
||||
val Right((reinterpreted, dependsOnTime @ _)) = freshEngine
|
||||
.reinterpret(
|
||||
txMeta.submissionTime,
|
||||
Some(txSeed),
|
||||
Set.empty,
|
||||
Seq(lookupNode),
|
||||
now,
|
||||
)
|
||||
.consume(lookupContract, lookupPackage, lookupKey)
|
||||
val Right((reinterpreted, _, _)) =
|
||||
Engine()
|
||||
.reinterpret(
|
||||
Set.empty,
|
||||
lookupNode,
|
||||
nodeSeedMap.get(nid),
|
||||
txMeta.submissionTime,
|
||||
now,
|
||||
)
|
||||
.consume(lookupContract, lookupPackage, lookupKey)
|
||||
|
||||
firstLookupNode(reinterpreted) shouldEqual Some(lookupNode)
|
||||
firstLookupNode(reinterpreted).map(_._2) shouldEqual Some(lookupNode)
|
||||
}
|
||||
|
||||
"reinterpret to the same node when lookup doesn't find a contract" in {
|
||||
@ -1299,18 +1313,18 @@ class EngineTest extends WordSpec with Matchers with EitherValues with BazelRunf
|
||||
participant,
|
||||
Some(submissionSeed))
|
||||
.consume(lookupContractMap.get, lookupPackage, lookupKey)
|
||||
val txSeed =
|
||||
crypto.Hash.deriveTransactionSeed(submissionSeed, participant, txMeta.submissionTime)
|
||||
|
||||
val Some(lookupNode) = firstLookupNode(tx)
|
||||
val nodeSeedMap = HashMap(txMeta.nodeSeeds.toSeq: _*)
|
||||
|
||||
val Some((nid, lookupNode)) = firstLookupNode(tx)
|
||||
lookupNode.result shouldBe None
|
||||
|
||||
val freshEngine = Engine()
|
||||
val Right((reinterpreted, dependsOnTime @ _)) = freshEngine
|
||||
.reinterpret(now, Some(txSeed), Set.empty, Seq(lookupNode), now)
|
||||
.consume(lookupContract, lookupPackage, lookupKey)
|
||||
val Right((reinterpreted, _, _)) =
|
||||
Engine()
|
||||
.reinterpret(Set.empty, lookupNode, nodeSeedMap.get(nid), txMeta.submissionTime, now)
|
||||
.consume(lookupContract, lookupPackage, lookupKey)
|
||||
|
||||
firstLookupNode(reinterpreted) shouldEqual Some(lookupNode)
|
||||
firstLookupNode(reinterpreted).map(_._2) shouldEqual Some(lookupNode)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1349,8 +1363,8 @@ class EngineTest extends WordSpec with Matchers with EitherValues with BazelRunf
|
||||
|
||||
val cmd = speedy.Command.Fetch(BasicTests_WithKey, SValue.SContractId(fetchedCid))
|
||||
|
||||
val Right((tx, dependsOnTime @ _)) = engine
|
||||
.interpretCommands(false, false, Set(alice), ImmArray(cmd), now, now, None)
|
||||
val Right((tx, _, _)) = engine
|
||||
.interpretCommands(false, false, Set(alice), ImmArray(cmd), now, now, InitialSeeding.NoSeed)
|
||||
.consume(lookupContractMap.get, lookupPackage, lookupKey)
|
||||
|
||||
tx.nodes.values.headOption match {
|
||||
@ -1403,8 +1417,8 @@ class EngineTest extends WordSpec with Matchers with EitherValues with BazelRunf
|
||||
))
|
||||
.consume(lookupContractMap.get, lookupPackage, lookupKey)
|
||||
|
||||
val Right((tx, dependsOnTime @ _)) = engine
|
||||
.interpretCommands(false, false, Set(alice), cmds, now, now, None)
|
||||
val Right((tx, _, _)) = engine
|
||||
.interpretCommands(false, false, Set(alice), cmds, now, now, InitialSeeding.NoSeed)
|
||||
.consume(lookupContractMap.get, lookupPackage, lookupKey)
|
||||
|
||||
tx.nodes.values.collectFirst {
|
||||
@ -1467,21 +1481,22 @@ class EngineTest extends WordSpec with Matchers with EitherValues with BazelRunf
|
||||
}
|
||||
|
||||
"be partially reinterpretable" in {
|
||||
val Right((tx, metaData)) = run(3)
|
||||
val Right((tx, txMeta)) = run(3)
|
||||
val ImmArray(_, exeNode1) = tx.roots
|
||||
val NodeExercises(Some(exeNode1Seed), _, _, _, _, _, _, _, _, _, _, children, _, _) =
|
||||
val NodeExercises(_, _, _, _, _, _, _, _, _, _, children, _, _) =
|
||||
tx.nodes(exeNode1)
|
||||
val ImmArray(createNode2, exeNode2, _, _) = children
|
||||
val nids = children.toSeq.take(2).toImmArray
|
||||
val nodeSeedMap = HashMap(txMeta.nodeSeeds.toSeq: _*)
|
||||
|
||||
engine
|
||||
.reinterpret(
|
||||
metaData.submissionTime,
|
||||
Some(exeNode1Seed),
|
||||
Set(party),
|
||||
List(createNode2, exeNode2).map(tx.nodes),
|
||||
let,
|
||||
)
|
||||
.consume(_ => None, lookupPackage, _ => None) shouldBe 'right
|
||||
reinterpret(
|
||||
engine,
|
||||
Set(party),
|
||||
nids.map(tx.nodes),
|
||||
nids.map(nodeSeedMap.get),
|
||||
txMeta.submissionTime,
|
||||
let,
|
||||
lookupPackage,
|
||||
) shouldBe 'right
|
||||
|
||||
}
|
||||
}
|
||||
@ -1515,4 +1530,92 @@ object EngineTest {
|
||||
case _ => false
|
||||
}
|
||||
|
||||
private def reinterpret(
|
||||
engine: Engine,
|
||||
submitters: Set[Party],
|
||||
nodes: ImmArray[GenNode.WithTxValue[Value.NodeId, Value.ContractId]],
|
||||
nodeSeeds: ImmArray[Option[crypto.Hash]],
|
||||
submissionTime: Time.Timestamp,
|
||||
ledgerEffectiveTime: Time.Timestamp,
|
||||
lookupPackages: PackageId => Option[Package],
|
||||
contracts: Map[AbsoluteContractId, Tx.ContractInst[AbsoluteContractId]] = Map.empty,
|
||||
): Either[Error, (Tx.Transaction, Boolean, ImmArray[(NodeId, crypto.Hash)])] = {
|
||||
type Acc =
|
||||
(
|
||||
HashMap[NodeId, Tx.Node],
|
||||
BackStack[NodeId],
|
||||
Boolean,
|
||||
BackStack[(NodeId, crypto.Hash)],
|
||||
Map[AbsoluteContractId, Tx.ContractInst[AbsoluteContractId]],
|
||||
Map[GlobalKey, AbsoluteContractId],
|
||||
)
|
||||
|
||||
val iterate =
|
||||
(nodes zip nodeSeeds).foldLeft[Either[Error, Acc]](
|
||||
Right((HashMap.empty, BackStack.empty, false, BackStack.empty, contracts, Map.empty))) {
|
||||
case (acc, (node, nodeSeed)) =>
|
||||
for {
|
||||
previousStep <- acc
|
||||
(nodes, roots, dependOnTime, nodeSeeds, contracts0, keys0) = previousStep
|
||||
currentStep <- engine
|
||||
.reinterpret(submitters, node, nodeSeed, submissionTime, ledgerEffectiveTime)
|
||||
.consume(contracts0.get, lookupPackages, keys0.get)
|
||||
(tr1, dependOnTime1, nodeSeeds1) = currentStep
|
||||
(contracts1, keys1) = tr1.fold((contracts0, keys0)) {
|
||||
case (
|
||||
(contracts, keys),
|
||||
(
|
||||
_,
|
||||
NodeExercises(
|
||||
targetCoid: AbsoluteContractId,
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
true,
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
_))) =>
|
||||
(contracts - targetCoid, keys)
|
||||
case (
|
||||
(contracts, keys),
|
||||
(_, NodeCreate(cid: AbsoluteContractId, coinst, _, _, _, key))) =>
|
||||
(
|
||||
contracts.updated(
|
||||
cid,
|
||||
coinst.assertNoRelCid(cid => s"unexpected relative contract ID $cid")),
|
||||
key.fold(keys)(
|
||||
k =>
|
||||
keys.updated(
|
||||
GlobalKey(
|
||||
coinst.template,
|
||||
k.key.value.assertNoCid(cid => s"unexpected relative contract ID $cid")),
|
||||
cid))
|
||||
)
|
||||
case (acc, _) => acc
|
||||
}
|
||||
n = nodes.size
|
||||
nodeRenaming = (nid: NodeId) => NodeId(nid.index + n)
|
||||
tr = tr1.mapNodeId(nodeRenaming)
|
||||
} yield
|
||||
(
|
||||
nodes ++ tr.nodes,
|
||||
roots :++ tr.roots,
|
||||
dependOnTime || dependOnTime1,
|
||||
nodeSeeds :++ nodeSeeds1.map { case (nid, seed) => nodeRenaming(nid) -> seed },
|
||||
contracts1,
|
||||
keys1,
|
||||
)
|
||||
}
|
||||
|
||||
iterate.map {
|
||||
case (nodes, roots, dependOnTime, nodeSeeds, _, _) =>
|
||||
(GenTx(nodes, roots.toImmArray), dependOnTime, nodeSeeds.toImmArray)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -92,9 +92,9 @@ class LargeTransactionTest extends WordSpec with Matchers with BazelRunfiles {
|
||||
cmdReference = "create RangeOfInts",
|
||||
seed = hash("testLargeTransactionOneContract:create", txSize))
|
||||
val contractId: AbsoluteContractId = firstRootNode(createCmdTx) match {
|
||||
case N.NodeCreate(_, x: RelativeContractId, _, _, _, _, _) =>
|
||||
case N.NodeCreate(x: RelativeContractId, _, _, _, _, _) =>
|
||||
AbsoluteContractId.V0(pcs.toContractIdString(pcs.transactionCounter - 1)(x))
|
||||
case N.NodeCreate(_, x: AbsoluteContractId, _, _, _, _, _) => x
|
||||
case N.NodeCreate(x: AbsoluteContractId, _, _, _, _, _) => x
|
||||
case n @ _ => fail(s"Expected NodeCreate, but got: $n")
|
||||
}
|
||||
val exerciseCmd = toListContainerExerciseCmd(rangeOfIntsTemplateId, contractId)
|
||||
@ -121,9 +121,9 @@ class LargeTransactionTest extends WordSpec with Matchers with BazelRunfiles {
|
||||
cmdReference = "create RangeOfInts",
|
||||
seed = hash("testLargeTransactionManySmallContracts:create", num))
|
||||
val contractId: AbsoluteContractId = firstRootNode(createCmdTx) match {
|
||||
case N.NodeCreate(_, x: RelativeContractId, _, _, _, _, _) =>
|
||||
case N.NodeCreate(x: RelativeContractId, _, _, _, _, _) =>
|
||||
AbsoluteContractId.V0(pcs.toContractIdString(pcs.transactionCounter - 1)(x))
|
||||
case N.NodeCreate(_, x: AbsoluteContractId, _, _, _, _, _) => x
|
||||
case N.NodeCreate(x: AbsoluteContractId, _, _, _, _, _) => x
|
||||
case n @ _ => fail(s"Expected NodeCreate, but got: $n")
|
||||
}
|
||||
val exerciseCmd = toListOfIntContainers(rangeOfIntsTemplateId, contractId)
|
||||
@ -150,9 +150,9 @@ class LargeTransactionTest extends WordSpec with Matchers with BazelRunfiles {
|
||||
cmdReference = "create ListUtil",
|
||||
seed = hash("testLargeChoiceArgument:create", size))
|
||||
val contractId: AbsoluteContractId = firstRootNode(createCmdTx) match {
|
||||
case N.NodeCreate(_, x: RelativeContractId, _, _, _, _, _) =>
|
||||
case N.NodeCreate(x: RelativeContractId, _, _, _, _, _) =>
|
||||
AbsoluteContractId.V0(pcs.toContractIdString(pcs.transactionCounter - 1)(x))
|
||||
case N.NodeCreate(_, x: AbsoluteContractId, _, _, _, _, _) => x
|
||||
case N.NodeCreate(x: AbsoluteContractId, _, _, _, _, _) => x
|
||||
case n @ _ => fail(s"Expected NodeCreate, but got: $n")
|
||||
}
|
||||
val exerciseCmd = sizeExerciseCmd(listUtilTemplateId, contractId)(size)
|
||||
@ -195,7 +195,7 @@ class LargeTransactionTest extends WordSpec with Matchers with BazelRunfiles {
|
||||
}
|
||||
|
||||
newContracts.count {
|
||||
case N.NodeCreate(_, _, _, _, _, _, _) => true
|
||||
case N.NodeCreate(_, _, _, _, _, _) => true
|
||||
case n @ _ => fail(s"Unexpected match: $n")
|
||||
} shouldBe expectedNumberOfContracts
|
||||
}
|
||||
@ -320,7 +320,7 @@ class LargeTransactionTest extends WordSpec with Matchers with BazelRunfiles {
|
||||
}
|
||||
|
||||
createNode match {
|
||||
case N.NodeCreate(_, _, x: ContractInst[_], _, _, _, _) => x
|
||||
case N.NodeCreate(_, x: ContractInst[_], _, _, _, _) => x
|
||||
case n @ _ => fail(s"Unexpected match: $n")
|
||||
}
|
||||
}
|
||||
|
@ -631,4 +631,12 @@ object Speedy {
|
||||
/** Internal exception thrown when a continuation result needs to be returned. */
|
||||
final case class SpeedyHungry(result: SResult) extends RuntimeException with NoStackTrace
|
||||
|
||||
def deriveTransactionSeed(
|
||||
submissionSeed: Option[crypto.Hash],
|
||||
participant: Ref.ParticipantId,
|
||||
submissionTime: Time.Timestamp,
|
||||
): InitialSeeding =
|
||||
InitialSeeding(
|
||||
submissionSeed.map(crypto.Hash.deriveTransactionSeed(_, participant, submissionTime)))
|
||||
|
||||
}
|
||||
|
@ -103,6 +103,7 @@ object PartialTransaction {
|
||||
submissionTime = submissionTime,
|
||||
nextNodeIdx = 0,
|
||||
nodes = HashMap.empty,
|
||||
nodeSeeds = BackStack.empty,
|
||||
consumedBy = Map.empty,
|
||||
context = Context(initialSeeds),
|
||||
aborted = None,
|
||||
@ -146,6 +147,7 @@ case class PartialTransaction(
|
||||
submissionTime: Time.Timestamp,
|
||||
nextNodeIdx: Int,
|
||||
nodes: HashMap[Value.NodeId, Tx.Node],
|
||||
nodeSeeds: BackStack[(Value.NodeId, crypto.Hash)],
|
||||
consumedBy: Map[Value.ContractId, Value.NodeId],
|
||||
context: PartialTransaction.Context,
|
||||
aborted: Option[Tx.TransactionError],
|
||||
@ -285,7 +287,6 @@ case class PartialTransaction(
|
||||
Value.RelativeContractId(Value.NodeId(nextNodeIdx))
|
||||
)(Value.AbsoluteContractId.V1(_))
|
||||
val createNode = Node.NodeCreate(
|
||||
nodeSeed,
|
||||
cid,
|
||||
coinst,
|
||||
optLocation,
|
||||
@ -298,6 +299,7 @@ case class PartialTransaction(
|
||||
nextNodeIdx = nextNodeIdx + 1,
|
||||
context = context.addChild(nid),
|
||||
nodes = nodes.updated(nid, createNode),
|
||||
nodeSeeds = nodeSeed.fold(nodeSeeds)(s => nodeSeeds :+ (nid -> s)),
|
||||
localContracts = localContracts.updated(cid, nid)
|
||||
)
|
||||
|
||||
@ -409,7 +411,6 @@ case class PartialTransaction(
|
||||
context.exeContext match {
|
||||
case Some(ec) =>
|
||||
val exerciseNode = Node.NodeExercises(
|
||||
nodeSeed = ec.parent.nextChildrenSeed,
|
||||
targetCoid = ec.targetId,
|
||||
templateId = ec.templateId,
|
||||
choiceId = ec.choiceId,
|
||||
@ -425,7 +426,12 @@ case class PartialTransaction(
|
||||
key = ec.contractKey,
|
||||
)
|
||||
val nodeId = ec.nodeId
|
||||
copy(context = ec.parent.addChild(nodeId), nodes = nodes.updated(nodeId, exerciseNode))
|
||||
val nodeSeed = ec.parent.nextChildrenSeed
|
||||
copy(
|
||||
context = ec.parent.addChild(nodeId),
|
||||
nodes = nodes.updated(nodeId, exerciseNode),
|
||||
nodeSeeds = nodeSeed.fold(nodeSeeds)(s => nodeSeeds :+ (nodeId -> s)),
|
||||
)
|
||||
case None =>
|
||||
noteAbort(Tx.EndExerciseInRootContext)
|
||||
}
|
||||
@ -461,7 +467,7 @@ case class PartialTransaction(
|
||||
|
||||
}
|
||||
|
||||
sealed abstract class InitialSeeding
|
||||
sealed abstract class InitialSeeding extends Product with Serializable
|
||||
|
||||
object InitialSeeding {
|
||||
def apply(transactionSeed: Option[crypto.Hash]): InitialSeeding =
|
||||
|
@ -243,7 +243,7 @@ object ValueGenerators {
|
||||
signatories <- genNonEmptyParties
|
||||
stakeholders <- genNonEmptyParties
|
||||
key <- Gen.option(keyWithMaintainersGen)
|
||||
} yield NodeCreate(None, coid, coinst, None, signatories, stakeholders, key)
|
||||
} yield NodeCreate(coid, coinst, None, signatories, stakeholders, key)
|
||||
}
|
||||
|
||||
val fetchNodeGen: Gen[NodeFetch.WithTxValue[ContractId]] = {
|
||||
@ -278,7 +278,6 @@ object ValueGenerators {
|
||||
maintainers <- genNonEmptyParties
|
||||
} yield
|
||||
NodeExercises(
|
||||
None,
|
||||
targetCoid,
|
||||
templateId,
|
||||
choiceId,
|
||||
|
@ -60,7 +60,6 @@ object Node {
|
||||
f3: A3 => B3,
|
||||
): GenNode[A1, A2, A3] => GenNode[B1, B2, B3] = {
|
||||
case NodeCreate(
|
||||
nodeSeed,
|
||||
coid,
|
||||
coinst,
|
||||
optLocation,
|
||||
@ -69,7 +68,6 @@ object Node {
|
||||
key,
|
||||
) =>
|
||||
NodeCreate(
|
||||
nodeSeed = nodeSeed,
|
||||
coid = f2(coid),
|
||||
coinst = value.Value.ContractInst.map1(f3)(coinst),
|
||||
optLocation = optLocation,
|
||||
@ -96,7 +94,6 @@ object Node {
|
||||
key = key.map(KeyWithMaintainers.map1(f3)),
|
||||
)
|
||||
case NodeExercises(
|
||||
nodeSeed,
|
||||
targetCoid,
|
||||
templateId,
|
||||
choiceId,
|
||||
@ -112,7 +109,6 @@ object Node {
|
||||
key,
|
||||
) =>
|
||||
NodeExercises(
|
||||
nodeSeed = nodeSeed,
|
||||
targetCoid = f2(targetCoid),
|
||||
templateId = templateId,
|
||||
choiceId = choiceId,
|
||||
@ -149,7 +145,6 @@ object Node {
|
||||
|
||||
/** Denotes the creation of a contract instance. */
|
||||
final case class NodeCreate[+Cid, +Val](
|
||||
nodeSeed: Option[crypto.Hash],
|
||||
coid: Cid,
|
||||
coinst: ContractInst[Val],
|
||||
optLocation: Option[Location], // Optional location of the create expression
|
||||
@ -181,7 +176,6 @@ object Node {
|
||||
* ledgers.
|
||||
*/
|
||||
final case class NodeExercises[+Nid, +Cid, +Val](
|
||||
nodeSeed: Option[crypto.Hash],
|
||||
targetCoid: Cid,
|
||||
templateId: Identifier,
|
||||
choiceId: ChoiceName,
|
||||
@ -210,7 +204,6 @@ object Node {
|
||||
* apply method enforces it.
|
||||
*/
|
||||
def apply[Nid, Cid, Val](
|
||||
nodeSeed: Option[crypto.Hash] = None,
|
||||
targetCoid: Cid,
|
||||
templateId: Identifier,
|
||||
choiceId: ChoiceName,
|
||||
@ -225,7 +218,6 @@ object Node {
|
||||
key: Option[KeyWithMaintainers[Val]],
|
||||
): NodeExercises[Nid, Cid, Val] =
|
||||
NodeExercises(
|
||||
nodeSeed,
|
||||
targetCoid,
|
||||
templateId,
|
||||
choiceId,
|
||||
@ -287,7 +279,7 @@ object Node {
|
||||
): Boolean =
|
||||
ScalazEqual.match2[recorded.type, isReplayedBy.type, Boolean](fallback = false) {
|
||||
case nc: NodeCreate[Cid, Val] => {
|
||||
case NodeCreate(_, coid2, coinst2, optLocation2 @ _, signatories2, stakeholders2, key2) =>
|
||||
case NodeCreate(coid2, coinst2, optLocation2 @ _, signatories2, stakeholders2, key2) =>
|
||||
import nc._
|
||||
// NOTE(JM): Do not compare location annotations as they may differ due to
|
||||
// differing update expression constructed from the root node.
|
||||
@ -313,7 +305,6 @@ object Node {
|
||||
}
|
||||
case ne: NodeExercises[Nothing, Cid, Val] => {
|
||||
case NodeExercises(
|
||||
_,
|
||||
targetCoid2,
|
||||
templateId2,
|
||||
choiceId2,
|
||||
|
@ -207,7 +207,7 @@ final case class GenTransaction[Nid, +Cid, +Val](
|
||||
|
||||
def localContracts[Cid2 >: Cid]: Map[Cid2, Nid] =
|
||||
fold(Map.empty[Cid2, Nid]) {
|
||||
case (acc, (nid, create @ Node.NodeCreate(_, _, _, _, _, _, _))) =>
|
||||
case (acc, (nid, create @ Node.NodeCreate(_, _, _, _, _, _))) =>
|
||||
acc.updated(create.coid, nid)
|
||||
case (acc, _) => acc
|
||||
}
|
||||
@ -217,7 +217,7 @@ final case class GenTransaction[Nid, +Cid, +Val](
|
||||
*/
|
||||
def inputContracts[Cid2 >: Cid]: Set[Cid2] =
|
||||
fold(Set.empty[Cid2]) {
|
||||
case (acc, (_, Node.NodeExercises(_, coid, _, _, _, _, _, _, _, _, _, _, _, _))) =>
|
||||
case (acc, (_, Node.NodeExercises(coid, _, _, _, _, _, _, _, _, _, _, _, _))) =>
|
||||
acc + coid
|
||||
case (acc, (_, Node.NodeFetch(coid, _, _, _, _, _, _))) =>
|
||||
acc + coid
|
||||
@ -392,9 +392,11 @@ object Transaction {
|
||||
* time.
|
||||
*/
|
||||
final case class Metadata(
|
||||
submissionSeed: Option[crypto.Hash],
|
||||
submissionTime: Time.Timestamp,
|
||||
usedPackages: Set[PackageId],
|
||||
dependsOnTime: Boolean
|
||||
dependsOnTime: Boolean,
|
||||
nodeSeeds: ImmArray[(Value.NodeId, crypto.Hash)],
|
||||
)
|
||||
|
||||
type AbsTransaction = GenTransaction.WithTxValue[NodeId, Value.AbsoluteContractId]
|
||||
|
@ -147,7 +147,7 @@ object TransactionCoder {
|
||||
minContractKeyInFetch,
|
||||
}
|
||||
node match {
|
||||
case nc @ NodeCreate(_, _, _, _, _, _, _) =>
|
||||
case nc @ NodeCreate(_, _, _, _, _, _) =>
|
||||
val createBuilder =
|
||||
TransactionOuterClass.NodeCreate
|
||||
.newBuilder()
|
||||
@ -211,7 +211,7 @@ object TransactionCoder {
|
||||
nodeBuilder.setFetch(fetchBuilder).build()
|
||||
}
|
||||
|
||||
case ne @ NodeExercises(_, _, _, _, _, _, _, _, _, _, _, _, _, _) =>
|
||||
case ne @ NodeExercises(_, _, _, _, _, _, _, _, _, _, _, _, _) =>
|
||||
for {
|
||||
argValue <- encodeValue(encodeCid, ne.chosenValue)
|
||||
(vversion, arg) = argValue
|
||||
@ -344,7 +344,7 @@ object TransactionCoder {
|
||||
else if (txVersion precedes minKeyOrLookupByKey)
|
||||
Left(DecodeError(s"$txVersion is too old to support NodeCreate's `key` field"))
|
||||
else decodeKeyWithMaintainers(decodeCid, protoCreate.getKeyWithMaintainers).map(Some(_))
|
||||
} yield (ni, NodeCreate(None, c, ci, None, signatories, stakeholders, key))
|
||||
} yield (ni, NodeCreate(c, ci, None, signatories, stakeholders, key))
|
||||
case NodeTypeCase.FETCH =>
|
||||
val protoFetch = protoNode.getFetch
|
||||
for {
|
||||
@ -434,7 +434,6 @@ object TransactionCoder {
|
||||
(
|
||||
ni,
|
||||
NodeExercises(
|
||||
None,
|
||||
targetCoid = targetCoid,
|
||||
templateId = templateId,
|
||||
choiceId = choiceName,
|
||||
|
@ -304,7 +304,6 @@ class TransactionCoderSpec
|
||||
"do tx with a lot of root nodes" in {
|
||||
val node =
|
||||
Node.NodeCreate[Value.AbsoluteContractId, Value.VersionedValue[Value.AbsoluteContractId]](
|
||||
nodeSeed = None,
|
||||
coid = absCid("#test-cid"),
|
||||
coinst = ContractInst(
|
||||
Identifier(
|
||||
|
@ -170,7 +170,6 @@ object TransactionSpec {
|
||||
hasExerciseResult: Boolean = true,
|
||||
): NodeExercises[V.NodeId, V.ContractId, Value] =
|
||||
NodeExercises(
|
||||
nodeSeed = None,
|
||||
targetCoid = toCid(cid),
|
||||
templateId = Ref.Identifier(
|
||||
Ref.PackageId.assertFromString("-dummyPkg-"),
|
||||
@ -191,7 +190,6 @@ object TransactionSpec {
|
||||
|
||||
def dummyCreateNode(cid: String): NodeCreate[V.ContractId, Value] =
|
||||
NodeCreate(
|
||||
nodeSeed = None,
|
||||
coid = toCid(cid),
|
||||
coinst = V.ContractInst(
|
||||
Ref.Identifier(
|
||||
|
@ -86,13 +86,13 @@ private[kvutils] object InputsAndEffects {
|
||||
GlobalKey(fetch.templateId, forceNoContractIds(keyWithMaintainers.key.value)))
|
||||
}
|
||||
|
||||
case create @ NodeCreate(_, _, _, _, _, _, _) =>
|
||||
case create @ NodeCreate(_, _, _, _, _, _) =>
|
||||
create.key.foreach { keyWithMaintainers =>
|
||||
inputs += globalKeyToStateKey(
|
||||
GlobalKey(create.coinst.template, forceNoContractIds(keyWithMaintainers.key.value)))
|
||||
}
|
||||
|
||||
case exe @ NodeExercises(_, _, _, _, _, _, _, _, _, _, _, _, _, _) =>
|
||||
case exe @ NodeExercises(_, _, _, _, _, _, _, _, _, _, _, _, _) =>
|
||||
addContractInput(exe.targetCoid)
|
||||
|
||||
case lookup @ NodeLookupByKey(_, _, _, _) =>
|
||||
@ -118,7 +118,7 @@ private[kvutils] object InputsAndEffects {
|
||||
node match {
|
||||
case fetch @ NodeFetch(_, _, _, _, _, _, _) =>
|
||||
effects
|
||||
case create @ NodeCreate(_, _, _, _, _, _, _) =>
|
||||
case create @ NodeCreate(_, _, _, _, _, _) =>
|
||||
effects.copy(
|
||||
createdContracts = contractIdToStateKey(create.coid) -> create :: effects.createdContracts,
|
||||
updatedContractKeys = create.key
|
||||
@ -141,7 +141,7 @@ private[kvutils] object InputsAndEffects {
|
||||
)
|
||||
)
|
||||
|
||||
case exe @ NodeExercises(_, _, _, _, _, _, _, _, _, _, _, _, _, _) =>
|
||||
case exe @ NodeExercises(_, _, _, _, _, _, _, _, _, _, _, _, _) =>
|
||||
if (exe.consuming) {
|
||||
effects.copy(
|
||||
consumedContracts = contractIdToStateKey(exe.targetCoid) :: effects.consumedContracts,
|
||||
|
@ -252,13 +252,13 @@ private[kvutils] class ProcessTransactionSubmission(
|
||||
.fold((true, startingKeys)) {
|
||||
case (
|
||||
(allUnique, existingKeys),
|
||||
(_, exe @ Node.NodeExercises(_, _, _, _, _, _, _, _, _, _, _, _, _, _)))
|
||||
(_, exe @ Node.NodeExercises(_, _, _, _, _, _, _, _, _, _, _, _, _)))
|
||||
if exe.key.isDefined && exe.consuming =>
|
||||
val stateKey = Conversions.globalKeyToStateKey(
|
||||
Node.GlobalKey(exe.templateId, Conversions.forceNoContractIds(exe.key.get.key.value)))
|
||||
(allUnique, existingKeys - stateKey)
|
||||
|
||||
case ((allUnique, existingKeys), (_, create @ Node.NodeCreate(_, _, _, _, _, _, _)))
|
||||
case ((allUnique, existingKeys), (_, create @ Node.NodeCreate(_, _, _, _, _, _)))
|
||||
if create.key.isDefined =>
|
||||
val stateKey = Conversions.globalKeyToStateKey(
|
||||
Node.GlobalKey(
|
||||
|
@ -25,7 +25,6 @@ class ProjectionsSpec extends WordSpec with Matchers {
|
||||
|
||||
def makeCreateNode(cid: ContractId, signatories: Set[Party], stakeholders: Set[Party]) =
|
||||
Node.NodeCreate(
|
||||
nodeSeed = None,
|
||||
coid = cid,
|
||||
coinst = ContractInst(
|
||||
Identifier(
|
||||
@ -47,7 +46,6 @@ class ProjectionsSpec extends WordSpec with Matchers {
|
||||
stakeholders: Set[Party],
|
||||
children: ImmArray[NodeId]) =
|
||||
Node.NodeExercises(
|
||||
nodeSeed = None,
|
||||
targetCoid = target,
|
||||
templateId = Identifier(
|
||||
PackageId.assertFromString("some-package"),
|
||||
|
@ -55,7 +55,7 @@ class V5_1__Populate_Event_Data extends BaseJavaMigration {
|
||||
val data = txs.flatMap {
|
||||
case (txId, tx) =>
|
||||
tx.nodes.collect {
|
||||
case (eventId, NodeCreate(nodeSeed @ _, cid, _, _, signatories, stakeholders, _)) =>
|
||||
case (eventId, NodeCreate(cid, _, _, signatories, stakeholders, _)) =>
|
||||
(cid, eventId, signatories, stakeholders -- signatories)
|
||||
}
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ class V10_1__Populate_Event_Data extends BaseJavaMigration {
|
||||
val data = txs.flatMap {
|
||||
case (txId, tx) =>
|
||||
tx.nodes.collect {
|
||||
case (eventId, NodeCreate(nodeSeed @ _, cid, _, _, signatories, stakeholders, _)) =>
|
||||
case (eventId, NodeCreate(cid, _, _, signatories, stakeholders, _)) =>
|
||||
(cid, eventId, signatories, stakeholders -- signatories)
|
||||
}
|
||||
}
|
||||
|
@ -47,7 +47,6 @@ private[dao] trait JdbcLedgerDaoContractsSpec {
|
||||
GenTransaction(
|
||||
HashMap(
|
||||
event1 -> NodeCreate(
|
||||
nodeSeed = None,
|
||||
coid = absCid,
|
||||
coinst = someContractInstance,
|
||||
optLocation = None,
|
||||
|
@ -66,7 +66,6 @@ private[dao] trait JdbcLedgerDaoLedgerEntriesSpec extends LoneElement {
|
||||
GenTransaction(
|
||||
HashMap(
|
||||
event1 -> NodeCreate(
|
||||
nodeSeed = None,
|
||||
coid = absCid,
|
||||
coinst = someContractInstance,
|
||||
optLocation = None,
|
||||
@ -114,7 +113,6 @@ private[dao] trait JdbcLedgerDaoLedgerEntriesSpec extends LoneElement {
|
||||
GenTransaction(
|
||||
HashMap(
|
||||
event1 -> NodeCreate(
|
||||
nodeSeed = None,
|
||||
coid = absCid,
|
||||
coinst = someContractInstance,
|
||||
optLocation = None,
|
||||
|
@ -110,7 +110,6 @@ private[dao] trait JdbcLedgerDaoSuite extends AkkaBeforeAndAfterAll with JdbcLed
|
||||
absCid: AbsoluteContractId,
|
||||
): NodeCreate.WithTxValue[AbsoluteContractId] =
|
||||
NodeCreate(
|
||||
nodeSeed = None,
|
||||
coid = absCid,
|
||||
coinst = someContractInstance,
|
||||
optLocation = None,
|
||||
@ -123,7 +122,6 @@ private[dao] trait JdbcLedgerDaoSuite extends AkkaBeforeAndAfterAll with JdbcLed
|
||||
targetCid: AbsoluteContractId,
|
||||
): NodeExercises.WithTxValue[EventId, AbsoluteContractId] =
|
||||
NodeExercises(
|
||||
nodeSeed = None,
|
||||
targetCoid = targetCid,
|
||||
templateId = someTemplateId,
|
||||
choiceId = Ref.Name.assertFromString("choice"),
|
||||
@ -425,7 +423,6 @@ private[dao] trait JdbcLedgerDaoSuite extends AkkaBeforeAndAfterAll with JdbcLed
|
||||
GenTransaction(
|
||||
HashMap(
|
||||
event(s"transactionId$id", id) -> NodeCreate(
|
||||
nodeSeed = None,
|
||||
coid = AbsoluteContractId.assertFromString(s"#contractId$id"),
|
||||
coinst = someContractInstance,
|
||||
optLocation = None,
|
||||
@ -465,7 +462,6 @@ private[dao] trait JdbcLedgerDaoSuite extends AkkaBeforeAndAfterAll with JdbcLed
|
||||
GenTransaction(
|
||||
HashMap(
|
||||
event(s"transactionId$id", id) -> NodeExercises(
|
||||
nodeSeed = None,
|
||||
targetCoid = AbsoluteContractId.assertFromString(s"#contractId${cid.toLong}"),
|
||||
templateId = someTemplateId,
|
||||
choiceId = Ref.ChoiceName.assertFromString("Archive"),
|
||||
|
@ -81,7 +81,6 @@ class ImplicitPartyAdditionIT
|
||||
"create-signatory",
|
||||
"CmdId1",
|
||||
NodeCreate(
|
||||
nodeSeed = None,
|
||||
coid = Value.AbsoluteContractId.assertFromString("#cId1"),
|
||||
coinst = Value.ContractInst(
|
||||
templateId1,
|
||||
@ -99,7 +98,6 @@ class ImplicitPartyAdditionIT
|
||||
"exercise-signatory",
|
||||
"CmdId2",
|
||||
NodeExercises(
|
||||
nodeSeed = None,
|
||||
targetCoid = Value.AbsoluteContractId.assertFromString("#cId1"),
|
||||
templateId = templateId1,
|
||||
choiceId = Ref.ChoiceName.assertFromString("choice"),
|
||||
|
Loading…
Reference in New Issue
Block a user