From 7fe66a2040140f0b9d15bb3c69440112c957503c Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Sat, 28 Oct 2023 03:33:24 +0800 Subject: [PATCH] testNotifySubscriptionChanged stability --- .../IntegrationTests/Push/NotifyTests.swift | 14 ++++---- .../Client/Wallet/NotifyWebDidResolver.swift | 34 +++++++++--------- .../DeleteNotifySubscriptionRequester.swift | 6 +--- ...ubscriptionsChangedRequestSubscriber.swift | 5 +-- .../NotifyWatchSubscriptionsRequester.swift | 6 ++-- ...WatchSubscriptionsResponseSubscriber.swift | 35 ++++++++++--------- .../NotifySubscribeRequester.swift | 3 +- 7 files changed, 52 insertions(+), 51 deletions(-) diff --git a/Example/IntegrationTests/Push/NotifyTests.swift b/Example/IntegrationTests/Push/NotifyTests.swift index 83de217cb..9c9fdc96d 100644 --- a/Example/IntegrationTests/Push/NotifyTests.swift +++ b/Example/IntegrationTests/Push/NotifyTests.swift @@ -136,13 +136,13 @@ final class NotifyTests: XCTestCase { let expectation = expectation(description: "expects client B to receive subscription after both clients are registered and client A creates one") expectation.assertForOverFulfill = false + var subscription: NotifySubscription! + let clientB = makeWalletClient(prefix: "👐🏼 Wallet B: ") - clientB.subscriptionChangedPublisher.sink { subscriptions in - guard let subscription = subscriptions.first else { return } - Task(priority: .high) { - try await clientB.deleteSubscription(topic: subscription.topic) - expectation.fulfill() - } + clientB.subscriptionsPublisher.sink { subscriptions in + guard let newSubscription = subscriptions.first else { return } + subscription = newSubscription + expectation.fulfill() }.store(in: &publishers) try! await walletNotifyClientA.register(account: account, domain: gmDappDomain, onSign: sign) @@ -150,6 +150,8 @@ final class NotifyTests: XCTestCase { try! await walletNotifyClientA.subscribe(appDomain: gmDappDomain, account: account) wait(for: [expectation], timeout: InputConfig.defaultTimeout) + + try await clientB.deleteSubscription(topic: subscription.topic) } func testWalletCreatesAndUpdatesSubscription() async { diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifyWebDidResolver.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifyWebDidResolver.swift index 5312e28bd..33a8449e6 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/NotifyWebDidResolver.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifyWebDidResolver.swift @@ -5,23 +5,21 @@ final class NotifyWebDidResolver { private static var subscribeKey = "wc-notify-subscribe-key" private static var authenticationKey = "wc-notify-authentication-key" - func resolveAgreementKey(domain: String) async throws -> AgreementPublicKey { - let didDoc = try await resolveDidDoc(domainUrl: domain) - let subscribeKeyPath = "\(didDoc.id)#\(Self.subscribeKey)" - guard let verificationMethod = didDoc.verificationMethod.first(where: { verificationMethod in verificationMethod.id == subscribeKeyPath }) else { throw Errors.noVerificationMethodForKey } - guard verificationMethod.publicKeyJwk.crv == .X25519 else { throw Errors.unsupportedCurve} - let pubKeyBase64Url = verificationMethod.publicKeyJwk.x - return try AgreementPublicKey(base64url: pubKeyBase64Url) + func resolveDidDoc(appDomain: String) async throws -> WebDidDoc { + guard let didDocUrl = URL(string: "https://\(appDomain)/.well-known/did.json") else { throw Errors.invalidUrl } + let (data, _) = try await URLSession.shared.data(from: didDocUrl) + return try JSONDecoder().decode(WebDidDoc.self, from: data) } - // TODO - Add cache for diddocs + func resolveAgreementKey(didDoc: WebDidDoc) throws -> AgreementPublicKey { + let keypath = "\(didDoc.id)#\(Self.subscribeKey)" + let pubKeyBase64Url = try resolveKey(didDoc: didDoc, curve: .X25519, keyPath: keypath) + return try AgreementPublicKey(base64url: pubKeyBase64Url) + } - func resolveAuthenticationKey(domain: String) async throws -> Data { - let didDoc = try await resolveDidDoc(domainUrl: domain) - let authenticationKeyPath = "\(didDoc.id)#\(Self.authenticationKey)" - guard let verificationMethod = didDoc.verificationMethod.first(where: { verificationMethod in verificationMethod.id == authenticationKeyPath }) else { throw Errors.noVerificationMethodForKey } - guard verificationMethod.publicKeyJwk.crv == .Ed25519 else { throw Errors.unsupportedCurve} - let pubKeyBase64Url = verificationMethod.publicKeyJwk.x + func resolveAuthenticationKey(didDoc: WebDidDoc) throws -> Data { + let keyPath = "\(didDoc.id)#\(Self.authenticationKey)" + let pubKeyBase64Url = try resolveKey(didDoc: didDoc, curve: .Ed25519, keyPath: keyPath) guard let raw = Data(base64url: pubKeyBase64Url) else { throw Errors.invalidBase64urlString } return raw } @@ -36,9 +34,9 @@ private extension NotifyWebDidResolver { case unsupportedCurve } - func resolveDidDoc(domainUrl: String) async throws -> WebDidDoc { - guard let didDocUrl = URL(string: "https://\(domainUrl)/.well-known/did.json") else { throw Errors.invalidUrl } - let (data, _) = try await URLSession.shared.data(from: didDocUrl) - return try JSONDecoder().decode(WebDidDoc.self, from: data) + func resolveKey(didDoc: WebDidDoc, curve: WebDidDoc.PublicKeyJwk.Curve, keyPath: String) throws -> String { + guard let verificationMethod = didDoc.verificationMethod.first(where: { verificationMethod in verificationMethod.id == keyPath }) else { throw Errors.noVerificationMethodForKey } + guard verificationMethod.publicKeyJwk.crv == curve else { throw Errors.unsupportedCurve } + return verificationMethod.publicKeyJwk.x } } diff --git a/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifyDelete/DeleteNotifySubscriptionRequester.swift b/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifyDelete/DeleteNotifySubscriptionRequester.swift index d0e49ef0f..a94e86420 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifyDelete/DeleteNotifySubscriptionRequester.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifyDelete/DeleteNotifySubscriptionRequester.swift @@ -54,11 +54,7 @@ class DeleteNotifySubscriptionRequester { logger.debug("Subscription removed, topic: \(topic)") kms.deleteSymmetricKey(for: topic) - } - - - - + } } private extension DeleteNotifySubscriptionRequester { diff --git a/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifySubscriptionsChanged/NotifySubscriptionsChangedRequestSubscriber.swift b/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifySubscriptionsChanged/NotifySubscriptionsChangedRequestSubscriber.swift index 87825fd58..18c8bbc31 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifySubscriptionsChanged/NotifySubscriptionsChangedRequestSubscriber.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifySubscriptionsChanged/NotifySubscriptionsChangedRequestSubscriber.swift @@ -53,7 +53,9 @@ class NotifySubscriptionsChangedRequestSubscriber { 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) @@ -61,7 +63,6 @@ class NotifySubscriptionsChangedRequestSubscriber { logger.debug("Received: \(newSubscriptions.count), changed: \(subscriptions.count)") if subscriptions.count > 0 { - subscriptionChangedSubject.send(newSubscriptions) notifyStorage.replaceAllSubscriptions(newSubscriptions, account: account) for subscription in newSubscriptions { diff --git a/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifyWatchSubscriptions/NotifyWatchSubscriptionsRequester.swift b/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifyWatchSubscriptions/NotifyWatchSubscriptionsRequester.swift index 72702c588..3b38ad939 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifyWatchSubscriptions/NotifyWatchSubscriptionsRequester.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifyWatchSubscriptions/NotifyWatchSubscriptionsRequester.swift @@ -41,8 +41,10 @@ class NotifyWatchSubscriptionsRequester: NotifyWatchSubscriptionsRequesting { logger.debug("Watching subscriptions") - let notifyServerPublicKey = try await webDidResolver.resolveAgreementKey(domain: notifyHost) - let notifyServerAuthenticationKey = try await webDidResolver.resolveAuthenticationKey(domain: notifyHost) + let didDoc = try await webDidResolver.resolveDidDoc(appDomain: notifyHost) + let notifyServerPublicKey = try webDidResolver.resolveAgreementKey(didDoc: didDoc) + let notifyServerAuthenticationKey = try webDidResolver.resolveAuthenticationKey(didDoc: didDoc) + let notifyServerAuthenticationDidKey = DIDKey(rawData: notifyServerAuthenticationKey) let watchSubscriptionsTopic = notifyServerPublicKey.rawRepresentation.sha256().toHexString() diff --git a/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifyWatchSubscriptions/NotifyWatchSubscriptionsResponseSubscriber.swift b/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifyWatchSubscriptions/NotifyWatchSubscriptionsResponseSubscriber.swift index 298932dec..55323843e 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifyWatchSubscriptions/NotifyWatchSubscriptionsResponseSubscriber.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifyWatchSubscriptions/NotifyWatchSubscriptionsResponseSubscriber.swift @@ -49,27 +49,28 @@ class NotifyWatchSubscriptionsResponseSubscriber { logger.debug("Received: \(newSubscriptions.count), changed: \(subscriptions.count)") - guard subscriptions.count > 0 else { return } - // TODO: unsubscribe for oldSubscriptions topics that are not included in new subscriptions - notifyStorage.replaceAllSubscriptions(newSubscriptions, account: account) - - 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) - } + if subscriptions.count > 0 { + // TODO: unsubscribe for oldSubscriptions topics that are not included in new subscriptions + notifyStorage.replaceAllSubscriptions(newSubscriptions, account: account) - try await networkingInteractor.batchSubscribe(topics: newSubscriptions.map { $0.topic }) + 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 Task.checkCancellation() + try await networkingInteractor.batchSubscribe(topics: newSubscriptions.map { $0.topic }) - var logProperties = [String: String]() - for (index, subscription) in newSubscriptions.enumerated() { - let key = "subscription_\(index + 1)" - logProperties[key] = subscription.topic - } + try Task.checkCancellation() - logger.debug("Updated Subscriptions with Watch Subscriptions Update, number of subscriptions: \(newSubscriptions.count)", properties: logProperties) + var logProperties = [String: String]() + for (index, subscription) in newSubscriptions.enumerated() { + let key = "subscription_\(index + 1)" + logProperties[key] = subscription.topic + } + + logger.debug("Updated Subscriptions with Watch Subscriptions Update, number of subscriptions: \(newSubscriptions.count)", properties: logProperties) + } } } diff --git a/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_pushSubscribe/NotifySubscribeRequester.swift b/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_pushSubscribe/NotifySubscribeRequester.swift index cc6894e86..32a875b43 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_pushSubscribe/NotifySubscribeRequester.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_pushSubscribe/NotifySubscribeRequester.swift @@ -38,7 +38,8 @@ class NotifySubscribeRequester { let config = await notifyConfigProvider.resolveNotifyConfig(appDomain: appDomain) - let peerPublicKey = try await webDidResolver.resolveAgreementKey(domain: appDomain) + let didDoc = try await webDidResolver.resolveDidDoc(appDomain: appDomain) + let peerPublicKey = try webDidResolver.resolveAgreementKey(didDoc: didDoc) let subscribeTopic = peerPublicKey.rawRepresentation.sha256().toHexString() let keysY = try generateAgreementKeys(peerPublicKey: peerPublicKey)