forked from hyperledger-identus/sdk-swift
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDIDCommAgent.swift
221 lines (201 loc) · 7.22 KB
/
DIDCommAgent.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
import Builders
import Combine
import Core
import Domain
import Foundation
public class DIDCommAgent {
/// Enumeration representing the current state of the agent.
public enum State: String {
case stopped
case starting
case running
case stopping
}
/// Represents the current state of the agent.
public private(set) var state = State.stopped
/// The mediator routing DID if one is currently registered.
public var mediatorRoutingDID: DID? {
connectionManager?.mediationHandler.mediator?.routingDID
}
public let mercury: Mercury
public let edgeAgent: EdgeAgent
public var apollo: Apollo & KeyRestoration { edgeAgent.apollo }
public var castor: Castor { edgeAgent.castor }
public var pluto: Pluto { edgeAgent.pluto }
public var pollux: Pollux { edgeAgent.pollux }
var logger: SDKLogger { edgeAgent.logger }
var mediationHandler: MediatorHandler?
var connectionManager: ConnectionsManagerImpl?
var cancellables = [AnyCancellable]()
// Not a "stream"
var messagesStreamTask: Task<Void, Error>?
/// Initializes a EdgeAgent 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(
edgeAgent: EdgeAgent,
mercury: Mercury,
mediationHandler: MediatorHandler? = nil
) {
self.edgeAgent = edgeAgent
self.mercury = mercury
self.mediationHandler = mediationHandler
mediationHandler.map {
self.connectionManager = ConnectionsManagerImpl(
castor: edgeAgent.castor,
mercury: mercury,
pluto: edgeAgent.pluto,
mediationHandler: $0,
pairings: []
)
}
}
/**
Convenience initializer for `EdgeAgent` 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,
mediatorDID: DID
) {
let edgeAgent = EdgeAgent(seedData: seedData)
let secretsStream = createSecretsStream(
keyRestoration: edgeAgent.apollo,
pluto: edgeAgent.pluto,
castor: edgeAgent.castor
)
let mercury = MercuryBuilder(
castor: edgeAgent.castor,
secretsStream: secretsStream
).build()
self.init(
edgeAgent: edgeAgent,
mercury: mercury,
mediationHandler: BasicMediatorHandler(
mediatorDID: mediatorDID,
mercury: mercury,
store: BasicMediatorHandler.PlutoMediatorStoreImpl(pluto: edgeAgent.pluto)
)
)
}
public func setupMediatorHandler(mediationHandler: MediatorHandler) async throws {
try await stop()
self.mediationHandler = mediationHandler
self.connectionManager = ConnectionsManagerImpl(
castor: castor,
mercury: mercury,
pluto: pluto,
mediationHandler: mediationHandler,
pairings: []
)
}
public func setupMediatorDID(did: DID) async throws {
let mediatorHandler = BasicMediatorHandler(
mediatorDID: did,
mercury: mercury,
store: BasicMediatorHandler.PlutoMediatorStoreImpl(pluto: pluto)
)
try await setupMediatorHandler(mediationHandler: mediatorHandler)
}
/**
Start the EdgeAgent and Mediator services
- Throws: EdgeAgentError.noMediatorAvailableError if no mediator is available,
as well as any error thrown by `createNewPeerDID` and `connectionManager.registerMediator`
*/
public func start() async throws {
guard
let connectionManager,
state == .stopped
else { return }
logger.info(message: "Starting agent")
state = .starting
do {
try await connectionManager.startMediator()
} catch EdgeAgentError.noMediatorAvailableError {
let hostDID = try await createNewPeerDID(updateMediator: false)
try await connectionManager.registerMediator(hostDID: hostDID)
}
try await edgeAgent.firstLinkSecretSetup()
state = .running
logger.info(message: "Mediation Achieved", metadata: [
.publicMetadata(key: "Routing DID", value: mediatorRoutingDID?.string ?? "")
])
logger.info(message: "Agent running")
}
/**
This function is used to stop the EdgeAgent.
The function sets the state of EdgeAgent to .stopping.
All ongoing events that was created by the EdgeAgent are stopped.
After all the events are stopped the state of the EdgeAgent is set to .stopped.
- Throws: If the current state is not running throws error.
*/
public func stop() async throws {
guard state == .running else { return }
logger.info(message: "Stopping agent")
state = .stopping
cancellables.forEach { $0.cancel() }
connectionManager?.stopAllEvents()
state = .stopped
logger.info(message: "Agent not running")
}
}
private func createSecretsStream(
keyRestoration: KeyRestoration,
pluto: Pluto,
castor: Castor
) -> AnyPublisher<[Secret], Error> {
pluto.getAllKeys()
.first()
.flatMap { keys in
Future {
let privateKeys = await keys.asyncMap {
try? await keyRestoration.restorePrivateKey($0)
}.compactMap { $0 }
return try parsePrivateKeys(
privateKeys: privateKeys,
castor: castor
)
}
}
.eraseToAnyPublisher()
}
private func parsePrivateKeys(
privateKeys: [PrivateKey],
castor: Castor
) throws -> [Domain.Secret] {
return try privateKeys
.map { $0 as? (PrivateKey & ExportableKey & StorableKey) }
.compactMap { $0 }
.map { privateKey in
return privateKey
}
.map { privateKey in
try parseToSecret(
privateKey: privateKey,
identifier: privateKey.identifier
)
}
}
private func parseToSecret(privateKey: PrivateKey & ExportableKey, identifier: String) throws -> Domain.Secret {
let jwk = privateKey.jwk
guard
let dataJson = try? JSONEncoder().encode(jwk),
let stringJson = String(data: dataJson, encoding: .utf8)
else {
throw CommonError.invalidCoding(message: "Could not encode privateKey.jwk")
}
return .init(
id: identifier,
type: .jsonWebKey2020,
secretMaterial: .jwk(value: stringJson)
)
}