Skip to content

Commit

Permalink
feat: Send problem report on missing protocol or wrong version or wor…
Browse files Browse the repository at this point in the history
…ng role
  • Loading branch information
FabioPinheiro committed Jul 25, 2023
1 parent 6dbd93c commit 15a727c
Show file tree
Hide file tree
Showing 7 changed files with 244 additions and 11 deletions.
77 changes: 77 additions & 0 deletions TASKs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# ATL-5236 - Error Handling (PRISM Agent and Mediator)

https://input-output.atlassian.net/browse/ATL-5236

https://identity.foundation/didcomm-messaging/spec/#problem-reports


On each step of all our protocols processing, when something wrong is happening, we need to:
Goals
- Update the record to a documented error state
- Log the error in the service logs
- Send the problem report message when appropriate

Goal other: error recover/resilient
- [optional] Send event that record state changed to error
- Decide on the policy of re-trying sending errors (one of the proposals is just to send it once, and if a recipient did not get this, then it’s on its own requesting record ID state)



Tasks:
- (2W * 2Dev) Mediator (Note: most errors in Mediator will be synchronous)
- Store messages when sending (1w)
- Catch Errors and send Problem Reports (1w):
- (sync) e.p.crypto - is message is tampering (any crypto error).
- (sync) e.p.crypto.unsupported - is message is tampering (any crypto error).
- (sync & async) e.p.crypto.replay - if the message is replay (possible he replay attack).
- (sync) e.p.req - pickup message before enroling.
- (sync) e.p.me.res.storage - connection MongoBD is not working.
- (sync) e.p.me.res.storage - business logic MongoBD is not working.
- (sync) e.p.did - for any DID method that is not `did.peer`.
- (sync) e.p.did.malformed - for any DID method malformed.
- (sync) e.p.msg - for parsing error from the message.
- (sync) e.p.msg.unsupported - for the message type LiveModeChange and all message that is not role of the mediator
- [DONE] MediateGrant
- [DONE] MediateDeny
- [DONE] KeylistResponse
- [DONE] Status = https://didcomm.org/messagepickup/3.0/status
- [DONE] LiveModeChange - https://didcomm.org/messagepickup/3.0/live-delivery-change
- [TODO] ...
- (sync) e.p.msg.unsupported - for parsing error due to unsupported version or protocol.
- [DONE] MissingProtocolExecuter (unsupported protocol it also works fine for unsupported versions)
- (sync & async) e.p.req.not_enroll - Get a Forward message to a DID that is not enrolled.
- (sync & async) e.p.me - catch all error at the end.
- Receive a problem report (1w):
- in case of Warnings Reply `w.p` -> log warnings and escalate to an error `e.p` on the reply
- in case of Error `e.p` -> log error

- (4/6W * 2Dev) PRISM Agent (Note: most error in PRISM Agent will be asynchronous)
- Store DID Comm messages in a searchable way (4W)
- Store receiving messages (in a searchable way)
- Store sending messages (in a searchable way)
- both plaintext and encrypted
- [optional] Maybe later MongoBD for PRISM Agent
- Implement Problem Reports in Mercury (DONE??)
- Catch all the Errors and send Problem Reports: (4W)
- e.p.xfer - if it found nobody listening on the specified port. (service endpoint unavailable)
- (async) e.p.crypto - is message is tampering (any crypto error).
- (async) e.p.crypto.unsupported - is message is tampering (any crypto error).
- (sync & async) e.p.crypto.replay - if the message is replay (possible he replay attack).
- (async) e.p.me.res.storage - connection MongoBD is not working.
- (async) e.p.me.res.storage - business logic MongoBD is not working.
- (async) e.p.did - for any DID method that is not `did.peer`.
- (async) e.p.did.malformed - for any DID method malformed.
- (async) e.p.msg - for parsing errors from the message.
- (async) e.p.msg.unsupported - for the message with the wrong role of the agent.
- (async) e.p.msg.unsupported - for parsing error due to unsupported version or protocol.
- (sync & async) e.p.me - catch all error at the end.
- (async) TODO `w.m`
- Receive e Problem Reports (1W):
- support for scope `m` -> state update (of the protocol execution)
- support for scope `p` -> state update (and fail protocol execution)

- Other task (not for this ticket)
- Traceability of the MsgID of the Problem Report to the original error (2d) -> ATL-4147
- [optional] Log - https://input-output.atlassian.net/browse/ATL-4147
- escalate_to must be configurable (1d)
- [optional] update the protocol with new tokens (2d)
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ inThisBuild(

/** Versions */
lazy val V = new {
val scalaDID = "0.1.0-M6"
val scalaDID = "0.1.0-M6+0-b02d2b9e+20230724-1637-SNAPSHOT"
// val scalajsJavaSecureRandom = "1.0.0"

// FIXME another bug in the test framework https://github.com/scalameta/munit/issues/554
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import io.iohk.atala.mediator.db.*
import io.iohk.atala.mediator.protocols.NullProtocolExecuter
import zio.*
import zio.json.*
import io.iohk.atala.mediator.protocols.MissingProtocolExecuter
//TODO pick a better name // maybe "Protocol" only

trait ProtocolExecuter[-R] {
Expand All @@ -29,7 +30,7 @@ trait ProtocolExecuter[-R] {
object ProtocolExecuter {
type Services = Resolver & Agent & Operations & MessageDispatcher
}
case class ProtocolExecuterCollection[-R](executers: ProtocolExecuter[R]*) extends ProtocolExecuter[R] {
case class ProtocolExecuterCollection[-R <: Agent](executers: ProtocolExecuter[R]*) extends ProtocolExecuter[R] {

override def suportedPIURI: Seq[PIURI] = executers.flatMap(_.suportedPIURI)

Expand All @@ -39,14 +40,16 @@ case class ProtocolExecuterCollection[-R](executers: ProtocolExecuter[R]*) exten
plaintextMessage: PlaintextMessage,
): ZIO[R1, MediatorError, Option[EncryptedMessage]] =
selectExecutersFor(plaintextMessage.`type`) match
case None => NullProtocolExecuter.execute(plaintextMessage)
// case None => NullProtocolExecuter.execute(plaintextMessage)
case None => MissingProtocolExecuter.execute(plaintextMessage)
case Some(px) => px.execute(plaintextMessage)

override def program[R1 <: R](
plaintextMessage: PlaintextMessage,
): ZIO[R1, MediatorError, Action] =
selectExecutersFor(plaintextMessage.`type`) match
case None => NullProtocolExecuter.program(plaintextMessage)
// case None => NullProtocolExecuter.program(plaintextMessage)
case None => MissingProtocolExecuter.program(plaintextMessage)
case Some(px) => px.program(plaintextMessage)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,34 @@ object MediatorCoordinationExecuter extends ProtocolExecuterWithServices[Protoco
case `piuriKeylistQuery` => plaintextMessage.toKeylistQuery
case `piuriKeylist` => plaintextMessage.toKeylist
}).map {
case m: MediateGrant => ZIO.logWarning("MediateGrant") *> ZIO.succeed(NoReply)
case m: MediateDeny => ZIO.logWarning("MediateDeny") *> ZIO.succeed(NoReply)
case m: MediateGrant =>
ZIO.logWarning("MediateGrant") *> ZIO.succeed(NoReply) *>
ZIO.succeed(
SyncReplyOnly(
Problems
.unsupportedProtocolRole(
from = m.to.asFROM,
to = m.from.asTO,
pthid = m.id, // TODO CHECK pthid
piuri = m.piuri,
)
.toPlaintextMessage
)
)
case m: MediateDeny =>
ZIO.logWarning("MediateDeny") *> ZIO.succeed(NoReply) *>
ZIO.succeed(
SyncReplyOnly(
Problems
.unsupportedProtocolRole(
from = m.to.asFROM,
to = m.from.asTO,
pthid = m.id, // TODO CHECK pthid
piuri = m.piuri,
)
.toPlaintextMessage
)
)
case m: MediateRequest =>
for {
_ <- ZIO.logInfo("MediateRequest")
Expand Down Expand Up @@ -74,7 +100,20 @@ object MediatorCoordinationExecuter extends ProtocolExecuterWithServices[Protoco
}
}
} yield SyncReplyOnly(m.makeKeylistResponse(updateResponse).toPlaintextMessage)
case m: KeylistResponse => ZIO.logWarning("KeylistResponse") *> ZIO.succeed(NoReply)
case m: KeylistResponse =>
ZIO.logWarning("KeylistResponse") *> ZIO.succeed(NoReply) *>
ZIO.succeed(
SyncReplyOnly(
Problems
.unsupportedProtocolRole(
from = m.to.asFROM,
to = m.from.asTO,
pthid = m.id, // TODO CHECK pthid
piuri = m.piuri,
)
.toPlaintextMessage
)
)
case m: KeylistQuery =>
for {
_ <- ZIO.logInfo("KeylistQuery")
Expand All @@ -94,7 +133,7 @@ object MediatorCoordinationExecuter extends ProtocolExecuterWithServices[Protoco
case Some(response) => SyncReplyOnly(response.toPlaintextMessage)
case m: Keylist => ZIO.logWarning("Keylist") *> ZIO.succeed(NoReply)
} match
case Left(error) => ZIO.logError(error) *> ZIO.succeed(NoReply)
case Left(error) => ZIO.logError(error) *> ZIO.succeed(NoReply) // TODO error report
case Right(program) => program
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package io.iohk.atala.mediator.protocols

import zio.ZIO

import fmgp.did.*
import fmgp.did.comm.PlaintextMessage
import io.iohk.atala.mediator.MissingProtocolError
import io.iohk.atala.mediator.actions.ProtocolExecuter
import io.iohk.atala.mediator.actions.Reply

object MissingProtocolExecuter extends ProtocolExecuter[Agent] {

override def suportedPIURI = Seq()
override def program[R1 <: Agent](plaintextMessage: PlaintextMessage) =
ZIO
.service[Agent]
.map(agent =>
Reply(
Problems
.unsupportedProtocolType(
to = plaintextMessage.from.map(_.asTO).toSet,
from = agent.id,
pthid = plaintextMessage.id,
piuri = plaintextMessage.`type`,
)
.toPlaintextMessage
)
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,20 @@ object PickupExecuter
live_delivery = None, // TODO
)
} yield SyncReplyOnly(status.toPlaintextMessage)
case m: Status => ZIO.logInfo("Status") *> ZIO.succeed(NoReply)
case m: Status =>
ZIO.logInfo("Status") *>
ZIO.succeed(
SyncReplyOnly(
Problems
.unsupportedProtocolRole(
from = m.to.asFROM,
to = m.from.asTO,
pthid = m.id, // TODO CHECK pthid
piuri = m.piuri,
)
.toPlaintextMessage
)
)
case m: DeliveryRequest =>
for {
_ <- ZIO.logInfo("DeliveryRequest")
Expand All @@ -73,7 +86,7 @@ object PickupExecuter
didRequestingMessages = m.from.asFROMTO
mDidAccount <- repoDidAccount.getDidAccount(didRequestingMessages.toDID)
msgHash = mDidAccount match
case None => ???
case None => ??? // TODO ERROR
case Some(didAccount) => didAccount.messagesRef.filter(_.state == false).map(_.hash)
allMessagesFor <- repoMessageItem.findByIds(msgHash)
messagesToReturn =
Expand Down Expand Up @@ -115,7 +128,20 @@ object PickupExecuter
m.message_id_list
)
} yield NoReply
case m: LiveModeChange => ZIO.logWarning("LiveModeChange not implemented") *> ZIO.succeed(NoReply) // TODO
case m: LiveModeChange =>
ZIO.logWarning("LiveModeChange not implemented") *>
ZIO.succeed(
SyncReplyOnly(
Problems
.protocolNotImplemented(
from = m.to.asFROM,
to = m.from.asTO,
pthid = m.id, // TODO CHECK pthid
piuri = m.piuri,
)
.toPlaintextMessage
)
)

} match
case Left(error) => ZIO.logError(error) *> ZIO.succeed(NoReply)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package io.iohk.atala.mediator.protocols

import fmgp.did.*
import fmgp.did.comm.*
import fmgp.did.comm.protocol.reportproblem2.*

object Problems {
def unsupportedProtocolType(
to: Set[TO],
from: FROM,
pthid: MsgID,
piuri: PIURI,
) = ProblemReport(
// id: MsgID = MsgID(),
to = to,
from = from, // Can it be Option?
pthid = pthid,
ack = None, // Option[Seq[MsgID]],
code = ProblemCode.ErroFail("msg", "unsupported"),
comment = None, // Option[String],
args = None, // Option[Seq[String]],
escalate_to = None, // Option[String],
)

def unsupportedProtocolRole(
to: TO,
from: FROM,
pthid: MsgID,
piuri: PIURI,
) = ProblemReport(
// id: MsgID = MsgID(),
to = Set(to),
from = from, // Can it be Option?
pthid = pthid,
ack = None, // Option[Seq[MsgID]],
code = ProblemCode.ErroFail("msg", "unsupported"),
comment = None, // Option[String],
args = None, // Option[Seq[String]],
escalate_to = None, // Option[String],
)

def protocolNotImplemented(
to: TO,
from: FROM,
pthid: MsgID,
piuri: PIURI,
) = ProblemReport(
// id: MsgID = MsgID(),
to = Set(to),
from = from, // Can it be Option?
pthid = pthid,
ack = None, // Option[Seq[MsgID]],
code = ProblemCode.ErroFail("msg", "unsupported"),
comment = None, // Option[String],
args = None, // Option[Seq[String]],
escalate_to = None, // Option[String],
)

}

0 comments on commit 15a727c

Please sign in to comment.