From 4c87e5655b7089d439692ae111fbc7ac4f8b0b12 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 10 Sep 2024 11:10:50 +0200 Subject: [PATCH 1/4] savepoint --- .github/workflows/cd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 6c110f787..0dfbfbbef 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -28,4 +28,4 @@ jobs: - name: Lint CocoaPods run: | - pod lib lint --verbose --no-clean --quick --allow-warnings --platforms=ios WalletConnectSwiftV2.podspec \ No newline at end of file + pod lib lint --verbose --no-clean --quick --allow-warnings --platforms=ios reown-swift.podspec From 7daf920af52f844c562c3e5be544cce0d42e95d5 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 10 Sep 2024 22:16:14 +0200 Subject: [PATCH 2/4] add better logging for socket connection --- Example/ExampleApp.xcodeproj/project.pbxproj | 14 ++--- Sources/WalletConnectRelay/Dispatching.swift | 5 ++ .../AutomaticSocketConnectionHandler.swift | 53 +++++++++++++++---- 3 files changed, 52 insertions(+), 20 deletions(-) diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index 4b19b0b33..cc49adc14 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -2419,11 +2419,9 @@ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CODE_SIGN_ENTITLEMENTS = PNDecryptionService/PNDecryptionService.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; - CODE_SIGN_STYLE = Manual; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 184; - DEVELOPMENT_TEAM = ""; - "DEVELOPMENT_TEAM[sdk=iphoneos*]" = W5R8AG9K22; + DEVELOPMENT_TEAM = W5R8AG9K22; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = PNDecryptionService/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = PNDecryptionService; @@ -2438,7 +2436,6 @@ PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.walletapp.PNDecryptionService; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore com.walletconnect.walletapp.PNDecryptionService"; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; @@ -2564,11 +2561,9 @@ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CODE_SIGN_ENTITLEMENTS = WalletApp/WalletApp.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; - CODE_SIGN_STYLE = Manual; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 184; - DEVELOPMENT_TEAM = ""; - "DEVELOPMENT_TEAM[sdk=iphoneos*]" = W5R8AG9K22; + DEVELOPMENT_TEAM = W5R8AG9K22; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = WalletApp/Other/Info.plist; @@ -2587,7 +2582,6 @@ PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.walletapp; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore com.walletconnect.walletapp"; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/Sources/WalletConnectRelay/Dispatching.swift b/Sources/WalletConnectRelay/Dispatching.swift index 48010907f..1dee272d9 100644 --- a/Sources/WalletConnectRelay/Dispatching.swift +++ b/Sources/WalletConnectRelay/Dispatching.swift @@ -69,21 +69,26 @@ final class Dispatcher: NSObject, Dispatching { } func protectedSend(_ string: String, completion: @escaping (Error?) -> Void) { + logger.debug("will try to send a socket frame") // Check if the socket is already connected and ready to send if socket.isConnected && networkMonitor.isConnected { + logger.debug("sending a socket frame") send(string, completion: completion) return } + logger.debug("Socket is not connected, will try to connect to send a frame") // Start the connection process if not already connected Task { do { // Await the connection handler to establish the connection try await socketConnectionHandler.handleInternalConnect() + logger.debug("internal connect successful, will try to send a socket frame") // If successful, send the message send(string, completion: completion) } catch { + logger.debug("failed to handle internal connect") // If an error occurs during connection, complete with that error completion(error) } diff --git a/Sources/WalletConnectRelay/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift b/Sources/WalletConnectRelay/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift index 6b17bd42d..b5133bdab 100644 --- a/Sources/WalletConnectRelay/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift +++ b/Sources/WalletConnectRelay/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift @@ -51,19 +51,23 @@ class AutomaticSocketConnectionHandler { func connect() { // Start the connection process + logger.debug("Starting connection process.") isConnecting = true socket.connect() } private func setUpSocketStatusObserving() { + logger.debug("Setting up socket status observing.") socketStatusProvider.socketConnectionStatusPublisher .sink { [unowned self] status in switch status { case .connected: + logger.debug("Socket connected.") isConnecting = false reconnectionAttempts = 0 // Reset reconnection attempts on successful connection stopPeriodicReconnectionTimer() // Stop any ongoing periodic reconnection attempts case .disconnected: + logger.debug("Socket disconnected.") if isConnecting { // Handle reconnection logic handleFailedConnectionAndReconnectIfNeeded() @@ -89,13 +93,18 @@ class AutomaticSocketConnectionHandler { } private func stopPeriodicReconnectionTimer() { + logger.debug("Stopping periodic reconnection timer.") reconnectionTimer?.cancel() reconnectionTimer = nil } private func startPeriodicReconnectionTimerIfNeeded() { - guard reconnectionTimer == nil else {return} + guard reconnectionTimer == nil else { + logger.debug("Reconnection timer is already running.") + return + } + logger.debug("Starting periodic reconnection timer.") reconnectionTimer = DispatchSource.makeTimerSource(queue: concurrentQueue) let initialDelay: DispatchTime = .now() + periodicReconnectionInterval @@ -112,48 +121,59 @@ class AutomaticSocketConnectionHandler { } private func setUpStateObserving() { + logger.debug("Setting up app state observing.") appStateObserver.onWillEnterBackground = { [unowned self] in + logger.debug("App will enter background. Registering background task.") registerBackgroundTask() } appStateObserver.onWillEnterForeground = { [unowned self] in + logger.debug("App will enter foreground. Reconnecting if needed.") reconnectIfNeeded() } } private func setUpNetworkMonitoring() { - networkMonitor.networkConnectionStatusPublisher.sink { [unowned self] networkConnectionStatus in - if networkConnectionStatus == .connected { - reconnectIfNeeded() + logger.debug("Setting up network monitoring.") + networkMonitor.networkConnectionStatusPublisher + .sink { [unowned self] networkConnectionStatus in + if networkConnectionStatus == .connected { + logger.debug("Network connected. Reconnecting if needed.") + reconnectIfNeeded() + } } - } - .store(in: &publishers) + .store(in: &publishers) } private func registerBackgroundTask() { + logger.debug("Registering background task.") backgroundTaskRegistrar.register(name: "Finish Network Tasks") { [unowned self] in endBackgroundTask() } } private func endBackgroundTask() { + logger.debug("Ending background task. Disconnecting socket.") socket.disconnect() } func reconnectIfNeeded() { // Check if client has active subscriptions and only then attempt to reconnect + logger.debug("Checking if reconnection is needed.") if !socket.isConnected && subscriptionsTracker.isSubscribed() { + logger.debug("Socket is not connected, but there are active subscriptions. Reconnecting...") connect() } } - var requestTimeout: TimeInterval = 15 + var requestTimeout: TimeInterval = 15 } // MARK: - SocketConnectionHandler extension AutomaticSocketConnectionHandler: SocketConnectionHandler { func handleInternalConnect() async throws { + logger.debug("Handling internal connection.") let maxAttempts = maxImmediateAttempts var attempts = 0 var isResumed = false // Track if continuation has been resumed @@ -161,6 +181,7 @@ extension AutomaticSocketConnectionHandler: SocketConnectionHandler { // Start the connection process immediately if not already connecting if !isConnecting { + logger.debug("Not already connecting. Starting connection.") connect() // This will set isConnecting = true and attempt to connect } @@ -172,7 +193,10 @@ extension AutomaticSocketConnectionHandler: SocketConnectionHandler { let connection = connectionStatusPublisher.connect() // Ensure connection is canceled when done - defer { connection.cancel() } + defer { + logger.debug("Cancelling connection status publisher.") + connection.cancel() + } // Use a Combine publisher to monitor disconnection and timeout try await withCheckedThrowingContinuation { (continuation: CheckedContinuation) in @@ -181,18 +205,20 @@ extension AutomaticSocketConnectionHandler: SocketConnectionHandler { cancellable = connectionStatusPublisher .setFailureType(to: NetworkError.self) // Set failure type to NetworkError .timeout(.seconds(requestTimeout), scheduler: concurrentQueue, customError: { NetworkError.connectionFailed }) - .sink(receiveCompletion: { completion in + .sink(receiveCompletion: { [unowned self] completion in guard !isResumed else { return } // Ensure continuation is only resumed once isResumed = true cancellable?.cancel() // Cancel the subscription to prevent further events // Handle only the failure case, as .finished is not expected to be meaningful here if case .failure(let error) = completion { + logger.debug("Connection failed with error: \(error).") continuation.resume(throwing: error) // Timeout or connection failure } }, receiveValue: { [unowned self] status in guard !isResumed else { return } // Ensure continuation is only resumed once if status == .connected { + logger.debug("Connection succeeded.") isResumed = true cancellable?.cancel() // Cancel the subscription to prevent further events continuation.resume() // Successfully connected @@ -201,6 +227,7 @@ extension AutomaticSocketConnectionHandler: SocketConnectionHandler { logger.debug("Disconnection observed, incrementing attempts to \(attempts)") if attempts >= maxAttempts { + logger.debug("Max attempts reached. Failing with connection error.") isResumed = true cancellable?.cancel() // Cancel the subscription to prevent further events continuation.resume(throwing: NetworkError.connectionFailed) @@ -214,15 +241,21 @@ extension AutomaticSocketConnectionHandler: SocketConnectionHandler { } func handleConnect() throws { + logger.debug("Manual connect requested but forbidden.") throw Errors.manualSocketConnectionForbidden } func handleDisconnect(closeCode: URLSessionWebSocketTask.CloseCode) throws { + logger.debug("Manual disconnect requested but forbidden.") throw Errors.manualSocketDisconnectionForbidden } func handleDisconnection() async { - guard await appStateObserver.currentState == .foreground else { return } + logger.debug("Handling disconnection.") + guard await appStateObserver.currentState == .foreground else { + logger.debug("App is not in foreground. No reconnection will be attempted.") + return + } reconnectIfNeeded() } } From 0712bb24fbf703675e177b8c5767eaa96e3f4820 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 11 Sep 2024 08:50:54 +0200 Subject: [PATCH 3/4] handle trying to pair stale pairing --- .../Services/Wallet/WalletPairService.swift | 8 ++++++-- .../WalletConnectSign/Engine/Common/ApproveEngine.swift | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Sources/WalletConnectPairing/Services/Wallet/WalletPairService.swift b/Sources/WalletConnectPairing/Services/Wallet/WalletPairService.swift index a025e49e1..2f2f2d0c9 100644 --- a/Sources/WalletConnectPairing/Services/Wallet/WalletPairService.swift +++ b/Sources/WalletConnectPairing/Services/Wallet/WalletPairService.swift @@ -2,6 +2,7 @@ import Foundation actor WalletPairService { enum Errors: Error { + case noPendingRequestsForPairing(topic: String) case networkNotConnected } @@ -70,13 +71,15 @@ extension WalletPairService { guard let pairing = pairingStorage.getPairing(forTopic: topic), pairing.requestReceived else { return false } - + let pendingRequests = history.getPending() .compactMap { record -> RPCRequest? in (record.topic == pairing.topic) ? record.request : nil } - guard !pendingRequests.isEmpty else { return false } + guard !pendingRequests.isEmpty else { + throw Errors.noPendingRequestsForPairing(topic: topic) + } pendingRequests.forEach { request in eventsClient.saveTraceEvent(PairingExecutionTraceEvents.emitSessionProposal) networkingInteractor.handleHistoryRequest(topic: topic, request: request) @@ -103,6 +106,7 @@ extension WalletPairService { extension WalletPairService.Errors: LocalizedError { var errorDescription: String? { switch self { + case .noPendingRequestsForPairing(let topic): return "No pending requests for pairing, topic: \(topic)" case .networkNotConnected: return "Pairing failed. You seem to be offline" } } diff --git a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift index ad2c52b20..a56f514c7 100644 --- a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift @@ -173,7 +173,7 @@ final class ApproveEngine { sessionStore.setSession(session) Task { - removePairing(pairingTopic: pairingTopic) + networkingInteractor.unsubscribe(topic: pairingTopic) } onSessionSettle?(session.publicRepresentation()) eventsClient.saveTraceEvent(SessionApproveExecutionTraceEvents.sessionSettleSuccess) @@ -202,7 +202,7 @@ final class ApproveEngine { if let pairingTopic = rpcHistory.get(recordId: payload.id)?.topic { Task { - removePairing(pairingTopic: pairingTopic) + networkingInteractor.unsubscribe(topic: pairingTopic) } } From fea452b6108ae0d65e4c2facab346a34929ca8bf Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 11 Sep 2024 09:26:33 +0200 Subject: [PATCH 4/4] update disconnection handling --- .../AutomaticSocketConnectionHandler.swift | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Sources/WalletConnectRelay/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift b/Sources/WalletConnectRelay/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift index b5133bdab..a3992fa00 100644 --- a/Sources/WalletConnectRelay/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift +++ b/Sources/WalletConnectRelay/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift @@ -163,6 +163,8 @@ class AutomaticSocketConnectionHandler { if !socket.isConnected && subscriptionsTracker.isSubscribed() { logger.debug("Socket is not connected, but there are active subscriptions. Reconnecting...") connect() + } else { + logger.debug("Will not attempt to reconnect") } } @@ -252,10 +254,10 @@ extension AutomaticSocketConnectionHandler: SocketConnectionHandler { func handleDisconnection() async { logger.debug("Handling disconnection.") - guard await appStateObserver.currentState == .foreground else { - logger.debug("App is not in foreground. No reconnection will be attempted.") - return - } +// guard await appStateObserver.currentState == .foreground else { +// logger.debug("App is not in foreground. No reconnection will be attempted.") +// return +// } reconnectIfNeeded() } }