kvutils: Extract a SimplePackage class from TestHelpers. [KVL-759] (#8203)

* kvutils: Simplify KVTest a little.

* kvutils: Split out a SimplePackage manager from TestHelpers.

In an effort to clean up the TestHelpers and associated calls, I have
extracted a class that takes the additional contract data type _once_.
It also provides a useful place to move associated methods and values.

CHANGELOG_BEGIN
CHANGELOG_END
This commit is contained in:
Samir Talwar 2020-12-08 17:08:30 +01:00 committed by GitHub
parent f7465f0646
commit c588b5cc34
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 431 additions and 353 deletions

View File

@ -12,10 +12,10 @@ import com.daml.ledger.participant.state.kvutils.KeyValueCommitting.PreExecution
import com.daml.ledger.participant.state.v1._
import com.daml.lf.command.{Command, Commands}
import com.daml.lf.crypto
import com.daml.lf.crypto.Hash
import com.daml.lf.data.Time.Timestamp
import com.daml.lf.data.{ImmArray, Ref}
import com.daml.lf.engine.Engine
import com.daml.lf.language.Ast
import com.daml.lf.transaction.Transaction
import com.daml.metrics.Metrics
import scalaz.State
@ -30,7 +30,12 @@ final case class KVTestState(
recordTime: Timestamp,
defaultConfig: Configuration,
nextEntryId: Int,
damlState: Map[DamlStateKey, DamlStateValue]) {}
engine: Engine,
keyValueSubmission: KeyValueSubmission,
keyValueCommitting: KeyValueCommitting,
uploadedPackages: Map[Ref.PackageId, Ast.Package],
damlState: Map[DamlStateKey, DamlStateValue],
)
object KVTest {
@ -39,38 +44,53 @@ object KVTest {
type KVTest[A] = State[KVTestState, A]
def initialTestState: KVTestState =
private[this] val MinMaxRecordTimeDelta: Duration = Duration.ofSeconds(1)
private[this] val DefaultAdditionalContractDataType: String = "Party"
private[this] val DefaultSimplePackage: SimplePackage = new SimplePackage(
DefaultAdditionalContractDataType)
private[kvutils] val metrics = new Metrics(new MetricRegistry)
private def initialTestState: KVTestState = {
val engine = Engine.DevEngine()
KVTestState(
participantId = mkParticipantId(0),
recordTime = Timestamp.Epoch.addMicros(1000000),
defaultConfig = theDefaultConfig,
nextEntryId = 0,
damlState = Map.empty
engine = engine,
keyValueSubmission = new KeyValueSubmission(metrics),
keyValueCommitting = new KeyValueCommitting(engine, metrics),
uploadedPackages = Map.empty,
damlState = Map.empty,
)
}
def runTest[A](test: KVTest[A]): A =
test.eval(initialTestState)
def runTestWithPackage[A](additionalContractDataTy: String, parties: Party*)(test: KVTest[A]): A =
def runTestWithPackage[A](simplePackage: SimplePackage, parties: Party*)(test: KVTest[A]): A =
(for {
_ <- uploadArchive(additionalContractDataTy)
_ <- uploadArchive(simplePackage)
_ <- parties.toList.traverse(p => allocateParty(p, p))
r <- test
} yield r).eval(initialTestState)
def runTestWithSimplePackage[A](parties: Party*)(test: KVTest[A]): A =
runTestWithPackage(DefaultAdditionalContractDataTy, parties: _*)(test)
def runTestWithSimplePackage[A](parties: Party*)(test: SimplePackage => KVTest[A]): A =
runTestWithPackage(DefaultSimplePackage, parties: _*)(test(DefaultSimplePackage))
def uploadArchive(additionalContractDataTy: String): KVTest[Unit] =
private def uploadArchive(simplePackage: SimplePackage): KVTest[Unit] =
for {
archiveLogEntry <- submitArchives(
"simple-archive-submission",
archiveWithContractData(additionalContractDataTy)).map(_._2)
simplePackage.archive,
).map(_._2)
_ = assert(archiveLogEntry.getPayloadCase == DamlLogEntry.PayloadCase.PACKAGE_UPLOAD_ENTRY)
_ <- modify[KVTestState](state =>
state.copy(
uploadedPackages = state.uploadedPackages + (simplePackage.packageId -> simplePackage.damlPackageWithContractData)))
} yield ()
def uploadSimpleArchive: KVTest[Unit] = uploadArchive(DefaultAdditionalContractDataTy)
def freshEntryId: KVTest.KVTest[DamlLogEntryId] =
for {
s <- get
@ -137,16 +157,15 @@ object KVTest {
def runCommand(
submitter: Party,
submissionSeed: crypto.Hash,
additionalContractDataTy: String,
cmds: Command*,
command: Command,
): KVTest[(SubmittedTransaction, Transaction.Metadata)] =
for {
s <- get[KVTestState]
(tx, meta) = engine
(tx, meta) = s.engine
.submit(
cmds = Commands(
submitters = Set(submitter),
commands = ImmArray(cmds),
commands = ImmArray(command),
ledgerEffectiveTime = s.recordTime,
commandsReference = "cmds-ref",
),
@ -154,17 +173,17 @@ object KVTest {
submissionSeed = submissionSeed,
)
.consume(
{ coid =>
pcs = contractId =>
s.damlState
.get(Conversions.contractIdToStateKey(coid))
.get(Conversions.contractIdToStateKey(contractId))
.map { v =>
Conversions.decodeContractInstance(v.getContractState.getContractInstance)
}
}, { _ =>
Some(decodedPackageWithContractData(additionalContractDataTy))
}, { _ =>
sys.error("no keys")
}
},
packages = s.uploadedPackages.get,
keys = globalKey =>
s.damlState
.get(Conversions.globalKeyToStateKey(globalKey.globalKey))
.map(value => Conversions.decodeContractId(value.getContractKeyState.getContractId)),
)
.getOrElse(sys.error("Engine.submit fail"))
} yield tx -> meta
@ -172,9 +191,9 @@ object KVTest {
def runSimpleCommand(
submitter: Party,
submissionSeed: crypto.Hash,
cmds: Command*,
command: Command,
): KVTest[(SubmittedTransaction, Transaction.Metadata)] =
runCommand(submitter, submissionSeed, DefaultAdditionalContractDataTy, cmds: _*)
runCommand(submitter, submissionSeed, command)
def submitTransaction(
submitter: Party,
@ -182,20 +201,16 @@ object KVTest {
submissionSeed: crypto.Hash,
letDelta: Duration = Duration.ZERO,
commandId: CommandId = randomLedgerString,
deduplicationTime: Duration = Duration.ofDays(1)): KVTest[(DamlLogEntryId, DamlLogEntry)] =
for {
testState <- get[KVTestState]
submissionInfo = createSubmitterInfo(submitter, commandId, deduplicationTime, testState)
(tx, txMetaData) = transaction
submission = transactionToSubmission(
submissionSeed,
letDelta,
testState,
submissionInfo,
tx,
txMetaData)
result <- submit(submission)
} yield result
deduplicationTime: Duration = Duration.ofDays(1),
): KVTest[(DamlLogEntryId, DamlLogEntry)] =
prepareTransactionSubmission(
submitter,
transaction,
submissionSeed,
letDelta,
commandId,
deduplicationTime,
).flatMap(submit)
def preExecuteTransaction(
submitter: Party,
@ -203,21 +218,49 @@ object KVTest {
submissionSeed: crypto.Hash,
letDelta: Duration = Duration.ZERO,
commandId: CommandId = randomLedgerString,
deduplicationTime: Duration = Duration.ofDays(1))
: KVTest[(DamlLogEntryId, PreExecutionResult)] =
deduplicationTime: Duration = Duration.ofDays(1),
): KVTest[(DamlLogEntryId, PreExecutionResult)] =
prepareTransactionSubmission(
submitter,
transaction,
submissionSeed,
letDelta,
commandId,
deduplicationTime,
).flatMap(preExecute)
def prepareTransactionSubmission(
submitter: Party,
transaction: (SubmittedTransaction, Transaction.Metadata),
submissionSeed: crypto.Hash,
letDelta: Duration,
commandId: CommandId,
deduplicationTime: Duration,
): KVTest[DamlSubmission] = {
val (tx, txMetaData) = transaction
for {
testState <- get[KVTestState]
submitterInfo = createSubmitterInfo(submitter, commandId, deduplicationTime, testState)
(tx, txMetaData) = transaction
submission = transactionToSubmission(
submissionSeed,
letDelta,
testState,
submitterInfo,
tx,
txMetaData)
result <- preExecute(submission)
} yield result
submitterInfo = createSubmitterInfo(
submitter,
commandId,
deduplicationTime,
testState.recordTime,
)
} yield
testState.keyValueSubmission.transactionToSubmission(
submitterInfo = submitterInfo,
meta = TransactionMeta(
ledgerEffectiveTime = testState.recordTime.addMicros(letDelta.toNanos / 1000),
workflowId = None,
submissionTime = txMetaData.submissionTime,
submissionSeed = submissionSeed,
optUsedPackages = Some(txMetaData.usedPackages),
optNodeSeeds = None,
optByKeyNodes = None,
),
tx = tx
)
}
def submitConfig(
configModify: Configuration => Configuration,
@ -258,18 +301,21 @@ object KVTest {
def submitPartyAllocation(
subId: String,
hint: String,
participantId: ParticipantId): KVTest[DamlLogEntry] =
submit(
createPartySubmission(subId, hint, participantId)
).map(_._2)
participantId: ParticipantId,
): KVTest[DamlLogEntry] =
get[KVTestState]
.flatMap(testState => submit(createPartySubmission(subId, hint, participantId, testState)))
.map(_._2)
def preExecutePartyAllocation(
subId: String,
hint: String,
participantId: ParticipantId): KVTest[PreExecutionResult] =
preExecute(
createPartySubmission(subId, hint, participantId)
).map(_._2)
participantId: ParticipantId,
): KVTest[PreExecutionResult] =
get[KVTestState]
.flatMap(testState =>
preExecute(createPartySubmission(subId, hint, participantId, testState)))
.map(_._2)
def allocateParty(subId: String, hint: String): KVTest[Party] =
for {
@ -284,7 +330,7 @@ object KVTest {
for {
testState <- get[KVTestState]
entryId <- freshEntryId
(logEntry, newState) = keyValueCommitting.processSubmission(
(logEntry, newState) = testState.keyValueCommitting.processSubmission(
entryId = entryId,
recordTime = testState.recordTime,
defaultConfig = testState.defaultConfig,
@ -307,24 +353,19 @@ object KVTest {
}
private def preExecute(
damlSubmission: DamlSubmission): KVTest[(DamlLogEntryId, PreExecutionResult)] =
damlSubmission: DamlSubmission,
): KVTest[(DamlLogEntryId, PreExecutionResult)] =
for {
testState <- get[KVTestState]
entryId <- freshEntryId
inputKeys = damlSubmission.getInputDamlStateList.asScala
preExecutionResult @ PreExecutionResult(
readSet,
successfulLogEntry,
newState,
outOfTimeBoundsLogEntry,
_,
_) = keyValueCommitting
.preExecuteSubmission(
defaultConfig = testState.defaultConfig,
submission = damlSubmission,
participantId = testState.participantId,
inputState = createInputState(inputKeys, testState),
)
preExecutionResult = testState.keyValueCommitting.preExecuteSubmission(
defaultConfig = testState.defaultConfig,
submission = damlSubmission,
participantId = testState.participantId,
inputState = createInputState(inputKeys, testState),
)
PreExecutionResult(readSet, successfulLogEntry, newState, outOfTimeBoundsLogEntry, _, _) = preExecutionResult
_ <- addDamlState(newState)
} yield {
assert(
@ -343,13 +384,6 @@ object KVTest {
entryId -> preExecutionResult
}
private[this] val MinMaxRecordTimeDelta: Duration = Duration.ofSeconds(1)
private[this] val DefaultAdditionalContractDataTy = "Party"
private[this] val engine = Engine.DevEngine()
private[kvutils] val metrics = new Metrics(new MetricRegistry)
private[this] val keyValueCommitting = new KeyValueCommitting(engine, metrics)
private[this] val keyValueSubmission = new KeyValueSubmission(metrics)
private[this] def createInputState(
inputKeys: mutable.Buffer[DamlStateKey],
testState: KVTestState): Map[DamlStateKey, Option[DamlStateValue]] = {
@ -362,44 +396,26 @@ object KVTest {
}.toMap
}
private[this] def createSubmitterInfo(
private def createSubmitterInfo(
submitter: Party,
commandId: CommandId,
deduplicationTime: Duration,
testState: KVTestState): SubmitterInfo =
recordTime: Timestamp,
): SubmitterInfo =
SubmitterInfo.withSingleSubmitter(
submitter = submitter,
applicationId = Ref.LedgerString.assertFromString("test"),
commandId = commandId,
deduplicateUntil = testState.recordTime.addMicros(deduplicationTime.toNanos / 1000).toInstant,
)
private[this] def transactionToSubmission(
submissionSeed: Hash,
letDelta: Duration,
testState: KVTestState,
submInfo: SubmitterInfo,
tx: SubmittedTransaction,
txMetaData: Transaction.Metadata): DamlSubmission =
keyValueSubmission.transactionToSubmission(
submitterInfo = submInfo,
meta = TransactionMeta(
ledgerEffectiveTime = testState.recordTime.addMicros(letDelta.toNanos / 1000),
workflowId = None,
submissionTime = txMetaData.submissionTime,
submissionSeed = submissionSeed,
optUsedPackages = Some(txMetaData.usedPackages),
optNodeSeeds = None,
optByKeyNodes = None,
),
tx = tx
deduplicateUntil = recordTime.addMicros(deduplicationTime.toNanos / 1000).toInstant,
)
private[this] def createPartySubmission(
subId: String,
hint: String,
participantId: ParticipantId): DamlSubmission =
keyValueSubmission.partyToSubmission(
participantId: ParticipantId,
testState: KVTestState,
): DamlSubmission =
testState.keyValueSubmission.partyToSubmission(
Ref.LedgerString.assertFromString(subId),
Some(hint),
None,
@ -410,8 +426,9 @@ object KVTest {
submissionId: SubmissionId,
minMaxRecordTimeDelta: Duration,
testState: KVTestState,
oldConf: Configuration): DamlSubmission =
keyValueSubmission.configurationToSubmission(
oldConf: Configuration,
): DamlSubmission =
testState.keyValueSubmission.configurationToSubmission(
maxRecordTime = testState.recordTime.addMicros(minMaxRecordTimeDelta.toNanos / 1000),
submissionId = submissionId,
participantId = testState.participantId,
@ -421,8 +438,9 @@ object KVTest {
private[this] def createArchiveSubmission(
submissionId: String,
testState: KVTestState,
archives: DamlLf.Archive*): DamlSubmission =
keyValueSubmission.archivesToSubmission(
archives: DamlLf.Archive*,
): DamlSubmission =
testState.keyValueSubmission.archivesToSubmission(
submissionId = submissionId,
archives = archives.toList,
sourceDescription = "description",

View File

@ -0,0 +1,132 @@
// Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.ledger.participant.state.kvutils
import com.daml.daml_lf_dev.DamlLf
import com.daml.lf.archive.testing.Encode
import com.daml.lf.command.{
Command,
CreateAndExerciseCommand,
CreateCommand,
ExerciseByKeyCommand,
ExerciseCommand
}
import com.daml.lf.data.Ref.QualifiedName
import com.daml.lf.data.{ImmArray, Ref}
import com.daml.lf.language.{Ast, LanguageVersion}
import com.daml.lf.testing.parser.Implicits._
import com.daml.lf.value.Value
import com.daml.lf.value.Value.{ValueParty, ValueUnit}
class SimplePackage(additionalContractDataType: String) {
val damlPackageWithContractData: Ast.Package =
p"""
module DA.Types {
record @serializable Tuple2 (a: *) (b: *) = { x1: a, x2: b } ;
}
module Simple {
record @serializable SimpleTemplate = { owner: Party, observer: Party, contractData: $additionalContractDataType } ;
variant @serializable SimpleVariant = SV: Party ;
template (this : SimpleTemplate) = {
precondition True,
signatories Cons @Party [Simple:SimpleTemplate {owner} this] (Nil @Party),
observers Cons @Party [Simple:SimpleTemplate {observer} this] (Nil @Party),
agreement "",
choices {
choice Consume (self) (x: Unit) : Unit, controllers Cons @Party [Simple:SimpleTemplate {owner} this] (Nil @Party) to upure @Unit ()
},
key @Party (Simple:SimpleTemplate {owner} this) (\ (p: Party) -> Cons @Party [p] (Nil @Party))
} ;
}
"""
val packageId: Ref.PackageId =
defaultParserParameters.defaultPackageId
val decodedPackage: Ast.Package = {
val metadata = if (LanguageVersion.ordering
.gteq(defaultParserParameters.languageVersion, LanguageVersion.Features.packageMetadata)) {
Some(
Ast.PackageMetadata(
Ref.PackageName.assertFromString("kvutils-tests"),
Ref.PackageVersion.assertFromString("1.0.0")))
} else None
damlPackageWithContractData.copy(metadata = metadata)
}
val archive: DamlLf.Archive = {
Encode.encodeArchive(
packageId -> decodedPackage,
defaultParserParameters.languageVersion,
)
}
val archiveHash: String =
archive.getHash
def typeConstructorId(typeConstructor: String): Ref.Identifier =
Ref.Identifier(packageId, QualifiedName.assertFromString(typeConstructor))
def createCmd(templateId: Ref.Identifier, templateArg: Value[Value.ContractId]): Command =
CreateCommand(templateId, templateArg)
def exerciseCmd(
contractId: Value.ContractId,
templateId: Ref.Identifier,
choiceName: Ref.ChoiceName,
): Command =
ExerciseCommand(templateId, contractId, choiceName, ValueUnit)
def exerciseByKeyCmd(
partyKey: Ref.Party,
templateId: Ref.Identifier,
choiceName: Ref.ChoiceName,
): Command =
ExerciseByKeyCommand(templateId, ValueParty(partyKey), choiceName, ValueUnit)
def createAndExerciseCmd(
templateId: Ref.Identifier,
templateArg: Value[Value.ContractId],
choiceName: Ref.ChoiceName,
): Command =
CreateAndExerciseCommand(templateId, templateArg, choiceName, ValueUnit)
private val simpleTemplateId: Ref.Identifier =
Ref.Identifier(
packageId,
Ref.QualifiedName(
Ref.ModuleName.assertFromString("Simple"),
Ref.DottedName.assertFromString("SimpleTemplate")
)
)
private val simpleConsumeChoiceName: Ref.ChoiceName =
Ref.ChoiceName.assertFromString("Consume")
def simpleCreateCmd(templateArg: Value[Value.ContractId]): Command =
createCmd(simpleTemplateId, templateArg)
def simpleExerciseConsumeCmd(contractId: Value.ContractId): Command =
exerciseCmd(contractId, simpleTemplateId, simpleConsumeChoiceName)
def simpleCreateAndExerciseConsumeCmd(templateArg: Value[Value.ContractId]): Command =
createAndExerciseCmd(simpleTemplateId, templateArg, simpleConsumeChoiceName)
def mkSimpleTemplateArg(
owner: String,
observer: String,
additionalContractValue: Value[Value.ContractId],
): Value[Value.ContractId] =
Value.ValueRecord(
Some(simpleTemplateId),
ImmArray(
Some(Ref.Name.assertFromString("owner")) -> Value.ValueParty(
Ref.Party.assertFromString(owner)),
Some(Ref.Name.assertFromString("observer")) -> Value.ValueParty(
Ref.Party.assertFromString(observer)),
Some(Ref.Name.assertFromString("contractData")) -> additionalContractValue
)
)
}

View File

@ -10,103 +10,20 @@ import com.daml.daml_lf_dev.DamlLf
import com.daml.ledger.participant.state.kvutils.DamlKvutils.DamlLogEntryId
import com.daml.ledger.participant.state.kvutils.committer.CommitContext
import com.daml.ledger.participant.state.v1.{Configuration, ParticipantId, TimeModel}
import com.daml.lf.archive.Decode
import com.daml.lf.archive.testing.Encode
import com.daml.lf.data.Ref.{IdString, QualifiedName}
import com.daml.lf.data.Ref
import com.daml.lf.data.Time.Timestamp
import com.daml.lf.data.{ImmArray, Ref}
import com.daml.lf.language.{Ast, LanguageVersion}
import com.daml.lf.testing.parser.Implicits._
import com.daml.lf.value.Value
import com.google.protobuf.ByteString
object TestHelpers {
def damlPackageWithContractData(additionalContractDataType: String): Ast.Package =
p"""
module DA.Types {
record @serializable Tuple2 (a: *) (b: *) = { x1: a, x2: b } ;
}
module Simple {
record @serializable SimpleTemplate = { owner: Party, observer: Party, contractData: $additionalContractDataType } ;
variant @serializable SimpleVariant = SV: Party ;
template (this : SimpleTemplate) = {
precondition True,
signatories Cons @Party [Simple:SimpleTemplate {owner} this] (Nil @Party),
observers Cons @Party [Simple:SimpleTemplate {observer} this] (Nil @Party),
agreement "",
choices {
choice Consume (self) (x: Unit) : Unit, controllers Cons @Party [Simple:SimpleTemplate {owner} this] (Nil @Party) to upure @Unit ()
},
key @Party (Simple:SimpleTemplate {owner} this) (\ (p: Party) -> Cons @Party [p] (Nil @Party))
} ;
}
"""
def archiveWithContractData(additionalContractDataType: String): DamlLf.Archive = {
val metadata = if (LanguageVersion.ordering
.gteq(defaultParserParameters.languageVersion, LanguageVersion.Features.packageMetadata)) {
Some(
Ast.PackageMetadata(
Ref.PackageName.assertFromString("kvutils-tests"),
Ref.PackageVersion.assertFromString("1.0.0")))
} else None
val pkg = damlPackageWithContractData(additionalContractDataType).copy(metadata = metadata)
Encode.encodeArchive(
defaultParserParameters.defaultPackageId -> pkg,
defaultParserParameters.languageVersion)
}
def packageIdWithContractData(additionalContractDataType: String): IdString.PackageId =
Ref.PackageId.assertFromString(archiveWithContractData(additionalContractDataType).getHash)
def typeConstructorId(ty: String, typeConstructor: String): Ref.Identifier =
Ref.Identifier(packageIdWithContractData(ty), QualifiedName.assertFromString(typeConstructor))
def typeConstructorId(ty: String): Ref.Identifier = typeConstructorId(ty, ty)
def name(value: String): Ref.Name = Ref.Name.assertFromString(value)
def party(value: String): Ref.Party = Ref.Party.assertFromString(value)
def decodedPackageWithContractData(additionalContractDataType: String): Ast.Package =
Decode.decodeArchive(archiveWithContractData(additionalContractDataType))._2
val badArchive: DamlLf.Archive =
DamlLf.Archive.newBuilder
.setHash("blablabla")
.build
val simpleConsumeChoiceid: Ref.ChoiceName =
Ref.ChoiceName.assertFromString("Consume")
def mkTemplateArg(
owner: String,
observer: String,
additionalContractDataType: String,
additionalContractValue: Value[Value.ContractId]): Value[Value.ContractId] =
Value.ValueRecord(
Some(templateIdWith(additionalContractDataType)),
ImmArray(
Some(Ref.Name.assertFromString("owner")) -> Value.ValueParty(
Ref.Party.assertFromString(owner)),
Some(Ref.Name.assertFromString("observer")) -> Value.ValueParty(
Ref.Party.assertFromString(observer)),
Some(Ref.Name.assertFromString("contractData")) -> additionalContractValue
)
)
def templateIdWith(additionalContractDataType: String): Ref.Identifier =
Ref.Identifier(
packageIdWithContractData(additionalContractDataType),
Ref.QualifiedName(
Ref.ModuleName.assertFromString("Simple"),
Ref.DottedName.assertFromString("SimpleTemplate")
)
)
val theRecordTime: Timestamp = Timestamp.Epoch
val theDefaultConfig: Configuration = Configuration(
generation = 0,

View File

@ -158,7 +158,7 @@ class KVUtilsConfigSpec extends AnyWordSpec with Matchers {
}
}
"update metrics" in KVTest.runTestWithSimplePackage() {
"update metrics" in KVTest.runTest {
for {
//Submit config twice to force one acceptance and one rejection on duplicate
_ <- submitConfig({ c =>

View File

@ -12,6 +12,7 @@ import com.daml.ledger.participant.state.kvutils.DamlKvutils.{
DamlPackageUploadRejectionEntry
}
import com.daml.lf.archive.DarReader
import com.daml.lf.data.Ref
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec
@ -25,9 +26,11 @@ class KVUtilsPackageSpec extends AnyWordSpec with Matchers with BazelRunfiles {
private val darReader = DarReader { case (_, is) => Try(DamlLf.Archive.parseFrom(is)) }
private def darFile = new File(rlocation("ledger/test-common/model-tests.dar"))
private val testStablePackages = darReader.readArchiveFromFile(darFile).get
private val simpleArchive = archiveWithContractData("Party")
private val simplePackage = new SimplePackage("Party")
private val simpleArchive = simplePackage.archive
"packages" should {
"be able to submit simple package" in KVTest.runTest {
@ -35,7 +38,7 @@ class KVUtilsPackageSpec extends AnyWordSpec with Matchers with BazelRunfiles {
// NOTE(JM): 'runTest' always uploads 'simpleArchive' by default.
logEntry <- submitArchives("simple-archive-submission-1", simpleArchive).map(_._2)
archiveState <- getDamlState(
Conversions.packageStateKey(packageIdWithContractData("Party")))
Conversions.packageStateKey(Ref.PackageId.assertFromString(simplePackage.archiveHash)))
// Submit again and verify that the uploaded archive didn't appear again.
logEntry2 <- submitArchives("simple-archive-submission-2", simpleArchive)
@ -97,7 +100,7 @@ class KVUtilsPackageSpec extends AnyWordSpec with Matchers with BazelRunfiles {
}
}
"update metrics" in KVTest.runTestWithSimplePackage() {
"update metrics" in KVTest.runTest {
for {
//Submit archive twice to force one acceptance and one rejection on duplicate
_ <- submitArchives("simple-archive-submission-1", simpleArchive).map(_._2)

View File

@ -84,7 +84,7 @@ class KVUtilsPartySpec extends AnyWordSpec with Matchers {
}
}
"update metrics" in KVTest.runTestWithSimplePackage() {
"update metrics" in KVTest.runTest {
for {
//Submit party twice to force one acceptance and one rejection on duplicate
_ <- submitPartyAllocation("submission-1", "alice", p0)

View File

@ -9,9 +9,9 @@ import com.daml.ledger.participant.state.kvutils.DamlKvutils.{
}
import com.daml.ledger.participant.state.kvutils.TestHelpers._
import com.daml.ledger.participant.state.v1.Update
import com.daml.lf.command.{Command, CreateAndExerciseCommand, CreateCommand, ExerciseCommand}
import com.daml.lf.command.Command
import com.daml.lf.crypto
import com.daml.lf.data.{FrontStack, Ref, SortedLookupList}
import com.daml.lf.data.{FrontStack, SortedLookupList}
import com.daml.lf.transaction.Node.NodeCreate
import com.daml.lf.value.Value
import com.daml.lf.value.Value.{
@ -38,21 +38,33 @@ class KVUtilsTransactionSpec extends AnyWordSpec with Matchers {
val eve = party("Eve")
val bobValue = ValueParty(bob)
val templateArgs: Map[String, Value[ContractId]] = Map(
"Party" -> bobValue,
"Option Party" -> ValueOptional(Some(bobValue)),
"List Party" -> ValueList(FrontStack(bobValue)),
"TextMap Party" -> ValueTextMap(SortedLookupList(Map("bob" -> bobValue))),
"Simple:SimpleVariant" ->
ValueVariant(Some(typeConstructorId("Simple:SimpleVariant")), name("SV"), bobValue),
"DA.Types:Tuple2 Party Unit" ->
ValueRecord(
Some(typeConstructorId("DA.Types:Tuple2 Party Unit", "DA.Types:Tuple2")),
FrontStack(
Some(name("x1")) -> bobValue,
Some(name("x2")) -> ValueUnit
).toImmArray
),
def simpleCreateCmd(simplePackage: SimplePackage): Command =
simplePackage.simpleCreateCmd(mkSimpleCreateArg(simplePackage))
def mkSimpleCreateArg(simplePackage: SimplePackage): Value[ContractId] =
simplePackage.mkSimpleTemplateArg("Alice", "Eve", bobValue)
val templateArgs: Map[String, SimplePackage => Value[ContractId]] = Map(
"Party" -> (_ => bobValue),
"Option Party" -> (_ => ValueOptional(Some(bobValue))),
"List Party" -> (_ => ValueList(FrontStack(bobValue))),
"TextMap Party" -> (_ => ValueTextMap(SortedLookupList(Map("bob" -> bobValue)))),
"Simple:SimpleVariant" -> (
simplePackage =>
ValueVariant(
Some(simplePackage.typeConstructorId("Simple:SimpleVariant")),
name("SV"),
bobValue,
)),
"DA.Types:Tuple2 Party Unit" -> (
simplePackage =>
ValueRecord(
Some(simplePackage.typeConstructorId("DA.Types:Tuple2")),
FrontStack(
Some(name("x1")) -> bobValue,
Some(name("x2")) -> ValueUnit
).toImmArray,
)),
// Not yet supported in DAML:
//
// "<party: Party>" -> Value.ValueStruct(FrontStack(Ref.Name.assertFromString("party") -> bobValue).toImmArray),
@ -60,90 +72,75 @@ class KVUtilsTransactionSpec extends AnyWordSpec with Matchers {
// "GenMap Unit Party" -> Value.ValueGenMap(FrontStack(ValueUnit -> bobValue).toImmArray),
)
def createCmd(templateId: Ref.Identifier, templateArg: Value[Value.ContractId]): Command =
CreateCommand(templateId, templateArg)
val simpleTemplateId = templateIdWith("Party")
val simpleTemplateArg = mkTemplateArg("Alice", "Eve", "Party", bobValue)
val simpleCreateCmd = createCmd(simpleTemplateId, simpleTemplateArg)
def exerciseCmd(coid: String, templateId: Ref.Identifier): Command =
ExerciseCommand(
templateId,
Value.ContractId.assertFromString(coid),
simpleConsumeChoiceid,
ValueUnit)
def createAndExerciseCmd(
templateId: Ref.Identifier,
templateArg: Value[Value.ContractId]): Command =
CreateAndExerciseCommand(templateId, templateArg, simpleConsumeChoiceid, ValueUnit)
val p0 = mkParticipantId(0)
val p1 = mkParticipantId(1)
"be able to submit a transaction" in KVTest.runTestWithSimplePackage(alice, bob, eve) {
val seed = hash(this.getClass.getName)
for {
transaction <- runSimpleCommand(alice, seed, simpleCreateCmd)
logEntry <- submitTransaction(
submitter = alice,
transaction = transaction,
submissionSeed = seed).map(_._2)
} yield {
logEntry.getPayloadCase shouldEqual DamlLogEntry.PayloadCase.TRANSACTION_ENTRY
logEntry.getTransactionEntry.hasBlindingInfo shouldBe true
}
simplePackage =>
val seed = hash(this.getClass.getName)
for {
transaction <- runSimpleCommand(alice, seed, simpleCreateCmd(simplePackage))
logEntry <- submitTransaction(
submitter = alice,
transaction = transaction,
submissionSeed = seed).map(_._2)
} yield {
logEntry.getPayloadCase shouldEqual DamlLogEntry.PayloadCase.TRANSACTION_ENTRY
logEntry.getTransactionEntry.hasBlindingInfo shouldBe true
}
}
"be able to pre-execute a transaction" in KVTest.runTestWithSimplePackage(alice, bob, eve) {
val seed = hash(this.getClass.getName)
for {
transaction <- runSimpleCommand(alice, seed, simpleCreateCmd)
preExecutionResult <- preExecuteTransaction(
submitter = alice,
transaction = transaction,
submissionSeed = seed).map(_._2)
} yield {
preExecutionResult.successfulLogEntry.getPayloadCase shouldEqual DamlLogEntry.PayloadCase.TRANSACTION_ENTRY
preExecutionResult.successfulLogEntry.getTransactionEntry.hasBlindingInfo shouldBe true
}
simplePackage =>
val seed = hash(this.getClass.getName)
for {
transaction <- runSimpleCommand(alice, seed, simpleCreateCmd(simplePackage))
preExecutionResult <- preExecuteTransaction(
submitter = alice,
transaction = transaction,
submissionSeed = seed,
).map(_._2)
} yield {
preExecutionResult.successfulLogEntry.getPayloadCase shouldEqual DamlLogEntry.PayloadCase.TRANSACTION_ENTRY
preExecutionResult.successfulLogEntry.getTransactionEntry.hasBlindingInfo shouldBe true
}
}
"reject transaction with out of bounds LET" in KVTest.runTestWithSimplePackage(alice, bob, eve) {
val seed = hash(this.getClass.getName)
for {
transaction <- runSimpleCommand(alice, seed, simpleCreateCmd)
conf <- getDefaultConfiguration
logEntry <- submitTransaction(
submitter = alice,
transaction = transaction,
submissionSeed = seed,
letDelta = conf.timeModel.maxSkew.plusMillis(1))
.map(_._2)
} yield {
logEntry.getPayloadCase shouldEqual DamlLogEntry.PayloadCase.TRANSACTION_REJECTION_ENTRY
logEntry.getTransactionRejectionEntry.getReasonCase shouldEqual DamlTransactionRejectionEntry.ReasonCase.INVALID_LEDGER_TIME
}
simplePackage =>
val seed = hash(this.getClass.getName)
for {
transaction <- runSimpleCommand(alice, seed, simpleCreateCmd(simplePackage))
conf <- getDefaultConfiguration
logEntry <- submitTransaction(
submitter = alice,
transaction = transaction,
submissionSeed = seed,
letDelta = conf.timeModel.maxSkew.plusMillis(1))
.map(_._2)
} yield {
logEntry.getPayloadCase shouldEqual DamlLogEntry.PayloadCase.TRANSACTION_REJECTION_ENTRY
logEntry.getTransactionRejectionEntry.getReasonCase shouldEqual DamlTransactionRejectionEntry.ReasonCase.INVALID_LEDGER_TIME
}
}
"be able to exercise and rejects double spends" in KVTest.runTestWithSimplePackage(
alice,
bob,
eve) {
eve) { simplePackage =>
val seeds =
Stream
.from(0)
.map(i => crypto.Hash.hashPrivateKey(this.getClass.getName + i.toString))
for {
transaction1 <- runSimpleCommand(alice, seeds.head, simpleCreateCmd)
transaction1 <- runSimpleCommand(alice, seeds.head, simpleCreateCmd(simplePackage))
result <- submitTransaction(
submitter = alice,
transaction = transaction1,
submissionSeed = seeds.head)
(entryId, logEntry) = result
update = KeyValueConsumption.logEntryToUpdate(entryId, logEntry).head
coid = update
contractId = update
.asInstanceOf[Update.TransactionAccepted]
.transaction
.nodes
@ -152,7 +149,11 @@ class KVUtilsTransactionSpec extends AnyWordSpec with Matchers {
.asInstanceOf[NodeCreate[ContractId, _]]
.coid
transaction2 <- runSimpleCommand(alice, seeds(1), exerciseCmd(coid.coid, simpleTemplateId))
transaction2 <- runSimpleCommand(
alice,
seeds(1),
simplePackage.simpleExerciseConsumeCmd(contractId),
)
logEntry2 <- submitTransaction(
submitter = alice,
transaction = transaction2,
@ -172,106 +173,112 @@ class KVUtilsTransactionSpec extends AnyWordSpec with Matchers {
}
"reject transactions by unallocated submitters" in KVTest.runTestWithSimplePackage(bob, eve) {
val seed = hash(this.getClass.getName)
for {
configEntry <- submitConfig { c =>
c.copy(generation = c.generation + 1)
simplePackage =>
val seed = hash(this.getClass.getName)
for {
configEntry <- submitConfig { c =>
c.copy(generation = c.generation + 1)
}
transaction <- runSimpleCommand(alice, seed, simpleCreateCmd(simplePackage))
txEntry <- submitTransaction(
submitter = alice,
transaction = transaction,
submissionSeed = seed).map(_._2)
} yield {
configEntry.getPayloadCase shouldEqual DamlLogEntry.PayloadCase.CONFIGURATION_ENTRY
txEntry.getPayloadCase shouldEqual DamlLogEntry.PayloadCase.TRANSACTION_REJECTION_ENTRY
txEntry.getTransactionRejectionEntry.getReasonCase shouldEqual DamlTransactionRejectionEntry.ReasonCase.PARTY_NOT_KNOWN_ON_LEDGER
}
transaction <- runSimpleCommand(alice, seed, simpleCreateCmd)
txEntry <- submitTransaction(
submitter = alice,
transaction = transaction,
submissionSeed = seed).map(_._2)
} yield {
configEntry.getPayloadCase shouldEqual DamlLogEntry.PayloadCase.CONFIGURATION_ENTRY
txEntry.getPayloadCase shouldEqual DamlLogEntry.PayloadCase.TRANSACTION_REJECTION_ENTRY
txEntry.getTransactionRejectionEntry.getReasonCase shouldEqual DamlTransactionRejectionEntry.ReasonCase.PARTY_NOT_KNOWN_ON_LEDGER
}
}
for ((additionalContractDataType, additionalContractValue) <- templateArgs) {
val command = createCmd(
templateIdWith(additionalContractDataType),
mkTemplateArg("Alice", "Eve", additionalContractDataType, additionalContractValue))
s"accept transactions with unallocated parties in values: $additionalContractDataType" in KVTest
.runTestWithPackage(additionalContractDataType, alice, eve) {
s"accept transactions with unallocated parties in values: $additionalContractDataType" in {
val simplePackage = new SimplePackage(additionalContractDataType)
val command = simplePackage.simpleCreateCmd(
simplePackage.mkSimpleTemplateArg("Alice", "Eve", additionalContractValue(simplePackage)))
KVTest.runTestWithPackage(simplePackage, alice, eve) {
val seed = hash(this.getClass.getName)
for {
transaction <- runCommand(alice, seed, additionalContractDataType, command)
transaction <- runCommand(alice, seed, command)
txEntry <- submitTransaction(
submitter = alice,
transaction = transaction,
submissionSeed = seed)
.map(_._2)
submissionSeed = seed,
).map(_._2)
} yield {
txEntry.getPayloadCase shouldEqual DamlLogEntry.PayloadCase.TRANSACTION_ENTRY
}
}
}
}
"reject transactions with unallocated informee" in KVTest.runTestWithSimplePackage(alice, bob) {
val seed = hash(this.getClass.getName)
for {
transaction <- runSimpleCommand(alice, seed, simpleCreateCmd)
txEntry1 <- submitTransaction(
submitter = alice,
transaction = transaction,
submissionSeed = seed)
.map(_._2)
} yield {
txEntry1.getPayloadCase shouldEqual DamlLogEntry.PayloadCase.TRANSACTION_REJECTION_ENTRY
txEntry1.getTransactionRejectionEntry.getReasonCase shouldEqual DamlTransactionRejectionEntry.ReasonCase.PARTY_NOT_KNOWN_ON_LEDGER
}
simplePackage =>
val seed = hash(this.getClass.getName)
for {
transaction <- runSimpleCommand(alice, seed, simpleCreateCmd(simplePackage))
txEntry1 <- submitTransaction(
submitter = alice,
transaction = transaction,
submissionSeed = seed)
.map(_._2)
} yield {
txEntry1.getPayloadCase shouldEqual DamlLogEntry.PayloadCase.TRANSACTION_REJECTION_ENTRY
txEntry1.getTransactionRejectionEntry.getReasonCase shouldEqual DamlTransactionRejectionEntry.ReasonCase.PARTY_NOT_KNOWN_ON_LEDGER
}
}
// FIXME: review this test
"reject transactions for unhosted parties" in KVTest.runTestWithSimplePackage(bob, eve) {
val seed = hash(this.getClass.getName)
for {
configEntry <- submitConfig { c =>
c.copy(generation = c.generation + 1)
simplePackage =>
val seed = hash(this.getClass.getName)
for {
configEntry <- submitConfig { c =>
c.copy(generation = c.generation + 1)
}
createTx <- withParticipantId(p1)(
runSimpleCommand(alice, seed, simpleCreateCmd(simplePackage)))
newParty <- withParticipantId(p1)(allocateParty("unhosted", alice))
txEntry1 <- withParticipantId(p0)(
submitTransaction(submitter = newParty, createTx, seed).map(_._2))
txEntry2 <- withParticipantId(p1)(
submitTransaction(submitter = newParty, transaction = createTx, submissionSeed = seed)
.map(_._2))
} yield {
configEntry.getPayloadCase shouldEqual DamlLogEntry.PayloadCase.CONFIGURATION_ENTRY
txEntry1.getPayloadCase shouldEqual DamlLogEntry.PayloadCase.TRANSACTION_REJECTION_ENTRY
txEntry1.getTransactionRejectionEntry.getReasonCase shouldEqual DamlTransactionRejectionEntry.ReasonCase.SUBMITTER_CANNOT_ACT_VIA_PARTICIPANT
txEntry2.getPayloadCase shouldEqual DamlLogEntry.PayloadCase.TRANSACTION_ENTRY
}
createTx <- withParticipantId(p1)(runSimpleCommand(alice, seed, simpleCreateCmd))
newParty <- withParticipantId(p1)(allocateParty("unhosted", alice))
txEntry1 <- withParticipantId(p0)(
submitTransaction(submitter = newParty, createTx, seed).map(_._2))
txEntry2 <- withParticipantId(p1)(
submitTransaction(submitter = newParty, transaction = createTx, submissionSeed = seed)
.map(_._2))
} yield {
configEntry.getPayloadCase shouldEqual DamlLogEntry.PayloadCase.CONFIGURATION_ENTRY
txEntry1.getPayloadCase shouldEqual DamlLogEntry.PayloadCase.TRANSACTION_REJECTION_ENTRY
txEntry1.getTransactionRejectionEntry.getReasonCase shouldEqual DamlTransactionRejectionEntry.ReasonCase.SUBMITTER_CANNOT_ACT_VIA_PARTICIPANT
txEntry2.getPayloadCase shouldEqual DamlLogEntry.PayloadCase.TRANSACTION_ENTRY
}
}
"reject unauthorized transactions" in KVTest.runTestWithSimplePackage(alice, eve) {
val seed = hash(this.getClass.getName)
for {
// Submit a creation of a contract with owner 'Alice', but submit it as 'Bob'.
transaction <- runSimpleCommand(alice, seed, simpleCreateCmd)
bob <- allocateParty("bob", bob)
txEntry <- submitTransaction(
submitter = bob,
transaction = transaction,
submissionSeed = seed)
.map(_._2)
} yield {
txEntry.getPayloadCase shouldEqual DamlLogEntry.PayloadCase.TRANSACTION_REJECTION_ENTRY
val disputed = DamlTransactionRejectionEntry.ReasonCase.DISPUTED
txEntry.getTransactionRejectionEntry.getReasonCase shouldEqual disputed
}
simplePackage =>
val seed = hash(this.getClass.getName)
for {
// Submit a creation of a contract with owner 'Alice', but submit it as 'Bob'.
transaction <- runSimpleCommand(alice, seed, simpleCreateCmd(simplePackage))
bob <- allocateParty("bob", bob)
txEntry <- submitTransaction(
submitter = bob,
transaction = transaction,
submissionSeed = seed)
.map(_._2)
} yield {
txEntry.getPayloadCase shouldEqual DamlLogEntry.PayloadCase.TRANSACTION_REJECTION_ENTRY
val disputed = DamlTransactionRejectionEntry.ReasonCase.DISPUTED
txEntry.getTransactionRejectionEntry.getReasonCase shouldEqual disputed
}
}
"update metrics" in KVTest.runTestWithSimplePackage(alice, eve) {
"update metrics" in KVTest.runTestWithSimplePackage(alice, eve) { simplePackage =>
val seed = hash(this.getClass.getName)
for {
// Submit a creation of a contract with owner 'Alice', but submit it as 'Bob'.
transaction <- runSimpleCommand(alice, seed, simpleCreateCmd)
transaction <- runSimpleCommand(alice, seed, simpleCreateCmd(simplePackage))
bob <- allocateParty("bob", bob)
_ <- submitTransaction(submitter = bob, transaction = transaction, submissionSeed = seed)
.map(_._2)
@ -289,13 +296,14 @@ class KVUtilsTransactionSpec extends AnyWordSpec with Matchers {
"properly archive transient contracts and keys" in KVTest.runTestWithSimplePackage(
alice,
bob,
eve) {
eve) { simplePackage =>
val seeds =
Stream
.from(0)
.map(i => crypto.Hash.hashPrivateKey(this.getClass.getName + i.toString))
val simpleCreateAndExerciseCmd = createAndExerciseCmd(simpleTemplateId, simpleTemplateArg)
val simpleCreateAndExerciseCmd =
simplePackage.simpleCreateAndExerciseConsumeCmd(mkSimpleCreateArg(simplePackage))
for {
tx1 <- runSimpleCommand(alice, seeds.head, simpleCreateAndExerciseCmd)
@ -330,10 +338,10 @@ class KVUtilsTransactionSpec extends AnyWordSpec with Matchers {
"allow for missing submitter info in log entries" in KVTest.runTestWithSimplePackage(
alice,
bob,
eve) {
eve) { simplePackage =>
val seed = hash(this.getClass.getName)
for {
transaction <- runSimpleCommand(alice, seed, simpleCreateCmd)
transaction <- runSimpleCommand(alice, seed, simpleCreateCmd(simplePackage))
result <- submitTransaction(
submitter = alice,
transaction = transaction,