From bc5c5b374d1c1dbec13cc88e7e2864008848653f Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 15 Dec 2022 20:27:16 +0500 Subject: [PATCH 1/3] CleanupService with unsubscribe --- .../NetworkInteracting.swift | 1 + .../NetworkingInteractor.swift | 5 ++++ Sources/WalletConnectRelay/RelayClient.swift | 26 +++++++++++++++++-- .../Services/SignCleanupService.swift | 18 +++++++++++-- .../WalletConnectSign/Sign/SignClient.swift | 8 ++---- .../Sign/SignClientFactory.swift | 2 +- .../RPCHistory/RPCHistory.swift | 8 ++++-- .../NetworkingInteractorMock.swift | 6 +++++ 8 files changed, 61 insertions(+), 13 deletions(-) diff --git a/Sources/WalletConnectNetworking/NetworkInteracting.swift b/Sources/WalletConnectNetworking/NetworkInteracting.swift index 30194ce37..ea759adfe 100644 --- a/Sources/WalletConnectNetworking/NetworkInteracting.swift +++ b/Sources/WalletConnectNetworking/NetworkInteracting.swift @@ -6,6 +6,7 @@ public protocol NetworkInteracting { var requestPublisher: AnyPublisher<(topic: String, request: RPCRequest), Never> { get } func subscribe(topic: String) async throws func unsubscribe(topic: String) + func batchUnsubscribe(topics: [String]) async throws func request(_ request: RPCRequest, topic: String, protocolMethod: ProtocolMethod, envelopeType: Envelope.EnvelopeType) async throws func requestNetworkAck(_ request: RPCRequest, topic: String, protocolMethod: ProtocolMethod) async throws func respond(topic: String, response: RPCResponse, protocolMethod: ProtocolMethod, envelopeType: Envelope.EnvelopeType) async throws diff --git a/Sources/WalletConnectNetworking/NetworkingInteractor.swift b/Sources/WalletConnectNetworking/NetworkingInteractor.swift index 236af5e54..9e72ba110 100644 --- a/Sources/WalletConnectNetworking/NetworkingInteractor.swift +++ b/Sources/WalletConnectNetworking/NetworkingInteractor.swift @@ -56,6 +56,11 @@ public class NetworkingInteractor: NetworkInteracting { } } + public func batchUnsubscribe(topics: [String]) async throws { + try await relayClient.batchUnsubscribe(topics: topics) + rpcHistory.deleteAll(forTopics: topics) + } + public func requestSubscription(on request: ProtocolMethod) -> AnyPublisher, Never> { return requestPublisher .filter { rpcRequest in diff --git a/Sources/WalletConnectRelay/RelayClient.swift b/Sources/WalletConnectRelay/RelayClient.swift index a55c1c941..1a618bda3 100644 --- a/Sources/WalletConnectRelay/RelayClient.swift +++ b/Sources/WalletConnectRelay/RelayClient.swift @@ -194,9 +194,31 @@ public final class RelayClient { subscribe(topic: topic) { error in if let error = error { continuation.resume(throwing: error) - return + } else { + continuation.resume(returning: ()) + } + } + } + } + + public func unsubscribe(topic: String) async throws { + return try await withCheckedThrowingContinuation { continuation in + unsubscribe(topic: topic) { error in + if let error = error { + continuation.resume(throwing: error) + } else { + continuation.resume(returning: ()) + } + } + } + } + + public func batchUnsubscribe(topics: [String]) async throws { + await withThrowingTaskGroup(of: Void.self) { group in + for topic in topics { + group.addTask { + try await self.unsubscribe(topic: topic) } - continuation.resume(returning: ()) } } } diff --git a/Sources/WalletConnectSign/Services/SignCleanupService.swift b/Sources/WalletConnectSign/Services/SignCleanupService.swift index 9142e0183..d3270d3bb 100644 --- a/Sources/WalletConnectSign/Services/SignCleanupService.swift +++ b/Sources/WalletConnectSign/Services/SignCleanupService.swift @@ -6,18 +6,32 @@ final class SignCleanupService { private let sessionStore: WCSessionStorage private let kms: KeyManagementServiceProtocol private let sessionToPairingTopic: CodableStore + private let networkInteractor: NetworkInteracting - init(pairingStore: WCPairingStorage, sessionStore: WCSessionStorage, kms: KeyManagementServiceProtocol, sessionToPairingTopic: CodableStore) { + init(pairingStore: WCPairingStorage, sessionStore: WCSessionStorage, kms: KeyManagementServiceProtocol, sessionToPairingTopic: CodableStore, networkInteractor: NetworkInteracting) { self.pairingStore = pairingStore self.sessionStore = sessionStore self.sessionToPairingTopic = sessionToPairingTopic + self.networkInteractor = networkInteractor self.kms = kms } - func cleanup() throws { + func cleanup() async throws { + try await unsubscribe() + pairingStore.deleteAll() sessionStore.deleteAll() sessionToPairingTopic.deleteAll() try kms.deleteAll() } } + +private extension SignCleanupService { + + func unsubscribe() async throws { + let pairing = pairingStore.getAll().map { $0.topic } + let session = sessionStore.getAll().map { $0.topic } + + try await networkInteractor.batchUnsubscribe(topics: pairing + session) + } +} diff --git a/Sources/WalletConnectSign/Sign/SignClient.swift b/Sources/WalletConnectSign/Sign/SignClient.swift index e00d55c98..795bc22b1 100644 --- a/Sources/WalletConnectSign/Sign/SignClient.swift +++ b/Sources/WalletConnectSign/Sign/SignClient.swift @@ -346,14 +346,10 @@ public final class SignClient { return Request(id: record.id, topic: record.topic, method: record.request.method, params: request, chainId: request.chainId) } -#if DEBUG /// Delete all stored data such as: pairings, sessions, keys - /// - /// - Note: Doesn't unsubscribe from topics - public func cleanup() throws { - try cleanupService.cleanup() + public func cleanup() async throws { + try await cleanupService.cleanup() } -#endif // MARK: - Private diff --git a/Sources/WalletConnectSign/Sign/SignClientFactory.swift b/Sources/WalletConnectSign/Sign/SignClientFactory.swift index 9fd5c17f4..ce27c1e4c 100644 --- a/Sources/WalletConnectSign/Sign/SignClientFactory.swift +++ b/Sources/WalletConnectSign/Sign/SignClientFactory.swift @@ -29,7 +29,7 @@ public struct SignClientFactory { let nonControllerSessionStateMachine = NonControllerSessionStateMachine(networkingInteractor: networkingClient, kms: kms, sessionStore: sessionStore, logger: logger) let controllerSessionStateMachine = ControllerSessionStateMachine(networkingInteractor: networkingClient, kms: kms, sessionStore: sessionStore, logger: logger) let approveEngine = ApproveEngine(networkingInteractor: networkingClient, proposalPayloadsStore: proposalPayloadsStore, sessionToPairingTopic: sessionToPairingTopic, pairingRegisterer: pairingClient, metadata: metadata, kms: kms, logger: logger, pairingStore: pairingStore, sessionStore: sessionStore) - let cleanupService = SignCleanupService(pairingStore: pairingStore, sessionStore: sessionStore, kms: kms, sessionToPairingTopic: sessionToPairingTopic) + let cleanupService = SignCleanupService(pairingStore: pairingStore, sessionStore: sessionStore, kms: kms, sessionToPairingTopic: sessionToPairingTopic, networkInteractor: networkingClient) let deleteSessionService = DeleteSessionService(networkingInteractor: networkingClient, kms: kms, sessionStore: sessionStore, logger: logger) let disconnectService = DisconnectService(deleteSessionService: deleteSessionService, sessionStorage: sessionStore) let sessionPingService = SessionPingService(sessionStorage: sessionStore, networkingInteractor: networkingClient, logger: logger) diff --git a/Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift b/Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift index 45129dd0c..8a556ee22 100644 --- a/Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift +++ b/Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift @@ -58,14 +58,18 @@ public final class RPCHistory { return record } - public func deleteAll(forTopic topic: String) { + public func deleteAll(forTopics topics: [String]){ storage.getAll().forEach { record in - if record.topic == topic { + if topics.contains(record.topic) { storage.delete(forKey: "\(record.id)") } } } + public func deleteAll(forTopic topic: String) { + deleteAll(forTopics: [topic]) + } + public func getPending() -> [Record] { storage.getAll().filter {$0.response == nil} } diff --git a/Tests/TestingUtils/NetworkingInteractorMock.swift b/Tests/TestingUtils/NetworkingInteractorMock.swift index 0ed0453bf..323c52279 100644 --- a/Tests/TestingUtils/NetworkingInteractorMock.swift +++ b/Tests/TestingUtils/NetworkingInteractorMock.swift @@ -99,6 +99,12 @@ public class NetworkingInteractorMock: NetworkInteracting { didCallUnsubscribe = true } + public func batchUnsubscribe(topics: [String]) async throws { + for topic in topics { + unsubscribe(topic: topic) + } + } + public func request(_ request: RPCRequest, topic: String, protocolMethod: ProtocolMethod, envelopeType: Envelope.EnvelopeType) async throws { requestCallCount += 1 requests.append((topic, request)) From 52a87931a2d59947fb561ceab0b9b2ac1390a066 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Fri, 16 Dec 2022 15:36:04 +0500 Subject: [PATCH 2/3] Separate debug cleanup --- Example/ExampleApp/SceneDelegate.swift | 4 ++-- .../Services/SignCleanupService.swift | 15 +++++++++++---- Sources/WalletConnectSign/Sign/SignClient.swift | 11 +++++++++++ 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/Example/ExampleApp/SceneDelegate.swift b/Example/ExampleApp/SceneDelegate.swift index c3ddd5743..38a75b651 100644 --- a/Example/ExampleApp/SceneDelegate.swift +++ b/Example/ExampleApp/SceneDelegate.swift @@ -10,7 +10,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? - func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) async { let metadata = AppMetadata( name: "Example Wallet", @@ -22,7 +22,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { Pair.configure(metadata: metadata) #if DEBUG if CommandLine.arguments.contains("-cleanInstall") { - try? Sign.instance.cleanup() + try? await Sign.instance.cleanup() } #endif diff --git a/Sources/WalletConnectSign/Services/SignCleanupService.swift b/Sources/WalletConnectSign/Services/SignCleanupService.swift index d3270d3bb..1da2aee3e 100644 --- a/Sources/WalletConnectSign/Services/SignCleanupService.swift +++ b/Sources/WalletConnectSign/Services/SignCleanupService.swift @@ -18,11 +18,11 @@ final class SignCleanupService { func cleanup() async throws { try await unsubscribe() + try cleanupStorages() + } - pairingStore.deleteAll() - sessionStore.deleteAll() - sessionToPairingTopic.deleteAll() - try kms.deleteAll() + func cleanup() throws { + try cleanupStorages() } } @@ -34,4 +34,11 @@ private extension SignCleanupService { try await networkInteractor.batchUnsubscribe(topics: pairing + session) } + + func cleanupStorages() throws { + pairingStore.deleteAll() + sessionStore.deleteAll() + sessionToPairingTopic.deleteAll() + try kms.deleteAll() + } } diff --git a/Sources/WalletConnectSign/Sign/SignClient.swift b/Sources/WalletConnectSign/Sign/SignClient.swift index 795bc22b1..dda1b477e 100644 --- a/Sources/WalletConnectSign/Sign/SignClient.swift +++ b/Sources/WalletConnectSign/Sign/SignClient.swift @@ -347,10 +347,21 @@ public final class SignClient { } /// Delete all stored data such as: pairings, sessions, keys + /// + /// - Note: Will unsubscribe from all topics public func cleanup() async throws { try await cleanupService.cleanup() } +#if DEBUG + /// Delete all stored data such as: pairings, sessions, keys + /// + /// - Note: Doesn't unsubscribe from topics + public func cleanup() throws { + try cleanupService.cleanup() + } +#endif + // MARK: - Private private func setUpEnginesCallbacks() { From 434882721083bc7df52ebdd357a3c39ef7dfa394 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Fri, 16 Dec 2022 16:02:34 +0500 Subject: [PATCH 3/3] ScenDelegate changes reverted --- Example/ExampleApp/SceneDelegate.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Example/ExampleApp/SceneDelegate.swift b/Example/ExampleApp/SceneDelegate.swift index 38a75b651..c3ddd5743 100644 --- a/Example/ExampleApp/SceneDelegate.swift +++ b/Example/ExampleApp/SceneDelegate.swift @@ -10,7 +10,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? - func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) async { + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { let metadata = AppMetadata( name: "Example Wallet", @@ -22,7 +22,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { Pair.configure(metadata: metadata) #if DEBUG if CommandLine.arguments.contains("-cleanInstall") { - try? await Sign.instance.cleanup() + try? Sign.instance.cleanup() } #endif