Skip to content

Commit

Permalink
feat(mercury): Expose pthid on the connection protocol (#333)
Browse files Browse the repository at this point in the history
  • Loading branch information
FabioPinheiro committed Jan 30, 2023
1 parent edaab33 commit 82eca31
Show file tree
Hide file tree
Showing 8 changed files with 127 additions and 79 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ object AgentCli extends ZIOAppDefault {
import io.iohk.atala.mercury.protocol.invitation._
for {
agentService <- ZIO.service[DidAgent]
invitation = OutOfBandConnection.createInvitation(from = agentService.id)
invitation = ConnectionInvitation.makeConnectionInvitation(from = agentService.id)
serverUrl = s"https://didcomm-bootstrap.atalaprism.com?_oob=${invitation.toBase64}"
_ <- Console.printLine(serverUrl)
_ <- Console.printLine(invitation.id + " -> " + invitation)
Expand Down Expand Up @@ -222,7 +222,8 @@ object AgentCli extends ZIOAppDefault {
connectionRequest = ConnectionRequest(
from = agentService.id,
to = connectionInvitation.from,
thid = Some(connectionInvitation.id), // TODO if this is coorect
thid = None,
pthid = Some(connectionInvitation.id),
body = ConnectionRequest.Body(goal_code = Some("connect"), goal = Some("Establish Connection"))
)
msg = connectionRequest.makeMessage
Expand Down Expand Up @@ -421,20 +422,20 @@ object AgentCli extends ZIOAppDefault {
for {
_ <- ZIO.logInfo("*" * 100)
_ <- ZIO.logInfo("As Inviter in Connect:")
connectionRequest = ConnectionRequest.readFromMessage(msg)
connectionRequest = ConnectionRequest.fromMessage(msg).toOption.get // TODO .get
_ <- 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)
connectionResponse = ConnectionResponse.makeResponseFromRequest(msg).toOption.get // TODO .get
msgToSend = connectionResponse.makeMessage
_ <- MessagingService.send(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)
connectionResponse = ConnectionResponse.fromMessage(msg).toOption.get // TODO .get
_ <- ZIO.logInfo("Got Connection Response: " + connectionResponse)
} yield ("Connection established")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,9 @@ Version 1.0: `https://atalaprism.io/mercury/connections/1.0/response`

- 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
- Will accept the Connection request and reply Connection Response `https://atalaprism.io/mercury/connections/1.0/response`
- Invitee
- Will accept the invitation
- Will create a did peer and reply to the Invitee with Connection Request
- Will accept the invitation by sending a Connection Request `https://atalaprism.io/mercury/connections/1.0/request`

### Notes

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
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 ConnectionInvitation {

/** Make a invitation to establish a trust connection between two peers */
def makeConnectionInvitation(from: DidId): Invitation = {
Invitation(
from = from,
body = Invitation.Body(
goal_code = "io.atalaprism.connect",
goal = s"Establish a trust connection between two peers using the protocol '${ConnectionRequest.`type`}'",
Nil
)
)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ 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
import io.iohk.atala.mercury.protocol.invitation.v2.Invitation

object ConnectionRequest {
def `type`: PIURI = "https://atalaprism.io/mercury/connections/1.0/request"
Expand All @@ -16,31 +17,59 @@ object ConnectionRequest {

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
/** Utility method to start a ConnectionRequest for the scenario where he has an OOB Invitation
*
* @see
* [[ConnectionInvitation.makeConnectionRequest]]
*/
def makeFromInvitation(invitation: Invitation, invitee: DidId): ConnectionRequest =
ConnectionRequest(
id = message.id,
`type` = message.piuri,
body = body,
thid = message.thid,
from = message.from.get, // TODO get
to = {
assert(
message.to.length == 1,
"The recipient is ambiguous. Need to have only 1 recipient"
) // TODO return error return error
message.to.head
},
`type` = ConnectionRequest.`type`,
body = Body(
goal_code = Some(invitation.body.goal_code),
goal = Some(invitation.body.goal),
accept = Some(invitation.body.accept)
),
thid = None,
pthid = Some(invitation.id),
from = invitee,
to = invitation.from
)
}

/** Parse a generecy DIDComm Message into a ConnectionRequest */
def fromMessage(message: Message): Either[String, ConnectionRequest] =
for {
piuri <-
if (message.`type` == ConnectionRequest.`type`) Right(message.`type`)
else Left(s"Message MUST be of the type '${ConnectionRequest.`type`}' instead of '${message.`type`}'")
body <- message.body.asJson
.as[ConnectionRequest.Body]
.left
.map(ex => "Fail to parse the body of the ConnectionRequest because: " + ex.message)
ret <- message.to match
case Seq(onlyOneRecipient) =>
message.from match
case Some(inviter) =>
Right(
ConnectionRequest(
id = message.id,
`type` = piuri,
body = body,
thid = message.thid,
pthid = message.pthid,
from = inviter,
to = onlyOneRecipient
)
)
case None => Left("ConnectionRequest needs to define the Inviter")
case _ => Left("The recipient is ambiguous. Need to have only 1 recipient")
} yield ret

}

Expand All @@ -50,6 +79,7 @@ final case class ConnectionRequest(
from: DidId,
to: DidId,
thid: Option[String],
pthid: Option[String],
body: Body,
) {
assert(`type` == "https://atalaprism.io/mercury/connections/1.0/request")
Expand All @@ -60,6 +90,7 @@ final case class ConnectionRequest(
from = Some(this.from),
to = Seq(this.to),
thid = this.thid,
pthid = this.pthid,
body = this.body.asJson.asObject.get,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,41 +19,55 @@ object ConnectionResponse {
given Decoder[Body] = deriveDecoder[Body]
}

def makeResponseFromRequest(msg: Message): ConnectionResponse = {
val cr: ConnectionRequest = ConnectionRequest.readFromMessage(msg)
def makeResponseFromRequest(msg: Message): Either[String, ConnectionResponse] =
for {
cr: ConnectionRequest <- ConnectionRequest.fromMessage(msg)
ret = makeResponseFromRequest(cr)
} yield (ret)

def makeResponseFromRequest(cr: ConnectionRequest): ConnectionResponse =
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 = {
assert(msg.to.length == 1, "The recipient is ambiguous. Need to have only 1 recipient") // TODO return error
msg.to.head
},
to = msg.from.get, // TODO get
thid = cr.thid.orElse(Some(cr.id)),
pthid = cr.pthid,
from = cr.to,
to = cr.from,
)
}

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 = {
assert(message.to.length == 1, "The recipient is ambiguous. Need to have only 1 recipient") // TODO return error
message.to.head
},
)
}
/** Parse a generecy DIDComm Message into a ConnectionResponse */
def fromMessage(message: Message): Either[String, ConnectionResponse] =
for {
piuri <-
if (message.`type` == ConnectionResponse.`type`) Right(message.`type`)
else Left(s"Message MUST be of the type '${ConnectionResponse.`type`}' instead of '${message.`type`}'")
body <- message.body.asJson
.as[ConnectionResponse.Body]
.left
.map(ex => "Fail to parse the body of the ConnectionResponse because: " + ex.message)
ret <- message.to match
case Seq(inviter) => // is from only one inviter
message.from match
case Some(invitee) =>
Right(
ConnectionResponse(
id = message.id,
`type` = piuri,
body = body,
thid = message.thid,
pthid = message.pthid,
from = invitee, // TODO get
to = inviter
)
)
case None => Left("ConnectionResponse needs to define the Inviter")
case _ => Left("The inviter (recipient) is ambiguous. Message need to have only 1 recipient")
} yield ret

given Encoder[ConnectionResponse] = deriveEncoder[ConnectionResponse]

given Decoder[ConnectionResponse] = deriveDecoder[ConnectionResponse]
}

Expand All @@ -63,6 +77,7 @@ final case class ConnectionResponse(
from: DidId,
to: DidId,
thid: Option[String],
pthid: Option[String],
body: ConnectionResponse.Body,
) {
assert(`type` == "https://atalaprism.io/mercury/connections/1.0/response")
Expand All @@ -73,6 +88,7 @@ final case class ConnectionResponse(
from = Some(this.from),
to = Seq(this.to),
thid = this.thid,
pthid = this.pthid,
body = this.body.asJson.asObject.get,
)
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class CoordinateMediationSpec extends ZSuite {
val connectionRequest = """{
| "id" : "46430433-d872-4d4c-8376-0fa72cf124c2",
| "thid" : "544659a7-a8e3-4101-a918-6afef6ad7bbb",
| "pthid": "some-pthid",
| "typ" : "application/didcomm-plain+json",
| "type" : "https://atalaprism.io/mercury/connections/1.0/request",
| "body" : {"goal_code": "Connect"},
Expand All @@ -22,7 +23,9 @@ class CoordinateMediationSpec extends ZSuite {
|}""".stripMargin
val aux = parse(connectionRequest)
.flatMap(_.as[Message]) // Message
.map(e => ConnectionRequest.readFromMessage(e))
.left
.map(ex => fail(ex.getMessage())) // fail / get error
.flatMap(e => ConnectionRequest.fromMessage(e))
assertEquals(
aux,
Right(
Expand All @@ -32,6 +35,7 @@ class CoordinateMediationSpec extends ZSuite {
from = DidId("did:test:alice"),
to = DidId("did:test:bob"),
thid = Some("544659a7-a8e3-4101-a918-6afef6ad7bbb"),
pthid = Some("some-pthid"),
body = ConnectionRequest.Body(goal_code = Some("Connect")),
)
)
Expand All @@ -47,7 +51,8 @@ class CoordinateMediationSpec extends ZSuite {
to = DidId(
"did:peer:2.Ez6LSr1TzNDH5S4GMtn1ELG6P6xBdLcFxQ8wBaZCn8bead7iK.Vz6MknkPqgbvK4c7GhsKzi2EyBV4rZbvtygJqxM4Eh8EF5DGB.SeyJyIjpbImRpZDpwZWVyOjIuRXo2TFNrV05SZ3k1d1pNTTJOQjg4aDRqakJwN0U4N0xLTXdkUGVCTFRjbUNabm5uby5WejZNa2pqQ3F5SkZUSHFpWGtZUndYcVhTZlo2WWtVMjFyMzdENkFCN1hLMkhZNXQyLlNleUpwWkNJNkltNWxkeTFwWkNJc0luUWlPaUprYlNJc0luTWlPaUpvZEhSd2N6b3ZMMjFsWkdsaGRHOXlMbkp2YjNSemFXUXVZMnh2ZFdRaUxDSmhJanBiSW1ScFpHTnZiVzB2ZGpJaVhYMCM2TFNrV05SZ3k1d1pNTTJOQjg4aDRqakJwN0U4N0xLTXdkUGVCTFRjbUNabm5ubyJdLCJzIjoiaHR0cHM6Ly9tZWRpYXRvci5yb290c2lkLmNsb3VkIiwiYSI6WyJkaWNvbW0vdjIiXSwidCI6ImRtIn0"
),
thid = Some("52dc177a-05dc-4deb-ab57-ac9d5e3ff10c"),
thid = None,
pthid = Some("52dc177a-05dc-4deb-ab57-ac9d5e3ff10c"),
body = ConnectionResponse.Body(
goal_code = Some("connect"),
goal = Some("Establish a trust connection between two peers"),
Expand All @@ -60,7 +65,7 @@ class CoordinateMediationSpec extends ZSuite {
| "id" : "b7878bfc-16d5-49dd-a443-4e87a3c4c8c6",
| "from" : "did:peer:2.Ez6LSpwvTbwvMF5xtSZ6uNoZvWNcPGx1J2ziuais63CpB1UDe.Vz6MkmutH2XW9ybLtSyYRvYcyUbUPWneev6oVu9zfoEmFxQ2y.SeyJ0IjoiZG0iLCJzIjoiaHR0cDovL2hvc3QuZG9ja2VyLmludGVybmFsOjgwODAvZGlkY29tbSIsInIiOltdLCJhIjpbImRpZGNvbW0vdjIiXX0",
| "to" : "did:peer:2.Ez6LSr1TzNDH5S4GMtn1ELG6P6xBdLcFxQ8wBaZCn8bead7iK.Vz6MknkPqgbvK4c7GhsKzi2EyBV4rZbvtygJqxM4Eh8EF5DGB.SeyJyIjpbImRpZDpwZWVyOjIuRXo2TFNrV05SZ3k1d1pNTTJOQjg4aDRqakJwN0U4N0xLTXdkUGVCTFRjbUNabm5uby5WejZNa2pqQ3F5SkZUSHFpWGtZUndYcVhTZlo2WWtVMjFyMzdENkFCN1hLMkhZNXQyLlNleUpwWkNJNkltNWxkeTFwWkNJc0luUWlPaUprYlNJc0luTWlPaUpvZEhSd2N6b3ZMMjFsWkdsaGRHOXlMbkp2YjNSemFXUXVZMnh2ZFdRaUxDSmhJanBiSW1ScFpHTnZiVzB2ZGpJaVhYMCM2TFNrV05SZ3k1d1pNTTJOQjg4aDRqakJwN0U4N0xLTXdkUGVCTFRjbUNabm5ubyJdLCJzIjoiaHR0cHM6Ly9tZWRpYXRvci5yb290c2lkLmNsb3VkIiwiYSI6WyJkaWNvbW0vdjIiXSwidCI6ImRtIn0",
| "thid" : "52dc177a-05dc-4deb-ab57-ac9d5e3ff10c",
| "pthid" : "52dc177a-05dc-4deb-ab57-ac9d5e3ff10c",
| "body" : {
| "goal_code" : "connect",
| "goal" : "Establish a trust connection between two peers",
Expand All @@ -69,12 +74,8 @@ class CoordinateMediationSpec extends ZSuite {
| }
|}""".stripMargin

println(obj.asJson)

val aux = parse(connectionRequest)
.flatMap(_.as[ConnectionResponse])
assertEquals(
aux,
parse(connectionRequest).flatMap(_.as[ConnectionResponse]),
Right(obj)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ object Invitation {

final case class Body(
goal_code: String,
goal: String,
goal: String, // TODO goal can be optional
accept: Seq[String]
)

Expand All @@ -40,11 +40,4 @@ object Invitation {
given Encoder[Invitation] = deriveEncoder[Invitation]
given Decoder[Invitation] = deriveDecoder[Invitation]

// Utils methods
def invitation2Connect(from: DidId): Invitation = {
Invitation(
from = from,
body = Invitation.Body(goal_code = "connect", goal = "Establish a trust connection between two peers", Nil)
)
}
}

0 comments on commit 82eca31

Please sign in to comment.