From d86eca0b05bd20aaa0f15324f38fb7f7a50dd8ea Mon Sep 17 00:00:00 2001 From: fabiotudone-da Date: Wed, 27 Oct 2021 16:39:05 +0200 Subject: [PATCH] Add documentation annotations to KV self-service errors [KVL-1144] (#11385) * KV: port V2 errors to self-service errors framework CHANGELOG_BEGIN CHANGELOG_END * Fix ConversionsSpec compiling * Use a valid ID for ValidationFailure * Fix error details in Conversions * Relax and simplify checks in KeyValueConsumptionSpec * Fix formatting * Fix definite answer in deduplication v2 * Try and make Scala 2.12 happy * Fix typos in comments * Use NOT_FOUND for unknown parties and provide them as resources * Race (group) -> SubmissionRaces * Don't deprecate resource exhausted (ABORTED) error * Add documentation annotations to KV self-service errors CHANGELOG_BEGIN CHANGELOG_END * Update ledger/participant-state/kvutils/src/main/scala/com/daml/ledger/participant/state/kvutils/errors/KVCompletionErrors.scala Co-authored-by: Samir Talwar * Address review comments * Reword `InvalidLedgerTime`'s explanation * Reword `CausalMonotonicityViolated`'s explanation * Fix formatting * Fix merge * Fix explanation of InvalidLedgerTime * Fix formatting * Document groups * Fix typo in group documentation Co-authored-by: tudor-da * Fix typo in group documentation Co-authored-by: tudor-da Co-authored-by: Samir Talwar Co-authored-by: tudor-da --- .../state/kvutils/Conversions.scala | 9 ++ .../state/kvutils/errors/KVErrors.scala | 107 +++++++++++++++++- .../updates/TransactionRejections.scala | 9 +- 3 files changed, 119 insertions(+), 6 deletions(-) diff --git a/ledger/participant-state/kvutils/src/main/scala/com/daml/ledger/participant/state/kvutils/Conversions.scala b/ledger/participant-state/kvutils/src/main/scala/com/daml/ledger/participant/state/kvutils/Conversions.scala index 71a95e853e..70a7c6fb1c 100644 --- a/ledger/participant-state/kvutils/src/main/scala/com/daml/ledger/participant/state/kvutils/Conversions.scala +++ b/ledger/participant-state/kvutils/src/main/scala/com/daml/ledger/participant/state/kvutils/Conversions.scala @@ -3,6 +3,7 @@ package com.daml.ledger.participant.state.kvutils +import java.io.StringWriter import java.time.{Duration, Instant} import com.daml.error.{ContextualizedErrorLogger, ValueSwitch} @@ -52,6 +53,7 @@ import com.daml.lf.value.Value.{ContractId, VersionedValue} import com.daml.lf.value.{Value, ValueCoder, ValueOuterClass} import com.daml.lf.{crypto, data} +import com.fasterxml.jackson.databind.ObjectMapper import com.google.protobuf.Empty import com.google.rpc.status.Status @@ -528,6 +530,13 @@ private[state] object Conversions { rejectionReasonNotSetStatus(entry, errorVersionSwitch) }) + private[kvutils] def objectToJsonString(obj: Object): String = { + val stringWriter = new StringWriter + val objectMapper = new ObjectMapper + objectMapper.writeValue(stringWriter, obj) + stringWriter.toString + } + private def encodeParties(parties: Set[Ref.Party]): List[String] = (parties.toList: List[String]).sorted diff --git a/ledger/participant-state/kvutils/src/main/scala/com/daml/ledger/participant/state/kvutils/errors/KVErrors.scala b/ledger/participant-state/kvutils/src/main/scala/com/daml/ledger/participant/state/kvutils/errors/KVErrors.scala index a4db87fa6d..3c70ad7760 100644 --- a/ledger/participant-state/kvutils/src/main/scala/com/daml/ledger/participant/state/kvutils/errors/KVErrors.scala +++ b/ledger/participant-state/kvutils/src/main/scala/com/daml/ledger/participant/state/kvutils/errors/KVErrors.scala @@ -4,13 +4,14 @@ package com.daml.ledger.participant.state.kvutils.errors import java.time.Instant - import com.daml.error.{ ContextualizedErrorLogger, ErrorCategory, ErrorCode, ErrorGroup, ErrorResource, + Explanation, + Resolution, } import com.daml.error.definitions.ErrorGroups.ParticipantErrorGroup.TransactionErrorGroup.LedgerApiErrorGroup import com.daml.ledger.participant.state.kvutils.committer.transaction.Rejection.{ @@ -18,10 +19,21 @@ import com.daml.ledger.participant.state.kvutils.committer.transaction.Rejection InternallyInconsistentTransaction, } +@Explanation( + "Errors that are specific to ledgers based on the KV architecture." + + "Note that this section will soon cover all ledgers due to an ongoing error consolidation effort." +) object KVErrors extends LedgerApiErrorGroup { + @Explanation("Errors that relate to the Daml concepts of time.") object Time extends ErrorGroup() { + @Explanation( + "The record time is not within bounds for reasons other than deduplication, such as " + + "excessive latency. Excessive clock skew between the participant and the committer " + + "or a time model that is too restrictive may also produce this rejection." + ) + @Resolution("Retry the submission or contact the participant operator.") object InvalidRecordTime extends ErrorCode( id = "INVALID_RECORD_TIME", @@ -37,6 +49,12 @@ object KVErrors extends LedgerApiErrorGroup { extends KVLoggingTransactionErrorImpl(cause) } + @Explanation( + "The record time is not within bounds for reasons other than deduplication, such as " + + "excessive latency. Excessive clock skew between the participant and the committer " + + "or a time model that is too restrictive may also produce this rejection." + ) + @Resolution("Retry the transaction submission or contact the participant operator.") object RecordTimeOutOfRange extends ErrorCode( id = "RECORD_TIME_OUT_OF_RANGE", @@ -52,6 +70,10 @@ object KVErrors extends LedgerApiErrorGroup { ) } + @Explanation( + "At least one input contract's ledger time is later than that of the submitted transaction." + ) + @Resolution("Retry the transaction submission.") object CausalMonotonicityViolated extends ErrorCode( id = "CAUSAL_MONOTONICITY_VIOLATED", @@ -66,8 +88,18 @@ object KVErrors extends LedgerApiErrorGroup { } + @Explanation( + "Errors that can arise due to concurrent processing of transactions in the participant." + ) object SubmissionRaces extends ErrorGroup() { + @Explanation( + "A contract with the same key has already been created by a concurrent transaction submission." + ) + @Resolution( + "The correct resolution depends on the business flow, " + + "for example using the existing contract or retrying after it has been archived." + ) object ExternallyDuplicateKeys extends ErrorCode( id = "EXTERNALLY_DUPLICATE_KEYS", @@ -80,6 +112,10 @@ object KVErrors extends LedgerApiErrorGroup { ) } + @Explanation( + "An input contract key was re-assigned to a different contract by a concurrent transaction submission." + ) + @Resolution("Retry the transaction submission.") object ExternallyInconsistentKeys extends ErrorCode( id = "EXTERNALLY_INCONSISTENT_KEYS", @@ -93,6 +129,11 @@ object KVErrors extends LedgerApiErrorGroup { ) } + @Explanation("An input contract has been archived by a concurrent transaction submission.") + @Resolution( + "The correct resolution depends on the business flow, for example it may be possible to " + + "proceed without the archived contract as an input, or a different contract could be used." + ) object ExternallyInconsistentContracts extends ErrorCode( id = "EXTERNALLY_INCONSISTENT_CONTRACTS", @@ -108,8 +149,14 @@ object KVErrors extends LedgerApiErrorGroup { } + @Explanation("Errors that relate to parties.") object Parties extends ErrorGroup { + @Explanation("The submitting party has not been allocated.") + @Resolution( + "Check that the party identifier is correct, allocate the submitting party, " + + "request its allocation or wait for it to be allocated before retrying the transaction submission." + ) object SubmittingPartyNotKnownOnLedger extends ErrorCode( id = "SUBMITTING_PARTY_NOT_KNOWN_ON_LEDGER", @@ -127,6 +174,11 @@ object KVErrors extends LedgerApiErrorGroup { } } + @Explanation("One or more informee parties have not been allocated.") + @Resolution( + "Check that all the informee party identifiers are correct, allocate all the informee parties, " + + "request their allocation or wait for them to be allocated before retrying the transactiomn submission." + ) object PartiesNotKnownOnLedger extends ErrorCode( id = "PARTIES_NOT_KNOWN_ON_LEDGER", @@ -145,8 +197,13 @@ object KVErrors extends LedgerApiErrorGroup { } + @Explanation("Errors that relate to system resources.") object Resources extends ErrorGroup() { + @Explanation("A system resource has been exhausted.") + @Resolution( + "Retry the transaction submission or provide the details to the participant operator." + ) object ResourceExhausted extends ErrorCode( id = "RESOURCE_EXHAUSTED", @@ -162,8 +219,11 @@ object KVErrors extends LedgerApiErrorGroup { } + @Explanation("Errors that arise due to insufficient authorization.") object Unauthorized extends ErrorGroup() { + @Explanation("A submitting party is not authorized to act through the participant.") + @Resolution("Contact the participant operator or re-submit with an authorized party.") object SubmitterCannotActViaParticipant extends ErrorCode( id = "SUBMITTER_CANNOT_ACT_VIA_PARTICIPANT", @@ -181,8 +241,11 @@ object KVErrors extends LedgerApiErrorGroup { } + @Explanation("Errors that arise from an internal system misbehavior.") object Internal extends ErrorGroup() { + @Explanation("A rejection reason has not been set.") + @Resolution("Contact support.") object RejectionReasonNotSet extends ErrorCode( id = "REJECTION_REASON_NOT_SET", @@ -195,6 +258,8 @@ object KVErrors extends LedgerApiErrorGroup { ) } + @Explanation("An invalid transaction submission was not detected by the participant.") + @Resolution("Contact support.") object ValidationFailure extends ErrorCode( id = "VALIDATION_FAILURE", @@ -208,6 +273,8 @@ object KVErrors extends LedgerApiErrorGroup { ) } + @Explanation("The participant didn't provide a necessary transaction submission input.") + @Resolution("Contact support.") object MissingInputState extends ErrorCode( id = "MISSING_INPUT_STATE", @@ -221,6 +288,11 @@ object KVErrors extends LedgerApiErrorGroup { ) } + @Explanation( + "The participant didn't detect an attempt by the transaction submission " + + "to use the same key for two active contracts." + ) + @Resolution("Contact support.") object InternallyDuplicateKeys extends ErrorCode( id = "INTERNALLY_DUPLICATE_KEYS", @@ -233,6 +305,11 @@ object KVErrors extends LedgerApiErrorGroup { ) } + @Explanation( + "The participant didn't detect an attempt by the transaction submission " + + "to use a stale contract key." + ) + @Resolution("Contact support.") object InternallyInconsistentKeys extends ErrorCode( id = "INTERNALLY_INCONSISTENT_KEYS", @@ -245,6 +322,8 @@ object KVErrors extends LedgerApiErrorGroup { ) } + @Explanation("An invalid participant state has been detected.") + @Resolution("Contact support.") object InvalidParticipantState extends ErrorCode( id = "INVALID_PARTICIPANT_STATE", @@ -264,6 +343,11 @@ object KVErrors extends LedgerApiErrorGroup { } + @Explanation( + "A command for the same ledger change has already been successfully processed " + + "within the current deduplication window." + ) + @Resolution("Celebrate, as your command has already been processed.") object DuplicateCommand extends ErrorCode( id = "DUPLICATE_COMMAND", @@ -277,11 +361,19 @@ object KVErrors extends LedgerApiErrorGroup { ) } + @Explanation( + "Errors that are not going to be produced anymore but may still be encountered " + + "when subscribing to past offsets." + ) @deprecated object Deprecated extends ErrorGroup() { object Time extends ErrorGroup() { + @Explanation( + "The ledger time of the submission violated some constraint on the ledger time." + ) + @Resolution("Retry the transaction submission.") @deprecated( "It was produced by submissions batching and it's not produced anymore by pre-execution." ) @@ -303,6 +395,11 @@ object KVErrors extends LedgerApiErrorGroup { object Parties extends ErrorGroup() { + @Explanation("One or more informee parties have not been allocated.") + @Resolution( + "Check that all the informee party identifiers are correct, allocate all the informee parties, " + + "request their allocation or wait for them to be allocated before retrying the transactiomn submission." + ) @deprecated("Corresponds to transaction submission rejections that are not produced anymore.") object PartyNotKnownOnLedger extends ErrorCode( @@ -323,6 +420,8 @@ object KVErrors extends LedgerApiErrorGroup { object Internal extends ErrorGroup() { + @Explanation("An invalid transaction submission was not detected by the participant.") + @Resolution("Contact support.") @deprecated("Corresponds to transaction submission rejections that are not produced anymore.") object Disputed extends ErrorCode( @@ -339,6 +438,12 @@ object KVErrors extends LedgerApiErrorGroup { } + @Explanation("At least one input has been altered by a concurrent transaction submission.") + @Resolution( + "The correct resolution depends on the business flow, for example it may be possible to proceed " + + "without an archived contract as an input, or the transaction submission may be retried " + + "to load the up-to-date value of a contract key." + ) @deprecated("Corresponds to transaction submission rejections that are not produced anymore.") object Inconsistent extends ErrorCode( diff --git a/ledger/participant-state/kvutils/src/main/scala/com/daml/ledger/participant/state/kvutils/updates/TransactionRejections.scala b/ledger/participant-state/kvutils/src/main/scala/com/daml/ledger/participant/state/kvutils/updates/TransactionRejections.scala index 2b1daf4180..9a44e6185a 100644 --- a/ledger/participant-state/kvutils/src/main/scala/com/daml/ledger/participant/state/kvutils/updates/TransactionRejections.scala +++ b/ledger/participant-state/kvutils/src/main/scala/com/daml/ledger/participant/state/kvutils/updates/TransactionRejections.scala @@ -6,11 +6,6 @@ package com.daml.ledger.participant.state.kvutils.updates import java.io.StringWriter import java.time.Instant -import com.google.protobuf.any.{Any => AnyProto} -import com.google.rpc.code.Code -import com.google.rpc.error_details.ErrorInfo -import com.google.rpc.status.Status - import com.daml.error.{ContextualizedErrorLogger, ValueSwitch} import com.daml.ledger.grpc.GrpcStatuses import com.daml.ledger.participant.state.kvutils.Conversions.parseCompletionInfo @@ -26,6 +21,10 @@ import com.daml.lf.data.Time.Timestamp import com.fasterxml.jackson.databind.ObjectMapper import com.google.protobuf.ProtocolStringList +import com.google.protobuf.any.{Any => AnyProto} +import com.google.rpc.code.Code +import com.google.rpc.error_details.ErrorInfo +import com.google.rpc.status.Status import scala.annotation.nowarn import scala.jdk.CollectionConverters._