mirror of
https://github.com/digital-asset/daml.git
synced 2024-11-10 00:35:25 +03:00
Benchtool: Non-stakeholder informees generation [DPP-978] (#13808)
We obtain non-empty non-stakeholder informees by performing an immediate divulgence of instances of Foo1, Foo2 or Foo3 templates through instances of a helper Divulger template. The divulgence is controlled by 1) configuring the number of all divulgees to generated and 2) probabilistically picking a non-empty subset from all divulgees for each contract (similar to how observers are picked for each contract with certain probabilities). If the divulgees for a contract are non-empty a Foo1, Foo2 or Foo3 contract is created via a helper choice on a Divulger contract. Otherwise a standard create Foo1, Foo2 or Foo3 command is issued. Each Divulger contract can divulge any number of Foo1, Foo2 or Foo3 contracts but it's limited to divulging to a fixed set of divulgees. In other words, there is a separate Divulger contract for each non-empty subset of all divulgees. We expect the set of all divulgees to be very small in practice similar to how only a small number observers (currently at most 3) is currently configured for existing benchmarks. Thus, the set of all subsets is also small. To be conservative, we allow the number of divulgees to be at most 5. changelog_begin changelog_end
This commit is contained in:
parent
3794012e11
commit
724a0c1b1a
@ -33,7 +33,7 @@ object ConfigEnricher {
|
||||
submissionResult match {
|
||||
case None => party
|
||||
case Some(summary) =>
|
||||
summary.observers
|
||||
summary.allocatedParties
|
||||
.map(_.unwrap)
|
||||
.find(_.contains(party))
|
||||
.getOrElse(throw new RuntimeException(s"Observer not found: $party"))
|
||||
|
@ -83,7 +83,10 @@ object LedgerApiBenchTool {
|
||||
case class SubmissionStepResult(
|
||||
signatory: Primitive.Party,
|
||||
observers: List[Primitive.Party],
|
||||
)
|
||||
divulgees: List[Primitive.Party],
|
||||
) {
|
||||
val allocatedParties: List[Primitive.Party] = List(signatory) ++ observers ++ divulgees
|
||||
}
|
||||
|
||||
class LedgerApiBenchTool(
|
||||
names: Names,
|
||||
@ -229,6 +232,8 @@ class LedgerApiBenchTool(
|
||||
signatory = signatory,
|
||||
config = submissionConfig,
|
||||
allObservers = List.empty,
|
||||
allDivulgees = List.empty,
|
||||
divulgeesToDivulgerKeyMap = Map.empty,
|
||||
)
|
||||
for {
|
||||
metricsManager <- MetricsManager(
|
||||
@ -248,7 +253,7 @@ class LedgerApiBenchTool(
|
||||
.generateAndSubmit(
|
||||
generator = generator,
|
||||
config = submissionConfig,
|
||||
signatory = signatory,
|
||||
actAs = List(signatory),
|
||||
maxInFlightCommands = config.maxInFlightCommands,
|
||||
submissionBatchSize = config.submissionBatchSize,
|
||||
)
|
||||
@ -290,7 +295,7 @@ class LedgerApiBenchTool(
|
||||
metricsManager = NoOpMetricsManager(),
|
||||
)
|
||||
for {
|
||||
(signatory, allObservers) <- submitter.prepare(
|
||||
(signatory, allObservers, allDivulgees) <- submitter.prepare(
|
||||
submissionConfig
|
||||
)
|
||||
_ <-
|
||||
@ -303,6 +308,7 @@ class LedgerApiBenchTool(
|
||||
submissionConfig = submissionConfig,
|
||||
signatory = signatory,
|
||||
allObservers = allObservers,
|
||||
allDivulgees = allDivulgees,
|
||||
).performSubmission()
|
||||
case submissionConfig: FibonacciSubmissionConfig =>
|
||||
val generator: CommandGenerator = new FibonacciCommandGenerator(
|
||||
@ -314,7 +320,7 @@ class LedgerApiBenchTool(
|
||||
.generateAndSubmit(
|
||||
generator = generator,
|
||||
config = submissionConfig,
|
||||
signatory = signatory,
|
||||
actAs = List(signatory) ++ allDivulgees,
|
||||
maxInFlightCommands = config.maxInFlightCommands,
|
||||
submissionBatchSize = config.submissionBatchSize,
|
||||
)
|
||||
@ -323,6 +329,7 @@ class LedgerApiBenchTool(
|
||||
} yield SubmissionStepResult(
|
||||
signatory = signatory,
|
||||
observers = allObservers,
|
||||
divulgees = allDivulgees,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@ object WorkflowConfig {
|
||||
sealed trait SubmissionConfig extends Product with Serializable {
|
||||
def numberOfInstances: Int
|
||||
def numberOfObservers: Int
|
||||
def numberOfDivulgees: Int
|
||||
def uniqueParties: Boolean
|
||||
}
|
||||
|
||||
@ -28,11 +29,13 @@ object WorkflowConfig {
|
||||
value: Int,
|
||||
) extends SubmissionConfig {
|
||||
override val numberOfObservers = 0
|
||||
override val numberOfDivulgees = 0
|
||||
}
|
||||
|
||||
final case class FooSubmissionConfig(
|
||||
numberOfInstances: Int,
|
||||
numberOfObservers: Int,
|
||||
numberOfDivulgees: Int,
|
||||
uniqueParties: Boolean,
|
||||
instanceDistribution: List[WorkflowConfig.FooSubmissionConfig.ContractDescription],
|
||||
nonConsumingExercises: Option[NonconsumingExercises],
|
||||
|
@ -117,9 +117,10 @@ object WorkflowConfigParser {
|
||||
)(FooSubmissionConfig.ConsumingExercises.apply)
|
||||
|
||||
implicit val fooSubmissionConfigDecoder: Decoder[FooSubmissionConfig] =
|
||||
Decoder.forProduct6(
|
||||
Decoder.forProduct7(
|
||||
"num_instances",
|
||||
"num_observers",
|
||||
"num_divulgees",
|
||||
"unique_parties",
|
||||
"instance_distribution",
|
||||
"nonconsuming_exercises",
|
||||
|
@ -40,20 +40,24 @@ case class CommandSubmitter(
|
||||
(
|
||||
client.binding.Primitive.Party,
|
||||
List[client.binding.Primitive.Party],
|
||||
List[client.binding.Primitive.Party],
|
||||
)
|
||||
] = {
|
||||
val observerPartyNames =
|
||||
names.observerPartyNames(config.numberOfObservers, config.uniqueParties)
|
||||
val divulgeePartyNames =
|
||||
names.divulgeePartyNames(config.numberOfDivulgees, config.uniqueParties)
|
||||
|
||||
logger.info("Generating contracts...")
|
||||
logger.info(s"Identifier suffix: ${names.identifierSuffix}")
|
||||
(for {
|
||||
signatory <- allocateSignatoryParty()
|
||||
observers <- allocateObserverParties(observerPartyNames)
|
||||
observers <- allocateParties(observerPartyNames)
|
||||
divulgees <- allocateParties(divulgeePartyNames)
|
||||
_ <- uploadTestDars()
|
||||
} yield {
|
||||
logger.info("Prepared command submission.")
|
||||
(signatory, observers)
|
||||
(signatory, observers, divulgees)
|
||||
})
|
||||
.recoverWith { case NonFatal(ex) =>
|
||||
logger.error(
|
||||
@ -64,10 +68,24 @@ case class CommandSubmitter(
|
||||
}
|
||||
}
|
||||
|
||||
def submitSingleBatch(
|
||||
commandId: String,
|
||||
actAs: Seq[Primitive.Party],
|
||||
commands: Seq[Command],
|
||||
)(implicit
|
||||
ec: ExecutionContext
|
||||
): Future[Unit] = {
|
||||
submitAndWait(
|
||||
id = commandId,
|
||||
actAs = actAs,
|
||||
commands = commands,
|
||||
)
|
||||
}
|
||||
|
||||
def generateAndSubmit(
|
||||
generator: CommandGenerator,
|
||||
config: SubmissionConfig,
|
||||
signatory: client.binding.Primitive.Party,
|
||||
actAs: List[client.binding.Primitive.Party],
|
||||
maxInFlightCommands: Int,
|
||||
submissionBatchSize: Int,
|
||||
)(implicit ec: ExecutionContext): Future[Unit] = {
|
||||
@ -76,9 +94,9 @@ case class CommandSubmitter(
|
||||
_ <- submitCommands(
|
||||
generator = generator,
|
||||
config = config,
|
||||
signatory = signatory,
|
||||
maxInFlightCommands = maxInFlightCommands,
|
||||
submissionBatchSize = submissionBatchSize,
|
||||
actAs = actAs,
|
||||
)
|
||||
} yield {
|
||||
logger.info("Commands submitted successfully.")
|
||||
@ -93,11 +111,11 @@ case class CommandSubmitter(
|
||||
private def allocateSignatoryParty()(implicit ec: ExecutionContext): Future[Primitive.Party] =
|
||||
adminServices.partyManagementService.allocateParty(names.signatoryPartyName)
|
||||
|
||||
private def allocateObserverParties(observerPartyNames: Seq[String])(implicit
|
||||
private def allocateParties(divulgeePartyNames: Seq[String])(implicit
|
||||
ec: ExecutionContext
|
||||
): Future[List[Primitive.Party]] = {
|
||||
Future.sequence(
|
||||
observerPartyNames.toList.map(adminServices.partyManagementService.allocateParty)
|
||||
divulgeePartyNames.toList.map(adminServices.partyManagementService.allocateParty)
|
||||
)
|
||||
}
|
||||
|
||||
@ -145,7 +163,7 @@ case class CommandSubmitter(
|
||||
private def submitCommands(
|
||||
generator: CommandGenerator,
|
||||
config: SubmissionConfig,
|
||||
signatory: Primitive.Party,
|
||||
actAs: List[Primitive.Party],
|
||||
maxInFlightCommands: Int,
|
||||
submissionBatchSize: Int,
|
||||
)(implicit
|
||||
@ -188,7 +206,7 @@ case class CommandSubmitter(
|
||||
timed(submitAndWaitTimer, metricsManager)(
|
||||
submitAndWait(
|
||||
id = names.commandId(index),
|
||||
actAs = Seq(signatory),
|
||||
actAs = actAs,
|
||||
commands = commands.flatten,
|
||||
)
|
||||
)
|
||||
|
@ -17,11 +17,15 @@ import com.daml.ledger.client.binding
|
||||
import scala.util.control.NonFatal
|
||||
import scala.util.{Failure, Try}
|
||||
|
||||
/** @param divulgeesToDivulgerKeyMap map whose keys are sorted divulgees lists
|
||||
*/
|
||||
final class FooCommandGenerator(
|
||||
randomnessProvider: RandomnessProvider,
|
||||
config: FooSubmissionConfig,
|
||||
signatory: Primitive.Party,
|
||||
allObservers: List[Primitive.Party],
|
||||
allDivulgees: List[Primitive.Party],
|
||||
divulgeesToDivulgerKeyMap: Map[Set[Primitive.Party], Value],
|
||||
) extends CommandGenerator {
|
||||
private val distribution = new Distribution(config.instanceDistribution.map(_.weight))
|
||||
private val descriptionMapping: Map[Int, FooSubmissionConfig.ContractDescription] =
|
||||
@ -30,6 +34,10 @@ final class FooCommandGenerator(
|
||||
.toMap
|
||||
private val observersWithUnlikelihood: List[(Primitive.Party, Int)] =
|
||||
allObservers.zipWithIndex.toMap.view.mapValues(unlikelihood).toList
|
||||
private val divulgeesWithUnlikelihood: List[(Primitive.Party, Int)] =
|
||||
allDivulgees.zipWithIndex.toMap.view.mapValues(unlikelihood).toList.sortBy { case (party, _) =>
|
||||
party.toString
|
||||
}
|
||||
|
||||
/** @return denominator of a 1/(10**i) likelihood
|
||||
*/
|
||||
@ -37,14 +45,16 @@ final class FooCommandGenerator(
|
||||
|
||||
def next(): Try[Seq[Command]] =
|
||||
(for {
|
||||
(description, observers) <- Try(
|
||||
(pickDescription(), pickObservers())
|
||||
(description, observers, divulgees) <- Try(
|
||||
(pickDescription(), pickObservers(), pickDivulgees())
|
||||
)
|
||||
createContractPayload <- Try(randomPayload(description.payloadSizeBytes))
|
||||
command = createCommands(
|
||||
templateDescriptor = FooTemplateDescriptor.forName(description.template),
|
||||
signatory = signatory,
|
||||
observers = observers,
|
||||
divulgerContractKeyO =
|
||||
if (divulgees.isEmpty) None else divulgeesToDivulgerKeyMap.get(divulgees),
|
||||
payload = createContractPayload,
|
||||
)
|
||||
} yield command).recoverWith { case NonFatal(ex) =>
|
||||
@ -64,6 +74,11 @@ final class FooCommandGenerator(
|
||||
.filter { case (_, unlikelihood) => randomDraw(unlikelihood) }
|
||||
.map(_._1)
|
||||
|
||||
private def pickDivulgees(): Set[Primitive.Party] =
|
||||
divulgeesWithUnlikelihood.view.collect {
|
||||
case (party, unlikelihood) if randomDraw(unlikelihood) => party
|
||||
}.toSet
|
||||
|
||||
private def randomDraw(unlikelihood: Int): Boolean =
|
||||
randomnessProvider.randomNatural(unlikelihood) == 0
|
||||
|
||||
@ -71,15 +86,27 @@ final class FooCommandGenerator(
|
||||
templateDescriptor: FooTemplateDescriptor,
|
||||
signatory: Primitive.Party,
|
||||
observers: List[Primitive.Party],
|
||||
divulgerContractKeyO: Option[Value],
|
||||
payload: String,
|
||||
): Seq[Command] = {
|
||||
val contractNumber = FooCommandGenerator.nextContractNumber.getAndIncrement()
|
||||
val fooKeyId = "foo-" + contractNumber
|
||||
val contractCounter = FooCommandGenerator.nextContractNumber.getAndIncrement()
|
||||
val fooKeyId = "foo-" + contractCounter
|
||||
val fooContractKey = FooCommandGenerator.makeContractKeyValue(signatory, fooKeyId)
|
||||
val createFooCmd = templateDescriptor.name match {
|
||||
case "Foo1" => Foo1(signatory, observers, payload, keyId = fooKeyId).create.command
|
||||
case "Foo2" => Foo2(signatory, observers, payload, keyId = fooKeyId).create.command
|
||||
case "Foo3" => Foo3(signatory, observers, payload, keyId = fooKeyId).create.command
|
||||
val createFooCmd = divulgerContractKeyO match {
|
||||
case Some(divulgerContractKey) =>
|
||||
makeCreateAndDivulgeFooCommand(
|
||||
divulgerContractKey = divulgerContractKey,
|
||||
payload = payload,
|
||||
fooKeyId = fooKeyId,
|
||||
observers = observers,
|
||||
templateName = templateDescriptor.name,
|
||||
)
|
||||
case None =>
|
||||
templateDescriptor.name match {
|
||||
case "Foo1" => Foo1(signatory, observers, payload, keyId = fooKeyId).create.command
|
||||
case "Foo2" => Foo2(signatory, observers, payload, keyId = fooKeyId).create.command
|
||||
case "Foo3" => Foo3(signatory, observers, payload, keyId = fooKeyId).create.command
|
||||
}
|
||||
}
|
||||
val nonconsumingExercisePayloads: Seq[String] =
|
||||
config.nonConsumingExercises.fold(Seq.empty[String]) { config =>
|
||||
@ -123,6 +150,45 @@ final class FooCommandGenerator(
|
||||
Seq(createFooCmd) ++ nonconsumingExercises ++ consumingExerciseO.toList
|
||||
}
|
||||
|
||||
private def makeCreateAndDivulgeFooCommand(
|
||||
divulgerContractKey: Value,
|
||||
payload: String,
|
||||
fooKeyId: String,
|
||||
observers: List[Primitive.Party],
|
||||
templateName: String,
|
||||
) = {
|
||||
makeExerciseByKeyCommand(
|
||||
templateId = FooTemplateDescriptor.Divulger_templateId,
|
||||
choiceName = FooTemplateDescriptor.Divulger_DivulgeImmediate,
|
||||
args = Seq(
|
||||
RecordField(
|
||||
label = "fooObservers",
|
||||
value = Some(
|
||||
Value(
|
||||
Value.Sum.List(
|
||||
com.daml.ledger.api.v1.value.List(
|
||||
observers.map(obs => Value(Value.Sum.Party(obs.toString)))
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
),
|
||||
RecordField(
|
||||
label = "fooPayload",
|
||||
value = Some(Value(Value.Sum.Text(payload))),
|
||||
),
|
||||
RecordField(
|
||||
label = "fooKeyId",
|
||||
value = Some(Value(Value.Sum.Text(fooKeyId))),
|
||||
),
|
||||
RecordField(
|
||||
label = "fooTemplateName",
|
||||
value = Some(Value(Value.Sum.Text(templateName))),
|
||||
),
|
||||
),
|
||||
)(contractKey = divulgerContractKey)
|
||||
}
|
||||
|
||||
def makeExerciseByKeyCommand(templateId: Identifier, choiceName: String, args: Seq[RecordField])(
|
||||
contractKey: Value
|
||||
): Command = {
|
||||
|
@ -0,0 +1,67 @@
|
||||
// Copyright (c) 2022 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.ledger.api.benchtool.submission
|
||||
|
||||
import com.daml.ledger.api.v1.commands.Command
|
||||
import com.daml.ledger.api.v1.value.Value
|
||||
import com.daml.ledger.client.binding.Primitive
|
||||
import com.daml.ledger.test.model.Foo.Divulger
|
||||
|
||||
object FooDivulgerCommandGenerator {
|
||||
|
||||
/** Builds a create Divulger command for each non-empty subset of divulgees
|
||||
* such that the created Divulger contract can be used to divulge (by immediate divulgence) Foo1, Foo2 or Foo3 contracts
|
||||
* to the corresponding subset of divulgees.
|
||||
*
|
||||
* @param allDivulgees - Small number of divulgees. At most 5.
|
||||
* @return A tuple of:
|
||||
* - a sequence of create Divulger commands,
|
||||
* - a map from sets of divulgees (all non-empty subsets of all divulgees) to corresponding contract keys,
|
||||
*/
|
||||
def makeCreateDivulgerCommands(
|
||||
divulgingParty: Primitive.Party,
|
||||
allDivulgees: List[Primitive.Party],
|
||||
): (List[Command], Map[Set[Primitive.Party], Value]) = {
|
||||
require(
|
||||
allDivulgees.size <= 5,
|
||||
s"Number of divulgee parties must be at most 5, was: ${allDivulgees.size}.",
|
||||
)
|
||||
def allNonEmptySubsets(divulgees: List[Primitive.Party]): List[List[Primitive.Party]] = {
|
||||
def iter(remaining: List[Primitive.Party]): List[List[Primitive.Party]] = {
|
||||
remaining match {
|
||||
case Nil => List(List.empty)
|
||||
case head :: tail =>
|
||||
val sub: List[List[Primitive.Party]] = iter(tail)
|
||||
val sub2: List[List[Primitive.Party]] = sub.map(xs => xs.prepended(head))
|
||||
sub ::: sub2
|
||||
}
|
||||
}
|
||||
import scalaz.syntax.tag._
|
||||
iter(divulgees)
|
||||
.collect {
|
||||
case parties if parties.nonEmpty => parties.sortBy(_.unwrap)
|
||||
}
|
||||
}
|
||||
|
||||
def createDivulgerFor(divulgees: List[Primitive.Party]): (Command, Value) = {
|
||||
val keyId = "divulger-" + FooCommandGenerator.nextContractNumber.getAndIncrement()
|
||||
val createDivulgerCmd = Divulger(
|
||||
divulgees = divulgees,
|
||||
divulger = divulgingParty,
|
||||
keyId = keyId,
|
||||
).create.command
|
||||
val divulgerKey: Value = FooCommandGenerator.makeContractKeyValue(divulgingParty, keyId)
|
||||
(createDivulgerCmd, divulgerKey)
|
||||
}
|
||||
|
||||
val allSubsets = allNonEmptySubsets(allDivulgees)
|
||||
val (commands, keys, divulgeeSets) = allSubsets.map { divulgees: List[Primitive.Party] =>
|
||||
val (cmd, key) = createDivulgerFor(divulgees)
|
||||
(cmd, key, divulgees.toSet)
|
||||
}.unzip3
|
||||
val divulgeesToContractKeysMap = divulgeeSets.zip(keys).toMap
|
||||
(commands, divulgeesToContractKeysMap)
|
||||
}
|
||||
|
||||
}
|
@ -8,8 +8,6 @@ import com.daml.ledger.client.binding
|
||||
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
|
||||
/** Generates and submits Foo and related commands
|
||||
*/
|
||||
class FooSubmission(
|
||||
submitter: CommandSubmitter,
|
||||
maxInFlightCommands: Int,
|
||||
@ -17,27 +15,49 @@ class FooSubmission(
|
||||
submissionConfig: FooSubmissionConfig,
|
||||
signatory: binding.Primitive.Party,
|
||||
allObservers: List[binding.Primitive.Party],
|
||||
allDivulgees: List[binding.Primitive.Party],
|
||||
) {
|
||||
|
||||
def performSubmission()(implicit
|
||||
ec: ExecutionContext
|
||||
): Future[Unit] = {
|
||||
val generator = new FooCommandGenerator(
|
||||
randomnessProvider = RandomnessProvider.Default,
|
||||
signatory = signatory,
|
||||
config = submissionConfig,
|
||||
allObservers = allObservers,
|
||||
)
|
||||
val (divulgerCmds, divulgeesToDivulgerKeyMap) = FooDivulgerCommandGenerator
|
||||
.makeCreateDivulgerCommands(
|
||||
divulgingParty = signatory,
|
||||
allDivulgees = allDivulgees,
|
||||
)
|
||||
|
||||
for {
|
||||
_ <-
|
||||
if (divulgerCmds.nonEmpty) {
|
||||
require(
|
||||
divulgeesToDivulgerKeyMap.nonEmpty,
|
||||
"Map from divulgees to Divulger contract keys must be non empty.",
|
||||
)
|
||||
submitter.submitSingleBatch(
|
||||
commandId = "divulgence-setup",
|
||||
actAs = Seq(signatory) ++ allDivulgees,
|
||||
commands = divulgerCmds,
|
||||
)
|
||||
} else {
|
||||
Future.unit
|
||||
}
|
||||
generator: CommandGenerator = new FooCommandGenerator(
|
||||
randomnessProvider = RandomnessProvider.Default,
|
||||
signatory = signatory,
|
||||
config = submissionConfig,
|
||||
allObservers = allObservers,
|
||||
allDivulgees = allDivulgees,
|
||||
divulgeesToDivulgerKeyMap = divulgeesToDivulgerKeyMap,
|
||||
)
|
||||
_ <- submitter
|
||||
.generateAndSubmit(
|
||||
generator = generator,
|
||||
config = submissionConfig,
|
||||
signatory = signatory,
|
||||
actAs = List(signatory) ++ allDivulgees,
|
||||
maxInFlightCommands = maxInFlightCommands,
|
||||
submissionBatchSize = submissionBatchSize,
|
||||
)
|
||||
} yield ()
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -41,4 +41,7 @@ object FooTemplateDescriptor {
|
||||
def forName(templateName: String): FooTemplateDescriptor =
|
||||
all.getOrElse(templateName, sys.error(s"Invalid template: $templateName"))
|
||||
|
||||
val Divulger_templateId: Identifier =
|
||||
com.daml.ledger.test.model.Foo.Divulger.id.asInstanceOf[Identifier]
|
||||
val Divulger_DivulgeImmediate = "DivulgeImmediate"
|
||||
}
|
||||
|
@ -17,9 +17,16 @@ class Names {
|
||||
if (uniqueParties) s"Obs-$index-$identifierSuffix"
|
||||
else s"Obs-$index"
|
||||
|
||||
def divulgeePartyName(index: Int, uniqueParties: Boolean): String =
|
||||
if (uniqueParties) s"Div-$index-$identifierSuffix"
|
||||
else s"Div-$index"
|
||||
|
||||
def observerPartyNames(numberOfObservers: Int, uniqueParties: Boolean): Seq[String] =
|
||||
(0 until numberOfObservers).map(i => observerPartyName(i, uniqueParties))
|
||||
|
||||
def divulgeePartyNames(numberOfDivulgees: Int, uniqueParties: Boolean): Seq[String] =
|
||||
(0 until numberOfDivulgees).map(i => divulgeePartyName(i, uniqueParties))
|
||||
|
||||
def commandId(index: Int): String = s"command-$index-$identifierSuffix"
|
||||
|
||||
def darId(index: Int) = s"submission-dars-$index-$identifierSuffix"
|
||||
|
@ -23,6 +23,7 @@ class WorkflowConfigParserSpec extends AnyWordSpec with Matchers {
|
||||
| type: foo
|
||||
| num_instances: 500
|
||||
| num_observers: 4
|
||||
| num_divulgees: 5
|
||||
| unique_parties: true
|
||||
| instance_distribution:
|
||||
| - template: Foo1
|
||||
@ -52,6 +53,7 @@ class WorkflowConfigParserSpec extends AnyWordSpec with Matchers {
|
||||
WorkflowConfig.FooSubmissionConfig(
|
||||
numberOfInstances = 500,
|
||||
numberOfObservers = 4,
|
||||
numberOfDivulgees = 5,
|
||||
uniqueParties = true,
|
||||
instanceDistribution = List(
|
||||
WorkflowConfig.FooSubmissionConfig.ContractDescription(
|
||||
@ -101,6 +103,7 @@ class WorkflowConfigParserSpec extends AnyWordSpec with Matchers {
|
||||
| type: foo
|
||||
| num_instances: 500
|
||||
| num_observers: 4
|
||||
| num_divulgees: 5
|
||||
| unique_parties: true
|
||||
| instance_distribution:
|
||||
| - template: Foo1
|
||||
@ -119,6 +122,7 @@ class WorkflowConfigParserSpec extends AnyWordSpec with Matchers {
|
||||
WorkflowConfig.FooSubmissionConfig(
|
||||
numberOfInstances = 500,
|
||||
numberOfObservers = 4,
|
||||
numberOfDivulgees = 5,
|
||||
uniqueParties = true,
|
||||
instanceDistribution = List(
|
||||
WorkflowConfig.FooSubmissionConfig.ContractDescription(
|
||||
|
@ -42,7 +42,8 @@ class FibonacciCommandSubmitterITSpec
|
||||
metricRegistry = new MetricRegistry,
|
||||
metricsManager = NoOpMetricsManager(),
|
||||
)
|
||||
(signatory, _) <- tested.prepare(config)
|
||||
(signatory, _, divulgees) <- tested.prepare(config)
|
||||
_ = divulgees shouldBe empty
|
||||
generator = new FibonacciCommandGenerator(
|
||||
signatory = signatory,
|
||||
config = config,
|
||||
@ -50,7 +51,7 @@ class FibonacciCommandSubmitterITSpec
|
||||
_ <- tested.generateAndSubmit(
|
||||
generator = generator,
|
||||
config = config,
|
||||
signatory = signatory,
|
||||
actAs = List(signatory) ++ divulgees,
|
||||
maxInFlightCommands = 1,
|
||||
submissionBatchSize = 5,
|
||||
)
|
||||
|
@ -49,6 +49,7 @@ class FooCommandSubmitterITSpec
|
||||
val config = WorkflowConfig.FooSubmissionConfig(
|
||||
numberOfInstances = 10,
|
||||
numberOfObservers = 1,
|
||||
numberOfDivulgees = 0,
|
||||
uniqueParties = false,
|
||||
instanceDistribution = List(
|
||||
foo1Config,
|
||||
@ -64,27 +65,25 @@ class FooCommandSubmitterITSpec
|
||||
authorizationHelper = None,
|
||||
)
|
||||
apiServices = ledgerApiServicesF("someUser")
|
||||
tested = CommandSubmitter(
|
||||
submitter = CommandSubmitter(
|
||||
names = new Names(),
|
||||
benchtoolUserServices = apiServices,
|
||||
adminServices = apiServices,
|
||||
metricRegistry = new MetricRegistry,
|
||||
metricsManager = NoOpMetricsManager(),
|
||||
)
|
||||
(signatory, observers) <- tested.prepare(config)
|
||||
generator: CommandGenerator = new FooCommandGenerator(
|
||||
randomnessProvider = RandomnessProvider.Default,
|
||||
signatory = signatory,
|
||||
config = config,
|
||||
allObservers = observers,
|
||||
)
|
||||
_ <- tested.generateAndSubmit(
|
||||
generator = generator,
|
||||
config = config,
|
||||
signatory = signatory,
|
||||
(signatory, observers, divulgees) <- submitter.prepare(config)
|
||||
_ = divulgees shouldBe empty
|
||||
tested = new FooSubmission(
|
||||
submitter = submitter,
|
||||
maxInFlightCommands = 1,
|
||||
submissionBatchSize = 5,
|
||||
submissionConfig = config,
|
||||
signatory = signatory,
|
||||
allObservers = observers,
|
||||
allDivulgees = divulgees,
|
||||
)
|
||||
_ <- tested.performSubmission()
|
||||
eventsObserver = TreeEventsObserver(expectedTemplateNames = Set("Foo1", "Foo2"))
|
||||
_ <- apiServices.transactionService.transactionTrees(
|
||||
config = WorkflowConfig.StreamConfig.TransactionTreesStreamConfig(
|
||||
|
@ -0,0 +1,177 @@
|
||||
// Copyright (c) 2022 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.ledger.api.benchtool.submission
|
||||
|
||||
import com.codahale.metrics.MetricRegistry
|
||||
import com.daml.ledger.api.benchtool.config.WorkflowConfig
|
||||
import com.daml.ledger.api.benchtool.metrics.MetricsManager.NoOpMetricsManager
|
||||
import com.daml.ledger.api.benchtool.services.LedgerApiServices
|
||||
import com.daml.ledger.api.testing.utils.SuiteResourceManagementAroundAll
|
||||
import com.daml.ledger.api.v1.ledger_offset.LedgerOffset
|
||||
import com.daml.ledger.client.binding
|
||||
import com.daml.platform.sandbox.fixture.SandboxFixture
|
||||
import org.scalatest.{AppendedClues, OptionValues}
|
||||
import org.scalatest.flatspec.AsyncFlatSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
|
||||
import scala.concurrent.Future
|
||||
|
||||
class NonStakeholderInformeesITSpec
|
||||
extends AsyncFlatSpec
|
||||
with SandboxFixture
|
||||
with SuiteResourceManagementAroundAll
|
||||
with Matchers
|
||||
with AppendedClues
|
||||
with OptionValues {
|
||||
|
||||
it should "divulge events" in {
|
||||
val expectedTemplateNames = Set("Foo1", "Divulger")
|
||||
val submissionConfig = WorkflowConfig.FooSubmissionConfig(
|
||||
numberOfInstances = 100,
|
||||
numberOfObservers = 1,
|
||||
numberOfDivulgees = 3,
|
||||
uniqueParties = false,
|
||||
instanceDistribution = List(
|
||||
WorkflowConfig.FooSubmissionConfig.ContractDescription(
|
||||
template = "Foo1",
|
||||
weight = 1,
|
||||
payloadSizeBytes = 0,
|
||||
)
|
||||
),
|
||||
nonConsumingExercises = None,
|
||||
consumingExercises = None,
|
||||
)
|
||||
for {
|
||||
ledgerApiServicesF <- LedgerApiServices.forChannel(
|
||||
channel = channel,
|
||||
authorizationHelper = None,
|
||||
)
|
||||
apiServices: LedgerApiServices = ledgerApiServicesF("someUser")
|
||||
submitter = CommandSubmitter(
|
||||
names = new Names(),
|
||||
benchtoolUserServices = apiServices,
|
||||
adminServices = apiServices,
|
||||
metricRegistry = new MetricRegistry,
|
||||
metricsManager = NoOpMetricsManager(),
|
||||
)
|
||||
(signatory, observers, divulgees) <- submitter.prepare(submissionConfig)
|
||||
tested = new FooSubmission(
|
||||
submitter = submitter,
|
||||
maxInFlightCommands = 1,
|
||||
submissionBatchSize = 5,
|
||||
submissionConfig = submissionConfig,
|
||||
signatory = signatory,
|
||||
allObservers = observers,
|
||||
allDivulgees = divulgees,
|
||||
)
|
||||
_ <- tested.performSubmission()
|
||||
(treeResults_divulgee0, flatResults_divulgee0) <- observeAllTemplatesForParty(
|
||||
party = divulgees(0),
|
||||
apiServices = apiServices,
|
||||
expectedTemplateNames = expectedTemplateNames,
|
||||
)
|
||||
(treeResults_divulgee1, flatResults_divulgee1) <- observeAllTemplatesForParty(
|
||||
party = divulgees(1),
|
||||
apiServices = apiServices,
|
||||
expectedTemplateNames = expectedTemplateNames,
|
||||
)
|
||||
(treeResults_observer0, flatResults_observer0) <- observeAllTemplatesForParty(
|
||||
party = observers(0),
|
||||
apiServices = apiServices,
|
||||
expectedTemplateNames = expectedTemplateNames,
|
||||
)
|
||||
(treeResults_signatory, _) <- observeAllTemplatesForParty(
|
||||
party = signatory,
|
||||
apiServices = apiServices,
|
||||
expectedTemplateNames = expectedTemplateNames,
|
||||
)
|
||||
} yield {
|
||||
// Creates of Foo1 are divulged to "divulgee" party,
|
||||
// thus, they are visible on transaction trees stream but absent from flat transactions stream.
|
||||
{
|
||||
// Divulge0
|
||||
val treeFoo1 = treeResults_divulgee0.numberOfCreatesPerTemplateName("Foo1")
|
||||
val flatFoo1 = flatResults_divulgee0.numberOfCreatesPerTemplateName("Foo1")
|
||||
treeFoo1 shouldBe 100 withClue ("number of Foo1 contracts visible to divulgee0 on tree transactions stream")
|
||||
flatFoo1 shouldBe 0 withClue ("number of Foo1 contracts visible to divulgee0 on flat transactions stream")
|
||||
val divulger = treeResults_divulgee0.numberOfCreatesPerTemplateName("Divulger")
|
||||
// For 3 divulgees in total (a, b, c) there are 4 subsets that contain 'a': a, ab, ac, abc.
|
||||
divulger shouldBe 4 withClue ("number divulger contracts visible to divulgee0")
|
||||
}
|
||||
{
|
||||
// Divulgee1
|
||||
val treeFoo1 = treeResults_divulgee1.numberOfCreatesPerTemplateName("Foo1")
|
||||
val flatFoo1 = flatResults_divulgee1.numberOfCreatesPerTemplateName("Foo1")
|
||||
// This assertion will fail once in ~37k test executions
|
||||
// because for 100 instances and 10% chance of divulging to divulgee1, divulgee1 won't be disclosed any contracts once in 1/(0.9**100) ~= 37649
|
||||
treeFoo1 should be > 0
|
||||
flatFoo1 shouldBe 0
|
||||
|
||||
val divulger = treeResults_divulgee1.numberOfCreatesPerTemplateName("Divulger")
|
||||
divulger shouldBe 4
|
||||
}
|
||||
{
|
||||
// Observer0
|
||||
val treeFoo1 = flatResults_observer0.numberOfCreatesPerTemplateName("Foo1")
|
||||
val flatFoo1 = treeResults_observer0.numberOfCreatesPerTemplateName("Foo1")
|
||||
flatFoo1 shouldBe 100
|
||||
flatFoo1 shouldBe treeFoo1
|
||||
|
||||
val divulger = treeResults_observer0.numberOfCreatesPerTemplateName("Divulger")
|
||||
divulger shouldBe 0
|
||||
}
|
||||
{
|
||||
val divulger = treeResults_signatory.numberOfCreatesPerTemplateName("Divulger")
|
||||
divulger shouldBe 7
|
||||
}
|
||||
succeed
|
||||
}
|
||||
}
|
||||
|
||||
private def observeAllTemplatesForParty(
|
||||
party: binding.Primitive.Party,
|
||||
apiServices: LedgerApiServices,
|
||||
expectedTemplateNames: Set[String],
|
||||
): Future[(ObservedEvents, ObservedEvents)] = {
|
||||
val treeTxObserver = TreeEventsObserver(expectedTemplateNames = expectedTemplateNames)
|
||||
val flatTxObserver = FlatEventsObserver(expectedTemplateNames = expectedTemplateNames)
|
||||
for {
|
||||
_ <- apiServices.transactionService.transactionTrees(
|
||||
config = WorkflowConfig.StreamConfig.TransactionTreesStreamConfig(
|
||||
name = "dummy-name",
|
||||
filters = List(
|
||||
WorkflowConfig.StreamConfig.PartyFilter(
|
||||
party = party.toString,
|
||||
templates = List.empty,
|
||||
)
|
||||
),
|
||||
beginOffset = None,
|
||||
endOffset = Some(LedgerOffset().withBoundary(LedgerOffset.LedgerBoundary.LEDGER_END)),
|
||||
objectives = None,
|
||||
),
|
||||
observer = treeTxObserver,
|
||||
)
|
||||
_ <- apiServices.transactionService.transactions(
|
||||
config = WorkflowConfig.StreamConfig.TransactionsStreamConfig(
|
||||
name = "dummy-name",
|
||||
filters = List(
|
||||
WorkflowConfig.StreamConfig.PartyFilter(
|
||||
party = party.toString,
|
||||
templates = List.empty,
|
||||
)
|
||||
),
|
||||
beginOffset = None,
|
||||
endOffset = Some(LedgerOffset().withBoundary(LedgerOffset.LedgerBoundary.LEDGER_END)),
|
||||
objectives = None,
|
||||
),
|
||||
observer = flatTxObserver,
|
||||
)
|
||||
treeResults: ObservedEvents <- treeTxObserver.result
|
||||
flatResults: ObservedEvents <- flatTxObserver.result
|
||||
} yield {
|
||||
(treeResults, flatResults)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -3,6 +3,50 @@
|
||||
|
||||
module Foo where
|
||||
|
||||
import DA.Functor (void)
|
||||
|
||||
|
||||
template Divulger
|
||||
with
|
||||
divulgees: [Party] -- Parties to whom something is divulged
|
||||
divulger: Party -- Party who divulges something
|
||||
keyId: Text
|
||||
where
|
||||
signatory [divulger] <> divulgees
|
||||
|
||||
key (divulger, keyId): (Party, Text)
|
||||
maintainer key._1
|
||||
|
||||
nonconsuming choice DivulgeImmediate: ()
|
||||
with
|
||||
fooObservers : [Party]
|
||||
fooPayload : Text
|
||||
fooKeyId: Text
|
||||
fooTemplateName: Text
|
||||
controller divulger
|
||||
do
|
||||
-- Party 'divulgee' sees the creation of Foo even though she is not a stakeholder i.e. immediate divulgence occurs.
|
||||
if fooTemplateName == "Foo1" then
|
||||
void $ create Foo1 with
|
||||
signatory = divulger
|
||||
observers = fooObservers
|
||||
payload = fooPayload
|
||||
keyId = fooKeyId
|
||||
else if fooTemplateName == "Foo2" then
|
||||
void $ create Foo2 with
|
||||
signatory = divulger
|
||||
observers = fooObservers
|
||||
payload = fooPayload
|
||||
keyId = fooKeyId
|
||||
else if fooTemplateName == "Foo3" then
|
||||
void $ create Foo3 with
|
||||
signatory = divulger
|
||||
observers = fooObservers
|
||||
payload = fooPayload
|
||||
keyId = fooKeyId
|
||||
else
|
||||
return ()
|
||||
|
||||
template Foo1
|
||||
with
|
||||
signatory : Party
|
||||
|
Loading…
Reference in New Issue
Block a user