From eebe2277bd5e99ee5bfbad9b57733c06869322e5 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Tue, 17 Oct 2023 17:52:26 +0800 Subject: [PATCH] NotifyConfig from explorer --- .../IntegrationTests/Push/NotifyTests.swift | 3 +- .../Client/Wallet/NotifyClientFactory.swift | 6 ++-- .../Client/Wallet/NotifyConfig.swift | 30 ++++++++++++++++ .../Client/Wallet/NotifyConfigAPI.swift | 33 +++++++++++++++++ .../Client/Wallet/NotifyConfigProvider.swift | 35 ++++++++----------- .../Wallet/NotifySubscriptionsBuilder.swift | 30 +++++++++------- .../NotifySubscribeRequester.swift | 19 ++++++---- ...NotifyConfig.swift => Notify+Config.swift} | 0 Sources/WalletConnectNotify/Notify.swift | 1 + .../DataStructures/NotificationConfig.swift | 10 ------ .../DataStructures/NotificationType.swift | 7 ---- 11 files changed, 115 insertions(+), 59 deletions(-) create mode 100644 Sources/WalletConnectNotify/Client/Wallet/NotifyConfig.swift create mode 100644 Sources/WalletConnectNotify/Client/Wallet/NotifyConfigAPI.swift rename Sources/WalletConnectNotify/{NotifyConfig.swift => Notify+Config.swift} (100%) delete mode 100644 Sources/WalletConnectNotify/Types/DataStructures/NotificationConfig.swift delete mode 100644 Sources/WalletConnectNotify/Types/DataStructures/NotificationType.swift diff --git a/Example/IntegrationTests/Push/NotifyTests.swift b/Example/IntegrationTests/Push/NotifyTests.swift index a4aa8dc97..264ca0e6d 100644 --- a/Example/IntegrationTests/Push/NotifyTests.swift +++ b/Example/IntegrationTests/Push/NotifyTests.swift @@ -74,7 +74,8 @@ final class NotifyTests: XCTestCase { keychainStorage: keychain, environment: .sandbox) let keyserverURL = URL(string: "https://keys.walletconnect.com")! - let client = NotifyClientFactory.create(keyserverURL: keyserverURL, + let client = NotifyClientFactory.create(projectId: InputConfig.projectId, + keyserverURL: keyserverURL, logger: notifyLogger, keyValueStorage: keyValueStorage, keychainStorage: keychain, diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifyClientFactory.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifyClientFactory.swift index 7cf0e8b5b..2896ec747 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/NotifyClientFactory.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifyClientFactory.swift @@ -2,7 +2,7 @@ import Foundation public struct NotifyClientFactory { - public static func create(groupIdentifier: String, networkInteractor: NetworkInteracting, pairingRegisterer: PairingRegisterer, pushClient: PushClient, crypto: CryptoProvider, notifyHost: String) -> NotifyClient { + public static func create(projectId: String, groupIdentifier: String, networkInteractor: NetworkInteracting, pairingRegisterer: PairingRegisterer, pushClient: PushClient, crypto: CryptoProvider, notifyHost: String) -> NotifyClient { let logger = ConsoleLogger(prefix: "🔔",loggingLevel: .debug) let keyValueStorage = UserDefaults.standard let keyserverURL = URL(string: "https://keys.walletconnect.com")! @@ -10,6 +10,7 @@ public struct NotifyClientFactory { let groupKeychainService = GroupKeychainStorage(serviceIdentifier: groupIdentifier) return NotifyClientFactory.create( + projectId: projectId, keyserverURL: keyserverURL, logger: logger, keyValueStorage: keyValueStorage, @@ -24,6 +25,7 @@ public struct NotifyClientFactory { } static func create( + projectId: String, keyserverURL: URL, logger: ConsoleLogging, keyValueStorage: KeyValueStorage, @@ -47,7 +49,7 @@ public struct NotifyClientFactory { let resubscribeService = NotifyResubscribeService(networkInteractor: networkInteractor, notifyStorage: notifyStorage, logger: logger) let dappsMetadataStore = CodableStore(defaults: keyValueStorage, identifier: NotifyStorageIdntifiers.dappsMetadataStore) - let notifyConfigProvider = NotifyConfigProvider() + let notifyConfigProvider = NotifyConfigProvider(projectId: projectId) let notifySubscribeRequester = NotifySubscribeRequester(keyserverURL: keyserverURL, networkingInteractor: networkInteractor, identityClient: identityClient, logger: logger, kms: kms, webDidResolver: webDidResolver, notifyConfigProvider: notifyConfigProvider, dappsMetadataStore: dappsMetadataStore) diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifyConfig.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifyConfig.swift new file mode 100644 index 000000000..5ae59614d --- /dev/null +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifyConfig.swift @@ -0,0 +1,30 @@ +import Foundation + +struct NotifyConfig: Codable { + struct NotificationType: Codable { + let id: String + let name: String + let description: String + } + struct ImageUrl: Codable { + let sm: String? + let md: String? + let lg: String? + } + let id: String + let name: String + let homepage: String + let description: String + let image_url: ImageUrl? + let notificationTypes: [NotificationType] + + var metadata: AppMetadata { + return AppMetadata( + name: name, + description: + description, + url: homepage, + icons: [image_url?.sm, image_url?.md, image_url?.lg].compactMap { $0 } + ) + } +} diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifyConfigAPI.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifyConfigAPI.swift new file mode 100644 index 000000000..362fce2fb --- /dev/null +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifyConfigAPI.swift @@ -0,0 +1,33 @@ +import Foundation + +enum NotifyConfigAPI: HTTPService { + + var path: String { + return "/w3i/v1/notify-config" + } + + var method: HTTPMethod { + return .get + } + + var body: Data? { + return nil + } + + var queryParameters: [String : String]? { + switch self { + case .notifyDApps(let projectId, let appDomain): + return ["projectId": projectId, "appDomain": appDomain] + } + } + + var additionalHeaderFields: [String : String]? { + return nil + } + + var scheme: String { + return "https" + } + + case notifyDApps(projectId: String, appDomain: String) +} diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifyConfigProvider.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifyConfigProvider.swift index 99a292a6d..e6b4746dc 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/NotifyConfigProvider.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifyConfigProvider.swift @@ -1,29 +1,24 @@ - import Foundation actor NotifyConfigProvider { - enum Errors: Error { - case invalidUrl - } - private var cache = [String: Set]() + private let projectId: String + + init(projectId: String) { + self.projectId = projectId + } - func getSubscriptionScope(appDomain: String) async throws -> Set { - if let availableScope = cache[appDomain] { - return availableScope - } - guard let notifyConfigUrl = URL(string: "https://\(appDomain)/.well-known/wc-notify-config.json") else { throw Errors.invalidUrl } - let (data, _) = try await URLSession.shared.data(from: notifyConfigUrl) - let config = try JSONDecoder().decode(NotificationConfig.self, from: data) - let availableScope = Set(config.types) - cache[appDomain] = availableScope - return availableScope + func resolveNotifyConfig(appDomain: String) async throws -> NotifyConfig { + let httpClient = HTTPNetworkClient(host: "explorer-api.walletconnect.com") + let request = NotifyConfigAPI.notifyDApps(projectId: projectId, appDomain: appDomain) + let response = try await httpClient.request(NotifyConfigResponse.self, at: request) + return response.data } +} + +private extension NotifyConfigProvider { - func getMetadata(appDomain: String) async throws -> AppMetadata { - guard let notifyConfigUrl = URL(string: "https://\(appDomain)/.well-known/wc-notify-config.json") else { throw Errors.invalidUrl } - let (data, _) = try await URLSession.shared.data(from: notifyConfigUrl) - let config = try JSONDecoder().decode(NotificationConfig.self, from: data) - return AppMetadata(name: config.name, description: config.description, url: appDomain, icons: config.icons) + struct NotifyConfigResponse: Codable { + let data: NotifyConfig } } diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifySubscriptionsBuilder.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifySubscriptionsBuilder.swift index fb1d0a325..7af9d2546 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/NotifySubscriptionsBuilder.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifySubscriptionsBuilder.swift @@ -11,25 +11,29 @@ class NotifySubscriptionsBuilder { var result = [NotifySubscription]() for subscription in notifyServerSubscriptions { - let scope = try await buildScope(selectedScope: subscription.scope, appDomain: subscription.appDomain) - guard let metadata = try? await notifyConfigProvider.getMetadata(appDomain: subscription.appDomain), - let topic = try? SymmetricKey(hex: subscription.symKey).derivedTopic() else { continue } + do { + let config = try await notifyConfigProvider.resolveNotifyConfig(appDomain: subscription.appDomain) + let topic = try SymmetricKey(hex: subscription.symKey).derivedTopic() + let scope = try await buildScope(selectedScope: subscription.scope, availableScope: config.notificationTypes) - let notifySubscription = NotifySubscription(topic: topic, - account: subscription.account, - relay: RelayProtocolOptions(protocol: "irn", data: nil), - metadata: metadata, - scope: scope, - expiry: subscription.expiry, - symKey: subscription.symKey) - result.append(notifySubscription) + result.append(NotifySubscription( + topic: topic, + account: subscription.account, + relay: RelayProtocolOptions(protocol: "irn", data: nil), + metadata: config.metadata, + scope: scope, + expiry: subscription.expiry, + symKey: subscription.symKey + )) + } catch { + continue + } } return result } - private func buildScope(selectedScope: [String], appDomain: String) async throws -> [String: ScopeValue] { - let availableScope = try await notifyConfigProvider.getSubscriptionScope(appDomain: appDomain) + private func buildScope(selectedScope: [String], availableScope: [NotifyConfig.NotificationType]) async throws -> [String: ScopeValue] { return availableScope.reduce(into: [:]) { $0[$1.name] = ScopeValue(description: $1.description, enabled: selectedScope.contains($1.name)) } diff --git a/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_pushSubscribe/NotifySubscribeRequester.swift b/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_pushSubscribe/NotifySubscribeRequester.swift index 6601d77b7..551a54058 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_pushSubscribe/NotifySubscribeRequester.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_pushSubscribe/NotifySubscribeRequester.swift @@ -39,16 +39,16 @@ class NotifySubscribeRequester { logger.debug("Subscribing for Notify, dappUrl: \(appDomain)") - let metadata = try await notifyConfigProvider.getMetadata(appDomain: appDomain) + let config = try await notifyConfigProvider.resolveNotifyConfig(appDomain: appDomain) - let peerPublicKey = try await webDidResolver.resolveAgreementKey(domain: metadata.url) + let peerPublicKey = try await webDidResolver.resolveAgreementKey(domain: appDomain) let subscribeTopic = peerPublicKey.rawRepresentation.sha256().toHexString() let keysY = try generateAgreementKeys(peerPublicKey: peerPublicKey) let responseTopic = keysY.derivedTopic() - dappsMetadataStore.set(metadata, forKey: responseTopic) + dappsMetadataStore.set(config.metadata, forKey: responseTopic) try kms.setSymmetricKey(keysY.sharedKey, for: subscribeTopic) try kms.setAgreementSecret(keysY, topic: responseTopic) @@ -80,10 +80,17 @@ class NotifySubscribeRequester { } private func createJWTWrapper(dappPubKey: DIDKey, subscriptionAccount: Account, appDomain: String) async throws -> NotifySubscriptionPayload.Wrapper { - let types = try await notifyConfigProvider.getSubscriptionScope(appDomain: appDomain) - let scope = types.map{$0.name}.joined(separator: " ") + let config = try await notifyConfigProvider.resolveNotifyConfig(appDomain: appDomain) let app = DIDWeb(host: appDomain) - let jwtPayload = NotifySubscriptionPayload(dappPubKey: dappPubKey, keyserver: keyserverURL, subscriptionAccount: subscriptionAccount, app: app, scope: scope) + let jwtPayload = NotifySubscriptionPayload( + dappPubKey: dappPubKey, + keyserver: keyserverURL, + subscriptionAccount: subscriptionAccount, + app: app, + scope: config.notificationTypes + .map { $0.name } + .joined(separator: " ") + ) return try identityClient.signAndCreateWrapper( payload: jwtPayload, account: subscriptionAccount diff --git a/Sources/WalletConnectNotify/NotifyConfig.swift b/Sources/WalletConnectNotify/Notify+Config.swift similarity index 100% rename from Sources/WalletConnectNotify/NotifyConfig.swift rename to Sources/WalletConnectNotify/Notify+Config.swift diff --git a/Sources/WalletConnectNotify/Notify.swift b/Sources/WalletConnectNotify/Notify.swift index 44c178032..039c139d6 100644 --- a/Sources/WalletConnectNotify/Notify.swift +++ b/Sources/WalletConnectNotify/Notify.swift @@ -7,6 +7,7 @@ public class Notify { } Push.configure(pushHost: config.pushHost, environment: config.environment) return NotifyClientFactory.create( + projectId: Networking.projectId, groupIdentifier: config.groupIdentifier, networkInteractor: Networking.interactor, pairingRegisterer: Pair.registerer, diff --git a/Sources/WalletConnectNotify/Types/DataStructures/NotificationConfig.swift b/Sources/WalletConnectNotify/Types/DataStructures/NotificationConfig.swift deleted file mode 100644 index f5d138793..000000000 --- a/Sources/WalletConnectNotify/Types/DataStructures/NotificationConfig.swift +++ /dev/null @@ -1,10 +0,0 @@ - -import Foundation - -struct NotificationConfig: Codable { - let schemaVersion: Int - let name: String - let description: String - let icons: [String] - let types: [NotificationType] -} diff --git a/Sources/WalletConnectNotify/Types/DataStructures/NotificationType.swift b/Sources/WalletConnectNotify/Types/DataStructures/NotificationType.swift deleted file mode 100644 index b741c4a2f..000000000 --- a/Sources/WalletConnectNotify/Types/DataStructures/NotificationType.swift +++ /dev/null @@ -1,7 +0,0 @@ - -import Foundation - -public struct NotificationType: Codable, Hashable { - let name: String - let description: String -}