From 56e5a43730d2f8ec2f9b1e465a0cbfcf6a0bdef0 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Mon, 9 Oct 2023 13:03:02 +0800 Subject: [PATCH 01/49] Release CI updated --- .github/workflows/cd.yml | 3 ++- .github/workflows/release.yml | 3 ++- Example/ExampleApp.xcodeproj/project.pbxproj | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 500646255..1f26d47f9 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -13,7 +13,8 @@ env: PACKAGE_VERSION: ${{ github.event.pull_request.title }} jobs: set-user-agent: - runs-on: macos-latest + runs-on: + group: apple-silicon steps: - uses: actions/checkout@v2 with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d11425c1c..df62cc8a9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,7 +9,8 @@ on: jobs: build: - runs-on: macos-12 + runs-on: + group: apple-silicon steps: - uses: actions/checkout@v3 diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index 5816bd42b..c3150fd0a 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -3223,7 +3223,7 @@ isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/WalletConnect/web3modal-swift"; requirement = { - branch = "feat/ui-package"; + branch = main; kind = branch; }; }; From 6e6341ccbf704f660289801be93a92abdf7cbd17 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Mon, 9 Oct 2023 13:28:00 +0800 Subject: [PATCH 02/49] web3modal from develop --- Example/ExampleApp.xcodeproj/project.pbxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index c3150fd0a..3a595c501 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -3223,7 +3223,7 @@ isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/WalletConnect/web3modal-swift"; requirement = { - branch = main; + branch = develop; kind = branch; }; }; From bdccbe379f4b68ea998ee5eca53f603226fa2ab9 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Mon, 9 Oct 2023 13:56:18 +0800 Subject: [PATCH 03/49] Package.resolved update --- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 7a1658ba6..600596394 100644 --- a/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -167,8 +167,8 @@ "package": "swift-web3modal", "repositoryURL": "https://github.com/WalletConnect/web3modal-swift", "state": { - "branch": "feat/ui-package", - "revision": "c87ab80f45452e1980b018bfabd8ec1f672c5ed3", + "branch": "develop", + "revision": "24602e2acee171fac26aa978b30666deec68a08f", "version": null } } From 9fdc1199944ea0e1972a7470298295a15a8ab61e Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 7 Sep 2023 16:46:03 +0800 Subject: [PATCH 04/49] ClientID public and private part --- .../HistoryClientFactory.swift | 10 ++- .../WalletConnectPush/PushClientFactory.swift | 5 +- .../ClientAuth/ClientIdStorage.swift | 71 +++++++++++++++++-- .../RelayClientFactory.swift | 2 +- 4 files changed, 76 insertions(+), 12 deletions(-) diff --git a/Sources/WalletConnectHistory/HistoryClientFactory.swift b/Sources/WalletConnectHistory/HistoryClientFactory.swift index 5168430a3..6ca9b78ab 100644 --- a/Sources/WalletConnectHistory/HistoryClientFactory.swift +++ b/Sources/WalletConnectHistory/HistoryClientFactory.swift @@ -4,15 +4,19 @@ class HistoryClientFactory { static func create() -> HistoryClient { let keychain = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk") + let keyValueStorage = UserDefaults.standard + let logger = ConsoleLogger() return HistoryClientFactory.create( historyUrl: "https://history.walletconnect.com", relayUrl: "wss://relay.walletconnect.com", - keychain: keychain + keyValueStorage: keyValueStorage, + keychain: keychain, + logger: logger ) } - static func create(historyUrl: String, relayUrl: String, keychain: KeychainStorageProtocol) -> HistoryClient { - let clientIdStorage = ClientIdStorage(keychain: keychain) + static func create(historyUrl: String, relayUrl: String, keyValueStorage: KeyValueStorage, keychain: KeychainStorageProtocol, logger: ConsoleLogging) -> HistoryClient { + let clientIdStorage = ClientIdStorage(defaults: keyValueStorage, keychain: keychain, logger: logger) let kms = KeyManagementService(keychain: keychain) let serializer = Serializer(kms: kms, logger: ConsoleLogger(prefix: "🔐", loggingLevel: .off)) let historyNetworkService = HistoryNetworkService(clientIdStorage: clientIdStorage) diff --git a/Sources/WalletConnectPush/PushClientFactory.swift b/Sources/WalletConnectPush/PushClientFactory.swift index 65c733886..fb2fb0d10 100644 --- a/Sources/WalletConnectPush/PushClientFactory.swift +++ b/Sources/WalletConnectPush/PushClientFactory.swift @@ -6,10 +6,12 @@ public struct PushClientFactory { environment: APNSEnvironment) -> PushClient { let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk") + let keyValueStorage = UserDefaults.standard return PushClientFactory.create( projectId: projectId, pushHost: pushHost, + keyValueStorage: keyValueStorage, keychainStorage: keychainStorage, environment: environment) } @@ -17,6 +19,7 @@ public struct PushClientFactory { public static func create( projectId: String, pushHost: String, + keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, environment: APNSEnvironment ) -> PushClient { @@ -28,7 +31,7 @@ public struct PushClientFactory { let logger = ConsoleLogger(prefix: "👂🏻", loggingLevel: .off) let httpClient = HTTPNetworkClient(host: pushHost, session: session) - let clientIdStorage = ClientIdStorage(keychain: keychainStorage) + let clientIdStorage = ClientIdStorage(defaults: keyValueStorage, keychain: keychainStorage, logger: logger) let pushAuthenticator = PushAuthenticator(clientIdStorage: clientIdStorage, pushHost: pushHost) diff --git a/Sources/WalletConnectRelay/ClientAuth/ClientIdStorage.swift b/Sources/WalletConnectRelay/ClientAuth/ClientIdStorage.swift index 2e3c9cd7f..04c82c68e 100644 --- a/Sources/WalletConnectRelay/ClientAuth/ClientIdStorage.swift +++ b/Sources/WalletConnectRelay/ClientAuth/ClientIdStorage.swift @@ -6,26 +6,83 @@ public protocol ClientIdStoring { } public struct ClientIdStorage: ClientIdStoring { - private let key = "com.walletconnect.iridium.client_id" + private let oldStorageKey = "com.walletconnect.iridium.client_id" + private let publicStorageKey = "com.walletconnect.iridium.client_id.public" + + private let defaults: KeyValueStorage private let keychain: KeychainStorageProtocol + private let logger: ConsoleLogging - public init(keychain: KeychainStorageProtocol) { + public init(defaults: KeyValueStorage, keychain: KeychainStorageProtocol, logger: ConsoleLogging) { + self.defaults = defaults self.keychain = keychain + self.logger = logger + + migrateIfNeeded() } public func getOrCreateKeyPair() throws -> SigningPrivateKey { do { - return try keychain.read(key: key) + let publicPart = try getPublicPart() + return try getPrivatePart(for: publicPart) } catch { let privateKey = SigningPrivateKey() - try keychain.add(privateKey, forKey: key) + try setPrivatePart(privateKey) + setPublicPart(privateKey.publicKey) return privateKey } } public func getClientId() throws -> String { - let privateKey: SigningPrivateKey = try keychain.read(key: key) - let pubKey = privateKey.publicKey.rawRepresentation - return DIDKey(rawData: pubKey).did(variant: .ED25519) + let pubKey = try getPublicPart() + return DIDKey(rawData: pubKey.rawRepresentation).did(variant: .ED25519) + } +} + +private extension ClientIdStorage { + + enum Errors: Error { + case publicPartNotFound + } + + func migrateIfNeeded() { + guard let privateKey: SigningPrivateKey = try? keychain.read(key: oldStorageKey) else { + return + } + + do { + try setPrivatePart(privateKey) + setPublicPart(privateKey.publicKey) + try keychain.delete(key: oldStorageKey) + logger.debug("ClientID migrated") + } catch { + logger.debug("ClientID migration failed with: \(error.localizedDescription)") + } + } + + func getPublicPart() throws -> SigningPublicKey { + guard let data = defaults.data(forKey: publicStorageKey) else { + throw Errors.publicPartNotFound + } + return try SigningPublicKey(rawRepresentation: data) + } + + func setPublicPart(_ newValue: SigningPublicKey) { + defaults.set(newValue.rawRepresentation, forKey: publicStorageKey) + } + + func getPrivatePart(for publicPart: SigningPublicKey) throws -> SigningPrivateKey { + return try keychain.read(key: publicPart.storageId) + } + + func setPrivatePart(_ newValue: SigningPrivateKey) throws { + try keychain.add(newValue, forKey: newValue.publicKey.storageId) + } +} + +private extension SigningPublicKey { + + var storageId: String { + return rawRepresentation.sha256().toHexString() } } diff --git a/Sources/WalletConnectRelay/RelayClientFactory.swift b/Sources/WalletConnectRelay/RelayClientFactory.swift index 6748811fb..48e7f766a 100644 --- a/Sources/WalletConnectRelay/RelayClientFactory.swift +++ b/Sources/WalletConnectRelay/RelayClientFactory.swift @@ -39,7 +39,7 @@ public struct RelayClientFactory { logger: ConsoleLogging ) -> RelayClient { - let clientIdStorage = ClientIdStorage(keychain: keychainStorage) + let clientIdStorage = ClientIdStorage(defaults: keyValueStorage, keychain: keychainStorage, logger: logger) let socketAuthenticator = ClientIdAuthenticator( clientIdStorage: clientIdStorage, From 958a750507bbdca753e5d017acf6d8545dbe6351 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 7 Sep 2023 19:16:44 +0800 Subject: [PATCH 05/49] Tests updated --- Example/IntegrationTests/Chat/ChatTests.swift | 4 +++- Example/IntegrationTests/History/HistoryTests.swift | 8 +++++--- Example/IntegrationTests/Pairing/PairingTests.swift | 5 ++++- Example/IntegrationTests/Push/NotifyTests.swift | 1 + Tests/RelayerTests/AuthTests/ClientIdStorageTests.swift | 2 +- 5 files changed, 14 insertions(+), 6 deletions(-) diff --git a/Example/IntegrationTests/Chat/ChatTests.swift b/Example/IntegrationTests/Chat/ChatTests.swift index 0d44be8b6..04cdc7e68 100644 --- a/Example/IntegrationTests/Chat/ChatTests.swift +++ b/Example/IntegrationTests/Chat/ChatTests.swift @@ -74,7 +74,9 @@ final class ChatTests: XCTestCase { let historyClient = HistoryClientFactory.create( historyUrl: "https://history.walletconnect.com", relayUrl: "wss://relay.walletconnect.com", - keychain: keychain + keyValueStorage: keyValueStorage, + keychain: keychain, + logger: logger ) let clientId = try! networkingInteractor.getClientId() diff --git a/Example/IntegrationTests/History/HistoryTests.swift b/Example/IntegrationTests/History/HistoryTests.swift index 40d52f5c0..d96e0f811 100644 --- a/Example/IntegrationTests/History/HistoryTests.swift +++ b/Example/IntegrationTests/History/HistoryTests.swift @@ -18,9 +18,11 @@ final class HistoryTests: XCTestCase { override func setUp() { let keychain1 = KeychainStorageMock() let keychain2 = KeychainStorageMock() + let logger1 = ConsoleLoggerMock() + let defaults1 = RuntimeKeyValueStorage() relayClient1 = makeRelayClient(prefix: "🐄", keychain: keychain1) relayClient2 = makeRelayClient(prefix: "🐫", keychain: keychain2) - historyClient = makeHistoryClient(keychain: keychain1) + historyClient = makeHistoryClient(defaults: defaults1, keychain: keychain1, logger: logger1) } private func makeRelayClient(prefix: String, keychain: KeychainStorageProtocol) -> RelayClient { @@ -33,8 +35,8 @@ final class HistoryTests: XCTestCase { logger: ConsoleLogger(prefix: prefix + " [Relay]", loggingLevel: .debug)) } - private func makeHistoryClient(keychain: KeychainStorageProtocol) -> HistoryNetworkService { - let clientIdStorage = ClientIdStorage(keychain: keychain) + private func makeHistoryClient(defaults: KeyValueStorage, keychain: KeychainStorageProtocol, logger: ConsoleLogging) -> HistoryNetworkService { + let clientIdStorage = ClientIdStorage(defaults: defaults, keychain: keychain, logger: logger) return HistoryNetworkService(clientIdStorage: clientIdStorage) } diff --git a/Example/IntegrationTests/Pairing/PairingTests.swift b/Example/IntegrationTests/Pairing/PairingTests.swift index a2b49e07b..f7ade4b87 100644 --- a/Example/IntegrationTests/Pairing/PairingTests.swift +++ b/Example/IntegrationTests/Pairing/PairingTests.swift @@ -78,11 +78,14 @@ final class PairingTests: XCTestCase { let prefix = "🐶 Wallet: " let (pairingClient, networkingInteractor, keychain, keyValueStorage) = makeClientDependencies(prefix: prefix) let notifyLogger = ConsoleLogger(prefix: prefix + " [Notify]", loggingLevel: .debug) + let defaults = RuntimeKeyValueStorage() walletPairingClient = pairingClient let historyClient = HistoryClientFactory.create( historyUrl: "https://history.walletconnect.com", relayUrl: "wss://relay.walletconnect.com", - keychain: keychain + keyValueStorage: defaults, + keychain: keychain, + logger: notifyLogger ) appAuthClient = AuthClientFactory.create( metadata: AppMetadata(name: name, description: "", url: "", icons: [""]), diff --git a/Example/IntegrationTests/Push/NotifyTests.swift b/Example/IntegrationTests/Push/NotifyTests.swift index 00783bd2a..67f2aa889 100644 --- a/Example/IntegrationTests/Push/NotifyTests.swift +++ b/Example/IntegrationTests/Push/NotifyTests.swift @@ -70,6 +70,7 @@ final class NotifyTests: XCTestCase { let notifyLogger = ConsoleLogger(prefix: prefix + " [Notify]", loggingLevel: .debug) let pushClient = PushClientFactory.create(projectId: "", pushHost: "echo.walletconnect.com", + keyValueStorage: keyValueStorage, keychainStorage: keychain, environment: .sandbox) let keyserverURL = URL(string: "https://keys.walletconnect.com")! diff --git a/Tests/RelayerTests/AuthTests/ClientIdStorageTests.swift b/Tests/RelayerTests/AuthTests/ClientIdStorageTests.swift index 05532fa8d..cceea9242 100644 --- a/Tests/RelayerTests/AuthTests/ClientIdStorageTests.swift +++ b/Tests/RelayerTests/AuthTests/ClientIdStorageTests.swift @@ -11,7 +11,7 @@ final class ClientIdStorageTests: XCTestCase { override func setUp() { keychain = KeychainStorageMock() - sut = ClientIdStorage(keychain: keychain) + sut = ClientIdStorage(defaults: RuntimeKeyValueStorage(), keychain: keychain, logger: ConsoleLoggerMock()) } func testGetOrCreate() throws { From c596996f8d65de3ce6caf137dd67564a0020e76b Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 7 Sep 2023 19:58:34 +0800 Subject: [PATCH 06/49] DispatcherTests updated --- Tests/RelayerTests/DispatcherTests.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Tests/RelayerTests/DispatcherTests.swift b/Tests/RelayerTests/DispatcherTests.swift index 35660c6f1..4ab1c0a48 100644 --- a/Tests/RelayerTests/DispatcherTests.swift +++ b/Tests/RelayerTests/DispatcherTests.swift @@ -60,8 +60,10 @@ final class DispatcherTests: XCTestCase { webSocket = WebSocketMock() let webSocketFactory = WebSocketFactoryMock(webSocket: webSocket) networkMonitor = NetworkMonitoringMock() + let defaults = RuntimeKeyValueStorage() + let logger = ConsoleLoggerMock() let keychainStorageMock = DispatcherKeychainStorageMock() - let clientIdStorage = ClientIdStorage(keychain: keychainStorageMock) + let clientIdStorage = ClientIdStorage(defaults: defaults, keychain: keychainStorageMock, logger: logger) let socketAuthenticator = ClientIdAuthenticator( clientIdStorage: clientIdStorage, url: "wss://relay.walletconnect.com" From 3587679b94fe0525f913f38d76ed899d56e29375 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 7 Sep 2023 20:05:20 +0800 Subject: [PATCH 07/49] RelayClientEndToEndTests updated --- .../RelayIntegrationTests/RelayClientEndToEndTests.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Example/RelayIntegrationTests/RelayClientEndToEndTests.swift b/Example/RelayIntegrationTests/RelayClientEndToEndTests.swift index 9dc595c95..e6e5263ae 100644 --- a/Example/RelayIntegrationTests/RelayClientEndToEndTests.swift +++ b/Example/RelayIntegrationTests/RelayClientEndToEndTests.swift @@ -31,7 +31,9 @@ final class RelayClientEndToEndTests: XCTestCase { private var publishers = Set() func makeRelayClient(prefix: String) -> RelayClient { - let clientIdStorage = ClientIdStorage(keychain: KeychainStorageMock()) + let keyValueStorage = RuntimeKeyValueStorage() + let logger = ConsoleLogger(prefix: prefix, loggingLevel: .debug) + let clientIdStorage = ClientIdStorage(defaults: keyValueStorage, keychain: KeychainStorageMock(), logger: logger) let socketAuthenticator = ClientIdAuthenticator( clientIdStorage: clientIdStorage, url: InputConfig.relayUrl @@ -43,7 +45,6 @@ final class RelayClientEndToEndTests: XCTestCase { ) let socket = WebSocket(url: urlFactory.create(fallback: false)) let webSocketFactory = WebSocketFactoryMock(webSocket: socket) - let logger = ConsoleLogger(prefix: prefix, loggingLevel: .debug) let dispatcher = Dispatcher( socketFactory: webSocketFactory, relayUrlFactory: urlFactory, @@ -51,7 +52,6 @@ final class RelayClientEndToEndTests: XCTestCase { logger: logger ) let keychain = KeychainStorageMock() - let keyValueStorage = RuntimeKeyValueStorage() let relayClient = RelayClientFactory.create( relayHost: InputConfig.relayHost, projectId: InputConfig.projectId, From 3abf31f2587f1b94f407dbbac276d8756845fada Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Tue, 3 Oct 2023 20:23:53 +0800 Subject: [PATCH 08/49] Migration tests --- .../ClientAuth/ClientIdStorage.swift | 8 ++- .../AuthTests/ClientIdStorageTests.swift | 49 +++++++++++++++++-- 2 files changed, 53 insertions(+), 4 deletions(-) diff --git a/Sources/WalletConnectRelay/ClientAuth/ClientIdStorage.swift b/Sources/WalletConnectRelay/ClientAuth/ClientIdStorage.swift index 04c82c68e..272451b96 100644 --- a/Sources/WalletConnectRelay/ClientAuth/ClientIdStorage.swift +++ b/Sources/WalletConnectRelay/ClientAuth/ClientIdStorage.swift @@ -35,6 +35,7 @@ public struct ClientIdStorage: ClientIdStoring { public func getClientId() throws -> String { let pubKey = try getPublicPart() + let _ = try getPrivatePart(for: pubKey) return DIDKey(rawData: pubKey.rawRepresentation).did(variant: .ED25519) } } @@ -43,6 +44,7 @@ private extension ClientIdStorage { enum Errors: Error { case publicPartNotFound + case privatePartNotFound } func migrateIfNeeded() { @@ -72,7 +74,11 @@ private extension ClientIdStorage { } func getPrivatePart(for publicPart: SigningPublicKey) throws -> SigningPrivateKey { - return try keychain.read(key: publicPart.storageId) + do { + return try keychain.read(key: publicPart.storageId) + } catch { + throw Errors.privatePartNotFound + } } func setPrivatePart(_ newValue: SigningPrivateKey) throws { diff --git a/Tests/RelayerTests/AuthTests/ClientIdStorageTests.swift b/Tests/RelayerTests/AuthTests/ClientIdStorageTests.swift index cceea9242..45b147637 100644 --- a/Tests/RelayerTests/AuthTests/ClientIdStorageTests.swift +++ b/Tests/RelayerTests/AuthTests/ClientIdStorageTests.swift @@ -8,17 +8,20 @@ final class ClientIdStorageTests: XCTestCase { var sut: ClientIdStorage! var keychain: KeychainStorageMock! + var defaults: RuntimeKeyValueStorage! override func setUp() { keychain = KeychainStorageMock() - sut = ClientIdStorage(defaults: RuntimeKeyValueStorage(), keychain: keychain, logger: ConsoleLoggerMock()) + defaults = RuntimeKeyValueStorage() + sut = ClientIdStorage(defaults: defaults, keychain: keychain, logger: ConsoleLoggerMock()) } func testGetOrCreate() throws { XCTAssertThrowsError(try keychain.read(key: "com.walletconnect.iridium.client_id") as SigningPrivateKey) let saved = try sut.getOrCreateKeyPair() - XCTAssertEqual(saved, try keychain.read(key: "com.walletconnect.iridium.client_id")) + let storageId = saved.publicKey.rawRepresentation.sha256().toHexString() + XCTAssertEqual(saved, try keychain.read(key: storageId)) let restored = try sut.getOrCreateKeyPair() XCTAssertEqual(saved, restored) @@ -27,11 +30,51 @@ final class ClientIdStorageTests: XCTestCase { func testGetClientId() throws { let didKey = try DIDKey(did: "did:key:z6MkodHZwneVRShtaLf8JKYkxpDGp1vGZnpGmdBpX8M2exxH") + /// Initial state + XCTAssertThrowsError(try sut.getClientId()) + let privateKey = try SigningPrivateKey(rawRepresentation: didKey.rawData) - try keychain.add(privateKey, forKey: "com.walletconnect.iridium.client_id") + + defaults.set(privateKey.publicKey.rawRepresentation, forKey: "com.walletconnect.iridium.client_id.public") + + /// Private part not found + XCTAssertThrowsError(try sut.getClientId()) + + let storageId = privateKey.publicKey.rawRepresentation.sha256().toHexString() + try keychain.add(privateKey, forKey: storageId) let clientId = try sut.getClientId() let didPublicKey = DIDKey(rawData: privateKey.publicKey.rawRepresentation) + XCTAssertEqual(clientId, didPublicKey.did(variant: .ED25519)) } + + func testMigration() throws { + let defaults = RuntimeKeyValueStorage() + let keychain = KeychainStorageMock() + let clientId = SigningPrivateKey() + + try keychain.add(clientId, forKey: "com.walletconnect.iridium.client_id") + + // Migration on init + let clientIdStorage = ClientIdStorage(defaults: defaults, keychain: keychain, logger: ConsoleLoggerMock()) + + let publicPartData = defaults.data(forKey: "com.walletconnect.iridium.client_id.public")! + let publicPart = try SigningPublicKey(rawRepresentation: publicPartData) + + let privatePartStorageId = publicPart.rawRepresentation.sha256().toHexString() + let privatePart: SigningPrivateKey = try keychain.read(key: privatePartStorageId) + + XCTAssertEqual(publicPart, clientId.publicKey) + XCTAssertEqual(privatePart, clientId) + + let oldClientId: SigningPrivateKey? = try? keychain.read(key: "com.walletconnect.iridium.client_id") + XCTAssertNil(oldClientId) + + let restoredPrivatePart = try clientIdStorage.getOrCreateKeyPair() + XCTAssertEqual(restoredPrivatePart, clientId) + + let restoredPublicPart = try clientIdStorage.getClientId() + XCTAssertEqual(restoredPublicPart, DIDKey(rawData: clientId.publicKey.rawRepresentation).did(variant: .ED25519)) + } } From c04d92b3c9ca50e4a46621dd8fcf6dc42e881b34 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Tue, 3 Oct 2023 20:32:22 +0800 Subject: [PATCH 09/49] JWT tests fixed --- Tests/RelayerTests/AuthTests/JWTTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/RelayerTests/AuthTests/JWTTests.swift b/Tests/RelayerTests/AuthTests/JWTTests.swift index 1d07ff0c7..b0b044de1 100644 --- a/Tests/RelayerTests/AuthTests/JWTTests.swift +++ b/Tests/RelayerTests/AuthTests/JWTTests.swift @@ -4,7 +4,7 @@ import XCTest @testable import WalletConnectJWT final class JWTTests: XCTestCase { - let expectedJWT = "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2NTY5MTAwOTcsImV4cCI6MTY1Njk5NjQ5NywiaXNzIjoiZGlkOmtleTp6Nk1rb2RIWnduZVZSU2h0YUxmOEpLWWt4cERHcDF2R1pucEdtZEJwWDhNMmV4eEgiLCJzdWIiOiJjNDc5ZmU1ZGM0NjRlNzcxZTc4YjE5M2QyMzlhNjViNThkMjc4Y2FkMWMzNGJmYjBiNTcxNmU1YmI1MTQ5MjhlIiwiYXVkIjoid3NzOi8vcmVsYXkud2FsbGV0Y29ubmVjdC5jb20ifQ.0JkxOM-FV21U7Hk-xycargj_qNRaYV2H5HYtE4GzAeVQYiKWj7YySY5AdSqtCgGzX4Gt98XWXn2kSr9rE1qvCA" + let expectedJWT = "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJjNDc5ZmU1ZGM0NjRlNzcxZTc4YjE5M2QyMzlhNjViNThkMjc4Y2FkMWMzNGJmYjBiNTcxNmU1YmI1MTQ5MjhlIiwiYXVkIjoid3NzOi8vcmVsYXkud2FsbGV0Y29ubmVjdC5jb20iLCJpYXQiOjE2NTY5MTAwOTcsImlzcyI6ImRpZDprZXk6ejZNa29kSFp3bmVWUlNodGFMZjhKS1lreHBER3AxdkdabnBHbWRCcFg4TTJleHhIIiwiZXhwIjoxNjU2OTk2NDk3fQ.0JkxOM-FV21U7Hk-xycargj_qNRaYV2H5HYtE4GzAeVQYiKWj7YySY5AdSqtCgGzX4Gt98XWXn2kSr9rE1qvCA" func testJWTEncoding() throws { let signer = EdDSASignerMock() From 2964768a067752e39450997fdca0925dd7881176 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Wed, 4 Oct 2023 20:11:08 +0800 Subject: [PATCH 10/49] History integration tests disabled --- Example/ExampleApp.xcodeproj/IntegrationTests.xctestplan | 1 + 1 file changed, 1 insertion(+) diff --git a/Example/ExampleApp.xcodeproj/IntegrationTests.xctestplan b/Example/ExampleApp.xcodeproj/IntegrationTests.xctestplan index 896a5c3c7..e3aa93b52 100644 --- a/Example/ExampleApp.xcodeproj/IntegrationTests.xctestplan +++ b/Example/ExampleApp.xcodeproj/IntegrationTests.xctestplan @@ -53,6 +53,7 @@ "AuthTests\/testEIP1271RespondSuccess()", "ChatTests", "ENSResolverTests", + "HistoryTests", "SyncDerivationServiceTests", "SyncTests" ], From 6812ee774a765127f562b76795e497b79ac3135d Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Wed, 4 Oct 2023 20:34:27 +0800 Subject: [PATCH 11/49] JWT tests fixed --- Sources/WalletConnectJWT/JSONEncoder+JWT.swift | 11 +++++++++++ Sources/WalletConnectJWT/JWT.swift | 6 +++--- Sources/WalletConnectJWT/JWTEncodable.swift | 7 ++----- Tests/RelayerTests/AuthTests/EdDSASignerTests.swift | 4 ++-- Tests/RelayerTests/AuthTests/JWTTests.swift | 7 +++++-- 5 files changed, 23 insertions(+), 12 deletions(-) create mode 100644 Sources/WalletConnectJWT/JSONEncoder+JWT.swift diff --git a/Sources/WalletConnectJWT/JSONEncoder+JWT.swift b/Sources/WalletConnectJWT/JSONEncoder+JWT.swift new file mode 100644 index 000000000..7f73ad98c --- /dev/null +++ b/Sources/WalletConnectJWT/JSONEncoder+JWT.swift @@ -0,0 +1,11 @@ +import Foundation + +extension JSONEncoder { + + public static var jwt: JSONEncoder { + let jsonEncoder = JSONEncoder() + jsonEncoder.outputFormatting = .withoutEscapingSlashes + jsonEncoder.dateEncodingStrategy = .secondsSince1970 + return jsonEncoder + } +} diff --git a/Sources/WalletConnectJWT/JWT.swift b/Sources/WalletConnectJWT/JWT.swift index 5e3259440..d37728840 100644 --- a/Sources/WalletConnectJWT/JWT.swift +++ b/Sources/WalletConnectJWT/JWT.swift @@ -7,12 +7,12 @@ struct JWT: Codable, Equatable { let signature: String let string: String - init(claims: JWTClaims, signer: JWTSigning) throws { + init(claims: JWTClaims, signer: JWTSigning, jsonEncoder: JSONEncoder = .jwt) throws { self.header = JWTHeader(alg: signer.alg) self.claims = claims - let headerString = try header.encode() - let claimsString = try claims.encode() + let headerString = try header.encode(jsonEncoder: jsonEncoder) + let claimsString = try claims.encode(jsonEncoder: jsonEncoder) let signature = try signer.sign(header: headerString, claims: claimsString) self.signature = signature diff --git a/Sources/WalletConnectJWT/JWTEncodable.swift b/Sources/WalletConnectJWT/JWTEncodable.swift index 04505fdb5..e612e47f8 100644 --- a/Sources/WalletConnectJWT/JWTEncodable.swift +++ b/Sources/WalletConnectJWT/JWTEncodable.swift @@ -1,17 +1,14 @@ import Foundation public protocol JWTEncodable: Codable, Equatable { - func encode() throws -> String + func encode(jsonEncoder: JSONEncoder) throws -> String static func decode(from string: String) throws -> Self } extension JWTEncodable { - public func encode() throws -> String { - let jsonEncoder = JSONEncoder() - jsonEncoder.outputFormatting = .withoutEscapingSlashes - jsonEncoder.dateEncodingStrategy = .secondsSince1970 + public func encode(jsonEncoder: JSONEncoder) throws -> String { let data = try jsonEncoder.encode(self) return JWTEncoder.base64urlEncodedString(data: data) } diff --git a/Tests/RelayerTests/AuthTests/EdDSASignerTests.swift b/Tests/RelayerTests/AuthTests/EdDSASignerTests.swift index 87d5ae4c3..cbd8d2878 100644 --- a/Tests/RelayerTests/AuthTests/EdDSASignerTests.swift +++ b/Tests/RelayerTests/AuthTests/EdDSASignerTests.swift @@ -11,8 +11,8 @@ final class EdDSASignerTests: XCTestCase { let keyRaw = Data(hex: "58e0254c211b858ef7896b00e3f36beeb13d568d47c6031c4218b87718061295") let signingKey = try! SigningPrivateKey(rawRepresentation: keyRaw) sut = EdDSASigner(signingKey) - let header = try! JWTHeader(alg: "EdDSA").encode() - let claims = try! RelayAuthPayload.Claims.stub().encode() + let header = try! JWTHeader(alg: "EdDSA").encode(jsonEncoder: .jwt) + let claims = try! RelayAuthPayload.Claims.stub().encode(jsonEncoder: .jwt) let signature = try! sut.sign(header: header, claims: claims) XCTAssertNotNil(signature) } diff --git a/Tests/RelayerTests/AuthTests/JWTTests.swift b/Tests/RelayerTests/AuthTests/JWTTests.swift index b0b044de1..9e662ff72 100644 --- a/Tests/RelayerTests/AuthTests/JWTTests.swift +++ b/Tests/RelayerTests/AuthTests/JWTTests.swift @@ -4,12 +4,15 @@ import XCTest @testable import WalletConnectJWT final class JWTTests: XCTestCase { - let expectedJWT = "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJjNDc5ZmU1ZGM0NjRlNzcxZTc4YjE5M2QyMzlhNjViNThkMjc4Y2FkMWMzNGJmYjBiNTcxNmU1YmI1MTQ5MjhlIiwiYXVkIjoid3NzOi8vcmVsYXkud2FsbGV0Y29ubmVjdC5jb20iLCJpYXQiOjE2NTY5MTAwOTcsImlzcyI6ImRpZDprZXk6ejZNa29kSFp3bmVWUlNodGFMZjhKS1lreHBER3AxdkdabnBHbWRCcFg4TTJleHhIIiwiZXhwIjoxNjU2OTk2NDk3fQ.0JkxOM-FV21U7Hk-xycargj_qNRaYV2H5HYtE4GzAeVQYiKWj7YySY5AdSqtCgGzX4Gt98XWXn2kSr9rE1qvCA" + let expectedJWT = "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJ3c3M6Ly9yZWxheS53YWxsZXRjb25uZWN0LmNvbSIsImV4cCI6MTY1Njk5NjQ5NywiaWF0IjoxNjU2OTEwMDk3LCJpc3MiOiJkaWQ6a2V5Ono2TWtvZEhad25lVlJTaHRhTGY4SktZa3hwREdwMXZHWm5wR21kQnBYOE0yZXh4SCIsInN1YiI6ImM0NzlmZTVkYzQ2NGU3NzFlNzhiMTkzZDIzOWE2NWI1OGQyNzhjYWQxYzM0YmZiMGI1NzE2ZTViYjUxNDkyOGUifQ.0JkxOM-FV21U7Hk-xycargj_qNRaYV2H5HYtE4GzAeVQYiKWj7YySY5AdSqtCgGzX4Gt98XWXn2kSr9rE1qvCA" func testJWTEncoding() throws { let signer = EdDSASignerMock() signer.signature = "0JkxOM-FV21U7Hk-xycargj_qNRaYV2H5HYtE4GzAeVQYiKWj7YySY5AdSqtCgGzX4Gt98XWXn2kSr9rE1qvCA" - let jwt = try JWT(claims: RelayAuthPayload.Claims.stub(), signer: signer) + let jsonEncoder = JSONEncoder() + jsonEncoder.outputFormatting = [.sortedKeys, .withoutEscapingSlashes] + jsonEncoder.dateEncodingStrategy = .secondsSince1970 + let jwt = try JWT(claims: RelayAuthPayload.Claims.stub(), signer: signer, jsonEncoder: jsonEncoder) XCTAssertEqual(expectedJWT, jwt.string) } From 3ef85df1ef69286184ec390dd7ff454ffd0328bf Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Wed, 4 Oct 2023 20:48:19 +0800 Subject: [PATCH 12/49] Double encoding bug workaround --- Sources/Commons/AnyCodable.swift | 2 +- Tests/CommonsTests/AnyCodableTests.swift | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Sources/Commons/AnyCodable.swift b/Sources/Commons/AnyCodable.swift index 895c7cfc2..8623d3e34 100644 --- a/Sources/Commons/AnyCodable.swift +++ b/Sources/Commons/AnyCodable.swift @@ -164,7 +164,7 @@ extension AnyCodable: Decodable, Encodable { if let intVal = try? container.decode(Int.self) { value = intVal } else if let doubleVal = try? container.decode(Double.self) { - value = doubleVal + value = Decimal(doubleVal) } else if let boolVal = try? container.decode(Bool.self) { value = boolVal } else if let stringVal = try? container.decode(String.self) { diff --git a/Tests/CommonsTests/AnyCodableTests.swift b/Tests/CommonsTests/AnyCodableTests.swift index bce2de631..9fe96b88d 100644 --- a/Tests/CommonsTests/AnyCodableTests.swift +++ b/Tests/CommonsTests/AnyCodableTests.swift @@ -28,7 +28,7 @@ private struct SampleStruct: Codable, Equatable { SampleStruct( bool: true, int: 1337, - double: 13.37, + double: 13, string: "verystringwow", object: SubObject( string: "0xdeadbeef" @@ -40,7 +40,7 @@ private struct SampleStruct: Codable, Equatable { { "bool": true, "int": 1337, - "double": 13.37, + "double": 13, "string": "verystringwow", "object": { "string": "0xdeadbeef" @@ -52,7 +52,7 @@ private struct SampleStruct: Codable, Equatable { { "bool": ****, "int": 1337, - "double": 13.37, + "double": 13, "string": "verystringwow", } """.data(using: .utf8)! From 4fd6d9b3f0c4ca957261316211a8dbaac95cbba7 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Mon, 9 Oct 2023 14:03:49 +0800 Subject: [PATCH 13/49] Revert AnyCodable parser changes --- Sources/Commons/AnyCodable.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Commons/AnyCodable.swift b/Sources/Commons/AnyCodable.swift index 8623d3e34..895c7cfc2 100644 --- a/Sources/Commons/AnyCodable.swift +++ b/Sources/Commons/AnyCodable.swift @@ -164,7 +164,7 @@ extension AnyCodable: Decodable, Encodable { if let intVal = try? container.decode(Int.self) { value = intVal } else if let doubleVal = try? container.decode(Double.self) { - value = Decimal(doubleVal) + value = doubleVal } else if let boolVal = try? container.decode(Bool.self) { value = boolVal } else if let stringVal = try? container.decode(String.self) { From 8dbdc359d8b47244341e8a21f328ea1d39a71d20 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Mon, 9 Oct 2023 14:25:25 +0800 Subject: [PATCH 14/49] Messages badge --- .../Models/SubscriptionsViewModel.swift | 14 ++++++++++++++ .../Notifications/NotificationsInteractor.swift | 4 ++-- .../Notifications/NotificationsPresenter.swift | 6 ++---- .../Wallet/Notifications/NotificationsView.swift | 12 ++++++++++++ .../NotifyPreferencesPresenter.swift | 2 +- 5 files changed, 31 insertions(+), 7 deletions(-) diff --git a/Example/WalletApp/PresentationLayer/Wallet/Notifications/Models/SubscriptionsViewModel.swift b/Example/WalletApp/PresentationLayer/Wallet/Notifications/Models/SubscriptionsViewModel.swift index 366ce4409..6e0f12f42 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/Notifications/Models/SubscriptionsViewModel.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/Notifications/Models/SubscriptionsViewModel.swift @@ -5,6 +5,12 @@ typealias SubscriptionScope = [String: ScopeValue] struct SubscriptionsViewModel: Identifiable { let subscription: NotifySubscription + let messages: [NotifyMessageRecord]? + + init(subscription: NotifySubscription, messages: [NotifyMessageRecord]? = nil) { + self.subscription = subscription + self.messages = messages + } var id: String { return subscription.topic @@ -33,4 +39,12 @@ struct SubscriptionsViewModel: Identifiable { var scope: SubscriptionScope { return subscription.scope } + + var messagesCount: Int { + return messages?.count ?? 0 + } + + var hasMessage: Bool { + return messagesCount != 0 + } } diff --git a/Example/WalletApp/PresentationLayer/Wallet/Notifications/NotificationsInteractor.swift b/Example/WalletApp/PresentationLayer/Wallet/Notifications/NotificationsInteractor.swift index eacede968..ae29a80b5 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/Notifications/NotificationsInteractor.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/Notifications/NotificationsInteractor.swift @@ -39,7 +39,7 @@ final class NotificationsInteractor { try await Notify.instance.deleteSubscription(topic: topic) } - func messagesCount(subscription: NotifySubscription) -> Int { - return Notify.instance.getMessageHistory(topic: subscription.topic).count + func messages(for subscription: NotifySubscription) -> [NotifyMessageRecord] { + return Notify.instance.getMessageHistory(topic: subscription.topic) } } diff --git a/Example/WalletApp/PresentationLayer/Wallet/Notifications/NotificationsPresenter.swift b/Example/WalletApp/PresentationLayer/Wallet/Notifications/NotificationsPresenter.swift index ecb906cc4..c14bf62b9 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/Notifications/NotificationsPresenter.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/Notifications/NotificationsPresenter.swift @@ -13,10 +13,8 @@ final class NotificationsPresenter: ObservableObject { var subscriptionViewModels: [SubscriptionsViewModel] { return subscriptions - .map { SubscriptionsViewModel(subscription: $0) } - .sorted { lhs, rhs in - return interactor.messagesCount(subscription: lhs.subscription) > interactor.messagesCount(subscription: rhs.subscription) - } + .map { SubscriptionsViewModel(subscription: $0, messages: interactor.messages(for: $0)) } + .sorted { $0.messagesCount > $1.messagesCount } } var listingViewModels: [ListingViewModel] { diff --git a/Example/WalletApp/PresentationLayer/Wallet/Notifications/NotificationsView.swift b/Example/WalletApp/PresentationLayer/Wallet/Notifications/NotificationsView.swift index a4e250e60..4ee4fd8c2 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/Notifications/NotificationsView.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/Notifications/NotificationsView.swift @@ -124,6 +124,18 @@ struct NotificationsView: View { .foregroundColor(.Foreground150) .font(.system(size: 14, weight: .regular, design: .rounded)) } + + Spacer() + + if subscription.hasMessage { + Text(String(subscription.messagesCount)) + .foregroundColor(.Inverse100) + .font(.system(size: 13, weight: .medium)) + .frame(width: 20, height: 20) + .background { + Circle().foregroundColor(.blue100) + } + } } } } diff --git a/Example/WalletApp/PresentationLayer/Wallet/NotifySettings/NotifyPreferencesPresenter.swift b/Example/WalletApp/PresentationLayer/Wallet/NotifySettings/NotifyPreferencesPresenter.swift index c811c4314..d2689effe 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/NotifySettings/NotifyPreferencesPresenter.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/NotifySettings/NotifyPreferencesPresenter.swift @@ -10,7 +10,7 @@ final class NotifyPreferencesPresenter: ObservableObject { private var disposeBag = Set() var subscriptionViewModel: SubscriptionsViewModel { - return SubscriptionsViewModel(subscription: subscription) + return SubscriptionsViewModel(subscription: subscription, messages: []) } var preferences: [String] { From 76733889a962047d0cbb93c284ca3dcc8c74c3fe Mon Sep 17 00:00:00 2001 From: Alexander Lisovik Date: Mon, 9 Oct 2023 11:32:48 +0400 Subject: [PATCH 15/49] Remove verify host --- Sources/WalletConnectVerify/OriginVerifier.swift | 10 +++------- Sources/WalletConnectVerify/VerifyClient.swift | 15 ++++----------- .../WalletConnectVerify/VerifyClientFactory.swift | 5 ++--- Sources/WalletConnectVerify/VerifyContext.swift | 4 +--- 4 files changed, 10 insertions(+), 24 deletions(-) diff --git a/Sources/WalletConnectVerify/OriginVerifier.swift b/Sources/WalletConnectVerify/OriginVerifier.swift index 10b403f81..0689088d2 100644 --- a/Sources/WalletConnectVerify/OriginVerifier.swift +++ b/Sources/WalletConnectVerify/OriginVerifier.swift @@ -5,15 +5,11 @@ public final class OriginVerifier { case registrationFailed } - private var verifyHost: String + private var verifyHost = "verify.walletconnect.com" /// The property is used to determine whether verify.walletconnect.org will be used /// in case verify.walletconnect.com doesn't respond for some reason (most likely due to being blocked in the user's location). private var fallback = false - - init(verifyHost: String) { - self.verifyHost = verifyHost - } - + func verifyOrigin(assertionId: String) async throws -> VerifyResponse { let sessionConfiguration = URLSessionConfiguration.default sessionConfiguration.timeoutIntervalForRequest = 5.0 @@ -27,7 +23,7 @@ public final class OriginVerifier { VerifyResponse.self, at: VerifyAPI.resolve(assertionId: assertionId) ) - guard let origin = response.origin else { + guard let _ = response.origin else { throw Errors.registrationFailed } return response diff --git a/Sources/WalletConnectVerify/VerifyClient.swift b/Sources/WalletConnectVerify/VerifyClient.swift index 37b74dfea..e528f2b60 100644 --- a/Sources/WalletConnectVerify/VerifyClient.swift +++ b/Sources/WalletConnectVerify/VerifyClient.swift @@ -14,16 +14,12 @@ public actor VerifyClient: VerifyClientProtocol { let originVerifier: OriginVerifier let assertionRegistrer: AssertionRegistrer let appAttestationRegistrer: AppAttestationRegistrer - - private let verifyHost: String init( - verifyHost: String, originVerifier: OriginVerifier, assertionRegistrer: AssertionRegistrer, appAttestationRegistrer: AppAttestationRegistrer ) { - self.verifyHost = verifyHost self.originVerifier = originVerifier self.assertionRegistrer = assertionRegistrer self.appAttestationRegistrer = appAttestationRegistrer @@ -41,21 +37,18 @@ public actor VerifyClient: VerifyClientProtocol { guard isScam == nil else { return VerifyContext( origin: origin, - validation: .scam, - verifyUrl: verifyHost + validation: .scam ) } if let origin, let originUrl = URL(string: origin), let domainUrl = URL(string: domain) { return VerifyContext( origin: origin, - validation: (originUrl.host == domainUrl.host) ? .valid : .invalid, - verifyUrl: verifyHost + validation: (originUrl.host == domainUrl.host) ? .valid : .invalid ) } else { return VerifyContext( origin: origin, - validation: .unknown, - verifyUrl: verifyHost + validation: .unknown ) } } @@ -75,7 +68,7 @@ public struct VerifyClientMock: VerifyClientProtocol { } public func createVerifyContext(origin: String?, domain: String, isScam: Bool?) -> VerifyContext { - return VerifyContext(origin: "domain.com", validation: .valid, verifyUrl: "verify.walletconnect.com") + return VerifyContext(origin: "domain.com", validation: .valid) } } diff --git a/Sources/WalletConnectVerify/VerifyClientFactory.swift b/Sources/WalletConnectVerify/VerifyClientFactory.swift index 1d00a6af1..8b230fc5f 100644 --- a/Sources/WalletConnectVerify/VerifyClientFactory.swift +++ b/Sources/WalletConnectVerify/VerifyClientFactory.swift @@ -1,8 +1,8 @@ import Foundation public class VerifyClientFactory { - public static func create(verifyHost: String = "verify.walletconnect.com") -> VerifyClient { - let originVerifier = OriginVerifier(verifyHost: verifyHost) + public static func create() -> VerifyClient { + let originVerifier = OriginVerifier() let assertionRegistrer = AssertionRegistrer() let logger = ConsoleLogger(loggingLevel: .off) let keyValueStorage = UserDefaults.standard @@ -18,7 +18,6 @@ public class VerifyClientFactory { keyAttestationService: keyAttestationService ) return VerifyClient( - verifyHost: verifyHost, originVerifier: originVerifier, assertionRegistrer: assertionRegistrer, appAttestationRegistrer: appAttestationRegistrer diff --git a/Sources/WalletConnectVerify/VerifyContext.swift b/Sources/WalletConnectVerify/VerifyContext.swift index 85382e090..e85613493 100644 --- a/Sources/WalletConnectVerify/VerifyContext.swift +++ b/Sources/WalletConnectVerify/VerifyContext.swift @@ -8,11 +8,9 @@ public struct VerifyContext: Equatable, Hashable, Codable { public let origin: String? public let validation: ValidationStatus - public let verifyUrl: String - public init(origin: String?, validation: ValidationStatus, verifyUrl: String) { + public init(origin: String?, validation: ValidationStatus) { self.origin = origin self.validation = validation - self.verifyUrl = verifyUrl } } From 0b1f67929f66248d9c444538a32a4eb68b3d1474 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Tue, 10 Oct 2023 23:03:36 +0800 Subject: [PATCH 16/49] isLimited removed --- .../Client/Wallet/NotifyClient.swift | 4 ++-- .../Client/Wallet/NotifyIdentityService.swift | 14 ++++---------- .../NotifyClientProxy/NotifyClientProxy.swift | 3 +-- 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift index 47ffbceec..ab3c48d3f 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift @@ -69,8 +69,8 @@ public class NotifyClient { self.subscriptionWatcher = subscriptionWatcher } - public func register(account: Account, domain: String, isLimited: Bool = false, onSign: @escaping SigningCallback) async throws { - try await identityService.register(account: account, domain: domain, isLimited: isLimited, onSign: onSign) + public func register(account: Account, domain: String, onSign: @escaping SigningCallback) async throws { + try await identityService.register(account: account, domain: domain, onSign: onSign) subscriptionWatcher.setAccount(account) } diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifyIdentityService.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifyIdentityService.swift index fc8091317..cdd5a91b9 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/NotifyIdentityService.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifyIdentityService.swift @@ -12,11 +12,10 @@ final class NotifyIdentityService { self.logger = logger } - public func register(account: Account, domain: String, isLimited: Bool, onSign: @escaping SigningCallback) async throws { - let statement = makeStatement(isLimited: isLimited, domain: domain, keyserverHost: keyserverURL.host!) + public func register(account: Account, domain: String, onSign: @escaping SigningCallback) async throws { let pubKey = try await identityClient.register(account: account, domain: domain, - statement: statement, + statement: makeStatement(), resources: [keyserverURL.absoluteString], onSign: onSign) logger.debug("Did register an account: \(account)") @@ -29,12 +28,7 @@ final class NotifyIdentityService { private extension NotifyIdentityService { - func makeStatement(isLimited: Bool, domain: String, keyserverHost: String) -> String { - switch isLimited { - case true: - return "I further authorize this DAPP to send and receive messages on my behalf for this domain and manage my identity at \(keyserverHost)." - case false: - return "I further authorize this WALLET to send and receive messages on my behalf for ALL domains and manage my identity at \(keyserverHost)." - } + func makeStatement() -> String { + return "I further authorize this app to send and receive messages on my behalf using my WalletConnect identity. Read more at https://walletconnect.com/identity" } } diff --git a/Sources/Web3Inbox/NotifyClientProxy/NotifyClientProxy.swift b/Sources/Web3Inbox/NotifyClientProxy/NotifyClientProxy.swift index 427ba04e0..8d8f5b5a0 100644 --- a/Sources/Web3Inbox/NotifyClientProxy/NotifyClientProxy.swift +++ b/Sources/Web3Inbox/NotifyClientProxy/NotifyClientProxy.swift @@ -44,7 +44,7 @@ final class NotifyClientProxy { try await respond(request: request) case .register: let params = try parse(RegisterRequest.self, params: request.params) - try await client.register(account: params.account, domain: params.domain, isLimited: params.isLimited, onSign: onSign) + try await client.register(account: params.account, domain: params.domain, onSign: onSign) try await respond(request: request) } } @@ -92,7 +92,6 @@ private extension NotifyClientProxy { struct RegisterRequest: Codable { let account: Account let domain: String - let isLimited: Bool } func parse(_ type: Request.Type, params: AnyCodable?) throws -> Request { From 0f185200133e8a782592b71cf8608d15f04c044c Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Tue, 10 Oct 2023 23:16:16 +0800 Subject: [PATCH 17/49] Force null app encoding --- .../NotifyWatchSubscriptionsPayload.swift | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/Sources/WalletConnectNotify/Types/JWTPayloads/notify_watch_subscriptions/NotifyWatchSubscriptionsPayload.swift b/Sources/WalletConnectNotify/Types/JWTPayloads/notify_watch_subscriptions/NotifyWatchSubscriptionsPayload.swift index fd92b9c79..0fad6272b 100644 --- a/Sources/WalletConnectNotify/Types/JWTPayloads/notify_watch_subscriptions/NotifyWatchSubscriptionsPayload.swift +++ b/Sources/WalletConnectNotify/Types/JWTPayloads/notify_watch_subscriptions/NotifyWatchSubscriptionsPayload.swift @@ -17,10 +17,30 @@ struct NotifyWatchSubscriptionsPayload: JWTClaimsCodable { let aud: String /// Blockchain account that notify subscription has been proposed for -`did:pkh` let sub: String + /// Dapp domain url + let app: String? static var action: String? { return "notify_watch_subscriptions" } + + // Note: - Overriding `encode(to encoder: Encoder)` implementation to force null app encoding + + enum CodingKeys: CodingKey { + case iat, exp, ksu, act, iss, aud, sub, app + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: NotifyWatchSubscriptionsPayload.Claims.CodingKeys.self) + try container.encode(self.iat, forKey: NotifyWatchSubscriptionsPayload.Claims.CodingKeys.iat) + try container.encode(self.exp, forKey: NotifyWatchSubscriptionsPayload.Claims.CodingKeys.exp) + try container.encode(self.ksu, forKey: NotifyWatchSubscriptionsPayload.Claims.CodingKeys.ksu) + try container.encodeIfPresent(self.act, forKey: NotifyWatchSubscriptionsPayload.Claims.CodingKeys.act) + try container.encode(self.iss, forKey: NotifyWatchSubscriptionsPayload.Claims.CodingKeys.iss) + try container.encode(self.aud, forKey: NotifyWatchSubscriptionsPayload.Claims.CodingKeys.aud) + try container.encode(self.sub, forKey: NotifyWatchSubscriptionsPayload.Claims.CodingKeys.sub) + try container.encode(self.app, forKey: NotifyWatchSubscriptionsPayload.Claims.CodingKeys.app) + } } struct Wrapper: JWTWrapper { @@ -59,7 +79,8 @@ struct NotifyWatchSubscriptionsPayload: JWTClaimsCodable { act: Claims.action, iss: iss, aud: notifyServerIdentityKey.did(variant: .ED25519), - sub: subscriptionAccount.did + sub: subscriptionAccount.did, + app: nil ) } From 2a2e1007b3bb4a37b51f04855336b1c12988e5b4 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Tue, 10 Oct 2023 23:43:07 +0800 Subject: [PATCH 18/49] Messages badge layout fixed --- .../Notifications/NotificationsView.swift | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/Example/WalletApp/PresentationLayer/Wallet/Notifications/NotificationsView.swift b/Example/WalletApp/PresentationLayer/Wallet/Notifications/NotificationsView.swift index 4ee4fd8c2..73151545c 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/Notifications/NotificationsView.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/Notifications/NotificationsView.swift @@ -126,15 +126,17 @@ struct NotificationsView: View { } Spacer() - + if subscription.hasMessage { - Text(String(subscription.messagesCount)) - .foregroundColor(.Inverse100) - .font(.system(size: 13, weight: .medium)) - .frame(width: 20, height: 20) - .background { - Circle().foregroundColor(.blue100) - } + VStack{ + Text(String(subscription.messagesCount)) + .foregroundColor(.Inverse100) + .font(.system(size: 13, weight: .medium).monospacedDigit()) + .padding(.horizontal, 8) + .padding(.vertical, 4) + }.background { + Capsule().foregroundColor(.blue100) + } } } } From 50b99f7f44bc5fe36f4c762f05dba1ddaec798da Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 12 Oct 2023 18:41:54 +0800 Subject: [PATCH 19/49] Revert "Merge pull request #1172 from WalletConnect/feature/siwe-spec-change" This reverts commit c29b11eff058c80146fb750c2b63b0a42082675c, reversing changes made to 6cb24056f085c092f956721425b08193207c7bec. --- .../Client/Wallet/NotifyClient.swift | 4 ++-- .../Client/Wallet/NotifyIdentityService.swift | 14 +++++++---- .../NotifyWatchSubscriptionsPayload.swift | 23 +------------------ .../NotifyClientProxy/NotifyClientProxy.swift | 3 ++- 4 files changed, 15 insertions(+), 29 deletions(-) diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift index ab3c48d3f..47ffbceec 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift @@ -69,8 +69,8 @@ public class NotifyClient { self.subscriptionWatcher = subscriptionWatcher } - public func register(account: Account, domain: String, onSign: @escaping SigningCallback) async throws { - try await identityService.register(account: account, domain: domain, onSign: onSign) + public func register(account: Account, domain: String, isLimited: Bool = false, onSign: @escaping SigningCallback) async throws { + try await identityService.register(account: account, domain: domain, isLimited: isLimited, onSign: onSign) subscriptionWatcher.setAccount(account) } diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifyIdentityService.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifyIdentityService.swift index cdd5a91b9..fc8091317 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/NotifyIdentityService.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifyIdentityService.swift @@ -12,10 +12,11 @@ final class NotifyIdentityService { self.logger = logger } - public func register(account: Account, domain: String, onSign: @escaping SigningCallback) async throws { + public func register(account: Account, domain: String, isLimited: Bool, onSign: @escaping SigningCallback) async throws { + let statement = makeStatement(isLimited: isLimited, domain: domain, keyserverHost: keyserverURL.host!) let pubKey = try await identityClient.register(account: account, domain: domain, - statement: makeStatement(), + statement: statement, resources: [keyserverURL.absoluteString], onSign: onSign) logger.debug("Did register an account: \(account)") @@ -28,7 +29,12 @@ final class NotifyIdentityService { private extension NotifyIdentityService { - func makeStatement() -> String { - return "I further authorize this app to send and receive messages on my behalf using my WalletConnect identity. Read more at https://walletconnect.com/identity" + func makeStatement(isLimited: Bool, domain: String, keyserverHost: String) -> String { + switch isLimited { + case true: + return "I further authorize this DAPP to send and receive messages on my behalf for this domain and manage my identity at \(keyserverHost)." + case false: + return "I further authorize this WALLET to send and receive messages on my behalf for ALL domains and manage my identity at \(keyserverHost)." + } } } diff --git a/Sources/WalletConnectNotify/Types/JWTPayloads/notify_watch_subscriptions/NotifyWatchSubscriptionsPayload.swift b/Sources/WalletConnectNotify/Types/JWTPayloads/notify_watch_subscriptions/NotifyWatchSubscriptionsPayload.swift index 0fad6272b..fd92b9c79 100644 --- a/Sources/WalletConnectNotify/Types/JWTPayloads/notify_watch_subscriptions/NotifyWatchSubscriptionsPayload.swift +++ b/Sources/WalletConnectNotify/Types/JWTPayloads/notify_watch_subscriptions/NotifyWatchSubscriptionsPayload.swift @@ -17,30 +17,10 @@ struct NotifyWatchSubscriptionsPayload: JWTClaimsCodable { let aud: String /// Blockchain account that notify subscription has been proposed for -`did:pkh` let sub: String - /// Dapp domain url - let app: String? static var action: String? { return "notify_watch_subscriptions" } - - // Note: - Overriding `encode(to encoder: Encoder)` implementation to force null app encoding - - enum CodingKeys: CodingKey { - case iat, exp, ksu, act, iss, aud, sub, app - } - - func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: NotifyWatchSubscriptionsPayload.Claims.CodingKeys.self) - try container.encode(self.iat, forKey: NotifyWatchSubscriptionsPayload.Claims.CodingKeys.iat) - try container.encode(self.exp, forKey: NotifyWatchSubscriptionsPayload.Claims.CodingKeys.exp) - try container.encode(self.ksu, forKey: NotifyWatchSubscriptionsPayload.Claims.CodingKeys.ksu) - try container.encodeIfPresent(self.act, forKey: NotifyWatchSubscriptionsPayload.Claims.CodingKeys.act) - try container.encode(self.iss, forKey: NotifyWatchSubscriptionsPayload.Claims.CodingKeys.iss) - try container.encode(self.aud, forKey: NotifyWatchSubscriptionsPayload.Claims.CodingKeys.aud) - try container.encode(self.sub, forKey: NotifyWatchSubscriptionsPayload.Claims.CodingKeys.sub) - try container.encode(self.app, forKey: NotifyWatchSubscriptionsPayload.Claims.CodingKeys.app) - } } struct Wrapper: JWTWrapper { @@ -79,8 +59,7 @@ struct NotifyWatchSubscriptionsPayload: JWTClaimsCodable { act: Claims.action, iss: iss, aud: notifyServerIdentityKey.did(variant: .ED25519), - sub: subscriptionAccount.did, - app: nil + sub: subscriptionAccount.did ) } diff --git a/Sources/Web3Inbox/NotifyClientProxy/NotifyClientProxy.swift b/Sources/Web3Inbox/NotifyClientProxy/NotifyClientProxy.swift index 8d8f5b5a0..427ba04e0 100644 --- a/Sources/Web3Inbox/NotifyClientProxy/NotifyClientProxy.swift +++ b/Sources/Web3Inbox/NotifyClientProxy/NotifyClientProxy.swift @@ -44,7 +44,7 @@ final class NotifyClientProxy { try await respond(request: request) case .register: let params = try parse(RegisterRequest.self, params: request.params) - try await client.register(account: params.account, domain: params.domain, onSign: onSign) + try await client.register(account: params.account, domain: params.domain, isLimited: params.isLimited, onSign: onSign) try await respond(request: request) } } @@ -92,6 +92,7 @@ private extension NotifyClientProxy { struct RegisterRequest: Codable { let account: Account let domain: String + let isLimited: Bool } func parse(_ type: Request.Type, params: AnyCodable?) throws -> Request { From 513fa23341e37fd9b906dd7b18ca5a4b65450c35 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 12 Oct 2023 23:11:41 +0800 Subject: [PATCH 20/49] Null app --- .../Client/Wallet/NotifyIdentityService.swift | 8 +++---- .../NotifyWatchSubscriptionsPayload.swift | 23 ++++++++++++++++++- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifyIdentityService.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifyIdentityService.swift index fc8091317..0a467049e 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/NotifyIdentityService.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifyIdentityService.swift @@ -13,7 +13,7 @@ final class NotifyIdentityService { } public func register(account: Account, domain: String, isLimited: Bool, onSign: @escaping SigningCallback) async throws { - let statement = makeStatement(isLimited: isLimited, domain: domain, keyserverHost: keyserverURL.host!) + let statement = makeStatement(isLimited: isLimited) let pubKey = try await identityClient.register(account: account, domain: domain, statement: statement, @@ -29,12 +29,12 @@ final class NotifyIdentityService { private extension NotifyIdentityService { - func makeStatement(isLimited: Bool, domain: String, keyserverHost: String) -> String { + func makeStatement(isLimited: Bool) -> String { switch isLimited { case true: - return "I further authorize this DAPP to send and receive messages on my behalf for this domain and manage my identity at \(keyserverHost)." + return "I further authorize this app to send and receive messages on my behalf for THIS domains using my WalletConnect identity. Read more at https://walletconnect.com/identity" case false: - return "I further authorize this WALLET to send and receive messages on my behalf for ALL domains and manage my identity at \(keyserverHost)." + return "I further authorize this app to send and receive messages on my behalf for ALL domain using my WalletConnect identity. Read more at https://walletconnect.com/identity" } } } diff --git a/Sources/WalletConnectNotify/Types/JWTPayloads/notify_watch_subscriptions/NotifyWatchSubscriptionsPayload.swift b/Sources/WalletConnectNotify/Types/JWTPayloads/notify_watch_subscriptions/NotifyWatchSubscriptionsPayload.swift index fd92b9c79..0fad6272b 100644 --- a/Sources/WalletConnectNotify/Types/JWTPayloads/notify_watch_subscriptions/NotifyWatchSubscriptionsPayload.swift +++ b/Sources/WalletConnectNotify/Types/JWTPayloads/notify_watch_subscriptions/NotifyWatchSubscriptionsPayload.swift @@ -17,10 +17,30 @@ struct NotifyWatchSubscriptionsPayload: JWTClaimsCodable { let aud: String /// Blockchain account that notify subscription has been proposed for -`did:pkh` let sub: String + /// Dapp domain url + let app: String? static var action: String? { return "notify_watch_subscriptions" } + + // Note: - Overriding `encode(to encoder: Encoder)` implementation to force null app encoding + + enum CodingKeys: CodingKey { + case iat, exp, ksu, act, iss, aud, sub, app + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: NotifyWatchSubscriptionsPayload.Claims.CodingKeys.self) + try container.encode(self.iat, forKey: NotifyWatchSubscriptionsPayload.Claims.CodingKeys.iat) + try container.encode(self.exp, forKey: NotifyWatchSubscriptionsPayload.Claims.CodingKeys.exp) + try container.encode(self.ksu, forKey: NotifyWatchSubscriptionsPayload.Claims.CodingKeys.ksu) + try container.encodeIfPresent(self.act, forKey: NotifyWatchSubscriptionsPayload.Claims.CodingKeys.act) + try container.encode(self.iss, forKey: NotifyWatchSubscriptionsPayload.Claims.CodingKeys.iss) + try container.encode(self.aud, forKey: NotifyWatchSubscriptionsPayload.Claims.CodingKeys.aud) + try container.encode(self.sub, forKey: NotifyWatchSubscriptionsPayload.Claims.CodingKeys.sub) + try container.encode(self.app, forKey: NotifyWatchSubscriptionsPayload.Claims.CodingKeys.app) + } } struct Wrapper: JWTWrapper { @@ -59,7 +79,8 @@ struct NotifyWatchSubscriptionsPayload: JWTClaimsCodable { act: Claims.action, iss: iss, aud: notifyServerIdentityKey.did(variant: .ED25519), - sub: subscriptionAccount.did + sub: subscriptionAccount.did, + app: nil ) } From e90ad976fd6eee56625dd41f88d83aae1c16c1db Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 12 Oct 2023 23:13:32 +0800 Subject: [PATCH 21/49] Update PushMessage.swift --- Example/IntegrationTests/Stubs/PushMessage.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Example/IntegrationTests/Stubs/PushMessage.swift b/Example/IntegrationTests/Stubs/PushMessage.swift index 1ad6880ee..477ff9e99 100644 --- a/Example/IntegrationTests/Stubs/PushMessage.swift +++ b/Example/IntegrationTests/Stubs/PushMessage.swift @@ -5,7 +5,7 @@ extension NotifyMessage { static func stub() -> NotifyMessage { return NotifyMessage( title: "swift_test", - body: "gm_hourly", + body: "cad9a52d-9b0f-4aed-9cca-3e9568a079f9", icon: "https://images.unsplash.com/photo-1581224463294-908316338239?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=250&q=80", url: "https://web3inbox.com", type: "private") From 8fb35cc364d4b8ec7e028d2d6a792933f055010a Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Fri, 13 Oct 2023 04:14:57 +0800 Subject: [PATCH 22/49] Sample app lists fixed --- Example/ExampleApp.xcodeproj/project.pbxproj | 12 +++++++++++ .../Extensions/Foundation/Sequence.swift | 21 +++++++++++++++++++ .../NotificationsPresenter.swift | 12 +++++++---- .../Client/Wallet/NotifyStorage.swift | 5 +---- .../WalletConnectUtils/KeyedDatabase.swift | 17 ++++++++++++--- 5 files changed, 56 insertions(+), 11 deletions(-) create mode 100644 Example/WalletApp/Common/Extensions/Foundation/Sequence.swift diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index 3a595c501..0b8a898c9 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -117,6 +117,7 @@ A5629AE828772A0100094373 /* InviteViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5629AE728772A0100094373 /* InviteViewModel.swift */; }; A5629AEA2877F2D600094373 /* WalletConnectChat in Frameworks */ = {isa = PBXBuildFile; productRef = A5629AE92877F2D600094373 /* WalletConnectChat */; }; A5629AF22877F75100094373 /* Starscream in Frameworks */ = {isa = PBXBuildFile; productRef = A5629AF12877F75100094373 /* Starscream */; }; + A56AC8F22AD88A5A001C8FAA /* Sequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = A56AC8F12AD88A5A001C8FAA /* Sequence.swift */; }; A573C53729EC34A600E3CBFD /* SyncDerivationServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A573C53629EC34A600E3CBFD /* SyncDerivationServiceTests.swift */; }; A573C53929EC365000E3CBFD /* HDWalletKit in Frameworks */ = {isa = PBXBuildFile; productRef = A573C53829EC365000E3CBFD /* HDWalletKit */; }; A573C53B29EC365800E3CBFD /* HDWalletKit in Frameworks */ = {isa = PBXBuildFile; productRef = A573C53A29EC365800E3CBFD /* HDWalletKit */; }; @@ -478,6 +479,7 @@ A5629AE32876E6D200094373 /* ThreadViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadViewModel.swift; sourceTree = ""; }; A5629AE728772A0100094373 /* InviteViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InviteViewModel.swift; sourceTree = ""; }; A5629AEF2877F73000094373 /* DefaultSocketFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultSocketFactory.swift; sourceTree = ""; }; + A56AC8F12AD88A5A001C8FAA /* Sequence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Sequence.swift; sourceTree = ""; }; A573C53629EC34A600E3CBFD /* SyncDerivationServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncDerivationServiceTests.swift; sourceTree = ""; }; A57879702A4EDC8100F8D10B /* TextFieldView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFieldView.swift; sourceTree = ""; }; A578FA312873036400AA7720 /* InputView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputView.swift; sourceTree = ""; }; @@ -1126,6 +1128,14 @@ path = Chat; sourceTree = ""; }; + A56AC8F02AD88A4B001C8FAA /* Foundation */ = { + isa = PBXGroup; + children = ( + A56AC8F12AD88A5A001C8FAA /* Sequence.swift */, + ); + path = Foundation; + sourceTree = ""; + }; A574B3592964570000C2BB91 /* Web3Inbox */ = { isa = PBXGroup; children = ( @@ -1664,6 +1674,7 @@ C56EE262293F56D6004840D1 /* Extensions */ = { isa = PBXGroup; children = ( + A56AC8F02AD88A4B001C8FAA /* Foundation */, 84F568C32795832A00D0A289 /* EthereumTransaction.swift */, C56EE26D293F56D6004840D1 /* SwiftUI */, C56EE269293F56D6004840D1 /* UIKit */, @@ -2438,6 +2449,7 @@ C5B2F6FB297055B0000DBA0E /* ETHSigner.swift in Sources */, C56EE274293F56D7004840D1 /* SceneViewController.swift in Sources */, A5D610D42AB35BED00C20083 /* FailableDecodable.swift in Sources */, + A56AC8F22AD88A5A001C8FAA /* Sequence.swift in Sources */, 847BD1E5298A806800076C90 /* NotificationsPresenter.swift in Sources */, A50D53C12ABA055700A4FD8B /* NotifyPreferencesModule.swift in Sources */, A5B4F7C52ABB20AE0099AF7C /* SubscriptionRouter.swift in Sources */, diff --git a/Example/WalletApp/Common/Extensions/Foundation/Sequence.swift b/Example/WalletApp/Common/Extensions/Foundation/Sequence.swift new file mode 100644 index 000000000..b1b86da7d --- /dev/null +++ b/Example/WalletApp/Common/Extensions/Foundation/Sequence.swift @@ -0,0 +1,21 @@ +import Foundation + +extension Sequence { + func sorted( + by firstPredicate: (Element, Element) -> Bool, + _ secondPredicate: (Element, Element) -> Bool, + _ otherPredicates: ((Element, Element) -> Bool)... + ) -> [Element] { + return sorted(by:) { lhs, rhs in + if firstPredicate(lhs, rhs) { return true } + if firstPredicate(rhs, lhs) { return false } + if secondPredicate(lhs, rhs) { return true } + if secondPredicate(rhs, lhs) { return false } + for predicate in otherPredicates { + if predicate(lhs, rhs) { return true } + if predicate(rhs, lhs) { return false } + } + return false + } + } +} diff --git a/Example/WalletApp/PresentationLayer/Wallet/Notifications/NotificationsPresenter.swift b/Example/WalletApp/PresentationLayer/Wallet/Notifications/NotificationsPresenter.swift index c14bf62b9..91a573857 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/Notifications/NotificationsPresenter.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/Notifications/NotificationsPresenter.swift @@ -14,15 +14,19 @@ final class NotificationsPresenter: ObservableObject { var subscriptionViewModels: [SubscriptionsViewModel] { return subscriptions .map { SubscriptionsViewModel(subscription: $0, messages: interactor.messages(for: $0)) } - .sorted { $0.messagesCount > $1.messagesCount } + .sorted(by: + { $0.messagesCount > $1.messagesCount }, + { $0.name < $1.name } + ) } var listingViewModels: [ListingViewModel] { return listings .map { ListingViewModel(listing: $0) } - .sorted { lhs, rhs in - return subscription(forListing: lhs) != nil && subscription(forListing: rhs) == nil - } + .sorted(by: + { subscription(forListing: $0) != nil && subscription(forListing: $1) == nil }, + { $0.title < $1.title } + ) } init(interactor: NotificationsInteractor, router: NotificationsRouter) { diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifyStorage.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifyStorage.swift index f08498f21..4b72d6026 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/NotifyStorage.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifyStorage.swift @@ -64,10 +64,7 @@ final class NotifyStorage: NotifyStoring { } func replaceAllSubscriptions(_ subscriptions: [NotifySubscription], account: Account) { - subscriptionStore.deleteAll(for: account.absoluteString) - // todo - compare old with new = delete messages for removed subscriptions - //messages for new subscriptions are not required - subscriptionStore.set(elements: subscriptions, for: account.absoluteString) + subscriptionStore.replace(elements: subscriptions, for: account.absoluteString) subscriptionsSubject.send(subscriptions) } diff --git a/Sources/WalletConnectUtils/KeyedDatabase.swift b/Sources/WalletConnectUtils/KeyedDatabase.swift index c619d62d5..5677946ae 100644 --- a/Sources/WalletConnectUtils/KeyedDatabase.swift +++ b/Sources/WalletConnectUtils/KeyedDatabase.swift @@ -61,9 +61,20 @@ public class KeyedDatabase where Element: DatabaseObject { var map = index[key] ?? [:] for element in elements { - guard - map[element.databaseId] == nil else { continue } - map[element.databaseId] = element + map[element.databaseId] = element + } + + index[key] = map + + return true + } + + @discardableResult + public func replace(elements: [Element], for key: String) -> Bool { + var map: [String: Element] = [:] + + for element in elements { + map[element.databaseId] = element } index[key] = map From 1d55d9d82566442b31f72a6be21b679335bacfa3 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Mon, 16 Oct 2023 21:11:27 +0800 Subject: [PATCH 23/49] Statements updated --- .../Client/Wallet/NotifyIdentityService.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifyIdentityService.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifyIdentityService.swift index 0a467049e..26d50fbfa 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/NotifyIdentityService.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifyIdentityService.swift @@ -32,9 +32,9 @@ private extension NotifyIdentityService { func makeStatement(isLimited: Bool) -> String { switch isLimited { case true: - return "I further authorize this app to send and receive messages on my behalf for THIS domains using my WalletConnect identity. Read more at https://walletconnect.com/identity" + return "I further authorize this app to send and receive messages on my behalf for THIS domain using my WalletConnect identity. Read more at https://walletconnect.com/identity" case false: - return "I further authorize this app to send and receive messages on my behalf for ALL domain using my WalletConnect identity. Read more at https://walletconnect.com/identity" + return "I further authorize this app to send and receive messages on my behalf for ALL domains using my WalletConnect identity. Read more at https://walletconnect.com/identity" } } } From 871f0d826bbec81cbcb7aab242846a2900382ad8 Mon Sep 17 00:00:00 2001 From: Alexander Lisovik Date: Mon, 16 Oct 2023 18:59:15 +0400 Subject: [PATCH 24/49] Fix WalletApp payload decryption --- .../Wallet/SessionRequest/SessionRequestPresenter.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Example/WalletApp/PresentationLayer/Wallet/SessionRequest/SessionRequestPresenter.swift b/Example/WalletApp/PresentationLayer/Wallet/SessionRequest/SessionRequestPresenter.swift index 2cd2fc2d8..103002dd6 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/SessionRequest/SessionRequestPresenter.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/SessionRequest/SessionRequestPresenter.swift @@ -15,7 +15,7 @@ final class SessionRequestPresenter: ObservableObject { var message: String { let message = try? sessionRequest.params.get([String].self) let decryptedMessage = message.map { String(data: Data(hex: $0.first ?? ""), encoding: .utf8) } - return (decryptedMessage ?? "Failed to decrypt") ?? "Failed to decrypt" + return (decryptedMessage ?? String(describing: sessionRequest.params.value)) ?? String(describing: sessionRequest.params.value) } @Published var showError = false From 4c97dda58853c5671f2a91c016549cba9204adf0 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Mon, 16 Oct 2023 20:30:57 +0800 Subject: [PATCH 25/49] unregister method --- .../NotificationsInteractor.swift | 2 +- Sources/Chat/ChatClient.swift | 5 +-- .../IdentityClient.swift | 4 +- .../IdentityService.swift | 2 +- .../Client/Wallet/Extensions/Array.swift | 10 +++++ .../Client/Wallet/NotifyAccountProvider.swift | 22 +++++++++++ .../Client/Wallet/NotifyClient.swift | 20 ++++++++-- .../Client/Wallet/NotifyClientFactory.swift | 5 ++- .../Client/Wallet/NotifyIdentityService.swift | 7 +++- .../Wallet/NotifyResubscribeService.swift | 32 ++++++++++------ .../Client/Wallet/NotifyStorage.swift | 10 +++-- ...ubscriptionsChangedRequestSubscriber.swift | 13 +++++-- .../NotifyWatchSubscriptionsRequester.swift | 16 ++------ ...WatchSubscriptionsResponseSubscriber.swift | 12 +++--- .../Client/Wallet/SubscriptionWatcher.swift | 38 ++++++++++++------- .../Wallet/SubscriptionsAutoUpdater.swift | 2 +- 16 files changed, 136 insertions(+), 64 deletions(-) create mode 100644 Sources/WalletConnectNotify/Client/Wallet/Extensions/Array.swift create mode 100644 Sources/WalletConnectNotify/Client/Wallet/NotifyAccountProvider.swift diff --git a/Example/WalletApp/PresentationLayer/Wallet/Notifications/NotificationsInteractor.swift b/Example/WalletApp/PresentationLayer/Wallet/Notifications/NotificationsInteractor.swift index ae29a80b5..0db26bcfb 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/Notifications/NotificationsInteractor.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/Notifications/NotificationsInteractor.swift @@ -14,7 +14,7 @@ final class NotificationsInteractor { } func getSubscriptions() -> [NotifySubscription] { - let subs = Notify.instance.getActiveSubscriptions() + let subs = Notify.instance.getActiveSubscriptions(account: importAccount.account) return subs } diff --git a/Sources/Chat/ChatClient.swift b/Sources/Chat/ChatClient.swift index 445c96ab8..13b66378b 100644 --- a/Sources/Chat/ChatClient.swift +++ b/Sources/Chat/ChatClient.swift @@ -115,9 +115,8 @@ public class ChatClient { /// Unregisters a blockchain account with previously registered identity key /// Must not unregister invite key but must stop listening for invites - /// - Parameter onSign: Callback for signing CAIP-122 message to verify blockchain account ownership - public func unregister(account: Account, onSign: @escaping SigningCallback) async throws { - try await identityClient.unregister(account: account, onSign: onSign) + public func unregister(account: Account) async throws { + try await identityClient.unregister(account: account) } /// Queries the keyserver with a blockchain account diff --git a/Sources/WalletConnectIdentity/IdentityClient.swift b/Sources/WalletConnectIdentity/IdentityClient.swift index 3fb32c92a..a776adb09 100644 --- a/Sources/WalletConnectIdentity/IdentityClient.swift +++ b/Sources/WalletConnectIdentity/IdentityClient.swift @@ -34,8 +34,8 @@ public final class IdentityClient { return inviteKey } - public func unregister(account: Account, onSign: SigningCallback) async throws { - try await identityService.unregister(account: account, onSign: onSign) + public func unregister(account: Account) async throws { + try await identityService.unregister(account: account) logger.debug("Did unregister an account: \(account)") } diff --git a/Sources/WalletConnectIdentity/IdentityService.swift b/Sources/WalletConnectIdentity/IdentityService.swift index a267a6a1b..0a974c474 100644 --- a/Sources/WalletConnectIdentity/IdentityService.swift +++ b/Sources/WalletConnectIdentity/IdentityService.swift @@ -58,7 +58,7 @@ actor IdentityService { return try storage.saveInviteKey(inviteKey, for: account) } - func unregister(account: Account, onSign: SigningCallback) async throws { + func unregister(account: Account) async throws { let identityKey = try storage.getIdentityKey(for: account) let identityPublicKey = DIDKey(rawData: identityKey.publicKey.rawRepresentation) let idAuth = try makeIDAuth(account: account, issuer: identityPublicKey, claims: UnregisterIdentityClaims.self) diff --git a/Sources/WalletConnectNotify/Client/Wallet/Extensions/Array.swift b/Sources/WalletConnectNotify/Client/Wallet/Extensions/Array.swift new file mode 100644 index 000000000..bd295ee30 --- /dev/null +++ b/Sources/WalletConnectNotify/Client/Wallet/Extensions/Array.swift @@ -0,0 +1,10 @@ +import Foundation + +extension Array where Element: Hashable { + + func difference(from other: [Element]) -> [Element] { + let thisSet = Set(self) + let otherSet = Set(other) + return Array(thisSet.symmetricDifference(otherSet)) + } +} diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifyAccountProvider.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifyAccountProvider.swift new file mode 100644 index 000000000..8a702fabd --- /dev/null +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifyAccountProvider.swift @@ -0,0 +1,22 @@ +import Foundation + +final class NotifyAccountProvider { + enum Errors: Error { + case currentAccountNotFound + } + + private var currentAccount: Account? + + func setAccount(_ account: Account) { + self.currentAccount = account + } + + func logout() { + self.currentAccount = nil + } + + func getCurrentAccount() throws -> Account { + guard let currentAccount else { throw Errors.currentAccountNotFound } + return currentAccount + } +} diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift index 47ffbceec..243babfd9 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift @@ -25,6 +25,7 @@ public class NotifyClient { private let pushClient: PushClient private let identityService: NotifyIdentityService private let notifyStorage: NotifyStorage + private let notifyAccountProvider: NotifyAccountProvider private let notifyMessageSubscriber: NotifyMessageSubscriber private let resubscribeService: NotifyResubscribeService private let notifySubscribeResponseSubscriber: NotifySubscribeResponseSubscriber @@ -47,6 +48,7 @@ public class NotifyClient { notifySubscribeResponseSubscriber: NotifySubscribeResponseSubscriber, notifyUpdateRequester: NotifyUpdateRequester, notifyUpdateResponseSubscriber: NotifyUpdateResponseSubscriber, + notifyAccountProvider: NotifyAccountProvider, subscriptionsAutoUpdater: SubscriptionsAutoUpdater, notifyWatchSubscriptionsResponseSubscriber: NotifyWatchSubscriptionsResponseSubscriber, notifySubscriptionsChangedRequestSubscriber: NotifySubscriptionsChangedRequestSubscriber, @@ -63,6 +65,7 @@ public class NotifyClient { self.notifySubscribeResponseSubscriber = notifySubscribeResponseSubscriber self.notifyUpdateRequester = notifyUpdateRequester self.notifyUpdateResponseSubscriber = notifyUpdateResponseSubscriber + self.notifyAccountProvider = notifyAccountProvider self.subscriptionsAutoUpdater = subscriptionsAutoUpdater self.notifyWatchSubscriptionsResponseSubscriber = notifyWatchSubscriptionsResponseSubscriber self.notifySubscriptionsChangedRequestSubscriber = notifySubscriptionsChangedRequestSubscriber @@ -71,7 +74,18 @@ public class NotifyClient { public func register(account: Account, domain: String, isLimited: Bool = false, onSign: @escaping SigningCallback) async throws { try await identityService.register(account: account, domain: domain, isLimited: isLimited, onSign: onSign) - subscriptionWatcher.setAccount(account) + try await resubscribeService.resubscribe(account: account) + + notifyAccountProvider.setAccount(account) + subscriptionWatcher.start() + } + + func unregister(account: Account) async throws { + try await identityService.unregister(account: account) + try await resubscribeService.unsubscribe(account: account) + + notifyAccountProvider.logout() + subscriptionWatcher.stop() } public func setLogging(level: LoggingLevel) { @@ -112,8 +126,8 @@ public class NotifyClient { try await notifyUpdateRequester.update(topic: topic, scope: scope) } - public func getActiveSubscriptions() -> [NotifySubscription] { - return notifyStorage.getSubscriptions() + public func getActiveSubscriptions(account: Account) -> [NotifySubscription] { + return notifyStorage.getSubscriptions(account: account) } public func getMessageHistory(topic: String) -> [NotifyMessageRecord] { diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifyClientFactory.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifyClientFactory.swift index 5f7d3ec22..b3e8fff1e 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/NotifyClientFactory.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifyClientFactory.swift @@ -58,7 +58,9 @@ public struct NotifyClientFactory { let subscriptionsAutoUpdater = SubscriptionsAutoUpdater(notifyUpdateRequester: notifyUpdateRequester, logger: logger, notifyStorage: notifyStorage) - let notifyWatchSubscriptionsRequester = NotifyWatchSubscriptionsRequester(keyserverURL: keyserverURL, networkingInteractor: networkInteractor, identityClient: identityClient, logger: logger, kms: kms, webDidResolver: webDidResolver, notifyHost: notifyHost) + let notifyAccountProvider = NotifyAccountProvider() + + let notifyWatchSubscriptionsRequester = NotifyWatchSubscriptionsRequester(keyserverURL: keyserverURL, networkingInteractor: networkInteractor, identityClient: identityClient, logger: logger, kms: kms, webDidResolver: webDidResolver, notifyAccountProvider: notifyAccountProvider, 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) @@ -79,6 +81,7 @@ public struct NotifyClientFactory { notifySubscribeResponseSubscriber: notifySubscribeResponseSubscriber, notifyUpdateRequester: notifyUpdateRequester, notifyUpdateResponseSubscriber: notifyUpdateResponseSubscriber, + notifyAccountProvider: notifyAccountProvider, subscriptionsAutoUpdater: subscriptionsAutoUpdater, notifyWatchSubscriptionsResponseSubscriber: notifyWatchSubscriptionsResponseSubscriber, notifySubscriptionsChangedRequestSubscriber: notifySubscriptionsChangedRequestSubscriber, diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifyIdentityService.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifyIdentityService.swift index 26d50fbfa..cf7b39101 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/NotifyIdentityService.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifyIdentityService.swift @@ -14,12 +14,15 @@ final class NotifyIdentityService { public func register(account: Account, domain: String, isLimited: Bool, onSign: @escaping SigningCallback) async throws { let statement = makeStatement(isLimited: isLimited) - let pubKey = try await identityClient.register(account: account, + _ = try await identityClient.register(account: account, domain: domain, statement: statement, resources: [keyserverURL.absoluteString], onSign: onSign) - logger.debug("Did register an account: \(account)") + } + + public func unregister(account: Account) async throws { + try await identityClient.unregister(account: account) } func isIdentityRegistered(account: Account) -> Bool { diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifyResubscribeService.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifyResubscribeService.swift index 80fbd12ec..2ae2e619d 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/NotifyResubscribeService.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifyResubscribeService.swift @@ -13,19 +13,27 @@ final class NotifyResubscribeService { self.networkInteractor = networkInteractor self.notifyStorage = notifyStorage self.logger = logger - setUpResubscription() } - func setUpResubscription() { - networkInteractor.socketConnectionStatusPublisher - .sink { [unowned self] status in - guard status == .connected else { return } - let topics = notifyStorage.getSubscriptions().map{$0.topic} - logger.debug("Resubscribing to notify subscription topics: \(topics)", properties: ["topics": topics.joined(separator: ", ")]) - Task(priority: .high) { - try await networkInteractor.batchSubscribe(topics: topics) - } - } - .store(in: &publishers) + func resubscribe(account: Account) async throws { + let topics = notifyStorage.getSubscriptions(account: account).map { $0.topic } + + logger.debug( + "Subscribed to notify subscription topics: \(topics)", + properties: ["topics": topics.joined(separator: ", ")] + ) + + try await networkInteractor.batchSubscribe(topics: topics) + } + + func unsubscribe(account: Account) async throws { + let topics = notifyStorage.getSubscriptions(account: account).map { $0.topic } + + logger.debug( + "Unsubscribed from notify subscription topics: \(topics)", + properties: ["topics": topics.joined(separator: ", ")] + ) + + try await networkInteractor.batchUnsubscribe(topics: topics) } } diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifyStorage.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifyStorage.swift index 4b72d6026..fb163b7cc 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/NotifyStorage.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifyStorage.swift @@ -2,7 +2,8 @@ import Foundation import Combine protocol NotifyStoring { - func getSubscriptions() -> [NotifySubscription] + func getAllSubscriptions() -> [NotifySubscription] + func getSubscriptions(account: Account) -> [NotifySubscription] func getSubscription(topic: String) -> NotifySubscription? func setSubscription(_ subscription: NotifySubscription) async throws func deleteSubscription(topic: String) async throws @@ -50,10 +51,14 @@ final class NotifyStorage: NotifyStoring { // MARK: Subscriptions - func getSubscriptions() -> [NotifySubscription] { + func getAllSubscriptions() -> [NotifySubscription] { return subscriptionStore.getAll() } + func getSubscriptions(account: Account) -> [NotifySubscription] { + return subscriptionStore.getAll(for: account.absoluteString) + } + func getSubscription(topic: String) -> NotifySubscription? { return subscriptionStore.getAll().first(where: { $0.topic == topic }) } @@ -65,7 +70,6 @@ final class NotifyStorage: NotifyStoring { func replaceAllSubscriptions(_ subscriptions: [NotifySubscription], account: Account) { subscriptionStore.replace(elements: subscriptions, for: account.absoluteString) - subscriptionsSubject.send(subscriptions) } func deleteSubscription(topic: String) throws { diff --git a/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifySubscriptionsChanged/NotifySubscriptionsChangedRequestSubscriber.swift b/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifySubscriptionsChanged/NotifySubscriptionsChangedRequestSubscriber.swift index 3c3f9e48a..95d92a0a1 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifySubscriptionsChanged/NotifySubscriptionsChangedRequestSubscriber.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifySubscriptionsChanged/NotifySubscriptionsChangedRequestSubscriber.swift @@ -43,13 +43,18 @@ class NotifySubscriptionsChangedRequestSubscriber { let (jwtPayload, _) = try NotifySubscriptionsChangedRequestPayload.decodeAndVerify(from: payload.request) let account = jwtPayload.account - // todo varify signature with notify server diddoc authentication key + // TODO: varify signature with notify server diddoc authentication key - let oldSubscriptions = notifyStorage.getSubscriptions() + let oldSubscriptions = notifyStorage.getSubscriptions(account: account) let newSubscriptions = try await notifySubscriptionsBuilder.buildSubscriptions(jwtPayload.subscriptions) - logger.debug("number of subscriptions: \(newSubscriptions.count)") + + try Task.checkCancellation() + + let subscriptions = oldSubscriptions.difference(from: newSubscriptions) + + logger.debug("Received: \(newSubscriptions.count), changed: \(subscriptions.count)") - guard newSubscriptions != oldSubscriptions else {return} + guard subscriptions.count > 0 else { return } notifyStorage.replaceAllSubscriptions(newSubscriptions, account: account) diff --git a/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifyWatchSubscriptions/NotifyWatchSubscriptionsRequester.swift b/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifyWatchSubscriptions/NotifyWatchSubscriptionsRequester.swift index 3d90c7d56..96df19892 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifyWatchSubscriptions/NotifyWatchSubscriptionsRequester.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifyWatchSubscriptions/NotifyWatchSubscriptionsRequester.swift @@ -2,7 +2,6 @@ import Foundation import Combine protocol NotifyWatchSubscriptionsRequesting { - func setAccount(_ account: Account) func watchSubscriptions() async throws } @@ -14,8 +13,8 @@ class NotifyWatchSubscriptionsRequester: NotifyWatchSubscriptionsRequesting { private let kms: KeyManagementService private let logger: ConsoleLogging private let webDidResolver: NotifyWebDidResolver + private let notifyAccountProvider: NotifyAccountProvider private let notifyHost: String - private var account: Account? private var publishers = Set() init(keyserverURL: URL, @@ -24,6 +23,7 @@ class NotifyWatchSubscriptionsRequester: NotifyWatchSubscriptionsRequesting { logger: ConsoleLogging, kms: KeyManagementService, webDidResolver: NotifyWebDidResolver, + notifyAccountProvider: NotifyAccountProvider, notifyHost: String ) { self.keyserverURL = keyserverURL @@ -32,16 +32,12 @@ class NotifyWatchSubscriptionsRequester: NotifyWatchSubscriptionsRequesting { self.logger = logger self.kms = kms self.webDidResolver = webDidResolver + self.notifyAccountProvider = notifyAccountProvider self.notifyHost = notifyHost } - func setAccount(_ account: Account) { - self.account = account - } - func watchSubscriptions() async throws { - - guard let account = account else { return } + let account = try notifyAccountProvider.getCurrentAccount() logger.debug("Watching subscriptions") @@ -52,18 +48,14 @@ class NotifyWatchSubscriptionsRequester: NotifyWatchSubscriptionsRequesting { let (responseTopic, selfPubKeyY) = try generateAgreementKeysIfNeeded(notifyServerPublicKey: notifyServerPublicKey, account: account) - - logger.debug("setting symm key for response topic \(responseTopic)") let protocolMethod = NotifyWatchSubscriptionsProtocolMethod() - let watchSubscriptionsAuthWrapper = try await createJWTWrapper( notifyServerAuthenticationDidKey: notifyServerAuthenticationDidKey, subscriptionAccount: account) - let request = RPCRequest(method: protocolMethod.method, params: watchSubscriptionsAuthWrapper) logger.debug("Subscribing to response topic: \(responseTopic)") diff --git a/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifyWatchSubscriptions/NotifyWatchSubscriptionsResponseSubscriber.swift b/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifyWatchSubscriptions/NotifyWatchSubscriptionsResponseSubscriber.swift index e96c8c614..298932dec 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifyWatchSubscriptions/NotifyWatchSubscriptionsResponseSubscriber.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifyWatchSubscriptions/NotifyWatchSubscriptionsResponseSubscriber.swift @@ -38,17 +38,19 @@ class NotifyWatchSubscriptionsResponseSubscriber { let (watchSubscriptionPayloadRequest, _) = try NotifyWatchSubscriptionsPayload.decodeAndVerify(from: payload.request) let account = watchSubscriptionPayloadRequest.subscriptionAccount - // todo varify signature with notify server diddoc authentication key + // TODO: varify signature with notify server diddoc authentication key - let oldSubscriptions = notifyStorage.getSubscriptions() + let oldSubscriptions = notifyStorage.getSubscriptions(account: account) let newSubscriptions = try await notifySubscriptionsBuilder.buildSubscriptions(responsePayload.subscriptions) try Task.checkCancellation() - logger.debug("number of subscriptions: \(newSubscriptions.count)") + let subscriptions = oldSubscriptions.difference(from: newSubscriptions) - guard newSubscriptions != oldSubscriptions else {return} - // todo: unsubscribe for oldSubscriptions topics that are not included in new subscriptions + 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 { diff --git a/Sources/WalletConnectNotify/Client/Wallet/SubscriptionWatcher.swift b/Sources/WalletConnectNotify/Client/Wallet/SubscriptionWatcher.swift index a98d70a05..aa8a51f40 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/SubscriptionWatcher.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/SubscriptionWatcher.swift @@ -26,26 +26,23 @@ class SubscriptionWatcher { self.notificationCenter = notificationCenter } - func setupTimer() { - onSetupTimer?() - logger.debug("Setting up Subscription Watcher timer") - timerCancellable?.cancel() - timerCancellable = Timer.publish(every: timerInterval, on: .main, in: .common) - .autoconnect() - .sink { [weak self] _ in - self?.backgroundQueue.async { - self?.watchSubscriptions() - } - } - } + deinit { stop() } - func setAccount(_ account: Account) { - notifyWatchSubscriptionsRequester.setAccount(account) + func start() { setupTimer() watchAppLifecycle() watchSubscriptions() } + func stop() { + timerCancellable?.cancel() + appLifecycleCancellable?.cancel() + watchSubscriptionsWorkItem?.cancel() + } +} + +private extension SubscriptionWatcher { + func watchSubscriptions() { watchSubscriptionsWorkItem?.cancel() @@ -71,4 +68,17 @@ class SubscriptionWatcher { } #endif } + + func setupTimer() { + onSetupTimer?() + logger.debug("Setting up Subscription Watcher timer") + timerCancellable?.cancel() + timerCancellable = Timer.publish(every: timerInterval, on: .main, in: .common) + .autoconnect() + .sink { [weak self] _ in + self?.backgroundQueue.async { + self?.watchSubscriptions() + } + } + } } diff --git a/Sources/WalletConnectNotify/Client/Wallet/SubscriptionsAutoUpdater.swift b/Sources/WalletConnectNotify/Client/Wallet/SubscriptionsAutoUpdater.swift index 4eff66e89..5aca863e3 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/SubscriptionsAutoUpdater.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/SubscriptionsAutoUpdater.swift @@ -16,7 +16,7 @@ class SubscriptionsAutoUpdater { } private func updateSubscriptionsIfNeeded() { - for subscription in notifyStorage.getSubscriptions() { + for subscription in notifyStorage.getAllSubscriptions() { if shouldUpdate(subscription: subscription) { let scope = Set(subscription.scope.filter{ $0.value.enabled == true }.keys) let topic = subscription.topic From 9dcde1670e40c9c2f65b008e7e257252ec1fe9be Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Mon, 16 Oct 2023 21:03:08 +0800 Subject: [PATCH 26/49] Notify tests fixed --- .../Client/Wallet/NotifyClientFactory.swift | 5 ++--- .../Client/Wallet/NotifyStorage.swift | 14 +++++++------- .../NotifyWatchSubscriptionsRequester.swift | 2 -- .../Client/Wallet/SubscriptionWatcher.swift | 2 +- Tests/NotifyTests/Mocks/MockNotifyStoring.swift | 9 +++++++-- Tests/NotifyTests/SubscriptionWatcherTests.swift | 3 +-- 6 files changed, 18 insertions(+), 17 deletions(-) diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifyClientFactory.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifyClientFactory.swift index b3e8fff1e..d1a7ceffb 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/NotifyClientFactory.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifyClientFactory.swift @@ -38,7 +38,8 @@ public struct NotifyClientFactory { let kms = KeyManagementService(keychain: keychainStorage) let subscriptionStore = KeyedDatabase(storage: keyValueStorage, identifier: NotifyStorageIdntifiers.notifySubscription) let messagesStore = KeyedDatabase(storage: keyValueStorage, identifier: NotifyStorageIdntifiers.notifyMessagesRecords) - let notifyStorage = NotifyStorage(subscriptionStore: subscriptionStore, messagesStore: messagesStore) + let notifyAccountProvider = NotifyAccountProvider() + let notifyStorage = NotifyStorage(subscriptionStore: subscriptionStore, messagesStore: messagesStore, accountProvider: notifyAccountProvider) 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() @@ -58,8 +59,6 @@ public struct NotifyClientFactory { let subscriptionsAutoUpdater = SubscriptionsAutoUpdater(notifyUpdateRequester: notifyUpdateRequester, logger: logger, notifyStorage: notifyStorage) - let notifyAccountProvider = NotifyAccountProvider() - let notifyWatchSubscriptionsRequester = NotifyWatchSubscriptionsRequester(keyserverURL: keyserverURL, networkingInteractor: networkInteractor, identityClient: identityClient, logger: logger, kms: kms, webDidResolver: webDidResolver, notifyAccountProvider: notifyAccountProvider, notifyHost: notifyHost) let notifySubscriptionsBuilder = NotifySubscriptionsBuilder(notifyConfigProvider: notifyConfigProvider) let notifyWatchSubscriptionsResponseSubscriber = NotifyWatchSubscriptionsResponseSubscriber(networkingInteractor: networkInteractor, kms: kms, logger: logger, notifyStorage: notifyStorage, groupKeychainStorage: groupKeychainStorage, notifySubscriptionsBuilder: notifySubscriptionsBuilder) diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifyStorage.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifyStorage.swift index fb163b7cc..539d932d7 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/NotifyStorage.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifyStorage.swift @@ -22,6 +22,8 @@ final class NotifyStorage: NotifyStoring { private let subscriptionsSubject = PassthroughSubject<[NotifySubscription], Never>() private let messagesSubject = PassthroughSubject<[NotifyMessageRecord], Never>() + private let accountProvider: NotifyAccountProvider + var newSubscriptionPublisher: AnyPublisher { return newSubscriptionSubject.eraseToAnyPublisher() } @@ -38,13 +40,10 @@ final class NotifyStorage: NotifyStoring { return subscriptionsSubject.eraseToAnyPublisher() } - var messagesPublisher: AnyPublisher<[NotifyMessageRecord], Never> { - return messagesSubject.eraseToAnyPublisher() - } - - init(subscriptionStore: KeyedDatabase, messagesStore: KeyedDatabase) { + init(subscriptionStore: KeyedDatabase, messagesStore: KeyedDatabase, accountProvider: NotifyAccountProvider) { self.subscriptionStore = subscriptionStore self.messagesStore = messagesStore + self.accountProvider = accountProvider setupSubscriptions() } @@ -90,7 +89,7 @@ final class NotifyStorage: NotifyStoring { // MARK: Messages func messagesPublisher(topic: String) -> AnyPublisher<[NotifyMessageRecord], Never> { - return messagesPublisher + return messagesSubject .map { $0.filter { $0.topic == topic } } .eraseToAnyPublisher() } @@ -126,7 +125,8 @@ private extension NotifyStorage { } subscriptionStore.onUpdate = { [unowned self] in - subscriptionsSubject.send(subscriptionStore.getAll()) + guard let account = try? accountProvider.getCurrentAccount() else { return } + subscriptionsSubject.send(getSubscriptions(account: account)) } } } diff --git a/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifyWatchSubscriptions/NotifyWatchSubscriptionsRequester.swift b/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifyWatchSubscriptions/NotifyWatchSubscriptionsRequester.swift index 96df19892..a1a5364bf 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifyWatchSubscriptions/NotifyWatchSubscriptionsRequester.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifyWatchSubscriptions/NotifyWatchSubscriptionsRequester.swift @@ -102,8 +102,6 @@ class NotifyWatchSubscriptionsRequester: NotifyWatchSubscriptionsRequesting { #if DEBUG class MockNotifyWatchSubscriptionsRequester: NotifyWatchSubscriptionsRequesting { - func setAccount(_ account: WalletConnectUtils.Account) {} - var onWatchSubscriptions: (() -> Void)? func watchSubscriptions() async throws { diff --git a/Sources/WalletConnectNotify/Client/Wallet/SubscriptionWatcher.swift b/Sources/WalletConnectNotify/Client/Wallet/SubscriptionWatcher.swift index aa8a51f40..00ae7bebc 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/SubscriptionWatcher.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/SubscriptionWatcher.swift @@ -41,7 +41,7 @@ class SubscriptionWatcher { } } -private extension SubscriptionWatcher { +internal extension SubscriptionWatcher { func watchSubscriptions() { watchSubscriptionsWorkItem?.cancel() diff --git a/Tests/NotifyTests/Mocks/MockNotifyStoring.swift b/Tests/NotifyTests/Mocks/MockNotifyStoring.swift index 4f1be0991..f6bc2c852 100644 --- a/Tests/NotifyTests/Mocks/MockNotifyStoring.swift +++ b/Tests/NotifyTests/Mocks/MockNotifyStoring.swift @@ -2,20 +2,25 @@ import Foundation @testable import WalletConnectNotify class MockNotifyStoring: NotifyStoring { + var subscriptions: [NotifySubscription] init(subscriptions: [NotifySubscription]) { self.subscriptions = subscriptions } - func getSubscriptions() -> [NotifySubscription] { - return subscriptions + func getSubscriptions(account: Account) -> [NotifySubscription] { + return subscriptions.filter { $0.account == account } } func getSubscription(topic: String) -> NotifySubscription? { return subscriptions.first { $0.topic == topic } } + func getAllSubscriptions() -> [WalletConnectNotify.NotifySubscription] { + return subscriptions + } + func setSubscription(_ subscription: NotifySubscription) async throws { if let index = subscriptions.firstIndex(where: { $0.topic == subscription.topic }) { subscriptions[index] = subscription diff --git a/Tests/NotifyTests/SubscriptionWatcherTests.swift b/Tests/NotifyTests/SubscriptionWatcherTests.swift index 68549edae..e2d389759 100644 --- a/Tests/NotifyTests/SubscriptionWatcherTests.swift +++ b/Tests/NotifyTests/SubscriptionWatcherTests.swift @@ -16,9 +16,8 @@ class SubscriptionWatcherTests: XCTestCase { mockLogger = ConsoleLoggerMock() mockNotificationCenter = MockNotificationCenter() sut = SubscriptionWatcher(notifyWatchSubscriptionsRequester: mockRequester, logger: mockLogger, notificationCenter: mockNotificationCenter) - let account = Account("eip155:1:0x1AAe9864337E821f2F86b5D27468C59AA333C877")! sut.debounceInterval = 0.0001 - sut.setAccount(account) + sut.start() } override func tearDown() { From d6f5294b738f6a8a9663a122ae0086e4cdaa9917 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Mon, 16 Oct 2023 21:08:07 +0800 Subject: [PATCH 27/49] unregister connected to sample app --- .../Wallet/Settings/SettingsInteractor.swift | 6 ++++++ .../Wallet/Settings/SettingsPresenter.swift | 6 ++++-- .../PresentationLayer/Wallet/Settings/SettingsRouter.swift | 2 +- .../PresentationLayer/Wallet/Settings/SettingsView.swift | 5 +++-- .../WalletConnectNotify/Client/Wallet/NotifyClient.swift | 2 +- 5 files changed, 15 insertions(+), 6 deletions(-) diff --git a/Example/WalletApp/PresentationLayer/Wallet/Settings/SettingsInteractor.swift b/Example/WalletApp/PresentationLayer/Wallet/Settings/SettingsInteractor.swift index 9b9e2779d..93592457e 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/Settings/SettingsInteractor.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/Settings/SettingsInteractor.swift @@ -1,3 +1,9 @@ +import Foundation +import WalletConnectNotify + final class SettingsInteractor { + func notifyUnregister(account: Account) async throws { + try await Notify.instance.unregister(account: account) + } } diff --git a/Example/WalletApp/PresentationLayer/Wallet/Settings/SettingsPresenter.swift b/Example/WalletApp/PresentationLayer/Wallet/Settings/SettingsPresenter.swift index 7a705a6bb..fb827251e 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/Settings/SettingsPresenter.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/Settings/SettingsPresenter.swift @@ -38,10 +38,12 @@ final class SettingsPresenter: ObservableObject { return deviceToken } - func logoutPressed() { + func logoutPressed() async throws { + guard let account = accountStorage.importAccount?.account else { return } + try await interactor.notifyUnregister(account: account) accountStorage.importAccount = nil UserDefaults.standard.set(nil, forKey: "deviceToken") - router.presentWelcome() + await router.presentWelcome() } } diff --git a/Example/WalletApp/PresentationLayer/Wallet/Settings/SettingsRouter.swift b/Example/WalletApp/PresentationLayer/Wallet/Settings/SettingsRouter.swift index 4069cd33d..7ef69186e 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/Settings/SettingsRouter.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/Settings/SettingsRouter.swift @@ -10,7 +10,7 @@ final class SettingsRouter { self.app = app } - func presentWelcome() { + @MainActor func presentWelcome() async { WelcomeModule.create(app: app).present() } } diff --git a/Example/WalletApp/PresentationLayer/Wallet/Settings/SettingsView.swift b/Example/WalletApp/PresentationLayer/Wallet/Settings/SettingsView.swift index 49578cf6e..137296793 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/Settings/SettingsView.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/Settings/SettingsView.swift @@ -1,4 +1,5 @@ import SwiftUI +import AsyncButton struct SettingsView: View { @@ -19,8 +20,8 @@ struct SettingsView: View { } Section { - Button { - viewModel.logoutPressed() + AsyncButton { + try await viewModel.logoutPressed() } label: { Text("Log out") .foregroundColor(.red) diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift index 243babfd9..68c397d08 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift @@ -80,7 +80,7 @@ public class NotifyClient { subscriptionWatcher.start() } - func unregister(account: Account) async throws { + public func unregister(account: Account) async throws { try await identityService.unregister(account: account) try await resubscribeService.unsubscribe(account: account) From 181a2fe1b45352243fcffe6e86b5f9d374854a9d Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Mon, 16 Oct 2023 22:58:24 +0800 Subject: [PATCH 28/49] Delete subscriptions on unregister --- .../Client/Wallet/NotifyClient.swift | 4 +-- .../Wallet/NotifyResubscribeService.swift | 32 +++++++------------ .../Client/Wallet/NotifyStorage.swift | 5 +++ 3 files changed, 18 insertions(+), 23 deletions(-) diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift index 68c397d08..5f526ffae 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift @@ -75,15 +75,13 @@ public class NotifyClient { public func register(account: Account, domain: String, isLimited: Bool = false, onSign: @escaping SigningCallback) async throws { try await identityService.register(account: account, domain: domain, isLimited: isLimited, onSign: onSign) try await resubscribeService.resubscribe(account: account) - notifyAccountProvider.setAccount(account) subscriptionWatcher.start() } public func unregister(account: Account) async throws { try await identityService.unregister(account: account) - try await resubscribeService.unsubscribe(account: account) - + notifyStorage.deleteSubscriptions(account: account) notifyAccountProvider.logout() subscriptionWatcher.stop() } diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifyResubscribeService.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifyResubscribeService.swift index 2ae2e619d..18ee0048d 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/NotifyResubscribeService.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifyResubscribeService.swift @@ -13,27 +13,19 @@ final class NotifyResubscribeService { self.networkInteractor = networkInteractor self.notifyStorage = notifyStorage self.logger = logger + setUpResubscription() } - func resubscribe(account: Account) async throws { - let topics = notifyStorage.getSubscriptions(account: account).map { $0.topic } - - logger.debug( - "Subscribed to notify subscription topics: \(topics)", - properties: ["topics": topics.joined(separator: ", ")] - ) - - try await networkInteractor.batchSubscribe(topics: topics) - } - - func unsubscribe(account: Account) async throws { - let topics = notifyStorage.getSubscriptions(account: account).map { $0.topic } - - logger.debug( - "Unsubscribed from notify subscription topics: \(topics)", - properties: ["topics": topics.joined(separator: ", ")] - ) - - try await networkInteractor.batchUnsubscribe(topics: topics) + private func setUpResubscription() { + networkInteractor.socketConnectionStatusPublisher + .sink { [unowned self] status in + guard status == .connected else { return } + let topics = notifyStorage.getAllSubscriptions().map { $0.topic } + logger.debug("Resubscribing to notify subscription topics: \(topics)", properties: ["topics": topics.joined(separator: ", ")]) + Task(priority: .high) { + try await networkInteractor.batchSubscribe(topics: topics) + } + } + .store(in: &publishers) } } diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifyStorage.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifyStorage.swift index 539d932d7..960203109 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/NotifyStorage.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifyStorage.swift @@ -7,6 +7,7 @@ protocol NotifyStoring { func getSubscription(topic: String) -> NotifySubscription? func setSubscription(_ subscription: NotifySubscription) async throws func deleteSubscription(topic: String) async throws + func deleteSubscriptions(account: Account) } final class NotifyStorage: NotifyStoring { @@ -79,6 +80,10 @@ final class NotifyStorage: NotifyStoring { deleteSubscriptionSubject.send(topic) } + func deleteSubscriptions(account: Account) { + subscriptionStore.deleteAll(for: account.absoluteString) + } + func updateSubscription(_ subscription: NotifySubscription, scope: [String: ScopeValue], expiry: UInt64) { let expiry = Date(timeIntervalSince1970: TimeInterval(expiry)) let updated = NotifySubscription(topic: subscription.topic, account: subscription.account, relay: subscription.relay, metadata: subscription.metadata, scope: scope, expiry: expiry, symKey: subscription.symKey) From 16dfc4b81c7ecb071a94bb981d24e63c38455e82 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Mon, 16 Oct 2023 23:04:29 +0800 Subject: [PATCH 29/49] Optional await --- Example/IntegrationTests/Push/NotifyTests.swift | 10 +++++----- .../Client/Wallet/NotifyClient.swift | 12 ++++++++++-- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/Example/IntegrationTests/Push/NotifyTests.swift b/Example/IntegrationTests/Push/NotifyTests.swift index 67f2aa889..ec892af74 100644 --- a/Example/IntegrationTests/Push/NotifyTests.swift +++ b/Example/IntegrationTests/Push/NotifyTests.swift @@ -104,7 +104,7 @@ final class NotifyTests: XCTestCase { }.store(in: &publishers) try! await walletNotifyClientA.register(account: account, domain: gmDappDomain, onSign: sign) - try! await walletNotifyClientA.subscribe(appDomain: gmDappDomain, account: account) + try! await walletNotifyClientA.subscribe(appDomain: gmDappDomain, account: account, await: false) wait(for: [expectation], timeout: InputConfig.defaultTimeout) } @@ -123,7 +123,7 @@ final class NotifyTests: XCTestCase { }.store(in: &publishers) try! await walletNotifyClientA.register(account: account, domain: gmDappDomain, onSign: sign) - try! await walletNotifyClientA.subscribe(appDomain: gmDappDomain, account: account) + try! await walletNotifyClientA.subscribe(appDomain: gmDappDomain, account: account, await: false) sleep(1) try! await clientB.register(account: account, domain: gmDappDomain, onSign: sign) @@ -149,7 +149,7 @@ final class NotifyTests: XCTestCase { sleep(1) - try! await walletNotifyClientA.subscribe(appDomain: gmDappDomain, account: account) + try! await walletNotifyClientA.subscribe(appDomain: gmDappDomain, account: account, await: false) wait(for: [expectation], timeout: InputConfig.defaultTimeout) } @@ -180,7 +180,7 @@ final class NotifyTests: XCTestCase { }.store(in: &publishers) try! await walletNotifyClientA.register(account: account, domain: gmDappDomain, onSign: sign) - try! await walletNotifyClientA.subscribe(appDomain: gmDappDomain, account: account) + try! await walletNotifyClientA.subscribe(appDomain: gmDappDomain, account: account, await: false) wait(for: [expectation], timeout: InputConfig.defaultTimeout) } @@ -215,7 +215,7 @@ final class NotifyTests: XCTestCase { }.store(in: &publishers) try! await walletNotifyClientA.register(account: account, domain: gmDappDomain, onSign: sign) - try! await walletNotifyClientA.subscribe(appDomain: gmDappDomain, account: account) + try! await walletNotifyClientA.subscribe(appDomain: gmDappDomain, account: account, await: false) wait(for: [subscribeExpectation, messageExpectation], timeout: InputConfig.defaultTimeout) } diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift index 5f526ffae..d8010a04d 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift @@ -90,9 +90,13 @@ public class NotifyClient { logger.setLogging(level: level) } - public func subscribe(appDomain: String, account: Account) async throws { - return try await withCheckedThrowingContinuation { continuation in + public func subscribe(appDomain: String, account: Account, await: Bool = true) async throws { + guard `await` else { + try await notifySubscribeRequester.subscribe(appDomain: appDomain, account: account) + return + } + return try await withCheckedThrowingContinuation { continuation in var cancellable: AnyCancellable? cancellable = subscriptionsPublisher .setFailureType(to: Error.self) @@ -120,6 +124,10 @@ public class NotifyClient { } } + public func subscribeAndAwait(appDomain: String, account: Account) async throws { + try await notifySubscribeRequester.subscribe(appDomain: appDomain, account: account) + } + public func update(topic: String, scope: Set) async throws { try await notifyUpdateRequester.update(topic: topic, scope: scope) } From 6e74c9c26ab920f9ae55bcbe63ed9a899f7479b1 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Mon, 16 Oct 2023 23:07:55 +0800 Subject: [PATCH 30/49] MockNotifyStoring updated --- Tests/NotifyTests/Mocks/MockNotifyStoring.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Tests/NotifyTests/Mocks/MockNotifyStoring.swift b/Tests/NotifyTests/Mocks/MockNotifyStoring.swift index f6bc2c852..84161a148 100644 --- a/Tests/NotifyTests/Mocks/MockNotifyStoring.swift +++ b/Tests/NotifyTests/Mocks/MockNotifyStoring.swift @@ -29,6 +29,10 @@ class MockNotifyStoring: NotifyStoring { } } + func deleteSubscriptions(account: WalletConnectUtils.Account) { + subscriptions = subscriptions.filter { $0.account != account } + } + func deleteSubscription(topic: String) async throws { subscriptions.removeAll(where: { $0.topic == topic }) } From 6929f0bce16657302f09d3a56a6752c8edf12004 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Tue, 17 Oct 2023 10:29:19 +0800 Subject: [PATCH 31/49] resubscribe removed --- Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift index d8010a04d..ee6e1bc44 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift @@ -74,7 +74,6 @@ public class NotifyClient { public func register(account: Account, domain: String, isLimited: Bool = false, onSign: @escaping SigningCallback) async throws { try await identityService.register(account: account, domain: domain, isLimited: isLimited, onSign: onSign) - try await resubscribeService.resubscribe(account: account) notifyAccountProvider.setAccount(account) subscriptionWatcher.start() } From e5f59f88befcebc40ed939d659228c81c663425b Mon Sep 17 00:00:00 2001 From: Alexander Lisovik Date: Tue, 17 Oct 2023 14:28:12 +0400 Subject: [PATCH 32/49] Fix nil optional namespaces --- Sources/WalletConnectSign/Services/App/AppProposeService.swift | 2 +- Sources/WalletConnectSign/Services/HistoryService.swift | 2 +- Sources/WalletConnectSign/Types/Session/SessionProposal.swift | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/WalletConnectSign/Services/App/AppProposeService.swift b/Sources/WalletConnectSign/Services/App/AppProposeService.swift index 541999a1a..a92b80bea 100644 --- a/Sources/WalletConnectSign/Services/App/AppProposeService.swift +++ b/Sources/WalletConnectSign/Services/App/AppProposeService.swift @@ -43,7 +43,7 @@ final class AppProposeService { relays: [relay], proposer: proposer, requiredNamespaces: namespaces, - optionalNamespaces: optionalNamespaces, + optionalNamespaces: optionalNamespaces ?? [:], sessionProperties: sessionProperties ) diff --git a/Sources/WalletConnectSign/Services/HistoryService.swift b/Sources/WalletConnectSign/Services/HistoryService.swift index 5a47a81d1..2a5974471 100644 --- a/Sources/WalletConnectSign/Services/HistoryService.swift +++ b/Sources/WalletConnectSign/Services/HistoryService.swift @@ -92,7 +92,7 @@ private extension HistoryService { pairingTopic: record.topic, proposer: proposal.proposer.metadata, requiredNamespaces: proposal.requiredNamespaces, - optionalNamespaces: proposal.optionalNamespaces, + optionalNamespaces: proposal.optionalNamespaces ?? [:], sessionProperties: proposal.sessionProperties, proposal: proposal ) diff --git a/Sources/WalletConnectSign/Types/Session/SessionProposal.swift b/Sources/WalletConnectSign/Types/Session/SessionProposal.swift index 9a10e3380..fa1ee979a 100644 --- a/Sources/WalletConnectSign/Types/Session/SessionProposal.swift +++ b/Sources/WalletConnectSign/Types/Session/SessionProposal.swift @@ -13,7 +13,7 @@ struct SessionProposal: Codable, Equatable { pairingTopic: pairingTopic, proposer: proposer.metadata, requiredNamespaces: requiredNamespaces, - optionalNamespaces: optionalNamespaces, + optionalNamespaces: optionalNamespaces ?? [:], sessionProperties: sessionProperties, proposal: self ) From a6838ac9455b03fec583249871f88301a3249349 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Tue, 17 Oct 2023 18:52:34 +0800 Subject: [PATCH 33/49] Timeout removed from client --- .../IntegrationTests/Push/NotifyTests.swift | 35 ++++++------- .../NotificationsInteractor.swift | 42 +++++++++++++++- .../Client/Wallet/NotifyClient.swift | 50 +------------------ 3 files changed, 57 insertions(+), 70 deletions(-) diff --git a/Example/IntegrationTests/Push/NotifyTests.swift b/Example/IntegrationTests/Push/NotifyTests.swift index ec892af74..a4aa8dc97 100644 --- a/Example/IntegrationTests/Push/NotifyTests.swift +++ b/Example/IntegrationTests/Push/NotifyTests.swift @@ -96,7 +96,7 @@ final class NotifyTests: XCTestCase { walletNotifyClientA.subscriptionsPublisher .sink { [unowned self] subscriptions in - guard let subscription = subscriptions.first else {return} + guard let subscription = subscriptions.first else { return } Task(priority: .high) { try await walletNotifyClientA.deleteSubscription(topic: subscription.topic) expectation.fulfill() @@ -104,7 +104,7 @@ final class NotifyTests: XCTestCase { }.store(in: &publishers) try! await walletNotifyClientA.register(account: account, domain: gmDappDomain, onSign: sign) - try! await walletNotifyClientA.subscribe(appDomain: gmDappDomain, account: account, await: false) + try! await walletNotifyClientA.subscribe(appDomain: gmDappDomain, account: account) wait(for: [expectation], timeout: InputConfig.defaultTimeout) } @@ -115,17 +115,15 @@ final class NotifyTests: XCTestCase { let clientB = makeWalletClient(prefix: "👐🏼 Wallet B: ") clientB.subscriptionsPublisher.sink { subscriptions in + guard let subscription = subscriptions.first else { return } Task(priority: .high) { - if !subscriptions.isEmpty { - expectation.fulfill() - } + try await clientB.deleteSubscription(topic: subscription.topic) + expectation.fulfill() } }.store(in: &publishers) try! await walletNotifyClientA.register(account: account, domain: gmDappDomain, onSign: sign) - try! await walletNotifyClientA.subscribe(appDomain: gmDappDomain, account: account, await: false) - - sleep(1) + try! await walletNotifyClientA.subscribe(appDomain: gmDappDomain, account: account) try! await clientB.register(account: account, domain: gmDappDomain, onSign: sign) wait(for: [expectation], timeout: InputConfig.defaultTimeout) @@ -137,19 +135,16 @@ final class NotifyTests: XCTestCase { let clientB = makeWalletClient(prefix: "👐🏼 Wallet B: ") clientB.subscriptionsPublisher.sink { subscriptions in + guard let subscription = subscriptions.first else { return } Task(priority: .high) { - if !subscriptions.isEmpty { - expectation.fulfill() - } + try await clientB.deleteSubscription(topic: subscription.topic) + expectation.fulfill() } }.store(in: &publishers) try! await walletNotifyClientA.register(account: account, domain: gmDappDomain, onSign: sign) try! await clientB.register(account: account, domain: gmDappDomain, onSign: sign) - - sleep(1) - - try! await walletNotifyClientA.subscribe(appDomain: gmDappDomain, account: account, await: false) + try! await walletNotifyClientA.subscribe(appDomain: gmDappDomain, account: account) wait(for: [expectation], timeout: InputConfig.defaultTimeout) } @@ -162,8 +157,8 @@ final class NotifyTests: XCTestCase { var didUpdate = false walletNotifyClientA.subscriptionsPublisher .sink { [unowned self] subscriptions in - guard let subscription = subscriptions.first else {return} - let updatedScope = Set(subscription.scope.filter{ $0.value.enabled == true }.keys) + guard let subscription = subscriptions.first else { return } + let updatedScope = Set(subscription.scope.filter { $0.value.enabled == true }.keys) if !didUpdate { didUpdate = true @@ -180,7 +175,7 @@ final class NotifyTests: XCTestCase { }.store(in: &publishers) try! await walletNotifyClientA.register(account: account, domain: gmDappDomain, onSign: sign) - try! await walletNotifyClientA.subscribe(appDomain: gmDappDomain, account: account, await: false) + try! await walletNotifyClientA.subscribe(appDomain: gmDappDomain, account: account) wait(for: [expectation], timeout: InputConfig.defaultTimeout) } @@ -193,7 +188,7 @@ final class NotifyTests: XCTestCase { var didNotify = false walletNotifyClientA.subscriptionsPublisher .sink { subscriptions in - guard let subscription = subscriptions.first else {return} + guard let subscription = subscriptions.first else { return } let notifier = Publisher() if !didNotify { didNotify = true @@ -215,7 +210,7 @@ final class NotifyTests: XCTestCase { }.store(in: &publishers) try! await walletNotifyClientA.register(account: account, domain: gmDappDomain, onSign: sign) - try! await walletNotifyClientA.subscribe(appDomain: gmDappDomain, account: account, await: false) + try! await walletNotifyClientA.subscribe(appDomain: gmDappDomain, account: account) wait(for: [subscribeExpectation, messageExpectation], timeout: InputConfig.defaultTimeout) } diff --git a/Example/WalletApp/PresentationLayer/Wallet/Notifications/NotificationsInteractor.swift b/Example/WalletApp/PresentationLayer/Wallet/Notifications/NotificationsInteractor.swift index 0db26bcfb..3eeeb8ea3 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/Notifications/NotificationsInteractor.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/Notifications/NotificationsInteractor.swift @@ -1,3 +1,4 @@ +import Foundation import WalletConnectNotify import Combine @@ -32,7 +33,32 @@ final class NotificationsInteractor { } func subscribe(domain: String) async throws { - try await Notify.instance.subscribe(appDomain: domain, account: importAccount.account) + return try await withCheckedThrowingContinuation { continuation in + var cancellable: AnyCancellable? + cancellable = subscriptionsPublisher + .setFailureType(to: Error.self) + .timeout(10, scheduler: RunLoop.main, customError: { Errors.subscribeTimeout }) + .sink(receiveCompletion: { completion in + defer { cancellable?.cancel() } + switch completion { + case .failure(let error): continuation.resume(with: .failure(error)) + case .finished: break + } + }, receiveValue: { subscriptions in + guard subscriptions.contains(where: { $0.metadata.url == domain }) else { return } + cancellable?.cancel() + continuation.resume(with: .success(())) + }) + + Task { [cancellable] in + do { + try await Notify.instance.subscribe(appDomain: domain, account: importAccount.account) + } catch { + cancellable?.cancel() + continuation.resume(throwing: error) + } + } + } } func unsubscribe(topic: String) async throws { @@ -43,3 +69,17 @@ final class NotificationsInteractor { return Notify.instance.getMessageHistory(topic: subscription.topic) } } + +private extension NotificationsInteractor { + + enum Errors: Error, LocalizedError { + case subscribeTimeout + + var errorDescription: String? { + switch self { + case .subscribeTimeout: + return "Subscribe method timeout" + } + } + } +} diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift index ee6e1bc44..baa1ea4a9 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift @@ -89,41 +89,7 @@ public class NotifyClient { logger.setLogging(level: level) } - public func subscribe(appDomain: String, account: Account, await: Bool = true) async throws { - guard `await` else { - try await notifySubscribeRequester.subscribe(appDomain: appDomain, account: account) - return - } - - return try await withCheckedThrowingContinuation { continuation in - var cancellable: AnyCancellable? - cancellable = subscriptionsPublisher - .setFailureType(to: Error.self) - .timeout(10, scheduler: RunLoop.main, customError: { Errors.subscribeTimeout }) - .sink(receiveCompletion: { completion in - defer { cancellable?.cancel() } - switch completion { - case .failure(let error): continuation.resume(with: .failure(error)) - case .finished: break - } - }, receiveValue: { subscriptions in - guard subscriptions.contains(where: { $0.metadata.url == appDomain }) else { return } - cancellable?.cancel() - continuation.resume(with: .success(())) - }) - - Task { [cancellable] in - do { - try await notifySubscribeRequester.subscribe(appDomain: appDomain, account: account) - } catch { - cancellable?.cancel() - continuation.resume(throwing: error) - } - } - } - } - - public func subscribeAndAwait(appDomain: String, account: Account) async throws { + public func subscribe(appDomain: String, account: Account) async throws { try await notifySubscribeRequester.subscribe(appDomain: appDomain, account: account) } @@ -160,20 +126,6 @@ public class NotifyClient { } } -private extension NotifyClient { - - enum Errors: Error, LocalizedError { - case subscribeTimeout - - var errorDescription: String? { - switch self { - case .subscribeTimeout: - return "Subscribe method timeout" - } - } - } -} - #if targetEnvironment(simulator) extension NotifyClient { public func register(deviceToken: String) async throws { From b1d0d17fb2f30909ff853544ec6b98b5a6dd0daf Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Tue, 17 Oct 2023 19:35:59 +0800 Subject: [PATCH 34/49] NotifyWatchAgreementService --- .../Crypto/KeyManagementService.swift | 11 +++++ .../Client/Wallet/NotifyClient.swift | 4 ++ .../Client/Wallet/NotifyClientFactory.swift | 6 ++- .../Wallet/NotifyWatchAgreementService.swift | 46 +++++++++++++++++++ .../NotifyWatchSubscriptionsRequester.swift | 34 ++------------ .../Mocks/KeyManagementServiceMock.swift | 14 ++++++ 6 files changed, 83 insertions(+), 32 deletions(-) create mode 100644 Sources/WalletConnectNotify/Client/Wallet/NotifyWatchAgreementService.swift diff --git a/Sources/WalletConnectKMS/Crypto/KeyManagementService.swift b/Sources/WalletConnectKMS/Crypto/KeyManagementService.swift index 90a6455fa..53a271b14 100644 --- a/Sources/WalletConnectKMS/Crypto/KeyManagementService.swift +++ b/Sources/WalletConnectKMS/Crypto/KeyManagementService.swift @@ -7,16 +7,19 @@ public protocol KeyManagementServiceProtocol { func setPublicKey(publicKey: AgreementPublicKey, for topic: String) throws func setAgreementSecret(_ agreementSecret: AgreementKeys, topic: String) throws func setSymmetricKey(_ symmetricKey: SymmetricKey, for topic: String) throws + func setTopic(_ topic: String, for key: String) throws func getPrivateKey(for publicKey: AgreementPublicKey) throws -> AgreementPrivateKey? func getAgreementSecret(for topic: String) -> AgreementKeys? func getSymmetricKey(for topic: String) -> SymmetricKey? func getSymmetricKeyRepresentable(for topic: String) -> Data? func getPublicKey(for topic: String) -> AgreementPublicKey? + func getTopic(for key: String) -> String? func deletePrivateKey(for publicKey: String) func deleteAgreementSecret(for topic: String) func deleteSymmetricKey(for topic: String) func deletePublicKey(for topic: String) func deleteAll() throws + func deleteTopic(for key: String) func performKeyAgreement(selfPublicKey: AgreementPublicKey, peerPublicKey hexRepresentation: String) throws -> AgreementKeys } @@ -63,6 +66,14 @@ public class KeyManagementService: KeyManagementServiceProtocol { try keychain.add(topic, forKey: key) } + public func deleteTopic(for key: String) { + do { + try keychain.delete(key: key) + } catch { + print("Error deleting topic: \(error)") + } + } + public func getSymmetricKey(for topic: String) -> SymmetricKey? { do { return try keychain.read(key: topic) as SymmetricKey diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift index baa1ea4a9..dc96ab262 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift @@ -33,6 +33,7 @@ public class NotifyClient { private let notifyUpdateResponseSubscriber: NotifyUpdateResponseSubscriber private let subscriptionsAutoUpdater: SubscriptionsAutoUpdater private let notifyWatchSubscriptionsResponseSubscriber: NotifyWatchSubscriptionsResponseSubscriber + private let notifyWatchAgreementService: NotifyWatchAgreementService private let notifySubscriptionsChangedRequestSubscriber: NotifySubscriptionsChangedRequestSubscriber private let subscriptionWatcher: SubscriptionWatcher @@ -51,6 +52,7 @@ public class NotifyClient { notifyAccountProvider: NotifyAccountProvider, subscriptionsAutoUpdater: SubscriptionsAutoUpdater, notifyWatchSubscriptionsResponseSubscriber: NotifyWatchSubscriptionsResponseSubscriber, + notifyWatchAgreementService: NotifyWatchAgreementService, notifySubscriptionsChangedRequestSubscriber: NotifySubscriptionsChangedRequestSubscriber, subscriptionWatcher: SubscriptionWatcher ) { @@ -68,6 +70,7 @@ public class NotifyClient { self.notifyAccountProvider = notifyAccountProvider self.subscriptionsAutoUpdater = subscriptionsAutoUpdater self.notifyWatchSubscriptionsResponseSubscriber = notifyWatchSubscriptionsResponseSubscriber + self.notifyWatchAgreementService = notifyWatchAgreementService self.notifySubscriptionsChangedRequestSubscriber = notifySubscriptionsChangedRequestSubscriber self.subscriptionWatcher = subscriptionWatcher } @@ -80,6 +83,7 @@ public class NotifyClient { public func unregister(account: Account) async throws { try await identityService.unregister(account: account) + notifyWatchAgreementService.removeAgreement(account: account) notifyStorage.deleteSubscriptions(account: account) notifyAccountProvider.logout() subscriptionWatcher.stop() diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifyClientFactory.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifyClientFactory.swift index d1a7ceffb..941b65177 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/NotifyClientFactory.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifyClientFactory.swift @@ -59,7 +59,8 @@ public struct NotifyClientFactory { let subscriptionsAutoUpdater = SubscriptionsAutoUpdater(notifyUpdateRequester: notifyUpdateRequester, logger: logger, notifyStorage: notifyStorage) - let notifyWatchSubscriptionsRequester = NotifyWatchSubscriptionsRequester(keyserverURL: keyserverURL, networkingInteractor: networkInteractor, identityClient: identityClient, logger: logger, kms: kms, webDidResolver: webDidResolver, notifyAccountProvider: notifyAccountProvider, notifyHost: notifyHost) + let notifyWatchAgreementService = NotifyWatchAgreementService(kms: kms) + let notifyWatchSubscriptionsRequester = NotifyWatchSubscriptionsRequester(keyserverURL: keyserverURL, networkingInteractor: networkInteractor, identityClient: identityClient, logger: logger, webDidResolver: webDidResolver, notifyAccountProvider: notifyAccountProvider, notifyWatchAgreementService: notifyWatchAgreementService, 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) @@ -82,7 +83,8 @@ public struct NotifyClientFactory { notifyUpdateResponseSubscriber: notifyUpdateResponseSubscriber, notifyAccountProvider: notifyAccountProvider, subscriptionsAutoUpdater: subscriptionsAutoUpdater, - notifyWatchSubscriptionsResponseSubscriber: notifyWatchSubscriptionsResponseSubscriber, + notifyWatchSubscriptionsResponseSubscriber: notifyWatchSubscriptionsResponseSubscriber, + notifyWatchAgreementService: notifyWatchAgreementService, notifySubscriptionsChangedRequestSubscriber: notifySubscriptionsChangedRequestSubscriber, subscriptionWatcher: subscriptionWatcher ) diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifyWatchAgreementService.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifyWatchAgreementService.swift new file mode 100644 index 000000000..9e58af00d --- /dev/null +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifyWatchAgreementService.swift @@ -0,0 +1,46 @@ +import Foundation + +final class NotifyWatchAgreementService { + + private let kms: KeyManagementServiceProtocol + + init(kms: KeyManagementServiceProtocol) { + self.kms = kms + } + + func generateAgreementKeysIfNeeded(notifyServerPublicKey: AgreementPublicKey, account: Account) throws -> (responseTopic: String, selfPubKeyY: Data) { + + let keyYStorageKey = storageKey(account: account) + + if let responseTopic = kms.getTopic(for: keyYStorageKey), let selfPubKeyY = kms.getAgreementSecret(for: responseTopic)?.publicKey { + return (responseTopic: responseTopic, selfPubKeyY: selfPubKeyY.rawRepresentation) + } else { + let selfPubKeyY = try kms.createX25519KeyPair() + let watchSubscriptionsTopic = notifyServerPublicKey.rawRepresentation.sha256().toHexString() + + let agreementKeys = try kms.performKeyAgreement(selfPublicKey: selfPubKeyY, peerPublicKey: notifyServerPublicKey.hexRepresentation) + + try kms.setSymmetricKey(agreementKeys.sharedKey, for: watchSubscriptionsTopic) + let responseTopic = agreementKeys.derivedTopic() + + try kms.setAgreementSecret(agreementKeys, topic: responseTopic) + + // save for later under dapp's account + pub key + try kms.setTopic(responseTopic, for: keyYStorageKey) + + return (responseTopic: responseTopic, selfPubKeyY: selfPubKeyY.rawRepresentation) + } + } + + func removeAgreement(account: Account) { + let keyYStorageKey = storageKey(account: account) + kms.deleteTopic(for: keyYStorageKey) + } +} + +private extension NotifyWatchAgreementService { + + func storageKey(account: Account) -> String { + return "watchSubscriptionResponseTopic_\(account.absoluteString)" + } +} diff --git a/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifyWatchSubscriptions/NotifyWatchSubscriptionsRequester.swift b/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifyWatchSubscriptions/NotifyWatchSubscriptionsRequester.swift index a1a5364bf..292e15a47 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifyWatchSubscriptions/NotifyWatchSubscriptionsRequester.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifyWatchSubscriptions/NotifyWatchSubscriptionsRequester.swift @@ -10,10 +10,10 @@ class NotifyWatchSubscriptionsRequester: NotifyWatchSubscriptionsRequesting { private let keyserverURL: URL private let identityClient: IdentityClient private let networkingInteractor: NetworkInteracting - private let kms: KeyManagementService private let logger: ConsoleLogging private let webDidResolver: NotifyWebDidResolver private let notifyAccountProvider: NotifyAccountProvider + private let notifyWatchAgreementService: NotifyWatchAgreementService private let notifyHost: String private var publishers = Set() @@ -21,18 +21,18 @@ class NotifyWatchSubscriptionsRequester: NotifyWatchSubscriptionsRequesting { networkingInteractor: NetworkInteracting, identityClient: IdentityClient, logger: ConsoleLogging, - kms: KeyManagementService, webDidResolver: NotifyWebDidResolver, notifyAccountProvider: NotifyAccountProvider, + notifyWatchAgreementService: NotifyWatchAgreementService, notifyHost: String ) { self.keyserverURL = keyserverURL self.identityClient = identityClient self.networkingInteractor = networkingInteractor self.logger = logger - self.kms = kms self.webDidResolver = webDidResolver self.notifyAccountProvider = notifyAccountProvider + self.notifyWatchAgreementService = notifyWatchAgreementService self.notifyHost = notifyHost } @@ -46,7 +46,7 @@ class NotifyWatchSubscriptionsRequester: NotifyWatchSubscriptionsRequesting { let notifyServerAuthenticationDidKey = DIDKey(rawData: notifyServerAuthenticationKey) let watchSubscriptionsTopic = notifyServerPublicKey.rawRepresentation.sha256().toHexString() - let (responseTopic, selfPubKeyY) = try generateAgreementKeysIfNeeded(notifyServerPublicKey: notifyServerPublicKey, account: account) + let (responseTopic, selfPubKeyY) = try notifyWatchAgreementService.generateAgreementKeysIfNeeded(notifyServerPublicKey: notifyServerPublicKey, account: account) logger.debug("setting symm key for response topic \(responseTopic)") @@ -65,32 +65,6 @@ class NotifyWatchSubscriptionsRequester: NotifyWatchSubscriptionsRequesting { try await networkingInteractor.request(request, topic: watchSubscriptionsTopic, protocolMethod: protocolMethod, envelopeType: .type1(pubKey: selfPubKeyY)) } - - private func generateAgreementKeysIfNeeded(notifyServerPublicKey: AgreementPublicKey, account: Account) throws -> (responseTopic: String, selfPubKeyY: Data) { - - let keyYStorageKey = "\(account)_\(notifyServerPublicKey.hexRepresentation)" - - if let responseTopic = kms.getTopic(for: keyYStorageKey), - let selfPubKeyY = kms.getAgreementSecret(for: responseTopic)?.publicKey { - return (responseTopic: responseTopic, selfPubKeyY: selfPubKeyY.rawRepresentation) - } else { - let selfPubKeyY = try kms.createX25519KeyPair() - let watchSubscriptionsTopic = notifyServerPublicKey.rawRepresentation.sha256().toHexString() - - let agreementKeys = try kms.performKeyAgreement(selfPublicKey: selfPubKeyY, peerPublicKey: notifyServerPublicKey.hexRepresentation) - - try kms.setSymmetricKey(agreementKeys.sharedKey, for: watchSubscriptionsTopic) - let responseTopic = agreementKeys.derivedTopic() - - try kms.setAgreementSecret(agreementKeys, topic: responseTopic) - - // save for later under dapp's account + pub key - try kms.setTopic(responseTopic, for: keyYStorageKey) - - return (responseTopic: responseTopic, selfPubKeyY: selfPubKeyY.rawRepresentation) - } - } - private func createJWTWrapper(notifyServerAuthenticationDidKey: DIDKey, subscriptionAccount: Account) async throws -> NotifyWatchSubscriptionsPayload.Wrapper { let jwtPayload = NotifyWatchSubscriptionsPayload(notifyServerAuthenticationKey: notifyServerAuthenticationDidKey, keyserver: keyserverURL, subscriptionAccount: subscriptionAccount) return try identityClient.signAndCreateWrapper( diff --git a/Tests/TestingUtils/Mocks/KeyManagementServiceMock.swift b/Tests/TestingUtils/Mocks/KeyManagementServiceMock.swift index 935043f72..29fe44452 100644 --- a/Tests/TestingUtils/Mocks/KeyManagementServiceMock.swift +++ b/Tests/TestingUtils/Mocks/KeyManagementServiceMock.swift @@ -2,10 +2,12 @@ import Foundation @testable import WalletConnectKMS final class KeyManagementServiceMock: KeyManagementServiceProtocol { + private(set) var privateKeys: [String: AgreementPrivateKey] = [:] private(set) var symmetricKeys: [String: SymmetricKey] = [:] private(set) var agreementKeys: [String: AgreementKeys] = [:] private(set) var publicKeys: [String: AgreementPublicKey] = [:] + private(set) var topics: [String: String] = [:] func getSymmetricKeyRepresentable(for topic: String) -> Data? { if let key = getAgreementSecret(for: topic)?.sharedKey { @@ -95,6 +97,18 @@ final class KeyManagementServiceMock: KeyManagementServiceProtocol { symmetricKeys = [:] agreementKeys = [:] } + + func setTopic(_ topic: String, for key: String) throws { + topics[key] = topic + } + + func getTopic(for key: String) -> String? { + return topics[key] + } + + func deleteTopic(for key: String) { + topics[key] = nil + } } extension KeyManagementServiceMock { From ac9abcdfe05d10edcb23c39a19429fb32bc48e8b Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Wed, 18 Oct 2023 12:22:18 +0800 Subject: [PATCH 35/49] kY cache: compare peerPubKeys --- .../Wallet/NotifyWatchAgreementService.swift | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifyWatchAgreementService.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifyWatchAgreementService.swift index 9e58af00d..63baaa070 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/NotifyWatchAgreementService.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifyWatchAgreementService.swift @@ -12,9 +12,17 @@ final class NotifyWatchAgreementService { let keyYStorageKey = storageKey(account: account) - if let responseTopic = kms.getTopic(for: keyYStorageKey), let selfPubKeyY = kms.getAgreementSecret(for: responseTopic)?.publicKey { - return (responseTopic: responseTopic, selfPubKeyY: selfPubKeyY.rawRepresentation) - } else { + if + let responseTopic = kms.getTopic(for: keyYStorageKey), + let agreement = kms.getAgreementSecret(for: responseTopic), + let recoveredAgreement = try? kms.performKeyAgreement( + selfPublicKey: agreement.publicKey, + peerPublicKey: notifyServerPublicKey.hexRepresentation + ), agreement == recoveredAgreement + { + return (responseTopic: responseTopic, selfPubKeyY: agreement.publicKey.rawRepresentation) + } + else { let selfPubKeyY = try kms.createX25519KeyPair() let watchSubscriptionsTopic = notifyServerPublicKey.rawRepresentation.sha256().toHexString() From eb8ddc36122f2261c9e80ccf44188af38c7f82a5 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Wed, 18 Oct 2023 12:22:31 +0800 Subject: [PATCH 36/49] Delete messages on unregister --- Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift | 2 +- .../WalletConnectNotify/Client/Wallet/NotifyStorage.swift | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift index dc96ab262..0ef1d0628 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift @@ -84,7 +84,7 @@ public class NotifyClient { public func unregister(account: Account) async throws { try await identityService.unregister(account: account) notifyWatchAgreementService.removeAgreement(account: account) - notifyStorage.deleteSubscriptions(account: account) + notifyStorage.clearDatabase(account: account) notifyAccountProvider.logout() subscriptionWatcher.stop() } diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifyStorage.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifyStorage.swift index 960203109..38e3e6e82 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/NotifyStorage.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifyStorage.swift @@ -80,7 +80,10 @@ final class NotifyStorage: NotifyStoring { deleteSubscriptionSubject.send(topic) } - func deleteSubscriptions(account: Account) { + func clearDatabase(account: Account) { + for subscription in getSubscriptions(account: account) { + deleteMessages(topic: subscription.topic) + } subscriptionStore.deleteAll(for: account.absoluteString) } From 33b32f2553afd8f3394774ec360cf0cf62a5874a Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Wed, 18 Oct 2023 12:25:00 +0800 Subject: [PATCH 37/49] Notify unit tests fixed --- Sources/WalletConnectNotify/Client/Wallet/NotifyStorage.swift | 2 +- Tests/NotifyTests/Mocks/MockNotifyStoring.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifyStorage.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifyStorage.swift index 38e3e6e82..a7e2c92b8 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/NotifyStorage.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifyStorage.swift @@ -7,7 +7,7 @@ protocol NotifyStoring { func getSubscription(topic: String) -> NotifySubscription? func setSubscription(_ subscription: NotifySubscription) async throws func deleteSubscription(topic: String) async throws - func deleteSubscriptions(account: Account) + func clearDatabase(account: Account) } final class NotifyStorage: NotifyStoring { diff --git a/Tests/NotifyTests/Mocks/MockNotifyStoring.swift b/Tests/NotifyTests/Mocks/MockNotifyStoring.swift index 84161a148..bd773936c 100644 --- a/Tests/NotifyTests/Mocks/MockNotifyStoring.swift +++ b/Tests/NotifyTests/Mocks/MockNotifyStoring.swift @@ -29,7 +29,7 @@ class MockNotifyStoring: NotifyStoring { } } - func deleteSubscriptions(account: WalletConnectUtils.Account) { + func clearDatabase(account: WalletConnectUtils.Account) { subscriptions = subscriptions.filter { $0.account != account } } From 5e4b5888cf5af21ecd184f016ee9ff6ca09a40f0 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Wed, 18 Oct 2023 18:26:25 +0800 Subject: [PATCH 38/49] NotifyWatcherAgreementKeysProvider renameing --- .../WalletConnectNotify/Client/Wallet/NotifyClient.swift | 8 ++++---- .../Client/Wallet/NotifyClientFactory.swift | 6 +++--- ...ice.swift => NotifyWatcherAgreementKeysProvider.swift} | 4 ++-- .../NotifyWatchSubscriptionsRequester.swift | 8 ++++---- 4 files changed, 13 insertions(+), 13 deletions(-) rename Sources/WalletConnectNotify/Client/Wallet/{NotifyWatchAgreementService.swift => NotifyWatcherAgreementKeysProvider.swift} (94%) diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift index 0ef1d0628..29a58e77f 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift @@ -33,7 +33,7 @@ public class NotifyClient { private let notifyUpdateResponseSubscriber: NotifyUpdateResponseSubscriber private let subscriptionsAutoUpdater: SubscriptionsAutoUpdater private let notifyWatchSubscriptionsResponseSubscriber: NotifyWatchSubscriptionsResponseSubscriber - private let notifyWatchAgreementService: NotifyWatchAgreementService + private let notifyWatcherAgreementKeysProvider: NotifyWatcherAgreementKeysProvider private let notifySubscriptionsChangedRequestSubscriber: NotifySubscriptionsChangedRequestSubscriber private let subscriptionWatcher: SubscriptionWatcher @@ -52,7 +52,7 @@ public class NotifyClient { notifyAccountProvider: NotifyAccountProvider, subscriptionsAutoUpdater: SubscriptionsAutoUpdater, notifyWatchSubscriptionsResponseSubscriber: NotifyWatchSubscriptionsResponseSubscriber, - notifyWatchAgreementService: NotifyWatchAgreementService, + notifyWatcherAgreementKeysProvider: NotifyWatcherAgreementKeysProvider, notifySubscriptionsChangedRequestSubscriber: NotifySubscriptionsChangedRequestSubscriber, subscriptionWatcher: SubscriptionWatcher ) { @@ -70,7 +70,7 @@ public class NotifyClient { self.notifyAccountProvider = notifyAccountProvider self.subscriptionsAutoUpdater = subscriptionsAutoUpdater self.notifyWatchSubscriptionsResponseSubscriber = notifyWatchSubscriptionsResponseSubscriber - self.notifyWatchAgreementService = notifyWatchAgreementService + self.notifyWatcherAgreementKeysProvider = notifyWatcherAgreementKeysProvider self.notifySubscriptionsChangedRequestSubscriber = notifySubscriptionsChangedRequestSubscriber self.subscriptionWatcher = subscriptionWatcher } @@ -83,7 +83,7 @@ public class NotifyClient { public func unregister(account: Account) async throws { try await identityService.unregister(account: account) - notifyWatchAgreementService.removeAgreement(account: account) + notifyWatcherAgreementKeysProvider.removeAgreement(account: account) notifyStorage.clearDatabase(account: account) notifyAccountProvider.logout() subscriptionWatcher.stop() diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifyClientFactory.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifyClientFactory.swift index 941b65177..7cf0e8b5b 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/NotifyClientFactory.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifyClientFactory.swift @@ -59,8 +59,8 @@ public struct NotifyClientFactory { let subscriptionsAutoUpdater = SubscriptionsAutoUpdater(notifyUpdateRequester: notifyUpdateRequester, logger: logger, notifyStorage: notifyStorage) - let notifyWatchAgreementService = NotifyWatchAgreementService(kms: kms) - let notifyWatchSubscriptionsRequester = NotifyWatchSubscriptionsRequester(keyserverURL: keyserverURL, networkingInteractor: networkInteractor, identityClient: identityClient, logger: logger, webDidResolver: webDidResolver, notifyAccountProvider: notifyAccountProvider, notifyWatchAgreementService: notifyWatchAgreementService, notifyHost: notifyHost) + 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) @@ -84,7 +84,7 @@ public struct NotifyClientFactory { notifyAccountProvider: notifyAccountProvider, subscriptionsAutoUpdater: subscriptionsAutoUpdater, notifyWatchSubscriptionsResponseSubscriber: notifyWatchSubscriptionsResponseSubscriber, - notifyWatchAgreementService: notifyWatchAgreementService, + notifyWatcherAgreementKeysProvider: notifyWatcherAgreementKeysProvider, notifySubscriptionsChangedRequestSubscriber: notifySubscriptionsChangedRequestSubscriber, subscriptionWatcher: subscriptionWatcher ) diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifyWatchAgreementService.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifyWatcherAgreementKeysProvider.swift similarity index 94% rename from Sources/WalletConnectNotify/Client/Wallet/NotifyWatchAgreementService.swift rename to Sources/WalletConnectNotify/Client/Wallet/NotifyWatcherAgreementKeysProvider.swift index 63baaa070..5789f26c3 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/NotifyWatchAgreementService.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifyWatcherAgreementKeysProvider.swift @@ -1,6 +1,6 @@ import Foundation -final class NotifyWatchAgreementService { +final class NotifyWatcherAgreementKeysProvider { private let kms: KeyManagementServiceProtocol @@ -46,7 +46,7 @@ final class NotifyWatchAgreementService { } } -private extension NotifyWatchAgreementService { +private extension NotifyWatcherAgreementKeysProvider { func storageKey(account: Account) -> String { return "watchSubscriptionResponseTopic_\(account.absoluteString)" diff --git a/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifyWatchSubscriptions/NotifyWatchSubscriptionsRequester.swift b/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifyWatchSubscriptions/NotifyWatchSubscriptionsRequester.swift index 292e15a47..72702c588 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifyWatchSubscriptions/NotifyWatchSubscriptionsRequester.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_notifyWatchSubscriptions/NotifyWatchSubscriptionsRequester.swift @@ -13,7 +13,7 @@ class NotifyWatchSubscriptionsRequester: NotifyWatchSubscriptionsRequesting { private let logger: ConsoleLogging private let webDidResolver: NotifyWebDidResolver private let notifyAccountProvider: NotifyAccountProvider - private let notifyWatchAgreementService: NotifyWatchAgreementService + private let notifyWatcherAgreementKeysProvider: NotifyWatcherAgreementKeysProvider private let notifyHost: String private var publishers = Set() @@ -23,7 +23,7 @@ class NotifyWatchSubscriptionsRequester: NotifyWatchSubscriptionsRequesting { logger: ConsoleLogging, webDidResolver: NotifyWebDidResolver, notifyAccountProvider: NotifyAccountProvider, - notifyWatchAgreementService: NotifyWatchAgreementService, + notifyWatcherAgreementKeysProvider: NotifyWatcherAgreementKeysProvider, notifyHost: String ) { self.keyserverURL = keyserverURL @@ -32,7 +32,7 @@ class NotifyWatchSubscriptionsRequester: NotifyWatchSubscriptionsRequesting { self.logger = logger self.webDidResolver = webDidResolver self.notifyAccountProvider = notifyAccountProvider - self.notifyWatchAgreementService = notifyWatchAgreementService + self.notifyWatcherAgreementKeysProvider = notifyWatcherAgreementKeysProvider self.notifyHost = notifyHost } @@ -46,7 +46,7 @@ class NotifyWatchSubscriptionsRequester: NotifyWatchSubscriptionsRequesting { let notifyServerAuthenticationDidKey = DIDKey(rawData: notifyServerAuthenticationKey) let watchSubscriptionsTopic = notifyServerPublicKey.rawRepresentation.sha256().toHexString() - let (responseTopic, selfPubKeyY) = try notifyWatchAgreementService.generateAgreementKeysIfNeeded(notifyServerPublicKey: notifyServerPublicKey, account: account) + let (responseTopic, selfPubKeyY) = try notifyWatcherAgreementKeysProvider.generateAgreementKeysIfNeeded(notifyServerPublicKey: notifyServerPublicKey, account: account) logger.debug("setting symm key for response topic \(responseTopic)") From eebe2277bd5e99ee5bfbad9b57733c06869322e5 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Tue, 17 Oct 2023 17:52:26 +0800 Subject: [PATCH 39/49] 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 -} From 261e2ee591e8fa6aa52ee33dffee037834565f31 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Wed, 18 Oct 2023 21:08:36 +0800 Subject: [PATCH 40/49] Store appDomain in AppMetadata url --- .../WalletConnectNotify/Client/Wallet/NotifyConfig.swift | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifyConfig.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifyConfig.swift index 5ae59614d..353eb5347 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/NotifyConfig.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifyConfig.swift @@ -18,12 +18,16 @@ struct NotifyConfig: Codable { let image_url: ImageUrl? let notificationTypes: [NotificationType] + var appDomain: String { + return URL(string: homepage)?.host ?? "" + } + var metadata: AppMetadata { return AppMetadata( name: name, description: description, - url: homepage, + url: appDomain, icons: [image_url?.sm, image_url?.md, image_url?.lg].compactMap { $0 } ) } From 850f7e9a6a59e04662a50d65f440a92672d3a5ce Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Wed, 18 Oct 2023 21:16:04 +0800 Subject: [PATCH 41/49] Empty config fallback --- .../Client/Wallet/NotifyConfigProvider.swift | 25 +++++++++++++++---- .../Wallet/NotifySubscriptionsBuilder.swift | 3 ++- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifyConfigProvider.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifyConfigProvider.swift index e6b4746dc..2ad490bfe 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/NotifyConfigProvider.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifyConfigProvider.swift @@ -8,11 +8,15 @@ actor NotifyConfigProvider { self.projectId = projectId } - 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 + func resolveNotifyConfig(appDomain: String) async -> NotifyConfig { + do { + 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 + } catch { + return emptyConfig(appDomain: appDomain) + } } } @@ -21,4 +25,15 @@ private extension NotifyConfigProvider { struct NotifyConfigResponse: Codable { let data: NotifyConfig } + + func emptyConfig(appDomain: String) -> NotifyConfig { + return NotifyConfig( + id: UUID().uuidString, + name: appDomain, + homepage: "https://\(appDomain)", + description: "", + image_url: nil, + notificationTypes: [] + ) + } } diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifySubscriptionsBuilder.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifySubscriptionsBuilder.swift index 7af9d2546..e62ba1bc7 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/NotifySubscriptionsBuilder.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifySubscriptionsBuilder.swift @@ -11,8 +11,9 @@ class NotifySubscriptionsBuilder { var result = [NotifySubscription]() for subscription in notifyServerSubscriptions { + let config = await notifyConfigProvider.resolveNotifyConfig(appDomain: subscription.appDomain) + 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) From 8609ac910e1b86f364067b673c1257ecbae8b476 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Wed, 18 Oct 2023 21:17:42 +0800 Subject: [PATCH 42/49] homepage fallback --- Sources/WalletConnectNotify/Client/Wallet/NotifyConfig.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifyConfig.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifyConfig.swift index 353eb5347..ef38764ca 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/NotifyConfig.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifyConfig.swift @@ -19,7 +19,7 @@ struct NotifyConfig: Codable { let notificationTypes: [NotificationType] var appDomain: String { - return URL(string: homepage)?.host ?? "" + return URL(string: homepage)?.host ?? homepage } var metadata: AppMetadata { From 2767ecf0eeafba26398587b4820fbe783890f072 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Wed, 18 Oct 2023 22:02:48 +0800 Subject: [PATCH 43/49] Replace homepage with dapp_url --- Sources/WalletConnectNotify/Client/Wallet/NotifyConfig.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifyConfig.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifyConfig.swift index ef38764ca..b0e88b181 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/NotifyConfig.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifyConfig.swift @@ -15,11 +15,12 @@ struct NotifyConfig: Codable { let name: String let homepage: String let description: String + let dapp_url: String let image_url: ImageUrl? let notificationTypes: [NotificationType] var appDomain: String { - return URL(string: homepage)?.host ?? homepage + return URL(string: dapp_url)?.host ?? dapp_url } var metadata: AppMetadata { From 86a9c2dcab5da4a7be168c157876ebed16a9665c Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Wed, 18 Oct 2023 22:11:37 +0800 Subject: [PATCH 44/49] Scope with notificationType name --- .../Client/Wallet/NotifyConfigProvider.swift | 3 ++- .../Client/Wallet/NotifySubscriptionsBuilder.swift | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifyConfigProvider.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifyConfigProvider.swift index 2ad490bfe..2c507f3d7 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/NotifyConfigProvider.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifyConfigProvider.swift @@ -31,7 +31,8 @@ private extension NotifyConfigProvider { id: UUID().uuidString, name: appDomain, homepage: "https://\(appDomain)", - description: "", + description: "", + dapp_url: "https://\(appDomain)", image_url: nil, notificationTypes: [] ) diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifySubscriptionsBuilder.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifySubscriptionsBuilder.swift index e62ba1bc7..1fa8f91a3 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/NotifySubscriptionsBuilder.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifySubscriptionsBuilder.swift @@ -36,7 +36,7 @@ class NotifySubscriptionsBuilder { 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)) + $0[$1.name] = ScopeValue(description: $1.name, enabled: selectedScope.contains($1.name)) } } } From 8ea888638417c68529007ce8d78ea0a5b6783a26 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 19 Oct 2023 03:39:48 +0800 Subject: [PATCH 45/49] testWalletCreatesAndUpdatesSubscription test fixed --- Example/IntegrationTests/Push/NotifyTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Example/IntegrationTests/Push/NotifyTests.swift b/Example/IntegrationTests/Push/NotifyTests.swift index 264ca0e6d..69483a21d 100644 --- a/Example/IntegrationTests/Push/NotifyTests.swift +++ b/Example/IntegrationTests/Push/NotifyTests.swift @@ -152,7 +152,7 @@ final class NotifyTests: XCTestCase { func testWalletCreatesAndUpdatesSubscription() async { let expectation = expectation(description: "expects to create and update notify subscription") - let updateScope: Set = ["alerts"] + let updateScope: Set = ["Alerts"] expectation.assertForOverFulfill = false var didUpdate = false From 2e7cc91a62ce0d6b9db9efaa5922817cb582cacc Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 19 Oct 2023 04:05:29 +0800 Subject: [PATCH 46/49] Config cache --- .../Client/Wallet/NotifyClientFactory.swift | 5 ++--- .../Client/Wallet/NotifyConfigProvider.swift | 10 +++++++++- .../wc_pushSubscribe/NotifySubscribeRequester.swift | 7 +------ .../NotifySubscribeResponseSubscriber.swift | 3 --- .../WalletConnectNotify/NotifyStorageIdntifiers.swift | 1 - 5 files changed, 12 insertions(+), 14 deletions(-) diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifyClientFactory.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifyClientFactory.swift index 2896ec747..8fac80ce5 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/NotifyClientFactory.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifyClientFactory.swift @@ -48,12 +48,11 @@ public struct NotifyClientFactory { let deleteNotifySubscriptionRequester = DeleteNotifySubscriptionRequester(keyserver: keyserverURL, networkingInteractor: networkInteractor, identityClient: identityClient, webDidResolver: webDidResolver, kms: kms, logger: logger, notifyStorage: notifyStorage) let resubscribeService = NotifyResubscribeService(networkInteractor: networkInteractor, notifyStorage: notifyStorage, logger: logger) - let dappsMetadataStore = CodableStore(defaults: keyValueStorage, identifier: NotifyStorageIdntifiers.dappsMetadataStore) let notifyConfigProvider = NotifyConfigProvider(projectId: projectId) - let notifySubscribeRequester = NotifySubscribeRequester(keyserverURL: keyserverURL, networkingInteractor: networkInteractor, identityClient: identityClient, logger: logger, kms: kms, webDidResolver: webDidResolver, notifyConfigProvider: notifyConfigProvider, dappsMetadataStore: dappsMetadataStore) + 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, dappsMetadataStore: dappsMetadataStore, notifyConfigProvider: notifyConfigProvider) + let notifySubscribeResponseSubscriber = NotifySubscribeResponseSubscriber(networkingInteractor: networkInteractor, kms: kms, logger: logger, groupKeychainStorage: groupKeychainStorage, notifyStorage: notifyStorage, notifyConfigProvider: notifyConfigProvider) let notifyUpdateRequester = NotifyUpdateRequester(keyserverURL: keyserverURL, webDidResolver: webDidResolver, identityClient: identityClient, networkingInteractor: networkInteractor, notifyConfigProvider: notifyConfigProvider, logger: logger, notifyStorage: notifyStorage) diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifyConfigProvider.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifyConfigProvider.swift index 2c507f3d7..8b196f592 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/NotifyConfigProvider.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifyConfigProvider.swift @@ -4,16 +4,24 @@ actor NotifyConfigProvider { private let projectId: String + private var cache: [String: NotifyConfig] = [:] + init(projectId: String) { self.projectId = projectId } func resolveNotifyConfig(appDomain: String) async -> NotifyConfig { + if let config = cache[appDomain] { + return config + } + do { 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 + let config = response.data + cache[appDomain] = config + return config } catch { return emptyConfig(appDomain: appDomain) } diff --git a/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_pushSubscribe/NotifySubscribeRequester.swift b/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_pushSubscribe/NotifySubscribeRequester.swift index 551a54058..eefba1ce6 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_pushSubscribe/NotifySubscribeRequester.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_pushSubscribe/NotifySubscribeRequester.swift @@ -13,7 +13,6 @@ class NotifySubscribeRequester { private let kms: KeyManagementService private let logger: ConsoleLogging private let webDidResolver: NotifyWebDidResolver - private let dappsMetadataStore: CodableStore private let notifyConfigProvider: NotifyConfigProvider init(keyserverURL: URL, @@ -22,8 +21,7 @@ class NotifySubscribeRequester { logger: ConsoleLogging, kms: KeyManagementService, webDidResolver: NotifyWebDidResolver, - notifyConfigProvider: NotifyConfigProvider, - dappsMetadataStore: CodableStore + notifyConfigProvider: NotifyConfigProvider ) { self.keyserverURL = keyserverURL self.identityClient = identityClient @@ -31,7 +29,6 @@ class NotifySubscribeRequester { self.logger = logger self.kms = kms self.webDidResolver = webDidResolver - self.dappsMetadataStore = dappsMetadataStore self.notifyConfigProvider = notifyConfigProvider } @@ -47,8 +44,6 @@ class NotifySubscribeRequester { let keysY = try generateAgreementKeys(peerPublicKey: peerPublicKey) let responseTopic = keysY.derivedTopic() - - dappsMetadataStore.set(config.metadata, forKey: responseTopic) try kms.setSymmetricKey(keysY.sharedKey, for: subscribeTopic) try kms.setAgreementSecret(keysY, topic: responseTopic) diff --git a/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_pushSubscribe/NotifySubscribeResponseSubscriber.swift b/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_pushSubscribe/NotifySubscribeResponseSubscriber.swift index 5dc224b6c..d8aa56a39 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_pushSubscribe/NotifySubscribeResponseSubscriber.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_pushSubscribe/NotifySubscribeResponseSubscriber.swift @@ -12,7 +12,6 @@ class NotifySubscribeResponseSubscriber { private let logger: ConsoleLogging private let notifyStorage: NotifyStorage private let groupKeychainStorage: KeychainStorageProtocol - private let dappsMetadataStore: CodableStore private let notifyConfigProvider: NotifyConfigProvider init(networkingInteractor: NetworkInteracting, @@ -20,7 +19,6 @@ class NotifySubscribeResponseSubscriber { logger: ConsoleLogging, groupKeychainStorage: KeychainStorageProtocol, notifyStorage: NotifyStorage, - dappsMetadataStore: CodableStore, notifyConfigProvider: NotifyConfigProvider ) { self.networkingInteractor = networkingInteractor @@ -28,7 +26,6 @@ class NotifySubscribeResponseSubscriber { self.logger = logger self.groupKeychainStorage = groupKeychainStorage self.notifyStorage = notifyStorage - self.dappsMetadataStore = dappsMetadataStore self.notifyConfigProvider = notifyConfigProvider subscribeForSubscriptionResponse() } diff --git a/Sources/WalletConnectNotify/NotifyStorageIdntifiers.swift b/Sources/WalletConnectNotify/NotifyStorageIdntifiers.swift index fb1b21c53..b68272b25 100644 --- a/Sources/WalletConnectNotify/NotifyStorageIdntifiers.swift +++ b/Sources/WalletConnectNotify/NotifyStorageIdntifiers.swift @@ -4,6 +4,5 @@ enum NotifyStorageIdntifiers { static let notifySubscription = "com.walletconnect.notify.notifySubscription" static let notifyMessagesRecords = "com.walletconnect.sdk.notifyMessagesRecords" - static let dappsMetadataStore = "com.walletconnect.sdk.dappsMetadataStore" static let coldStartStore = "com.walletconnect.sdk.coldStartStore" } From 9cf14ae491761f39ef9dd91f2142e2aa7a85f6a5 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 19 Oct 2023 04:46:50 +0800 Subject: [PATCH 47/49] Notification type id's --- Example/IntegrationTests/Push/NotifyTests.swift | 4 ++-- Example/IntegrationTests/Stubs/PushMessage.swift | 6 +++--- .../Wallet/NotifySettings/NotifyPreferencesView.swift | 4 ++-- .../Client/Wallet/NotifySubscriptionsBuilder.swift | 7 ++++++- .../wc_pushSubscribe/NotifySubscribeRequester.swift | 6 ++---- .../Types/DataStructures/NotifySubscription.swift | 6 +++++- 6 files changed, 20 insertions(+), 13 deletions(-) diff --git a/Example/IntegrationTests/Push/NotifyTests.swift b/Example/IntegrationTests/Push/NotifyTests.swift index 69483a21d..a0bfed40e 100644 --- a/Example/IntegrationTests/Push/NotifyTests.swift +++ b/Example/IntegrationTests/Push/NotifyTests.swift @@ -152,7 +152,7 @@ final class NotifyTests: XCTestCase { func testWalletCreatesAndUpdatesSubscription() async { let expectation = expectation(description: "expects to create and update notify subscription") - let updateScope: Set = ["Alerts"] + let updateScope: Set = ["8529aae8-cb26-4d49-922e-eb099044bebe"] expectation.assertForOverFulfill = false var didUpdate = false @@ -184,7 +184,7 @@ final class NotifyTests: XCTestCase { func testNotifyServerSubscribeAndNotifies() async throws { let subscribeExpectation = expectation(description: "creates notify subscription") let messageExpectation = expectation(description: "receives a notify message") - let notifyMessage = NotifyMessage.stub() + let notifyMessage = NotifyMessage.stub(type: "8529aae8-cb26-4d49-922e-eb099044bebe") var didNotify = false walletNotifyClientA.subscriptionsPublisher diff --git a/Example/IntegrationTests/Stubs/PushMessage.swift b/Example/IntegrationTests/Stubs/PushMessage.swift index 477ff9e99..18387013b 100644 --- a/Example/IntegrationTests/Stubs/PushMessage.swift +++ b/Example/IntegrationTests/Stubs/PushMessage.swift @@ -2,12 +2,12 @@ import Foundation import WalletConnectNotify extension NotifyMessage { - static func stub() -> NotifyMessage { + static func stub(type: String) -> NotifyMessage { return NotifyMessage( title: "swift_test", - body: "cad9a52d-9b0f-4aed-9cca-3e9568a079f9", + body: "body", icon: "https://images.unsplash.com/photo-1581224463294-908316338239?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=250&q=80", url: "https://web3inbox.com", - type: "private") + type: type) } } diff --git a/Example/WalletApp/PresentationLayer/Wallet/NotifySettings/NotifyPreferencesView.swift b/Example/WalletApp/PresentationLayer/Wallet/NotifySettings/NotifyPreferencesView.swift index 764907d7a..327c2b4d6 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/NotifySettings/NotifyPreferencesView.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/NotifySettings/NotifyPreferencesView.swift @@ -53,10 +53,10 @@ struct NotifyPreferencesView: View { Toggle(isOn: .init(get: { viewModel.update[title]?.enabled ?? value.enabled }, set: { newValue in - viewModel.update[title] = ScopeValue(description: value.description, enabled: newValue) + viewModel.update[title] = ScopeValue(id: value.id, name: value.name, description: value.description, enabled: newValue) })) { VStack(alignment: .leading, spacing: 4) { - Text(title) + Text(value.name) .foregroundColor(.primary) .font(.system(size: 14, weight: .semibold)) diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifySubscriptionsBuilder.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifySubscriptionsBuilder.swift index 1fa8f91a3..d2c9cd582 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/NotifySubscriptionsBuilder.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifySubscriptionsBuilder.swift @@ -36,7 +36,12 @@ class NotifySubscriptionsBuilder { private func buildScope(selectedScope: [String], availableScope: [NotifyConfig.NotificationType]) async throws -> [String: ScopeValue] { return availableScope.reduce(into: [:]) { - $0[$1.name] = ScopeValue(description: $1.name, enabled: selectedScope.contains($1.name)) + $0[$1.id] = ScopeValue( + id: $1.id, + name: $1.name, + description: $1.description, + enabled: selectedScope.contains($1.id) + ) } } } diff --git a/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_pushSubscribe/NotifySubscribeRequester.swift b/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_pushSubscribe/NotifySubscribeRequester.swift index eefba1ce6..ad5c0cee0 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_pushSubscribe/NotifySubscribeRequester.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/ProtocolEngine/wc_pushSubscribe/NotifySubscribeRequester.swift @@ -75,16 +75,14 @@ class NotifySubscribeRequester { } private func createJWTWrapper(dappPubKey: DIDKey, subscriptionAccount: Account, appDomain: String) async throws -> NotifySubscriptionPayload.Wrapper { - let config = try await notifyConfigProvider.resolveNotifyConfig(appDomain: appDomain) + let config = await notifyConfigProvider.resolveNotifyConfig(appDomain: appDomain) let app = DIDWeb(host: appDomain) let jwtPayload = NotifySubscriptionPayload( dappPubKey: dappPubKey, keyserver: keyserverURL, subscriptionAccount: subscriptionAccount, app: app, - scope: config.notificationTypes - .map { $0.name } - .joined(separator: " ") + scope: config.notificationTypes.map { $0.id }.joined(separator: " ") ) return try identityClient.signAndCreateWrapper( payload: jwtPayload, diff --git a/Sources/WalletConnectNotify/Types/DataStructures/NotifySubscription.swift b/Sources/WalletConnectNotify/Types/DataStructures/NotifySubscription.swift index 38676d60a..cc47fce34 100644 --- a/Sources/WalletConnectNotify/Types/DataStructures/NotifySubscription.swift +++ b/Sources/WalletConnectNotify/Types/DataStructures/NotifySubscription.swift @@ -15,10 +15,14 @@ public struct NotifySubscription: DatabaseObject { } public struct ScopeValue: Codable, Equatable { + public let id: String + public let name: String public let description: String public let enabled: Bool - public init(description: String, enabled: Bool) { + public init(id: String, name: String, description: String, enabled: Bool) { + self.id = id + self.name = name self.description = description self.enabled = enabled } From 33daa179f8c7595195abdd0e724b7d2f88c3392e Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 19 Oct 2023 06:30:38 +0800 Subject: [PATCH 48/49] NotifySubscription fixed --- Tests/NotifyTests/Stubs/NotifySubscription.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/NotifyTests/Stubs/NotifySubscription.swift b/Tests/NotifyTests/Stubs/NotifySubscription.swift index 7251c8477..3e9a1892e 100644 --- a/Tests/NotifyTests/Stubs/NotifySubscription.swift +++ b/Tests/NotifyTests/Stubs/NotifySubscription.swift @@ -13,7 +13,7 @@ extension NotifySubscription { account: account, relay: relay, metadata: metadata, - scope: ["test": ScopeValue(description: "desc", enabled: true)], + scope: ["test": ScopeValue(id: "id", name: "name", description: "desc", enabled: true)], expiry: expiry, symKey: symKey ) From cc7f36b2fa291995764b3526b11ce6faf68291f9 Mon Sep 17 00:00:00 2001 From: flypaper0 Date: Thu, 19 Oct 2023 01:47:57 +0200 Subject: [PATCH 49/49] Set User Agent --- Sources/WalletConnectRelay/PackageConfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/WalletConnectRelay/PackageConfig.json b/Sources/WalletConnectRelay/PackageConfig.json index 40d0cfc51..52e73e014 100644 --- a/Sources/WalletConnectRelay/PackageConfig.json +++ b/Sources/WalletConnectRelay/PackageConfig.json @@ -1 +1 @@ -{"version": "1.8.7"} +{"version": "1.9.0"}