mirror of
https://github.com/digital-asset/daml.git
synced 2024-09-20 01:07:18 +03:00
Port error code changes (#11113)
* Port changes from Canton PR #7365 CHANGELOG_BEGIN CHANGELOG_END * Ported ErrorResource changes * Ported constant use change * Fix compilation issues * Missing dependency on ledger-grpc * Compilation issue SI-4440 in RejectionGenerators * Re-review
This commit is contained in:
parent
9fd8182bbb
commit
31db15d555
@ -21,18 +21,18 @@ object ErrorResource {
|
||||
def fromString(str: String): Option[ErrorResource] = all.find(_.asString == str)
|
||||
|
||||
object ContractId extends ErrorResource {
|
||||
def asString: String = "contract-id"
|
||||
def asString: String = "CONTRACT_ID"
|
||||
}
|
||||
object ContractKey extends ErrorResource {
|
||||
def asString: String = "contract-key"
|
||||
def asString: String = "CONTRACT_KEY"
|
||||
}
|
||||
object DalfPackage extends ErrorResource {
|
||||
def asString: String = "lf-package"
|
||||
def asString: String = "PACKAGE"
|
||||
}
|
||||
object LedgerId extends ErrorResource {
|
||||
def asString: String = "ledger-id"
|
||||
def asString: String = "LEDGER_ID"
|
||||
}
|
||||
object CommandId extends ErrorResource {
|
||||
def asString: String = "command-id"
|
||||
def asString: String = "COMMAND_ID"
|
||||
}
|
||||
}
|
||||
|
@ -52,6 +52,7 @@ compile_deps = [
|
||||
"//ledger/ledger-api-domain",
|
||||
"//ledger/ledger-api-health",
|
||||
"//ledger/ledger-configuration",
|
||||
"//ledger/ledger-grpc",
|
||||
"//ledger/ledger-offset",
|
||||
"//ledger/ledger-resources",
|
||||
"//ledger/metrics",
|
||||
|
@ -273,8 +273,27 @@ object LedgerApiErrors extends LedgerApiErrorGroup {
|
||||
object GenericInterpretationError
|
||||
extends ErrorCode(
|
||||
id = "DAML_INTERPRETATION_ERROR",
|
||||
// TODO error codes: this is a bad error message and needs to be fixed by also adjusting the expected ledger-api conformance tests
|
||||
ErrorCategory.InvalidIndependentOfSystemState, // (is INVALID_ARGUMENT, should be PRECONDITION_FAILED)
|
||||
ErrorCategory.InvalidGivenCurrentSystemStateOther,
|
||||
) {
|
||||
|
||||
case class Error(override val cause: String)(implicit
|
||||
loggingContext: LoggingContext,
|
||||
logger: ContextualizedLogger,
|
||||
correlationId: CorrelationId,
|
||||
) extends LoggingTransactionErrorImpl(
|
||||
cause = cause
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
@Explanation(
|
||||
"""This error occurs if the Daml transaction failed during interpretation due to an invalid argument."""
|
||||
)
|
||||
@Resolution("This error type occurs if there is an application error.")
|
||||
object InvalidArgumentInterpretationError
|
||||
extends ErrorCode(
|
||||
id = "DAML_INTERPRETER_INVALID_ARGUMENT",
|
||||
ErrorCategory.InvalidIndependentOfSystemState,
|
||||
) {
|
||||
|
||||
case class Error(override val cause: String)(implicit
|
||||
@ -292,10 +311,9 @@ object LedgerApiErrors extends LedgerApiErrorGroup {
|
||||
)
|
||||
@Resolution("This error indicates an application error.")
|
||||
object ContractNotActive
|
||||
// Is INVALID_ARGUMENT, SHOULD BE NOT_EXISTS
|
||||
extends ErrorCode(
|
||||
id = "CONTRACT_NOT_ACTIVE",
|
||||
ErrorCategory.InvalidIndependentOfSystemState,
|
||||
ErrorCategory.InvalidGivenCurrentSystemStateResourceMissing,
|
||||
) {
|
||||
|
||||
case class Reject(
|
||||
@ -351,8 +369,7 @@ object LedgerApiErrors extends LedgerApiErrorGroup {
|
||||
object ContractNotFound
|
||||
extends ErrorCode(
|
||||
id = "CONTRACT_NOT_FOUND",
|
||||
// TODO error codes: this is a bad error message and needs to be fixed by also adjusting the expected ledger-api conformance tests
|
||||
ErrorCategory.IsAbortShouldBePrecondition, // IS ABORTED, should be NOT_EXISTS
|
||||
ErrorCategory.InvalidGivenCurrentSystemStateResourceMissing,
|
||||
) {
|
||||
|
||||
case class Reject(
|
||||
@ -381,8 +398,7 @@ object LedgerApiErrors extends LedgerApiErrorGroup {
|
||||
object ContractKeyNotFound
|
||||
extends ErrorCode(
|
||||
id = "CONTRACT_KEY_NOT_FOUND",
|
||||
// TODO error codes: this is a bad error message and needs to be fixed by also adjusting the expected ledger-api conformance tests
|
||||
ErrorCategory.InvalidIndependentOfSystemState, // IS INVALID_ARGUMENT, should be NOT_EXISTS
|
||||
ErrorCategory.InvalidGivenCurrentSystemStateResourceMissing,
|
||||
) {
|
||||
|
||||
case class Reject(
|
||||
|
@ -3,26 +3,56 @@
|
||||
|
||||
package com.daml.platform.apiserver.error
|
||||
|
||||
import com.daml.error.BaseError
|
||||
import com.daml.error.{BaseError, ErrorCode}
|
||||
import com.daml.ledger.participant.state
|
||||
import com.daml.lf.engine.Error.{Interpretation, Package, Preprocessing, Validation}
|
||||
import com.daml.lf.engine.{Error => LfError}
|
||||
import com.daml.lf.interpretation.{Error => LfInterpretationError}
|
||||
import com.daml.logging.{ContextualizedLogger, LoggingContext}
|
||||
import com.daml.platform.apiserver.error.RejectionGenerators.ErrorCauseExport
|
||||
import com.daml.platform.store.ErrorCause
|
||||
import io.grpc.Status.Code
|
||||
import io.grpc.StatusRuntimeException
|
||||
import io.grpc.protobuf.StatusProto
|
||||
|
||||
import scala.util.{Failure, Success, Try}
|
||||
|
||||
object RejectionGenerators {
|
||||
class RejectionGenerators(conformanceMode: Boolean) {
|
||||
private val adjustErrors = Map(
|
||||
LedgerApiErrors.InterpreterErrors.LookupErrors.ContractKeyNotFound -> Code.INVALID_ARGUMENT,
|
||||
LedgerApiErrors.InterpreterErrors.ContractNotActive -> Code.INVALID_ARGUMENT,
|
||||
LedgerApiErrors.InterpreterErrors.LookupErrors.ContractNotFound -> Code.ABORTED,
|
||||
LedgerApiErrors.InterpreterErrors.LookupErrors.ContractKeyNotFound -> Code.INVALID_ARGUMENT,
|
||||
LedgerApiErrors.InterpreterErrors.GenericInterpretationError -> Code.INVALID_ARGUMENT,
|
||||
)
|
||||
|
||||
private def enforceConformance(ex: StatusRuntimeException): StatusRuntimeException =
|
||||
if (!conformanceMode) ex
|
||||
else {
|
||||
adjustErrors
|
||||
.find { case (k, _) =>
|
||||
ex.getStatus.getDescription.startsWith(k.id + "(")
|
||||
}
|
||||
.fold(ex) { case (_, newGrpcCode) =>
|
||||
val parsed = StatusProto.fromThrowable(ex)
|
||||
// rewrite status to use "conformance" code
|
||||
val bld = com.google.rpc.Status
|
||||
.newBuilder()
|
||||
.setCode(newGrpcCode.value())
|
||||
.setMessage(parsed.getMessage)
|
||||
.addAllDetails(parsed.getDetailsList)
|
||||
val newEx = StatusProto.toStatusRuntimeException(bld.build())
|
||||
// strip stack trace from exception
|
||||
new ErrorCode.ApiException(newEx.getStatus, newEx.getTrailers)
|
||||
}
|
||||
}
|
||||
|
||||
def toGrpc(reject: BaseError)(implicit
|
||||
logger: ContextualizedLogger,
|
||||
loggingContext: LoggingContext,
|
||||
correlationId: CorrelationId,
|
||||
): StatusRuntimeException =
|
||||
reject.asGrpcErrorFromContext(correlationId.id, logger)(loggingContext)
|
||||
enforceConformance(reject.asGrpcErrorFromContext(correlationId.id, logger)(loggingContext))
|
||||
|
||||
def duplicateCommand(implicit
|
||||
logger: ContextualizedLogger,
|
||||
@ -66,6 +96,7 @@ object RejectionGenerators {
|
||||
def processDamlException(
|
||||
err: com.daml.lf.interpretation.Error,
|
||||
renderedMessage: String,
|
||||
detailMessage: Option[String],
|
||||
): BaseError = {
|
||||
// detailMessage is only suitable for server side debugging but not for the user, so don't pass except on internal errors
|
||||
|
||||
@ -85,25 +116,41 @@ object RejectionGenerators {
|
||||
case LfInterpretationError.DuplicateContractKey(key) =>
|
||||
LedgerApiErrors.InterpreterErrors.DuplicateContractKey.Reject(renderedMessage, key)
|
||||
case _: LfInterpretationError.UnhandledException =>
|
||||
LedgerApiErrors.InterpreterErrors.GenericInterpretationError.Error(renderedMessage)
|
||||
LedgerApiErrors.InterpreterErrors.GenericInterpretationError.Error(
|
||||
renderedMessage + detailMessage.fold("")(x => ". Details: " + x)
|
||||
)
|
||||
case _: LfInterpretationError.UserError =>
|
||||
LedgerApiErrors.InterpreterErrors.GenericInterpretationError.Error(renderedMessage)
|
||||
case _: LfInterpretationError.TemplatePreconditionViolated =>
|
||||
LedgerApiErrors.InterpreterErrors.GenericInterpretationError.Error(renderedMessage)
|
||||
case _: LfInterpretationError.CreateEmptyContractKeyMaintainers =>
|
||||
LedgerApiErrors.InterpreterErrors.GenericInterpretationError.Error(renderedMessage)
|
||||
LedgerApiErrors.InterpreterErrors.InvalidArgumentInterpretationError.Error(
|
||||
renderedMessage
|
||||
)
|
||||
case _: LfInterpretationError.FetchEmptyContractKeyMaintainers =>
|
||||
LedgerApiErrors.InterpreterErrors.GenericInterpretationError.Error(renderedMessage)
|
||||
LedgerApiErrors.InterpreterErrors.InvalidArgumentInterpretationError.Error(
|
||||
renderedMessage
|
||||
)
|
||||
case _: LfInterpretationError.WronglyTypedContract =>
|
||||
LedgerApiErrors.InterpreterErrors.GenericInterpretationError.Error(renderedMessage)
|
||||
LedgerApiErrors.InterpreterErrors.InvalidArgumentInterpretationError.Error(
|
||||
renderedMessage
|
||||
)
|
||||
case LfInterpretationError.NonComparableValues =>
|
||||
LedgerApiErrors.InterpreterErrors.GenericInterpretationError.Error(renderedMessage)
|
||||
LedgerApiErrors.InterpreterErrors.InvalidArgumentInterpretationError.Error(
|
||||
renderedMessage
|
||||
)
|
||||
case _: LfInterpretationError.ContractIdInContractKey =>
|
||||
LedgerApiErrors.InterpreterErrors.GenericInterpretationError.Error(renderedMessage)
|
||||
LedgerApiErrors.InterpreterErrors.InvalidArgumentInterpretationError.Error(
|
||||
renderedMessage
|
||||
)
|
||||
case LfInterpretationError.ValueExceedsMaxNesting =>
|
||||
LedgerApiErrors.InterpreterErrors.GenericInterpretationError.Error(renderedMessage)
|
||||
LedgerApiErrors.InterpreterErrors.InvalidArgumentInterpretationError.Error(
|
||||
renderedMessage
|
||||
)
|
||||
case _: LfInterpretationError.ContractIdComparability =>
|
||||
LedgerApiErrors.InterpreterErrors.GenericInterpretationError.Error(renderedMessage)
|
||||
LedgerApiErrors.InterpreterErrors.InvalidArgumentInterpretationError.Error(
|
||||
renderedMessage
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -114,7 +161,8 @@ object RejectionGenerators {
|
||||
err match {
|
||||
case Interpretation.Internal(location, message) =>
|
||||
LedgerApiErrors.InternalError.Interpretation(location, message, detailMessage)
|
||||
case m @ Interpretation.DamlException(error) => processDamlException(error, m.message)
|
||||
case m @ Interpretation.DamlException(error) =>
|
||||
processDamlException(error, m.message, detailMessage)
|
||||
}
|
||||
|
||||
def processLfError(error: LfError) = {
|
||||
@ -178,7 +226,9 @@ object RejectionGenerators {
|
||||
reject
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object RejectionGenerators {
|
||||
sealed trait ErrorCauseExport
|
||||
object ErrorCauseExport {
|
||||
final case class DamlLf(error: LfError) extends ErrorCauseExport
|
||||
|
@ -11,9 +11,20 @@ import com.daml.ledger.participant.state.v2.Update.CommandRejected.{
|
||||
}
|
||||
import com.daml.logging.{ContextualizedLogger, LoggingContext}
|
||||
import com.google.rpc.status.{Status => RpcStatus}
|
||||
import io.grpc.StatusRuntimeException
|
||||
import io.grpc.{Status, StatusRuntimeException}
|
||||
|
||||
trait TransactionError extends BaseError {
|
||||
@Deprecated
|
||||
def createRejectionDeprecated(
|
||||
rewrite: Map[ErrorCode, Status.Code]
|
||||
)(implicit
|
||||
logger: ContextualizedLogger,
|
||||
loggingContext: LoggingContext,
|
||||
correlationId: Option[String],
|
||||
): RejectionReasonTemplate = {
|
||||
FinalReason(_rpcStatus(rewrite.get(this.code), correlationId))
|
||||
}
|
||||
|
||||
def createRejection(
|
||||
correlationId: Option[String]
|
||||
)(implicit
|
||||
@ -30,6 +41,12 @@ trait TransactionError extends BaseError {
|
||||
|
||||
def rpcStatus(
|
||||
correlationId: Option[String]
|
||||
)(implicit logger: ContextualizedLogger, loggingContext: LoggingContext): RpcStatus =
|
||||
_rpcStatus(None, correlationId)
|
||||
|
||||
def _rpcStatus(
|
||||
overrideCode: Option[Status.Code],
|
||||
correlationId: Option[String],
|
||||
)(implicit logger: ContextualizedLogger, loggingContext: LoggingContext): RpcStatus = {
|
||||
|
||||
// yes, this is a horrible duplication of ErrorCode.asGrpcError. why? because
|
||||
@ -38,16 +55,10 @@ trait TransactionError extends BaseError {
|
||||
// objects. however, the sync-api uses the scala variant whereas we have to return StatusRuntimeExceptions.
|
||||
// therefore, we have to compose the status code a second time here ...
|
||||
// the ideal fix would be to extend scalapb accordingly ...
|
||||
val ErrorCode.StatusInfo(codeGrpc, messageWithoutContext, contextMap, _) =
|
||||
val ErrorCode.StatusInfo(codeGrpc, message, contextMap, _) =
|
||||
code.getStatusInfo(this, correlationId, logger)(loggingContext)
|
||||
|
||||
// TODO error codes: avoid appending the context to the description. right now, we need to do that as the ledger api server is throwing away any error details
|
||||
val message =
|
||||
if (code.category.securitySensitive) messageWithoutContext
|
||||
else messageWithoutContext + "; " + code.formatContextAsString(contextMap)
|
||||
|
||||
val definiteAnswerKey =
|
||||
"definite_answer" // TODO error codes: Can we use a constant from some upstream class?
|
||||
val definiteAnswerKey = com.daml.ledger.grpc.GrpcStatuses.DefiniteAnswerKey
|
||||
|
||||
val metadata = if (code.category.securitySensitive) Map.empty[String, String] else contextMap
|
||||
val errorInfo = com.google.rpc.error_details.ErrorInfo(
|
||||
@ -82,7 +93,7 @@ trait TransactionError extends BaseError {
|
||||
) ++ retryInfoO.toList ++ requestInfoO.toList ++ resourceInfos
|
||||
|
||||
com.google.rpc.status.Status(
|
||||
codeGrpc.value(),
|
||||
overrideCode.getOrElse(codeGrpc).value(),
|
||||
message,
|
||||
details,
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user