Skip to content

Commit

Permalink
feat(agent): add didcomm connection protocol and invitation protocol
Browse files Browse the repository at this point in the history
  • Loading branch information
goncalo-frade-iohk committed Dec 1, 2022
1 parent 40ce0e3 commit c112c8b
Show file tree
Hide file tree
Showing 7 changed files with 254 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import Domain
import Foundation

class DIDCommConnectionRunner {
private let mercury: Mercury
private let invitationMessage: Message
private let ownDID: DID
private let connectionMaker: (DID, DID, Mercury) -> DIDCommConnection
private var request: HandshakeRequest?

init(
mercury: Mercury,
invitationMessage: Message,
ownDID: DID,
connectionMaker: @escaping (DID, DID, Mercury) -> DIDCommConnection
) {
self.mercury = mercury
self.invitationMessage = invitationMessage
self.ownDID = ownDID
self.connectionMaker = connectionMaker
}

func run() async throws -> DIDCommConnection {
let request = try HandshakeRequest(inviteMessage: invitationMessage, from: ownDID)
try await mercury.sendMessage(msg: try request.makeMessage())
let connection = connectionMaker(ownDID, request.to, mercury)
guard
let message = try await connection.awaitMessageResponse(id: request.id),
message.piuri == ProtocolTypes.didcommconnectionResponse.rawValue
else { throw PrismAgentError.noHandshakeResponseError }
return connection
}
}
62 changes: 62 additions & 0 deletions PrismAgent/Sources/Protocols/Connection/HandshakeRequest.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import Domain
import Foundation

struct HandshakeRequest {
// Connection Body is the same as Invitation body message
struct Body: Codable {
let goalCode: String?
let goal: String?
let accept: [String]

init(
goalCode: String? = nil,
goal: String? = nil,
accept: [String] = []
) {
self.goalCode = goalCode
self.goal = goal
self.accept = accept
}
}
let type: String = ProtocolTypes.didcommconnectionRequest.rawValue
let id: String
let from: DID
// swiftlint:disable identifier_name
let to: DID
// swiftlint:enable identifier_name
let thid: String?
let body: Body

init(inviteMessage: Message, from: DID) throws {
guard let toDID = inviteMessage.from else { throw PrismAgentError.invitationIsInvalidError }
let body = try JSONDecoder().decode(Body.self, from: inviteMessage.body)
self.init(from: from, to: toDID, thid: inviteMessage.id, body: body)
}

init(
id: String = UUID().uuidString,
from: DID,
// swiftlint:disable identifier_name
to: DID,
// swiftlint:enable identifier_name
thid: String?,
body: Body
) {
self.id = id
self.from = from
self.to = to
self.thid = thid
self.body = body
}

func makeMessage() throws -> Message {
Message(
id: id,
piuri: type,
from: from,
to: to,
body: try JSONEncoder().encode(self.body),
thid: thid
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import Domain
import Foundation

class DIDCommInvitationRunner {
private let mercury: Mercury
private let url: URL

init(mercury: Mercury, url: URL) {
self.mercury = mercury
self.url = url
}

func run() throws -> Message {
let messageString = try OutOfBandParser().parseMessage(url: url)
let message = try mercury.unpackMessage(msg: messageString, options: .expectDecryptByAllKeys).result
guard message.piuri == ProtocolTypes.didcomminvitation.rawValue else {
throw PrismAgentError.unknownInvitationTypeError
}
return message
}
}
17 changes: 17 additions & 0 deletions PrismAgent/Sources/Protocols/Invitation/V2/InvitationRunner.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import Domain
import Foundation

class InvitationRunner {
private let mercury: Mercury
private let url: URL

init(mercury: Mercury, url: URL) {
self.mercury = mercury
self.url = url
}

func run() async throws -> Message {
let messageString = try OutOfBandParser().parseMessage(url: url)
return try await mercury.unpackMessage(msg: messageString, options: .expectDecryptByAllKeys).result
}
}
43 changes: 43 additions & 0 deletions PrismAgent/Tests/ConnectionRunnerTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import Domain
@testable import PrismAgent
import XCTest

final class ConnectionRunnerTests: XCTestCase {
private let mercury = MercuryStub()

func testWhenInvitationMessageThenTryConnectingWith() async throws {
let body = HandshakeRequest.Body(
goalCode: "123",
goal: "Test",
accept: ["Test1"]
)

let exampleMessage = Message(
piuri: ProtocolTypes.didcomminvitation.rawValue,
from: DID(index: 1),
to: DID(index: 2),
body: try JSONEncoder().encode(body)
)

let exampleMessageResponse = Message(
piuri: ProtocolTypes.didcommconnectionResponse.rawValue,
from: DID(index: 2),
to: DID(index: 1),
body: try JSONEncoder().encode(body)
)

let selfDID = DID(index: 3)
let connection = ConnectionStub()
connection.awaitMessageResponse = exampleMessageResponse

_ = try await DIDCommConnectionRunner(
mercury: mercury,
invitationMessage: exampleMessage,
ownDID: selfDID
) { holderDID, otherDID, _ in
connection.holderDID = holderDID
connection.otherDID = otherDID
return connection
}.run()
}
}
24 changes: 24 additions & 0 deletions PrismAgent/Tests/DIDCommInvitationRunnerTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import Domain
@testable import PrismAgent
import XCTest

final class DIDCommInvitationRunnerTests: XCTestCase {
private let mercury = MercuryStub()

func testWhenReceivedOOBUrlThenParseMessage() throws {
let exampleMessage = Message(piuri: ProtocolTypes.didcomminvitation.rawValue, body: Data())
let queryString = try mercury.packMessage(msg: exampleMessage).result
let exampleURL = URL(string: "localhost:8080?_oob=\(queryString)")!

let parsedMessage = try DIDCommInvitationRunner(mercury: mercury, url: exampleURL).run()
XCTAssertEqual(exampleMessage, parsedMessage)
}

func testWhenInvalidInvitationTypeThenThrowError() throws {
let exampleMessage = Message(piuri: "Something wrong", body: Data())
let queryString = try mercury.packMessage(msg: exampleMessage).result
let exampleURL = URL(string: "localhost:8080?_oob=\(queryString)")!

XCTAssertThrowsError(try DIDCommInvitationRunner(mercury: mercury, url: exampleURL).run())
}
}
54 changes: 54 additions & 0 deletions PrismAgent/Tests/HandshakeRequestTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import Domain
@testable import PrismAgent
import XCTest

final class HandshakeRequestTests: XCTestCase {
func testHandshakeRequestMakeMessage() throws {
let request = HandshakeRequest(
from: DID(index: 1),
to: DID(index: 2),
thid: "0",
body: .init(goalCode: "1", goal: "Test", accept: ["Test1"])
)

let message = try request.makeMessage()
XCTAssertEqual(message.id, request.id)
XCTAssertEqual(message.piuri, request.type)
XCTAssertEqual(message.from, request.from)
XCTAssertEqual(message.to, request.to)
XCTAssertEqual(message.thid, request.thid)
let decodedBody = try JSONDecoder().decode(HandshakeRequest.Body.self, from: message.body)
XCTAssertEqual(decodedBody, request.body)
}

func testHandshakeRequestInitFromInvitationMessage() throws {
let body = HandshakeRequest.Body(
goalCode: "123",
goal: "Test",
accept: ["Test1"]
)

let exampleMessage = Message(
piuri: ProtocolTypes.didcomminvitation.rawValue,
from: DID(index: 1),
to: DID(index: 2),
body: try JSONEncoder().encode(body)
)

let selfDID = DID(index: 3)

let request = try HandshakeRequest(inviteMessage: exampleMessage, from: selfDID)
XCTAssertNotEqual(exampleMessage.id, request.id)
XCTAssertEqual(request.type, ProtocolTypes.didcommconnectionRequest.rawValue)
XCTAssertEqual(selfDID, request.from)
XCTAssertEqual(exampleMessage.from, request.to)
XCTAssertEqual(exampleMessage.id, request.thid)
XCTAssertEqual(body, request.body)
}
}

extension HandshakeRequest.Body: Equatable {
public static func == (lhs: HandshakeRequest.Body, rhs: HandshakeRequest.Body) -> Bool {
lhs.goalCode == rhs.goalCode && lhs.goal == rhs.goal && lhs.accept == rhs.accept
}
}

0 comments on commit c112c8b

Please sign in to comment.