Skip to content

Commit

Permalink
feat(mercury): ATL-2287 Connection Protocol (#140)
Browse files Browse the repository at this point in the history
Added Connection Protocol
Refactoring and removing the todos

Co-authored-by: FabioPinheiro <[email protected]>
  • Loading branch information
mineme0110 and FabioPinheiro authored Nov 21, 2022
1 parent 46ef902 commit 402248b
Show file tree
Hide file tree
Showing 16 changed files with 364 additions and 104 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
* {{{
Expand Down Expand Up @@ -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._

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions mercury/mercury-library/build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ lazy val agent = project // maybe merge into models
protocolLogin,
protocolIssueCredential,
protocolPresentProof,
protocolConnection,
)

/** agents implementation with didcommx */
Expand Down
52 changes: 52 additions & 0 deletions mercury/mercury-library/protocol-connection/Connection-Protocol.md
Original file line number Diff line number Diff line change
@@ -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 --> [*]
```
Original file line number Diff line number Diff line change
@@ -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,
)
}
Original file line number Diff line number Diff line change
@@ -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,
)
}
Original file line number Diff line number Diff line change
@@ -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)
}

}

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading

0 comments on commit 402248b

Please sign in to comment.