Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Port error code changes #11113

Merged
merged 5 commits into from
Oct 5, 2021
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions ledger/error/src/main/scala/com/daml/error/ErrorResource.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
1 change: 1 addition & 0 deletions ledger/participant-integration-api/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,27 @@ object LedgerApiErrors extends LedgerApiErrorGroup {
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
Expand All @@ -292,10 +312,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(
Expand Down Expand Up @@ -351,8 +370,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(
Expand Down Expand Up @@ -381,8 +399,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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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

Expand All @@ -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
)
}
}

Expand All @@ -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) = {
Expand Down Expand Up @@ -178,7 +226,9 @@ object RejectionGenerators {
reject
}
}
}

object RejectionGenerators {
sealed trait ErrorCauseExport
object ErrorCauseExport {
final case class DamlLf(error: LfError) extends ErrorCauseExport
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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(
Expand Down Expand Up @@ -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,
)
Expand Down