Skip to content

Commit

Permalink
kvutils: Add structured error data to the error metadata [KVL-1032] (#…
Browse files Browse the repository at this point in the history
…10964)

CHANGELOG_BEGIN
CHANGELOG_END
  • Loading branch information
hubert-da authored Sep 22, 2021
1 parent 3f4dbf6 commit f56ce2a
Show file tree
Hide file tree
Showing 3 changed files with 210 additions and 55 deletions.
4 changes: 4 additions & 0 deletions ledger/participant-state/kvutils/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ da_scala_library(
"//libs-scala/resources",
"//libs-scala/resources-akka",
"//libs-scala/resources-grpc",
"@maven//:com_fasterxml_jackson_core_jackson_core",
"@maven//:com_fasterxml_jackson_core_jackson_databind",
"@maven//:com_google_guava_guava",
"@maven//:com_google_protobuf_protobuf_java",
"@maven//:io_dropwizard_metrics_metrics_core",
Expand Down Expand Up @@ -198,6 +200,8 @@ da_scala_test_suite(
"//ledger/test-common:dar-files-default-lib",
"//libs-scala/contextualized-logging",
"//libs-scala/logging-entries",
"@maven//:com_fasterxml_jackson_core_jackson_core",
"@maven//:com_fasterxml_jackson_core_jackson_databind",
"@maven//:com_google_guava_guava",
"@maven//:com_google_protobuf_protobuf_java",
"@maven//:io_dropwizard_metrics_metrics_core",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,14 @@ import com.daml.lf.transaction._
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.protobuf.any.{Any => AnyProto}
import com.google.rpc.code.Code
import com.google.rpc.error_details.ErrorInfo
import com.google.rpc.status.Status

import java.io.StringWriter
import java.time.{Duration, Instant}
import scala.annotation.nowarn
import scala.collection.mutable
Expand Down Expand Up @@ -448,19 +450,21 @@ private[state] object Conversions {
def decodeTransactionRejectionEntry(
entry: DamlTransactionRejectionEntry
): Option[FinalReason] = {
def buildStatus(code: Code, message: String) = {
Status.of(
code.value,
message,
Seq(
AnyProto.pack[ErrorInfo](
ErrorInfo(metadata =
Map(GrpcStatuses.DefiniteAnswerKey -> entry.getDefiniteAnswer.toString)
)
def buildStatus(
code: Code,
message: String,
additionalMetadata: Map[String, String] = Map.empty,
) = Status.of(
code.value,
message,
Seq(
AnyProto.pack[ErrorInfo](
ErrorInfo(metadata =
additionalMetadata + (GrpcStatuses.DefiniteAnswerKey -> entry.getDefiniteAnswer.toString)
)
),
)
}
)
),
)

val status = entry.getReasonCase match {
case DamlTransactionRejectionEntry.ReasonCase.INVALID_LEDGER_TIME =>
Expand All @@ -469,6 +473,11 @@ private[state] object Conversions {
buildStatus(
Code.ABORTED,
s"Invalid ledger time: ${rejection.getDetails}",
Map(
"ledger_time" -> rejection.getLedgerTime.toString,
"lower_bound" -> rejection.getLowerBound.toString,
"upper_bound" -> rejection.getUpperBound.toString,
),
)
)
case DamlTransactionRejectionEntry.ReasonCase.DISPUTED =>
Expand All @@ -485,6 +494,10 @@ private[state] object Conversions {
buildStatus(
Code.PERMISSION_DENIED,
s"Submitter cannot act via participant: ${rejection.getDetails}",
Map(
"submitter_party" -> rejection.getSubmitterParty,
"participant_id" -> rejection.getParticipantId,
),
)
)
case DamlTransactionRejectionEntry.ReasonCase.INCONSISTENT =>
Expand Down Expand Up @@ -567,6 +580,7 @@ private[state] object Conversions {
buildStatus(
Code.ABORTED,
s"Inconsistent: Missing input state for key ${rejection.getKey.toString}",
Map("key" -> rejection.getKey.toString),
)
)
case DamlTransactionRejectionEntry.ReasonCase.RECORD_TIME_OUT_OF_RANGE =>
Expand All @@ -575,6 +589,20 @@ private[state] object Conversions {
buildStatus(
Code.ABORTED,
s"Invalid ledger time: Record time is outside of valid range [${rejection.getMinimumRecordTime}, ${rejection.getMaximumRecordTime}]",
Map(
"minimum_record_time" -> Instant
.ofEpochSecond(
rejection.getMinimumRecordTime.getSeconds,
rejection.getMinimumRecordTime.getNanos.toLong,
)
.toString,
"maximum_record_time" -> Instant
.ofEpochSecond(
rejection.getMaximumRecordTime.getSeconds,
rejection.getMaximumRecordTime.getNanos.toLong,
)
.toString,
),
)
)
case DamlTransactionRejectionEntry.ReasonCase.CAUSAL_MONOTONICITY_VIOLATED =>
Expand All @@ -590,15 +618,17 @@ private[state] object Conversions {
buildStatus(
Code.INVALID_ARGUMENT,
s"Party not known on ledger: Submitting party '${rejection.getSubmitterParty}' not known",
Map("submitter_party" -> rejection.getSubmitterParty),
)
)
case DamlTransactionRejectionEntry.ReasonCase.PARTIES_NOT_KNOWN_ON_LEDGER =>
val rejection = entry.getPartiesNotKnownOnLedger
val parties = rejection.getPartiesList
Some(
buildStatus(
Code.INVALID_ARGUMENT,
s"Party not known on ledger: Parties not known on ledger ${rejection.getPartiesList.asScala
.mkString("[", ",", "]")}",
s"Party not known on ledger: Parties not known on ledger ${parties.asScala.mkString("[", ",", "]")}",
Map("parties" -> objectToJsonString(parties)),
)
)
case DamlTransactionRejectionEntry.ReasonCase.INVALID_PARTICIPANT_STATE =>
Expand All @@ -620,6 +650,13 @@ private[state] object Conversions {
status.map(FinalReason)
}

private 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

Expand Down
Loading

0 comments on commit f56ce2a

Please sign in to comment.