diff --git a/mercury/mercury-library/agent-cli-didcommx/src/main/scala/io/iohk/atala/mercury/AgentCli.scala b/mercury/mercury-library/agent-cli-didcommx/src/main/scala/io/iohk/atala/mercury/AgentCli.scala
index 12cbca93ff..de46f6b496 100644
--- a/mercury/mercury-library/agent-cli-didcommx/src/main/scala/io/iohk/atala/mercury/AgentCli.scala
+++ b/mercury/mercury-library/agent-cli-didcommx/src/main/scala/io/iohk/atala/mercury/AgentCli.scala
@@ -20,6 +20,9 @@ import io.iohk.atala.resolvers.UniversalDidResolver
import io.iohk.atala.mercury.protocol.connection.*
import io.iohk.atala.mercury.protocol.invitation.v2.Invitation
import io.iohk.atala.resolvers.DIDResolver
+import io.circe.Json
+import io.circe.parser.*
+import io.circe.syntax.*
/** AgentCli
* {{{
@@ -187,12 +190,14 @@ object AgentCli extends ZIOAppDefault {
}
// Make a Request
- body = RequestPresentation.Body(goal_code = Some("Propose Presentation"))
- attachmentDescriptor = AttachmentDescriptor(
- "1",
- Some("application/json"),
- LinkData(links = Seq("http://test"), hash = "1234")
+ body = RequestPresentation.Body(goal_code = Some("Presentation Request"))
+ presentationAttachment = PresentationAttachment.build(
+ Some(Options(challenge = "somechallenge", domain = "somedomain"))
)
+ // attachmentDescriptor = AttachmentDescriptor.buildBase64Attachment(payload =
+ // presentationAttachment.asJson.noSpaces.getBytes()
+ // )
+ attachmentDescriptor = AttachmentDescriptor.buildJsonAttachment(payload = presentationAttachment)
requestPresentation = RequestPresentation(
body = body,
attachments = Seq(attachmentDescriptor),
diff --git a/mercury/mercury-library/agent-didcommx/src/main/scala/io/iohk/atala/mercury/model/Conversions.scala b/mercury/mercury-library/agent-didcommx/src/main/scala/io/iohk/atala/mercury/model/Conversions.scala
index f2bcc311ab..3df958781b 100644
--- a/mercury/mercury-library/agent-didcommx/src/main/scala/io/iohk/atala/mercury/model/Conversions.scala
+++ b/mercury/mercury-library/agent-didcommx/src/main/scala/io/iohk/atala/mercury/model/Conversions.scala
@@ -56,6 +56,37 @@ def json2Map(json: Json): Any = json match {
case _ => null // Impossible case but Json cases are private in circe ...
}
+def mapValueToJson(obj: java.lang.Object): Json = {
+ obj match {
+ case null => Json.Null
+ case b: java.lang.Boolean => Json.fromBoolean(b)
+ case i: java.lang.Integer => Json.fromInt(i)
+ case d: java.lang.Double =>
+ Json.fromDouble(d).getOrElse(Json.fromDouble(0d).get)
+ case l: java.lang.Long => Json.fromLong(l)
+ case s: java.lang.String => Json.fromString(String.valueOf(s))
+ case array: com.nimbusds.jose.shaded.json.JSONArray => {
+ Json.fromValues(array.iterator().asScala.map(mapValueToJson).toList)
+ }
+ case joseObject: com.nimbusds.jose.shaded.json.JSONObject =>
+ Json.fromJsonObject {
+ JsonObject.fromMap(
+ joseObject
+ .asInstanceOf[java.util.Map[String, Object]]
+ .asScala
+ .toMap
+ .view
+ .mapValues(mapValueToJson)
+ .toMap
+ )
+ }
+ case any => {
+ println("*****NotImplemented***" + any.getClass().getCanonicalName() + "**********") // FIXME
+ ???
+ }
+ }
+}
+
given Conversion[AttachmentDescriptor, XAttachment] with {
def apply(attachment: AttachmentDescriptor): XAttachment = {
val id = attachment.id
@@ -82,8 +113,8 @@ given Conversion[XAttachment, AttachmentDescriptor] with {
val data: AttachmentData = attachment.getData().toJSONObject.asScala.toMap match {
case e if e contains ("json") =>
val aux = e("json")
- println(aux.getClass().getCanonicalName()) // TODO
- ???
+ val x = aux.asInstanceOf[java.util.Map[String, Object]].asScala.toMap.view.mapValues(mapValueToJson)
+ JsonData(JsonObject.fromMap(x.toMap))
case e if e contains ("base64") =>
val tmp = e("base64").asInstanceOf[String] // ...
Base64(tmp)
diff --git a/mercury/mercury-library/models/src/main/scala/io/iohk/atala/mercury/model/AttachmentDescriptor.scala b/mercury/mercury-library/models/src/main/scala/io/iohk/atala/mercury/model/AttachmentDescriptor.scala
index c6da09d962..75bb99a202 100644
--- a/mercury/mercury-library/models/src/main/scala/io/iohk/atala/mercury/model/AttachmentDescriptor.scala
+++ b/mercury/mercury-library/models/src/main/scala/io/iohk/atala/mercury/model/AttachmentDescriptor.scala
@@ -43,7 +43,7 @@ object LinkData {
}
-final case class JsonData(data: JsonObject) extends AttachmentData
+final case class JsonData(json: JsonObject) extends AttachmentData
object JsonData {
given Encoder[JsonData] = deriveEncoder[JsonData]
given Decoder[JsonData] = deriveDecoder[JsonData]
diff --git a/mercury/mercury-library/protocol-issue-credential/src/main/scala/io/iohk/atala/mercury/protocol/issuecredential/Utils.scala b/mercury/mercury-library/protocol-issue-credential/src/main/scala/io/iohk/atala/mercury/protocol/issuecredential/Utils.scala
index 42a74a83ff..5cda6ded11 100644
--- a/mercury/mercury-library/protocol-issue-credential/src/main/scala/io/iohk/atala/mercury/protocol/issuecredential/Utils.scala
+++ b/mercury/mercury-library/protocol-issue-credential/src/main/scala/io/iohk/atala/mercury/protocol/issuecredential/Utils.scala
@@ -31,7 +31,7 @@ private[this] trait ReadAttachmentsUtils {
case obj: JsonData =>
java.util.Base64
.getUrlEncoder()
- .encode(obj.data.asJson.noSpaces.getBytes())
+ .encode(obj.json.asJson.noSpaces.getBytes())
})
maybeAttachament.map(formatName -> _)
}
diff --git a/mercury/mercury-library/protocol-present-proof/src/main/scala/io/iohk/atala/mercury/protocol/presentproof/PresentationAttachment.scala b/mercury/mercury-library/protocol-present-proof/src/main/scala/io/iohk/atala/mercury/protocol/presentproof/PresentationAttachment.scala
new file mode 100644
index 0000000000..b22dafd23e
--- /dev/null
+++ b/mercury/mercury-library/protocol-present-proof/src/main/scala/io/iohk/atala/mercury/protocol/presentproof/PresentationAttachment.scala
@@ -0,0 +1,79 @@
+package io.iohk.atala.mercury.protocol.presentproof
+
+import io.circe._
+import io.circe.generic.semiauto._
+import io.circe.syntax._
+
+case class Field(
+ id: Option[String] = None,
+ path: Seq[String] = Seq.empty,
+ name: Option[String] = None,
+ purpose: Option[String] = None,
+)
+object Field {
+ given Encoder[Field] = deriveEncoder[Field]
+ given Decoder[Field] = deriveDecoder[Field]
+}
+
+case class Jwt(alg: Seq[String], proof_type: Seq[String])
+object Jwt {
+ given Encoder[Jwt] = deriveEncoder[Jwt]
+ given Decoder[Jwt] = deriveDecoder[Jwt]
+}
+case class ClaimFormat(jwt: Jwt)
+object ClaimFormat {
+ given Encoder[ClaimFormat] = deriveEncoder[ClaimFormat]
+ given Decoder[ClaimFormat] = deriveDecoder[ClaimFormat]
+}
+case class Constraints(fields: Option[Seq[Field]])
+object Constraints {
+ given Encoder[Constraints] = deriveEncoder[Constraints]
+ given Decoder[Constraints] = deriveDecoder[Constraints]
+}
+
+/** Refer to Input Descriptors
+ */
+case class InputDescriptor(
+ id: String = java.util.UUID.randomUUID.toString(),
+ name: Option[String] = None,
+ purpose: Option[String] = None,
+ format: Option[ClaimFormat] = None,
+ constraints: Constraints
+)
+object InputDescriptor {
+ given Encoder[InputDescriptor] = deriveEncoder[InputDescriptor]
+ given Decoder[InputDescriptor] = deriveDecoder[InputDescriptor]
+}
+
+/** Refer to Presentation
+ * Definition
+ */
+case class PresentationDefinition(
+ id: String = java.util.UUID.randomUUID.toString(), // UUID
+ input_descriptors: Seq[InputDescriptor] = Seq.empty,
+ name: Option[String] = None,
+ purpose: Option[String] = None,
+ format: Option[ClaimFormat] = None,
+)
+object PresentationDefinition {
+ given Encoder[PresentationDefinition] = deriveEncoder[PresentationDefinition]
+ given Decoder[PresentationDefinition] = deriveDecoder[PresentationDefinition]
+}
+
+case class Options(challenge: String, domain: String)
+object Options {
+ given Encoder[Options] = deriveEncoder[Options]
+ given Decoder[Options] = deriveDecoder[Options]
+}
+
+case class PresentationAttachment(options: Option[Options] = None, presentation_definition: PresentationDefinition)
+object PresentationAttachment {
+ given Encoder[PresentationAttachment] = deriveEncoder[PresentationAttachment]
+ given Decoder[PresentationAttachment] = deriveDecoder[PresentationAttachment]
+
+ def build(options: Option[Options] = None): PresentationAttachment = {
+ val presentationDefinition =
+ PresentationDefinition(input_descriptors = Seq.empty)
+ PresentationAttachment(options, presentationDefinition)
+ }
+}
diff --git a/mercury/mercury-library/protocol-present-proof/src/test/scala/io/iohk/atala/mercury/protocol/presentproof/PresentationAttachmentSpec.scala b/mercury/mercury-library/protocol-present-proof/src/test/scala/io/iohk/atala/mercury/protocol/presentproof/PresentationAttachmentSpec.scala
new file mode 100644
index 0000000000..5fbf8778cc
--- /dev/null
+++ b/mercury/mercury-library/protocol-present-proof/src/test/scala/io/iohk/atala/mercury/protocol/presentproof/PresentationAttachmentSpec.scala
@@ -0,0 +1,141 @@
+package io.iohk.atala.mercury.protocol.presentproof
+
+import cats.implicits.*
+import io.circe.Json
+import io.circe.parser.*
+import io.circe.syntax.*
+import io.iohk.atala.mercury.model.AttachmentDescriptor.attachmentDescriptorEncoderV2
+import io.iohk.atala.mercury.model.{AttachmentDescriptor, DidId}
+import munit.*
+import zio.*
+import io.iohk.atala.mercury.model._
+
+class PresentationAttachmentSpec extends ZSuite {
+
+ test("Verifier Request Presentation Attachment") {
+ val expectedConstraintJson = parse(s"""
+ {
+ "fields": [
+ {
+ "path": [
+ "credentialSubject.dateOfBirth",
+ "credentialSubject.dob",
+ "vc.credentialSubject.dateOfBirth",
+ "vc.credentialSubject.dob"
+ ]
+ }
+ ]
+ }
+ """.stripMargin).getOrElse(Json.Null)
+ val field = Field(
+ None,
+ path = Seq(
+ "credentialSubject.dateOfBirth",
+ "credentialSubject.dob",
+ "vc.credentialSubject.dateOfBirth",
+ "vc.credentialSubject.dob"
+ )
+ )
+ val constraints = Constraints(fields = Some(Seq(field)))
+ val result = constraints.asJson.deepDropNullValues
+ assertEquals(result, expectedConstraintJson)
+
+ val expectedInputDescriptorJson = parse(s"""
+ {
+ "id": "wa_driver_license",
+ "name": "Washington State Business License",
+ "purpose": "We can only allow licensed Washington State business representatives into the WA Business Conference",
+ "constraints": {
+ "fields": [
+ {
+ "path": [
+ "credentialSubject.dateOfBirth",
+ "credentialSubject.dob",
+ "vc.credentialSubject.dateOfBirth",
+ "vc.credentialSubject.dob"
+ ]
+ }
+ ]
+ }
+ }
+ """.stripMargin).getOrElse(Json.Null)
+
+ val inputDescriptor = InputDescriptor(
+ id = "wa_driver_license",
+ name = Some("Washington State Business License"),
+ purpose =
+ Some("We can only allow licensed Washington State business representatives into the WA Business Conference"),
+ constraints = constraints
+ )
+ val resultInputDescriptor = inputDescriptor.asJson.deepDropNullValues
+ assertEquals(resultInputDescriptor, expectedInputDescriptorJson)
+
+ val expectedPresentationDefinitionJson = parse(s"""
+ {
+ "id": "32f54163-7166-48f1-93d8-ff217bdb0653",
+ "input_descriptors": [
+ {
+ "id": "wa_driver_license",
+ "name": "Washington State Business License",
+ "purpose": "We can only allow licensed Washington State business representatives into the WA Business Conference",
+ "constraints": {
+ "fields": [
+ {
+ "path": [
+ "credentialSubject.dateOfBirth",
+ "credentialSubject.dob",
+ "vc.credentialSubject.dateOfBirth",
+ "vc.credentialSubject.dob"
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ }
+ """.stripMargin).getOrElse(Json.Null)
+
+ val presentationDefinition =
+ PresentationDefinition(id = "32f54163-7166-48f1-93d8-ff217bdb0653", input_descriptors = Seq(inputDescriptor))
+ val resultPresentationDefinition = presentationDefinition.asJson.deepDropNullValues
+ assertEquals(resultPresentationDefinition, expectedPresentationDefinitionJson)
+
+ val expectedPresentationAttachmentJson = parse(s"""
+ {
+ "options": {
+ "challenge": "23516943-1d79-4ebd-8981-623f036365ef",
+ "domain": "us.gov/DriversLicense"
+ },
+ "presentation_definition": {
+ "id": "32f54163-7166-48f1-93d8-ff217bdb0653",
+ "input_descriptors": [
+ {
+ "id": "wa_driver_license",
+ "name": "Washington State Business License",
+ "purpose": "We can only allow licensed Washington State business representatives into the WA Business Conference",
+ "constraints": {
+ "fields": [
+ {
+ "path": [
+ "credentialSubject.dateOfBirth",
+ "credentialSubject.dob",
+ "vc.credentialSubject.dateOfBirth",
+ "vc.credentialSubject.dob"
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ }
+ }
+ """.stripMargin).getOrElse(Json.Null)
+ val options = Options(challenge = "23516943-1d79-4ebd-8981-623f036365ef", domain = "us.gov/DriversLicense")
+ val presentationAttachment =
+ PresentationAttachment(presentation_definition = presentationDefinition, options = Some(options))
+ val resultPresentationAttachment = presentationAttachment.asJson.deepDropNullValues
+ println(resultPresentationAttachment)
+ assertEquals(resultPresentationAttachment, expectedPresentationAttachmentJson)
+
+ }
+}