From d917bd9b55d78764bb87af499bfa696f627902a2 Mon Sep 17 00:00:00 2001 From: Goncalo-FradeIOHK Date: Tue, 10 Jan 2023 20:45:51 +0000 Subject: [PATCH] feat(sample): Fifth batch of sample app code This also introduces documentation on multiple public models and functions. --- Apollo/Sources/ApolloImpl+Public.swift | 6 +- Builders/Sources/PolluxBuilder.swift | 2 +- Domain/Sources/BBs/Apollo.swift | 80 ++++ Domain/Sources/BBs/Castor.swift | 49 ++ Domain/Sources/BBs/Mercury.swift | 23 + Domain/Sources/Models/DID.swift | 13 +- Domain/Sources/Models/DIDDocument.swift | 86 +++- Domain/Sources/Models/DIDPair.swift | 6 + Domain/Sources/Models/DIDUrl.swift | 11 +- Domain/Sources/Models/KeyPair.swift | 13 + Domain/Sources/Models/PrivateKey.swift | 4 + Domain/Sources/Models/PublicKey.swift | 8 + Domain/Sources/Models/Secret.swift | 22 +- Domain/Sources/Models/SecretResolver.swift | 8 +- Domain/Sources/Models/Seed.swift | 2 + Domain/Sources/Models/Signature.swift | 2 + .../Sources/Models/VerifiableCredential.swift | 25 +- .../Models/W3CVerifiableCredential.swift | 1 + .../DIDCommSecretsResolverWrapper.swift | 6 - .../PackEncryptedOperation.swift | 4 - Mercury/Sources/Helpers/Session.swift | 1 - Mercury/Sources/MercuryImpl+Public.swift | 4 - .../ConnectionsManager.swift | 1 - .../Sources/PrismAgent+Credentials.swift | 14 + .../PrismAgent+DIDHigherFucntions.swift | 162 +++++++ .../Sources/PrismAgent+Invitations.swift | 131 ++++++ .../Sources/PrismAgent+MessageEvents.swift | 79 ++++ PrismAgent/Sources/PrismAgent.swift | 434 ++++-------------- .../IssueCredentialProtocol.swift | 2 - .../IssueCredential/RequestCredential.swift | 2 +- .../Protocols/Pickup/PickupRunner.swift | 2 + .../RequestPresentation.swift | 2 +- PrismAgent/Tests/PresentationTests.swift | 19 +- PrismAgent/Tests/PrismAgentStartTests.swift | 13 - .../Tests/ProposePresentationTests.swift | 25 +- .../Tests/RequestPresentationTests.swift | 25 +- 36 files changed, 845 insertions(+), 442 deletions(-) create mode 100644 PrismAgent/Sources/PrismAgent+Credentials.swift create mode 100644 PrismAgent/Sources/PrismAgent+DIDHigherFucntions.swift create mode 100644 PrismAgent/Sources/PrismAgent+Invitations.swift create mode 100644 PrismAgent/Sources/PrismAgent+MessageEvents.swift delete mode 100644 PrismAgent/Tests/PrismAgentStartTests.swift diff --git a/Apollo/Sources/ApolloImpl+Public.swift b/Apollo/Sources/ApolloImpl+Public.swift index 4daf6e00..ebd3b7a3 100644 --- a/Apollo/Sources/ApolloImpl+Public.swift +++ b/Apollo/Sources/ApolloImpl+Public.swift @@ -100,13 +100,11 @@ returns random mnemonics nerver returns invalid mnemonics switch publicKey.curve { case "secp256k1": let verifier = JWTVerifier.es256(publicKey: publicKey.value) - let decoder = JWTDecoder.init(jwtVerifier: verifier) - let jwt = try decoder.decode(JWT.self, fromString: jwk) + let decoder = JWTDecoder(jwtVerifier: verifier) return jwk default: let verifier = JWTVerifier.none - let decoder = JWTDecoder.init(jwtVerifier: verifier) - let jwt = try decoder.decode(JWT.self, fromString: jwk) + let decoder = JWTDecoder(jwtVerifier: verifier) return jwk } } diff --git a/Builders/Sources/PolluxBuilder.swift b/Builders/Sources/PolluxBuilder.swift index c453e9a5..4d82e1b7 100644 --- a/Builders/Sources/PolluxBuilder.swift +++ b/Builders/Sources/PolluxBuilder.swift @@ -1,5 +1,5 @@ -import Pollux import Domain +import Pollux public struct PolluxBuilder { let castor: Castor diff --git a/Domain/Sources/BBs/Apollo.swift b/Domain/Sources/BBs/Apollo.swift index bb0d9508..3ed52902 100644 --- a/Domain/Sources/BBs/Apollo.swift +++ b/Domain/Sources/BBs/Apollo.swift @@ -1,20 +1,100 @@ import Foundation public protocol Apollo { + /// createRandomMnemonics creates a random set of mnemonic phrases that can be used as a seed for generating a private key. + /// + /// - Returns: An array of mnemonic phrases func createRandomMnemonics() -> [String] + + /// createSeed takes in a set of mnemonics and a passphrase, and returns a seed object used to generate a private key. + /// This function may throw an error if the mnemonics or passphrase are invalid. + /// + /// - Parameters: + /// - mnemonics: An array of mnemonic phrases + /// - passphrase: A passphrase used to enhance the security of the seed + /// - Returns: A seed object + /// - Throws: An error if the mnemonics or passphrase are invalid func createSeed(mnemonics: [String], passphrase: String) throws -> Seed + + /// createRandomSeed creates a random seed and a corresponding set of mnemonic phrases. + /// + /// - Returns: A tuple containing an array of mnemonic phrases and a seed object func createRandomSeed() -> (mnemonic: [String], seed: Seed) + + /// createKeyPair creates a key pair (a private and public key) using a given seed and key curve. + /// + /// - Parameters: + /// - seed: A seed object used to generate the key pair + /// - curve: The key curve to use for generating the key pair + /// - Returns: A key pair object containing a private and public key func createKeyPair(seed: Seed, curve: KeyCurve) -> KeyPair + + /// createKeyPair creates a key pair using a given seed and a specified private key. This function may throw an error if the private key is invalid. + /// + /// - Parameters: + /// - seed: A seed object used to generate the key pair + /// - privateKey: The private key to use for generating the key pair + /// - Returns: A key pair object containing a private and public key + /// - Throws: An error if the private key is invalid func createKeyPair(seed: Seed, privateKey: PrivateKey) throws -> KeyPair + + /// compressedPublicKey compresses a given public key into a shorter, more efficient form. + /// + /// - Parameter publicKey: The public key to compress + /// - Returns: The compressed public key func compressedPublicKey(publicKey: PublicKey) -> CompressedPublicKey + + /// compressedPublicKey decompresses a given compressed public key into its original form. + /// + /// - Parameter compressedData: The compressed public key data + /// - Returns: The decompressed public key func compressedPublicKey(compressedData: Data) -> CompressedPublicKey + + /// signMessage signs a message using a given private key, returning the signature. + /// + /// - Parameters: + /// - privateKey: The private key to use for signing the message + /// - message: The message to sign, in binary data form + /// - Returns: The signature of the message func signMessage(privateKey: PrivateKey, message: Data) -> Signature + + /// signMessage signs a message using a given private key, returning the signature. This function may throw an error if the message is invalid. + /// + /// - Parameters: + /// - privateKey: The private key to use for signing the message + /// - message: The message to sign, in string form + /// - Returns: The signature of the message + /// - Throws: An error if the message is invalid func signMessage(privateKey: PrivateKey, message: String) throws -> Signature + + /// verifySignature verifies the authenticity of a signature using the corresponding public key, challenge, and signature. This function returns a boolean value indicating whether the signature is valid or not. + /// + /// - Parameters: + /// - publicKey: The public key associated with the signature + /// - challenge: The challenge used to generate the signature + /// - signature: The signature to verify + /// - Returns: A boolean value indicating whether the signature is valid or not func verifySignature( publicKey: PublicKey, challenge: Data, signature: Signature ) -> Bool + + /// getPrivateJWKJson converts a private key pair into a JSON Web Key (JWK) format with a given ID. This function may throw an error if the key pair is invalid. + /// + /// - Parameters: + /// - id: The ID to use for the JWK + /// - keyPair: The private key pair to convert to JWK format + /// - Returns: The private key pair in JWK format, as a string + /// - Throws: An error if the key pair is invalid func getPrivateJWKJson(id: String, keyPair: KeyPair) throws -> String + + /// getPublicJWKJson converts a public key pair into a JSON Web Key (JWK) format with a given ID. This function may throw an error if the key pair is invalid. + /// + /// - Parameters: + /// - id: The ID to use for the JWK + /// - keyPair: The public key pair to convert to JWK format + /// - Returns: The public key pair in JWK format, as a string + /// - Throws: An error if the key pair is invalid func getPublicJWKJson(id: String, keyPair: KeyPair) throws -> String } diff --git a/Domain/Sources/BBs/Castor.swift b/Domain/Sources/BBs/Castor.swift index fcc1983d..a2b8a85e 100644 --- a/Domain/Sources/BBs/Castor.swift +++ b/Domain/Sources/BBs/Castor.swift @@ -1,32 +1,81 @@ import Foundation public protocol Castor { + /// parseDID parses a string representation of a Decentralized Identifier (DID) into a DID object. This function may throw an error if the string is not a valid DID. + /// + /// - Parameter str: The string representation of the DID + /// - Returns: The DID object + /// - Throws: An error if the string is not a valid DID func parseDID(str: String) throws -> DID + + /// createPrismDID creates a DID for a prism (a device or server that acts as a DID owner and controller) using a given master public key and list of services. This function may throw an error if the master public key or services are invalid. + /// + /// - Parameters: + /// - masterPublicKey: The master public key of the prism + /// - services: The list of services offered by the prism + /// - Returns: The DID of the prism + /// - Throws: An error if the master public key or services are invalid func createPrismDID( masterPublicKey: PublicKey, services: [DIDDocument.Service] ) throws -> DID + /// createPeerDID creates a DID for a peer (a device or server that acts as a DID subject) using given key agreement and authentication key pairs and a list of services. This function may throw an error if the key pairs or services are invalid. + /// + /// - Parameters: + /// - keyAgreementKeyPair: The key pair used for key agreement (establishing secure communication between peers) + /// - authenticationKeyPair: The key pair used for authentication (verifying the identity of a peer) + /// - services: The list of services offered by the peer + /// - Returns: The DID of the peer + /// - Throws: An error if the key pairs or services are invalid func createPeerDID( keyAgreementKeyPair: KeyPair, authenticationKeyPair: KeyPair, services: [DIDDocument.Service] ) throws -> DID + /// resolveDID asynchronously resolves a DID to its corresponding DID Document. This function may throw an error if the DID is invalid or the document cannot be retrieved. + /// + /// - Parameter did: The DID to resolve + /// - Returns: The DID Document associated with the DID + /// - Throws: An error if the DID is invalid or the document cannot be retrieved func resolveDID(did: DID) async throws -> DIDDocument + /// verifySignature asynchronously verifies the authenticity of a signature using the corresponding DID, challenge, and signature data. This function returns a boolean value indicating whether the signature is valid or not. This function may throw an error if the DID or signature data are invalid. + /// + /// - Parameters: + /// - did: The DID associated with the signature + /// - challenge: The challenge used to generate the signature + /// - signature: The signature data to verify + /// - Returns: A boolean value indicating whether the signature is valid or not + /// - Throws: An error if the DID or signature data are invalid func verifySignature( did: DID, challenge: Data, signature: Data ) async throws -> Bool + /// verifySignature verifies the authenticity of a signature using the corresponding DID Document, challenge, and signature data. This function returns a boolean value indicating whether the signature is valid or not. This function may throw an error if the DID Document or signature data are invalid. + /// + /// - Parameters: + /// - document: The DID Document associated with the signature + /// - challenge: The challenge used to generate the signature + /// - signature: The signature data to verify + /// - Returns: A boolean value indicating whether the signature is valid or not + /// - Throws: An error if the DID Document or signature data are invalid func verifySignature( document: DIDDocument, challenge: Data, signature: Data ) throws -> Bool + /// getEcnumbasis generates a unique ECNUM basis string for a given DID and key pair. This function may throw an error if the DID or key pair are invalid. + /// + /// - Parameters: + /// - did: The DID associated with the key pair + /// - keyPair: The key pair to use for generating the ECNUM basis + /// - Returns: The ECNUM basis string + /// - Throws: An error if the DID or key pair are invalid func getEcnumbasis(did: DID, keyPair: KeyPair) throws -> String } diff --git a/Domain/Sources/BBs/Mercury.swift b/Domain/Sources/BBs/Mercury.swift index 2ab00bfa..b5a878ef 100644 --- a/Domain/Sources/BBs/Mercury.swift +++ b/Domain/Sources/BBs/Mercury.swift @@ -1,10 +1,33 @@ import Foundation public protocol Mercury { + /// packMessage asynchronously packs a given message object into a string representation. This function may throw an error if the message object is invalid. + /// + /// - Parameter msg: The message object to pack + /// - Returns: The string representation of the packed message + /// - Throws: An error if the message object is invalid func packMessage(msg: Domain.Message) async throws -> String + + /// unpackMessage asynchronously unpacks a given string representation of a message into a message object. This function may throw an error if the string is not a valid message representation. + /// + /// - Parameter msg: The string representation of the message to unpack + /// - Returns: The message object + /// - Throws: An error if the string is not a valid message representation func unpackMessage(msg: String) async throws -> Domain.Message + + /// sendMessage asynchronously sends a given message and returns the response data. This function may throw an error if the message is invalid or the send operation fails. + /// + /// - Parameter msg: The message to send + /// - Returns: The response data + /// - Throws: An error if the message is invalid or the send operation fails @discardableResult func sendMessage(msg: Message) async throws -> Data? + + /// sendMessageParseMessage asynchronously sends a given message and returns the response message object. This function may throw an error if the message is invalid, the send operation fails, or the response message is invalid. + /// + /// - Parameter msg: The message to send + /// - Returns: The response message object + /// - Throws: An error if the message is invalid, the send operation fails, or the response message is invalid @discardableResult func sendMessageParseMessage(msg: Message) async throws -> Message? } diff --git a/Domain/Sources/Models/DID.swift b/Domain/Sources/Models/DID.swift index 6de47e30..72c91da4 100644 --- a/Domain/Sources/Models/DID.swift +++ b/Domain/Sources/Models/DID.swift @@ -1,13 +1,21 @@ import Foundation +/// A type alias representing a DID method (a specific protocol or process used to resolve and manage DIDs) as a string. public typealias DIDMethod = String + +/// A type alias representing a DID method ID (a unique identifier within a DID method) as a string. public typealias DIDMethodId = String -/// Represents a DID with ``DIDMethod`` and ``DIDMethodId`` -/// As specified in [w3 standards](https://www.w3.org/TR/did-core/#dfn-did-schemes) +/// A DID is a unique and persistent identifier for a subject or object, such as a person, organization, or device. It is created and managed using a specific DID method, and consists of a schema, method, and method ID. The schema indicates the type of DID (e.g. "did"), the method indicates the specific protocol or process used to resolve and manage the DID (e.g. "prism"), and the method ID is a unique identifier within the DID method. +/// As specified in the [W3C DID standards](https://www.w3.org/TR/did-core/#dfn-did-schemes). public struct DID: Equatable { + /// The schema of the DID (e.g. "did") public let schema: String + + /// The method of the DID (e.g. "prism") public let method: DIDMethod + + /// The method ID of the DID public let methodId: DIDMethodId /// Initializes a standard DID @@ -26,6 +34,7 @@ public struct DID: Equatable { } /// String representation of this DID as specified in [w3 standards](https://www.w3.org/TR/did-core/#dfn-did-schemes) + /// This is a combination of the schema, method, and method ID, separated by colons (e.g. "did:prism:0xabc123"). public var string: String { "\(schema):\(method):\(methodId)" } /// Simple initializer that receives a String and returns a DID diff --git a/Domain/Sources/Models/DIDDocument.swift b/Domain/Sources/Models/DIDDocument.swift index 218d3272..26b5bcc9 100644 --- a/Domain/Sources/Models/DIDDocument.swift +++ b/Domain/Sources/Models/DIDDocument.swift @@ -7,12 +7,25 @@ public protocol DIDDocumentCoreProperty {} /// Represents a DIDDocument with ``DID`` and ``[DIDDocumentCoreProperty]`` /// As specified in [w3 standards](https://www.w3.org/TR/did-core/#data-model) +/// A DID Document consists of a DID, public keys, authentication protocols, service endpoints, and other metadata. It is used to verify the authenticity and identity of the DID, and to discover and interact with the associated subjects or objects. public struct DIDDocument { + /// Represents a Verification Method, which is a public key or other evidence used to authenticate the identity of a Decentralized Identifier (DID) or other subject or object. + /// + /// A Verification Method consists of a type (indicating the type of key or evidence), a public key or other data, and optional metadata such as a controller (the DID that controls the verification method) and purpose (the intended use of the verification method). It is typically included in a DID Document or other authentication credential. public struct VerificationMethod { + /// The ID of the verification method, represented as a DID URL. public let id: DIDUrl + + /// The controller of the verification method, represented as a DID. public let controller: DID + + /// The type of the verification method, indicated as a string (e.g. "EcdsaSecp256k1VerificationKey2019"). public let type: String + + /// The public key of the verification method, represented as a JSON Web Key (JWK). public let publicKeyJwk: [String: String]? + + /// The public key of the verification method, represented as a multibase encoded string. public let publicKeyMultibase: String? public init( @@ -36,10 +49,19 @@ public struct DIDDocument { } } + /// Represents a Service, which is a capability or endpoint offered by a Decentralized Identifier (DID) or other subject or object. + /// + /// A Service consists of an ID, type, and service endpoint, as well as optional metadata such as a priority and a description. It is typically included in a DID Document and can be used to discover and interact with the associated DID or subject or object. public struct Service: DIDDocumentCoreProperty { + /// Represents a service endpoint, which is a URI and other information that indicates how to access the service. public struct ServiceEndpoint { + /// The URI of the service endpoint. public let uri: String + + /// The types of content that the service endpoint can accept. public let accept: [String] + + /// The routing keys that can be used to route messages to the service endpoint. public let routingKeys: [String] public init( @@ -53,8 +75,13 @@ public struct DIDDocument { } } + /// The ID of the service, represented as a string. public let id: String + + /// The types of the service, indicated as an array of strings. public let type: [String] + + /// The service endpoint of the service. public let serviceEndpoint: ServiceEndpoint public init( @@ -68,7 +95,11 @@ public struct DIDDocument { } } + /// Represents a "also known as" property, which is a list of alternative names or identifiers for a Decentralized Identifier (DID) or other subject or object. + /// + /// The "also known as" property is typically included in a DID Document and can be used to associate the DID or subject or object with other names or identifiers. public struct AlsoKnownAs: DIDDocumentCoreProperty { + /// The values of the "also known as" property, represented as an array of strings. public let values: [String] public init(values: [String]) { @@ -76,7 +107,11 @@ public struct DIDDocument { } } + /// Represents a "controller" property, which is a list of Decentralized Identifiers (DIDs) that control the associated DID or subject or object. + /// + /// The "controller" property is typically included in a DID Document and can be used to indicate who has the authority to update or deactivate the DID or subject or object. public struct Controller: DIDDocumentCoreProperty { + /// The values of the "controller" property, represented as an array of DIDs. public let values: [DID] public init(values: [DID]) { @@ -84,7 +119,11 @@ public struct DIDDocument { } } + /// Represents a "verification methods" property, which is a list of Verification Methods associated with a Decentralized Identifier (DID) or other subject or object. + /// + /// The "verification methods" property is typically included in a DID Document and can be used to authenticate the identity of the DID or subject or object. public struct VerificationMethods: DIDDocumentCoreProperty { + /// The values of the "verification methods" property, represented as an array of VerificationMethod structs. public let values: [VerificationMethod] public init(values: [VerificationMethod]) { @@ -92,7 +131,11 @@ public struct DIDDocument { } } + /// Represents a "services" property, which is a list of Services associated with a Decentralized Identifier (DID) or other subject or object. + /// + /// The "services" property is typically included in a DID Document and can be used to discover and interact with the associated DID or subject or object. public struct Services: DIDDocumentCoreProperty { + /// The values of the "services" property, represented as an array of Service structs. public let values: [Service] public init(values: [Service]) { @@ -100,8 +143,14 @@ public struct DIDDocument { } } + /// Represents an "authentication" property, which is a list of URIs and Verification Methods that can be used to authenticate the associated DID or subject or object. + /// + /// The "authentication" property is typically included in a DID Document and can be used to verify the identity of the DID or subject or object. public struct Authentication: DIDDocumentCoreProperty { + /// The URIs of the authentication property. public let urls: [String] + + /// The Verification Methods of the authentication property. public let verificationMethods: [VerificationMethod] public init(urls: [String], verificationMethods: [VerificationMethod]) { @@ -110,8 +159,14 @@ public struct DIDDocument { } } + /// Represents an "assertion method" property, which is a list of URIs and Verification Methods that can be used to assert the authenticity of a message or credential associated with a DID or other subject or object. + /// + /// The "assertion method" property is typically included in a DID Document and can be used to verify the authenticity of messages or credentials related to the DID or subject or object. public struct AssertionMethod: DIDDocumentCoreProperty { + /// The URIs of the assertion method property. public let urls: [String] + + /// The Verification Methods of the assertion method property. public let verificationMethods: [VerificationMethod] public init(urls: [String], verificationMethods: [VerificationMethod]) { @@ -120,18 +175,31 @@ public struct DIDDocument { } } + /// Represents a "key agreement" property, which is a list of URIs and Verification Methods that can be used to establish a secure communication channel with a DID or other subject or object. + /// + /// The "key agreement" property is typically included in a DID Document and can be used to establish a secure communication channel with the DID or subject or object. public struct KeyAgreement: DIDDocumentCoreProperty { + /// The URIs of the key agreement property. public let urls: [String] + + /// The Verification Methods of the key agreement property. public let verificationMethods: [VerificationMethod] + /// Initializes the KeyAgreement struct with an array of URIs and an array of VerificationMethods. public init(urls: [String], verificationMethods: [VerificationMethod]) { self.urls = urls self.verificationMethods = verificationMethods } } + /// Represents a "capability invocation" property, which is a list of URIs and Verification Methods that can be used to invoke a specific capability or service provided by a DID or other subject or object. + /// + /// The "capability invocation" property is typically included in a DID Document and can be used to invoke a specific capability or service provided by the DID or subject or object. public struct CapabilityInvocation: DIDDocumentCoreProperty { + /// The URIs of the capability invocation property. public let urls: [String] + + /// The Verification Methods of the capability invocation property. public let verificationMethods: [VerificationMethod] public init(urls: [String], verificationMethods: [VerificationMethod]) { @@ -140,8 +208,14 @@ public struct DIDDocument { } } + /// Represents a "capability delegation" property, which is a list of URIs and Verification Methods that can be used to delegate a specific capability or service provided by a DID or other subject or object to another subject or object. + /// + /// The "capability delegation" property is typically included in a DID Document and can be used to delegate a specific capability or service provided by the DID or subject or object. public struct CapabilityDelegation: DIDDocumentCoreProperty { + /// The URIs of the capability delegation property. public let urls: [String] + + /// The Verification Methods of the capability delegation property. public let verificationMethods: [VerificationMethod] public init(urls: [String], verificationMethods: [VerificationMethod]) { @@ -161,8 +235,8 @@ public struct DIDDocument { public var authenticate: [VerificationMethod] { guard let property = coreProperties - .first(where: { $0 as? Authentication != nil }) - .map({ $0 as? Authentication }), + .first(where: { $0 as? Authentication != nil }) + .map({ $0 as? Authentication }), let authenticateProperty = property else { return [] } @@ -177,8 +251,8 @@ public struct DIDDocument { public var verificationMethods: [VerificationMethod] { guard let property = coreProperties - .first(where: { $0 as? VerificationMethods != nil }) - .map({ $0 as? VerificationMethods }), + .first(where: { $0 as? VerificationMethods != nil }) + .map({ $0 as? VerificationMethods }), let verificationMethodsProperty = property else { return [] } @@ -188,8 +262,8 @@ public struct DIDDocument { public var services: [Service] { guard let property = coreProperties - .first(where: { $0 as? Services != nil }) - .map({ $0 as? Services }), + .first(where: { $0 as? Services != nil }) + .map({ $0 as? Services }), let servicesProperty = property else { return [] } diff --git a/Domain/Sources/Models/DIDPair.swift b/Domain/Sources/Models/DIDPair.swift index 0f3321de..dbae5fd5 100644 --- a/Domain/Sources/Models/DIDPair.swift +++ b/Domain/Sources/Models/DIDPair.swift @@ -1,6 +1,12 @@ +/// Represents a pair of DIDs, typically used for secure communication or delegation of capabilities or services. public struct DIDPair: Equatable { + /// The holder DID. public let holder: DID + + /// The other DID in the pair. public let other: DID + + /// An optional name for the pair. public let name: String? public init(holder: DID, other: DID, name: String?) { diff --git a/Domain/Sources/Models/DIDUrl.swift b/Domain/Sources/Models/DIDUrl.swift index a9978d64..b745665d 100644 --- a/Domain/Sources/Models/DIDUrl.swift +++ b/Domain/Sources/Models/DIDUrl.swift @@ -1,9 +1,16 @@ -/// Represents a DIDUrl with ``did``, ``path``, ``parameters``, ``fragment`` -/// As specified in [w3 standards](`https://www.w3.org/TR/did-core/#dfn-did-urls`) +/// Represents a DIDUrl with "did", "path", "parameters", "fragment" +/// As specified in [w3 standards](`https://www.w3.org/TR/did-core/#dfn-did-urls`) public struct DIDUrl { + /// The associated DID. public let did: DID + + /// The path component of the DIDUrl. public let path: [String] + + /// The parameters component of the DIDUrl. public let parameters: [String: String] + + /// The fragment component of the DIDUrl. public let fragment: String? public init( diff --git a/Domain/Sources/Models/KeyPair.swift b/Domain/Sources/Models/KeyPair.swift index 1cedb12a..a89dc4a0 100644 --- a/Domain/Sources/Models/KeyPair.swift +++ b/Domain/Sources/Models/KeyPair.swift @@ -1,10 +1,17 @@ import Foundation +/// Enumeration representing supported key curves for key generation. public enum KeyCurve: Equatable { + /// The x25519 key curve. case x25519 + + /// The ed25519 key curve. case ed25519 + + /// The secp256k1 key curve with an optional index. case secp256k1(index: Int = 0) + /// Returns the name of the key curve as a string. public var name: String { switch self { case .x25519: @@ -17,9 +24,15 @@ public enum KeyCurve: Equatable { } } +/// Represents a pair of private and public keys for a specific key curve. public struct KeyPair { + /// The key curve used for the key pair. public let curve: KeyCurve + + /// The private key of the key pair. public let privateKey: PrivateKey + + /// The public key of the key pair. public let publicKey: PublicKey public init(curve: KeyCurve = .secp256k1(), privateKey: PrivateKey, publicKey: PublicKey) { diff --git a/Domain/Sources/Models/PrivateKey.swift b/Domain/Sources/Models/PrivateKey.swift index 84670b0b..82b503d3 100644 --- a/Domain/Sources/Models/PrivateKey.swift +++ b/Domain/Sources/Models/PrivateKey.swift @@ -1,7 +1,11 @@ import Foundation +/// Represents a private key with a specific key curve and value. public struct PrivateKey { + /// The key curve used for the private key. public let curve: KeyCurve + + /// The value of the private key as raw data. public let value: Data public init(curve: KeyCurve, value: Data) { diff --git a/Domain/Sources/Models/PublicKey.swift b/Domain/Sources/Models/PublicKey.swift index 0b8ebd4b..5f6b6120 100644 --- a/Domain/Sources/Models/PublicKey.swift +++ b/Domain/Sources/Models/PublicKey.swift @@ -1,7 +1,11 @@ import Foundation +/// Represents a public key with a specific key curve and value. public struct PublicKey { + /// The key curve used for the public key. public let curve: String + + /// The value of the public key as raw data. public let value: Data public init(curve: String, value: Data) { @@ -10,8 +14,12 @@ public struct PublicKey { } } +/// Represents a compressed public key and its uncompressed version. public struct CompressedPublicKey { + /// The uncompressed version of the public key. public let uncompressed: PublicKey + + /// The compressed version of the public key as raw data. public let value: Data public init(uncompressed: PublicKey, value: Data) { diff --git a/Domain/Sources/Models/Secret.swift b/Domain/Sources/Models/Secret.swift index c8f69cfa..78619696 100644 --- a/Domain/Sources/Models/Secret.swift +++ b/Domain/Sources/Models/Secret.swift @@ -1,24 +1,24 @@ +/// Represents a secret, which is a piece of secret material and its type. public struct Secret { + /// Enumeration representing the secret material. public enum SecretMaterial { - case jwk(value: String ) -// case multibase(value: String ) -// case base58(value: String ) -// case hex(value: String ) -// case other(value: String ) + /// The secret material is a JSON web key (JWK). + case jwk(value: String) } + /// Enumeration representing the secret type. public enum SecretType { + /// The secret type is a JSON web key (JWK) as specified in [RFC7517](https://tools.ietf.org/html/rfc7517). case jsonWebKey2020 -// case x25519KeyAgreementKey2019 -// case ed25519VerificationKey2018 -// case ecdsaSecp256k1VerificationKey2019 -// case x25519KeyAgreementKey2020 -// case ed25519VerificationKey2020 -// case other } + /// The ID of the secret. public var id: String + + /// The type of the secret public var type: SecretType + + /// The secret material public var secretMaterial: SecretMaterial public init(id: String, type: SecretType, secretMaterial: SecretMaterial) { diff --git a/Domain/Sources/Models/SecretResolver.swift b/Domain/Sources/Models/SecretResolver.swift index 04f58f24..f2743eda 100644 --- a/Domain/Sources/Models/SecretResolver.swift +++ b/Domain/Sources/Models/SecretResolver.swift @@ -1,4 +1,8 @@ +/// Protocol for resolving secrets by their ID. public protocol SecretResolver { - func resolve(secretids: [String]) async throws -> Secret - func resolve(secretid: [String]) async throws -> Secret + /// Resolves a list of secrets by their IDs. + func resolve(secretids: [String]) async throws -> [Secret] + + /// Resolves a single secret by its ID. + func resolve(secretid: String) async throws -> Secret } diff --git a/Domain/Sources/Models/Seed.swift b/Domain/Sources/Models/Seed.swift index f71487fb..d907d83c 100644 --- a/Domain/Sources/Models/Seed.swift +++ b/Domain/Sources/Models/Seed.swift @@ -1,6 +1,8 @@ import Foundation +/// Represents a seed used for key generation. public struct Seed { + /// The value of the seed as raw data. public let value: Data public init(value: Data) { diff --git a/Domain/Sources/Models/Signature.swift b/Domain/Sources/Models/Signature.swift index 3031d5b2..c2996161 100644 --- a/Domain/Sources/Models/Signature.swift +++ b/Domain/Sources/Models/Signature.swift @@ -1,6 +1,8 @@ import Foundation +/// Represents a digital signature. public struct Signature { + /// The value of the signature as raw data. public let value: Data public init(value: Data) { diff --git a/Domain/Sources/Models/VerifiableCredential.swift b/Domain/Sources/Models/VerifiableCredential.swift index 81f71a8f..8766be56 100644 --- a/Domain/Sources/Models/VerifiableCredential.swift +++ b/Domain/Sources/Models/VerifiableCredential.swift @@ -22,32 +22,49 @@ public struct VerifiableCredentialTypeContainer: Codable { } } +/// Enum representing different types of verifiable credentials. public enum CredentialType { case jwt case w3c case unknown } +/// Protocol for objects representing verifiable credentials. public protocol VerifiableCredential { + /// The type of this credential. var credentialType: CredentialType { get } + /// The ID of this credential. var id: String { get } + /// The context(s) of this credential. var context: Set { get } + /// The type(s) of this credential. var type: Set { get } + /// The issuer of this credential. var issuer: DID { get } + /// The date of issuance of this credential. var issuanceDate: Date { get } + /// The expiration date of this credential, if any. var expirationDate: Date? { get } + /// The schema of this credential. var credentialSchema: VerifiableCredentialTypeContainer? { get } + /// The subject of this credential. var credentialSubject: [String: String] { get } + /// The status of this credential. var credentialStatus: VerifiableCredentialTypeContainer? { get } + /// The refresh service of this credential. var refreshService: VerifiableCredentialTypeContainer? { get } + /// The evidence of this credential. var evidence: VerifiableCredentialTypeContainer? { get } + /// The terms of use of this credential. var termsOfUse: VerifiableCredentialTypeContainer? { get } + /// The valid-from date of this credential. var validFrom: VerifiableCredentialTypeContainer? { get } + /// The valid-until date of this credential. var validUntil: VerifiableCredentialTypeContainer? { get } - - // JsonString containing proof content as per `https://www.w3.org/2018/credentials/v1` + /// JsonString containing proof content as per `https://www.w3.org/2018/credentials/v1` + /// The proof of this credential, if any. var proof: String? { get } - - // Not part of W3C Credential but included to preserve in case of conversion from JWT. + /// Not part of W3C Credential but included to preserve in case of conversion from JWT. + /// The audience of this credential, if any. var aud: Set { get } } diff --git a/Domain/Sources/Models/W3CVerifiableCredential.swift b/Domain/Sources/Models/W3CVerifiableCredential.swift index f79a1dfd..bb4e2580 100644 --- a/Domain/Sources/Models/W3CVerifiableCredential.swift +++ b/Domain/Sources/Models/W3CVerifiableCredential.swift @@ -1,5 +1,6 @@ import Foundation +/// Struct representing a W3C Verifiable Credential public struct W3CVerifiableCredential: VerifiableCredential { public let credentialType = CredentialType.w3c public let context: Set diff --git a/Mercury/Sources/DIDCommWrappers/DIDCommSecretsResolverWrapper.swift b/Mercury/Sources/DIDCommWrappers/DIDCommSecretsResolverWrapper.swift index 98a8345f..6cd76e1c 100644 --- a/Mercury/Sources/DIDCommWrappers/DIDCommSecretsResolverWrapper.swift +++ b/Mercury/Sources/DIDCommWrappers/DIDCommSecretsResolverWrapper.swift @@ -70,9 +70,6 @@ extension DIDCommSecretsResolverWrapper: SecretsResolver { .map { $0.first { $0.id == secretid } } .sink { do { - if $0 == nil { - print("Secret not found: \(secretid)") - } try cb.success(result: $0.map { DIDCommxSwift.Secret(from: $0) }) } catch { print(error.localizedDescription) @@ -95,9 +92,6 @@ extension DIDCommSecretsResolverWrapper: SecretsResolver { } .sink { do { - if $0.isEmpty { - print("Secrets not found: \(secretids)") - } try cb.success(result: $0) } catch { print(error.localizedDescription) diff --git a/Mercury/Sources/DIDCommWrappers/PackEncryptedOperation.swift b/Mercury/Sources/DIDCommWrappers/PackEncryptedOperation.swift index 1586b181..03d602df 100644 --- a/Mercury/Sources/DIDCommWrappers/PackEncryptedOperation.swift +++ b/Mercury/Sources/DIDCommWrappers/PackEncryptedOperation.swift @@ -27,9 +27,6 @@ final class PackEncryptedOperation: OnPackEncryptedResult { case .finished: break case let .failure(error): - print("Error packing message type:\(msg.piuri)") - print("Error packing message from:\(fromDID.string)") - print("Error packing message to:\(toDID.string)") continuation.resume(throwing: error) } }, receiveValue: { @@ -71,7 +68,6 @@ final class PackEncryptedOperation: OnPackEncryptedResult { } func error(err: DIDCommxSwift.ErrorKind, msg: String) { - print("Error packing message: \(msg)") published.send(completion: .failure(MercuryError.didcommError(msg: msg))) } } diff --git a/Mercury/Sources/Helpers/Session.swift b/Mercury/Sources/Helpers/Session.swift index 9ab75abe..4c627297 100644 --- a/Mercury/Sources/Helpers/Session.swift +++ b/Mercury/Sources/Helpers/Session.swift @@ -34,7 +34,6 @@ struct SessionManager { let (data, response) = try await session.data(for: request) if let urlResponse = response as? HTTPURLResponse { guard 200...299 ~= urlResponse.statusCode else { - print("HTTP Error: \(urlResponse.statusCode)") throw MercuryError.urlSessionError( statusCode: urlResponse.statusCode, error: nil, diff --git a/Mercury/Sources/MercuryImpl+Public.swift b/Mercury/Sources/MercuryImpl+Public.swift index 11810d5d..146afe03 100644 --- a/Mercury/Sources/MercuryImpl+Public.swift +++ b/Mercury/Sources/MercuryImpl+Public.swift @@ -13,9 +13,6 @@ extension MercuryImpl: Mercury { } public func sendMessage(msg: Domain.Message) async throws -> Data? { - print("Preparing message of type: \(msg.piuri)") - print("From: \(msg.from?.string)") - print("to: \(msg.to?.string)") guard let toDID = msg.to else { throw MercuryError.noDIDReceiverSetError } let document = try await castor.resolveDID(did: toDID) guard @@ -23,7 +20,6 @@ extension MercuryImpl: Mercury { let url = URL(string: urlString) else { throw MercuryError.noValidServiceFoundError } let packedMessage = try await packMessage(msg: msg) - print("Sending message of type: \(msg.piuri)") return try await session.post( url: url, body: packedMessage.data(using: .utf8), diff --git a/PrismAgent/Sources/ConnectionsManager/ConnectionsManager.swift b/PrismAgent/Sources/ConnectionsManager/ConnectionsManager.swift index 0c57dbf5..9b0f94e5 100644 --- a/PrismAgent/Sources/ConnectionsManager/ConnectionsManager.swift +++ b/PrismAgent/Sources/ConnectionsManager/ConnectionsManager.swift @@ -200,7 +200,6 @@ extension ConnectionsManagerImpl: DIDCommConnection { func getMessagesPublisher(message: Message) -> AnyPublisher { struct RetryError: Error {} let mercury = self.mercury - print(message) return Future, Error> { promise in Task { do { diff --git a/PrismAgent/Sources/PrismAgent+Credentials.swift b/PrismAgent/Sources/PrismAgent+Credentials.swift new file mode 100644 index 00000000..fc10e7f0 --- /dev/null +++ b/PrismAgent/Sources/PrismAgent+Credentials.swift @@ -0,0 +1,14 @@ +import Combine +import Domain +import Foundation + +// MARK: Verifiable credentials functionalities +public extension PrismAgent { + /// This function returns the verifiable credentials stored in pluto database + /// + /// - Returns: A publisher that emits an array of `VerifiableCredential` and completes when all the + /// credentials are emitted or terminates with an error if any occurs + func verifiableCredentials() -> AnyPublisher<[VerifiableCredential], Error> { + pluto.getAllCredentials() + } +} diff --git a/PrismAgent/Sources/PrismAgent+DIDHigherFucntions.swift b/PrismAgent/Sources/PrismAgent+DIDHigherFucntions.swift new file mode 100644 index 00000000..3592bd8b --- /dev/null +++ b/PrismAgent/Sources/PrismAgent+DIDHigherFucntions.swift @@ -0,0 +1,162 @@ +import Domain +import Foundation + +// MARK: DID High Level functionalities +public extension PrismAgent { + /// Enumeration representing the type of DID used. + enum DIDType { + case prism + case peer + } + + /** + This function will use the provided DID to sign a given message + - Parameters: + - did: The DID which will be used to sign the message. + - message: The message to be signed + - Throws: + - PrismAgentError.cannotFindDIDKeyPairIndex If the DID provided has no register with the Agent + - Any other errors thrown by the `getPrismDIDInfo` function or the `createKeyPair` function + - Returns: + - Signature: The signature of the message. + */ + func signWith(did: DID, message: Data) async throws -> Signature { + let seed = self.seed + let apollo = self.apollo + let pluto = self.pluto + return try await withCheckedThrowingContinuation { continuation in + pluto + // First get DID info (KeyPathIndex in this case) + .getPrismDIDInfo(did: did) + .tryMap { + // if no register is found throw an error + guard let index = $0?.keyPairIndex else { throw PrismAgentError.cannotFindDIDKeyPairIndex } + // Re-Create the key pair to sign the message + let keyPair = apollo.createKeyPair(seed: seed, curve: .secp256k1(index: index)) + return apollo.signMessage(privateKey: keyPair.privateKey, message: message) + } + .first() + .sink(receiveCompletion: { + switch $0 { + case .finished: + break + case let .failure(error): + continuation.resume(throwing: error) + } + }, receiveValue: { + continuation.resume(returning: $0) + }) + .store(in: &self.cancellables) + } + } + + /// This method create a new Prism DID, that can be used to identify the agent and interact with other agents. + /// - Parameters: + /// - keyPathIndex: key path index used to identify the DID + /// - alias: An alias that can be used to identify the DID + /// - services: an array of services associated to the DID + /// - Returns: The new created DID + func createNewPrismDID( + keyPathIndex: Int? = nil, + alias: String? = nil, + services: [DIDDocument.Service] = [] + ) async throws -> DID { + let seed = self.seed + let apollo = self.apollo + let castor = self.castor + let pluto = self.pluto + + return try await withCheckedThrowingContinuation { continuation in + pluto + // Retrieve the last keyPath index used + .getPrismLastKeyPairIndex() + .tryMap { + // If the user provided a key path index use it, if not use the last + 1 + let index = keyPathIndex ?? ($0 + 1) + // Create the key pair + let keyPair = apollo.createKeyPair(seed: seed, curve: .secp256k1(index: index)) + let newDID = try castor.createPrismDID(masterPublicKey: keyPair.publicKey, services: services) + return (newDID, index, alias) + } + .flatMap { did, index, alias in + // Store the did and its index path + return pluto + .storePrismDID(did: did, keyPairIndex: index, alias: alias) + .map { did } + } + .first() + .sink(receiveCompletion: { + switch $0 { + case .finished: + break + case let .failure(error): + continuation.resume(throwing: error) + } + }, receiveValue: { + continuation.resume(returning: $0) + }) + .store(in: &self.cancellables) + } + } + + /// This function creates a new Peer DID, stores it in pluto database and updates the mediator if requested. + /// + /// - Parameters: + /// - services: The services associated to the new DID. + /// - updateMediator: Indicates if the new DID should be added to the mediator's list. + /// - Returns: A new DID + /// - Throws: PrismAgentError, if updateMediator is true and there is no mediator available or if storing the new DID failed + func createNewPeerDID( + services: [DIDDocument.Service] = [], + updateMediator: Bool + ) async throws -> DID { + let apollo = self.apollo + let castor = self.castor + let pluto = self.pluto + + let keyAgreementKeyPair = apollo.createKeyPair(seed: seed, curve: .x25519) + let authenticationKeyPair = apollo.createKeyPair(seed: seed, curve: .ed25519) + + let did = try castor.createPeerDID( + keyAgreementKeyPair: keyAgreementKeyPair, + authenticationKeyPair: authenticationKeyPair, + services: services + ) + + if updateMediator { + guard let mediator = connectionManager.mediator else { + throw PrismAgentError.noMediatorAvailableError + } + let keyListUpdateMessage = try MediationKeysUpdateList( + from: mediator.peerDID, + to: mediator.mediatorDID, + recipientDid: did + ).makeMessage() + + try await mercury.sendMessage(msg: keyListUpdateMessage) + } + + return try await withCheckedThrowingContinuation { continuation in + pluto + .storePeerDID( + did: did, + privateKeys: [ + keyAgreementKeyPair.privateKey, + authenticationKeyPair.privateKey + ]) + .map { did } + .first() + .sink(receiveCompletion: { + switch $0 { + case .finished: + break + case let .failure(error): + continuation.resume(throwing: error) + } + }, receiveValue: { + continuation.resume(returning: $0) + }) + .store(in: &self.cancellables) + } + } +} diff --git a/PrismAgent/Sources/PrismAgent+Invitations.swift b/PrismAgent/Sources/PrismAgent+Invitations.swift new file mode 100644 index 00000000..ff4bbb26 --- /dev/null +++ b/PrismAgent/Sources/PrismAgent+Invitations.swift @@ -0,0 +1,131 @@ +import Domain +import Foundation + +// MARK: Invitation funcionalities +public extension PrismAgent { + /// Enumeration representing the type of invitation + enum InvitationType { + /// Struct representing a Prism Onboarding invitation + public struct PrismOnboarding { + /// Sender of the invitation + public let from: String + /// Onboarding endpoint + public let endpoint: URL + /// The own DID of the user + public let ownDID: DID + } + + /// Case representing a Prism Onboarding invitation + case onboardingPrism(PrismOnboarding) + /// Case representing a DIDComm Out-of-Band invitation + case onboardingDIDComm(OutOfBandInvitation) + } + + /// Parses the given string as an invitation + /// - Parameter str: The string to parse + /// - Returns: The parsed invitation + /// - Throws: `PrismAgentError` if the invitation is not a valid Prism or OOB type + func parseInvitation(str: String) async throws -> InvitationType { + if let prismOnboarding = try? await parsePrismInvitation(str: str) { + return .onboardingPrism(prismOnboarding) + } else if let message = try? parseOOBInvitation(url: str) { + return .onboardingDIDComm(message) + } + throw PrismAgentError.unknownInvitationTypeError + } + + /// Parses the given string as a Prism Onboarding invitation + /// - Parameter str: The string to parse + /// - Returns: The parsed Prism Onboarding invitation + /// - Throws: `PrismAgentError` if the string is not a valid Prism Onboarding invitation + func parsePrismInvitation( + str: String + ) async throws -> InvitationType.PrismOnboarding { + let prismOnboarding = try PrismOnboardingInvitation(jsonString: str) + guard + let url = URL(string: prismOnboarding.body.onboardEndpoint) + else { throw PrismAgentError.invalidURLError } + + let ownDID = try await createNewPeerDID( + services: [.init( + id: "#didcomm-1", + type: ["DIDCommMessaging"], + serviceEndpoint: .init( + uri: "https://localhost:8080/didcomm" + )) + ], + updateMediator: false + ) + + return .init( + from: prismOnboarding.body.from, + endpoint: url, + ownDID: ownDID + ) + } + + /// Parses the given string as an Out-of-Band invitation + /// - Parameter url: The string to parse + /// - Returns: The parsed Out-of-Band invitation + /// - Throws: `PrismAgentError` if the string is not a valid URL + func parseOOBInvitation(url: String) throws -> OutOfBandInvitation { + guard let url = URL(string: url) else { throw PrismAgentError.invalidURLError } + return try parseOOBInvitation(url: url) + } + + /// Parses the given URL as an Out-of-Band invitation + /// - Parameter url: The URL to parse + /// - Returns: The parsed Out-of-Band invitation + /// - Throws: `PrismAgentError` if the URL is not a valid Out-of-Band invitation + func parseOOBInvitation(url: URL) throws -> OutOfBandInvitation { + return try DIDCommInvitationRunner(url: url).run() + } + + /// Accepts an Out-of-Band (DIDComm) invitation and establishes a new connection + /// - Parameter invitation: The Out-of-Band invitation to accept + /// - Throws: `PrismAgentError` if there is no mediator available or other errors occur during the acceptance process + func acceptDIDCommInvitation(invitation: OutOfBandInvitation) async throws { + guard let mediatorRoutingDID else { throw PrismAgentError.noMediatorAvailableError } + logger.info(message: "Start accept DIDComm invitation") + let ownDID = try await createNewPeerDID( + services: [.init( + id: "#didcomm-1", + type: ["DIDCommMessaging"], + serviceEndpoint: .init( + uri: mediatorRoutingDID.string + )) + ], + updateMediator: true + ) + + logger.info(message: "Created DID", metadata: [ + .publicMetadata(key: "DID", value: ownDID.string) + ]) + + let pair = try await DIDCommConnectionRunner( + invitationMessage: invitation, + pluto: pluto, + ownDID: ownDID, + connection: connectionManager + ).run() + try await connectionManager.addConnection(pair) + } + + /// Accepts a Prism Onboarding invitation and performs the onboarding process + /// - Parameter invitation: The Prism Onboarding invitation to accept + /// - Throws: `PrismAgentError` if the onboarding process fails + func acceptPrismInvitation(invitation: InvitationType.PrismOnboarding) async throws { + struct SendDID: Encodable { + let did: String + } + var request = URLRequest(url: invitation.endpoint) + request.httpMethod = "POST" + request.httpBody = try JSONEncoder().encode(SendDID(did: invitation.ownDID.string)) + request.setValue("application/json", forHTTPHeaderField: "content-type") + let response = try await URLSession.shared.data(for: request) + guard + let urlResponse = response.1 as? HTTPURLResponse, + urlResponse.statusCode == 200 + else { throw PrismAgentError.failedToOnboardError } + } +} diff --git a/PrismAgent/Sources/PrismAgent+MessageEvents.swift b/PrismAgent/Sources/PrismAgent+MessageEvents.swift new file mode 100644 index 00000000..6b27f959 --- /dev/null +++ b/PrismAgent/Sources/PrismAgent+MessageEvents.swift @@ -0,0 +1,79 @@ +import Combine +import Domain +import Foundation + +// MARK: Messaging events funcionalities +public extension PrismAgent { + /// Start fetching the messages from the mediator + func startFetchingMessages() { + // Check if messagesStreamTask is nil + guard messagesStreamTask == nil else { return } + let manager = connectionManager + messagesStreamTask = Task { + // Keep trying to fetch messages until the task is cancelled + while true { + do { + // Wait for new messages to arrive + _ = try await manager.awaitMessages() + sleep(5) + } catch { + // Handle errors that occur during the message fetching process + logger.error(error: error) + } + } + } + } + + /// Stop fetching messages + func stopFetchingMessages() { + messagesStreamTask?.cancel() + } + + /// Handles the messages events and return a publisher of the messages + /// - Returns: A publisher of the messages that emits an event with a `Message` and completed or failed with an `Error` + func handleMessagesEvents() -> AnyPublisher { + pluto.getAllMessages() + .flatMap { $0.publisher } + .eraseToAnyPublisher() + } + + /// Handles the received messages events and return a publisher of the messages + /// - Returns: A publisher of the messages that emits an event with a `Message` and completed or failed with an `Error` + func handleReceivedMessagesEvents() -> AnyPublisher { + pluto.getAllMessagesReceived() + .flatMap { $0.publisher } + .eraseToAnyPublisher() +// .flatMap { message -> AnyPublisher in +// if let issueCredential = try? IssueCredential(fromMessage: message) { +// let credentials = try? issueCredential.getCredentialStrings().map { +// try pollux.parseVerifiableCredential(jwtString: $0) +// } +// guard let credential = credentials?.first else { +// return Just(message) +// .tryMap { $0 } +// .eraseToAnyPublisher() +// } +// return pluto +// .storeCredential(credential: credential) +// .map { message } +// .eraseToAnyPublisher() +// } +// return Just(message) +// .tryMap { $0 } +// .eraseToAnyPublisher() +// } +// .flatMap { [weak self] message -> AnyPublisher in +// if +// let self, +// let request = try? RequestPresentation(fromMessage: message), +// !self.requestedPresentations.value.contains(where: { $0.0.id == request.id }) +// { +// self.requestedPresentations.value = self.requestedPresentations.value + [(request, false)] +// } +// return Just(message) +// .tryMap { $0 } +// .eraseToAnyPublisher() +// } +// .eraseToAnyPublisher() + } +} diff --git a/PrismAgent/Sources/PrismAgent.swift b/PrismAgent/Sources/PrismAgent.swift index 9720fe55..1c13cc12 100644 --- a/PrismAgent/Sources/PrismAgent.swift +++ b/PrismAgent/Sources/PrismAgent.swift @@ -1,10 +1,13 @@ import Builders import Combine +import Core import Domain import Foundation -import Core +/// PrismAgent class is responsible for handling the connection to other agents in the network using +/// a provided Mediator Service Endpoint and seed data. public class PrismAgent { + /// Enumeration representing the current state of the agent. public enum State: String { case stoped case starting @@ -12,47 +15,47 @@ public class PrismAgent { case stoping } - public enum DIDType { - case prism - case peer - } - - public enum InvitationType { - public struct PrismOnboarding { - public let from: String - public let endpoint: URL - public let ownDID: DID - } - - case onboardingPrism(PrismOnboarding) - case onboardingDIDComm(OutOfBandInvitation) - } + /// Represents the seed data used to create a unique DID. + public let seed: Seed + /// Represents the current state of the agent. public private(set) var state = State.stoped - private static let prismMediatorEndpoint = DID(method: "peer", methodId: "other") - - private let logger = PrismLogger(category: .prismAgent) - private let apollo: Apollo - private let castor: Castor - private let pluto: Pluto - private let pollux: Pollux - private let mercury: Mercury - private let mediatorServiceEnpoint: DID - - private var connectionManager: ConnectionsManagerImpl - private var cancellables = [AnyCancellable]() - // Not a "stream" - private var messagesStreamTask: Task? - - public let seed: Seed - - public private(set) var requestedPresentations: CurrentValueSubject<[(RequestPresentation, Bool)], Never> = .init([]) + // TODO: This is to be deleted + public private(set) var requestedPresentations: CurrentValueSubject< + [(RequestPresentation, Bool)], Never + > = .init([]) + /// The mediator routing DID if one is currently registered. public var mediatorRoutingDID: DID? { connectionManager.mediator?.routingDID } + static let prismMediatorEndpoint = DID(method: "peer", methodId: "other") + + let logger = PrismLogger(category: .prismAgent) + let apollo: Apollo + let castor: Castor + let pluto: Pluto + let pollux: Pollux + let mercury: Mercury + let mediatorServiceEnpoint: DID + + var connectionManager: ConnectionsManagerImpl + var cancellables = [AnyCancellable]() + // Not a "stream" + var messagesStreamTask: Task? + + /// Initializes a PrismAgent with the given dependency objects and seed data. + /// + /// - Parameters: + /// - apollo: An instance of Apollo. + /// - castor: An instance of Castor. + /// - pluto: An instance of Pluto. + /// - pollux: An instance of Pollux. + /// - mercury: An instance of Mercury. + /// - seed: A unique seed used to generate the unique DID. + /// - mediatorServiceEnpoint: The endpoint of the Mediator service to use. public init( apollo: Apollo, castor: Castor, @@ -77,6 +80,13 @@ public class PrismAgent { ) } + /** + Convenience initializer for `PrismAgent` that allows for optional initialization of seed data and mediator service endpoint. + + - Parameters: + - seedData: Optional seed data for creating a new seed. If not provided, a random seed will be generated. + - mediatorServiceEnpoint: Optional DID representing the service endpoint of the mediator. If not provided, the default Prism mediator endpoint will be used. + */ public convenience init(seedData: Data? = nil, mediatorServiceEnpoint: DID? = nil) { let apollo = ApolloBuilder().build() let castor = CastorBuilder(apollo: apollo).build() @@ -98,314 +108,54 @@ public class PrismAgent { ) } - public func start() async throws { - guard state == .stoped else { return } - state = .starting - do { - try await connectionManager.startMediator() - } catch PrismAgentError.noMediatorAvailableError { - let hostDID = try await createNewPeerDID( - services: [.init( - id: "#didcomm-1", - type: ["DIDCommMessaging"], - serviceEndpoint:.init(uri: mediatorServiceEnpoint.string)) - ], - updateMediator: false - ) - try await connectionManager.registerMediator( - hostDID: hostDID, - mediatorDID: mediatorServiceEnpoint - ) - } - state = .running - logger.info(message: "Mediation Achieved", metadata: [ - .publicMetadata(key: "Routing DID", value: mediatorRoutingDID?.string ?? "") - ]) - } - - public func stop() async throws { - guard state == .running else { return } - state = .stoping - cancellables.forEach { $0.cancel() } - connectionManager.stopAllEvents() - state = .stoped - } - - public func signWith(did: DID, message: Data) async throws -> Signature { - let seed = self.seed - let apollo = self.apollo - let pluto = self.pluto - return try await withCheckedThrowingContinuation { continuation in - pluto - // First get DID info (KeyPathIndex in this case) - .getPrismDIDInfo(did: did) - .tryMap { - // if no register is found throw an error - guard let index = $0?.keyPairIndex else { throw PrismAgentError.cannotFindDIDKeyPairIndex } - // Re-Create the key pair to sign the message - let keyPair = apollo.createKeyPair(seed: seed, curve: .secp256k1(index: index)) - return apollo.signMessage(privateKey: keyPair.privateKey, message: message) - } - .first() - .sink(receiveCompletion: { - switch $0 { - case .finished: - break - case let .failure(error): - continuation.resume(throwing: error) - } - }, receiveValue: { - continuation.resume(returning: $0) - }) - .store(in: &self.cancellables) - } - } - - public func parseInvitation(str: String) async throws -> InvitationType { - if let prismOnboarding = try? await parsePrismInvitation(str: str) { - return .onboardingPrism(prismOnboarding) - } else if let message = try? parseOOBInvitation(url: str) { - return .onboardingDIDComm(message) - } - throw PrismAgentError.unknownInvitationTypeError - } - - public func parsePrismInvitation(str: String) async throws -> InvitationType.PrismOnboarding { - let prismOnboarding = try PrismOnboardingInvitation(jsonString: str) -// guard let mediatorRoutingDID else { throw PrismAgentError.noMediatorAvailableError } - guard - let url = URL(string: prismOnboarding.body.onboardEndpoint) - else { throw PrismAgentError.invalidURLError } - - let ownDID = try await createNewPeerDID( - services: [.init( - id: "#didcomm-1", - type: ["DIDCommMessaging"], - serviceEndpoint: .init( - uri: "https://localhost:8080/didcomm" - )) - ], - updateMediator: false - ) - - return .init( - from: prismOnboarding.body.from, - endpoint: url, - ownDID: ownDID - ) - } - - public func parseOOBInvitation(url: String) throws -> OutOfBandInvitation { - guard let url = URL(string: url) else { throw PrismAgentError.invalidURLError } - return try parseOOBInvitation(url: url) - } - - public func parseOOBInvitation(url: URL) throws -> OutOfBandInvitation { - return try DIDCommInvitationRunner(url: url).run() - } - - public func acceptDIDCommInvitation(invitation: OutOfBandInvitation) async throws { - guard let mediatorRoutingDID else { throw PrismAgentError.noMediatorAvailableError } - logger.info(message: "Start accept DIDComm invitation") - let ownDID = try await createNewPeerDID( - services: [.init( - id: "#didcomm-1", - type: ["DIDCommMessaging"], - serviceEndpoint: .init( - uri: mediatorRoutingDID.string - )) - ], - updateMediator: true - ) - - logger.info(message: "Created DID", metadata: [ - .publicMetadata(key: "DID", value: ownDID.string) - ]) - - let pair = try await DIDCommConnectionRunner( - invitationMessage: invitation, - pluto: pluto, - ownDID: ownDID, - connection: connectionManager - ).run() - issueCredentialProtocol() - try await connectionManager.addConnection(pair) - - } - - public func acceptPrismInvitation(invitation: InvitationType.PrismOnboarding) async throws { - struct SendDID: Encodable { - let did: String - } - var request = URLRequest(url: invitation.endpoint) - request.httpMethod = "POST" - request.httpBody = try JSONEncoder().encode(SendDID(did: invitation.ownDID.string)) - request.setValue("application/json", forHTTPHeaderField: "content-type") - let response = try await URLSession.shared.data(for: request) - guard - let urlResponse = response.1 as? HTTPURLResponse, - urlResponse.statusCode == 200 - else { throw PrismAgentError.failedToOnboardError } - } - - public func createNewPrismDID( - keyPathIndex: Int? = nil, - alias: String? = nil, - services: [DIDDocument.Service] = [] - ) async throws -> DID { - let seed = self.seed - let apollo = self.apollo - let castor = self.castor - let pluto = self.pluto - - return try await withCheckedThrowingContinuation { continuation in - pluto - // Retrieve the last keyPath index used - .getPrismLastKeyPairIndex() - .tryMap { - // If the user provided a key path index use it, if not use the last + 1 - let index = keyPathIndex ?? ($0 + 1) - // Create the key pair - let keyPair = apollo.createKeyPair(seed: seed, curve: .secp256k1(index: index)) - let newDID = try castor.createPrismDID(masterPublicKey: keyPair.publicKey, services: services) - return (newDID, index, alias) - } - .flatMap { did, index, alias in - // Store the did and its index path - return pluto - .storePrismDID(did: did, keyPairIndex: index, alias: alias) - .map { did } - } - .first() - .sink(receiveCompletion: { - switch $0 { - case .finished: - break - case let .failure(error): - continuation.resume(throwing: error) - } - }, receiveValue: { - continuation.resume(returning: $0) - }) - .store(in: &self.cancellables) - } - } - - public func createNewPeerDID( - services: [DIDDocument.Service] = [], - updateMediator: Bool - ) async throws -> DID { - let apollo = self.apollo - let castor = self.castor - let pluto = self.pluto - - let keyAgreementKeyPair = apollo.createKeyPair(seed: seed, curve: .x25519) - let authenticationKeyPair = apollo.createKeyPair(seed: seed, curve: .ed25519) - - let did = try castor.createPeerDID( - keyAgreementKeyPair: keyAgreementKeyPair, - authenticationKeyPair: authenticationKeyPair, - services: services - ) - - if updateMediator { - guard let mediator = connectionManager.mediator else { - throw PrismAgentError.noMediatorAvailableError - } - let keyListUpdateMessage = try MediationKeysUpdateList( - from: mediator.peerDID, - to: mediator.mediatorDID, - recipientDid: did - ).makeMessage() - - try await mercury.sendMessage(msg: keyListUpdateMessage) - } - - return try await withCheckedThrowingContinuation { continuation in - pluto - .storePeerDID( - did: did, - privateKeys: [ - keyAgreementKeyPair.privateKey, - authenticationKeyPair.privateKey - ]) - .map { did } - .first() - .sink(receiveCompletion: { - switch $0 { - case .finished: - break - case let .failure(error): - continuation.resume(throwing: error) - } - }, receiveValue: { - continuation.resume(returning: $0) - }) - .store(in: &self.cancellables) - } - } + /** + Start the PrismAgent and Mediator services - public func startFetchingMessages() { - // TODO: This needs to be better thought for sure it cannot be left like this - guard messagesStreamTask == nil else { return } - let manager = connectionManager - messagesStreamTask = Task { - while true { - _ = try? await manager.awaitMessages() - sleep(5) + - Throws: PrismAgentError.noMediatorAvailableError if no mediator is available, + as well as any error thrown by `createNewPeerDID` and `connectionManager.registerMediator` + */ + public func start() async throws { + guard state == .stoped else { return } + state = .starting + do { + try await connectionManager.startMediator() + } catch PrismAgentError.noMediatorAvailableError { + let hostDID = try await createNewPeerDID( + services: [.init( + id: "#didcomm-1", + type: ["DIDCommMessaging"], + serviceEndpoint:.init(uri: mediatorServiceEnpoint.string)) + ], + updateMediator: false + ) + try await connectionManager.registerMediator( + hostDID: hostDID, + mediatorDID: mediatorServiceEnpoint + ) } + state = .running + logger.info(message: "Mediation Achieved", metadata: [ + .publicMetadata(key: "Routing DID", value: mediatorRoutingDID?.string ?? "") + ]) } - } - - public func stopFetchingMessages() { - messagesStreamTask?.cancel() - } - - public func handleMessagesEvents() -> AnyPublisher { - pluto.getAllMessages() - .flatMap { $0.publisher } - .eraseToAnyPublisher() - } - - public func handleReceivedMessagesEvents() -> AnyPublisher { - let pollux = self.pollux - let pluto = self.pluto - return pluto.getAllMessagesReceived() - .flatMap { $0.publisher } - .flatMap { message -> AnyPublisher in - if let issueCredential = try? IssueCredential(fromMessage: message) { - let credentials = try? issueCredential.getCredentialStrings().map { - try pollux.parseVerifiableCredential(jwtString: $0) - } - guard let credential = credentials?.first else { - return Just(message) - .tryMap { $0 } - .eraseToAnyPublisher() - } - return pluto - .storeCredential(credential: credential) - .map { message } - .eraseToAnyPublisher() - } - return Just(message) - .tryMap { $0 } - .eraseToAnyPublisher() - } - .flatMap { [weak self] message -> AnyPublisher in - if - let self, - let request = try? RequestPresentation(fromMessage: message), - !self.requestedPresentations.value.contains(where: { $0.0.id == request.id }) - { - self.requestedPresentations.value = self.requestedPresentations.value + [(request, false)] - } - return Just(message) - .tryMap { $0 } - .eraseToAnyPublisher() - } - .eraseToAnyPublisher() - } + /** + This function is used to stop the PrismAgent. + The function sets the state of PrismAgent to .stoping. + All ongoing events that was created by the PrismAgent are stopped. + After all the events are stopped the state of the PrismAgent is set to .stoped. + + - Throws: If the current state is not running throws error. + */ + public func stop() async throws { + guard state == .running else { return } + state = .stoping + cancellables.forEach { $0.cancel() } + connectionManager.stopAllEvents() + state = .stoped + } + + // TODO: This is to be deleted in the future. For now it helps with proof of request logic public func presentCredentialProof( request: RequestPresentation, credential: VerifiableCredential @@ -413,7 +163,6 @@ public class PrismAgent { guard let jwtBase64 = credential.id.data(using: .utf8)?.base64UrlEncodedString() else { throw PrismAgentError.invalidRequestPresentationMessageError } - print(credential.id) let presentation = Presentation( body: .init(goalCode: request.body.goalCode, comment: request.body.comment), attachments: [try .build( @@ -427,16 +176,13 @@ public class PrismAgent { _ = try await connectionManager.sendMessage(presentation.makeMessage()) } - public func verifiableCredentials() -> AnyPublisher<[VerifiableCredential], Error> { - pluto.getAllCredentials() - } - + // TODO: This is to be deleted in the future. For now it helps with issue credentials logic public func issueCredentialProtocol() { startFetchingMessages() Task { do { for try await offer in handleReceivedMessagesEvents() - .drop(while:{ (try? OfferCredential(fromMessage: $0)) != nil }) + .drop(while: { (try? OfferCredential(fromMessage: $0)) != nil }) .values { if let issueProtocol = try? IssueCredentialProtocol(offer, connector: connectionManager) { diff --git a/PrismAgent/Sources/Protocols/IssueCredential/IssueCredentialProtocol.swift b/PrismAgent/Sources/Protocols/IssueCredential/IssueCredentialProtocol.swift index 9a04db96..45d368ca 100644 --- a/PrismAgent/Sources/Protocols/IssueCredential/IssueCredentialProtocol.swift +++ b/PrismAgent/Sources/Protocols/IssueCredential/IssueCredentialProtocol.swift @@ -64,7 +64,6 @@ public class IssueCredentialProtocol { return } let message = try RequestCredential.makeRequestFromOfferCredential(offer: offer).makeMessage() - print(message) try await connector.sendMessage(message) messageId = message.id case .request: @@ -91,7 +90,6 @@ public class IssueCredentialProtocol { self.request = request } else if let issued = try? IssueCredential(fromMessage: response) { stage = .completed - print(issued) } } } diff --git a/PrismAgent/Sources/Protocols/IssueCredential/RequestCredential.swift b/PrismAgent/Sources/Protocols/IssueCredential/RequestCredential.swift index 733096ce..40118d73 100644 --- a/PrismAgent/Sources/Protocols/IssueCredential/RequestCredential.swift +++ b/PrismAgent/Sources/Protocols/IssueCredential/RequestCredential.swift @@ -83,7 +83,7 @@ public struct RequestCredential { formats: offer.body.formats ), attachments: offer.attachments, - thid: offer.thid, //TODO: This needs to be changed in the pr + thid: offer.thid, // TODO: This needs to be changed in the pr from: offer.to, to: offer.from ) diff --git a/PrismAgent/Sources/Protocols/Pickup/PickupRunner.swift b/PrismAgent/Sources/Protocols/Pickup/PickupRunner.swift index 6903fc59..9ee44142 100644 --- a/PrismAgent/Sources/Protocols/Pickup/PickupRunner.swift +++ b/PrismAgent/Sources/Protocols/Pickup/PickupRunner.swift @@ -27,6 +27,8 @@ class PickupRunner { case let .delivery(message): return try await message.attachments.compactMap { attachment in switch attachment.data { + case let base64 as AttachmentBase64: + return (base64.base64, attachment.id) case let json as AttachmentJsonData: return String(data: json.data, encoding: .utf8).map { ($0, attachment.id) } default: diff --git a/PrismAgent/Sources/Protocols/ProofPresentation/RequestPresentation.swift b/PrismAgent/Sources/Protocols/ProofPresentation/RequestPresentation.swift index 2884eabb..8c1862cb 100644 --- a/PrismAgent/Sources/Protocols/ProofPresentation/RequestPresentation.swift +++ b/PrismAgent/Sources/Protocols/ProofPresentation/RequestPresentation.swift @@ -22,7 +22,7 @@ public struct RequestPresentation { } } public let id: String - public let type = ProtocolTypes.didcommRequestCredential.rawValue + public let type = ProtocolTypes.didcommRequestPresentation.rawValue public let body: Body public let attachments: [AttachmentDescriptor] public let thid: String? diff --git a/PrismAgent/Tests/PresentationTests.swift b/PrismAgent/Tests/PresentationTests.swift index bc81b196..b33a389c 100644 --- a/PrismAgent/Tests/PresentationTests.swift +++ b/PrismAgent/Tests/PresentationTests.swift @@ -7,13 +7,7 @@ final class PresentationTests: XCTestCase { let fromDID = DID(index: 0) let toDID = DID(index: 1) let validPresentation = Presentation( - body: .init( - formats: [ - .init( - attachId: "test1", - format: "test") - ] - ), + body: .init(), attachments: [], thid: "1", from: fromDID, @@ -40,9 +34,13 @@ final class PresentationTests: XCTestCase { let fromDID = DID(index: 0) let toDID = DID(index: 1) let validRequest = RequestPresentation( - body: .init( - formats: [.init(attachId: "test1", format: "test")] - ), + body: .init(proofTypes: [ + .init( + schema: "testSchema", + requiredFields: nil, + trustIssuers: nil + ) + ]), attachments: [], thid: "1", from: fromDID, @@ -57,6 +55,5 @@ final class PresentationTests: XCTestCase { XCTAssertEqual(validRequest.id, testPresentation.thid) XCTAssertEqual(validRequest.body.goalCode, testPresentation.body.goalCode) XCTAssertEqual(validRequest.body.comment, testPresentation.body.comment) - XCTAssertEqual(validRequest.body.formats, testPresentation.body.formats) } } diff --git a/PrismAgent/Tests/PrismAgentStartTests.swift b/PrismAgent/Tests/PrismAgentStartTests.swift deleted file mode 100644 index 6d3cd330..00000000 --- a/PrismAgent/Tests/PrismAgentStartTests.swift +++ /dev/null @@ -1,13 +0,0 @@ -import Domain -@testable import PrismAgent -import XCTest - -final class PrismAgentStartTests: XCTestCase { - - func testPrismAgentStart() async throws { - let did = try DID(string: "did:peer:2.Ez6LScc4S6tTSf5PnB7tWAna8Ee2aL7z2nRgo6aCHQwLds3m4.Vz6MktCyutFBcZcAWBnE2shqqUQDyRdnvcwqMTPqWsGHMnHyT.SeyJpZCI6Im5ldy1pZCIsInQiOiJkbSIsInMiOiJodHRwOi8vcm9vdHNpZC1tZWRpYXRvcjo4MDAwIiwiYSI6WyJkaWRjb21tL3YyIl19") - let agent = PrismAgent(mediatorServiceEnpoint: did) - - try await agent.start() - } -} diff --git a/PrismAgent/Tests/ProposePresentationTests.swift b/PrismAgent/Tests/ProposePresentationTests.swift index 9104a297..a0e0f84c 100644 --- a/PrismAgent/Tests/ProposePresentationTests.swift +++ b/PrismAgent/Tests/ProposePresentationTests.swift @@ -7,13 +7,13 @@ final class ProposePresentationTests: XCTestCase { let fromDID = DID(index: 0) let toDID = DID(index: 1) let validProposePresentation = ProposePresentation( - body: .init( - formats: [ - .init( - attachId: "test1", - format: "test") - ] - ), + body: .init(proofTypes: [ + .init( + schema: "testSchema", + requiredFields: nil, + trustIssuers: nil + ) + ]), attachments: [], thid: "1", from: fromDID, @@ -40,9 +40,13 @@ final class ProposePresentationTests: XCTestCase { let fromDID = DID(index: 0) let toDID = DID(index: 1) let validRequestPresentation = RequestPresentation( - body: .init( - formats: [.init(attachId: "test1", format: "test")] - ), + body: .init(proofTypes: [ + .init( + schema: "testSchema", + requiredFields: nil, + trustIssuers: nil + ) + ]), attachments: [], thid: "1", from: fromDID, @@ -57,6 +61,5 @@ final class ProposePresentationTests: XCTestCase { XCTAssertEqual(validRequestPresentation.id, testProposePresentation.thid) XCTAssertEqual(validRequestPresentation.body.goalCode, testProposePresentation.body.goalCode) XCTAssertEqual(validRequestPresentation.body.comment, testProposePresentation.body.comment) - XCTAssertEqual(validRequestPresentation.body.formats, testProposePresentation.body.formats) } } diff --git a/PrismAgent/Tests/RequestPresentationTests.swift b/PrismAgent/Tests/RequestPresentationTests.swift index 12f3f2b9..cd89627e 100644 --- a/PrismAgent/Tests/RequestPresentationTests.swift +++ b/PrismAgent/Tests/RequestPresentationTests.swift @@ -7,13 +7,13 @@ final class RequestPresentationTests: XCTestCase { let fromDID = DID(index: 0) let toDID = DID(index: 1) let validRequestPresentation = RequestPresentation( - body: .init( - formats: [ - .init( - attachId: "test1", - format: "test") - ] - ), + body: .init(proofTypes: [ + .init( + schema: "testSchema", + requiredFields: nil, + trustIssuers: nil + ) + ]), attachments: [], thid: "1", from: fromDID, @@ -40,9 +40,13 @@ final class RequestPresentationTests: XCTestCase { let fromDID = DID(index: 0) let toDID = DID(index: 1) let validProposalRequest = ProposePresentation( - body: .init( - formats: [.init(attachId: "test1", format: "test")] - ), + body: .init(proofTypes: [ + .init( + schema: "testSchema", + requiredFields: nil, + trustIssuers: nil + ) + ]), attachments: [], thid: "1", from: fromDID, @@ -57,6 +61,5 @@ final class RequestPresentationTests: XCTestCase { XCTAssertEqual(validProposalRequest.id, testRequestPresentation.thid) XCTAssertEqual(validProposalRequest.body.goalCode, testRequestPresentation.body.goalCode) XCTAssertEqual(validProposalRequest.body.comment, testRequestPresentation.body.comment) - XCTAssertEqual(validProposalRequest.body.formats, testRequestPresentation.body.formats) } }