Ensure out of time bounds entries are set for all rejections [KVL-1412] (#13595)

This commit is contained in:
Nicu Reut 2022-04-18 12:04:49 +02:00 committed by GitHub
parent 6b3e7969cf
commit 988b609051
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 84 additions and 18 deletions

View File

@ -23,7 +23,6 @@ import com.daml.ledger.participant.state.kvutils.store.events.{
}
import com.daml.ledger.participant.state.kvutils.store.{
DamlCommandDedupValue,
DamlLogEntry,
DamlStateValue,
PreExecutionDeduplicationBounds,
}
@ -71,8 +70,6 @@ private[transaction] object CommandDeduplication {
StepContinue(transactionEntry)
} else {
if (commitContext.preExecute) {
// The out of time bounds entry is required in the committer, so we set it to the default value as we stop the steps here with the duplicate rejection
commitContext.outOfTimeBoundsLogEntry = Some(DamlLogEntry.getDefaultInstance)
preExecutionDuplicateRejection(
commitContext,
transactionEntry,

View File

@ -69,12 +69,16 @@ private[kvutils] class TransactionCommitter(
private val committerModelConformanceValidator =
new CommitterModelConformanceValidator(engine, metrics)
/** The order of the steps matters
*/
override protected val steps: Steps[DamlTransactionEntrySummary] = Iterable(
"set_context_min_max_record_time" -> TimeBoundBindingStep.setTimeBoundsInContextStep(
defaultConfig
),
"set_context_out_of_time_bounds_entry" -> ledgerTimeValidator.createValidationStep(rejections),
"authorize_submitter" -> authorizeSubmitters,
"check_informee_parties_allocation" -> checkInformeePartiesAllocation,
"set_time_bounds" -> TimeBoundBindingStep.setTimeBoundsInContextStep(defaultConfig),
"deduplicate" -> CommandDeduplication.deduplicateCommandStep(rejections),
"validate_ledger_time" -> ledgerTimeValidator.createValidationStep(rejections),
"validate_committer_model_conformance" -> committerModelConformanceValidator
.createValidationStep(rejections),
"validate_consistency" -> TransactionConsistencyValidator.createValidationStep(rejections),

View File

@ -73,6 +73,7 @@ object TestHelpers {
DamlSubmitterInfo.newBuilder
.setCommandId("commandId")
.addAllSubmitters(submitters.asJava)
.setDeduplicationDuration(Conversions.buildDuration(Duration.ofSeconds(30)))
)
.setSubmissionSeed(ByteString.copyFromUtf8("a" * 32))
.build

View File

@ -206,18 +206,6 @@ class CommandDeduplicationSpec
deduplicateStepHasTransactionRejectionEntry(commitContext)
}
"set the out of time bounds log entry during rejections" in {
val dedupValue = newDedupValue(
_.setRecordTimeBounds(buildPreExecutionDeduplicationBounds(timestamp, timestamp))
)
val commitContext = createCommitContext(None, Map(aDedupKey -> Some(dedupValue)))
commitContext.minimumRecordTime = Some(timestamp)
commitContext.maximumRecordTime = Some(timestamp)
deduplicateStepHasTransactionRejectionEntry(commitContext)
commitContext.outOfTimeBoundsLogEntry shouldBe 'defined
}
"return the command deduplication duration as deduplication duration when this exceeds the max delta between record times" in {
val dedupValue = newDedupValue(
_.setRecordTimeBounds(buildPreExecutionDeduplicationBounds(timestamp, timestamp))

View File

@ -14,11 +14,15 @@ import com.daml.ledger.participant.state.kvutils.store.events.{
DamlTransactionRejectionEntry,
}
import com.daml.ledger.participant.state.kvutils.store.{
DamlCommandDedupValue,
DamlLogEntry,
DamlPartyAllocation,
DamlStateKey,
DamlStateValue,
PreExecutionDeduplicationBounds,
}
import com.daml.ledger.participant.state.kvutils.{Conversions, Err, committer}
import com.daml.ledger.participant.state.kvutils.wire.DamlSubmission
import com.daml.ledger.participant.state.kvutils.{Conversions, Err, KeyValueCommitting, committer}
import com.daml.lf.data.Time.Timestamp
import com.daml.lf.data.{ImmArray, Ref}
import com.daml.lf.engine.Engine
@ -289,6 +293,78 @@ class TransactionCommitterSpec
}
}
"out of time bounds entry" should {
"be set" when {
"a submitting party is not known" in {
val context = createCommitContext(
recordTime = None,
inputs = createInputs(
Alice -> Some(hostedParty(Alice)),
Bob -> None,
) + (Conversions.configurationStateKey -> None),
participantId = ParticipantId,
)
val transactionEntry = createEmptyTransactionEntry(List(Alice, Bob))
val result = transactionCommitter.preExecute(
DamlSubmission.newBuilder().setTransactionEntry(transactionEntry).build(),
context,
)
resultIsRejectedWithPayload(
result,
DamlTransactionRejectionEntry.ReasonCase.SUBMITTING_PARTY_NOT_KNOWN_ON_LEDGER,
)
}
"the command is a duplicate" in {
val transactionEntry = createEmptyTransactionEntry(List(Alice))
val configurationInput = Conversions.configurationStateKey -> None
val commandDeduplicationInput = Conversions.commandDedupKey(
transactionEntry.getSubmitterInfo
) -> Some(
DamlStateValue.newBuilder
.setCommandDedup(
DamlCommandDedupValue.newBuilder
.setRecordTimeBounds(
PreExecutionDeduplicationBounds
.newBuilder()
.setMaxRecordTime(transactionEntry.getSubmissionTime)
.setMinRecordTime(transactionEntry.getSubmissionTime)
)
)
.build
)
val context = createCommitContext(
recordTime = None,
inputs = createInputs(
Alice -> Some(hostedParty(Alice))
) + configurationInput + commandDeduplicationInput,
participantId = ParticipantId,
)
val result = transactionCommitter.preExecute(
DamlSubmission.newBuilder().setTransactionEntry(transactionEntry).build(),
context,
)
resultIsRejectedWithPayload(
result,
DamlTransactionRejectionEntry.ReasonCase.DUPLICATE_COMMAND,
)
}
}
def resultIsRejectedWithPayload(
result: KeyValueCommitting.PreExecutionResult,
transactionRejectionReason: DamlTransactionRejectionEntry.ReasonCase,
) = {
result.outOfTimeBoundsLogEntry.getPayloadCase shouldBe DamlLogEntry.PayloadCase.OUT_OF_TIME_BOUNDS_ENTRY
result.outOfTimeBoundsLogEntry.getOutOfTimeBoundsEntry.getEntry.getPayloadCase shouldBe DamlLogEntry.PayloadCase.TRANSACTION_REJECTION_ENTRY
result.outOfTimeBoundsLogEntry.getOutOfTimeBoundsEntry.getEntry.getTransactionRejectionEntry.getReasonCase shouldBe DamlTransactionRejectionEntry.ReasonCase.RECORD_TIME_OUT_OF_RANGE
result.successfulLogEntry.getPayloadCase shouldBe DamlLogEntry.PayloadCase.TRANSACTION_REJECTION_ENTRY
result.successfulLogEntry.getTransactionRejectionEntry.getReasonCase shouldBe transactionRejectionReason
}
}
private def createTransactionCommitter(
defaultConfig: Configuration = theDefaultConfig
): committer.transaction.TransactionCommitter =