diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift index d806dba04..6b5aa22fc 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift @@ -17,7 +17,7 @@ public class NotifyClient { return logger.logsPublisher } - private let deleteNotifySubscriptionRequester: DeleteNotifySubscriptionRequester + private let notifyDeleteSubscriptionRequester: NotifyDeleteSubscriptionRequester private let notifySubscribeRequester: NotifySubscribeRequester public let logger: ConsoleLogging @@ -31,12 +31,14 @@ public class NotifyClient { private let notifyMessageSubscriber: NotifyMessageSubscriber private let resubscribeService: NotifyResubscribeService private let notifySubscribeResponseSubscriber: NotifySubscribeResponseSubscriber + private let notifyDeleteSubscriptionSubscriber: NotifyDeleteSubscriptionSubscriber private let notifyUpdateRequester: NotifyUpdateRequester private let notifyUpdateResponseSubscriber: NotifyUpdateResponseSubscriber private let subscriptionsAutoUpdater: SubscriptionsAutoUpdater private let notifyWatchSubscriptionsResponseSubscriber: NotifyWatchSubscriptionsResponseSubscriber private let notifyWatcherAgreementKeysProvider: NotifyWatcherAgreementKeysProvider private let notifySubscriptionsChangedRequestSubscriber: NotifySubscriptionsChangedRequestSubscriber + private let notifySubscriptionsUpdater: NotifySubsctiptionsUpdater private let subscriptionWatcher: SubscriptionWatcher init(logger: ConsoleLogging, @@ -47,10 +49,11 @@ public class NotifyClient { pushClient: PushClient, notifyMessageSubscriber: NotifyMessageSubscriber, notifyStorage: NotifyStorage, - deleteNotifySubscriptionRequester: DeleteNotifySubscriptionRequester, + notifyDeleteSubscriptionRequester: NotifyDeleteSubscriptionRequester, resubscribeService: NotifyResubscribeService, notifySubscribeRequester: NotifySubscribeRequester, notifySubscribeResponseSubscriber: NotifySubscribeResponseSubscriber, + notifyDeleteSubscriptionSubscriber: NotifyDeleteSubscriptionSubscriber, notifyUpdateRequester: NotifyUpdateRequester, notifyUpdateResponseSubscriber: NotifyUpdateResponseSubscriber, notifyAccountProvider: NotifyAccountProvider, @@ -58,6 +61,7 @@ public class NotifyClient { notifyWatchSubscriptionsResponseSubscriber: NotifyWatchSubscriptionsResponseSubscriber, notifyWatcherAgreementKeysProvider: NotifyWatcherAgreementKeysProvider, notifySubscriptionsChangedRequestSubscriber: NotifySubscriptionsChangedRequestSubscriber, + notifySubscriptionsUpdater: NotifySubsctiptionsUpdater, subscriptionWatcher: SubscriptionWatcher ) { self.logger = logger @@ -67,10 +71,11 @@ public class NotifyClient { self.historyService = historyService self.notifyMessageSubscriber = notifyMessageSubscriber self.notifyStorage = notifyStorage - self.deleteNotifySubscriptionRequester = deleteNotifySubscriptionRequester + self.notifyDeleteSubscriptionRequester = notifyDeleteSubscriptionRequester self.resubscribeService = resubscribeService self.notifySubscribeRequester = notifySubscribeRequester self.notifySubscribeResponseSubscriber = notifySubscribeResponseSubscriber + self.notifyDeleteSubscriptionSubscriber = notifyDeleteSubscriptionSubscriber self.notifyUpdateRequester = notifyUpdateRequester self.notifyUpdateResponseSubscriber = notifyUpdateResponseSubscriber self.notifyAccountProvider = notifyAccountProvider @@ -78,6 +83,7 @@ public class NotifyClient { self.notifyWatchSubscriptionsResponseSubscriber = notifyWatchSubscriptionsResponseSubscriber self.notifyWatcherAgreementKeysProvider = notifyWatcherAgreementKeysProvider self.notifySubscriptionsChangedRequestSubscriber = notifySubscriptionsChangedRequestSubscriber + self.notifySubscriptionsUpdater = notifySubscriptionsUpdater self.subscriptionWatcher = subscriptionWatcher } @@ -125,7 +131,7 @@ public class NotifyClient { } public func deleteSubscription(topic: String) async throws { - try await deleteNotifySubscriptionRequester.delete(topic: topic) + try await notifyDeleteSubscriptionRequester.delete(topic: topic) } public func deleteNotifyMessage(id: String) { @@ -184,7 +190,7 @@ private extension NotifyClient { extension NotifyClient { public var subscriptionChangedPublisher: AnyPublisher<[NotifySubscription], Never> { - return notifySubscriptionsChangedRequestSubscriber.subscriptionChangedPublisher + return notifySubscriptionsUpdater.subscriptionChangedPublisher } public func register(deviceToken: String) async throws { diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifyClientFactory.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifyClientFactory.swift index 676f37b5a..14fa2fdef 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/NotifyClientFactory.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifyClientFactory.swift @@ -45,28 +45,32 @@ public struct NotifyClientFactory { let identityClient = IdentityClientFactory.create(keyserver: keyserverURL, keychain: keychainStorage, logger: logger) let notifyMessageSubscriber = NotifyMessageSubscriber(keyserver: keyserverURL, networkingInteractor: networkInteractor, identityClient: identityClient, notifyStorage: notifyStorage, crypto: crypto, logger: logger) let webDidResolver = NotifyWebDidResolver() - let deleteNotifySubscriptionRequester = DeleteNotifySubscriptionRequester(keyserver: keyserverURL, networkingInteractor: networkInteractor, identityClient: identityClient, kms: kms, logger: logger, notifyStorage: notifyStorage) + let notifyDeleteSubscriptionRequester = NotifyDeleteSubscriptionRequester(keyserver: keyserverURL, networkingInteractor: networkInteractor, identityClient: identityClient, logger: logger, notifyStorage: notifyStorage) let resubscribeService = NotifyResubscribeService(networkInteractor: networkInteractor, notifyStorage: notifyStorage, logger: logger) let notifyConfigProvider = NotifyConfigProvider(projectId: projectId, explorerHost: explorerHost) let notifySubscribeRequester = NotifySubscribeRequester(keyserverURL: keyserverURL, networkingInteractor: networkInteractor, identityClient: identityClient, logger: logger, kms: kms, webDidResolver: webDidResolver, notifyConfigProvider: notifyConfigProvider) - let notifySubscribeResponseSubscriber = NotifySubscribeResponseSubscriber(networkingInteractor: networkInteractor, kms: kms, logger: logger, groupKeychainStorage: groupKeychainStorage, notifyStorage: notifyStorage, notifyConfigProvider: notifyConfigProvider) + let notifySubscriptionsUpdater = NotifySubsctiptionsUpdater(networkingInteractor: networkInteractor, kms: kms, logger: logger, notifyStorage: notifyStorage, groupKeychainStorage: groupKeychainStorage) + + let notifySubscriptionsBuilder = NotifySubscriptionsBuilder(notifyConfigProvider: notifyConfigProvider) + + let notifySubscribeResponseSubscriber = NotifySubscribeResponseSubscriber(networkingInteractor: networkInteractor, logger: logger, notifySubscriptionsBuilder: notifySubscriptionsBuilder, notifySubscriptionsUpdater: notifySubscriptionsUpdater) let notifyUpdateRequester = NotifyUpdateRequester(keyserverURL: keyserverURL, identityClient: identityClient, networkingInteractor: networkInteractor, notifyConfigProvider: notifyConfigProvider, logger: logger, notifyStorage: notifyStorage) - let notifyUpdateResponseSubscriber = NotifyUpdateResponseSubscriber(networkingInteractor: networkInteractor, logger: logger, notifyConfigProvider: notifyConfigProvider, notifyStorage: notifyStorage) + let notifyUpdateResponseSubscriber = NotifyUpdateResponseSubscriber(networkingInteractor: networkInteractor, logger: logger, notifySubscriptionsBuilder: notifySubscriptionsBuilder, notifySubscriptionsUpdater: notifySubscriptionsUpdater) let subscriptionsAutoUpdater = SubscriptionsAutoUpdater(notifyUpdateRequester: notifyUpdateRequester, logger: logger, notifyStorage: notifyStorage) let notifyWatcherAgreementKeysProvider = NotifyWatcherAgreementKeysProvider(kms: kms) let notifyWatchSubscriptionsRequester = NotifyWatchSubscriptionsRequester(keyserverURL: keyserverURL, networkingInteractor: networkInteractor, identityClient: identityClient, logger: logger, webDidResolver: webDidResolver, notifyAccountProvider: notifyAccountProvider, notifyWatcherAgreementKeysProvider: notifyWatcherAgreementKeysProvider, notifyHost: notifyHost) - let notifySubscriptionsBuilder = NotifySubscriptionsBuilder(notifyConfigProvider: notifyConfigProvider) - let notifyWatchSubscriptionsResponseSubscriber = NotifyWatchSubscriptionsResponseSubscriber(networkingInteractor: networkInteractor, kms: kms, logger: logger, notifyStorage: notifyStorage, groupKeychainStorage: groupKeychainStorage, notifySubscriptionsBuilder: notifySubscriptionsBuilder) - let notifySubscriptionsChangedRequestSubscriber = NotifySubscriptionsChangedRequestSubscriber(keyserver: keyserverURL, networkingInteractor: networkInteractor, kms: kms, identityClient: identityClient, logger: logger, groupKeychainStorage: groupKeychainStorage, notifyStorage: notifyStorage, notifySubscriptionsBuilder: notifySubscriptionsBuilder) + let notifyWatchSubscriptionsResponseSubscriber = NotifyWatchSubscriptionsResponseSubscriber(networkingInteractor: networkInteractor, logger: logger, notifySubscriptionsBuilder: notifySubscriptionsBuilder, notifySubscriptionsUpdater: notifySubscriptionsUpdater) + let notifySubscriptionsChangedRequestSubscriber = NotifySubscriptionsChangedRequestSubscriber(keyserver: keyserverURL, networkingInteractor: networkInteractor, identityClient: identityClient, logger: logger, notifySubscriptionsUpdater: notifySubscriptionsUpdater, notifySubscriptionsBuilder: notifySubscriptionsBuilder) let subscriptionWatcher = SubscriptionWatcher(notifyWatchSubscriptionsRequester: notifyWatchSubscriptionsRequester, logger: logger) let historyService = HistoryService(keyserver: keyserverURL, networkingClient: networkInteractor, identityClient: identityClient) + let notifyDeleteSubscriptionSubscriber = NotifyDeleteSubscriptionSubscriber(networkingInteractor: networkInteractor, kms: kms, logger: logger, notifySubscriptionsBuilder: notifySubscriptionsBuilder, notifySubscriptionsUpdater: notifySubscriptionsUpdater) return NotifyClient( logger: logger, @@ -77,17 +81,19 @@ public struct NotifyClientFactory { pushClient: pushClient, notifyMessageSubscriber: notifyMessageSubscriber, notifyStorage: notifyStorage, - deleteNotifySubscriptionRequester: deleteNotifySubscriptionRequester, + notifyDeleteSubscriptionRequester: notifyDeleteSubscriptionRequester, resubscribeService: resubscribeService, notifySubscribeRequester: notifySubscribeRequester, - notifySubscribeResponseSubscriber: notifySubscribeResponseSubscriber, + notifySubscribeResponseSubscriber: notifySubscribeResponseSubscriber, + notifyDeleteSubscriptionSubscriber: notifyDeleteSubscriptionSubscriber, notifyUpdateRequester: notifyUpdateRequester, notifyUpdateResponseSubscriber: notifyUpdateResponseSubscriber, notifyAccountProvider: notifyAccountProvider, subscriptionsAutoUpdater: subscriptionsAutoUpdater, notifyWatchSubscriptionsResponseSubscriber: notifyWatchSubscriptionsResponseSubscriber, notifyWatcherAgreementKeysProvider: notifyWatcherAgreementKeysProvider, - notifySubscriptionsChangedRequestSubscriber: notifySubscriptionsChangedRequestSubscriber, + notifySubscriptionsChangedRequestSubscriber: notifySubscriptionsChangedRequestSubscriber, + notifySubscriptionsUpdater: notifySubscriptionsUpdater, subscriptionWatcher: subscriptionWatcher ) } diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifySubsctiptionsUpdater.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifySubsctiptionsUpdater.swift new file mode 100644 index 000000000..6b48492d0 --- /dev/null +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifySubsctiptionsUpdater.swift @@ -0,0 +1,60 @@ +import Foundation +import Combine + +final class NotifySubsctiptionsUpdater { + private let networkingInteractor: NetworkInteracting + private let kms: KeyManagementServiceProtocol + private let logger: ConsoleLogging + private let notifyStorage: NotifyStorage + private let groupKeychainStorage: KeychainStorageProtocol + + private let subscriptionChangedSubject = PassthroughSubject<[NotifySubscription], Never>() + + var subscriptionChangedPublisher: AnyPublisher<[NotifySubscription], Never> { + return subscriptionChangedSubject.eraseToAnyPublisher() + } + + init(networkingInteractor: NetworkInteracting, kms: KeyManagementServiceProtocol, logger: ConsoleLogging, notifyStorage: NotifyStorage, groupKeychainStorage: KeychainStorageProtocol) { + self.networkingInteractor = networkingInteractor + self.kms = kms + self.logger = logger + self.notifyStorage = notifyStorage + self.groupKeychainStorage = groupKeychainStorage + } + + func update(subscriptions newSubscriptions: [NotifySubscription], for account: Account) async throws { + let oldSubscriptions = notifyStorage.getSubscriptions(account: account) + + subscriptionChangedSubject.send(newSubscriptions) + + try Task.checkCancellation() + + let subscriptions = oldSubscriptions.difference(from: newSubscriptions) + + logger.debug("Received: \(newSubscriptions.count), changed: \(subscriptions.count)") + + if subscriptions.count > 0 { + try notifyStorage.replaceAllSubscriptions(newSubscriptions) + + for subscription in newSubscriptions { + let symKey = try SymmetricKey(hex: subscription.symKey) + try groupKeychainStorage.add(symKey, forKey: subscription.topic) + try kms.setSymmetricKey(symKey, for: subscription.topic) + } + + let topicsToSubscribe = newSubscriptions.map { $0.topic } + + let oldTopics = Set(oldSubscriptions.map { $0.topic }) + let topicsToUnsubscribe = Array(oldTopics.subtracting(topicsToSubscribe)) + + try await networkingInteractor.batchUnsubscribe(topics: topicsToUnsubscribe) + try await networkingInteractor.batchSubscribe(topics: topicsToSubscribe) + + try Task.checkCancellation() + + logger.debug("Updated Subscriptions by Subscriptions Changed Request", properties: [ + "topics": newSubscriptions.map { $0.topic }.joined(separator: ",") + ]) + } + } +} diff --git a/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifyDelete/DeleteNotifySubscriptionRequester.swift b/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifyDelete/NotifyDeleteSubscriptionRequester.swift similarity index 84% rename from Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifyDelete/DeleteNotifySubscriptionRequester.swift rename to Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifyDelete/NotifyDeleteSubscriptionRequester.swift index b9c86009c..762f4948d 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifyDelete/DeleteNotifySubscriptionRequester.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifyDelete/NotifyDeleteSubscriptionRequester.swift @@ -1,13 +1,12 @@ import Foundation -class DeleteNotifySubscriptionRequester { +class NotifyDeleteSubscriptionRequester { enum Errors: Error { case notifySubscriptionNotFound } private let keyserver: URL private let networkingInteractor: NetworkInteracting private let identityClient: IdentityClient - private let kms: KeyManagementServiceProtocol private let logger: ConsoleLogging private let notifyStorage: NotifyStorage @@ -15,14 +14,12 @@ class DeleteNotifySubscriptionRequester { keyserver: URL, networkingInteractor: NetworkInteracting, identityClient: IdentityClient, - kms: KeyManagementServiceProtocol, logger: ConsoleLogging, notifyStorage: NotifyStorage ) { self.keyserver = keyserver self.networkingInteractor = networkingInteractor self.identityClient = identityClient - self.kms = kms self.logger = logger self.notifyStorage = notifyStorage } @@ -49,15 +46,11 @@ class DeleteNotifySubscriptionRequester { try notifyStorage.deleteSubscription(topic: topic) try notifyStorage.deleteMessages(topic: topic) - networkingInteractor.unsubscribe(topic: topic) - - logger.debug("Subscription removed, topic: \(topic)") - - kms.deleteSymmetricKey(for: topic) + logger.debug("Subscription delete request sent, topic: \(topic)") } } -private extension DeleteNotifySubscriptionRequester { +private extension NotifyDeleteSubscriptionRequester { func createJWTWrapper(dappPubKey: DIDKey, reason: String, app: DIDWeb, account: Account) throws -> NotifyDeletePayload.Wrapper { let jwtPayload = NotifyDeletePayload(account: account, keyserver: keyserver, dappPubKey: dappPubKey, app: app) diff --git a/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifyDelete/NotifyDeleteSubscriptionSubscriber.swift b/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifyDelete/NotifyDeleteSubscriptionSubscriber.swift new file mode 100644 index 000000000..3768b6697 --- /dev/null +++ b/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifyDelete/NotifyDeleteSubscriptionSubscriber.swift @@ -0,0 +1,51 @@ +import Foundation +import Combine + +class NotifyDeleteSubscriptionSubscriber { + + private let networkingInteractor: NetworkInteracting + private let kms: KeyManagementServiceProtocol + private let logger: ConsoleLogging + private let notifySubscriptionsBuilder: NotifySubscriptionsBuilder + private let notifySubscriptionsUpdater: NotifySubsctiptionsUpdater + + init( + networkingInteractor: NetworkInteracting, + kms: KeyManagementServiceProtocol, + logger: ConsoleLogging, + notifySubscriptionsBuilder: NotifySubscriptionsBuilder, + notifySubscriptionsUpdater: NotifySubsctiptionsUpdater + ) { + self.networkingInteractor = networkingInteractor + self.kms = kms + self.logger = logger + self.notifySubscriptionsBuilder = notifySubscriptionsBuilder + self.notifySubscriptionsUpdater = notifySubscriptionsUpdater + + subscribeForDeleteResponse() + } +} + +private extension NotifyDeleteSubscriptionSubscriber { + + func subscribeForDeleteResponse() { + networkingInteractor.subscribeOnResponse( + protocolMethod: NotifyDeleteProtocolMethod(), + requestOfType: NotifyDeletePayload.Wrapper.self, + responseOfType: NotifyDeleteResponsePayload.Wrapper.self, + errorHandler: logger + ) { [unowned self] payload in + + let (responsePayload, _) = try NotifyDeleteResponsePayload.decodeAndVerify(from: payload.response) + + let subscriptions = try await notifySubscriptionsBuilder.buildSubscriptions(responsePayload.subscriptions) + + try await notifySubscriptionsUpdater.update(subscriptions: subscriptions, for: responsePayload.account) + + logger.debug("Received Notify Delete response") + + networkingInteractor.unsubscribe(topic: payload.topic) + kms.deleteSymmetricKey(for: payload.topic) + } + } +} diff --git a/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifySubscriptionsChanged/NotifySubscriptionsChangedRequestSubscriber.swift b/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifySubscriptionsChanged/NotifySubscriptionsChangedRequestSubscriber.swift index a7c9cdd95..5125fc9f4 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifySubscriptionsChanged/NotifySubscriptionsChangedRequestSubscriber.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifySubscriptionsChanged/NotifySubscriptionsChangedRequestSubscriber.swift @@ -5,36 +5,25 @@ class NotifySubscriptionsChangedRequestSubscriber { private let keyserver: URL private let networkingInteractor: NetworkInteracting private let identityClient: IdentityClient - private let kms: KeyManagementServiceProtocol private let logger: ConsoleLogging - private let groupKeychainStorage: KeychainStorageProtocol - private let notifyStorage: NotifyStorage + private let notifySubscriptionsUpdater: NotifySubsctiptionsUpdater private let notifySubscriptionsBuilder: NotifySubscriptionsBuilder - - private let subscriptionChangedSubject = PassthroughSubject<[NotifySubscription], Never>() - - var subscriptionChangedPublisher: AnyPublisher<[NotifySubscription], Never> { - return subscriptionChangedSubject.eraseToAnyPublisher() - } init( keyserver: URL, networkingInteractor: NetworkInteracting, - kms: KeyManagementServiceProtocol, identityClient: IdentityClient, logger: ConsoleLogging, - groupKeychainStorage: KeychainStorageProtocol, - notifyStorage: NotifyStorage, + notifySubscriptionsUpdater: NotifySubsctiptionsUpdater, notifySubscriptionsBuilder: NotifySubscriptionsBuilder ) { self.keyserver = keyserver self.networkingInteractor = networkingInteractor - self.kms = kms self.logger = logger self.identityClient = identityClient - self.groupKeychainStorage = groupKeychainStorage - self.notifyStorage = notifyStorage + self.notifySubscriptionsUpdater = notifySubscriptionsUpdater self.notifySubscriptionsBuilder = notifySubscriptionsBuilder + subscribeForNofifyChangedRequests() } @@ -44,54 +33,20 @@ class NotifySubscriptionsChangedRequestSubscriber { protocolMethod: NotifySubscriptionsChangedProtocolMethod(), requestOfType: NotifySubscriptionsChangedRequestPayload.Wrapper.self, errorHandler: logger) { [unowned self] payload in + logger.debug("Received Subscriptions Changed Request") let (jwtPayload, _) = try NotifySubscriptionsChangedRequestPayload.decodeAndVerify(from: payload.request) - let account = jwtPayload.account - - // TODO: varify signature with notify server diddoc authentication key - - let oldSubscriptions = notifyStorage.getSubscriptions(account: account) - let newSubscriptions = try await notifySubscriptionsBuilder.buildSubscriptions(jwtPayload.subscriptions) - - subscriptionChangedSubject.send(newSubscriptions) - - try Task.checkCancellation() - let subscriptions = oldSubscriptions.difference(from: newSubscriptions) + let subscriptions = try await notifySubscriptionsBuilder.buildSubscriptions(jwtPayload.subscriptions) - logger.debug("Received: \(newSubscriptions.count), changed: \(subscriptions.count)") - - if subscriptions.count > 0 { - try notifyStorage.replaceAllSubscriptions(newSubscriptions) - - for subscription in newSubscriptions { - let symKey = try SymmetricKey(hex: subscription.symKey) - try groupKeychainStorage.add(symKey, forKey: subscription.topic) - try kms.setSymmetricKey(symKey, for: subscription.topic) - } - - let topics = newSubscriptions.map { $0.topic } - - try await networkingInteractor.batchSubscribe(topics: topics) - - try Task.checkCancellation() - - var logProperties = ["rpcId": payload.id.string] - for (index, subscription) in newSubscriptions.enumerated() { - let key = "subscription_\(index + 1)" - logProperties[key] = subscription.topic - } - - logger.debug("Updated Subscriptions by Subscriptions Changed Request", properties: logProperties) - } + try await notifySubscriptionsUpdater.update(subscriptions: subscriptions, for: jwtPayload.account) try await respond(topic: payload.topic, account: jwtPayload.account, rpcId: payload.id, notifyServerAuthenticationKey: jwtPayload.notifyServerAuthenticationKey) } } private func respond(topic: String, account: Account, rpcId: RPCID, notifyServerAuthenticationKey: DIDKey) async throws { - let receiptPayload = NotifySubscriptionsChangedResponsePayload(account: account, keyserver: keyserver, notifyServerAuthenticationKey: notifyServerAuthenticationKey) let wrapper = try identityClient.signAndCreateWrapper( diff --git a/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateResponseSubscriber.swift b/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateResponseSubscriber.swift index d9b2bafbd..76b2a539b 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateResponseSubscriber.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateResponseSubscriber.swift @@ -2,21 +2,23 @@ import Foundation import Combine class NotifyUpdateResponseSubscriber { + private let networkingInteractor: NetworkInteracting - private var publishers = [AnyCancellable]() private let logger: ConsoleLogging - private let notifyStorage: NotifyStorage - private let nofityConfigProvider: NotifyConfigProvider + private let notifySubscriptionsBuilder: NotifySubscriptionsBuilder + private let notifySubscriptionsUpdater: NotifySubsctiptionsUpdater - init(networkingInteractor: NetworkInteracting, - logger: ConsoleLogging, - notifyConfigProvider: NotifyConfigProvider, - notifyStorage: NotifyStorage + init( + networkingInteractor: NetworkInteracting, + logger: ConsoleLogging, + notifySubscriptionsBuilder: NotifySubscriptionsBuilder, + notifySubscriptionsUpdater: NotifySubsctiptionsUpdater ) { self.networkingInteractor = networkingInteractor self.logger = logger - self.notifyStorage = notifyStorage - self.nofityConfigProvider = notifyConfigProvider + self.notifySubscriptionsBuilder = notifySubscriptionsBuilder + self.notifySubscriptionsUpdater = notifySubscriptionsUpdater + subscribeForUpdateResponse() } @@ -24,10 +26,6 @@ class NotifyUpdateResponseSubscriber { } private extension NotifyUpdateResponseSubscriber { - enum Errors: Error { - case subscriptionDoesNotExist - case selectedScopeNotFound - } func subscribeForUpdateResponse() { networkingInteractor.subscribeOnResponse( @@ -37,7 +35,11 @@ private extension NotifyUpdateResponseSubscriber { errorHandler: logger ) { [unowned self] payload in - let _ = try NotifyUpdateResponsePayload.decodeAndVerify(from: payload.response) + let (responsePayload, _) = try NotifyUpdateResponsePayload.decodeAndVerify(from: payload.response) + + let subscriptions = try await notifySubscriptionsBuilder.buildSubscriptions(responsePayload.subscriptions) + + try await notifySubscriptionsUpdater.update(subscriptions: subscriptions, for: responsePayload.account) logger.debug("Received Notify Update response") } diff --git a/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifyWatchSubscriptions/NotifyWatchSubscriptionsResponseSubscriber.swift b/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifyWatchSubscriptions/NotifyWatchSubscriptionsResponseSubscriber.swift index dccdcbdd7..34239ec13 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifyWatchSubscriptions/NotifyWatchSubscriptionsResponseSubscriber.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifyWatchSubscriptions/NotifyWatchSubscriptionsResponseSubscriber.swift @@ -3,25 +3,20 @@ import Combine class NotifyWatchSubscriptionsResponseSubscriber { private let networkingInteractor: NetworkInteracting - private let kms: KeyManagementServiceProtocol private let logger: ConsoleLogging - private let notifyStorage: NotifyStorage - private let groupKeychainStorage: KeychainStorageProtocol private let notifySubscriptionsBuilder: NotifySubscriptionsBuilder + private let notifySubscriptionsUpdater: NotifySubsctiptionsUpdater init(networkingInteractor: NetworkInteracting, - kms: KeyManagementServiceProtocol, logger: ConsoleLogging, - notifyStorage: NotifyStorage, - groupKeychainStorage: KeychainStorageProtocol, - notifySubscriptionsBuilder: NotifySubscriptionsBuilder + notifySubscriptionsBuilder: NotifySubscriptionsBuilder, + notifySubscriptionsUpdater: NotifySubsctiptionsUpdater ) { self.networkingInteractor = networkingInteractor - self.kms = kms self.logger = logger - self.notifyStorage = notifyStorage - self.groupKeychainStorage = groupKeychainStorage self.notifySubscriptionsBuilder = notifySubscriptionsBuilder + self.notifySubscriptionsUpdater = notifySubscriptionsUpdater + subscribeForWatchSubscriptionsResponse() } @@ -32,45 +27,15 @@ class NotifyWatchSubscriptionsResponseSubscriber { requestOfType: NotifyWatchSubscriptionsPayload.Wrapper.self, responseOfType: NotifyWatchSubscriptionsResponsePayload.Wrapper.self, errorHandler: logger) { [unowned self] payload in + logger.debug("Received Notify Watch Subscriptions response") + let (requestPayload, _) = try NotifyWatchSubscriptionsPayload.decodeAndVerify(from: payload.request) let (responsePayload, _) = try NotifyWatchSubscriptionsResponsePayload.decodeAndVerify(from: payload.response) - let (watchSubscriptionPayloadRequest, _) = try NotifyWatchSubscriptionsPayload.decodeAndVerify(from: payload.request) - - let account = watchSubscriptionPayloadRequest.subscriptionAccount - // TODO: varify signature with notify server diddoc authentication key - - let oldSubscriptions = notifyStorage.getSubscriptions(account: account) - let newSubscriptions = try await notifySubscriptionsBuilder.buildSubscriptions(responsePayload.subscriptions) - - try Task.checkCancellation() - - let subscriptions = oldSubscriptions.difference(from: newSubscriptions) - - logger.debug("Received: \(newSubscriptions.count), changed: \(subscriptions.count)") - - if subscriptions.count > 0 { - // TODO: unsubscribe for oldSubscriptions topics that are not included in new subscriptions - try notifyStorage.replaceAllSubscriptions(newSubscriptions) - - for subscription in newSubscriptions { - let symKey = try SymmetricKey(hex: subscription.symKey) - try groupKeychainStorage.add(symKey, forKey: subscription.topic) - try kms.setSymmetricKey(symKey, for: subscription.topic) - } - - try await networkingInteractor.batchSubscribe(topics: newSubscriptions.map { $0.topic }) - - try Task.checkCancellation() - var logProperties = [String: String]() - for (index, subscription) in newSubscriptions.enumerated() { - let key = "subscription_\(index + 1)" - logProperties[key] = subscription.topic - } + let subscriptions = try await notifySubscriptionsBuilder.buildSubscriptions(responsePayload.subscriptions) - logger.debug("Updated Subscriptions with Watch Subscriptions Update, number of subscriptions: \(newSubscriptions.count)", properties: logProperties) - } + try await notifySubscriptionsUpdater.update(subscriptions: subscriptions, for: requestPayload.subscriptionAccount) } } diff --git a/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_pushSubscribe/NotifySubscribeResponseSubscriber.swift b/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_pushSubscribe/NotifySubscribeResponseSubscriber.swift index d8aa56a39..d42e8b1f0 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_pushSubscribe/NotifySubscribeResponseSubscriber.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_pushSubscribe/NotifySubscribeResponseSubscriber.swift @@ -2,31 +2,22 @@ import Foundation import Combine class NotifySubscribeResponseSubscriber { - enum Errors: Error { - case couldNotCreateSubscription - } private let networkingInteractor: NetworkInteracting - private let kms: KeyManagementServiceProtocol - private var publishers = [AnyCancellable]() private let logger: ConsoleLogging - private let notifyStorage: NotifyStorage - private let groupKeychainStorage: KeychainStorageProtocol - private let notifyConfigProvider: NotifyConfigProvider + private let notifySubscriptionsBuilder: NotifySubscriptionsBuilder + private let notifySubscriptionsUpdater: NotifySubsctiptionsUpdater init(networkingInteractor: NetworkInteracting, - kms: KeyManagementServiceProtocol, logger: ConsoleLogging, - groupKeychainStorage: KeychainStorageProtocol, - notifyStorage: NotifyStorage, - notifyConfigProvider: NotifyConfigProvider + notifySubscriptionsBuilder: NotifySubscriptionsBuilder, + notifySubscriptionsUpdater: NotifySubsctiptionsUpdater ) { self.networkingInteractor = networkingInteractor - self.kms = kms self.logger = logger - self.groupKeychainStorage = groupKeychainStorage - self.notifyStorage = notifyStorage - self.notifyConfigProvider = notifyConfigProvider + self.notifySubscriptionsBuilder = notifySubscriptionsBuilder + self.notifySubscriptionsUpdater = notifySubscriptionsUpdater + subscribeForSubscriptionResponse() } @@ -39,7 +30,12 @@ class NotifySubscribeResponseSubscriber { ) { [unowned self] payload in logger.debug("Received Notify Subscribe response") - let _ = try NotifySubscriptionResponsePayload.decodeAndVerify(from: payload.response) + let (requestPayload, _) = try NotifySubscriptionPayload.decodeAndVerify(from: payload.request) + let (responsePayload, _) = try NotifySubscriptionResponsePayload.decodeAndVerify(from: payload.response) + + let subscriptions = try await notifySubscriptionsBuilder.buildSubscriptions(responsePayload.subscriptions) + + try await notifySubscriptionsUpdater.update(subscriptions: subscriptions, for: requestPayload.subscriptionAccount) logger.debug("NotifySubscribeResponseSubscriber: unsubscribing from response topic: \(payload.topic)") diff --git a/Sources/WalletConnectNotify/Types/JWTPayloads/NotifySubscriptionResponsePayload.swift b/Sources/WalletConnectNotify/Types/JWTPayloads/NotifySubscriptionResponsePayload.swift index 8ed7e775e..d9f04440a 100644 --- a/Sources/WalletConnectNotify/Types/JWTPayloads/NotifySubscriptionResponsePayload.swift +++ b/Sources/WalletConnectNotify/Types/JWTPayloads/NotifySubscriptionResponsePayload.swift @@ -18,6 +18,8 @@ struct NotifySubscriptionResponsePayload: JWTClaimsCodable { let sub: String /// Dapp's domain url let app: String + /// array of Notify Subscriptions + let sbs: [NotifyServerSubscription] static var action: String? { return "notify_subscription_response" @@ -39,22 +41,16 @@ struct NotifySubscriptionResponsePayload: JWTClaimsCodable { let account: Account let selfPubKey: DIDKey let app: String + let subscriptions: [NotifyServerSubscription] init(claims: Claims) throws { self.account = try Account(DIDPKHString: claims.sub) self.selfPubKey = try DIDKey(did: claims.aud) self.app = claims.app + self.subscriptions = claims.sbs } func encode(iss: String) throws -> Claims { - return Claims( - iat: defaultIat(), - exp: expiry(days: 1), - act: Claims.action, - iss: iss, - aud: selfPubKey.did(variant: .ED25519), - sub: account.did, - app: app - ) + fatalError("Client is not supposed to encode this JWT payload") } } diff --git a/Sources/WalletConnectNotify/Types/JWTPayloads/NotifyUpdateResponsePayload.swift b/Sources/WalletConnectNotify/Types/JWTPayloads/NotifyUpdateResponsePayload.swift index a03ca61f8..7e49d08c3 100644 --- a/Sources/WalletConnectNotify/Types/JWTPayloads/NotifyUpdateResponsePayload.swift +++ b/Sources/WalletConnectNotify/Types/JWTPayloads/NotifyUpdateResponsePayload.swift @@ -16,6 +16,8 @@ struct NotifyUpdateResponsePayload: JWTClaimsCodable { let aud: String /// Blockchain account that notify subscription has been proposed for -`did:pkh` let sub: String + /// array of Notify Server Subscriptions + let sbs: [NotifyServerSubscription] /// Dapp's domain url let app: String @@ -39,22 +41,16 @@ struct NotifyUpdateResponsePayload: JWTClaimsCodable { let account: Account let selfPubKey: DIDKey let app: DIDWeb + let subscriptions: [NotifyServerSubscription] init(claims: Claims) throws { self.account = try Account(DIDPKHString: claims.sub) self.selfPubKey = try DIDKey(did: claims.aud) self.app = try DIDWeb(did: claims.app) + self.subscriptions = claims.sbs } func encode(iss: String) throws -> Claims { - return Claims( - iat: defaultIat(), - exp: expiry(days: 1), - act: Claims.action, - iss: iss, - aud: selfPubKey.did(variant: .ED25519), - sub: account.did, - app: app.did - ) + fatalError() } } diff --git a/Sources/WalletConnectNotify/Types/JWTPayloads/NotifyDeletePayload.swift b/Sources/WalletConnectNotify/Types/JWTPayloads/notify_delete/NotifyDeletePayload.swift similarity index 100% rename from Sources/WalletConnectNotify/Types/JWTPayloads/NotifyDeletePayload.swift rename to Sources/WalletConnectNotify/Types/JWTPayloads/notify_delete/NotifyDeletePayload.swift diff --git a/Sources/WalletConnectNotify/Types/JWTPayloads/NotifyDeleteResponsePayload.swift b/Sources/WalletConnectNotify/Types/JWTPayloads/notify_delete/NotifyDeleteResponsePayload.swift similarity index 84% rename from Sources/WalletConnectNotify/Types/JWTPayloads/NotifyDeleteResponsePayload.swift rename to Sources/WalletConnectNotify/Types/JWTPayloads/notify_delete/NotifyDeleteResponsePayload.swift index b9f97cc43..ac7f76e0d 100644 --- a/Sources/WalletConnectNotify/Types/JWTPayloads/NotifyDeleteResponsePayload.swift +++ b/Sources/WalletConnectNotify/Types/JWTPayloads/notify_delete/NotifyDeleteResponsePayload.swift @@ -16,6 +16,8 @@ struct NotifyDeleteResponsePayload: JWTClaimsCodable { let aud: String /// Blockchain account that notify subscription has been proposed for -`did:pkh` let sub: String + /// array of Notify Server Subscriptions + let sbs: [NotifyServerSubscription] /// Dapp's domain url let app: String @@ -39,22 +41,16 @@ struct NotifyDeleteResponsePayload: JWTClaimsCodable { let account: Account let selfPubKey: DIDKey let app: DIDWeb + let subscriptions: [NotifyServerSubscription] init(claims: Claims) throws { self.account = try Account(DIDPKHString: claims.sub) self.selfPubKey = try DIDKey(did: claims.aud) self.app = try DIDWeb(did: claims.app) + self.subscriptions = claims.sbs } func encode(iss: String) throws -> Claims { - return Claims( - iat: defaultIat(), - exp: expiry(days: 1), - act: Claims.action, - iss: iss, - aud: selfPubKey.did(variant: .ED25519), - sub: account.did, - app: app.did - ) + fatalError() } }