Cleanup GlobalKey use (#19027)

* Cleanup GlobalKey use

* With canton one line fix

* Update with review comments

* Add packageName to GlobalKey.toString
This commit is contained in:
Simon Maxen 2024-04-18 15:00:52 +01:00 committed by GitHub
parent 52c63cdfde
commit 05212c9fd4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 21 additions and 2905 deletions

View File

@ -2448,14 +2448,11 @@ class EngineTestHelpers(majorLanguageVersion: LanguageMajorVersion) {
// non-dev dar
s"daml-lf/engine/BasicTests-v${majorLanguageVersion.pretty}dev.dar"
)
val basicTestsHashPkgName =
if (GlobalKey.useDummyHashPackageName) GlobalKey.dummyHashPackageName else basicTestsPkg.name
val basicTestsHashPkgName: PackageName = basicTestsPkg.name
val basicTestsSignatures: PackageInterface =
language.PackageInterface(Map(basicTestsPkgId -> basicTestsPkg))
val basicUseSharedKeys: Boolean = true
val party: Ref.IdString.Party = Party.assertFromString("Party")
val alice: Ref.IdString.Party = Party.assertFromString("Alice")
val bob: Ref.IdString.Party = Party.assertFromString("Bob")

View File

@ -5,7 +5,7 @@ package com.daml.lf
package engine
import com.daml.lf.crypto.Hash
import com.daml.lf.data.Ref.Party
import com.daml.lf.data.Ref.{PackageRef, Party}
import com.daml.lf.data.{Bytes, FrontStack, ImmArray, Ref}
import com.daml.lf.command.ApiCommand
import com.daml.lf.language.{Ast, LanguageMajorVersion, LanguageVersion}
@ -20,7 +20,6 @@ import com.daml.lf.testing.parser.ParserParameters
import com.daml.lf.transaction.test.TransactionBuilder.Implicits.{defaultPackageId => _, _}
import com.daml.lf.value.Value
import com.daml.lf.speedy.Compiler
import com.daml.lf.transaction.GlobalKey
class PreprocessorSpecV2 extends PreprocessorSpec(LanguageMajorVersion.V2)
@ -335,7 +334,7 @@ final class PreprocessorSpecHelpers(majorLanguageVersion: LanguageMajorVersion)
crypto.Hash.hashPrivateKey("test-contract-id"),
Bytes.assertFromString("deadbeef"),
)
val pkgRef = Ref.PackageRef.Name(pkgName)
val pkgRef: PackageRef.Name = Ref.PackageRef.Name(pkgName)
val withoutKeyTmplId: Ref.TypeConName = Ref.Identifier.assertFromString("-pkgId-:Mod:WithoutKey")
val withoutKeyTmplRef: Ref.TypeConRef = Ref.TypeConRef(pkgRef, withoutKeyTmplId.qualifiedName)
val withKeyTmplId: Ref.TypeConName = Ref.Identifier.assertFromString("-pkgId-:Mod:WithKey")
@ -347,14 +346,9 @@ final class PreprocessorSpecHelpers(majorLanguageVersion: LanguageMajorVersion)
None -> Value.ValueList(FrontStack.from(ImmArray(ValueParty(alice)))),
),
)
val keyHash: Hash = crypto.Hash.assertHashContractKey(withKeyTmplId, pkgName, key)
val basicTestsHashPkgName =
if (GlobalKey.useDummyHashPackageName) GlobalKey.dummyHashPackageName else pkgName
val keyHash: Hash =
crypto.Hash.assertHashContractKey(withKeyTmplId, basicTestsHashPkgName, key)
val choiceId = Ref.Name.assertFromString("Noop")
val choiceId: Ref.Name = Ref.Name.assertFromString("Noop")
def buildDisclosedContract(
contractId: ContractId = contractId,

View File

@ -96,7 +96,7 @@ private[lf] class ExplicitDisclosureLib(majorLanguageVersion: LanguageMajorVersi
val caveTemplateId: Ref.Identifier = Ref.Identifier.assertFromString("-pkgId-:TestMod:Cave")
val caveTemplateType: Ref.TypeConName = Ref.TypeConName.assertFromString("-pkgId-:TestMod:Cave")
val keyType: Ref.TypeConName = Ref.TypeConName.assertFromString("-pkgId-:TestMod:Key")
val contractKey: GlobalKey = buildContractKey(maintainerParty, somePackageName)
val contractKey: GlobalKey = buildContractKey(maintainerParty, pkg.name)
val contractSStructKey: SValue =
SValue.SStruct(
fieldNames =

View File

@ -7,7 +7,6 @@ package transaction
import com.daml.lf.crypto.Hash
import com.daml.lf.data.Ref
import com.daml.lf.data.Ref.{Party, TypeConName}
import com.daml.lf.transaction.GlobalKey.dummyHashPackageName
import com.daml.lf.value.Value
/** Useful in various circumstances -- basically this is what a ledger implementation must use as
@ -30,21 +29,15 @@ final class GlobalKey private (
override def hashCode(): Int = hash.hashCode()
override def toString: String = s"GlobalKey($templateId, $key)"
override def toString: String = s"GlobalKey($templateId, $packageName, $key)"
}
object GlobalKey {
// #TODO(18828) Use dummy package name until all call sites are updated
private[lf] val useDummyHashPackageName = true
private[lf] val dummyHashPackageName =
Ref.PackageName.assertFromString("dummy-package-name")
def assertWithRenormalizedValue(key: GlobalKey, value: Value): GlobalKey = {
val hashPackageName = if (useDummyHashPackageName) dummyHashPackageName else key.packageName
if (
key.key != value &&
Hash.assertHashContractKey(key.templateId, hashPackageName, value) != key.hash
Hash.assertHashContractKey(key.templateId, key.packageName, value) != key.hash
) {
throw new IllegalArgumentException(
s"Hash must not change as a result of value renormalization key=$key, value=$value"
@ -59,18 +52,17 @@ object GlobalKey {
def build(
templateId: TypeConName,
key: Value,
packageName: Ref.PackageName = dummyHashPackageName,
packageName: Ref.PackageName,
): Either[crypto.Hash.HashingError, GlobalKey] = {
val hashPackageName = if (useDummyHashPackageName) dummyHashPackageName else packageName
crypto.Hash
.hashContractKey(templateId, hashPackageName, key)
.hashContractKey(templateId, packageName, key)
.map(new GlobalKey(templateId, packageName, key, _))
}
def assertBuild(
templateId: TypeConName,
key: Value,
packageName: Ref.PackageName = dummyHashPackageName,
packageName: Ref.PackageName,
): GlobalKey = {
data.assertRight(build(templateId, key, packageName).left.map(_.msg))
}
@ -93,7 +85,7 @@ object GlobalKeyWithMaintainers {
templateId: TypeConName,
value: Value,
maintainers: Set[Party],
packageName: Ref.PackageName = dummyHashPackageName,
packageName: Ref.PackageName,
): GlobalKeyWithMaintainers =
data.assertRight(build(templateId, value, maintainers, packageName).left.map(_.msg))
@ -101,7 +93,7 @@ object GlobalKeyWithMaintainers {
templateId: TypeConName,
value: Value,
maintainers: Set[Party],
packageName: Ref.PackageName = dummyHashPackageName,
packageName: Ref.PackageName,
): Either[Hash.HashingError, GlobalKeyWithMaintainers] =
GlobalKey.build(templateId, value, packageName).map(GlobalKeyWithMaintainers(_, maintainers))
}

View File

@ -61,20 +61,6 @@ object GrpcErrorParser {
.lift(resourceDetails)
.getOrElse(new SubmitError.TruncatedError(classNameOf[A], message))
// Only needed until canton is updated to provide package names
def casePnErr[A <: SubmitError: ClassTag](
handler: PartialFunction[Seq[(ErrorResource, String)], A]
): SubmitError = {
val hasPn = resourceDetails.exists({ case (er, _) => er == ErrorResource.PackageName })
val pnResourceDetails =
if (hasPn) resourceDetails
else
resourceDetails :+ ErrorResource.PackageName -> GlobalKey.dummyHashPackageName
handler
.lift(pnResourceDetails)
.getOrElse(new SubmitError.TruncatedError(classNameOf[A], message))
}
errorCode match {
case "CONTRACT_NOT_FOUND" =>
caseErr {
@ -96,7 +82,7 @@ object GrpcErrorParser {
)
}
case "CONTRACT_KEY_NOT_FOUND" =>
casePnErr {
caseErr {
case Seq(
(ErrorResource.TemplateId, tid),
(ErrorResource.ContractKey, decodeValue.unlift(key)),
@ -117,7 +103,7 @@ object GrpcErrorParser {
)
}
case "DISCLOSED_CONTRACT_KEY_HASHING_ERROR" =>
casePnErr {
caseErr {
case Seq(
(ErrorResource.TemplateId, tid),
(ErrorResource.ContractId, cid),
@ -134,7 +120,7 @@ object GrpcErrorParser {
)
}
case "DUPLICATE_CONTRACT_KEY" =>
casePnErr {
caseErr {
case Seq(
(ErrorResource.TemplateId, tid),
(ErrorResource.ContractKey, decodeValue.unlift(key)),
@ -163,7 +149,7 @@ object GrpcErrorParser {
case _ => SubmitError.LocalVerdictLockedContracts(Seq())
}
case "INCONSISTENT_CONTRACT_KEY" =>
casePnErr {
caseErr {
case Seq(
(ErrorResource.TemplateId, tid),
@ -199,7 +185,7 @@ object GrpcErrorParser {
SubmitError.CreateEmptyContractKeyMaintainers(Identifier.assertFromString(tid), arg)
}
case "FETCH_EMPTY_CONTRACT_KEY_MAINTAINERS" =>
casePnErr {
caseErr {
case Seq(
(ErrorResource.TemplateId, tid),
(ErrorResource.ContractKey, decodeValue.unlift(key)),

View File

@ -12,15 +12,7 @@ da_scala_library(
tags = ["maven_coordinates=com.daml:ledger-api-errors:__VERSION__"],
visibility = ["//visibility:public"],
deps = [
"//canton:ledger_api_proto_scala",
"//daml-lf/data",
"//daml-lf/engine",
"//daml-lf/language",
"//daml-lf/transaction",
"//daml-lf/validation",
"//ledger/error",
"@maven//:com_google_api_grpc_proto_google_common_protos",
"@maven//:io_grpc_grpc_api",
"@maven//:org_slf4j_slf4j_api",
],
)

View File

@ -1,12 +1,6 @@
# Ledger error definitions
Home to error definitions commonly reported via the Ledger API server.
These errors are generic wrt to the ledger backend used by the participant server.
Error codes in the ledger-api-errors package were duplicates of those in the Canton repository and are no longer
used or needed.
## Daml-LF dependencies
Multiple error definitions depend on Daml LF types whose Bazel targets
pull in unrelated dependencies, such as the Daml engine.
**TODO (https://github.com/digital-asset/daml/issues/15453):** Extract Daml-LF interface types to separate package
in order to decouple the error definitions from the Daml engine.
This entire module is set for removal as part of https://github.com/DACH-NY/canton/issues/18444

View File

@ -1,72 +0,0 @@
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.error.definitions
import com.daml.error.ErrorCode.LoggedApiException
import com.daml.error._
import com.daml.error.definitions.ErrorGroups.ParticipantErrorGroup.IndexErrorGroup
@Explanation("Errors raised by the Participant Index persistence layer.")
object IndexErrors extends IndexErrorGroup {
object DatabaseErrors extends DatabaseErrorGroup {
@Explanation(
"This error occurs if a transient error arises when executing a query against the index database."
)
@Resolution("Re-submit the request.")
object SqlTransientError
extends ErrorCode(
id = "INDEX_DB_SQL_TRANSIENT_ERROR",
ErrorCategory.TransientServerFailure,
) {
case class Reject(throwable: Throwable)(implicit
val loggingContext: ContextualizedErrorLogger
) extends DbError(
cause =
s"Processing the request failed due to a transient database error: ${throwable.getMessage}",
throwableO = Some(throwable),
)
}
@Explanation(
"This error occurs if a non-transient error arises when executing a query against the index database."
)
@Resolution("Contact the participant operator.")
object SqlNonTransientError
extends ErrorCode(
id = "INDEX_DB_SQL_NON_TRANSIENT_ERROR",
ErrorCategory.SystemInternalAssumptionViolated,
) {
case class Reject(throwable: Throwable)(implicit
val loggingContext: ContextualizedErrorLogger
) extends DbError(
cause =
s"Processing the request failed due to a non-transient database error: ${throwable.getMessage}",
throwableO = Some(throwable),
)
}
}
// Decorator that returns a specialized StatusRuntimeException (IndexDbException)
// that can be used for precise matching of persistence exceptions (e.g. for index initialization failures that need retrying).
// Without this specialization, internal errors just appear as StatusRuntimeExceptions (see INDEX_DB_SQL_NON_TRANSIENT_ERROR)
// without any marker, impeding us to assert whether they are emitted by the persistence layer or not.
abstract class DbError(
override val cause: String,
override val throwableO: Option[Throwable] = None,
)(implicit
code: ErrorCode,
loggingContext: ContextualizedErrorLogger,
) extends DamlErrorWithDefiniteAnswer(cause, throwableO) {
override def asGrpcError: IndexDbException = {
val err = super.asGrpcError
IndexDbException(err.getStatus, err.getTrailers)
}
}
case class IndexDbException(status: io.grpc.Status, metadata: io.grpc.Metadata)
extends LoggedApiException(status, metadata)
}

View File

@ -1,74 +0,0 @@
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.error.definitions
import com.daml.error._
import com.daml.error.definitions.ErrorGroups.ParticipantErrorGroup.LedgerApiErrorGroup
import com.daml.lf.engine.Error.Validation.ReplayMismatch
import com.daml.lf.engine.{Error => LfError}
@Explanation(
"Errors raised by or forwarded by the Ledger API."
)
object LedgerApiErrors extends LedgerApiErrorGroup {
val Admin: groups.AdminServices.type = groups.AdminServices
val CommandExecution: groups.CommandExecution.type = groups.CommandExecution
val AuthorizationChecks: groups.AuthorizationChecks.type = groups.AuthorizationChecks
val ConsistencyErrors: groups.ConsistencyErrors.type = groups.ConsistencyErrors
val RequestValidation: groups.RequestValidation.type = groups.RequestValidation
val WriteServiceRejections: groups.WriteServiceRejections.type = groups.WriteServiceRejections
val EarliestOffsetMetadataKey = "earliest_offset"
@Explanation("""This error occurs if there was an unexpected error in the Ledger API.""")
@Resolution("Contact support.")
object InternalError
extends ErrorCode(
id = "LEDGER_API_INTERNAL_ERROR",
ErrorCategory.SystemInternalAssumptionViolated,
) {
case class Generic(
message: String,
override val throwableO: Option[Throwable] = None,
)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(
cause = message,
extraContext = Map("throwableO" -> throwableO.toString),
)
case class Preprocessing(
err: LfError.Preprocessing.Internal
)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(cause = err.message)
case class Validation(reason: ReplayMismatch)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(
cause = s"Observed un-expected replay mismatch: $reason"
)
case class Interpretation(
where: String,
message: String,
detailMessage: Option[String],
)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(
cause = s"Daml-Engine interpretation failed with internal error: $where / $message",
extraContext = Map("detailMessage" -> detailMessage),
)
case class VersionService(message: String)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(cause = message)
case class Buffer(message: String, override val throwableO: Option[Throwable])(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(cause = message, throwableO = throwableO)
}
}

View File

@ -1,141 +0,0 @@
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.error.definitions
import com.daml.error._
import com.daml.lf.data.Ref
import com.daml.lf.data.Ref.PackageId
import com.daml.lf.validation
@Explanation(
"Errors raised by the Package Management Service on package uploads."
)
object PackageServiceError extends LedgerApiErrors.PackageServiceErrorGroup {
@Explanation("Package parsing errors raised during package upload.")
object Reading extends ErrorGroup {
@Explanation("""This error indicates that the supplied dar file was invalid.""")
@Resolution("Inspect the error message for details and contact support.")
object InvalidDar
extends ErrorCode(id = "INVALID_DAR", ErrorCategory.InvalidIndependentOfSystemState) {
final case class Error(entries: Seq[String], throwable: Throwable)(implicit
val loggingContext: ContextualizedErrorLogger
) extends DamlError(
cause = "Dar file is corrupt",
throwableO = Some(throwable),
extraContext = Map(
"entries" -> entries,
"throwable" -> throwable,
),
)
}
@Explanation("""This error indicates that the supplied zipped dar file was invalid.""")
@Resolution("Inspect the error message for details and contact support.")
object InvalidZipEntry
extends ErrorCode(id = "INVALID_ZIP_ENTRY", ErrorCategory.InvalidIndependentOfSystemState) {
final case class Error(name: String, entries: Seq[String])(implicit
val loggingContext: ContextualizedErrorLogger
) extends DamlError(
cause = "Dar zip file is corrupt",
extraContext = Map(
"name" -> name,
"entries" -> entries,
),
)
}
@Explanation("""This error indicates that the supplied zipped dar is regarded as zip-bomb.""")
@Resolution("Inspect the dar and contact support.")
object ZipBomb
extends ErrorCode(id = "ZIP_BOMB", ErrorCategory.InvalidIndependentOfSystemState) {
final case class Error(msg: String)(implicit
val loggingContext: ContextualizedErrorLogger
) extends DamlError(
cause = "Dar zip file seems to be a zip bomb.",
extraContext = Map("msg" -> msg),
)
}
}
@Explanation("""This error indicates an internal issue within the package service.""")
@Resolution("Inspect the error message and contact support.")
object InternalError
extends ErrorCode(
id = "PACKAGE_SERVICE_INTERNAL_ERROR",
ErrorCategory.SystemInternalAssumptionViolated,
) {
final case class Validation(nameOfFunc: String, msg: String, detailMsg: String = "")(implicit
val loggingContext: ContextualizedErrorLogger
) extends DamlError(
cause = "Internal package validation error.",
extraContext = Map(
"nameOfFunc" -> nameOfFunc,
"msg" -> msg,
"detailMsg" -> detailMsg,
),
)
final case class Error(missing: Set[PackageId])(implicit
val loggingContext: ContextualizedErrorLogger
) extends DamlError(
cause = "Failed to resolve package ids locally.",
extraContext = Map("missing" -> missing),
)
final case class Generic(reason: String)(implicit
val loggingContext: ContextualizedErrorLogger
) extends DamlError(
cause = "Generic error (please check the reason string).",
extraContext = Map("reason" -> reason),
)
final case class Unhandled(throwable: Throwable)(implicit
val loggingContext: ContextualizedErrorLogger
) extends DamlError(
cause = "Failed with an unknown error cause",
throwableO = Some(throwable),
extraContext = Map("throwable" -> throwable),
)
}
object Validation {
@Explanation("""This error indicates that the validation of the uploaded dar failed.""")
@Resolution("Inspect the error message and contact support.")
object ValidationError
extends ErrorCode(
id = "DAR_VALIDATION_ERROR",
ErrorCategory.InvalidIndependentOfSystemState,
) {
final case class Error(validationError: validation.ValidationError)(implicit
val loggingContext: ContextualizedErrorLogger
) extends DamlError(
cause = "Package validation failed.",
extraContext = Map("validationError" -> validationError),
)
}
@Explanation(
"""This error indicates that the uploaded Dar is broken because it is missing internal dependencies."""
)
@Resolution("Contact the supplier of the Dar.")
object SelfConsistency
extends ErrorCode(
id = "DAR_NOT_SELF_CONSISTENT",
ErrorCategory.InvalidIndependentOfSystemState,
) {
final case class Error(
packageIds: Set[Ref.PackageId],
missingDependencies: Set[Ref.PackageId],
)(implicit
val loggingContext: ContextualizedErrorLogger
) extends DamlError(
cause =
"The set of packages in the dar is not self-consistent and is missing dependencies",
extraContext = Map(
"packageIds" -> packageIds,
"missingDependencies" -> missingDependencies,
),
)
}
}
}

View File

@ -1,43 +0,0 @@
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.error.definitions.groups
import com.daml.error.definitions.{LedgerApiErrors}
import com.daml.error.definitions.groups
import com.daml.error.{
ContextualizedErrorLogger,
DamlErrorWithDefiniteAnswer,
ErrorCategory,
ErrorCode,
Explanation,
Resolution,
}
@Explanation("Errors raised by Ledger API admin services.")
object AdminServices extends LedgerApiErrors.AdminServicesErrorGroup {
val UserManagement: groups.UserManagementServiceErrorGroup.type =
groups.UserManagementServiceErrorGroup
val IdentityProviderConfig: groups.IdentityProviderConfigServiceErrorGroup.type =
groups.IdentityProviderConfigServiceErrorGroup
val PartyManagement: groups.PartyManagementServiceErrorGroup.type =
groups.PartyManagementServiceErrorGroup
@Explanation("This rejection is given when a new configuration is rejected.")
@Resolution("Fetch newest configuration and/or retry.")
object ConfigurationEntryRejected
extends ErrorCode(
id = "CONFIGURATION_ENTRY_REJECTED",
ErrorCategory.InvalidGivenCurrentSystemStateOther,
) {
case class Reject(_message: String)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(
cause = _message
)
}
}

View File

@ -1,98 +0,0 @@
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.error.definitions.groups
import com.daml.error.definitions.{LedgerApiErrors}
import com.daml.error.{
ContextualizedErrorLogger,
DamlErrorWithDefiniteAnswer,
ErrorCategory,
ErrorCategoryRetry,
ErrorCode,
Explanation,
Resolution,
}
import scala.concurrent.duration._
@Explanation("Authentication and authorization errors.")
object AuthorizationChecks extends LedgerApiErrors.AuthorizationChecks {
@Explanation("""The stream was aborted because the authenticated user's rights changed,
|and the user might thus no longer be authorized to this stream.
|""")
@Resolution(
"The application should automatically retry fetching the stream. It will either succeed, or fail with an explicit denial of authentication or permission."
)
object StaleUserManagementBasedStreamClaims
extends ErrorCode(
id = "STALE_STREAM_AUTHORIZATION",
ErrorCategory.ContentionOnSharedResources,
) {
case class Reject()(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer("Stale stream authorization. Retry quickly.") {
override def retryable: Option[ErrorCategoryRetry] = Some(
ErrorCategoryRetry(duration = 0.seconds)
)
}
}
@Explanation(
"""This rejection is given if the submitted command does not contain a JWT token on a participant enforcing JWT authentication."""
)
@Resolution(
"Ask your participant operator to provide you with an appropriate JWT token."
)
object Unauthenticated
extends ErrorCode(
id = "UNAUTHENTICATED",
ErrorCategory.AuthInterceptorInvalidAuthenticationCredentials,
) {
case class MissingJwtToken()(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(
cause = "The command is missing a (valid) JWT token"
)
case class UserBasedAuthenticationIsDisabled()(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(
cause = "User based authentication is disabled."
)
}
@Explanation("An internal system authorization error occurred.")
@Resolution("Contact the participant operator.")
object InternalAuthorizationError
extends ErrorCode(
id = "INTERNAL_AUTHORIZATION_ERROR",
ErrorCategory.SystemInternalAssumptionViolated,
) {
case class Reject(message: String, throwable: Throwable)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(
cause = message,
throwableO = Some(throwable),
)
}
@Explanation(
"""This rejection is given if the supplied authorization token is not sufficient for the intended command.
|The exact reason is logged on the participant, but not given to the user for security reasons."""
)
@Resolution(
"Inspect your command and your token or ask your participant operator for an explanation why this command failed."
)
object PermissionDenied
extends ErrorCode(id = "PERMISSION_DENIED", ErrorCategory.InsufficientPermission) {
case class Reject(override val cause: String)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(
cause =
s"The provided authorization token is not sufficient to authorize the intended command: $cause"
)
}
}

View File

@ -1,475 +0,0 @@
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.error.definitions.groups
import com.daml.error.definitions.{LedgerApiErrors}
import com.daml.error.{
ContextualizedErrorLogger,
DamlErrorWithDefiniteAnswer,
ErrorCategory,
ErrorCode,
ErrorGroup,
ErrorResource,
Explanation,
Resolution,
}
import com.daml.lf.data.Ref.Identifier
import com.daml.lf.engine.{Error => LfError}
import com.daml.lf.interpretation.{Error => LfInterpretationError}
import com.daml.lf.language.Ast
import com.daml.lf.transaction.{GlobalKey, TransactionVersion}
import com.daml.lf.value.{Value, ValueCoder}
@Explanation(
"Errors raised during the command execution phase of the command submission evaluation."
)
object CommandExecution extends ErrorGroup()(LedgerApiErrors.errorClass) {
def encodeValue(v: Value): Either[ValueCoder.EncodeError, String] =
ValueCoder
.encodeValue(TransactionVersion.VDev, v)
.map(_.toStringUtf8)
def withEncodedValue(
v: Value
)(
f: String => Seq[(ErrorResource, String)]
)(implicit loggingContext: ContextualizedErrorLogger): Seq[(ErrorResource, String)] =
encodeValue(v).fold(
{ case ValueCoder.EncodeError(msg) =>
loggingContext.error(msg)
Seq.empty
},
f,
)
@Explanation(
"Errors raised during command conversion to the internal data representation."
)
object Preprocessing extends ErrorGroup {
@Explanation("""This error occurs if a command fails during interpreter pre-processing.""")
@Resolution("Inspect error details and correct your application.")
object PreprocessingFailed
extends ErrorCode(
id = "COMMAND_PREPROCESSING_FAILED",
ErrorCategory.InvalidIndependentOfSystemState,
) {
final case class Reject(
err: LfError.Preprocessing.Error
)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(
cause = err.message
)
}
}
@Explanation(
"Errors raised during the command interpretation phase of the command submission evaluation."
)
object Interpreter extends ErrorGroup {
@Explanation(
"""This error occurs if an exercise or fetch happens on a transaction-locally consumed contract."""
)
@Resolution("This error indicates an application error.")
object ContractNotActive
extends ErrorCode(
id = "CONTRACT_NOT_ACTIVE",
ErrorCategory.InvalidGivenCurrentSystemStateResourceMissing,
) {
final case class Reject(
override val cause: String,
err: LfInterpretationError.ContractNotActive,
)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(
cause = cause
) {
override def resources: Seq[(ErrorResource, String)] = Seq(
(ErrorResource.TemplateId, err.templateId.toString),
(ErrorResource.ContractId, err.coid.coid),
)
}
}
@Explanation("Errors raised in lookups during the command interpretation phase.")
object LookupErrors extends ErrorGroup {
@Explanation(
"""This error occurs if the Daml engine interpreter cannot resolve a contract key to an active contract. This
|can be caused by either the contract key not being known to the participant, or not being known to
|the submitting parties or the contract representing an already archived key."""
)
@Resolution("This error type occurs if there is contention on a contract.")
object ContractKeyNotFound
extends ErrorCode(
id = "CONTRACT_KEY_NOT_FOUND",
ErrorCategory.InvalidGivenCurrentSystemStateResourceMissing,
) {
final case class Reject(
override val cause: String,
key: GlobalKey,
)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(
cause = cause
) {
override def resources: Seq[(ErrorResource, String)] =
withEncodedValue(key.key) { encodedKey =>
Seq(
(ErrorResource.TemplateId, key.templateId.toString),
(ErrorResource.ContractKey, encodedKey),
)
}
}
}
}
@Explanation("""This error occurs if a Daml transaction fails due to an authorization error.
|An authorization means that the Daml transaction computed a different set of required submitters than
|you have provided during the submission as `actAs` parties.""")
@Resolution("This error type occurs if there is an application error.")
object AuthorizationError
extends ErrorCode(
id = "DAML_AUTHORIZATION_ERROR",
ErrorCategory.InvalidIndependentOfSystemState,
) {
final case class Reject(override val cause: String)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(
cause = cause
)
}
@Explanation(
"""This error occurs if a user attempts to provide a key hash for a disclosed contract which we have already cached to be different."""
)
@Resolution(
"Ensure the contract ID and contract payload you have provided in your disclosed contract is correct."
)
object DisclosedContractKeyHashingError
extends ErrorCode(
id = "DISCLOSED_CONTRACT_KEY_HASHING_ERROR",
ErrorCategory.InvalidGivenCurrentSystemStateOther,
) {
final case class Reject(
override val cause: String,
err: LfInterpretationError.DisclosedContractKeyHashingError,
)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(
cause = cause
) {
override def resources: Seq[(ErrorResource, String)] =
withEncodedValue(err.key.key) { encodedKey =>
Seq(
(ErrorResource.TemplateId, err.key.templateId.toString),
(ErrorResource.ContractId, err.coid.coid),
(ErrorResource.ContractKey, encodedKey),
(ErrorResource.ContractKeyHash, err.declaredHash.toString),
)
}
}
}
private def getTypeIdentifier(t: Ast.Type): Option[Identifier] =
t match {
case Ast.TTyCon(ty) => Some(ty)
case _ => None
}
@Explanation(
"""This error occurs when a user throws an error and does not catch it with try-catch."""
)
@Resolution(
"Either your error handling in a choice body is insufficient, or you are using a contract incorrectly."
)
object UnhandledException
extends ErrorCode(
id = "UNHANDLED_EXCEPTION",
ErrorCategory.InvalidGivenCurrentSystemStateOther,
) {
final case class Reject(
override val cause: String,
err: LfInterpretationError.UnhandledException,
)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(
cause = cause
) {
override def resources: Seq[(ErrorResource, String)] =
withEncodedValue(err.value) { encodedValue =>
getTypeIdentifier(err.exceptionType)
.map(ty =>
Seq(
(ErrorResource.ExceptionType, ty.toString),
(ErrorResource.ExceptionValue, encodedValue),
)
)
.getOrElse(Nil)
}
}
}
@Explanation(
"""This error occurs when a contract's pre-condition (the ensure clause) is violated on contract creation."""
)
@Resolution(
"Ensure the contract argument you are passing into your create doesn't violate the conditions of the contract."
)
object TemplatePreconditionViolated
extends ErrorCode(
id = "TEMPLATE_PRECONDITION_VIOLATED",
ErrorCategory.InvalidIndependentOfSystemState,
) {
final case class Reject(
override val cause: String
)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(
cause = cause
) {}
}
@Explanation(
"""This error occurs when you try to create a contract that has a key, but with empty maintainers."""
)
@Resolution(
"Check the definition of the contract key's maintainers, and ensure this list won't be empty given your creation arguments."
)
object CreateEmptyContractKeyMaintainers
extends ErrorCode(
id = "CREATE_EMPTY_CONTRACT_KEY_MAINTAINERS",
ErrorCategory.InvalidIndependentOfSystemState,
) {
final case class Reject(
override val cause: String,
err: LfInterpretationError.CreateEmptyContractKeyMaintainers,
)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(
cause = cause
) {
override def resources: Seq[(ErrorResource, String)] =
withEncodedValue(err.arg) { encodedArg =>
Seq(
(ErrorResource.TemplateId, err.templateId.toString),
(ErrorResource.ContractArg, encodedArg),
)
}
}
}
@Explanation(
"""This error occurs when you try to fetch a contract by key, but that key would have empty maintainers."""
)
@Resolution(
"Check the definition of the contract key's maintainers, and ensure this list won't be empty given the contract key you are fetching."
)
object FetchEmptyContractKeyMaintainers
extends ErrorCode(
id = "FETCH_EMPTY_CONTRACT_KEY_MAINTAINERS",
ErrorCategory.InvalidIndependentOfSystemState,
) {
final case class Reject(
override val cause: String,
err: LfInterpretationError.FetchEmptyContractKeyMaintainers,
)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(
cause = cause
) {
override def resources: Seq[(ErrorResource, String)] =
withEncodedValue(err.key) { encodedKey =>
Seq(
(ErrorResource.TemplateId, err.templateId.toString),
(ErrorResource.ContractKey, encodedKey),
)
}
}
}
@Explanation(
"""This error occurs when you try to fetch/use a contract in some way with a contract ID that doesn't match the template type on the ledger."""
)
@Resolution(
"Ensure the contract IDs you are using are of the type we expect on the ledger. Avoid unsafely coercing contract IDs."
)
object WronglyTypedContract
extends ErrorCode(
id = "WRONGLY_TYPED_CONTRACT",
ErrorCategory.InvalidGivenCurrentSystemStateOther,
) {
final case class Reject(
override val cause: String,
err: LfInterpretationError.WronglyTypedContract,
)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(
cause = cause
) {
override def resources: Seq[(ErrorResource, String)] =
Seq(
(ErrorResource.ContractId, err.coid.coid),
(ErrorResource.TemplateId, err.expected.toString),
(ErrorResource.TemplateId, err.actual.toString),
)
}
}
@Explanation(
"""This error occurs when you try to coerce/use a contract via an interface that it does not implement."""
)
@Resolution(
"Ensure the contract you are calling does implement the interface you are using to do so. Avoid writing LF/low-level interface implementation classes manually."
)
object ContractDoesNotImplementInterface
extends ErrorCode(
id = "CONTRACT_DOES_NOT_IMPLEMENT_INTERFACE",
ErrorCategory.InvalidIndependentOfSystemState,
) {
final case class Reject(
override val cause: String,
err: LfInterpretationError.ContractDoesNotImplementInterface,
)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(
cause = cause
) {
override def resources: Seq[(ErrorResource, String)] =
Seq(
(ErrorResource.ContractId, err.coid.coid),
(ErrorResource.TemplateId, err.templateId.toString),
(ErrorResource.InterfaceId, err.interfaceId.toString),
)
}
}
@Explanation(
"""This error occurs when you try to create/use a contract that does not implement the requiring interfaces of some other interface that it does implement."""
)
@Resolution(
"Ensure you implement all required interfaces correctly, and avoid writing LF/low-level interface implementation classes manually."
)
object ContractDoesNotImplementRequiringInterface
extends ErrorCode(
id = "CONTRACT_DOES_NOT_IMPLEMENT_REQUIRING_INTERFACE",
ErrorCategory.InvalidIndependentOfSystemState,
) {
final case class Reject(
override val cause: String,
err: LfInterpretationError.ContractDoesNotImplementRequiringInterface,
)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(
cause = cause
) {
override def resources: Seq[(ErrorResource, String)] =
Seq(
(ErrorResource.ContractId, err.coid.coid),
(ErrorResource.TemplateId, err.templateId.toString),
(ErrorResource.InterfaceId, err.requiredInterfaceId.toString),
(ErrorResource.InterfaceId, err.requiringInterfaceId.toString),
)
}
}
@Explanation(
"""This error occurs when you attempt to compare two values of different types using the built-in comparison types."""
)
@Resolution(
"Avoid using the low level comparison build, and instead use the Eq class."
)
object NonComparableValues
extends ErrorCode(
id = "NON_COMPARABLE_VALUES",
ErrorCategory.InvalidIndependentOfSystemState,
) {
final case class Reject(
override val cause: String
)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(
cause = cause
) {}
}
@Explanation(
"""This error occurs when a contract key contains a contract ID, which is illegal for hashing reasons."""
)
@Resolution(
"Ensure your contracts key field cannot contain a contract ID."
)
object ContractIdInContractKey
extends ErrorCode(
id = "CONTRACT_ID_IN_CONTRACT_KEY",
ErrorCategory.InvalidIndependentOfSystemState,
) {
final case class Reject(
override val cause: String
)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(
cause = cause
) {}
}
@Explanation(
"""This error occurs when you attempt to compare a global and local contract ID of the same discriminator."""
)
@Resolution(
"Avoid constructing contract IDs manually."
)
object ContractIdComparability
extends ErrorCode(
id = "CONTRACT_ID_COMPARABILITY",
ErrorCategory.InvalidIndependentOfSystemState,
) {
final case class Reject(
override val cause: String,
err: LfInterpretationError.ContractIdComparability,
)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(
cause = cause
) {
override def resources: Seq[(ErrorResource, String)] =
Seq(
(ErrorResource.ContractId, err.globalCid.coid)
)
}
}
@Explanation("This error occurs when you nest values too deeply.")
@Resolution("Restructure your code and reduce value nesting.")
object ValueNesting
extends ErrorCode(id = "VALUE_NESTING", ErrorCategory.InvalidIndependentOfSystemState) {
final case class Reject(override val cause: String, err: LfInterpretationError.ValueNesting)(
implicit loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(cause = cause) {}
}
}
}

View File

@ -1,132 +0,0 @@
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.error.definitions.groups
import com.daml.error.definitions.{ChangeId, LedgerApiErrors}
import com.daml.error.{
ContextualizedErrorLogger,
DamlErrorWithDefiniteAnswer,
ErrorCategory,
ErrorCode,
ErrorGroup,
ErrorResource,
Explanation,
Resolution,
}
import com.daml.lf.value.Value
@Explanation(
"Potential consistency errors raised due to race conditions during command submission or returned as submission rejections by the backing ledger."
)
object ConsistencyErrors extends ErrorGroup()(LedgerApiErrors.errorClass) {
@Explanation("A command with the given command id has already been successfully processed.")
@Resolution(
"""The correct resolution depends on the use case. If the error received pertains to a submission retried due to a timeout,
|do nothing, as the previous command has already been accepted.
|If the intent is to submit a new command, re-submit using a distinct command id.
|"""
)
object DuplicateCommand
extends ErrorCode(
id = "DUPLICATE_COMMAND",
ErrorCategory.InvalidGivenCurrentSystemStateResourceExists,
) {
final case class Reject(
override val definiteAnswer: Boolean = false,
existingCommandSubmissionId: Option[String],
changeId: Option[ChangeId] = None,
)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(
cause = "A command with the given command id has already been successfully processed",
definiteAnswer = definiteAnswer,
) {
override def context: Map[String, String] =
super.context ++ existingCommandSubmissionId
.map("existing_submission_id" -> _)
.toList ++ changeId
.map(changeId => Seq("changeId" -> changeId.toString))
.getOrElse(Seq.empty)
}
}
@Explanation(
"""This error occurs if the Daml engine can not find a referenced contract. This
|can be caused by either the contract not being known to the participant, or not being known to
|the submitting parties or already being archived."""
)
@Resolution("This error type occurs if there is contention on a contract.")
object ContractNotFound
extends ErrorCode(
id = "CONTRACT_NOT_FOUND",
ErrorCategory.InvalidGivenCurrentSystemStateResourceMissing,
) {
final case class Reject(
override val cause: String,
cid: Value.ContractId,
)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(
cause = cause
) {
override def resources: Seq[(ErrorResource, String)] = Seq(
(ErrorResource.ContractId, cid.coid)
)
}
}
@Explanation(
"An input contract key was re-assigned to a different contract by a concurrent transaction submission."
)
@Resolution("Retry the transaction submission.")
object InconsistentContractKey
extends ErrorCode(
id = "INCONSISTENT_CONTRACT_KEY",
ErrorCategory.InvalidGivenCurrentSystemStateOther,
) {
final case class Reject(reason: String)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(cause = reason)
}
@Explanation(
"""This error signals that within the transaction we got to a point where two contracts with the same key were active."""
)
@Resolution("This error indicates an application error.")
object DuplicateContractKey
extends ErrorCode(
id = "DUPLICATE_CONTRACT_KEY",
ErrorCategory.InvalidGivenCurrentSystemStateResourceExists,
) {
final case class Reject(override val cause: String)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(cause = cause)
}
@Explanation(
"Another command submission with the same change ID (application ID, command ID, actAs) is already being processed."
)
@Resolution(
"""Listen to the command completion stream until a completion for the in-flight command submission is published.
|Alternatively, resubmit the command. If the in-flight submission has finished successfully by then,
|this will return more detailed information about the earlier one.
|If the in-flight submission has failed by then, the resubmission will attempt to record the new transaction on the ledger.
|"""
)
// This command deduplication error is currently used only by Canton.
// It is defined here so that the general command deduplication documentation can refer to it.
object SubmissionAlreadyInFlight
extends ErrorCode(
id = "SUBMISSION_ALREADY_IN_FLIGHT",
ErrorCategory.ContentionOnSharedResources,
)
}

View File

@ -1,101 +0,0 @@
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.error.definitions.groups
import com.daml.error.{DamlError, DamlErrorWithDefiniteAnswer, _}
object IdentityProviderConfigServiceErrorGroup
extends AdminServices.IdentityProviderConfigServiceErrorGroup {
@Explanation(
"There was an attempt to update an identity provider config using an invalid update request."
)
@Resolution(
"""|Inspect the error details for specific information on what made the request invalid.
|Retry with an adjusted update request."""
)
object InvalidUpdateIdentityProviderConfigRequest
extends ErrorCode(
id = "INVALID_IDENTITY_PROVIDER_UPDATE_REQUEST",
ErrorCategory.InvalidIndependentOfSystemState,
) {
case class Reject(identityProviderId: String, reason: String)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlError(
cause =
s"Update operation for identity provider config '$identityProviderId' failed due to: $reason"
) {
override def resources: Seq[(ErrorResource, String)] = Seq(
ErrorResource.IdentityProviderConfig -> identityProviderId
)
}
}
@Explanation("The identity provider config referred to by the request was not found.")
@Resolution(
"Check that you are connecting to the right participant node and the identity provider config is spelled correctly, or create the configuration."
)
object IdentityProviderConfigNotFound
extends ErrorCode(
id = "IDP_CONFIG_NOT_FOUND",
ErrorCategory.InvalidGivenCurrentSystemStateResourceMissing,
) {
case class Reject(operation: String, identityProviderId: String)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(
cause = s"${operation} failed for unknown identity provider id=\"${identityProviderId}\""
) {
override def resources: Seq[(ErrorResource, String)] = Seq(
ErrorResource.IdentityProviderConfig -> identityProviderId
)
}
}
@Explanation(
"There already exists an identity provider configuration with the same identity provider id."
)
@Resolution(
"Check that you are connecting to the right participant node and the identity provider id is spelled correctly, or use an identity provider that already exists."
)
object IdentityProviderConfigAlreadyExists
extends ErrorCode(
id = "IDP_CONFIG_ALREADY_EXISTS",
ErrorCategory.InvalidGivenCurrentSystemStateResourceExists,
) {
case class Reject(operation: String, identityProviderId: String)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(
cause =
s"${operation} failed, as identity provider \"${identityProviderId}\" already exists"
) {
override def resources: Seq[(ErrorResource, String)] = Seq(
ErrorResource.IdentityProviderConfig -> identityProviderId
)
}
}
@Explanation(
"There already exists an identity provider configuration with the same issuer."
)
@Resolution(
"Check that you are connecting to the right participant node and the identity provider id is spelled correctly, or use an identity provider that already exists."
)
object IdentityProviderConfigIssuerAlreadyExists
extends ErrorCode(
id = "IDP_CONFIG_ISSUER_ALREADY_EXISTS",
ErrorCategory.InvalidGivenCurrentSystemStateResourceExists,
) {
case class Reject(operation: String, issuer: String)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(
cause =
s"${operation} failed, as identity provider with issuer \"${issuer}\" already exists"
) {
override def resources: Seq[(ErrorResource, String)] = Seq(
ErrorResource.IdentityProviderConfig -> issuer
)
}
}
}

View File

@ -1,86 +0,0 @@
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.error.definitions.groups
import com.daml.error.{
ContextualizedErrorLogger,
DamlError,
DamlErrorWithDefiniteAnswer,
ErrorCategory,
ErrorCode,
ErrorResource,
Explanation,
Resolution,
}
object PartyManagementServiceErrorGroup extends AdminServices.PartyManagementServiceErrorGroup {
@Explanation("There was an attempt to update a party using an invalid update request.")
@Resolution(
"""|Inspect the error details for specific information on what made the request invalid.
|Retry with an adjusted update request."""
)
object InvalidUpdatePartyDetailsRequest
extends ErrorCode(
id = "INVALID_PARTY_DETAILS_UPDATE_REQUEST",
ErrorCategory.InvalidIndependentOfSystemState,
) {
case class Reject(party: String, reason: String)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlError(
cause = s"Update operation for party '$party' failed due to: $reason"
) {
override def resources: Seq[(ErrorResource, String)] = Seq(
ErrorResource.Party -> party
)
}
}
@Explanation(
"""|Concurrent updates to a party can be controlled by supplying an update request with a resource version (this is optional).
|A party's resource version can be obtained by reading the party on the Ledger API.
|There was attempt to update a party using a stale resource version, indicating that a different process had updated the party earlier."""
)
@Resolution(
"""|Read this party again to obtain its most recent state and
|in particular its most recent resource version. Use the obtained information to build and send a new update request."""
)
object ConcurrentPartyDetailsUpdateDetected
extends ErrorCode(
id = "CONCURRENT_PARTY_DETAILS_UPDATE_DETECTED",
ErrorCategory.ContentionOnSharedResources,
) {
case class Reject(party: String)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlError(
cause =
s"Update operation for party '$party' failed due to a concurrent update to the same party"
) {
override def resources: Seq[(ErrorResource, String)] = Seq(
ErrorResource.Party -> party
)
}
}
@Explanation("The party referred to by the request was not found.")
@Resolution(
"Check that you are connecting to the right participant node and that the party is spelled correctly."
)
object PartyNotFound
extends ErrorCode(
id = "PARTY_NOT_FOUND",
ErrorCategory.InvalidGivenCurrentSystemStateResourceMissing,
) {
case class Reject(operation: String, party: String)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(
cause = s"Party: '$party' was not found when $operation"
) {
override def resources: Seq[(ErrorResource, String)] = Seq(
ErrorResource.Party -> party
)
}
}
}

View File

@ -1,280 +0,0 @@
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.error.definitions.groups
import java.time.Duration
import com.daml.error.definitions.LedgerApiErrors.EarliestOffsetMetadataKey
import com.daml.error.definitions.{LedgerApiErrors}
import com.daml.error.{
ContextualizedErrorLogger,
DamlError,
DamlErrorWithDefiniteAnswer,
ErrorCategory,
ErrorCode,
ErrorGroup,
ErrorResource,
Explanation,
Resolution,
}
import com.daml.lf.data.Ref.Identifier
@Explanation(
"Validation errors raised when evaluating requests in the Ledger API."
)
object RequestValidation extends LedgerApiErrors.RequestValidation {
object NotFound extends ErrorGroup() {
@Explanation(
"This rejection is given when a read request tries to access a package which does not exist on the ledger."
)
@Resolution("Use a package id pertaining to a package existing on the ledger.")
object Package
extends ErrorCode(
id = "PACKAGE_NOT_FOUND",
ErrorCategory.InvalidGivenCurrentSystemStateResourceMissing,
) {
case class Reject(packageId: String)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(
cause = "Could not find package."
) {
override def resources: Seq[(ErrorResource, String)] = {
super.resources :+ ((ErrorResource.DalfPackage, packageId))
}
}
}
@Explanation(
"The transaction does not exist or the requesting set of parties are not authorized to fetch it."
)
@Resolution(
"Check the transaction id and verify that the requested transaction is visible to the requesting parties."
)
object Transaction
extends ErrorCode(
id = "TRANSACTION_NOT_FOUND",
ErrorCategory.InvalidGivenCurrentSystemStateResourceMissing,
) {
case class Reject(transactionId: String)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(cause = "Transaction not found, or not visible.") {
override def resources: Seq[(ErrorResource, String)] = Seq(
(ErrorResource.TransactionId, transactionId)
)
}
}
@Explanation(
"The ledger configuration could not be retrieved. This could happen due to incomplete initialization of the participant or due to an internal system error."
)
@Resolution("Contact the participant operator.")
object LedgerConfiguration
extends ErrorCode(
id = "LEDGER_CONFIGURATION_NOT_FOUND",
ErrorCategory.InvalidGivenCurrentSystemStateResourceMissing,
) {
case class Reject()(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(
cause = "The ledger configuration could not be retrieved."
)
}
@Explanation(
"The queried template or interface ids do not exist."
)
@Resolution(
"Use valid template or interface ids in your query or ask the participant operator to upload the package containing the necessary interfaces/templates."
)
object TemplateOrInterfaceIdsNotFound
extends ErrorCode(
id = "TEMPLATES_OR_INTERFACES_NOT_FOUND",
ErrorCategory.InvalidGivenCurrentSystemStateResourceMissing,
) {
private def buildCause(
unknownTemplatesOrInterfaces: Seq[Either[Identifier, Identifier]]
): String = {
val unknownTemplateIds =
unknownTemplatesOrInterfaces.collect { case Left(id) => id.toString }
val unknownInterfaceIds =
unknownTemplatesOrInterfaces.collect { case Right(id) => id.toString }
val templatesMessage = if (unknownTemplateIds.nonEmpty) {
s"Templates do not exist: [${unknownTemplateIds.mkString(", ")}]. "
} else ""
val interfacesMessage = if (unknownInterfaceIds.nonEmpty) {
s"Interfaces do not exist: [${unknownInterfaceIds.mkString(", ")}]. "
} else
""
(templatesMessage + interfacesMessage).trim
}
case class Reject(unknownTemplatesOrInterfaces: Seq[Either[Identifier, Identifier]])(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(cause = buildCause(unknownTemplatesOrInterfaces)) {
override def resources: Seq[(ErrorResource, String)] =
unknownTemplatesOrInterfaces.map {
case Left(templateId) => ErrorResource.TemplateId -> templateId.toString
case Right(interfaceId) => ErrorResource.InterfaceId -> interfaceId.toString
}
}
}
}
@Explanation("This rejection is given when a read request tries to access pruned data.")
@Resolution("Use an offset that is after the pruning offset.")
object ParticipantPrunedDataAccessed
extends ErrorCode(
id = "PARTICIPANT_PRUNED_DATA_ACCESSED",
ErrorCategory.InvalidGivenCurrentSystemStateOther,
) {
case class Reject(override val cause: String, earliestOffset: String)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(
cause = cause,
extraContext = Map(EarliestOffsetMetadataKey -> earliestOffset),
)
}
@Explanation(
"This rejection is given when a read request uses an offset beyond the current ledger end."
)
@Resolution("Use an offset that is before the ledger end.")
object OffsetAfterLedgerEnd
extends ErrorCode(
id = "OFFSET_AFTER_LEDGER_END",
ErrorCategory.InvalidGivenCurrentSystemStateSeekAfterEnd,
) {
case class Reject(offsetType: String, requestedOffset: String, ledgerEnd: String)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(
cause = s"${offsetType} offset (${requestedOffset}) is after ledger end (${ledgerEnd})"
)
}
@Explanation(
"This rejection is given when a read request uses an offset invalid in the requests' context."
)
@Resolution("Inspect the error message and use a valid offset.")
object OffsetOutOfRange
extends ErrorCode(
id = "OFFSET_OUT_OF_RANGE",
ErrorCategory.InvalidGivenCurrentSystemStateOther,
) {
case class Reject(message: String)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(cause = message)
}
@Explanation(
"""Every ledger API command contains a ledger-id which is verified against the running ledger.
This error indicates that the provided ledger-id does not match the expected one."""
)
@Resolution("Ensure that your application is correctly configured to use the correct ledger.")
object LedgerIdMismatch
extends ErrorCode(
id = "LEDGER_ID_MISMATCH",
ErrorCategory.InvalidGivenCurrentSystemStateResourceMissing,
) {
case class Reject(expectedLedgerId: String, receivedLegerId: String)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(
cause =
s"Ledger ID '${receivedLegerId}' not found. Actual Ledger ID is '${expectedLedgerId}'.",
definiteAnswer = true,
)
}
@Explanation(
"""This error is emitted when a mandatory field is not set in a submitted ledger API command."""
)
@Resolution("Inspect the reason given and correct your application.")
object MissingField
extends ErrorCode(id = "MISSING_FIELD", ErrorCategory.InvalidIndependentOfSystemState) {
case class Reject(missingField: String)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(
cause = s"The submitted command is missing a mandatory field: ${missingField}",
extraContext = Map("field_name" -> missingField),
)
}
@Explanation(
"""This error is emitted when a submitted ledger API command contains an invalid argument."""
)
@Resolution("Inspect the reason given and correct your application.")
object InvalidArgument
extends ErrorCode(id = "INVALID_ARGUMENT", ErrorCategory.InvalidIndependentOfSystemState) {
case class Reject(reason: String)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(
// TODO um-for-hub: Update the cause to mention a 'request' instead of a 'command'
cause = s"The submitted command has invalid arguments: ${reason}"
)
}
@Explanation(
"""This error is emitted when a submitted ledger API command contains a field value that cannot be understood."""
)
@Resolution("Inspect the reason given and correct your application.")
object InvalidField
extends ErrorCode(id = "INVALID_FIELD", ErrorCategory.InvalidIndependentOfSystemState) {
case class Reject(fieldName: String, message: String)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(
cause =
s"The submitted command has a field with invalid value: Invalid field ${fieldName}: ${message}"
)
}
@Explanation(
"This error is emitted when a submitted ledger API command specifies an invalid deduplication period."
)
@Resolution(
"Inspect the error message, adjust the value of the deduplication period or ask the participant operator to increase the maximum deduplication period."
)
object InvalidDeduplicationPeriodField
extends ErrorCode(
id = "INVALID_DEDUPLICATION_PERIOD",
ErrorCategory.InvalidGivenCurrentSystemStateOther,
) {
val ValidMaxDeduplicationFieldKey = "longest_duration"
case class Reject(
reason: String,
maxDeduplicationDuration: Option[Duration],
)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(
cause = s"The submitted command had an invalid deduplication period: ${reason}"
) {
override def context: Map[String, String] = {
super.context ++ maxDeduplicationDuration
.map(ValidMaxDeduplicationFieldKey -> _.toString)
.toList
}
}
}
@Explanation("""The supplied offset could not be converted to a binary offset.""")
@Resolution("Ensure the offset is specified as a hexadecimal string.")
object NonHexOffset
extends ErrorCode(
id = "NON_HEXADECIMAL_OFFSET",
ErrorCategory.InvalidIndependentOfSystemState,
) {
case class Error(
fieldName: String,
offsetValue: String,
message: String,
)(implicit
val loggingContext: ContextualizedErrorLogger
) extends DamlError(
cause = s"Offset in ${fieldName} not specified in hexadecimal: ${offsetValue}: ${message}"
)
}
}

View File

@ -1,129 +0,0 @@
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.error.definitions.groups
import com.daml.error.{
ContextualizedErrorLogger,
DamlError,
DamlErrorWithDefiniteAnswer,
ErrorCategory,
ErrorCode,
ErrorResource,
Explanation,
Resolution,
}
object UserManagementServiceErrorGroup extends AdminServices.UserManagementServiceErrorGroup {
@Explanation("There was an attempt to update a user using an invalid update request.")
@Resolution(
"""|Inspect the error details for specific information on what made the request invalid.
|Retry with an adjusted update request."""
)
object InvalidUpdateUserRequest
extends ErrorCode(
id = "INVALID_USER_UPDATE_REQUEST",
ErrorCategory.InvalidIndependentOfSystemState,
) {
case class Reject(userId: String, reason: String)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlError(
cause = s"Update operation for user id '$userId' failed due to: $reason"
) {
override def resources: Seq[(ErrorResource, String)] = Seq(
ErrorResource.User -> userId
)
}
}
@Explanation(
"""|Concurrent updates to a user can be controlled by supplying an update request with a resource version (this is optional).
|A user's resource version can be obtained by reading the user on the Ledger API.
|There was attempt to update a user using a stale resource version, indicating that a different process had updated the user earlier."""
)
@Resolution(
"""|Read this user again to obtain its most recent state and
|in particular its most recent resource version. Use the obtained information to build and send a new update request."""
)
object ConcurrentUserUpdateDetected
extends ErrorCode(
id = "CONCURRENT_USER_UPDATE_DETECTED",
ErrorCategory.ContentionOnSharedResources,
) {
case class Reject(userId: String)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlError(
cause =
s"Update operation for user '$userId' failed due to a concurrent update to the same user"
) {
override def resources: Seq[(ErrorResource, String)] = Seq(
ErrorResource.User -> userId
)
}
}
@Explanation("The user referred to by the request was not found.")
@Resolution(
"Check that you are connecting to the right participant node and the user-id is spelled correctly, if yes, create the user."
)
object UserNotFound
extends ErrorCode(
id = "USER_NOT_FOUND",
ErrorCategory.InvalidGivenCurrentSystemStateResourceMissing,
) {
case class Reject(operation: String, userId: String)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(
cause = s"${operation} failed for unknown user \"${userId}\""
) {
override def resources: Seq[(ErrorResource, String)] = Seq(
ErrorResource.User -> userId
)
}
}
@Explanation("There already exists a user with the same user-id.")
@Resolution(
"Check that you are connecting to the right participant node and the user-id is spelled correctly, or use the user that already exists."
)
object UserAlreadyExists
extends ErrorCode(
id = "USER_ALREADY_EXISTS",
ErrorCategory.InvalidGivenCurrentSystemStateResourceExists,
) {
case class Reject(operation: String, userId: String)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(
cause = s"${operation} failed, as user \"${userId}\" already exists"
) {
override def resources: Seq[(ErrorResource, String)] = Seq(
ErrorResource.User -> userId
)
}
}
@Explanation(
"""|A user can have only a limited number of user rights.
|There was an attempt to create a user with too many rights or grant too many rights to a user."""
)
@Resolution(
"""|Retry with a smaller number of rights or delete some of the already existing rights of this user.
|Contact the participant operator if the limit is too low."""
)
object TooManyUserRights
extends ErrorCode(
id = "TOO_MANY_USER_RIGHTS",
ErrorCategory.InvalidGivenCurrentSystemStateOther,
) {
case class Reject(operation: String, userId: String)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(
cause = s"${operation} failed, as user \"${userId}\" would have too many rights."
) {
override def resources: Seq[(ErrorResource, String)] = Seq(
ErrorResource.User -> userId
)
}
}
}

View File

@ -1,88 +0,0 @@
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.error.definitions.groups
import com.daml.error.definitions.LedgerApiErrors
import com.daml.error.{
ContextualizedErrorLogger,
DamlErrorWithDefiniteAnswer,
ErrorCategory,
ErrorCode,
ErrorResource,
Explanation,
Resolution,
}
@Explanation(
"Generic submission rejection errors returned by the backing ledger's write service."
)
object WriteServiceRejections extends LedgerApiErrors.WriteServiceRejections {
@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 transaction submission."
)
object PartyNotKnownOnLedger
extends ErrorCode(
id = "PARTY_NOT_KNOWN_ON_LEDGER",
ErrorCategory.InvalidGivenCurrentSystemStateResourceMissing,
) {
case class Reject(parties: Set[String])(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(cause = s"Parties not known on ledger: ${parties
.mkString("[", ",", "]")}") {
override def resources: Seq[(ErrorResource, String)] =
parties.map((ErrorResource.Party, _)).toSeq
}
@deprecated(
"Corresponds to transaction submission rejections that are not produced anymore.",
since = "1.18.0",
)
case class RejectDeprecated(
description: String
)(implicit loggingContext: ContextualizedErrorLogger)
extends DamlErrorWithDefiniteAnswer(
cause = s"Party not known on ledger: $description"
)
}
@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.",
since = "1.18.0",
)
object Disputed
extends ErrorCode(
id = "DISPUTED",
ErrorCategory.SystemInternalAssumptionViolated, // It should have been caught by the participant
) {
case class Reject(
details: String
)(implicit loggingContext: ContextualizedErrorLogger)
extends DamlErrorWithDefiniteAnswer(
cause = s"Disputed: $details"
)
}
@Explanation(
"The Participant node did not have sufficient resource quota to submit the transaction."
)
@Resolution(
"Inspect the error message and retry after after correcting the underlying issue."
)
@deprecated(
"Corresponds to transaction submission rejections that are not produced anymore.",
since = "1.18.0",
)
object OutOfQuota
extends ErrorCode(id = "OUT_OF_QUOTA", ErrorCategory.ContentionOnSharedResources) {
case class Reject(reason: String)(implicit
loggingContext: ContextualizedErrorLogger
) extends DamlErrorWithDefiniteAnswer(cause = reason)
}
}