Skip to content

Commit

Permalink
feat: Reply to Trust Pings (#496)
Browse files Browse the repository at this point in the history
Implement the Trust Ping protocol
Reply to Trust Pings in the Prism agent
  • Loading branch information
FabioPinheiro authored Apr 6, 2023
1 parent 2bde732 commit b07da78
Show file tree
Hide file tree
Showing 4 changed files with 166 additions and 5 deletions.
9 changes: 9 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,14 @@ lazy val protocolPresentProof = project
.settings(libraryDependencies += D.munitZio)
.dependsOn(models)

lazy val protocolTrustPing = project
.in(file("mercury/mercury-library/protocol-trust-ping"))
.settings(name := "mercury-protocol-trust-ping")
.settings(libraryDependencies += D.zio)
.settings(libraryDependencies ++= Seq(D.circeCore, D.circeGeneric, D.circeParser))
.settings(libraryDependencies += D.munitZio)
.dependsOn(models)

// ################
// ### Resolver ###
// ################
Expand Down Expand Up @@ -518,6 +526,7 @@ lazy val agent = project // maybe merge into models
protocolPresentProof,
protocolConnection,
protocolReportProblem,
protocolTrustPing,
)

/** agents implementation with didcommx */
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package io.iohk.atala.mercury.protocol.trustping

import io.circe._
import io.circe.syntax._
import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder}
import io.iohk.atala.mercury.model._

/** https://identity.foundation/didcomm-messaging/spec/#trust-ping-protocol-20 */
final case class TrustPing(
`type`: PIURI = TrustPing.`type`,
id: String = java.util.UUID.randomUUID().toString,
from: DidId,
to: DidId,
body: TrustPing.Body,
) {
assert(`type` == TrustPing.`type`)

def makeMessage: Message = Message(
id = this.id,
`type` = this.`type`,
from = Some(this.from),
to = Seq(this.to),
body = this.body.asJson.asObject.get,
)

def makeReply = TrustPingResponse(
thid = this.id,
from = to,
to = from,
)
}

object TrustPing {
def `type`: PIURI = "https://didcomm.org/trust-ping/2.0/ping"
case class Body(
response_requested: Option[Boolean] = Some(true),
)
object Body {
given Encoder[Body] = deriveEncoder[Body]
given Decoder[Body] = deriveDecoder[Body]
}
given Encoder[TrustPing] = deriveEncoder[TrustPing]
given Decoder[TrustPing] = deriveDecoder[TrustPing]

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

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package io.iohk.atala.mercury.protocol.trustping

import io.circe._
import io.circe.syntax._
import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder}
import io.iohk.atala.mercury.model._

final case class TrustPingResponse(
`type`: PIURI = TrustPingResponse.`type`,
id: String = java.util.UUID.randomUUID().toString,
thid: String,
from: DidId,
to: DidId,
) {
assert(`type` == TrustPingResponse.`type`)

def makeMessage: Message = Message(
id = this.id,
thid = Some(this.thid),
`type` = this.`type`,
from = Some(this.from),
to = Seq(this.to),
)
}

object TrustPingResponse {
def `type`: PIURI = "https://didcomm.org/trust-ping/2.0/ping-response"

/** Parse a generecy DIDComm Message into a TrustPingResponse */
def fromMessage(message: Message): Either[String, TrustPingResponse] =
for {
piuri <-
if (message.`type` == TrustPingResponse.`type`) Right(message.`type`)
else Left(s"Message MUST be of the type '${TrustPingResponse.`type`}' instead of '${message.`type`}'")
ret <- message.to match
case Seq(onlyOneRecipient) =>
message.from match
case Some(from) =>
Right(
TrustPingResponse(
id = message.id,
thid = message.thid.getOrElse(???), // TODO
`type` = piuri,
from = from,
to = onlyOneRecipient,
)
)
case None => Left("TrustPingResponse needs to define the recipient")
case _ => Left("The recipient is ambiguous. Need to have only 1 recipient")
} yield ret
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ import zio.stream.ZStream

import java.io.IOException
import java.util.concurrent.Executors
import io.iohk.atala.mercury.protocol.trustping.TrustPing

object Modules {

Expand Down Expand Up @@ -98,7 +99,8 @@ object Modules {
def didCommServiceEndpoint(port: Int) = {
val header = "content-type" -> MediaTypes.contentTypeEncrypted
val app: HttpApp[
DidOps & DidAgent & CredentialService & PresentationService & ConnectionService & ManagedDIDService,
DidOps & DidAgent & CredentialService & PresentationService & ConnectionService & ManagedDIDService & HttpClient &
DidAgent & DIDResolver,
Throwable
] =
Http.collectZIO[Request] {
Expand Down Expand Up @@ -194,10 +196,9 @@ object Modules {
} yield msg.message
}

def webServerProgram(
jsonString: String
): ZIO[
DidOps & CredentialService & PresentationService & ConnectionService & ManagedDIDService,
def webServerProgram(jsonString: String): ZIO[
DidOps & CredentialService & PresentationService & ConnectionService & ManagedDIDService & HttpClient & DidAgent &
DIDResolver,
MercuryThrowable | DIDSecretStorageError,
Unit
] = {
Expand All @@ -211,6 +212,34 @@ object Modules {
connectionService <- ZIO.service[ConnectionService]
_ <- {
msg.piuri match {
// ##################
// ### Trust-Ping ###
// ##################
case s if s == TrustPing.`type` =>
for {
_ <- ZIO.logInfo("*" * 100)
trustPingMsg = TrustPing.fromMessage(msg)
_ <- ZIO.logInfo(s"TrustPing from ${msg.from}: " + msg + " -> " + trustPingMsg)
trustPingResponseMsg = trustPingMsg match {
case Left(value) => None
case Right(trustPing) =>
trustPing.body.response_requested match
case None => Some(trustPing.makeReply.makeMessage)
case Some(true) => Some(trustPing.makeReply.makeMessage)
case Some(false) => None
}
_ <- trustPingResponseMsg match
case None => ZIO.logWarning(s"Did not reply to the ${TrustPing.`type`}")
case Some(message) =>
MessagingService
.send(message)
.flatMap(response =>
response.status match
case c if c >= 200 & c < 300 => ZIO.unit
case c => ZIO.logWarning(response.toString())
)
} yield ()

// ########################
// ### issue-credential ###
// ########################
Expand Down

0 comments on commit b07da78

Please sign in to comment.