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 34ba5b3f8b..5bcc47cfc5 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 @@ -16,6 +16,8 @@ import io.iohk.atala.mercury.protocol.issuecredential._ import io.iohk.atala.mercury.protocol.presentproof._ import io.iohk.atala.resolvers.PeerDidMediatorSecretResolver import io.iohk.atala.resolvers.UniversalDidResolver +import io.iohk.atala.mercury.protocol.connection.* +import io.iohk.atala.mercury.protocol.invitation.v2.Invitation /** AgentCli * {{{ @@ -93,6 +95,17 @@ object AgentCli extends ZIOAppDefault { } yield () } + def generateConnectionInvitation = { + import io.iohk.atala.mercury.protocol.invitation._ + for { + didCommService <- ZIO.service[DidComm] + invitation = OutOfBandConnection.createInvitation(from = didCommService.myDid) + serverUrl = s"https://didcomm-bootstrap.atalaprism.com?_oob=${invitation.toBase64}" + _ <- Console.printLine(serverUrl) + _ <- Console.printLine(invitation.id + " -> " + invitation) + } yield () + } + def loginInvitation = { import io.iohk.atala.mercury.protocol.outofbandlogin._ @@ -213,6 +226,34 @@ object AgentCli extends ZIOAppDefault { } yield () } + def connect: ZIO[DidComm, MercuryError | IOException, Unit] = { + + import io.iohk.atala.mercury.protocol.invitation.OutOfBand + import io.circe._, io.circe.parser._ + for { + didCommService <- ZIO.service[DidComm] + _ <- Console.printLine("Read OutOfBand Invitation") + data <- Console.readLine.flatMap { + case "" => ZIO.fail(???) // TODO retry + case url => ZIO.succeed(OutOfBand.parseLink(url).getOrElse(???)) /// TODO make ERROR + } + _ <- Console.printLine(s"Decoded Invitation = $data") + parseResult = parse(data).getOrElse(null) + connectionInvitation = parseResult.as[Invitation].getOrElse(???) + _ <- Console.printLine(s"Invitation to ${connectionInvitation.id} with $connectionInvitation") + connectionRequest = ConnectionRequest( + from = didCommService.myDid, + to = connectionInvitation.from, + thid = Some(connectionInvitation.id), // TODO if this is coorect + body = ConnectionRequest.Body(goal_code = Some("connect"), goal = Some("Establish Connection")) + ) + msg = connectionRequest.makeMessage + _ <- Console.printLine("Sending: " + msg) + _ <- sendMessage(msg) + + } yield () + } + /** Encrypt and send a Message via HTTP * * TODO Move this method to another model @@ -336,6 +377,8 @@ object AgentCli extends ZIOAppDefault { "Login with DID" -> loginInvitation.provide(didCommLayer), "Propose Credential" -> proposeAndSendCredential.provide(didCommLayer), "Present Proof" -> presentProof.provide(didCommLayer), + "Generate Connection invitation" -> generateConnectionInvitation.provide(didCommLayer), + "Connect" -> connect.provide(didCommLayer), ) ).repeatWhile((_) => true) @@ -422,6 +465,28 @@ object AgentCli extends ZIOAppDefault { presentation = Presentation.readFromMessage(msg) _ <- ZIO.logInfo("Got Presentation: " + presentation) } yield ("Presentation Recived") + // ########################Comnnect############################################## + case s if s == ConnectionRequest.`type` => // Inviter + for { + _ <- ZIO.logInfo("*" * 100) + _ <- ZIO.logInfo("As Inviter in Connect:") + connectionRequest = ConnectionRequest.readFromMessage(msg) + _ <- ZIO.logInfo("Got ConnectionRequest: " + connectionRequest) + _ <- ZIO.logInfo("Creating New PeerDID...") +// peer <- ZIO.succeed(PeerDID.makePeerDid(serviceEndpoint = serviceEndpoint)) TODO +// _ <- ZIO.logInfo(s"My new DID => $peer") + connectionResponse = ConnectionResponse.makeResponseFromRequest(msg) + msgToSend = connectionResponse.makeMessage + _ <- sendMessage(msgToSend) + } yield ("Connection Request Sent") + case s if s == ConnectionResponse.`type` => // Invitee + for { + _ <- ZIO.logInfo("*" * 100) + _ <- ZIO.logInfo("As Invitee in Connect:") + connectionResponse = ConnectionResponse.readFromMessage(msg) + _ <- ZIO.logInfo("Got Connection Response: " + connectionResponse) + } yield ("Connection established") + case "https://didcomm.org/routing/2.0/forward" => ??? // SEE mediator case "https://atalaprism.io/mercury/mailbox/1.0/ReadMessages" => ??? // SEE mediator case "https://didcomm.org/coordinate-mediation/2.0/mediate-request" => ??? // SEE mediator diff --git a/mercury/mercury-library/agent/src/main/scala/io/iohk/atala/mercury/InvitationPrograms.scala b/mercury/mercury-library/agent/src/main/scala/io/iohk/atala/mercury/InvitationPrograms.scala index e817fd3520..025b210ec3 100644 --- a/mercury/mercury-library/agent/src/main/scala/io/iohk/atala/mercury/InvitationPrograms.scala +++ b/mercury/mercury-library/agent/src/main/scala/io/iohk/atala/mercury/InvitationPrograms.scala @@ -8,7 +8,7 @@ import io.iohk.atala.mercury.{*, given} import io.iohk.atala.mercury.model.* import io.iohk.atala.mercury.protocol.invitation.* import io.iohk.atala.mercury.protocol.invitation.v2.* -import io.iohk.atala.mercury.protocol.invitation.InvitationCodec.* +import io.iohk.atala.mercury.protocol.invitation.v2.Invitation.Body import cats.implicits.* import io.circe.syntax.* import io.circe.Json diff --git a/mercury/mercury-library/build.sbt b/mercury/mercury-library/build.sbt index 4b05cd0d4f..13c02b7ca8 100644 --- a/mercury/mercury-library/build.sbt +++ b/mercury/mercury-library/build.sbt @@ -214,6 +214,7 @@ lazy val agent = project // maybe merge into models protocolLogin, protocolIssueCredential, protocolPresentProof, + protocolConnection, ) /** agents implementation with didcommx */ diff --git a/mercury/mercury-library/protocol-connection/Connection-Protocol.md b/mercury/mercury-library/protocol-connection/Connection-Protocol.md new file mode 100644 index 0000000000..12a7b29e7c --- /dev/null +++ b/mercury/mercury-library/protocol-connection/Connection-Protocol.md @@ -0,0 +1,52 @@ +# Connection Protocol + +This Protocol is for DID base connection + + +The protocol is used when you wish to create a connection with another agent. + + +## PIURI + +Version 1.0: `https://atalaprism.io/mercury/connections/1.0/request` + +Version 1.0: `https://atalaprism.io/mercury/connections/1.0/response` + +### Roles + +- Inviter + - Will create the message `https://didcomm.org/out-of-band/2.0/invitation` + - will accept the Connection request and create new did peer and reply Connection response +- Invitee + - Will accept the invitation + - Will create a did peer and reply to the Invitee with Connection Request + +### Notes + + + +### Inviter create invitation message for connection (Flow Diagram) + +```mermaid +stateDiagram-v2 + [*] --> Initial + Initial --> await_response:Send out-of-band invitation message + await_response --> connection_request:receive DIDCOMM Connection Request message + connection_request --> connection_response:send DIDCOMM Connection Response message + connection_response --> done + await_response --> error:recieve problem report response + done --> [*] +``` + +### Invitee accepting invitation message for connection (Flow Diagram) + +```mermaid +stateDiagram-v2 + [*] --> Initial: out-of-band invitation connection message + Initial --> connection_request:Create Connection Request + connection_request --> await_response: Send Connection Request DIDCOMM message + await_response --> connection_response:receive DIDCOMM Connection Response message + await_response --> error:recieve problem report response + connection_response --> done:send ACK + done --> [*] +``` diff --git a/mercury/mercury-library/protocol-connection/src/main/scala/io/iohk/atala/mercury/protocol/connection/ConnectionRequest.scala b/mercury/mercury-library/protocol-connection/src/main/scala/io/iohk/atala/mercury/protocol/connection/ConnectionRequest.scala new file mode 100644 index 0000000000..4635f6d936 --- /dev/null +++ b/mercury/mercury-library/protocol-connection/src/main/scala/io/iohk/atala/mercury/protocol/connection/ConnectionRequest.scala @@ -0,0 +1,59 @@ +package io.iohk.atala.mercury.protocol.connection +import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} +import io.circe.{Decoder, Encoder} +import io.iohk.atala.mercury.model.{AttachmentDescriptor, DidId, Message, PIURI} +import io.circe.syntax.* +import io.iohk.atala.mercury.protocol.connection.ConnectionRequest.Body + +object ConnectionRequest { + def `type`: PIURI = "https://atalaprism.io/mercury/connections/1.0/request" + + case class Body( + goal_code: Option[String] = None, + goal: Option[String] = None, + accept: Seq[String] = Seq.empty + ) + + object Body { + given Encoder[Body] = deriveEncoder[Body] + + given Decoder[Body] = deriveDecoder[Body] + } + + given Encoder[ConnectionRequest] = deriveEncoder[ConnectionRequest] + + given Decoder[ConnectionRequest] = deriveDecoder[ConnectionRequest] + + def readFromMessage(message: Message): ConnectionRequest = { + val body = message.body.asJson.as[ConnectionRequest.Body].toOption.get // TODO get + ConnectionRequest( + id = message.id, + `type` = message.piuri, + body = body, + thid = message.thid, + from = message.from.get, // TODO get + to = message.to.get, // TODO get + ) + } + +} + +final case class ConnectionRequest( + `type`: PIURI = ConnectionRequest.`type`, + id: String = java.util.UUID.randomUUID().toString, + from: DidId, + to: DidId, + thid: Option[String], + body: Body, +) { + assert(`type` == "https://atalaprism.io/mercury/connections/1.0/request") + + def makeMessage: Message = Message( + id = this.id, + piuri = this.`type`, + from = Some(this.from), + to = Some(this.to), + thid = this.thid, + body = this.body.asJson.asObject.get, + ) +} diff --git a/mercury/mercury-library/protocol-connection/src/main/scala/io/iohk/atala/mercury/protocol/connection/ConnectionResponse.scala b/mercury/mercury-library/protocol-connection/src/main/scala/io/iohk/atala/mercury/protocol/connection/ConnectionResponse.scala new file mode 100644 index 0000000000..09b685a8e0 --- /dev/null +++ b/mercury/mercury-library/protocol-connection/src/main/scala/io/iohk/atala/mercury/protocol/connection/ConnectionResponse.scala @@ -0,0 +1,72 @@ +package io.iohk.atala.mercury.protocol.connection + +import io.circe.{Decoder, Encoder} +import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder} +import io.iohk.atala.mercury.model.{DidId, Message, PIURI} +import io.circe.syntax.* + +object ConnectionResponse { + def `type`: PIURI = "https://atalaprism.io/mercury/connections/1.0/response" + + final case class Body( + goal_code: Option[String] = None, + goal: Option[String] = None, + accept: Seq[String] = Seq.empty + ) + + object Body { + given Encoder[Body] = deriveEncoder[Body] + given Decoder[Body] = deriveDecoder[Body] + } + + def makeResponseFromRequest(msg: Message): ConnectionResponse = { + val cr: ConnectionRequest = ConnectionRequest.readFromMessage(msg) + + ConnectionResponse( + body = ConnectionResponse.Body( + goal_code = cr.body.goal_code, + goal = cr.body.goal, + accept = cr.body.accept, + ), + thid = msg.thid.orElse(Some(cr.id)), + from = msg.to.get, // TODO need new PeerDid + to = msg.from.get, // TODO get + ) + } + + def readFromMessage(message: Message): ConnectionResponse = { + val body = message.body.asJson.as[ConnectionResponse.Body].toOption.get // TODO get + ConnectionResponse( + id = message.id, + `type` = message.piuri, + body = body, + thid = message.thid, + from = message.from.get, // TODO get + to = message.to.get, // TODO get + ) + } + + given Encoder[ConnectionResponse] = deriveEncoder[ConnectionResponse] + + given Decoder[ConnectionResponse] = deriveDecoder[ConnectionResponse] +} + +final case class ConnectionResponse( + `type`: PIURI = ConnectionResponse.`type`, + id: String = java.util.UUID.randomUUID().toString, + from: DidId, + to: DidId, + thid: Option[String], + body: ConnectionResponse.Body, +) { + assert(`type` == "https://atalaprism.io/mercury/connections/1.0/response") + + def makeMessage: Message = Message( + id = this.id, + piuri = this.`type`, + from = Some(this.from), + to = Some(this.to), + thid = this.thid, + body = this.body.asJson.asObject.get, + ) +} diff --git a/mercury/mercury-library/protocol-connection/src/main/scala/io/iohk/atala/mercury/protocol/connection/OutOfBandConnection.scala b/mercury/mercury-library/protocol-connection/src/main/scala/io/iohk/atala/mercury/protocol/connection/OutOfBandConnection.scala new file mode 100644 index 0000000000..5e10ca5951 --- /dev/null +++ b/mercury/mercury-library/protocol-connection/src/main/scala/io/iohk/atala/mercury/protocol/connection/OutOfBandConnection.scala @@ -0,0 +1,14 @@ +package io.iohk.atala.mercury.protocol.connection + +import io.iohk.atala.mercury.model.DidId +import io.iohk.atala.mercury.protocol.invitation.v2.Invitation +import io.iohk.atala.mercury.protocol.invitation.v2.Invitation.Body + +object OutOfBandConnection { + + def createInvitation(from: DidId): Invitation = { + val body = Body("connect", "Start relationship", Seq("didcomm/v2")) + Invitation(`type` = Invitation.`type`, from, body) + } + +} diff --git a/mercury/mercury-library/protocol-connection/src/main/scala/io/iohk/atala/mercury/protocol/connection/Request.scala b/mercury/mercury-library/protocol-connection/src/main/scala/io/iohk/atala/mercury/protocol/connection/Request.scala deleted file mode 100644 index 9bca273bd0..0000000000 --- a/mercury/mercury-library/protocol-connection/src/main/scala/io/iohk/atala/mercury/protocol/connection/Request.scala +++ /dev/null @@ -1,7 +0,0 @@ -package io.iohk.atala.mercury.protocol.connection -import io.iohk.atala.mercury.model.PIURI - -object Request { - def `type`: PIURI = "https://atalaprism.io/mercury/connections/1.0/request" - -} diff --git a/mercury/mercury-library/protocol-connection/src/main/scala/io/iohk/atala/mercury/protocol/connection/Response.scala b/mercury/mercury-library/protocol-connection/src/main/scala/io/iohk/atala/mercury/protocol/connection/Response.scala deleted file mode 100644 index 56d10b3b00..0000000000 --- a/mercury/mercury-library/protocol-connection/src/main/scala/io/iohk/atala/mercury/protocol/connection/Response.scala +++ /dev/null @@ -1,7 +0,0 @@ -package io.iohk.atala.mercury.protocol.connection - -import io.iohk.atala.mercury.model.PIURI - -object Response { - def `type`: PIURI = "https://atalaprism.io/mercury/connections/1.0/response" -} diff --git a/mercury/mercury-library/protocol-invitation/src/main/scala/io/iohk/atala/mercury/protocol/invitation/InvitationCodec.scala b/mercury/mercury-library/protocol-invitation/src/main/scala/io/iohk/atala/mercury/protocol/invitation/InvitationCodec.scala deleted file mode 100644 index 488a430d5a..0000000000 --- a/mercury/mercury-library/protocol-invitation/src/main/scala/io/iohk/atala/mercury/protocol/invitation/InvitationCodec.scala +++ /dev/null @@ -1,45 +0,0 @@ -package io.iohk.atala.mercury.protocol.invitation -import cats.implicits._ -import io.circe.syntax._ -import io.circe.generic.semiauto._ -import io.circe.{Decoder, Encoder, HCursor, Json} -import io.iohk.atala.mercury.protocol.invitation.v1.{Invitation => InvitationV1} -import io.iohk.atala.mercury.protocol.invitation.v2.{Invitation => InvitationV2} -import io.iohk.atala.mercury.model.AttachmentDescriptor.attachmentDescriptorEncoderV1 - -object InvitationCodec { - - implicit val serviceEncoder: Encoder[Service] = deriveEncoder[Service] - implicit val serviceDecoder: Decoder[Service] = deriveDecoder[Service] - - implicit val didEncoder: Encoder[Did] = (a: Did) => Json.fromString(a.did) - implicit val didDecoder: Decoder[Did] = (c: HCursor) => c.value.as[String].map(did => Did(did)) - - implicit val serviceTypeEncoder: Encoder[ServiceType] = Encoder.instance { - case service @ Service(_, _, _, _, _) => service.asJson - case did @ Did(_) => did.asJson - } - - implicit val serviceTypeDecoder: Decoder[ServiceType] = - List[Decoder[ServiceType]]( - Decoder[Service].widen, - Decoder[Did].widen - ).reduceLeft(_ or _) - - implicit val invitationEncoderV1: Encoder[InvitationV1] = (entity: InvitationV1) => - Json.obj( - "@id" -> Json.fromString(entity.`@id`), - "@type" -> Json.fromString(entity.`@type`), - "label" -> Json.fromString(entity.label), - "goal" -> Json.fromString(entity.goal), - "goal_code" -> Json.fromString(entity.goal_code), - "accept" -> entity.accept.asJson, - "handshake_protocols" -> entity.handshake_protocols.asJson, - "requests~attach" -> entity.`requests~attach`.asJson, - "services" -> entity.services.asJson - ) - implicit val invitationDecoderV1: Decoder[InvitationV1] = deriveDecoder[InvitationV1] - - implicit val invitationEncoderV2: Encoder[InvitationV2] = deriveEncoder[InvitationV2] - implicit val invitationDecoderV2: Decoder[InvitationV2] = deriveDecoder[InvitationV2] -} diff --git a/mercury/mercury-library/protocol-invitation/src/main/scala/io/iohk/atala/mercury/protocol/invitation/OutOfBand.scala b/mercury/mercury-library/protocol-invitation/src/main/scala/io/iohk/atala/mercury/protocol/invitation/OutOfBand.scala index 80df4c96e6..8a8976cb0e 100644 --- a/mercury/mercury-library/protocol-invitation/src/main/scala/io/iohk/atala/mercury/protocol/invitation/OutOfBand.scala +++ b/mercury/mercury-library/protocol-invitation/src/main/scala/io/iohk/atala/mercury/protocol/invitation/OutOfBand.scala @@ -3,7 +3,6 @@ package io.iohk.atala.mercury.protocol.invitation import java.net.URL import java.{util => ju} import io.iohk.atala.mercury.protocol.invitation.v2._ -import io.iohk.atala.mercury.protocol.invitation.InvitationCodec._ import io.circe._ import io.circe.parser._ import io.iohk.atala.mercury diff --git a/mercury/mercury-library/protocol-invitation/src/main/scala/io/iohk/atala/mercury/protocol/invitation/ServiceType.scala b/mercury/mercury-library/protocol-invitation/src/main/scala/io/iohk/atala/mercury/protocol/invitation/ServiceType.scala index eaaa0344d8..11bdd389c4 100644 --- a/mercury/mercury-library/protocol-invitation/src/main/scala/io/iohk/atala/mercury/protocol/invitation/ServiceType.scala +++ b/mercury/mercury-library/protocol-invitation/src/main/scala/io/iohk/atala/mercury/protocol/invitation/ServiceType.scala @@ -7,6 +7,20 @@ import io.circe.{Decoder, Encoder, HCursor, Json} sealed trait ServiceType +object ServiceType { + + given Encoder[ServiceType] = (a: ServiceType) => { + a match + case data @ Did(_) => data.did.asJson + case data @ Service(_, _, _, _, _) => data.asJson + } + + given Decoder[ServiceType] = List[Decoder[ServiceType]]( + Decoder[Did].widen, + Decoder[Service].widen, + ).reduceLeft(_ or _) +} + /** Service block * @see * https://github.com/hyperledger/aries-rfcs/tree/main/features/0434-outofband @@ -24,4 +38,14 @@ case class Service( serviceEndpoint: String, ) extends ServiceType +object Service { + given Encoder[Service] = deriveEncoder[Service] + given Decoder[Service] = deriveDecoder[Service] +} + case class Did(did: String) extends ServiceType + +object Did { + given Encoder[Did] = deriveEncoder[Did] + given Decoder[Did] = deriveDecoder[Did] +} diff --git a/mercury/mercury-library/protocol-invitation/src/main/scala/io/iohk/atala/mercury/protocol/invitation/v1/Invitation.scala b/mercury/mercury-library/protocol-invitation/src/main/scala/io/iohk/atala/mercury/protocol/invitation/v1/Invitation.scala index e37efa8cd0..2bfd7d62f9 100644 --- a/mercury/mercury-library/protocol-invitation/src/main/scala/io/iohk/atala/mercury/protocol/invitation/v1/Invitation.scala +++ b/mercury/mercury-library/protocol-invitation/src/main/scala/io/iohk/atala/mercury/protocol/invitation/v1/Invitation.scala @@ -1,12 +1,13 @@ package io.iohk.atala.mercury.protocol.invitation.v1 -import cats.implicits._ -import io.circe.syntax._ -import io.circe.generic.semiauto._ -import io.circe.{Encoder, Json} +import cats.implicits.* +import io.circe.syntax.* +import io.circe.generic.semiauto.* +import io.circe.{Decoder, Encoder, Json} import io.iohk.atala.mercury.model.PIURI import scala.annotation.targetName import io.iohk.atala.mercury.model.AttachmentDescriptor +import io.iohk.atala.mercury.model.AttachmentDescriptor.attachmentDescriptorEncoderV1 import io.iohk.atala.mercury.protocol.invitation.ServiceType /** Out-Of-Band invitation Example @@ -33,3 +34,19 @@ final case class Invitation( ) { val `@type`: PIURI = "https://didcomm.org/out-of-band/2.0/invitation" } + +object Invitation { + given Encoder[Invitation] = (entity: Invitation) => + Json.obj( + "@id" -> Json.fromString(entity.`@id`), + "@type" -> Json.fromString(entity.`@type`), + "label" -> Json.fromString(entity.label), + "goal" -> Json.fromString(entity.goal), + "goal_code" -> Json.fromString(entity.goal_code), + "accept" -> entity.accept.asJson, + "handshake_protocols" -> entity.handshake_protocols.asJson, + "requests~attach" -> entity.`requests~attach`.asJson, + "services" -> entity.services.asJson + ) + given Decoder[Invitation] = deriveDecoder[Invitation] +} diff --git a/mercury/mercury-library/protocol-invitation/src/main/scala/io/iohk/atala/mercury/protocol/invitation/v2/Invitation.scala b/mercury/mercury-library/protocol-invitation/src/main/scala/io/iohk/atala/mercury/protocol/invitation/v2/Invitation.scala index 4793d5fdd3..af3a36adf7 100644 --- a/mercury/mercury-library/protocol-invitation/src/main/scala/io/iohk/atala/mercury/protocol/invitation/v2/Invitation.scala +++ b/mercury/mercury-library/protocol-invitation/src/main/scala/io/iohk/atala/mercury/protocol/invitation/v2/Invitation.scala @@ -1,28 +1,43 @@ package io.iohk.atala.mercury.protocol.invitation.v2 -import cats.implicits._ -import io.circe.syntax._ +import cats.implicits.* +import io.circe.syntax.* +import io.circe.{Encoder, Decoder, Json} +import io.iohk.atala.mercury.model.* +import AttachmentDescriptor.attachmentDescriptorEncoderV2 import io.circe.generic.semiauto._ -import io.circe.{Encoder, Json} -import io.iohk.atala.mercury.model._ - -import scala.annotation.targetName /** Out-Of-Band invitation * @see * https://identity.foundation/didcomm-messaging/spec/#invitation */ final case class Invitation( - `type`: PIURI, - id: String, + id: String = java.util.UUID.randomUUID.toString(), + `type`: PIURI = Invitation.`type`, from: DidId, - body: Body, - attachments: Option[AttachmentDescriptor] // TODO + body: Invitation.Body, + attachments: Option[Seq[AttachmentDescriptor]] = None ) { assert(`type` == "https://didcomm.org/out-of-band/2.0/invitation") + def toBase64: String = java.util.Base64.getUrlEncoder.encodeToString(this.asJson.deepDropNullValues.noSpaces.getBytes) + } -case class Body( - goal_code: String, - goal: String, - accept: Seq[String] -) +object Invitation { + + final case class Body( + goal_code: String, + goal: String, + accept: Seq[String] + ) + + object Body { + given Encoder[Body] = deriveEncoder[Body] + + given Decoder[Body] = deriveDecoder[Body] + } + + def `type`: PIURI = "https://didcomm.org/out-of-band/2.0/invitation" + given Encoder[Invitation] = deriveEncoder[Invitation] + given Decoder[Invitation] = deriveDecoder[Invitation] + +} diff --git a/mercury/mercury-library/protocol-invitation/src/test/scala/io/iohk/atala/mercury/protocol/invitation/v1/InvitationV1Spec.scala b/mercury/mercury-library/protocol-invitation/src/test/scala/io/iohk/atala/mercury/protocol/invitation/v1/InvitationV1Spec.scala index efa6cc2c63..80d3122159 100644 --- a/mercury/mercury-library/protocol-invitation/src/test/scala/io/iohk/atala/mercury/protocol/invitation/v1/InvitationV1Spec.scala +++ b/mercury/mercury-library/protocol-invitation/src/test/scala/io/iohk/atala/mercury/protocol/invitation/v1/InvitationV1Spec.scala @@ -7,8 +7,8 @@ import io.circe.syntax._ import io.circe.Json import io.circe.generic.semiauto._ import io.circe.parser._ +import io.iohk.atala.mercury.protocol.invitation.v1.Invitation import io.iohk.atala.mercury.protocol.invitation._ -import io.iohk.atala.mercury.protocol.invitation.InvitationCodec._ import io.iohk.atala.mercury.model.AttachmentDescriptor import io.iohk.atala.mercury.model.AttachmentDescriptor.attachmentDescriptorEncoderV1 @@ -19,28 +19,28 @@ class InvitationV1Spec extends ZSuite { val payloadBase64 = java.util.Base64.getUrlEncoder.encodeToString(Json.fromString(payload).noSpaces.getBytes) val expectedJson = parse(s"""{ - | "@id": "f3375429-b116-4224-b55f-563d7ef461f1", - | "@type": "https://didcomm.org/out-of-band/2.0/invitation", - | "label": "Faber College", - | "goal": "To issue a Faber College Graduate credential", - | "goal_code": "issue-vc", - | "accept": [ - | "didcomm/aip2;env=rfc587", - | "didcomm/aip2;env=rfc19" - | ], - | "handshake_protocols": [ - | "https://didcomm.org/didexchange/1.0", - | "https://didcomm.org/connections/1.0" - | ], - | "requests~attach": [ - | { - | "@id": "request-0", - | "mime-type": "application/json", - | "data": {"base64" : "$payloadBase64"} - | } - | ], - | "services": ["did:sov:LjgpST2rjsoxYegQDRm7EL"] - |}""".stripMargin).getOrElse(Json.Null) + | "@id": "f3375429-b116-4224-b55f-563d7ef461f1", + | "@type": "https://didcomm.org/out-of-band/2.0/invitation", + | "label": "Faber College", + | "goal": "To issue a Faber College Graduate credential", + | "goal_code": "issue-vc", + | "accept": [ + | "didcomm/aip2;env=rfc587", + | "didcomm/aip2;env=rfc19" + | ], + | "handshake_protocols": [ + | "https://didcomm.org/didexchange/1.0", + | "https://didcomm.org/connections/1.0" + | ], + | "requests~attach": [ + | { + | "@id": "request-0", + | "mime-type": "application/json", + | "data": {"base64" : "$payloadBase64"} + | } + | ], + | "services": ["did:sov:LjgpST2rjsoxYegQDRm7EL"] + |}""".stripMargin).getOrElse(Json.Null) val service = Service( id = "did:prism:PR6vs6GEZ8rHaVgjg2WodM#did-communication", diff --git a/mercury/mercury-library/protocol-invitation/src/test/scala/io/iohk/atala/mercury/protocol/invitation/v2/OutOfBandSpec.scala b/mercury/mercury-library/protocol-invitation/src/test/scala/io/iohk/atala/mercury/protocol/invitation/v2/OutOfBandSpec.scala index 0a5d1bdf13..d04f1266bd 100644 --- a/mercury/mercury-library/protocol-invitation/src/test/scala/io/iohk/atala/mercury/protocol/invitation/v2/OutOfBandSpec.scala +++ b/mercury/mercury-library/protocol-invitation/src/test/scala/io/iohk/atala/mercury/protocol/invitation/v2/OutOfBandSpec.scala @@ -3,6 +3,7 @@ package io.iohk.atala.mercury.protocol.invitation.v2 import munit.* import io.iohk.atala.mercury.protocol.invitation.v2._ import io.iohk.atala.mercury.model.DidId +import io.iohk.atala.mercury.protocol.invitation.v2.Invitation.Body import io.iohk.atala.mercury.protocol.invitation.OutOfBand class OutOfBandSpec extends FunSuite { @@ -15,8 +16,8 @@ class OutOfBandSpec extends FunSuite { assert(ret.isInstanceOf[Right[_, Invitation]]) val expected = Invitation( - "https://didcomm.org/out-of-band/2.0/invitation", "421dbbc8-57ca-4341-aa3a-f5b4215c568f", + "https://didcomm.org/out-of-band/2.0/invitation", DidId( "did:peer:2.Ez6LSmLmWmTvwjgLSuUaEQHdHSFWPwyibgzomWjFmnC6FhLnU.Vz6MktNgLh4N1u9KNhDiqe8KZ8bsLzLcqsifoNiUtBoSs9jxf.SeyJpZCI6Im5ldy1pZCIsInQiOiJkbSIsInMiOiJodHRwOi8vMTI3LjAuMC4xOjgwMDAiLCJhIjpbImRpZGNvbW0vdjIiXX0" ),