From f4d9f4f43d89c768bba16533a53e9370a685c351 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 12 Sep 2022 09:23:06 +0200 Subject: [PATCH 001/175] savepoint --- Sources/WalletConnectPairing/PairingClient.swift | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 Sources/WalletConnectPairing/PairingClient.swift diff --git a/Sources/WalletConnectPairing/PairingClient.swift b/Sources/WalletConnectPairing/PairingClient.swift new file mode 100644 index 000000000..98c45a723 --- /dev/null +++ b/Sources/WalletConnectPairing/PairingClient.swift @@ -0,0 +1,6 @@ +import Foundation + +class PairingClient { + + +} From f59f7bfb24305afd6de14e18d128de0e1b9a5222 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 12 Sep 2022 09:55:55 +0200 Subject: [PATCH 002/175] savepoint --- .../WalletConnectPairing/PairingClient.swift | 28 +++++++++- .../Services/App/AppPairService.swift | 26 +++++++++ .../Services/Wallet/WalletPairService.swift | 38 +++++++++++++ .../Wallet/WalletRequestSubscriber.swift | 55 +++++++++++++++++++ 4 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 Sources/WalletConnectPairing/Services/App/AppPairService.swift create mode 100644 Sources/WalletConnectPairing/Services/Wallet/WalletPairService.swift create mode 100644 Sources/WalletConnectPairing/Services/Wallet/WalletRequestSubscriber.swift diff --git a/Sources/WalletConnectPairing/PairingClient.swift b/Sources/WalletConnectPairing/PairingClient.swift index 98c45a723..7c2b06efd 100644 --- a/Sources/WalletConnectPairing/PairingClient.swift +++ b/Sources/WalletConnectPairing/PairingClient.swift @@ -1,6 +1,32 @@ import Foundation +import WalletConnectUtils class PairingClient { + private let walletPairService: WalletPairService + private let appPairService: AppPairService + + + init(appPairService: AppPairService, + walletPairService: WalletPairService + ) { + self.appPairService = appPairService + self.walletPairService = walletPairService + } + /// For wallet to establish a pairing and receive an authentication request + /// Wallet should call this function in order to accept peer's pairing proposal and be able to subscribe for future authentication request. + /// - Parameter uri: Pairing URI that is commonly presented as a QR code by a dapp or delivered with universal linking. + /// + /// Throws Error: + /// - When URI is invalid format or missing params + /// - When topic is already in use + public func pair(uri: WalletConnectURI) async throws { + try await walletPairService.pair(uri) + } + + public func create() async throws -> WalletConnectURI { + return try await appPairService.create() + } + + public func addSubscriber() - } diff --git a/Sources/WalletConnectPairing/Services/App/AppPairService.swift b/Sources/WalletConnectPairing/Services/App/AppPairService.swift new file mode 100644 index 000000000..9d223d06a --- /dev/null +++ b/Sources/WalletConnectPairing/Services/App/AppPairService.swift @@ -0,0 +1,26 @@ +import Foundation +import WalletConnectKMS +import WalletConnectNetworking +import WalletConnectUtils + +actor AppPairService { + private let networkingInteractor: NetworkInteracting + private let kms: KeyManagementServiceProtocol + private let pairingStorage: WCPairingStorage + + init(networkingInteractor: NetworkInteracting, kms: KeyManagementServiceProtocol, pairingStorage: WCPairingStorage) { + self.networkingInteractor = networkingInteractor + self.kms = kms + self.pairingStorage = pairingStorage + } + + func create() async throws -> WalletConnectURI { + let topic = String.generateTopic() + try await networkingInteractor.subscribe(topic: topic) + let symKey = try! kms.createSymmetricKey(topic) + let pairing = WCPairing(topic: topic) + let uri = WalletConnectURI(topic: topic, symKey: symKey.hexRepresentation, relay: pairing.relay, api: .auth) + pairingStorage.setPairing(pairing) + return uri + } +} diff --git a/Sources/WalletConnectPairing/Services/Wallet/WalletPairService.swift b/Sources/WalletConnectPairing/Services/Wallet/WalletPairService.swift new file mode 100644 index 000000000..6dd6b5e40 --- /dev/null +++ b/Sources/WalletConnectPairing/Services/Wallet/WalletPairService.swift @@ -0,0 +1,38 @@ +import Foundation +import WalletConnectKMS +import WalletConnectNetworking +import WalletConnectUtils + +actor WalletPairService { + enum Errors: Error { + case pairingAlreadyExist + } + + private let networkingInteractor: NetworkInteracting + private let kms: KeyManagementServiceProtocol + private let pairingStorage: WCPairingStorage + + init(networkingInteractor: NetworkInteracting, + kms: KeyManagementServiceProtocol, + pairingStorage: WCPairingStorage) { + self.networkingInteractor = networkingInteractor + self.kms = kms + self.pairingStorage = pairingStorage + } + + func pair(_ uri: WalletConnectURI) async throws { + guard !hasPairing(for: uri.topic) else { + throw Errors.pairingAlreadyExist + } + var pairing = WCPairing(uri: uri) + try await networkingInteractor.subscribe(topic: pairing.topic) + let symKey = try SymmetricKey(hex: uri.symKey) + try kms.setSymmetricKey(symKey, for: pairing.topic) + pairing.activate() + pairingStorage.setPairing(pairing) + } + + func hasPairing(for topic: String) -> Bool { + return pairingStorage.hasPairing(forTopic: topic) + } +} diff --git a/Sources/WalletConnectPairing/Services/Wallet/WalletRequestSubscriber.swift b/Sources/WalletConnectPairing/Services/Wallet/WalletRequestSubscriber.swift new file mode 100644 index 000000000..b22de980d --- /dev/null +++ b/Sources/WalletConnectPairing/Services/Wallet/WalletRequestSubscriber.swift @@ -0,0 +1,55 @@ +import Foundation +import Combine +import JSONRPC +import WalletConnectUtils +import WalletConnectKMS +import WalletConnectNetworking + +protocol PairingRequestSubscriber { + func subscribeForRequest() +} + +class PushRequestSubscriber: PairingRequestSubscriber { + private let networkingInteractor: NetworkInteracting + private let kms: KeyManagementServiceProtocol + private var publishers = [AnyCancellable]() + var onRequest: ((AuthRequest) -> Void)? + + init(networkingInteractor: NetworkInteracting, + logger: ConsoleLogging, + kms: KeyManagementServiceProtocol) { + self.networkingInteractor = networkingInteractor + self.kms = kms + subscribeForRequest() + } + + func subscribeForRequest() { + + networkingInteractor.requestSubscription(on: AuthProtocolMethod.authRequest) + .sink { [unowned self] (payload: RequestSubscriptionPayload) in + + }.store(in: &publishers) + } +} + +enum PushProtocolMethod: String, ProtocolMethod { +case authRequest = "wc_pushRequest" + +var method: String { + return self.rawValue +} + +var requestTag: Int { + switch self { + case .authRequest: + return 3000 + } +} + +var responseTag: Int { + switch self { + case .authRequest: + return 3001 + } +} +} From f9f87f2a5a79314d627929151752576cad140e16 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 12 Sep 2022 14:56:18 +0200 Subject: [PATCH 003/175] savepoint --- Example/ExampleApp.xcodeproj/project.pbxproj | 4 + .../IntegrationTests/Auth/PairingTest.swift | 83 +++++++++++++++++++ .../WalletConnectPairing/PairingClient.swift | 25 ++++-- .../Push/PairingClientFactory.swift | 45 ++++++++++ .../Push/PushClient.swift | 42 ++++++++++ .../Push/PushProtocolMethod.swift | 26 ++++++ .../Push/PushRequestSubscriber.swift | 61 ++++++++++++++ .../Services/Wallet/WalletPairService.swift | 4 +- .../Wallet/WalletRequestSubscriber.swift | 55 ------------ 9 files changed, 283 insertions(+), 62 deletions(-) create mode 100644 Example/IntegrationTests/Auth/PairingTest.swift create mode 100644 Sources/WalletConnectPairing/Push/PairingClientFactory.swift create mode 100644 Sources/WalletConnectPairing/Push/PushClient.swift create mode 100644 Sources/WalletConnectPairing/Push/PushProtocolMethod.swift create mode 100644 Sources/WalletConnectPairing/Push/PushRequestSubscriber.swift delete mode 100644 Sources/WalletConnectPairing/Services/Wallet/WalletRequestSubscriber.swift diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index bafe7c5f1..6ee905a66 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -30,6 +30,7 @@ 8448F1D427E4726F0000B866 /* WalletConnect in Frameworks */ = {isa = PBXBuildFile; productRef = 8448F1D327E4726F0000B866 /* WalletConnect */; }; 84494388278D9C1B00CC26BB /* UIAlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84494387278D9C1B00CC26BB /* UIAlertController.swift */; }; 8460DCFC274F98A10081F94C /* RequestViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8460DCFB274F98A10081F94C /* RequestViewController.swift */; }; + 84AA01DD28CF2845005D48D8 /* PairingTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84AA01DC28CF2845005D48D8 /* PairingTest.swift */; }; 84CE641F27981DED00142511 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CE641E27981DED00142511 /* AppDelegate.swift */; }; 84CE642127981DED00142511 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CE642027981DED00142511 /* SceneDelegate.swift */; }; 84CE642827981DF000142511 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 84CE642727981DF000142511 /* Assets.xcassets */; }; @@ -219,6 +220,7 @@ 76B6E39E2807A3B6004DF775 /* WalletViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WalletViewController.swift; sourceTree = ""; }; 84494387278D9C1B00CC26BB /* UIAlertController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIAlertController.swift; sourceTree = ""; }; 8460DCFB274F98A10081F94C /* RequestViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestViewController.swift; sourceTree = ""; }; + 84AA01DC28CF2845005D48D8 /* PairingTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PairingTest.swift; sourceTree = ""; }; 84CE641C27981DED00142511 /* DApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; 84CE641E27981DED00142511 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 84CE642027981DED00142511 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; @@ -581,6 +583,7 @@ 84D2A66728A4F5260088AE09 /* Auth */ = { isa = PBXGroup; children = ( + 84AA01DC28CF2845005D48D8 /* PairingTest.swift */, 84D2A66528A4F51E0088AE09 /* AuthTests.swift */, ); path = Auth; @@ -1458,6 +1461,7 @@ 84FE684628ACDB4700C893FF /* RequestParams.swift in Sources */, 7694A5262874296A0001257E /* RegistryTests.swift in Sources */, A50C036528AAD32200FE72D3 /* ClientDelegate.swift in Sources */, + 84AA01DD28CF2845005D48D8 /* PairingTest.swift in Sources */, A5E03E0D28646AD200888481 /* RelayClientEndToEndTests.swift in Sources */, A501AC2728C8E59800CEAA42 /* URLConfig.swift in Sources */, A5E03DFA286465C700888481 /* SignClientTests.swift in Sources */, diff --git a/Example/IntegrationTests/Auth/PairingTest.swift b/Example/IntegrationTests/Auth/PairingTest.swift new file mode 100644 index 000000000..c5d6fee88 --- /dev/null +++ b/Example/IntegrationTests/Auth/PairingTest.swift @@ -0,0 +1,83 @@ +import Foundation +import XCTest +import WalletConnectUtils +@testable import WalletConnectKMS +import WalletConnectRelay +import Combine +import WalletConnectNetworking +import WalletConnectPairing + + +final class Pairingtests: XCTestCase { + var appPairingClient: PairingClient! + var walletPairingClient: PairingClient! + + private var publishers = [AnyCancellable]() + + override func setUp() { + appPairingClient = makeClient(prefix: "👻 App") + walletPairingClient = makeClient(prefix: "🤑 Wallet") + + let expectation = expectation(description: "Wait Clients Connected") + expectation.expectedFulfillmentCount = 2 + + appPairingClient.socketConnectionStatusPublisher.sink { status in + if status == .connected { + expectation.fulfill() + } + }.store(in: &publishers) + + walletPairingClient.socketConnectionStatusPublisher.sink { status in + if status == .connected { + expectation.fulfill() + } + }.store(in: &publishers) + + wait(for: [expectation], timeout: 5) + } + + + func makeClient(prefix: String) -> PairingClient { + let logger = ConsoleLogger(suffix: prefix, loggingLevel: .debug) + let projectId = "3ca2919724fbfa5456a25194e369a8b4" + let keychain = KeychainStorageMock() + let relayClient = RelayClient(relayHost: URLConfig.relayHost, projectId: projectId, keychainStorage: keychain, socketFactory: SocketFactory(), logger: logger) + + let pairingClient = PairingClientFactory.create(logger: logger, keyValueStorage: RuntimeKeyValueStorage(), keychainStorage: keychain, relayClient: relayClient) + return pairingClient + } + + func makePushClient() -> PushClient { + let logger = ConsoleLogger(suffix: "", loggingLevel: .debug) + let projectId = "3ca2919724fbfa5456a25194e369a8b4" + let keychain = KeychainStorageMock() + let relayClient = RelayClient(relayHost: URLConfig.relayHost, projectId: projectId, keychainStorage: keychain, socketFactory: SocketFactory(), logger: logger) + return PushClientFactory.create(logger: logger, keyValueStorage: RuntimeKeyValueStorage(), keychainStorage: keychain, relayClient: relayClient) + } + + func testProposePushOnPairing() async { + let exp = expectation(description: "") + let appPushClient = makePushClient() + let walletPushClient = makePushClient() + + appPairingClient.configure(with: [appPushClient]) + + walletPairingClient.configure(with: [walletPushClient, KYC]) + + let uri = try! await appPairingClient.create() + + try! await walletPairingClient.pair(uri: uri) + + try! await appPushClient.propose(topic: uri.topic) + + walletPushClient.proposalPublisher.sink { _ in + exp.fulfill() + print("received push proposal") + }.store(in: &publishers) + + wait(for: [exp], timeout: 2) + + } + +} + diff --git a/Sources/WalletConnectPairing/PairingClient.swift b/Sources/WalletConnectPairing/PairingClient.swift index 7c2b06efd..10a760784 100644 --- a/Sources/WalletConnectPairing/PairingClient.swift +++ b/Sources/WalletConnectPairing/PairingClient.swift @@ -1,16 +1,22 @@ import Foundation import WalletConnectUtils +import WalletConnectRelay +import Combine -class PairingClient { +public class PairingClient { private let walletPairService: WalletPairService private let appPairService: AppPairService - - + public let socketConnectionStatusPublisher: AnyPublisher + let logger: ConsoleLogging init(appPairService: AppPairService, - walletPairService: WalletPairService + logger: ConsoleLogging, + walletPairService: WalletPairService, + socketConnectionStatusPublisher: AnyPublisher ) { self.appPairService = appPairService self.walletPairService = walletPairService + self.socketConnectionStatusPublisher = socketConnectionStatusPublisher + self.logger = logger } /// For wallet to establish a pairing and receive an authentication request /// Wallet should call this function in order to accept peer's pairing proposal and be able to subscribe for future authentication request. @@ -27,6 +33,15 @@ class PairingClient { return try await appPairService.create() } - public func addSubscriber() + public func configure(with paringables: [Paringable]) { + var p = paringables.first! + + p.pairingRequestSubscriber = PairingRequestSubscriber(networkingInteractor: walletPairService.networkingInteractor, logger: logger, kms: walletPairService.kms, protocolMethod: p.protocolMethod) + + + p.pairingRequester = PairingRequester(networkingInteractor: walletPairService.networkingInteractor, kms: walletPairService.kms, logger: logger, protocolMethod: p.protocolMethod) + } + + } diff --git a/Sources/WalletConnectPairing/Push/PairingClientFactory.swift b/Sources/WalletConnectPairing/Push/PairingClientFactory.swift new file mode 100644 index 000000000..8410ee0a3 --- /dev/null +++ b/Sources/WalletConnectPairing/Push/PairingClientFactory.swift @@ -0,0 +1,45 @@ +import Foundation +import WalletConnectRelay +import WalletConnectUtils +import WalletConnectKMS +import WalletConnectNetworking + +public struct PairingClientFactory { + public static func create(logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, relayClient: RelayClient) -> PairingClient { + let kms = KeyManagementService(keychain: keychainStorage) + let serializer = Serializer(kms: kms) + let kv = RuntimeKeyValueStorage() + let historyStorage = CodableStore(defaults: kv, identifier: "") + let history = RPCHistory(keyValueStore: historyStorage) + + + let networkingInt = NetworkingInteractor(relayClient: relayClient, serializer: serializer, logger: logger, rpcHistory: history) + let pairingStore = PairingStorage(storage: SequenceStore(store: .init(defaults: kv, identifier: ""))) + + + let appPairService = AppPairService(networkingInteractor: networkingInt, kms: kms, pairingStorage: pairingStore) + + let walletPaS = WalletPairService(networkingInteractor: networkingInt, kms: kms, pairingStorage: pairingStore) + + return PairingClient(appPairService: appPairService, logger: logger, walletPairService: walletPaS, socketConnectionStatusPublisher: relayClient.socketConnectionStatusPublisher) + } +} + + + +public struct PushClientFactory { + public static func create(logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, relayClient: RelayClient) -> PushClient { + let kms = KeyManagementService(keychain: keychainStorage) + let serializer = Serializer(kms: kms) + let kv = RuntimeKeyValueStorage() + let historyStorage = CodableStore(defaults: kv, identifier: "") + let history = RPCHistory(keyValueStore: historyStorage) + + + let networkingInt = NetworkingInteractor(relayClient: relayClient, serializer: serializer, logger: logger, rpcHistory: history) + + + + return PushClient(networkingInteractor: networkingInt, logger: logger, kms: kms) + } +} diff --git a/Sources/WalletConnectPairing/Push/PushClient.swift b/Sources/WalletConnectPairing/Push/PushClient.swift new file mode 100644 index 000000000..15164aa79 --- /dev/null +++ b/Sources/WalletConnectPairing/Push/PushClient.swift @@ -0,0 +1,42 @@ +import Foundation +import WalletConnectKMS +import WalletConnectUtils +import WalletConnectNetworking +import Combine + +public class PushClient: Paringable { + + public var protocolMethod: ProtocolMethod + public var proposalPublisher: AnyPublisher { + proposalPublisherSubject.eraseToAnyPublisher() + } + private let proposalPublisherSubject = PassthroughSubject() + + public var pairingRequestSubscriber: PairingRequestSubscriber! { + didSet { + handleProposal() + } + } + + public var pairingRequester: PairingRequester! + + public let logger: ConsoleLogging + + init(networkingInteractor: NetworkInteracting, + logger: ConsoleLogging, + kms: KeyManagementServiceProtocol) { + self.logger = logger + + protocolMethod = PushProtocolMethod.propose + } + + func handleProposal() { + pairingRequestSubscriber.onRequest = { [unowned self] _ in + logger.debug("Push: received proposal") + } + } + + public func propose(topic: String) async throws { + try await pairingRequester.request(topic: topic) + } +} diff --git a/Sources/WalletConnectPairing/Push/PushProtocolMethod.swift b/Sources/WalletConnectPairing/Push/PushProtocolMethod.swift new file mode 100644 index 000000000..0b6772da0 --- /dev/null +++ b/Sources/WalletConnectPairing/Push/PushProtocolMethod.swift @@ -0,0 +1,26 @@ +import Foundation +import WalletConnectNetworking + +enum PushProtocolMethod: String, ProtocolMethod { + case propose = "wc_pushPropose" + + var method: String { + return self.rawValue + } + + var requestTag: Int { + switch self { + case .propose: + return 3000 + } + } + + var responseTag: Int { + switch self { + case .propose: + return 3001 + } + } +} + +struct PushRequestParams: Codable {} diff --git a/Sources/WalletConnectPairing/Push/PushRequestSubscriber.swift b/Sources/WalletConnectPairing/Push/PushRequestSubscriber.swift new file mode 100644 index 000000000..d0f04445f --- /dev/null +++ b/Sources/WalletConnectPairing/Push/PushRequestSubscriber.swift @@ -0,0 +1,61 @@ +import Foundation +import Combine +import JSONRPC +import WalletConnectUtils +import WalletConnectKMS +import WalletConnectNetworking + +public protocol Paringable { + var protocolMethod: ProtocolMethod { get set } + var pairingRequestSubscriber: PairingRequestSubscriber! {get set} + var pairingRequester: PairingRequester! {get set} +} + +public class PairingRequestSubscriber { + private let networkingInteractor: NetworkInteracting + private let kms: KeyManagementServiceProtocol + private var publishers = [AnyCancellable]() + var onRequest: ((RequestSubscriptionPayload) -> Void)? + let protocolMethod: ProtocolMethod + + init(networkingInteractor: NetworkInteracting, + logger: ConsoleLogging, + kms: KeyManagementServiceProtocol, + protocolMethod: ProtocolMethod) { + self.networkingInteractor = networkingInteractor + self.kms = kms + self.protocolMethod = protocolMethod + subscribeForRequest() + } + + func subscribeForRequest() { + + networkingInteractor.requestSubscription(on: protocolMethod) + .sink { [unowned self] (payload: RequestSubscriptionPayload) in + onRequest?(payload) + }.store(in: &publishers) + } +} + +public class PairingRequester { + private let networkingInteractor: NetworkInteracting + private let kms: KeyManagementServiceProtocol + private let logger: ConsoleLogging + let protocolMethod: ProtocolMethod + + init(networkingInteractor: NetworkInteracting, + kms: KeyManagementServiceProtocol, + logger: ConsoleLogging, + protocolMethod: ProtocolMethod) { + self.networkingInteractor = networkingInteractor + self.kms = kms + self.logger = logger + self.protocolMethod = protocolMethod + } + + func request(topic: String) async throws { + let request = RPCRequest(method: protocolMethod.method, params: AnyCodable("")) + + try await networkingInteractor.requestNetworkAck(request, topic: topic, tag: PushProtocolMethod.propose.requestTag) + } +} diff --git a/Sources/WalletConnectPairing/Services/Wallet/WalletPairService.swift b/Sources/WalletConnectPairing/Services/Wallet/WalletPairService.swift index 6dd6b5e40..199b5a9a9 100644 --- a/Sources/WalletConnectPairing/Services/Wallet/WalletPairService.swift +++ b/Sources/WalletConnectPairing/Services/Wallet/WalletPairService.swift @@ -8,8 +8,8 @@ actor WalletPairService { case pairingAlreadyExist } - private let networkingInteractor: NetworkInteracting - private let kms: KeyManagementServiceProtocol + let networkingInteractor: NetworkInteracting + let kms: KeyManagementServiceProtocol private let pairingStorage: WCPairingStorage init(networkingInteractor: NetworkInteracting, diff --git a/Sources/WalletConnectPairing/Services/Wallet/WalletRequestSubscriber.swift b/Sources/WalletConnectPairing/Services/Wallet/WalletRequestSubscriber.swift deleted file mode 100644 index b22de980d..000000000 --- a/Sources/WalletConnectPairing/Services/Wallet/WalletRequestSubscriber.swift +++ /dev/null @@ -1,55 +0,0 @@ -import Foundation -import Combine -import JSONRPC -import WalletConnectUtils -import WalletConnectKMS -import WalletConnectNetworking - -protocol PairingRequestSubscriber { - func subscribeForRequest() -} - -class PushRequestSubscriber: PairingRequestSubscriber { - private let networkingInteractor: NetworkInteracting - private let kms: KeyManagementServiceProtocol - private var publishers = [AnyCancellable]() - var onRequest: ((AuthRequest) -> Void)? - - init(networkingInteractor: NetworkInteracting, - logger: ConsoleLogging, - kms: KeyManagementServiceProtocol) { - self.networkingInteractor = networkingInteractor - self.kms = kms - subscribeForRequest() - } - - func subscribeForRequest() { - - networkingInteractor.requestSubscription(on: AuthProtocolMethod.authRequest) - .sink { [unowned self] (payload: RequestSubscriptionPayload) in - - }.store(in: &publishers) - } -} - -enum PushProtocolMethod: String, ProtocolMethod { -case authRequest = "wc_pushRequest" - -var method: String { - return self.rawValue -} - -var requestTag: Int { - switch self { - case .authRequest: - return 3000 - } -} - -var responseTag: Int { - switch self { - case .authRequest: - return 3001 - } -} -} From 395962526662dda2e5f1e0a8f6eb55c9513ae14e Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Tue, 6 Sep 2022 23:16:50 +0300 Subject: [PATCH 004/175] SessionEngine --- Package.swift | 2 +- .../NetworkInteracting.swift | 4 +- .../NetworkInteractor.swift | 14 +- .../RequestSubscriptionPayload.swift | 2 +- .../ResponseSubscriptionPayload.swift | 2 +- .../SubscriptionPayload.swift | 7 + .../Engine/Common/ApproveEngine.swift | 5 +- .../Engine/Common/SessionEngine.swift | 171 +++--- .../NetworkInteractor/NetworkInteractor.swift | 518 +++++++++--------- .../NetworkInteractor/NetworkRelaying.swift | 38 +- Sources/WalletConnectSign/Reason.swift | 11 - Sources/WalletConnectSign/Request.swift | 9 +- .../WCRequestSubscriptionPayload.swift | 11 - .../WalletConnectSign/Types/ReasonCode.swift | 4 +- .../WalletConnectSign/Types/WCMethod.swift | 37 -- .../WalletConnectSign/Types/WCRequest.swift | 236 +++----- .../WalletConnectSign/Types/WCResponse.swift | 14 - 17 files changed, 473 insertions(+), 612 deletions(-) create mode 100644 Sources/WalletConnectNetworking/SubscriptionPayload.swift delete mode 100644 Sources/WalletConnectSign/Reason.swift delete mode 100644 Sources/WalletConnectSign/Subscription/WCRequestSubscriptionPayload.swift delete mode 100644 Sources/WalletConnectSign/Types/WCMethod.swift delete mode 100644 Sources/WalletConnectSign/Types/WCResponse.swift diff --git a/Package.swift b/Package.swift index 7640db6ad..a991ee8bf 100644 --- a/Package.swift +++ b/Package.swift @@ -32,7 +32,7 @@ let package = Package( targets: [ .target( name: "WalletConnectSign", - dependencies: ["WalletConnectRelay", "WalletConnectUtils", "WalletConnectKMS", "WalletConnectPairing"], + dependencies: ["WalletConnectNetworking", "WalletConnectPairing"], path: "Sources/WalletConnectSign"), .target( name: "Chat", diff --git a/Sources/WalletConnectNetworking/NetworkInteracting.swift b/Sources/WalletConnectNetworking/NetworkInteracting.swift index 8dd02d382..67d5e9673 100644 --- a/Sources/WalletConnectNetworking/NetworkInteracting.swift +++ b/Sources/WalletConnectNetworking/NetworkInteracting.swift @@ -15,11 +15,11 @@ public protocol NetworkInteracting { func respondError(topic: String, requestId: RPCID, tag: Int, reason: Reason, envelopeType: Envelope.EnvelopeType) async throws func requestSubscription( - on request: ProtocolMethod + on request: ProtocolMethod? ) -> AnyPublisher, Never> func responseSubscription( - on request: ProtocolMethod + on request: ProtocolMethod? ) -> AnyPublisher, Never> func responseErrorSubscription(on request: ProtocolMethod) -> AnyPublisher diff --git a/Sources/WalletConnectNetworking/NetworkInteractor.swift b/Sources/WalletConnectNetworking/NetworkInteractor.swift index 13859ead6..f5ed34273 100644 --- a/Sources/WalletConnectNetworking/NetworkInteractor.swift +++ b/Sources/WalletConnectNetworking/NetworkInteractor.swift @@ -56,9 +56,12 @@ public class NetworkingInteractor: NetworkInteracting { } } - public func requestSubscription(on request: ProtocolMethod) -> AnyPublisher, Never> { + public func requestSubscription(on request: ProtocolMethod?) -> AnyPublisher, Never> { return requestPublisher - .filter { $0.request.method == request.method } + .filter { rpcRequest in + guard let request = request else { return true } + return rpcRequest.request.method == request.method + } .compactMap { topic, rpcRequest in guard let id = rpcRequest.id, let request = try? rpcRequest.params?.get(Request.self) else { return nil } return RequestSubscriptionPayload(id: id, topic: topic, request: request) @@ -66,9 +69,12 @@ public class NetworkingInteractor: NetworkInteracting { .eraseToAnyPublisher() } - public func responseSubscription(on request: ProtocolMethod) -> AnyPublisher, Never> { + public func responseSubscription(on request: ProtocolMethod?) -> AnyPublisher, Never> { return responsePublisher - .filter { $0.request.method == request.method } + .filter { rpcRequest in + guard let request = request else { return true } + return rpcRequest.request.method == request.method + } .compactMap { topic, rpcRequest, rpcResponse in guard let id = rpcRequest.id, diff --git a/Sources/WalletConnectNetworking/RequestSubscriptionPayload.swift b/Sources/WalletConnectNetworking/RequestSubscriptionPayload.swift index fa6b0e8db..d8767d692 100644 --- a/Sources/WalletConnectNetworking/RequestSubscriptionPayload.swift +++ b/Sources/WalletConnectNetworking/RequestSubscriptionPayload.swift @@ -1,7 +1,7 @@ import Foundation import JSONRPC -public struct RequestSubscriptionPayload: Codable { +public struct RequestSubscriptionPayload: Codable, SubscriptionPayload { public let id: RPCID public let topic: String public let request: Request diff --git a/Sources/WalletConnectNetworking/ResponseSubscriptionPayload.swift b/Sources/WalletConnectNetworking/ResponseSubscriptionPayload.swift index 21043eb9d..93b21538a 100644 --- a/Sources/WalletConnectNetworking/ResponseSubscriptionPayload.swift +++ b/Sources/WalletConnectNetworking/ResponseSubscriptionPayload.swift @@ -1,7 +1,7 @@ import Foundation import JSONRPC -public struct ResponseSubscriptionPayload { +public struct ResponseSubscriptionPayload: SubscriptionPayload { public let id: RPCID public let topic: String public let request: Request diff --git a/Sources/WalletConnectNetworking/SubscriptionPayload.swift b/Sources/WalletConnectNetworking/SubscriptionPayload.swift new file mode 100644 index 000000000..ad5e60eab --- /dev/null +++ b/Sources/WalletConnectNetworking/SubscriptionPayload.swift @@ -0,0 +1,7 @@ +import Foundation +import JSONRPC + +public protocol SubscriptionPayload { + var id: RPCID { get } + var topic: String { get } +} diff --git a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift index 229e2e698..a5a310a1d 100644 --- a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift @@ -3,6 +3,7 @@ import Combine import WalletConnectUtils import WalletConnectKMS import WalletConnectPairing +import WalletConnectNetworking final class ApproveEngine { enum Errors: Error { @@ -180,10 +181,10 @@ private extension ApproveEngine { }.store(in: &publishers) } - func respondError(payload: WCRequestSubscriptionPayload, reason: ReasonCode) { + func respondError(payload: SubscriptionPayload, reason: ReasonCode) { Task { do { - try await networkingInteractor.respondError(payload: payload, reason: reason) + try await networkingInteractor.respondError(topic: payload.topic, requestId: payload.id, tag: <#T##Int#>, reason: reason) } catch { logger.error("Respond Error failed with: \(error.localizedDescription)") } diff --git a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift index a1197c8e1..df321e01a 100644 --- a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift @@ -1,11 +1,13 @@ import Foundation import Combine +import JSONRPC import WalletConnectUtils import WalletConnectKMS +import WalletConnectNetworking final class SessionEngine { enum Errors: Error { - case respondError(payload: WCRequestSubscriptionPayload, reason: ReasonCode) + case respondError(payload: SubscriptionPayload, reason: ReasonCode) case sessionNotFound(topic: String) } @@ -49,15 +51,16 @@ final class SessionEngine { logger.debug("Could not find session to ping for topic \(topic)") return } - networkingInteractor.requestPeerResponse(.wcSessionPing, onTopic: topic) { [unowned self] result in - switch result { - case .success: - logger.debug("Did receive ping response") - completion(.success(())) - case .failure(let error): - logger.debug("error: \(error)") - } - } +// TODO: Ping disabled +// networkingInteractor.requestPeerResponse(.wcSessionPing, onTopic: topic) { [unowned self] result in +// switch result { +// case .success: +// logger.debug("Did receive ping response") +// completion(.success(())) +// case .failure(let error): +// logger.debug("error: \(error)") +// } +// } } func request(_ request: Request) async throws { @@ -71,14 +74,18 @@ final class SessionEngine { } let chainRequest = SessionType.RequestParams.Request(method: request.method, params: request.params) let sessionRequestParams = SessionType.RequestParams(request: chainRequest, chainId: request.chainId) - try await networkingInteractor.request(.wcSessionRequest(sessionRequestParams), onTopic: request.topic) + + let payload = WCRequest.sessionRequest(sessionRequestParams) + let rpcRequest = RPCRequest(method: WCRequest.Method.sessionRequest.method, params: payload) + try await networkingInteractor.request(rpcRequest, topic: request.topic, tag: WCRequest.Method.sessionRequest.requestTag) } func respondSessionRequest(topic: String, response: JsonRpcResult) async throws { guard sessionStore.hasSession(forTopic: topic) else { throw Errors.sessionNotFound(topic: topic) } - try await networkingInteractor.respond(topic: topic, response: response, tag: 1109) // FIXME: Hardcoded tag + // TODO: ??? +// try await networkingInteractor.respond(topic: topic, response: response, tag: 1109) // FIXME: Hardcoded tag } func emit(topic: String, event: SessionType.EventParams.Event, chainId: Blockchain) async throws { @@ -90,7 +97,8 @@ final class SessionEngine { throw WalletConnectError.invalidEvent } let params = SessionType.EventParams(event: event, chainId: chainId) - try await networkingInteractor.request(.wcSessionEvent(params), onTopic: topic) + let rpcRequest = RPCRequest(method: WCRequest.Method.sessionEvent.method, params: params) + try await networkingInteractor.request(rpcRequest, topic: topic, tag: WCRequest.Method.sessionEvent.requestTag) } } @@ -99,100 +107,113 @@ final class SessionEngine { private extension SessionEngine { func setupNetworkingSubscriptions() { - networkingInteractor.wcRequestPublisher.sink { [unowned self] subscriptionPayload in - do { - switch subscriptionPayload.wcRequest.params { - case .sessionDelete(let deleteParams): - try onSessionDelete(subscriptionPayload, deleteParams: deleteParams) - case .sessionRequest(let sessionRequestParams): - try onSessionRequest(subscriptionPayload, payloadParams: sessionRequestParams) + networkingInteractor.socketConnectionStatusPublisher + .sink { [unowned self] status in + guard status == .connected else { return } + sessionStore.getAll() + .forEach { session in + Task { try await networkingInteractor.subscribe(topic: session.topic) } + } + } + .store(in: &publishers) + + networkingInteractor.requestSubscription(on: nil) + .sink { [unowned self] (payload: RequestSubscriptionPayload) in + switch payload.request { + case .sessionDelete(let params): + onSessionDelete(payload, params: params) + case .sessionRequest(let params): + onSessionRequest(payload, params: params) case .sessionPing: - onSessionPing(subscriptionPayload) - case .sessionEvent(let eventParams): - try onSessionEvent(subscriptionPayload, eventParams: eventParams) + onSessionPing(payload) + case .sessionEvent(let params): + onSessionEvent(payload, params: params) default: return } - } catch Errors.respondError(let payload, let reason) { - respondError(payload: payload, reason: reason) - } catch { - logger.error("Unexpected Error: \(error.localizedDescription)") } - }.store(in: &publishers) - - networkingInteractor.transportConnectionPublisher - .sink { [unowned self] (_) in - let topics = sessionStore.getAll().map {$0.topic} - topics.forEach { topic in Task { try? await networkingInteractor.subscribe(topic: topic) } } - }.store(in: &publishers) - - networkingInteractor.responsePublisher - .sink { [unowned self] response in - self.handleResponse(response) - }.store(in: &publishers) + .store(in: &publishers) + + networkingInteractor.responseSubscription(on: nil) + .sink { [unowned self] (payload: ResponseSubscriptionPayload) in + switch payload.request { + case .sessionRequest(let params): + // TODO: ??? Chain ID from request is ok? + // Need to check absolute string + let response = Response(topic: payload.topic, chainId: params.chainId.absoluteString, result: payload.response) + onSessionResponse?(response) + default: + break + } + } + .store(in: &publishers) } - func respondError(payload: WCRequestSubscriptionPayload, reason: ReasonCode) { + func respondError(payload: SubscriptionPayload, reason: ReasonCode, tag: Int) { Task { do { - try await networkingInteractor.respondError(payload: payload, reason: reason) + try await networkingInteractor.respondError(topic: payload.topic, requestId: payload.id, tag: tag, reason: reason) } catch { logger.error("Respond Error failed with: \(error.localizedDescription)") } } } - func onSessionDelete(_ payload: WCRequestSubscriptionPayload, deleteParams: SessionType.DeleteParams) throws { + func onSessionDelete(_ payload: SubscriptionPayload, params: SessionType.DeleteParams) { + let tag = WCRequest.Method.sessionDelete.responseTag let topic = payload.topic guard sessionStore.hasSession(forTopic: topic) else { - throw Errors.respondError(payload: payload, reason: .noSessionForTopic) + return respondError(payload: payload, reason: .noSessionForTopic, tag: tag) } sessionStore.delete(topic: topic) networkingInteractor.unsubscribe(topic: topic) - networkingInteractor.respondSuccess(for: payload) - onSessionDelete?(topic, deleteParams) + Task(priority: .high) { + try await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, tag: tag) + } + onSessionDelete?(topic, params) } - func onSessionRequest(_ payload: WCRequestSubscriptionPayload, payloadParams: SessionType.RequestParams) throws { + func onSessionRequest(_ payload: SubscriptionPayload, params: SessionType.RequestParams) { + let tag = WCRequest.Method.sessionRequest.responseTag let topic = payload.topic - let jsonRpcRequest = JSONRPCRequest(id: payload.wcRequest.id, method: payloadParams.request.method, params: payloadParams.request.params) let request = Request( - id: jsonRpcRequest.id, - topic: topic, - method: jsonRpcRequest.method, - params: jsonRpcRequest.params, - chainId: payloadParams.chainId) + id: payload.id, + topic: payload.topic, + method: WCRequest.Method.sessionRequest.method, + params: params, + chainId: params.chainId) guard let session = sessionStore.getSession(forTopic: topic) else { - throw Errors.respondError(payload: payload, reason: .noSessionForTopic) + return respondError(payload: payload, reason: .noSessionForTopic, tag: tag) } - let chain = request.chainId - guard session.hasNamespace(for: chain) else { - throw Errors.respondError(payload: payload, reason: .unauthorizedChain) + guard session.hasNamespace(for: request.chainId) else { + return respondError(payload: payload, reason: .unauthorizedChain, tag: tag) } - guard session.hasPermission(forMethod: request.method, onChain: chain) else { - throw Errors.respondError(payload: payload, reason: .unauthorizedMethod(request.method)) + guard session.hasPermission(forMethod: request.method, onChain: request.chainId) else { + return respondError(payload: payload, reason: .unauthorizedMethod(request.method), tag: tag) } onSessionRequest?(request) } - func onSessionPing(_ payload: WCRequestSubscriptionPayload) { - networkingInteractor.respondSuccess(for: payload) + func onSessionPing(_ payload: SubscriptionPayload) { + Task(priority: .high) { + try await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, tag: WCRequest.Method.sessionPing.responseTag) + } } - func onSessionEvent(_ payload: WCRequestSubscriptionPayload, eventParams: SessionType.EventParams) throws { - let event = eventParams.event + func onSessionEvent(_ payload: SubscriptionPayload, params: SessionType.EventParams) { + let tag = WCRequest.Method.sessionEvent.responseTag + let event = params.event let topic = payload.topic guard let session = sessionStore.getSession(forTopic: topic) else { - throw Errors.respondError(payload: payload, reason: .noSessionForTopic) + return respondError(payload: payload, reason: .noSessionForTopic, tag: tag) + } + guard session.peerIsController, session.hasPermission(forEvent: event.name, onChain: params.chainId) else { + return respondError(payload: payload, reason: .unauthorizedEvent(event.name), tag: tag) } - guard - session.peerIsController, - session.hasPermission(forEvent: event.name, onChain: eventParams.chainId) - else { - throw Errors.respondError(payload: payload, reason: .unauthorizedEvent(event.name)) + Task(priority: .high) { + try await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, tag: tag) } - networkingInteractor.respondSuccess(for: payload) - onEventReceived?(topic, event.publicRepresentation(), eventParams.chainId) + onEventReceived?(topic, event.publicRepresentation(), params.chainId) } func setupExpirationSubscriptions() { @@ -201,14 +222,4 @@ private extension SessionEngine { self?.kms.deleteAgreementSecret(for: session.topic) } } - - func handleResponse(_ response: WCResponse) { - switch response.requestParams { - case .sessionRequest: - let response = Response(topic: response.topic, chainId: response.chainId, result: response.result) - onSessionResponse?(response) - default: - break - } - } } diff --git a/Sources/WalletConnectSign/NetworkInteractor/NetworkInteractor.swift b/Sources/WalletConnectSign/NetworkInteractor/NetworkInteractor.swift index 3389771b3..f23cc922f 100644 --- a/Sources/WalletConnectSign/NetworkInteractor/NetworkInteractor.swift +++ b/Sources/WalletConnectSign/NetworkInteractor/NetworkInteractor.swift @@ -1,259 +1,259 @@ -import Foundation -import Combine -import WalletConnectUtils -import WalletConnectKMS - -protocol NetworkInteracting: AnyObject { - var transportConnectionPublisher: AnyPublisher {get} - var wcRequestPublisher: AnyPublisher {get} - var responsePublisher: AnyPublisher {get} - /// Completes when request sent from a networking client - func request(_ wcMethod: WCMethod, onTopic topic: String) async throws - /// Completes with an acknowledgement from the relay network - func requestNetworkAck(_ wcMethod: WCMethod, onTopic topic: String, completion: @escaping ((Error?) -> Void)) - /// Completes with a peer response - func requestPeerResponse(_ wcMethod: WCMethod, onTopic topic: String, completion: ((Result, JSONRPCErrorResponse>) -> Void)?) - func respond(topic: String, response: JsonRpcResult, tag: Int) async throws - func respondSuccess(payload: WCRequestSubscriptionPayload) async throws - func respondSuccess(for payload: WCRequestSubscriptionPayload) - func respondError(payload: WCRequestSubscriptionPayload, reason: ReasonCode) async throws - func subscribe(topic: String) async throws - func unsubscribe(topic: String) -} - -extension NetworkInteracting { - func request(_ wcMethod: WCMethod, onTopic topic: String) { - requestPeerResponse(wcMethod, onTopic: topic, completion: nil) - } -} - -class NetworkInteractor: NetworkInteracting { - - private var publishers = Set() - - private var relayClient: NetworkRelaying - private let serializer: Serializing - private let jsonRpcHistory: JsonRpcHistoryRecording - - private let transportConnectionPublisherSubject = PassthroughSubject() - private let responsePublisherSubject = PassthroughSubject() - private let wcRequestPublisherSubject = PassthroughSubject() - - var transportConnectionPublisher: AnyPublisher { - transportConnectionPublisherSubject.eraseToAnyPublisher() - } - var wcRequestPublisher: AnyPublisher { - wcRequestPublisherSubject.eraseToAnyPublisher() - } - var responsePublisher: AnyPublisher { - responsePublisherSubject.eraseToAnyPublisher() - } - - let logger: ConsoleLogging - - init(relayClient: NetworkRelaying, - serializer: Serializing, - logger: ConsoleLogging, - jsonRpcHistory: JsonRpcHistoryRecording) { - self.relayClient = relayClient - self.serializer = serializer - self.logger = logger - self.jsonRpcHistory = jsonRpcHistory - setUpPublishers() - } - - func request(_ wcMethod: WCMethod, onTopic topic: String) async throws { - try await request(topic: topic, payload: wcMethod.asRequest()) - } - - /// Completes when networking client sends a request - func request(topic: String, payload: WCRequest) async throws { - try jsonRpcHistory.set(topic: topic, request: payload, chainId: getChainId(payload)) - let message = try serializer.serialize(topic: topic, encodable: payload) - let prompt = shouldPrompt(payload.method) - try await relayClient.publish(topic: topic, payload: message, tag: payload.tag, prompt: prompt) - } - - func requestPeerResponse(_ wcMethod: WCMethod, onTopic topic: String, completion: ((Result, JSONRPCErrorResponse>) -> Void)?) { - let payload = wcMethod.asRequest() - do { - try jsonRpcHistory.set(topic: topic, request: payload, chainId: getChainId(payload)) - let message = try serializer.serialize(topic: topic, encodable: payload) - let prompt = shouldPrompt(payload.method) - relayClient.publish(topic: topic, payload: message, tag: payload.tag, prompt: prompt) { [weak self] error in - guard let self = self else {return} - if let error = error { - self.logger.error(error) - } else { - var cancellable: AnyCancellable! - cancellable = self.responsePublisher - .filter {$0.result.id == payload.id} - .sink { (response) in - cancellable.cancel() - self.logger.debug("WC Relay - received response on topic: \(topic)") - switch response.result { - case .response(let response): - completion?(.success(response)) - case .error(let error): - self.logger.debug("Request error: \(error)") - completion?(.failure(error)) - } - } - } - } - } catch WalletConnectError.internal(.jsonRpcDuplicateDetected) { - logger.info("Info: Json Rpc Duplicate Detected") - } catch { - logger.error(error) - } - } - - /// Completes with an acknowledgement from the relay network. - /// completes with error if networking client was not able to send a message - func requestNetworkAck(_ wcMethod: WCMethod, onTopic topic: String, completion: @escaping ((Error?) -> Void)) { - do { - let payload = wcMethod.asRequest() - try jsonRpcHistory.set(topic: topic, request: payload, chainId: getChainId(payload)) - let message = try serializer.serialize(topic: topic, encodable: payload) - let prompt = shouldPrompt(payload.method) - relayClient.publish(topic: topic, payload: message, tag: payload.tag, prompt: prompt) { error in - completion(error) - } - } catch WalletConnectError.internal(.jsonRpcDuplicateDetected) { - logger.info("Info: Json Rpc Duplicate Detected") - } catch { - logger.error(error) - } - } - - func respond(topic: String, response: JsonRpcResult, tag: Int) async throws { - _ = try jsonRpcHistory.resolve(response: response) - - let message = try serializer.serialize(topic: topic, encodable: response.value) - logger.debug("Responding....topic: \(topic)") - - do { - try await relayClient.publish(topic: topic, payload: message, tag: tag, prompt: false) - } catch WalletConnectError.internal(.jsonRpcDuplicateDetected) { - logger.info("Info: Json Rpc Duplicate Detected") - } - } - - func respondSuccess(payload: WCRequestSubscriptionPayload) async throws { - let response = JSONRPCResponse(id: payload.wcRequest.id, result: AnyCodable(true)) - try await respond(topic: payload.topic, response: JsonRpcResult.response(response), tag: payload.wcRequest.responseTag) - } - - func respondError(payload: WCRequestSubscriptionPayload, reason: ReasonCode) async throws { - let response = JSONRPCErrorResponse(id: payload.wcRequest.id, error: JSONRPCErrorResponse.Error(code: reason.code, message: reason.message)) - try await respond(topic: payload.topic, response: JsonRpcResult.error(response), tag: payload.wcRequest.responseTag) - } - - // TODO: Move to async - func respondSuccess(for payload: WCRequestSubscriptionPayload) { - Task(priority: .background) { - do { - try await respondSuccess(payload: payload) - } catch { - self.logger.error("Respond Success failed with: \(error.localizedDescription)") - } - } - } - - func subscribe(topic: String) async throws { - try await relayClient.subscribe(topic: topic) - } - - func unsubscribe(topic: String) { - relayClient.unsubscribe(topic: topic) { [weak self] error in - if let error = error { - self?.logger.error(error) - } else { - self?.jsonRpcHistory.delete(topic: topic) - } - } - } - - // MARK: - Private - - private func setUpPublishers() { - relayClient.socketConnectionStatusPublisher.sink { [weak self] status in - if status == .connected { - self?.transportConnectionPublisherSubject.send() - } - }.store(in: &publishers) - - relayClient.messagePublisher.sink { [weak self] (topic, message) in - self?.manageSubscription(topic, message) - } - .store(in: &publishers) - } - - private func manageSubscription(_ topic: String, _ encodedEnvelope: String) { - if let deserializedJsonRpcRequest: WCRequest = serializer.tryDeserialize(topic: topic, encodedEnvelope: encodedEnvelope) { - handleWCRequest(topic: topic, request: deserializedJsonRpcRequest) - } else if let deserializedJsonRpcResponse: JSONRPCResponse = serializer.tryDeserialize(topic: topic, encodedEnvelope: encodedEnvelope) { - handleJsonRpcResponse(response: deserializedJsonRpcResponse) - } else if let deserializedJsonRpcError: JSONRPCErrorResponse = serializer.tryDeserialize(topic: topic, encodedEnvelope: encodedEnvelope) { - handleJsonRpcErrorResponse(response: deserializedJsonRpcError) - } else { - logger.warn("Warning: Networking Interactor - Received unknown object type from networking relay") - } - } - - private func handleWCRequest(topic: String, request: WCRequest) { - do { - try jsonRpcHistory.set(topic: topic, request: request, chainId: getChainId(request)) - let payload = WCRequestSubscriptionPayload(topic: topic, wcRequest: request) - wcRequestPublisherSubject.send(payload) - } catch WalletConnectError.internal(.jsonRpcDuplicateDetected) { - logger.info("Info: Json Rpc Duplicate Detected") - } catch { - logger.error(error) - } - } - - private func handleJsonRpcResponse(response: JSONRPCResponse) { - do { - let record = try jsonRpcHistory.resolve(response: JsonRpcResult.response(response)) - let wcResponse = WCResponse( - topic: record.topic, - chainId: record.chainId, - requestMethod: record.request.method, - requestParams: record.request.params, - result: JsonRpcResult.response(response)) - responsePublisherSubject.send(wcResponse) - } catch { - logger.info("Info: \(error.localizedDescription)") - } - } - - private func handleJsonRpcErrorResponse(response: JSONRPCErrorResponse) { - do { - let record = try jsonRpcHistory.resolve(response: JsonRpcResult.error(response)) - let wcResponse = WCResponse( - topic: record.topic, - chainId: record.chainId, - requestMethod: record.request.method, - requestParams: record.request.params, - result: JsonRpcResult.error(response)) - responsePublisherSubject.send(wcResponse) - } catch { - logger.info("Info: \(error.localizedDescription)") - } - } - - private func shouldPrompt(_ method: WCRequest.Method) -> Bool { - switch method { - case .sessionRequest: - return true - default: - return false - } - } - - func getChainId(_ request: WCRequest) -> String? { - guard case let .sessionRequest(payload) = request.params else {return nil} - return payload.chainId.absoluteString - } -} +//import Foundation +//import Combine +//import WalletConnectUtils +//import WalletConnectKMS +// +//protocol NetworkInteracting: AnyObject { +// var transportConnectionPublisher: AnyPublisher {get} +// var wcRequestPublisher: AnyPublisher {get} +// var responsePublisher: AnyPublisher {get} +// /// Completes when request sent from a networking client +// func request(_ wcMethod: WCMethod, onTopic topic: String) async throws +// /// Completes with an acknowledgement from the relay network +// func requestNetworkAck(_ wcMethod: WCMethod, onTopic topic: String, completion: @escaping ((Error?) -> Void)) +// /// Completes with a peer response +// func requestPeerResponse(_ wcMethod: WCMethod, onTopic topic: String, completion: ((Result, JSONRPCErrorResponse>) -> Void)?) +// func respond(topic: String, response: JsonRpcResult, tag: Int) async throws +// func respondSuccess(payload: WCRequestSubscriptionPayload) async throws +// func respondSuccess(for payload: WCRequestSubscriptionPayload) +// func respondError(payload: WCRequestSubscriptionPayload, reason: ReasonCode) async throws +// func subscribe(topic: String) async throws +// func unsubscribe(topic: String) +//} +// +//extension NetworkInteracting { +// func request(_ wcMethod: WCMethod, onTopic topic: String) { +// requestPeerResponse(wcMethod, onTopic: topic, completion: nil) +// } +//} +// +//class NetworkInteractor: NetworkInteracting { +// +// private var publishers = Set() +// +// private var relayClient: NetworkRelaying +// private let serializer: Serializing +// private let jsonRpcHistory: JsonRpcHistoryRecording +// +// private let transportConnectionPublisherSubject = PassthroughSubject() +// private let responsePublisherSubject = PassthroughSubject() +// private let wcRequestPublisherSubject = PassthroughSubject() +// +// var transportConnectionPublisher: AnyPublisher { +// transportConnectionPublisherSubject.eraseToAnyPublisher() +// } +// var wcRequestPublisher: AnyPublisher { +// wcRequestPublisherSubject.eraseToAnyPublisher() +// } +// var responsePublisher: AnyPublisher { +// responsePublisherSubject.eraseToAnyPublisher() +// } +// +// let logger: ConsoleLogging +// +// init(relayClient: NetworkRelaying, +// serializer: Serializing, +// logger: ConsoleLogging, +// jsonRpcHistory: JsonRpcHistoryRecording) { +// self.relayClient = relayClient +// self.serializer = serializer +// self.logger = logger +// self.jsonRpcHistory = jsonRpcHistory +// setUpPublishers() +// } +// +// func request(_ wcMethod: WCMethod, onTopic topic: String) async throws { +// try await request(topic: topic, payload: wcMethod.asRequest()) +// } +// +// /// Completes when networking client sends a request +// func request(topic: String, payload: WCRequest) async throws { +// try jsonRpcHistory.set(topic: topic, request: payload, chainId: getChainId(payload)) +// let message = try serializer.serialize(topic: topic, encodable: payload) +// let prompt = shouldPrompt(payload.method) +// try await relayClient.publish(topic: topic, payload: message, tag: payload.tag, prompt: prompt) +// } +// +// func requestPeerResponse(_ wcMethod: WCMethod, onTopic topic: String, completion: ((Result, JSONRPCErrorResponse>) -> Void)?) { +// let payload = wcMethod.asRequest() +// do { +// try jsonRpcHistory.set(topic: topic, request: payload, chainId: getChainId(payload)) +// let message = try serializer.serialize(topic: topic, encodable: payload) +// let prompt = shouldPrompt(payload.method) +// relayClient.publish(topic: topic, payload: message, tag: payload.tag, prompt: prompt) { [weak self] error in +// guard let self = self else {return} +// if let error = error { +// self.logger.error(error) +// } else { +// var cancellable: AnyCancellable! +// cancellable = self.responsePublisher +// .filter {$0.result.id == payload.id} +// .sink { (response) in +// cancellable.cancel() +// self.logger.debug("WC Relay - received response on topic: \(topic)") +// switch response.result { +// case .response(let response): +// completion?(.success(response)) +// case .error(let error): +// self.logger.debug("Request error: \(error)") +// completion?(.failure(error)) +// } +// } +// } +// } +// } catch WalletConnectError.internal(.jsonRpcDuplicateDetected) { +// logger.info("Info: Json Rpc Duplicate Detected") +// } catch { +// logger.error(error) +// } +// } +// +// /// Completes with an acknowledgement from the relay network. +// /// completes with error if networking client was not able to send a message +// func requestNetworkAck(_ wcMethod: WCMethod, onTopic topic: String, completion: @escaping ((Error?) -> Void)) { +// do { +// let payload = wcMethod.asRequest() +// try jsonRpcHistory.set(topic: topic, request: payload, chainId: getChainId(payload)) +// let message = try serializer.serialize(topic: topic, encodable: payload) +// let prompt = shouldPrompt(payload.method) +// relayClient.publish(topic: topic, payload: message, tag: payload.tag, prompt: prompt) { error in +// completion(error) +// } +// } catch WalletConnectError.internal(.jsonRpcDuplicateDetected) { +// logger.info("Info: Json Rpc Duplicate Detected") +// } catch { +// logger.error(error) +// } +// } +// +// func respond(topic: String, response: JsonRpcResult, tag: Int) async throws { +// _ = try jsonRpcHistory.resolve(response: response) +// +// let message = try serializer.serialize(topic: topic, encodable: response.value) +// logger.debug("Responding....topic: \(topic)") +// +// do { +// try await relayClient.publish(topic: topic, payload: message, tag: tag, prompt: false) +// } catch WalletConnectError.internal(.jsonRpcDuplicateDetected) { +// logger.info("Info: Json Rpc Duplicate Detected") +// } +// } +// +// func respondSuccess(payload: WCRequestSubscriptionPayload) async throws { +// let response = JSONRPCResponse(id: payload.wcRequest.id, result: AnyCodable(true)) +// try await respond(topic: payload.topic, response: JsonRpcResult.response(response), tag: payload.wcRequest.responseTag) +// } +// +// func respondError(payload: WCRequestSubscriptionPayload, reason: ReasonCode) async throws { +// let response = JSONRPCErrorResponse(id: payload.wcRequest.id, error: JSONRPCErrorResponse.Error(code: reason.code, message: reason.message)) +// try await respond(topic: payload.topic, response: JsonRpcResult.error(response), tag: payload.wcRequest.responseTag) +// } +// +// // TODO: Move to async +// func respondSuccess(for payload: WCRequestSubscriptionPayload) { +// Task(priority: .background) { +// do { +// try await respondSuccess(payload: payload) +// } catch { +// self.logger.error("Respond Success failed with: \(error.localizedDescription)") +// } +// } +// } +// +// func subscribe(topic: String) async throws { +// try await relayClient.subscribe(topic: topic) +// } +// +// func unsubscribe(topic: String) { +// relayClient.unsubscribe(topic: topic) { [weak self] error in +// if let error = error { +// self?.logger.error(error) +// } else { +// self?.jsonRpcHistory.delete(topic: topic) +// } +// } +// } +// +// // MARK: - Private +// +// private func setUpPublishers() { +// relayClient.socketConnectionStatusPublisher.sink { [weak self] status in +// if status == .connected { +// self?.transportConnectionPublisherSubject.send() +// } +// }.store(in: &publishers) +// +// relayClient.messagePublisher.sink { [weak self] (topic, message) in +// self?.manageSubscription(topic, message) +// } +// .store(in: &publishers) +// } +// +// private func manageSubscription(_ topic: String, _ encodedEnvelope: String) { +// if let deserializedJsonRpcRequest: WCRequest = serializer.tryDeserialize(topic: topic, encodedEnvelope: encodedEnvelope) { +// handleWCRequest(topic: topic, request: deserializedJsonRpcRequest) +// } else if let deserializedJsonRpcResponse: JSONRPCResponse = serializer.tryDeserialize(topic: topic, encodedEnvelope: encodedEnvelope) { +// handleJsonRpcResponse(response: deserializedJsonRpcResponse) +// } else if let deserializedJsonRpcError: JSONRPCErrorResponse = serializer.tryDeserialize(topic: topic, encodedEnvelope: encodedEnvelope) { +// handleJsonRpcErrorResponse(response: deserializedJsonRpcError) +// } else { +// logger.warn("Warning: Networking Interactor - Received unknown object type from networking relay") +// } +// } +// +// private func handleWCRequest(topic: String, request: WCRequest) { +// do { +// try jsonRpcHistory.set(topic: topic, request: request, chainId: getChainId(request)) +// let payload = WCRequestSubscriptionPayload(topic: topic, wcRequest: request) +// wcRequestPublisherSubject.send(payload) +// } catch WalletConnectError.internal(.jsonRpcDuplicateDetected) { +// logger.info("Info: Json Rpc Duplicate Detected") +// } catch { +// logger.error(error) +// } +// } +// +// private func handleJsonRpcResponse(response: JSONRPCResponse) { +// do { +// let record = try jsonRpcHistory.resolve(response: JsonRpcResult.response(response)) +// let wcResponse = WCResponse( +// topic: record.topic, +// chainId: record.chainId, +// requestMethod: record.request.method, +// requestParams: record.request.params, +// result: JsonRpcResult.response(response)) +// responsePublisherSubject.send(wcResponse) +// } catch { +// logger.info("Info: \(error.localizedDescription)") +// } +// } +// +// private func handleJsonRpcErrorResponse(response: JSONRPCErrorResponse) { +// do { +// let record = try jsonRpcHistory.resolve(response: JsonRpcResult.error(response)) +// let wcResponse = WCResponse( +// topic: record.topic, +// chainId: record.chainId, +// requestMethod: record.request.method, +// requestParams: record.request.params, +// result: JsonRpcResult.error(response)) +// responsePublisherSubject.send(wcResponse) +// } catch { +// logger.info("Info: \(error.localizedDescription)") +// } +// } +// +// private func shouldPrompt(_ method: WCRequest.Method) -> Bool { +// switch method { +// case .sessionRequest: +// return true +// default: +// return false +// } +// } +// +// func getChainId(_ request: WCRequest) -> String? { +// guard case let .sessionRequest(payload) = request.params else {return nil} +// return payload.chainId.absoluteString +// } +//} diff --git a/Sources/WalletConnectSign/NetworkInteractor/NetworkRelaying.swift b/Sources/WalletConnectSign/NetworkInteractor/NetworkRelaying.swift index 5574cf826..fec9c6ed8 100644 --- a/Sources/WalletConnectSign/NetworkInteractor/NetworkRelaying.swift +++ b/Sources/WalletConnectSign/NetworkInteractor/NetworkRelaying.swift @@ -1,19 +1,19 @@ -import Foundation -import WalletConnectRelay -import Combine - -extension RelayClient: NetworkRelaying {} - -protocol NetworkRelaying { - var messagePublisher: AnyPublisher<(topic: String, message: String), Never> { get } - var socketConnectionStatusPublisher: AnyPublisher { get } - func connect() throws - func disconnect(closeCode: URLSessionWebSocketTask.CloseCode) throws - func publish(topic: String, payload: String, tag: Int, prompt: Bool) async throws - /// - returns: request id - func publish(topic: String, payload: String, tag: Int, prompt: Bool, onNetworkAcknowledge: @escaping ((Error?) -> Void)) - func subscribe(topic: String, completion: @escaping (Error?) -> Void) - func subscribe(topic: String) async throws - /// - returns: request id - func unsubscribe(topic: String, completion: @escaping ((Error?) -> Void)) -} +//import Foundation +//import WalletConnectRelay +//import Combine +// +//extension RelayClient: NetworkRelaying {} +// +//protocol NetworkRelaying { +// var messagePublisher: AnyPublisher<(topic: String, message: String), Never> { get } +// var socketConnectionStatusPublisher: AnyPublisher { get } +// func connect() throws +// func disconnect(closeCode: URLSessionWebSocketTask.CloseCode) throws +// func publish(topic: String, payload: String, tag: Int, prompt: Bool) async throws +// /// - returns: request id +// func publish(topic: String, payload: String, tag: Int, prompt: Bool, onNetworkAcknowledge: @escaping ((Error?) -> Void)) +// func subscribe(topic: String, completion: @escaping (Error?) -> Void) +// func subscribe(topic: String) async throws +// /// - returns: request id +// func unsubscribe(topic: String, completion: @escaping ((Error?) -> Void)) +//} diff --git a/Sources/WalletConnectSign/Reason.swift b/Sources/WalletConnectSign/Reason.swift deleted file mode 100644 index d49aab825..000000000 --- a/Sources/WalletConnectSign/Reason.swift +++ /dev/null @@ -1,11 +0,0 @@ -// TODO: Refactor into codes. Reference: https://docs.walletconnect.com/2.0/protocol/reason-codes -public struct Reason { - - public let code: Int - public let message: String - - public init(code: Int, message: String) { - self.code = code - self.message = message - } -} diff --git a/Sources/WalletConnectSign/Request.swift b/Sources/WalletConnectSign/Request.swift index da55722b2..5cca66797 100644 --- a/Sources/WalletConnectSign/Request.swift +++ b/Sources/WalletConnectSign/Request.swift @@ -1,14 +1,15 @@ import Foundation +import JSONRPC import WalletConnectUtils public struct Request: Codable, Equatable { - public let id: Int64 + public let id: RPCID public let topic: String public let method: String public let params: AnyCodable public let chainId: Blockchain - internal init(id: Int64, topic: String, method: String, params: AnyCodable, chainId: Blockchain) { + internal init(id: RPCID, topic: String, method: String, params: AnyCodable, chainId: Blockchain) { self.id = id self.topic = topic self.method = method @@ -17,10 +18,10 @@ public struct Request: Codable, Equatable { } public init(topic: String, method: String, params: AnyCodable, chainId: Blockchain) { - self.init(id: JsonRpcID.generate(), topic: topic, method: method, params: params, chainId: chainId) + self.init(id: RPCID(JsonRpcID.generate()), topic: topic, method: method, params: params, chainId: chainId) } - internal init(id: Int64, topic: String, method: String, params: C, chainId: Blockchain) where C: Codable { + internal init(id: RPCID, topic: String, method: String, params: C, chainId: Blockchain) where C: Codable { self.init(id: id, topic: topic, method: method, params: AnyCodable(params), chainId: chainId) } } diff --git a/Sources/WalletConnectSign/Subscription/WCRequestSubscriptionPayload.swift b/Sources/WalletConnectSign/Subscription/WCRequestSubscriptionPayload.swift deleted file mode 100644 index 91efa9654..000000000 --- a/Sources/WalletConnectSign/Subscription/WCRequestSubscriptionPayload.swift +++ /dev/null @@ -1,11 +0,0 @@ -import Foundation -import WalletConnectUtils - -struct WCRequestSubscriptionPayload: Codable { - let topic: String - let wcRequest: WCRequest - - var timestamp: Date { - return JsonRpcID.timestamp(from: wcRequest.id) - } -} diff --git a/Sources/WalletConnectSign/Types/ReasonCode.swift b/Sources/WalletConnectSign/Types/ReasonCode.swift index cdc4f8db5..8ca23bd4c 100644 --- a/Sources/WalletConnectSign/Types/ReasonCode.swift +++ b/Sources/WalletConnectSign/Types/ReasonCode.swift @@ -1,4 +1,6 @@ -enum ReasonCode: Codable, Equatable { +import WalletConnectNetworking + +enum ReasonCode: Reason, Codable, Equatable { enum Context: String, Codable { case pairing = "pairing" diff --git a/Sources/WalletConnectSign/Types/WCMethod.swift b/Sources/WalletConnectSign/Types/WCMethod.swift deleted file mode 100644 index e2002188a..000000000 --- a/Sources/WalletConnectSign/Types/WCMethod.swift +++ /dev/null @@ -1,37 +0,0 @@ -import Foundation -import WalletConnectPairing - -enum WCMethod { - case wcPairingPing - case wcSessionPropose(SessionType.ProposeParams) - case wcSessionSettle(SessionType.SettleParams) - case wcSessionUpdate(SessionType.UpdateParams) - case wcSessionExtend(SessionType.UpdateExpiryParams) - case wcSessionDelete(SessionType.DeleteParams) - case wcSessionRequest(SessionType.RequestParams) - case wcSessionPing - case wcSessionEvent(SessionType.EventParams) - - func asRequest() -> WCRequest { - switch self { - case .wcPairingPing: - return WCRequest(method: .pairingPing, params: .pairingPing(PairingType.PingParams())) - case .wcSessionPropose(let proposalParams): - return WCRequest(method: .sessionPropose, params: .sessionPropose(proposalParams)) - case .wcSessionSettle(let settleParams): - return WCRequest(method: .sessionSettle, params: .sessionSettle(settleParams)) - case .wcSessionUpdate(let updateParams): - return WCRequest(method: .sessionUpdate, params: .sessionUpdate(updateParams)) - case .wcSessionExtend(let updateExpiryParams): - return WCRequest(method: .sessionExtend, params: .sessionExtend(updateExpiryParams)) - case .wcSessionDelete(let deleteParams): - return WCRequest(method: .sessionDelete, params: .sessionDelete(deleteParams)) - case .wcSessionRequest(let payloadParams): - return WCRequest(method: .sessionRequest, params: .sessionRequest(payloadParams)) - case .wcSessionPing: - return WCRequest(method: .sessionPing, params: .sessionPing(SessionType.PingParams())) - case .wcSessionEvent(let eventParams): - return WCRequest(method: .sessionEvent, params: .sessionEvent(eventParams)) - } - } -} diff --git a/Sources/WalletConnectSign/Types/WCRequest.swift b/Sources/WalletConnectSign/Types/WCRequest.swift index ec9edfdf8..a4a7e44df 100644 --- a/Sources/WalletConnectSign/Types/WCRequest.swift +++ b/Sources/WalletConnectSign/Types/WCRequest.swift @@ -1,179 +1,85 @@ import Foundation +import JSONRPC import WalletConnectPairing import WalletConnectUtils +import WalletConnectNetworking -struct WCRequest: Codable { - let id: Int64 - let jsonrpc: String - let method: Method - let params: Params +enum WCRequest: Codable { + case pairingDelete(PairingType.DeleteParams) + case pairingPing(PairingType.PingParams) + case sessionPropose(SessionType.ProposeParams) + case sessionSettle(SessionType.SettleParams) + case sessionUpdate(SessionType.UpdateParams) + case sessionExtend(SessionType.UpdateExpiryParams) + case sessionDelete(SessionType.DeleteParams) + case sessionRequest(SessionType.RequestParams) + case sessionPing(SessionType.PingParams) + case sessionEvent(SessionType.EventParams) - enum CodingKeys: CodingKey { - case id - case jsonrpc - case method - case params - } - - internal init(id: Int64 = JsonRpcID.generate(), jsonrpc: String = "2.0", method: Method, params: Params) { - self.id = id - self.jsonrpc = jsonrpc - self.method = method - self.params = params - } + enum Method: ProtocolMethod { + case pairingDelete + case pairingPing + case sessionPropose + case sessionSettle + case sessionUpdate + case sessionExtend + case sessionDelete + case sessionRequest + case sessionPing + case sessionEvent - init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - id = try container.decode(Int64.self, forKey: .id) - jsonrpc = try container.decode(String.self, forKey: .jsonrpc) - method = try container.decode(Method.self, forKey: .method) - switch method { - case .pairingDelete: - let paramsValue = try container.decode(PairingType.DeleteParams.self, forKey: .params) - params = .pairingDelete(paramsValue) - case .pairingPing: - let paramsValue = try container.decode(PairingType.PingParams.self, forKey: .params) - params = .pairingPing(paramsValue) - case .sessionPropose: - let paramsValue = try container.decode(SessionType.ProposeParams.self, forKey: .params) - params = .sessionPropose(paramsValue) - case .sessionSettle: - let paramsValue = try container.decode(SessionType.SettleParams.self, forKey: .params) - params = .sessionSettle(paramsValue) - case .sessionUpdate: - let paramsValue = try container.decode(SessionType.UpdateParams.self, forKey: .params) - params = .sessionUpdate(paramsValue) - case .sessionDelete: - let paramsValue = try container.decode(SessionType.DeleteParams.self, forKey: .params) - params = .sessionDelete(paramsValue) - case .sessionRequest: - let paramsValue = try container.decode(SessionType.RequestParams.self, forKey: .params) - params = .sessionRequest(paramsValue) - case .sessionPing: - let paramsValue = try container.decode(SessionType.PingParams.self, forKey: .params) - params = .sessionPing(paramsValue) - case .sessionExtend: - let paramsValue = try container.decode(SessionType.UpdateExpiryParams.self, forKey: .params) - params = .sessionExtend(paramsValue) - case .sessionEvent: - let paramsValue = try container.decode(SessionType.EventParams.self, forKey: .params) - params = .sessionEvent(paramsValue) - } - } - - func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(id, forKey: .id) - try container.encode(jsonrpc, forKey: .jsonrpc) - try container.encode(method.rawValue, forKey: .method) - switch params { - case .pairingDelete(let params): - try container.encode(params, forKey: .params) - case .pairingPing(let params): - try container.encode(params, forKey: .params) - case .sessionPropose(let params): - try container.encode(params, forKey: .params) - case .sessionSettle(let params): - try container.encode(params, forKey: .params) - case .sessionUpdate(let params): - try container.encode(params, forKey: .params) - case .sessionExtend(let params): - try container.encode(params, forKey: .params) - case .sessionDelete(let params): - try container.encode(params, forKey: .params) - case .sessionRequest(let params): - try container.encode(params, forKey: .params) - case .sessionPing(let params): - try container.encode(params, forKey: .params) - case .sessionEvent(let params): - try container.encode(params, forKey: .params) + var method: String { + switch self { + case .pairingDelete: + return "wc_pairingDelete" + case .pairingPing: + return "wc_pairingPing" + case .sessionPropose: + return "wc_sessionPropose" + case .sessionSettle: + return "wc_sessionSettle" + case .sessionUpdate: + return "wc_sessionUpdate" + case .sessionExtend: + return "wc_sessionExtend" + case .sessionDelete: + return "wc_sessionDelete" + case .sessionRequest: + return "wc_sessionRequest" + case .sessionPing: + return "wc_sessionPing" + case .sessionEvent: + return "wc_sessionEvent" + } } - } -} -extension WCRequest { - enum Method: String, Codable { - case pairingDelete = "wc_pairingDelete" - case pairingPing = "wc_pairingPing" - case sessionPropose = "wc_sessionPropose" - case sessionSettle = "wc_sessionSettle" - case sessionUpdate = "wc_sessionUpdate" - case sessionExtend = "wc_sessionExtend" - case sessionDelete = "wc_sessionDelete" - case sessionRequest = "wc_sessionRequest" - case sessionPing = "wc_sessionPing" - case sessionEvent = "wc_sessionEvent" - } -} - -extension WCRequest { - enum Params: Codable, Equatable { - case pairingDelete(PairingType.DeleteParams) - case pairingPing(PairingType.PingParams) - case sessionPropose(SessionType.ProposeParams) - case sessionSettle(SessionType.SettleParams) - case sessionUpdate(SessionType.UpdateParams) - case sessionExtend(SessionType.UpdateExpiryParams) - case sessionDelete(SessionType.DeleteParams) - case sessionRequest(SessionType.RequestParams) - case sessionPing(SessionType.PingParams) - case sessionEvent(SessionType.EventParams) - - static func == (lhs: Params, rhs: Params) -> Bool { - switch (lhs, rhs) { - case (.pairingDelete(let lhsParam), pairingDelete(let rhsParam)): - return lhsParam == rhsParam - case (.sessionPropose(let lhsParam), sessionPropose(let rhsParam)): - return lhsParam == rhsParam - case (.sessionSettle(let lhsParam), sessionSettle(let rhsParam)): - return lhsParam == rhsParam - case (.sessionUpdate(let lhsParam), sessionUpdate(let rhsParam)): - return lhsParam == rhsParam - case (.sessionExtend(let lhsParam), sessionExtend(let rhsParams)): - return lhsParam == rhsParams - case (.sessionDelete(let lhsParam), sessionDelete(let rhsParam)): - return lhsParam == rhsParam - case (.sessionRequest(let lhsParam), sessionRequest(let rhsParam)): - return lhsParam == rhsParam - case (.sessionPing(let lhsParam), sessionPing(let rhsParam)): - return lhsParam == rhsParam - case (.sessionEvent(let lhsParam), sessionEvent(let rhsParam)): - return lhsParam == rhsParam - default: - return false + var requestTag: Int { + switch self { + case .pairingDelete: + return 1000 + case .pairingPing: + return 1002 + case .sessionPropose: + return 1100 + case .sessionSettle: + return 1102 + case .sessionUpdate: + return 1104 + case .sessionExtend: + return 1106 + case .sessionDelete: + return 1112 + case .sessionRequest: + return 1108 + case .sessionPing: + return 1114 + case .sessionEvent: + return 1110 } } - } -} - -extension WCRequest { - var tag: Int { - switch method { - case .pairingDelete: - return 1000 - case .pairingPing: - return 1002 - case .sessionPropose: - return 1100 - case .sessionSettle: - return 1102 - case .sessionUpdate: - return 1104 - case .sessionExtend: - return 1106 - case .sessionDelete: - return 1112 - case .sessionRequest: - return 1108 - case .sessionPing: - return 1114 - case .sessionEvent: - return 1110 + var responseTag: Int { + return requestTag + 1 } } - - var responseTag: Int { - return tag + 1 - } } diff --git a/Sources/WalletConnectSign/Types/WCResponse.swift b/Sources/WalletConnectSign/Types/WCResponse.swift deleted file mode 100644 index b457b9ed0..000000000 --- a/Sources/WalletConnectSign/Types/WCResponse.swift +++ /dev/null @@ -1,14 +0,0 @@ -import Foundation -import WalletConnectUtils - -struct WCResponse: Codable { - let topic: String - let chainId: String? - let requestMethod: WCRequest.Method - let requestParams: WCRequest.Params - let result: JsonRpcResult - - var timestamp: Date { - return JsonRpcID.timestamp(from: result.id) - } -} From a67e09dfccde79224b771d77b8a425781bf387a2 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Wed, 7 Sep 2022 00:02:10 +0300 Subject: [PATCH 005/175] ApproveEngine --- .../Engine/Common/ApproveEngine.swift | 107 +++++++++--------- .../Engine/Common/PairingEngine.swift | 1 + .../Engine/Common/SessionEngine.swift | 1 - .../Engine/Controller/PairEngine.swift | 1 + .../NetworkingInteractorMock.swift | 16 ++- 5 files changed, 69 insertions(+), 57 deletions(-) diff --git a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift index a5a310a1d..e75cc8bde 100644 --- a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift @@ -1,5 +1,6 @@ import Foundation import Combine +import JSONRPC import WalletConnectUtils import WalletConnectKMS import WalletConnectPairing @@ -12,7 +13,6 @@ final class ApproveEngine { case proposalPayloadsNotFound case pairingNotFound case agreementMissingOrInvalid - case respondError(payload: WCRequestSubscriptionPayload, reason: ReasonCode) } var onSessionProposal: ((Session.Proposal) -> Void)? @@ -24,7 +24,7 @@ final class ApproveEngine { private let networkingInteractor: NetworkInteracting private let pairingStore: WCPairingStorage private let sessionStore: WCSessionStorage - private let proposalPayloadsStore: CodableStore + private let proposalPayloadsStore: CodableStore> private let sessionToPairingTopic: CodableStore private let metadata: AppMetadata private let kms: KeyManagementServiceProtocol @@ -34,7 +34,7 @@ final class ApproveEngine { init( networkingInteractor: NetworkInteracting, - proposalPayloadsStore: CodableStore, + proposalPayloadsStore: CodableStore>, sessionToPairingTopic: CodableStore, metadata: AppMetadata, kms: KeyManagementServiceProtocol, @@ -57,7 +57,7 @@ final class ApproveEngine { func approveProposal(proposerPubKey: String, validating sessionNamespaces: [String: SessionNamespace]) async throws { let payload = try proposalPayloadsStore.get(key: proposerPubKey) - guard let payload = payload, case .sessionPropose(let proposal) = payload.wcRequest.params else { + guard let payload = payload, case .sessionPropose(let proposal) = payload.request else { throw Errors.wrongRequestParams } @@ -80,14 +80,13 @@ final class ApproveEngine { throw Errors.relayNotFound } - let proposeResponse = SessionType.ProposeResponse(relay: relay, responderPublicKey: selfPublicKey.hexRepresentation) - let response = JSONRPCResponse(id: payload.wcRequest.id, result: AnyCodable(proposeResponse)) - guard var pairing = pairingStore.getPairing(forTopic: payload.topic) else { throw Errors.pairingNotFound } - try await networkingInteractor.respond(topic: payload.topic, response: .response(response), tag: payload.wcRequest.responseTag) + let result = SessionType.ProposeResponse(relay: relay, responderPublicKey: selfPublicKey.hexRepresentation) + let response = RPCResponse(id: payload.id, result: result) + try await networkingInteractor.respond(topic: payload.topic, response: response, tag: WCRequest.Method.sessionPropose.responseTag) try pairing.updateExpiry() pairingStore.setPairing(pairing) @@ -100,7 +99,7 @@ final class ApproveEngine { throw Errors.proposalPayloadsNotFound } proposalPayloadsStore.delete(forKey: proposerPubKey) - try await networkingInteractor.respondError(payload: payload, reason: reason) + try await networkingInteractor.respondError(topic: payload.topic, requestId: payload.id, tag: WCRequest.Method.sessionPropose.responseTag, reason: reason) // TODO: Delete pairing if inactive } @@ -141,7 +140,8 @@ final class ApproveEngine { try await networkingInteractor.subscribe(topic: topic) sessionStore.setSession(session) - try await networkingInteractor.request(.wcSessionSettle(settleParams), onTopic: topic) + let request = RPCRequest(method: WCRequest.Method.sessionSettle.method, params: WCRequest.sessionSettle(settleParams)) + try await networkingInteractor.request(request, topic: topic, tag: WCRequest.Method.sessionSettle.requestTag) onSessionSettle?(session.publicRepresentation()) } } @@ -151,40 +151,34 @@ final class ApproveEngine { private extension ApproveEngine { func setupNetworkingSubscriptions() { - networkingInteractor.responsePublisher - .sink { [unowned self] response in - switch response.requestParams { + networkingInteractor.responseSubscription(on: nil) + .sink { [unowned self] (payload: ResponseSubscriptionPayload) in + switch payload.request { case .sessionPropose(let proposal): - handleSessionProposeResponse(response: response, proposal: proposal) + handleSessionProposeResponse(payload: payload, proposal: proposal) case .sessionSettle: - handleSessionSettleResponse(response: response) + handleSessionSettleResponse(payload: payload) default: break } }.store(in: &publishers) - networkingInteractor.wcRequestPublisher - .sink { [unowned self] subscriptionPayload in - do { - switch subscriptionPayload.wcRequest.params { - case .sessionPropose(let proposal): - try handleSessionProposeRequest(payload: subscriptionPayload, proposal: proposal) - case .sessionSettle(let settleParams): - try handleSessionSettleRequest(payload: subscriptionPayload, settleParams: settleParams) - default: return - } - } catch Errors.respondError(let payload, let reason) { - respondError(payload: payload, reason: reason) - } catch { - logger.error("Unexpected Error: \(error.localizedDescription)") + networkingInteractor.requestSubscription(on: nil) + .sink { [unowned self] (payload: RequestSubscriptionPayload) in + switch payload.request { + case .sessionPropose(let proposal): + handleSessionProposeRequest(payload: payload, proposal: proposal) + case .sessionSettle(let params): + handleSessionSettleRequest(payload: payload, params: params) + default: return } }.store(in: &publishers) } - func respondError(payload: SubscriptionPayload, reason: ReasonCode) { + func respondError(payload: SubscriptionPayload, reason: ReasonCode, tag: Int) { Task { do { - try await networkingInteractor.respondError(topic: payload.topic, requestId: payload.id, tag: <#T##Int#>, reason: reason) + try await networkingInteractor.respondError(topic: payload.topic, requestId: payload.id, tag: tag, reason: reason) } catch { logger.error("Respond Error failed with: \(error.localizedDescription)") } @@ -199,12 +193,12 @@ private extension ApproveEngine { // MARK: SessionProposeResponse // TODO: Move to Non-Controller SettleEngine - func handleSessionProposeResponse(response: WCResponse, proposal: SessionType.ProposeParams) { + func handleSessionProposeResponse(payload: ResponseSubscriptionPayload, proposal: SessionType.ProposeParams) { do { let sessionTopic = try handleProposeResponse( - pairingTopic: response.topic, + pairingTopic: payload.topic, proposal: proposal, - result: response.result + result: payload.response ) settlingProposal = proposal @@ -260,48 +254,55 @@ private extension ApproveEngine { // MARK: SessionSettleResponse - func handleSessionSettleResponse(response: WCResponse) { - guard let session = sessionStore.getSession(forTopic: response.topic) else { return } - switch response.result { + func handleSessionSettleResponse(payload: ResponseSubscriptionPayload) { + guard let session = sessionStore.getSession(forTopic: payload.topic) else { return } + switch payload.response { case .response: logger.debug("Received session settle response") - guard var session = sessionStore.getSession(forTopic: response.topic) else { return } + guard var session = sessionStore.getSession(forTopic: payload.topic) else { return } session.acknowledge() sessionStore.setSession(session) case .error(let error): logger.error("Error - session rejected, Reason: \(error)") - networkingInteractor.unsubscribe(topic: response.topic) - sessionStore.delete(topic: response.topic) - kms.deleteAgreementSecret(for: response.topic) + networkingInteractor.unsubscribe(topic: payload.topic) + sessionStore.delete(topic: payload.topic) + kms.deleteAgreementSecret(for: payload.topic) kms.deletePrivateKey(for: session.publicKey!) } } // MARK: SessionProposeRequest - func handleSessionProposeRequest(payload: WCRequestSubscriptionPayload, proposal: SessionType.ProposeParams) throws { + func handleSessionProposeRequest(payload: RequestSubscriptionPayload, proposal: SessionType.ProposeParams) { logger.debug("Received Session Proposal") - do { try Namespace.validate(proposal.requiredNamespaces) } catch { throw Errors.respondError(payload: payload, reason: .invalidUpdateRequest) } + do { try Namespace.validate(proposal.requiredNamespaces) } catch { + return respondError(payload: payload, reason: .invalidUpdateRequest, tag: WCRequest.Method.sessionPropose.responseTag) + } proposalPayloadsStore.set(payload, forKey: proposal.proposer.publicKey) onSessionProposal?(proposal.publicRepresentation()) } // MARK: SessionSettleRequest - func handleSessionSettleRequest(payload: WCRequestSubscriptionPayload, settleParams: SessionType.SettleParams) throws { + func handleSessionSettleRequest(payload: RequestSubscriptionPayload, params: SessionType.SettleParams) { logger.debug("Did receive session settle request") - guard let proposedNamespaces = settlingProposal?.requiredNamespaces - else { throw Errors.respondError(payload: payload, reason: .invalidUpdateRequest) } + let tag = WCRequest.Method.sessionSettle.responseTag + + guard let proposedNamespaces = settlingProposal?.requiredNamespaces else { + return respondError(payload: payload, reason: .invalidUpdateRequest, tag: tag) + } settlingProposal = nil - let sessionNamespaces = settleParams.namespaces + let sessionNamespaces = params.namespaces do { try Namespace.validate(sessionNamespaces) try Namespace.validateApproved(sessionNamespaces, against: proposedNamespaces) } catch WalletConnectError.unsupportedNamespace(let reason) { - throw Errors.respondError(payload: payload, reason: reason) + return respondError(payload: payload, reason: reason, tag: tag) + } catch { + return respondError(payload: payload, reason: .invalidUpdateRequest, tag: tag) } let topic = payload.topic @@ -311,20 +312,22 @@ private extension ApproveEngine { metadata: metadata ) if let pairingTopic = try? sessionToPairingTopic.get(key: topic) { - updatePairingMetadata(topic: pairingTopic, metadata: settleParams.controller.metadata) + updatePairingMetadata(topic: pairingTopic, metadata: params.controller.metadata) } let session = WCSession( topic: topic, timestamp: Date(), selfParticipant: selfParticipant, - peerParticipant: settleParams.controller, - settleParams: settleParams, + peerParticipant: params.controller, + settleParams: params, requiredNamespaces: proposedNamespaces, acknowledged: true ) sessionStore.setSession(session) - networkingInteractor.respondSuccess(for: payload) + Task(priority: .high) { + try await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, tag: tag) + } onSessionSettle?(session.publicRepresentation()) } } diff --git a/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift b/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift index d9ec3ac3c..43a1b4b63 100644 --- a/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift @@ -3,6 +3,7 @@ import Combine import WalletConnectPairing import WalletConnectUtils import WalletConnectKMS +import WalletConnectNetworking final class PairingEngine { diff --git a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift index df321e01a..35934cf24 100644 --- a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift @@ -7,7 +7,6 @@ import WalletConnectNetworking final class SessionEngine { enum Errors: Error { - case respondError(payload: SubscriptionPayload, reason: ReasonCode) case sessionNotFound(topic: String) } diff --git a/Sources/WalletConnectSign/Engine/Controller/PairEngine.swift b/Sources/WalletConnectSign/Engine/Controller/PairEngine.swift index 9ad8956b7..81b4465d1 100644 --- a/Sources/WalletConnectSign/Engine/Controller/PairEngine.swift +++ b/Sources/WalletConnectSign/Engine/Controller/PairEngine.swift @@ -1,6 +1,7 @@ import Foundation import WalletConnectKMS import WalletConnectPairing +import WalletConnectNetworking actor PairEngine { private let networkingInteractor: NetworkInteracting diff --git a/Tests/TestingUtils/NetworkingInteractorMock.swift b/Tests/TestingUtils/NetworkingInteractorMock.swift index 10f42fc08..0ddc96adc 100644 --- a/Tests/TestingUtils/NetworkingInteractorMock.swift +++ b/Tests/TestingUtils/NetworkingInteractorMock.swift @@ -25,9 +25,13 @@ public class NetworkingInteractorMock: NetworkInteracting { responsePublisherSubject.eraseToAnyPublisher() } - public func requestSubscription(on request: ProtocolMethod) -> AnyPublisher, Never> { + // TODO: Avoid copy paste from NetworkInteractor + public func requestSubscription(on request: ProtocolMethod?) -> AnyPublisher, Never> { return requestPublisher - .filter { $0.request.method == request.method } + .filter { rpcRequest in + guard let request = request else { return true } + return rpcRequest.request.method == request.method + } .compactMap { topic, rpcRequest in guard let id = rpcRequest.id, let request = try? rpcRequest.params?.get(Request.self) else { return nil } return RequestSubscriptionPayload(id: id, topic: topic, request: request) @@ -35,9 +39,13 @@ public class NetworkingInteractorMock: NetworkInteracting { .eraseToAnyPublisher() } - public func responseSubscription(on request: ProtocolMethod) -> AnyPublisher, Never> { + // TODO: Avoid copy paste from NetworkInteractor + public func responseSubscription(on request: ProtocolMethod?) -> AnyPublisher, Never> { return responsePublisher - .filter { $0.request.method == request.method } + .filter { rpcRequest in + guard let request = request else { return true } + return rpcRequest.request.method == request.method + } .compactMap { topic, rpcRequest, rpcResponse in guard let id = rpcRequest.id, From bfcd3af60ad1b02fce4f8f19c0a0adb64344fa6a Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 8 Sep 2022 04:00:32 +0300 Subject: [PATCH 006/175] Sign: Networking package imported --- .../Sign/SignClientTests.swift | 20 +-- .../Services/App/AppRespondSubscriber.swift | 2 +- Sources/JSONRPC/RPCID.swift | 10 ++ Sources/JSONRPC/RPCResponse.swift | 38 +++--- Sources/JSONRPC/RPCResult.swift | 39 ++++++ .../NetworkInteracting.swift | 8 +- .../NetworkInteractor.swift | 14 +- .../ResponseSubscriptionErrorPayload.swift | 8 +- Sources/WalletConnectRelay/RelayClient.swift | 4 +- .../Engine/Common/ApproveEngine.swift | 98 +++++++------- .../Engine/Common/DeletePairingService.swift | 5 +- .../Engine/Common/DeleteSessionService.swift | 5 +- .../Engine/Common/PairingEngine.swift | 65 +++++----- .../Engine/Common/SessionEngine.swift | 122 +++++++++--------- .../ControllerSessionStateMachine.swift | 52 ++++---- .../NonControllerSessionStateMachine.swift | 67 +++++----- .../JsonRpcHistory/JsonRpcHistory.swift | 63 --------- .../JsonRpcHistory/JsonRpcRecord.swift | 15 --- Sources/WalletConnectSign/Response.swift | 3 +- Sources/WalletConnectSign/Sign/Sign.swift | 6 +- .../WalletConnectSign/Sign/SignClient.swift | 34 ++--- .../Sign/SignClientFactory.swift | 9 +- .../Types/Session/SessionType.swift | 15 +-- .../Types/SignProtocolMethod.swift | 72 +++++++++++ .../WalletConnectSign/Types/WCRequest.swift | 85 ------------ .../JSONRPC/JSONRPCErrorResponse.swift | 27 ---- .../JSONRPC/JSONRPCRequest.swift | 23 ---- .../JSONRPC/JSONRPCResponse.swift | 18 --- .../JSONRPC/JsonRpcResult.swift | 24 ---- .../WalletConnectUtils/JsonRpcHistory.swift | 58 --------- .../WalletConnectUtils/JsonRpcRecord.swift | 27 ---- .../NetworkingInteractorMock.swift | 15 +-- 32 files changed, 426 insertions(+), 625 deletions(-) create mode 100644 Sources/JSONRPC/RPCResult.swift delete mode 100644 Sources/WalletConnectSign/JsonRpcHistory/JsonRpcHistory.swift delete mode 100644 Sources/WalletConnectSign/JsonRpcHistory/JsonRpcRecord.swift create mode 100644 Sources/WalletConnectSign/Types/SignProtocolMethod.swift delete mode 100644 Sources/WalletConnectSign/Types/WCRequest.swift delete mode 100644 Sources/WalletConnectUtils/JSONRPC/JSONRPCErrorResponse.swift delete mode 100644 Sources/WalletConnectUtils/JSONRPC/JSONRPCRequest.swift delete mode 100644 Sources/WalletConnectUtils/JSONRPC/JSONRPCResponse.swift delete mode 100644 Sources/WalletConnectUtils/JSONRPC/JsonRpcResult.swift delete mode 100644 Sources/WalletConnectUtils/JsonRpcHistory.swift delete mode 100644 Sources/WalletConnectUtils/JsonRpcRecord.swift diff --git a/Example/IntegrationTests/Sign/SignClientTests.swift b/Example/IntegrationTests/Sign/SignClientTests.swift index e00529b0b..3dd004d78 100644 --- a/Example/IntegrationTests/Sign/SignClientTests.swift +++ b/Example/IntegrationTests/Sign/SignClientTests.swift @@ -1,5 +1,6 @@ import XCTest import WalletConnectUtils +import JSONRPC @testable import WalletConnectKMS @testable import WalletConnectSign @testable import WalletConnectRelay @@ -171,7 +172,7 @@ final class SignClientTests: XCTestCase { } dapp.onSessionSettled = { [unowned self] settledSession in Task(priority: .high) { - let request = Request(id: 0, topic: settledSession.topic, method: requestMethod, params: requestParams, chainId: chain) + let request = Request(id: RPCID(0), topic: settledSession.topic, method: requestMethod, params: requestParams, chainId: chain) try await dapp.client.request(params: request) } } @@ -181,14 +182,13 @@ final class SignClientTests: XCTestCase { XCTAssertEqual(sessionRequest.method, requestMethod) requestExpectation.fulfill() Task(priority: .high) { - let jsonrpcResponse = JSONRPCResponse(id: sessionRequest.id, result: AnyCodable(responseParams)) - try await wallet.client.respond(topic: sessionRequest.topic, response: .response(jsonrpcResponse)) + try await wallet.client.respond(topic: sessionRequest.topic, requestId: sessionRequest.id, response: .response(AnyCodable(responseParams))) } } dapp.onSessionResponse = { response in switch response.result { case .response(let response): - XCTAssertEqual(try! response.result.get(String.self), responseParams) + XCTAssertEqual(try! response.get(String.self), responseParams) case .error: XCTFail() } @@ -207,7 +207,8 @@ final class SignClientTests: XCTestCase { let requestMethod = "eth_sendTransaction" let requestParams = [EthSendTransaction.stub()] - let error = JSONRPCErrorResponse.Error(code: 0, message: "error") + let error = JSONRPCError(code: 0, message: "error") + let chain = Blockchain("eip155:1")! wallet.onSessionProposal = { [unowned self] proposal in @@ -217,22 +218,21 @@ final class SignClientTests: XCTestCase { } dapp.onSessionSettled = { [unowned self] settledSession in Task(priority: .high) { - let request = Request(id: 0, topic: settledSession.topic, method: requestMethod, params: requestParams, chainId: chain) + let request = Request(id: RPCID(0), topic: settledSession.topic, method: requestMethod, params: requestParams, chainId: chain) try await dapp.client.request(params: request) } } wallet.onSessionRequest = { [unowned self] sessionRequest in Task(priority: .high) { - let response = JSONRPCErrorResponse(id: sessionRequest.id, error: error) - try await wallet.client.respond(topic: sessionRequest.topic, response: .error(response)) + try await wallet.client.respond(topic: sessionRequest.topic, requestId: sessionRequest.id, response: .error(error)) } } dapp.onSessionResponse = { response in switch response.result { case .response: XCTFail() - case .error(let errorResponse): - XCTAssertEqual(error, errorResponse.error) + case .error(let receivedError): + XCTAssertEqual(error, receivedError) } expectation.fulfill() } diff --git a/Sources/Auth/Services/App/AppRespondSubscriber.swift b/Sources/Auth/Services/App/AppRespondSubscriber.swift index 2b3b353fa..a0e9f8c40 100644 --- a/Sources/Auth/Services/App/AppRespondSubscriber.swift +++ b/Sources/Auth/Services/App/AppRespondSubscriber.swift @@ -33,7 +33,7 @@ class AppRespondSubscriber { private func subscribeForResponse() { networkingInteractor.responseErrorSubscription(on: AuthProtocolMethod.authRequest) - .sink { [unowned self] payload in + .sink { [unowned self] (payload: ResponseSubscriptionErrorPayload) in guard let error = AuthError(code: payload.error.code) else { return } onResponse?(payload.id, .failure(error)) }.store(in: &publishers) diff --git a/Sources/JSONRPC/RPCID.swift b/Sources/JSONRPC/RPCID.swift index ff0ef3c83..21dcfb682 100644 --- a/Sources/JSONRPC/RPCID.swift +++ b/Sources/JSONRPC/RPCID.swift @@ -1,3 +1,4 @@ +import Foundation import Commons import Foundation @@ -15,3 +16,12 @@ struct IntIdentifierGenerator: IdentifierGenerator { return RPCID(timestamp + random) } } + +extension RPCID { + + public var timestamp: Date { + guard let id = self.right else { return .distantPast } + let interval = TimeInterval(id / 1000 / 1000) + return Date(timeIntervalSince1970: interval) + } +} diff --git a/Sources/JSONRPC/RPCResponse.swift b/Sources/JSONRPC/RPCResponse.swift index ad6ba9ca6..d38ef9c49 100644 --- a/Sources/JSONRPC/RPCResponse.swift +++ b/Sources/JSONRPC/RPCResponse.swift @@ -10,65 +10,65 @@ public struct RPCResponse: Equatable { public let id: RPCID? public var result: AnyCodable? { - if case .success(let value) = outcome { return value } + if case .response(let value) = outcome { return value } return nil } public var error: JSONRPCError? { - if case .failure(let error) = outcome { return error } + if case .error(let error) = outcome { return error } return nil } - public let outcome: Result + public let outcome: RPCResult - internal init(id: RPCID?, outcome: Result) { + internal init(id: RPCID?, outcome: RPCResult) { self.jsonrpc = "2.0" self.id = id self.outcome = outcome } public init(matchingRequest: RPCRequest, result: C) where C: Codable { - self.init(id: matchingRequest.id, outcome: .success(AnyCodable(result))) + self.init(id: matchingRequest.id, outcome: .response(AnyCodable(result))) } public init(matchingRequest: RPCRequest, error: JSONRPCError) { - self.init(id: matchingRequest.id, outcome: .failure(error)) + self.init(id: matchingRequest.id, outcome: .error(error)) } public init(id: Int64, result: C) where C: Codable { - self.init(id: RPCID(id), outcome: .success(AnyCodable(result))) + self.init(id: RPCID(id), outcome: .response(AnyCodable(result))) } public init(id: String, result: C) where C: Codable { - self.init(id: RPCID(id), outcome: .success(AnyCodable(result))) + self.init(id: RPCID(id), outcome: .response(AnyCodable(result))) } public init(id: RPCID, result: C) where C: Codable { - self.init(id: id, outcome: .success(AnyCodable(result))) + self.init(id: id, outcome: .response(AnyCodable(result))) } public init(id: RPCID?, error: JSONRPCError) { - self.init(id: id, outcome: .failure(error)) + self.init(id: id, outcome: .error(error)) } public init(id: Int64, error: JSONRPCError) { - self.init(id: RPCID(id), outcome: .failure(error)) + self.init(id: RPCID(id), outcome: .error(error)) } public init(id: String, error: JSONRPCError) { - self.init(id: RPCID(id), outcome: .failure(error)) + self.init(id: RPCID(id), outcome: .error(error)) } public init(id: Int64, errorCode: Int, message: String, associatedData: AnyCodable? = nil) { - self.init(id: RPCID(id), outcome: .failure(JSONRPCError(code: errorCode, message: message, data: associatedData))) + self.init(id: RPCID(id), outcome: .error(JSONRPCError(code: errorCode, message: message, data: associatedData))) } public init(id: String, errorCode: Int, message: String, associatedData: AnyCodable? = nil) { - self.init(id: RPCID(id), outcome: .failure(JSONRPCError(code: errorCode, message: message, data: associatedData))) + self.init(id: RPCID(id), outcome: .error(JSONRPCError(code: errorCode, message: message, data: associatedData))) } public init(errorWithoutID: JSONRPCError) { - self.init(id: nil, outcome: .failure(errorWithoutID)) + self.init(id: nil, outcome: .error(errorWithoutID)) } } @@ -104,9 +104,9 @@ extension RPCResponse: Codable { codingPath: [CodingKeys.result, CodingKeys.id], debugDescription: "A success response must have a valid `id`.")) } - outcome = .success(result) + outcome = .response(result) } else if let error = error { - outcome = .failure(error) + outcome = .error(error) } else { throw DecodingError.dataCorrupted(.init( codingPath: [CodingKeys.result, CodingKeys.error], @@ -119,9 +119,9 @@ extension RPCResponse: Codable { try container.encode(jsonrpc, forKey: .jsonrpc) try container.encode(id, forKey: .id) switch outcome { - case .success(let anyCodable): + case .response(let anyCodable): try container.encode(anyCodable, forKey: .result) - case .failure(let rpcError): + case .error(let rpcError): try container.encode(rpcError, forKey: .error) } } diff --git a/Sources/JSONRPC/RPCResult.swift b/Sources/JSONRPC/RPCResult.swift new file mode 100644 index 000000000..a3b81e094 --- /dev/null +++ b/Sources/JSONRPC/RPCResult.swift @@ -0,0 +1,39 @@ +import Foundation +import Commons + +public enum RPCResult: Codable, Equatable { + enum Errors: Error { + case decoding + } + + case response(AnyCodable) + case error(JSONRPCError) + + public var value: Codable { + switch self { + case .response(let value): + return value + case .error(let value): + return value + } + } + + public init(from decoder: Decoder) throws { + if let value = try? JSONRPCError(from: decoder) { + self = .error(value) + } else if let value = try? AnyCodable(from: decoder) { + self = .response(value) + } else { + throw Errors.decoding + } + } + + public func encode(to encoder: Encoder) throws { + switch self { + case .error(let value): + try value.encode(to: encoder) + case .response(let value): + try value.encode(to: encoder) + } + } +} diff --git a/Sources/WalletConnectNetworking/NetworkInteracting.swift b/Sources/WalletConnectNetworking/NetworkInteracting.swift index 67d5e9673..85ca9177e 100644 --- a/Sources/WalletConnectNetworking/NetworkInteracting.swift +++ b/Sources/WalletConnectNetworking/NetworkInteracting.swift @@ -15,14 +15,16 @@ public protocol NetworkInteracting { func respondError(topic: String, requestId: RPCID, tag: Int, reason: Reason, envelopeType: Envelope.EnvelopeType) async throws func requestSubscription( - on request: ProtocolMethod? + on request: ProtocolMethod ) -> AnyPublisher, Never> func responseSubscription( - on request: ProtocolMethod? + on request: ProtocolMethod ) -> AnyPublisher, Never> - func responseErrorSubscription(on request: ProtocolMethod) -> AnyPublisher + func responseErrorSubscription( + on request: ProtocolMethod + ) -> AnyPublisher, Never> } extension NetworkInteracting { diff --git a/Sources/WalletConnectNetworking/NetworkInteractor.swift b/Sources/WalletConnectNetworking/NetworkInteractor.swift index f5ed34273..405533ef1 100644 --- a/Sources/WalletConnectNetworking/NetworkInteractor.swift +++ b/Sources/WalletConnectNetworking/NetworkInteractor.swift @@ -56,10 +56,9 @@ public class NetworkingInteractor: NetworkInteracting { } } - public func requestSubscription(on request: ProtocolMethod?) -> AnyPublisher, Never> { + public func requestSubscription(on request: ProtocolMethod) -> AnyPublisher, Never> { return requestPublisher .filter { rpcRequest in - guard let request = request else { return true } return rpcRequest.request.method == request.method } .compactMap { topic, rpcRequest in @@ -69,10 +68,9 @@ public class NetworkingInteractor: NetworkInteracting { .eraseToAnyPublisher() } - public func responseSubscription(on request: ProtocolMethod?) -> AnyPublisher, Never> { + public func responseSubscription(on request: ProtocolMethod) -> AnyPublisher, Never> { return responsePublisher .filter { rpcRequest in - guard let request = request else { return true } return rpcRequest.request.method == request.method } .compactMap { topic, rpcRequest, rpcResponse in @@ -85,12 +83,12 @@ public class NetworkingInteractor: NetworkInteracting { .eraseToAnyPublisher() } - public func responseErrorSubscription(on request: ProtocolMethod) -> AnyPublisher { + public func responseErrorSubscription(on request: ProtocolMethod) -> AnyPublisher, Never> { return responsePublisher .filter { $0.request.method == request.method } - .compactMap { (_, _, rpcResponse) in - guard let id = rpcResponse.id, let error = rpcResponse.error else { return nil } - return ResponseSubscriptionErrorPayload(id: id, error: error) + .compactMap { (topic, rpcRequest, rpcResponse) in + guard let id = rpcResponse.id, let request = try? rpcRequest.params?.get(Request.self), let error = rpcResponse.error else { return nil } + return ResponseSubscriptionErrorPayload(id: id, topic: topic, request: request, error: error) } .eraseToAnyPublisher() } diff --git a/Sources/WalletConnectNetworking/ResponseSubscriptionErrorPayload.swift b/Sources/WalletConnectNetworking/ResponseSubscriptionErrorPayload.swift index 8b38df244..589ebbcc2 100644 --- a/Sources/WalletConnectNetworking/ResponseSubscriptionErrorPayload.swift +++ b/Sources/WalletConnectNetworking/ResponseSubscriptionErrorPayload.swift @@ -1,12 +1,16 @@ import Foundation import JSONRPC -public struct ResponseSubscriptionErrorPayload { +public struct ResponseSubscriptionErrorPayload: Codable, SubscriptionPayload { public let id: RPCID + public let topic: String + public let request: Request public let error: JSONRPCError - public init(id: RPCID, error: JSONRPCError) { + public init(id: RPCID, topic: String, request: Request, error: JSONRPCError) { self.id = id + self.topic = topic + self.request = request self.error = error } } diff --git a/Sources/WalletConnectRelay/RelayClient.swift b/Sources/WalletConnectRelay/RelayClient.swift index 94ab8ceab..874bb6267 100644 --- a/Sources/WalletConnectRelay/RelayClient.swift +++ b/Sources/WalletConnectRelay/RelayClient.swift @@ -253,13 +253,13 @@ public final class RelayClient { } } else if let response = tryDecode(RPCResponse.self, from: payload) { switch response.outcome { - case .success(let anyCodable): + case .response(let anyCodable): if let _ = try? anyCodable.get(Bool.self) { // TODO: Handle success vs. error requestAcknowledgePublisherSubject.send(response.id) } else if let subscriptionId = try? anyCodable.get(String.self) { subscriptionResponsePublisherSubject.send((response.id, subscriptionId)) } - case .failure(let rpcError): + case .error(let rpcError): logger.error("Received RPC error from relay network: \(rpcError)") } } else { diff --git a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift index e75cc8bde..e29813c63 100644 --- a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift @@ -16,7 +16,7 @@ final class ApproveEngine { } var onSessionProposal: ((Session.Proposal) -> Void)? - var onSessionRejected: ((Session.Proposal, SessionType.Reason) -> Void)? + var onSessionRejected: ((Session.Proposal, Reason) -> Void)? var onSessionSettle: ((Session) -> Void)? var settlingProposal: SessionProposal? @@ -24,7 +24,7 @@ final class ApproveEngine { private let networkingInteractor: NetworkInteracting private let pairingStore: WCPairingStorage private let sessionStore: WCSessionStorage - private let proposalPayloadsStore: CodableStore> + private let proposalPayloadsStore: CodableStore> private let sessionToPairingTopic: CodableStore private let metadata: AppMetadata private let kms: KeyManagementServiceProtocol @@ -34,7 +34,7 @@ final class ApproveEngine { init( networkingInteractor: NetworkInteracting, - proposalPayloadsStore: CodableStore>, + proposalPayloadsStore: CodableStore>, sessionToPairingTopic: CodableStore, metadata: AppMetadata, kms: KeyManagementServiceProtocol, @@ -51,16 +51,17 @@ final class ApproveEngine { self.pairingStore = pairingStore self.sessionStore = sessionStore - setupNetworkingSubscriptions() + setupRequestSubscriptions() + setupResponseSubscriptions() } func approveProposal(proposerPubKey: String, validating sessionNamespaces: [String: SessionNamespace]) async throws { - let payload = try proposalPayloadsStore.get(key: proposerPubKey) - - guard let payload = payload, case .sessionPropose(let proposal) = payload.request else { + guard let payload = try proposalPayloadsStore.get(key: proposerPubKey) else { throw Errors.wrongRequestParams } + let proposal = payload.request + proposalPayloadsStore.delete(forKey: proposerPubKey) try Namespace.validate(sessionNamespaces) @@ -86,7 +87,7 @@ final class ApproveEngine { let result = SessionType.ProposeResponse(relay: relay, responderPublicKey: selfPublicKey.hexRepresentation) let response = RPCResponse(id: payload.id, result: result) - try await networkingInteractor.respond(topic: payload.topic, response: response, tag: WCRequest.Method.sessionPropose.responseTag) + try await networkingInteractor.respond(topic: payload.topic, response: response, tag: SignProtocolMethod.sessionPropose.responseTag) try pairing.updateExpiry() pairingStore.setPairing(pairing) @@ -99,7 +100,7 @@ final class ApproveEngine { throw Errors.proposalPayloadsNotFound } proposalPayloadsStore.delete(forKey: proposerPubKey) - try await networkingInteractor.respondError(topic: payload.topic, requestId: payload.id, tag: WCRequest.Method.sessionPropose.responseTag, reason: reason) + try await networkingInteractor.respondError(topic: payload.topic, requestId: payload.id, tag: SignProtocolMethod.sessionPropose.responseTag, reason: reason) // TODO: Delete pairing if inactive } @@ -140,8 +141,8 @@ final class ApproveEngine { try await networkingInteractor.subscribe(topic: topic) sessionStore.setSession(session) - let request = RPCRequest(method: WCRequest.Method.sessionSettle.method, params: WCRequest.sessionSettle(settleParams)) - try await networkingInteractor.request(request, topic: topic, tag: WCRequest.Method.sessionSettle.requestTag) + let request = RPCRequest(method: SignProtocolMethod.sessionSettle.method, params: settleParams) + try await networkingInteractor.request(request, topic: topic, tag: SignProtocolMethod.sessionSettle.requestTag) onSessionSettle?(session.publicRepresentation()) } } @@ -150,28 +151,35 @@ final class ApproveEngine { private extension ApproveEngine { - func setupNetworkingSubscriptions() { - networkingInteractor.responseSubscription(on: nil) - .sink { [unowned self] (payload: ResponseSubscriptionPayload) in - switch payload.request { - case .sessionPropose(let proposal): - handleSessionProposeResponse(payload: payload, proposal: proposal) - case .sessionSettle: - handleSessionSettleResponse(payload: payload) - default: - break - } + func setupRequestSubscriptions() { + networkingInteractor.requestSubscription(on: SignProtocolMethod.sessionPropose) + .sink { [unowned self] (payload: RequestSubscriptionPayload) in + handleSessionProposeRequest(payload: payload) + }.store(in: &publishers) + + networkingInteractor.requestSubscription(on: SignProtocolMethod.sessionSettle) + .sink { [unowned self] (payload: RequestSubscriptionPayload) in + handleSessionSettleRequest(payload: payload) + }.store(in: &publishers) + } + + func setupResponseSubscriptions() { + networkingInteractor.responseSubscription(on: SignProtocolMethod.sessionPropose) + .sink { [unowned self] (payload: ResponseSubscriptionPayload) in + handleSessionProposeResponse(payload: payload) + }.store(in: &publishers) + + networkingInteractor.responseSubscription(on: SignProtocolMethod.sessionSettle) + .sink { [unowned self] (payload: ResponseSubscriptionPayload) in + handleSessionSettleResponse(payload: payload) }.store(in: &publishers) - networkingInteractor.requestSubscription(on: nil) - .sink { [unowned self] (payload: RequestSubscriptionPayload) in - switch payload.request { - case .sessionPropose(let proposal): - handleSessionProposeRequest(payload: payload, proposal: proposal) - case .sessionSettle(let params): - handleSessionSettleRequest(payload: payload, params: params) - default: return - } + networkingInteractor.responseErrorSubscription(on: SignProtocolMethod.sessionPropose) + .sink { [unowned self] (payload: ResponseSubscriptionErrorPayload) in + onSessionRejected?( + payload.request.publicRepresentation(), + SessionType.Reason(code: payload.error.code, message: payload.error.message) + ) }.store(in: &publishers) } @@ -193,27 +201,27 @@ private extension ApproveEngine { // MARK: SessionProposeResponse // TODO: Move to Non-Controller SettleEngine - func handleSessionProposeResponse(payload: ResponseSubscriptionPayload, proposal: SessionType.ProposeParams) { + func handleSessionProposeResponse(payload: ResponseSubscriptionPayload) { do { let sessionTopic = try handleProposeResponse( pairingTopic: payload.topic, - proposal: proposal, + proposal: payload.request, result: payload.response ) - settlingProposal = proposal + settlingProposal = payload.request Task(priority: .high) { - try? await networkingInteractor.subscribe(topic: sessionTopic) + try await networkingInteractor.subscribe(topic: sessionTopic) } } catch { - guard let error = error as? JSONRPCErrorResponse else { + guard let error = error as? Reason else { return logger.debug(error.localizedDescription) } - onSessionRejected?(proposal.publicRepresentation(), SessionType.Reason(code: error.error.code, message: error.error.message)) + onSessionRejected?(payload.request.publicRepresentation(), SessionType.Reason(code: error.code, message: error.message)) } } - func handleProposeResponse(pairingTopic: String, proposal: SessionProposal, result: JsonRpcResult) throws -> String { + func handleProposeResponse(pairingTopic: String, proposal: SessionProposal, result: RPCResult) throws -> String { guard var pairing = pairingStore.getPairing(forTopic: pairingTopic) else { throw Errors.pairingNotFound } @@ -229,7 +237,7 @@ private extension ApproveEngine { pairingStore.setPairing(pairing) let selfPublicKey = try AgreementPublicKey(hex: proposal.proposer.publicKey) - let proposeResponse = try response.result.get(SessionType.ProposeResponse.self) + let proposeResponse = try response.get(SessionType.ProposeResponse.self) let agreementKeys = try kms.performKeyAgreement(selfPublicKey: selfPublicKey, peerPublicKey: proposeResponse.responderPublicKey) let sessionTopic = agreementKeys.derivedTopic() @@ -254,7 +262,7 @@ private extension ApproveEngine { // MARK: SessionSettleResponse - func handleSessionSettleResponse(payload: ResponseSubscriptionPayload) { + func handleSessionSettleResponse(payload: ResponseSubscriptionPayload) { guard let session = sessionStore.getSession(forTopic: payload.topic) else { return } switch payload.response { case .response: @@ -273,20 +281,21 @@ private extension ApproveEngine { // MARK: SessionProposeRequest - func handleSessionProposeRequest(payload: RequestSubscriptionPayload, proposal: SessionType.ProposeParams) { + func handleSessionProposeRequest(payload: RequestSubscriptionPayload) { logger.debug("Received Session Proposal") + let proposal = payload.request do { try Namespace.validate(proposal.requiredNamespaces) } catch { - return respondError(payload: payload, reason: .invalidUpdateRequest, tag: WCRequest.Method.sessionPropose.responseTag) + return respondError(payload: payload, reason: .invalidUpdateRequest, tag: SignProtocolMethod.sessionPropose.responseTag) } proposalPayloadsStore.set(payload, forKey: proposal.proposer.publicKey) onSessionProposal?(proposal.publicRepresentation()) } // MARK: SessionSettleRequest - func handleSessionSettleRequest(payload: RequestSubscriptionPayload, params: SessionType.SettleParams) { + func handleSessionSettleRequest(payload: RequestSubscriptionPayload) { logger.debug("Did receive session settle request") - let tag = WCRequest.Method.sessionSettle.responseTag + let tag = SignProtocolMethod.sessionSettle.responseTag guard let proposedNamespaces = settlingProposal?.requiredNamespaces else { return respondError(payload: payload, reason: .invalidUpdateRequest, tag: tag) @@ -294,6 +303,7 @@ private extension ApproveEngine { settlingProposal = nil + let params = payload.request let sessionNamespaces = params.namespaces do { diff --git a/Sources/WalletConnectSign/Engine/Common/DeletePairingService.swift b/Sources/WalletConnectSign/Engine/Common/DeletePairingService.swift index a6324481e..2dbbede9d 100644 --- a/Sources/WalletConnectSign/Engine/Common/DeletePairingService.swift +++ b/Sources/WalletConnectSign/Engine/Common/DeletePairingService.swift @@ -1,7 +1,9 @@ import Foundation +import JSONRPC import WalletConnectKMS import WalletConnectUtils import WalletConnectPairing +import WalletConnectNetworking class DeletePairingService { private let networkingInteractor: NetworkInteracting @@ -23,7 +25,8 @@ class DeletePairingService { let reasonCode = ReasonCode.userDisconnected let reason = SessionType.Reason(code: reasonCode.code, message: reasonCode.message) logger.debug("Will delete pairing for reason: message: \(reason.message) code: \(reason.code)") - try await networkingInteractor.request(.wcSessionDelete(reason), onTopic: topic) + let request = RPCRequest(method: SignProtocolMethod.sessionDelete.method, params: reason) + try await networkingInteractor.request(request, topic: topic, tag: SignProtocolMethod.sessionDelete.requestTag) pairingStorage.delete(topic: topic) kms.deleteSymmetricKey(for: topic) networkingInteractor.unsubscribe(topic: topic) diff --git a/Sources/WalletConnectSign/Engine/Common/DeleteSessionService.swift b/Sources/WalletConnectSign/Engine/Common/DeleteSessionService.swift index 561af3507..f2ff1442d 100644 --- a/Sources/WalletConnectSign/Engine/Common/DeleteSessionService.swift +++ b/Sources/WalletConnectSign/Engine/Common/DeleteSessionService.swift @@ -1,6 +1,8 @@ import Foundation +import JSONRPC import WalletConnectKMS import WalletConnectUtils +import WalletConnectNetworking class DeleteSessionService { private let networkingInteractor: NetworkInteracting @@ -22,7 +24,8 @@ class DeleteSessionService { let reasonCode = ReasonCode.userDisconnected let reason = SessionType.Reason(code: reasonCode.code, message: reasonCode.message) logger.debug("Will delete session for reason: message: \(reason.message) code: \(reason.code)") - try await networkingInteractor.request(.wcSessionDelete(reason), onTopic: topic) + let request = RPCRequest(method: SignProtocolMethod.sessionDelete.method, params: reason) + try await networkingInteractor.request(request, topic: topic, tag: SignProtocolMethod.sessionDelete.requestTag) sessionStore.delete(topic: topic) kms.deleteSymmetricKey(for: topic) networkingInteractor.unsubscribe(topic: topic) diff --git a/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift b/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift index 43a1b4b63..0561e6446 100644 --- a/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift @@ -1,5 +1,6 @@ import Foundation import Combine +import JSONRPC import WalletConnectPairing import WalletConnectUtils import WalletConnectKMS @@ -70,15 +71,9 @@ final class PairingEngine { relays: [relay], proposer: proposer, requiredNamespaces: namespaces) - return try await withCheckedThrowingContinuation { continuation in - networkingInteractor.requestNetworkAck(.wcSessionPropose(proposal), onTopic: pairingTopic) { error in - if let error = error { - continuation.resume(throwing: error) - } else { - continuation.resume() - } - } - } + + let request = RPCRequest(method: SignProtocolMethod.sessionPropose.method, params: proposal) + try await networkingInteractor.request(request, topic: pairingTopic, tag: SignProtocolMethod.sessionPropose.requestTag) } func ping(topic: String, completion: @escaping ((Result) -> Void)) { @@ -86,15 +81,16 @@ final class PairingEngine { logger.debug("Could not find pairing to ping for topic \(topic)") return } - networkingInteractor.requestPeerResponse(.wcPairingPing, onTopic: topic) { [unowned self] result in - switch result { - case .success: - logger.debug("Did receive ping response") - completion(.success(())) - case .failure(let error): - logger.debug("error: \(error)") - } - } +// TODO: Ping disabled +// networkingInteractor.requestPeerResponse(.wcPairingPing, onTopic: topic) { [unowned self] result in +// switch result { +// case .success: +// logger.debug("Did receive ping response") +// completion(.success(())) +// case .failure(let error): +// logger.debug("error: \(error)") +// } +// } } } @@ -103,26 +99,23 @@ final class PairingEngine { private extension PairingEngine { func setupNetworkingSubscriptions() { - networkingInteractor.transportConnectionPublisher - .sink { [unowned self] (_) in - let topics = pairingStore.getAll() - .map {$0.topic} - topics.forEach { topic in Task {try? await networkingInteractor.subscribe(topic: topic)}} - }.store(in: &publishers) + networkingInteractor.socketConnectionStatusPublisher + .sink { [unowned self] status in + guard status == .connected else { return } + pairingStore.getAll() + .forEach { pairing in + Task(priority: .high) { try await networkingInteractor.subscribe(topic: pairing.topic) } + } + } + .store(in: &publishers) - networkingInteractor.wcRequestPublisher - .sink { [unowned self] subscriptionPayload in - switch subscriptionPayload.wcRequest.params { - case .pairingPing: - wcPairingPing(subscriptionPayload) - default: - return + networkingInteractor.requestSubscription(on: SignProtocolMethod.pairingPing) + .sink { [unowned self] (payload: RequestSubscriptionPayload) in + Task(priority: .high) { + try await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, tag: SignProtocolMethod.pairingPing.responseTag) } - }.store(in: &publishers) - } - - func wcPairingPing(_ payload: WCRequestSubscriptionPayload) { - networkingInteractor.respondSuccess(for: payload) + } + .store(in: &publishers) } func setupExpirationHandling() { diff --git a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift index 35934cf24..0905d4f3d 100644 --- a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift @@ -33,7 +33,9 @@ final class SessionEngine { self.sessionStore = sessionStore self.logger = logger - setupNetworkingSubscriptions() + setupConnectionSubscriptions() + setupRequestSubscriptions() + setupResponseSubscriptions() setupExpirationSubscriptions() } @@ -74,17 +76,16 @@ final class SessionEngine { let chainRequest = SessionType.RequestParams.Request(method: request.method, params: request.params) let sessionRequestParams = SessionType.RequestParams(request: chainRequest, chainId: request.chainId) - let payload = WCRequest.sessionRequest(sessionRequestParams) - let rpcRequest = RPCRequest(method: WCRequest.Method.sessionRequest.method, params: payload) - try await networkingInteractor.request(rpcRequest, topic: request.topic, tag: WCRequest.Method.sessionRequest.requestTag) + let rpcRequest = RPCRequest(method: SignProtocolMethod.sessionRequest.method, params: sessionRequestParams) + try await networkingInteractor.request(rpcRequest, topic: request.topic, tag: SignProtocolMethod.sessionRequest.requestTag) } - func respondSessionRequest(topic: String, response: JsonRpcResult) async throws { + func respondSessionRequest(topic: String, requestId: RPCID, response: RPCResult) async throws { guard sessionStore.hasSession(forTopic: topic) else { throw Errors.sessionNotFound(topic: topic) } - // TODO: ??? -// try await networkingInteractor.respond(topic: topic, response: response, tag: 1109) // FIXME: Hardcoded tag + let response = RPCResponse(id: requestId, result: response) + try await networkingInteractor.respond(topic: topic, response: response, tag: 1109) // FIXME: Hardcoded tag } func emit(topic: String, event: SessionType.EventParams.Event, chainId: Blockchain) async throws { @@ -95,9 +96,8 @@ final class SessionEngine { guard session.hasPermission(forEvent: event.name, onChain: chainId) else { throw WalletConnectError.invalidEvent } - let params = SessionType.EventParams(event: event, chainId: chainId) - let rpcRequest = RPCRequest(method: WCRequest.Method.sessionEvent.method, params: params) - try await networkingInteractor.request(rpcRequest, topic: topic, tag: WCRequest.Method.sessionEvent.requestTag) + let rpcRequest = RPCRequest(method: SignProtocolMethod.sessionEvent.method, params: SessionType.EventParams(event: event, chainId: chainId)) + try await networkingInteractor.request(rpcRequest, topic: topic, tag: SignProtocolMethod.sessionEvent.requestTag) } } @@ -105,7 +105,7 @@ final class SessionEngine { private extension SessionEngine { - func setupNetworkingSubscriptions() { + func setupConnectionSubscriptions() { networkingInteractor.socketConnectionStatusPublisher .sink { [unowned self] status in guard status == .connected else { return } @@ -115,38 +115,49 @@ private extension SessionEngine { } } .store(in: &publishers) + } - networkingInteractor.requestSubscription(on: nil) - .sink { [unowned self] (payload: RequestSubscriptionPayload) in - switch payload.request { - case .sessionDelete(let params): - onSessionDelete(payload, params: params) - case .sessionRequest(let params): - onSessionRequest(payload, params: params) - case .sessionPing: - onSessionPing(payload) - case .sessionEvent(let params): - onSessionEvent(payload, params: params) - default: return - } - } - .store(in: &publishers) + func setupRequestSubscriptions() { + networkingInteractor.requestSubscription(on: SignProtocolMethod.sessionDelete) + .sink { [unowned self] (payload: RequestSubscriptionPayload) in + onSessionDelete(payload: payload) + }.store(in: &publishers) + + networkingInteractor.requestSubscription(on: SignProtocolMethod.sessionRequest) + .sink { [unowned self] (payload: RequestSubscriptionPayload) in + onSessionRequest(payload: payload) + }.store(in: &publishers) + + networkingInteractor.requestSubscription(on: SignProtocolMethod.sessionPing) + .sink { [unowned self] (payload: RequestSubscriptionPayload) in + onSessionPing(payload: payload) + }.store(in: &publishers) + + networkingInteractor.requestSubscription(on: SignProtocolMethod.sessionEvent) + .sink { [unowned self] (payload: RequestSubscriptionPayload) in + onSessionEvent(payload: payload) + }.store(in: &publishers) + } - networkingInteractor.responseSubscription(on: nil) - .sink { [unowned self] (payload: ResponseSubscriptionPayload) in - switch payload.request { - case .sessionRequest(let params): - // TODO: ??? Chain ID from request is ok? - // Need to check absolute string - let response = Response(topic: payload.topic, chainId: params.chainId.absoluteString, result: payload.response) - onSessionResponse?(response) - default: - break - } + func setupResponseSubscriptions() { + networkingInteractor.responseSubscription(on: SignProtocolMethod.sessionRequest) + .sink { [unowned self] (payload: ResponseSubscriptionPayload) in + onSessionResponse?(Response( + topic: payload.topic, + chainId: payload.request.chainId.absoluteString, + result: payload.response + )) } .store(in: &publishers) } + func setupExpirationSubscriptions() { + sessionStore.onSessionExpiration = { [weak self] session in + self?.kms.deletePrivateKey(for: session.selfParticipant.publicKey) + self?.kms.deleteAgreementSecret(for: session.topic) + } + } + func respondError(payload: SubscriptionPayload, reason: ReasonCode, tag: Int) { Task { do { @@ -157,8 +168,8 @@ private extension SessionEngine { } } - func onSessionDelete(_ payload: SubscriptionPayload, params: SessionType.DeleteParams) { - let tag = WCRequest.Method.sessionDelete.responseTag + func onSessionDelete(payload: RequestSubscriptionPayload) { + let tag = SignProtocolMethod.sessionDelete.responseTag let topic = payload.topic guard sessionStore.hasSession(forTopic: topic) else { return respondError(payload: payload, reason: .noSessionForTopic, tag: tag) @@ -168,18 +179,18 @@ private extension SessionEngine { Task(priority: .high) { try await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, tag: tag) } - onSessionDelete?(topic, params) + onSessionDelete?(topic, payload.request) } - func onSessionRequest(_ payload: SubscriptionPayload, params: SessionType.RequestParams) { - let tag = WCRequest.Method.sessionRequest.responseTag + func onSessionRequest(payload: RequestSubscriptionPayload) { + let tag = SignProtocolMethod.sessionRequest.responseTag let topic = payload.topic let request = Request( id: payload.id, topic: payload.topic, - method: WCRequest.Method.sessionRequest.method, - params: params, - chainId: params.chainId) + method: payload.request.request.method, + params: payload.request.request.params, + chainId: payload.request.chainId) guard let session = sessionStore.getSession(forTopic: topic) else { return respondError(payload: payload, reason: .noSessionForTopic, tag: tag) @@ -193,32 +204,25 @@ private extension SessionEngine { onSessionRequest?(request) } - func onSessionPing(_ payload: SubscriptionPayload) { + func onSessionPing(payload: SubscriptionPayload) { Task(priority: .high) { - try await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, tag: WCRequest.Method.sessionPing.responseTag) + try await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, tag: SignProtocolMethod.sessionPing.responseTag) } } - func onSessionEvent(_ payload: SubscriptionPayload, params: SessionType.EventParams) { - let tag = WCRequest.Method.sessionEvent.responseTag - let event = params.event + func onSessionEvent(payload: RequestSubscriptionPayload) { + let tag = SignProtocolMethod.sessionEvent.responseTag + let event = payload.request.event let topic = payload.topic guard let session = sessionStore.getSession(forTopic: topic) else { return respondError(payload: payload, reason: .noSessionForTopic, tag: tag) } - guard session.peerIsController, session.hasPermission(forEvent: event.name, onChain: params.chainId) else { + guard session.peerIsController, session.hasPermission(forEvent: event.name, onChain: payload.request.chainId) else { return respondError(payload: payload, reason: .unauthorizedEvent(event.name), tag: tag) } Task(priority: .high) { try await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, tag: tag) } - onEventReceived?(topic, event.publicRepresentation(), params.chainId) - } - - func setupExpirationSubscriptions() { - sessionStore.onSessionExpiration = { [weak self] session in - self?.kms.deletePrivateKey(for: session.selfParticipant.publicKey) - self?.kms.deleteAgreementSecret(for: session.topic) - } + onEventReceived?(topic, event.publicRepresentation(), payload.request.chainId) } } diff --git a/Sources/WalletConnectSign/Engine/Controller/ControllerSessionStateMachine.swift b/Sources/WalletConnectSign/Engine/Controller/ControllerSessionStateMachine.swift index b76b63d50..be4733984 100644 --- a/Sources/WalletConnectSign/Engine/Controller/ControllerSessionStateMachine.swift +++ b/Sources/WalletConnectSign/Engine/Controller/ControllerSessionStateMachine.swift @@ -1,7 +1,9 @@ import Foundation +import Combine +import JSONRPC import WalletConnectUtils import WalletConnectKMS -import Combine +import WalletConnectNetworking final class ControllerSessionStateMachine { @@ -22,9 +24,8 @@ final class ControllerSessionStateMachine { self.kms = kms self.sessionStore = sessionStore self.logger = logger - networkingInteractor.responsePublisher.sink { [unowned self] response in - handleResponse(response) - }.store(in: &publishers) + + setupSubscriptions() } func update(topic: String, namespaces: [String: SessionNamespace]) async throws { @@ -33,7 +34,8 @@ final class ControllerSessionStateMachine { try Namespace.validate(namespaces) logger.debug("Controller will update methods") sessionStore.setSession(session) - try await networkingInteractor.request(.wcSessionUpdate(SessionType.UpdateParams(namespaces: namespaces)), onTopic: topic) + let request = RPCRequest(method: SignProtocolMethod.sessionUpdate.method, params: SessionType.UpdateParams(namespaces: namespaces)) + try await networkingInteractor.request(request, topic: topic, tag: SignProtocolMethod.sessionUpdate.requestTag) } func extend(topic: String, by ttl: Int64) async throws { @@ -42,28 +44,32 @@ final class ControllerSessionStateMachine { try session.updateExpiry(by: ttl) let newExpiry = Int64(session.expiryDate.timeIntervalSince1970 ) sessionStore.setSession(session) - try await networkingInteractor.request(.wcSessionExtend(SessionType.UpdateExpiryParams(expiry: newExpiry)), onTopic: topic) + let request = RPCRequest(method: SignProtocolMethod.sessionExtend.method, params: SessionType.UpdateExpiryParams(expiry: newExpiry)) + try await networkingInteractor.request(request, topic: topic, tag: SignProtocolMethod.sessionExtend.requestTag) } // MARK: - Handle Response - private func handleResponse(_ response: WCResponse) { - switch response.requestParams { - case .sessionUpdate(let payload): - handleUpdateResponse(response: response, payload: payload) - case .sessionExtend(let payload): - handleUpdateExpiryResponse(response: response, payload: payload) - default: - break - } + private func setupSubscriptions() { + networkingInteractor.responseSubscription(on: SignProtocolMethod.sessionUpdate) + .sink { [unowned self] (payload: ResponseSubscriptionPayload) in + handleUpdateResponse(payload: payload) + } + .store(in: &publishers) + + networkingInteractor.responseSubscription(on: SignProtocolMethod.sessionExtend) + .sink { [unowned self] (payload: ResponseSubscriptionPayload) in + handleUpdateExpiryResponse(payload: payload) + } + .store(in: &publishers) } - private func handleUpdateResponse(response: WCResponse, payload: SessionType.UpdateParams) { - guard var session = sessionStore.getSession(forTopic: response.topic) else { return } - switch response.result { + private func handleUpdateResponse(payload: ResponseSubscriptionPayload) { + guard var session = sessionStore.getSession(forTopic: payload.topic) else { return } + switch payload.response { case .response: do { - try session.updateNamespaces(payload.namespaces, timestamp: response.timestamp) + try session.updateNamespaces(payload.request.namespaces, timestamp: payload.id.timestamp) if sessionStore.setSessionIfNewer(session) { onNamespacesUpdate?(session.topic, session.namespaces) @@ -76,12 +82,12 @@ final class ControllerSessionStateMachine { } } - private func handleUpdateExpiryResponse(response: WCResponse, payload: SessionType.UpdateExpiryParams) { - guard var session = sessionStore.getSession(forTopic: response.topic) else { return } - switch response.result { + private func handleUpdateExpiryResponse(payload: ResponseSubscriptionPayload) { + guard var session = sessionStore.getSession(forTopic: payload.topic) else { return } + switch payload.response { case .response: do { - try session.updateExpiry(to: payload.expiry) + try session.updateExpiry(to: payload.request.expiry) sessionStore.setSession(session) onExtend?(session.topic, session.expiryDate) } catch { diff --git a/Sources/WalletConnectSign/Engine/NonController/NonControllerSessionStateMachine.swift b/Sources/WalletConnectSign/Engine/NonController/NonControllerSessionStateMachine.swift index 0e4e22f66..46ab1c516 100644 --- a/Sources/WalletConnectSign/Engine/NonController/NonControllerSessionStateMachine.swift +++ b/Sources/WalletConnectSign/Engine/NonController/NonControllerSessionStateMachine.swift @@ -1,12 +1,10 @@ import Foundation import WalletConnectUtils import WalletConnectKMS +import WalletConnectNetworking import Combine final class NonControllerSessionStateMachine { - enum Errors: Error { - case respondError(payload: WCRequestSubscriptionPayload, reason: ReasonCode) - } var onNamespacesUpdate: ((String, [String: SessionNamespace]) -> Void)? var onExtend: ((String, Date) -> Void)? @@ -25,32 +23,25 @@ final class NonControllerSessionStateMachine { self.kms = kms self.sessionStore = sessionStore self.logger = logger - setUpWCRequestHandling() + setupSubscriptions() } - private func setUpWCRequestHandling() { - networkingInteractor.wcRequestPublisher - .sink { [unowned self] subscriptionPayload in - do { - switch subscriptionPayload.wcRequest.params { - case .sessionUpdate(let updateParams): - try onSessionUpdateNamespacesRequest(payload: subscriptionPayload, updateParams: updateParams) - case .sessionExtend(let updateExpiryParams): - try onSessionUpdateExpiry(subscriptionPayload, updateExpiryParams: updateExpiryParams) - default: return - } - } catch Errors.respondError(let payload, let reason) { - respondError(payload: payload, reason: reason) - } catch { - logger.error("Unexpected Error: \(error.localizedDescription)") - } + private func setupSubscriptions() { + networkingInteractor.requestSubscription(on: SignProtocolMethod.sessionUpdate) + .sink { [unowned self] (payload: RequestSubscriptionPayload) in + onSessionUpdateNamespacesRequest(payload: payload, updateParams: payload.request) + }.store(in: &publishers) + + networkingInteractor.requestSubscription(on: SignProtocolMethod.sessionExtend) + .sink { [unowned self] (payload: RequestSubscriptionPayload) in + onSessionUpdateExpiry(payload: payload, updateExpiryParams: payload.request) }.store(in: &publishers) } - private func respondError(payload: WCRequestSubscriptionPayload, reason: ReasonCode) { + private func respondError(payload: SubscriptionPayload, reason: ReasonCode, tag: Int) { Task { do { - try await networkingInteractor.respondError(payload: payload, reason: reason) + try await networkingInteractor.respondError(topic: payload.topic, requestId: payload.id, tag: tag, reason: reason) } catch { logger.error("Respond Error failed with: \(error.localizedDescription)") } @@ -58,43 +49,51 @@ final class NonControllerSessionStateMachine { } // TODO: Update stored session namespaces - private func onSessionUpdateNamespacesRequest(payload: WCRequestSubscriptionPayload, updateParams: SessionType.UpdateParams) throws { + private func onSessionUpdateNamespacesRequest(payload: SubscriptionPayload, updateParams: SessionType.UpdateParams) { do { try Namespace.validate(updateParams.namespaces) } catch { - throw Errors.respondError(payload: payload, reason: .invalidUpdateRequest) + return respondError(payload: payload, reason: .invalidUpdateRequest, tag: SignProtocolMethod.sessionUpdate.responseTag) } guard var session = sessionStore.getSession(forTopic: payload.topic) else { - throw Errors.respondError(payload: payload, reason: .noSessionForTopic) + return respondError(payload: payload, reason: .noSessionForTopic, tag: SignProtocolMethod.sessionUpdate.responseTag) } guard session.peerIsController else { - throw Errors.respondError(payload: payload, reason: .unauthorizedUpdateRequest) + return respondError(payload: payload, reason: .unauthorizedUpdateRequest, tag: SignProtocolMethod.sessionUpdate.responseTag) } do { - try session.updateNamespaces(updateParams.namespaces, timestamp: payload.timestamp) + try session.updateNamespaces(updateParams.namespaces, timestamp: payload.id.timestamp) } catch { - throw Errors.respondError(payload: payload, reason: .invalidUpdateRequest) + return respondError(payload: payload, reason: .invalidUpdateRequest, tag: SignProtocolMethod.sessionUpdate.responseTag) } sessionStore.setSession(session) - networkingInteractor.respondSuccess(for: payload) + + Task(priority: .high) { + try await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, tag: SignProtocolMethod.sessionUpdate.responseTag) + } + onNamespacesUpdate?(session.topic, updateParams.namespaces) } - private func onSessionUpdateExpiry(_ payload: WCRequestSubscriptionPayload, updateExpiryParams: SessionType.UpdateExpiryParams) throws { + private func onSessionUpdateExpiry(payload: SubscriptionPayload, updateExpiryParams: SessionType.UpdateExpiryParams) { let topic = payload.topic guard var session = sessionStore.getSession(forTopic: topic) else { - throw Errors.respondError(payload: payload, reason: .noSessionForTopic) + return respondError(payload: payload, reason: .noSessionForTopic, tag: SignProtocolMethod.sessionExtend.responseTag) } guard session.peerIsController else { - throw Errors.respondError(payload: payload, reason: .unauthorizedExtendRequest) + return respondError(payload: payload, reason: .unauthorizedExtendRequest, tag: SignProtocolMethod.sessionExtend.responseTag) } do { try session.updateExpiry(to: updateExpiryParams.expiry) } catch { - throw Errors.respondError(payload: payload, reason: .invalidExtendRequest) + return respondError(payload: payload, reason: .invalidExtendRequest, tag: SignProtocolMethod.sessionExtend.responseTag) } sessionStore.setSession(session) - networkingInteractor.respondSuccess(for: payload) + + Task(priority: .high) { + try await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, tag: SignProtocolMethod.sessionExtend.responseTag) + } + onExtend?(session.topic, session.expiryDate) } } diff --git a/Sources/WalletConnectSign/JsonRpcHistory/JsonRpcHistory.swift b/Sources/WalletConnectSign/JsonRpcHistory/JsonRpcHistory.swift deleted file mode 100644 index 321bb5a62..000000000 --- a/Sources/WalletConnectSign/JsonRpcHistory/JsonRpcHistory.swift +++ /dev/null @@ -1,63 +0,0 @@ -import Foundation -import WalletConnectUtils - -protocol JsonRpcHistoryRecording { - func get(id: Int64) -> JsonRpcRecord? - func set(topic: String, request: WCRequest, chainId: String?) throws - func delete(topic: String) - func resolve(response: JsonRpcResult) throws -> JsonRpcRecord - func exist(id: Int64) -> Bool -} -// TODO -remove and use jsonrpc history only from utils -class JsonRpcHistory: JsonRpcHistoryRecording { - let storage: CodableStore - let logger: ConsoleLogging - - init(logger: ConsoleLogging, keyValueStore: CodableStore) { - self.logger = logger - self.storage = keyValueStore - } - - func get(id: Int64) -> JsonRpcRecord? { - try? storage.get(key: "\(id)") - } - - func set(topic: String, request: WCRequest, chainId: String? = nil) throws { - guard !exist(id: request.id) else { - throw WalletConnectError.internal(.jsonRpcDuplicateDetected) - } - logger.debug("Setting JSON-RPC request history record - ID: \(request.id)") - let record = JsonRpcRecord(id: request.id, topic: topic, request: JsonRpcRecord.Request(method: request.method, params: request.params), response: nil, chainId: chainId) - storage.set(record, forKey: "\(request.id)") - } - - func delete(topic: String) { - storage.getAll().forEach { record in - if record.topic == topic { - storage.delete(forKey: "\(record.id)") - } - } - } - - func resolve(response: JsonRpcResult) throws -> JsonRpcRecord { - logger.debug("Resolving JSON-RPC response - ID: \(response.id)") - guard var record = try? storage.get(key: "\(response.id)") else { - throw WalletConnectError.internal(.noJsonRpcRequestMatchingResponse) - } - if record.response != nil { - throw WalletConnectError.internal(.jsonRpcDuplicateDetected) - } else { - record.response = response - storage.set(record, forKey: "\(record.id)") - return record - } - } - - func exist(id: Int64) -> Bool { - return (try? storage.get(key: "\(id)")) != nil - } - - public func getPending() -> [JsonRpcRecord] { - storage.getAll().filter {$0.response == nil} - } -} diff --git a/Sources/WalletConnectSign/JsonRpcHistory/JsonRpcRecord.swift b/Sources/WalletConnectSign/JsonRpcHistory/JsonRpcRecord.swift deleted file mode 100644 index edfb14dc8..000000000 --- a/Sources/WalletConnectSign/JsonRpcHistory/JsonRpcRecord.swift +++ /dev/null @@ -1,15 +0,0 @@ -import Foundation -import WalletConnectUtils - -struct JsonRpcRecord: Codable { - let id: Int64 - let topic: String - let request: Request - var response: JsonRpcResult? - let chainId: String? - - struct Request: Codable { - let method: WCRequest.Method - let params: WCRequest.Params - } -} diff --git a/Sources/WalletConnectSign/Response.swift b/Sources/WalletConnectSign/Response.swift index 8dd4a0f0f..c01e438e5 100644 --- a/Sources/WalletConnectSign/Response.swift +++ b/Sources/WalletConnectSign/Response.swift @@ -1,8 +1,9 @@ import Foundation +import JSONRPC import WalletConnectUtils public struct Response: Codable { public let topic: String public let chainId: String? - public let result: JsonRpcResult + public let result: RPCResult } diff --git a/Sources/WalletConnectSign/Sign/Sign.swift b/Sources/WalletConnectSign/Sign/Sign.swift index 42685d660..1005410cc 100644 --- a/Sources/WalletConnectSign/Sign/Sign.swift +++ b/Sources/WalletConnectSign/Sign/Sign.swift @@ -1,10 +1,14 @@ import Foundation +import Combine +import JSONRPC import WalletConnectUtils import WalletConnectRelay -import Combine +import WalletConnectNetworking public typealias Account = WalletConnectUtils.Account public typealias Blockchain = WalletConnectUtils.Blockchain +public typealias Reason = WalletConnectNetworking.Reason +public typealias RPCID = JSONRPC.RPCID /// Sign instatnce wrapper /// diff --git a/Sources/WalletConnectSign/Sign/SignClient.swift b/Sources/WalletConnectSign/Sign/SignClient.swift index befbca947..0d774c529 100644 --- a/Sources/WalletConnectSign/Sign/SignClient.swift +++ b/Sources/WalletConnectSign/Sign/SignClient.swift @@ -1,8 +1,10 @@ import Foundation +import Combine +import JSONRPC import WalletConnectRelay import WalletConnectUtils import WalletConnectKMS -import Combine +import WalletConnectNetworking /// WalletConnect Sign Client /// @@ -94,7 +96,7 @@ public final class SignClient { private let disconnectService: DisconnectService private let nonControllerSessionStateMachine: NonControllerSessionStateMachine private let controllerSessionStateMachine: ControllerSessionStateMachine - private let history: JsonRpcHistory + private let history: RPCHistory private let cleanupService: CleanupService private let sessionProposalPublisherSubject = PassthroughSubject() @@ -121,7 +123,7 @@ public final class SignClient { nonControllerSessionStateMachine: NonControllerSessionStateMachine, controllerSessionStateMachine: ControllerSessionStateMachine, disconnectService: DisconnectService, - history: JsonRpcHistory, + history: RPCHistory, cleanupService: CleanupService ) { self.logger = logger @@ -221,9 +223,10 @@ public final class SignClient { /// For the wallet to respond on pending dApp's JSON-RPC request /// - Parameters: /// - topic: Topic of the session for which the request was received. + /// - requestId: RPC request ID /// - response: Your JSON RPC response or an error. - public func respond(topic: String, response: JsonRpcResult) async throws { - try await sessionEngine.respondSessionRequest(topic: topic, response: response) + public func respond(topic: String, requestId: RPCID, response: RPCResult) async throws { + try await sessionEngine.respondSessionRequest(topic: topic, requestId: requestId, response: response) } /// Ping method allows to check if peer client is online and is subscribing for given topic @@ -289,10 +292,9 @@ public final class SignClient { /// - Parameter topic: topic representing session for which you want to get pending requests. If nil, you will receive pending requests for all active sessions. public func getPendingRequests(topic: String? = nil) -> [Request] { let pendingRequests: [Request] = history.getPending() - .filter {$0.request.method == .sessionRequest} .compactMap { - guard case let .sessionRequest(payloadRequest) = $0.request.params else {return nil} - return Request(id: $0.id, topic: $0.topic, method: payloadRequest.request.method, params: payloadRequest.request.params, chainId: payloadRequest.chainId) + guard let request = try? $0.request.params?.get(SessionType.RequestParams.self) else { return nil } + return Request(id: $0.id, topic: $0.topic, method: request.request.method, params: request.request.params, chainId: request.chainId) } if let topic = topic { return pendingRequests.filter {$0.topic == topic} @@ -303,11 +305,13 @@ public final class SignClient { /// - Parameter id: id of a wc_sessionRequest jsonrpc request /// - Returns: json rpc record object for given id or nil if record for give id does not exits - public func getSessionRequestRecord(id: Int64) -> WalletConnectUtils.JsonRpcRecord? { - guard let record = history.get(id: id), - case .sessionRequest(let payload) = record.request.params else {return nil} - let request = WalletConnectUtils.JsonRpcRecord.Request(method: payload.request.method, params: payload.request.params) - return WalletConnectUtils.JsonRpcRecord(id: record.id, topic: record.topic, request: request, response: record.response, chainId: record.chainId) + public func getSessionRequestRecord(id: Int64) -> Request? { + guard + let record = history.get(recordId: RPCID(id)), + let request = try? record.request.params?.get(SessionType.RequestParams.self) + else { return nil } + + return Request(id: record.id, topic: record.topic, method: record.request.method, params: request, chainId: request.chainId) } #if DEBUG @@ -326,7 +330,7 @@ public final class SignClient { sessionProposalPublisherSubject.send(proposal) } approveEngine.onSessionRejected = { [unowned self] proposal, reason in - sessionRejectionPublisherSubject.send((proposal, reason.publicRepresentation())) + sessionRejectionPublisherSubject.send((proposal, reason)) } approveEngine.onSessionSettle = { [unowned self] settledSession in sessionSettlePublisherSubject.send(settledSession) @@ -335,7 +339,7 @@ public final class SignClient { sessionRequestPublisherSubject.send(sessionRequest) } sessionEngine.onSessionDelete = { [unowned self] topic, reason in - sessionDeletePublisherSubject.send((topic, reason.publicRepresentation())) + sessionDeletePublisherSubject.send((topic, reason)) } controllerSessionStateMachine.onNamespacesUpdate = { [unowned self] topic, namespaces in sessionUpdatePublisherSubject.send((topic, namespaces)) diff --git a/Sources/WalletConnectSign/Sign/SignClientFactory.swift b/Sources/WalletConnectSign/Sign/SignClientFactory.swift index 9a0412ad4..3ca457f06 100644 --- a/Sources/WalletConnectSign/Sign/SignClientFactory.swift +++ b/Sources/WalletConnectSign/Sign/SignClientFactory.swift @@ -3,6 +3,7 @@ import WalletConnectRelay import WalletConnectUtils import WalletConnectKMS import WalletConnectPairing +import WalletConnectNetworking public struct SignClientFactory { @@ -25,12 +26,12 @@ public struct SignClientFactory { static func create(metadata: AppMetadata, logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, relayClient: RelayClient) -> SignClient { let kms = KeyManagementService(keychain: keychainStorage) let serializer = Serializer(kms: kms) - let history = JsonRpcHistory(logger: logger, keyValueStore: CodableStore(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.jsonRpcHistory.rawValue)) - let networkingInteractor = NetworkInteractor(relayClient: relayClient, serializer: serializer, logger: logger, jsonRpcHistory: history) + let rpcHistory = RPCHistory(keyValueStore: CodableStore(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.jsonRpcHistory.rawValue)) + let networkingInteractor = NetworkingInteractor(relayClient: relayClient, serializer: serializer, logger: logger, rpcHistory: rpcHistory) let pairingStore = PairingStorage(storage: SequenceStore(store: .init(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.pairings.rawValue))) let sessionStore = SessionStorage(storage: SequenceStore(store: .init(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.sessions.rawValue))) let sessionToPairingTopic = CodableStore(defaults: RuntimeKeyValueStorage(), identifier: StorageDomainIdentifiers.sessionToPairingTopic.rawValue) - let proposalPayloadsStore = CodableStore(defaults: RuntimeKeyValueStorage(), identifier: StorageDomainIdentifiers.proposals.rawValue) + let proposalPayloadsStore = CodableStore>(defaults: RuntimeKeyValueStorage(), identifier: StorageDomainIdentifiers.proposals.rawValue) let pairingEngine = PairingEngine(networkingInteractor: networkingInteractor, kms: kms, pairingStore: pairingStore, metadata: metadata, logger: logger) let sessionEngine = SessionEngine(networkingInteractor: networkingInteractor, kms: kms, sessionStore: sessionStore, logger: logger) let nonControllerSessionStateMachine = NonControllerSessionStateMachine(networkingInteractor: networkingInteractor, kms: kms, sessionStore: sessionStore, logger: logger) @@ -51,7 +52,7 @@ public struct SignClientFactory { approveEngine: approveEngine, nonControllerSessionStateMachine: nonControllerSessionStateMachine, controllerSessionStateMachine: controllerSessionStateMachine, disconnectService: disconnectService, - history: history, + history: rpcHistory, cleanupService: cleanupService ) return client diff --git a/Sources/WalletConnectSign/Types/Session/SessionType.swift b/Sources/WalletConnectSign/Types/Session/SessionType.swift index 054704eae..f450e94aa 100644 --- a/Sources/WalletConnectSign/Types/Session/SessionType.swift +++ b/Sources/WalletConnectSign/Types/Session/SessionType.swift @@ -1,5 +1,6 @@ import Foundation import WalletConnectUtils +import WalletConnectNetworking // Internal namespace for session payloads. internal enum SessionType { @@ -24,7 +25,7 @@ internal enum SessionType { typealias DeleteParams = SessionType.Reason - struct Reason: Codable, Equatable { + struct Reason: Codable, Equatable, WalletConnectNetworking.Reason { let code: Int let message: String @@ -64,15 +65,3 @@ internal enum SessionType { let expiry: Int64 } } - -internal extension Reason { - func internalRepresentation() -> SessionType.Reason { - SessionType.Reason(code: self.code, message: self.message) - } -} - -extension SessionType.Reason { - func publicRepresentation() -> Reason { - Reason(code: self.code, message: self.message) - } -} diff --git a/Sources/WalletConnectSign/Types/SignProtocolMethod.swift b/Sources/WalletConnectSign/Types/SignProtocolMethod.swift new file mode 100644 index 000000000..90d4a0c54 --- /dev/null +++ b/Sources/WalletConnectSign/Types/SignProtocolMethod.swift @@ -0,0 +1,72 @@ +import Foundation +import JSONRPC +import WalletConnectPairing +import WalletConnectUtils +import WalletConnectNetworking + +enum SignProtocolMethod: ProtocolMethod { + case pairingDelete + case pairingPing + case sessionPropose + case sessionSettle + case sessionUpdate + case sessionExtend + case sessionDelete + case sessionRequest + case sessionPing + case sessionEvent + + var method: String { + switch self { + case .pairingDelete: + return "wc_pairingDelete" + case .pairingPing: + return "wc_pairingPing" + case .sessionPropose: + return "wc_sessionPropose" + case .sessionSettle: + return "wc_sessionSettle" + case .sessionUpdate: + return "wc_sessionUpdate" + case .sessionExtend: + return "wc_sessionExtend" + case .sessionDelete: + return "wc_sessionDelete" + case .sessionRequest: + return "wc_sessionRequest" + case .sessionPing: + return "wc_sessionPing" + case .sessionEvent: + return "wc_sessionEvent" + } + } + + var requestTag: Int { + switch self { + case .pairingDelete: + return 1000 + case .pairingPing: + return 1002 + case .sessionPropose: + return 1100 + case .sessionSettle: + return 1102 + case .sessionUpdate: + return 1104 + case .sessionExtend: + return 1106 + case .sessionDelete: + return 1112 + case .sessionRequest: + return 1108 + case .sessionPing: + return 1114 + case .sessionEvent: + return 1110 + } + } + + var responseTag: Int { + return requestTag + 1 + } +} diff --git a/Sources/WalletConnectSign/Types/WCRequest.swift b/Sources/WalletConnectSign/Types/WCRequest.swift deleted file mode 100644 index a4a7e44df..000000000 --- a/Sources/WalletConnectSign/Types/WCRequest.swift +++ /dev/null @@ -1,85 +0,0 @@ -import Foundation -import JSONRPC -import WalletConnectPairing -import WalletConnectUtils -import WalletConnectNetworking - -enum WCRequest: Codable { - case pairingDelete(PairingType.DeleteParams) - case pairingPing(PairingType.PingParams) - case sessionPropose(SessionType.ProposeParams) - case sessionSettle(SessionType.SettleParams) - case sessionUpdate(SessionType.UpdateParams) - case sessionExtend(SessionType.UpdateExpiryParams) - case sessionDelete(SessionType.DeleteParams) - case sessionRequest(SessionType.RequestParams) - case sessionPing(SessionType.PingParams) - case sessionEvent(SessionType.EventParams) - - enum Method: ProtocolMethod { - case pairingDelete - case pairingPing - case sessionPropose - case sessionSettle - case sessionUpdate - case sessionExtend - case sessionDelete - case sessionRequest - case sessionPing - case sessionEvent - - var method: String { - switch self { - case .pairingDelete: - return "wc_pairingDelete" - case .pairingPing: - return "wc_pairingPing" - case .sessionPropose: - return "wc_sessionPropose" - case .sessionSettle: - return "wc_sessionSettle" - case .sessionUpdate: - return "wc_sessionUpdate" - case .sessionExtend: - return "wc_sessionExtend" - case .sessionDelete: - return "wc_sessionDelete" - case .sessionRequest: - return "wc_sessionRequest" - case .sessionPing: - return "wc_sessionPing" - case .sessionEvent: - return "wc_sessionEvent" - } - } - - var requestTag: Int { - switch self { - case .pairingDelete: - return 1000 - case .pairingPing: - return 1002 - case .sessionPropose: - return 1100 - case .sessionSettle: - return 1102 - case .sessionUpdate: - return 1104 - case .sessionExtend: - return 1106 - case .sessionDelete: - return 1112 - case .sessionRequest: - return 1108 - case .sessionPing: - return 1114 - case .sessionEvent: - return 1110 - } - } - - var responseTag: Int { - return requestTag + 1 - } - } -} diff --git a/Sources/WalletConnectUtils/JSONRPC/JSONRPCErrorResponse.swift b/Sources/WalletConnectUtils/JSONRPC/JSONRPCErrorResponse.swift deleted file mode 100644 index 15f2c3de1..000000000 --- a/Sources/WalletConnectUtils/JSONRPC/JSONRPCErrorResponse.swift +++ /dev/null @@ -1,27 +0,0 @@ -import Foundation - -public struct JSONRPCErrorResponse: Error, Equatable, Codable { - public let jsonrpc = "2.0" - public let id: Int64 - public let error: JSONRPCErrorResponse.Error - - enum CodingKeys: String, CodingKey { - case jsonrpc - case id - case error - } - - public init(id: Int64, error: JSONRPCErrorResponse.Error) { - self.id = id - self.error = error - } - - public struct Error: Codable, Equatable { - public let code: Int - public let message: String - public init(code: Int, message: String) { - self.code = code - self.message = message - } - } -} diff --git a/Sources/WalletConnectUtils/JSONRPC/JSONRPCRequest.swift b/Sources/WalletConnectUtils/JSONRPC/JSONRPCRequest.swift deleted file mode 100644 index 2c3f57bb9..000000000 --- a/Sources/WalletConnectUtils/JSONRPC/JSONRPCRequest.swift +++ /dev/null @@ -1,23 +0,0 @@ -import Foundation - -public struct JSONRPCRequest: Codable, Equatable { - - public let id: Int64 - public let jsonrpc: String - public let method: String - public let params: T - - public enum CodingKeys: CodingKey { - case id - case jsonrpc - case method - case params - } - - public init(id: Int64 = JsonRpcID.generate(), method: String, params: T) { - self.id = id - self.jsonrpc = "2.0" - self.method = method - self.params = params - } -} diff --git a/Sources/WalletConnectUtils/JSONRPC/JSONRPCResponse.swift b/Sources/WalletConnectUtils/JSONRPC/JSONRPCResponse.swift deleted file mode 100644 index e7dd48923..000000000 --- a/Sources/WalletConnectUtils/JSONRPC/JSONRPCResponse.swift +++ /dev/null @@ -1,18 +0,0 @@ -import Foundation - -public struct JSONRPCResponse: Codable, Equatable { - public let jsonrpc = "2.0" - public let id: Int64 - public let result: T - - enum CodingKeys: String, CodingKey { - case jsonrpc - case id - case result - } - - public init(id: Int64, result: T) { - self.id = id - self.result = result - } -} diff --git a/Sources/WalletConnectUtils/JSONRPC/JsonRpcResult.swift b/Sources/WalletConnectUtils/JSONRPC/JsonRpcResult.swift deleted file mode 100644 index 611712500..000000000 --- a/Sources/WalletConnectUtils/JSONRPC/JsonRpcResult.swift +++ /dev/null @@ -1,24 +0,0 @@ -import Foundation - -public enum JsonRpcResult: Codable { - case error(JSONRPCErrorResponse) - case response(JSONRPCResponse) - - public var id: Int64 { - switch self { - case .error(let value): - return value.id - case .response(let value): - return value.id - } - } - - public var value: Codable { - switch self { - case .error(let value): - return value - case .response(let value): - return value - } - } -} diff --git a/Sources/WalletConnectUtils/JsonRpcHistory.swift b/Sources/WalletConnectUtils/JsonRpcHistory.swift deleted file mode 100644 index 9616b794b..000000000 --- a/Sources/WalletConnectUtils/JsonRpcHistory.swift +++ /dev/null @@ -1,58 +0,0 @@ -import Foundation - -public class JsonRpcHistory where T: Codable&Equatable { - enum RecordingError: Error { - case jsonRpcDuplicateDetected - case noJsonRpcRequestMatchingResponse - } - private let storage: CodableStore - private let logger: ConsoleLogging - - public init(logger: ConsoleLogging, keyValueStore: CodableStore) { - self.logger = logger - self.storage = keyValueStore - } - - public func get(id: Int64) -> JsonRpcRecord? { - try? storage.get(key: "\(id)") - } - - public func set(topic: String, request: JSONRPCRequest, chainId: String? = nil) throws { - guard !exist(id: request.id) else { - throw RecordingError.jsonRpcDuplicateDetected - } - logger.debug("Setting JSON-RPC request history record") - let record = JsonRpcRecord(id: request.id, topic: topic, request: JsonRpcRecord.Request(method: request.method, params: AnyCodable(request.params)), response: nil, chainId: chainId) - storage.set(record, forKey: "\(request.id)") - } - - public func delete(topic: String) { - storage.getAll().forEach { record in - if record.topic == topic { - storage.delete(forKey: "\(record.id)") - } - } - } - - public func resolve(response: JsonRpcResult) throws -> JsonRpcRecord { - logger.debug("Resolving JSON-RPC response - ID: \(response.id)") - guard var record = try? storage.get(key: "\(response.id)") else { - throw RecordingError.noJsonRpcRequestMatchingResponse - } - if record.response != nil { - throw RecordingError.jsonRpcDuplicateDetected - } else { - record.response = response - storage.set(record, forKey: "\(record.id)") - return record - } - } - - public func exist(id: Int64) -> Bool { - return (try? storage.get(key: "\(id)")) != nil - } - - public func getPending() -> [JsonRpcRecord] { - storage.getAll().filter {$0.response == nil} - } -} diff --git a/Sources/WalletConnectUtils/JsonRpcRecord.swift b/Sources/WalletConnectUtils/JsonRpcRecord.swift deleted file mode 100644 index 48013221f..000000000 --- a/Sources/WalletConnectUtils/JsonRpcRecord.swift +++ /dev/null @@ -1,27 +0,0 @@ -import Foundation - -public struct JsonRpcRecord: Codable { - public let id: Int64 - public let topic: String - public let request: Request - public var response: JsonRpcResult? - public let chainId: String? - - public init(id: Int64, topic: String, request: JsonRpcRecord.Request, response: JsonRpcResult? = nil, chainId: String?) { - self.id = id - self.topic = topic - self.request = request - self.response = response - self.chainId = chainId - } - - public struct Request: Codable { - public let method: String - public let params: AnyCodable - - public init(method: String, params: AnyCodable) { - self.method = method - self.params = params - } - } -} diff --git a/Tests/TestingUtils/NetworkingInteractorMock.swift b/Tests/TestingUtils/NetworkingInteractorMock.swift index 0ddc96adc..7942fdac1 100644 --- a/Tests/TestingUtils/NetworkingInteractorMock.swift +++ b/Tests/TestingUtils/NetworkingInteractorMock.swift @@ -26,10 +26,9 @@ public class NetworkingInteractorMock: NetworkInteracting { } // TODO: Avoid copy paste from NetworkInteractor - public func requestSubscription(on request: ProtocolMethod?) -> AnyPublisher, Never> { + public func requestSubscription(on request: ProtocolMethod) -> AnyPublisher, Never> { return requestPublisher .filter { rpcRequest in - guard let request = request else { return true } return rpcRequest.request.method == request.method } .compactMap { topic, rpcRequest in @@ -40,10 +39,9 @@ public class NetworkingInteractorMock: NetworkInteracting { } // TODO: Avoid copy paste from NetworkInteractor - public func responseSubscription(on request: ProtocolMethod?) -> AnyPublisher, Never> { + public func responseSubscription(on request: ProtocolMethod) -> AnyPublisher, Never> { return responsePublisher .filter { rpcRequest in - guard let request = request else { return true } return rpcRequest.request.method == request.method } .compactMap { topic, rpcRequest, rpcResponse in @@ -56,12 +54,13 @@ public class NetworkingInteractorMock: NetworkInteracting { .eraseToAnyPublisher() } - public func responseErrorSubscription(on request: ProtocolMethod) -> AnyPublisher { + // TODO: Avoid copy paste from NetworkInteractor + public func responseErrorSubscription(on request: ProtocolMethod) -> AnyPublisher, Never> { return responsePublisher .filter { $0.request.method == request.method } - .compactMap { (_, _, rpcResponse) in - guard let id = rpcResponse.id, let error = rpcResponse.error else { return nil } - return ResponseSubscriptionErrorPayload(id: id, error: error) + .compactMap { (topic, rpcRequest, rpcResponse) in + guard let id = rpcResponse.id, let request = try? rpcRequest.params?.get(Request.self), let error = rpcResponse.error else { return nil } + return ResponseSubscriptionErrorPayload(id: id, topic: topic, request: request, error: error) } .eraseToAnyPublisher() } From 41a54d556f6fded8082ad9a92c296fcf4cef9593 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 8 Sep 2022 22:51:54 +0300 Subject: [PATCH 007/175] Sign new ping --- .../Sign/Helpers/ClientDelegate.swift | 10 +++++ .../Sign/SignClientTests.swift | 41 +++++++++++++++++-- .../Services/PairingPingService.swift | 12 +++--- .../Services/PingRequester.swift | 17 ++++---- .../Services/PingResponder.swift | 11 +++-- .../Services/PingResponseSubscriber.swift | 11 +++-- .../Engine/Common/PairingEngine.swift | 17 -------- .../Engine/Common/SessionEngine.swift | 17 -------- .../Services/SessionPingService.swift | 35 ++++++++++++++++ .../WalletConnectSign/Sign/SignClient.swift | 39 ++++++++++++++---- .../Sign/SignClientFactory.swift | 4 ++ 11 files changed, 147 insertions(+), 67 deletions(-) create mode 100644 Sources/WalletConnectSign/Services/SessionPingService.swift diff --git a/Example/IntegrationTests/Sign/Helpers/ClientDelegate.swift b/Example/IntegrationTests/Sign/Helpers/ClientDelegate.swift index 71ac99ab3..ab7b43e11 100644 --- a/Example/IntegrationTests/Sign/Helpers/ClientDelegate.swift +++ b/Example/IntegrationTests/Sign/Helpers/ClientDelegate.swift @@ -14,6 +14,8 @@ class ClientDelegate { var onSessionDelete: (() -> Void)? var onSessionUpdateNamespaces: ((String, [String: SessionNamespace]) -> Void)? var onSessionExtend: ((String, Date) -> Void)? + var onSessionPing: ((String) -> Void)? + var onPairingPing: ((String) -> Void)? var onEventReceived: ((Session.Event, String) -> Void)? private var publishers = Set() @@ -63,5 +65,13 @@ class ClientDelegate { client.sessionExtendPublisher.sink { (topic, date) in self.onSessionExtend?(topic, date) }.store(in: &publishers) + + client.sessionPingResponsePublisher.sink { topic in + self.onSessionPing?(topic) + }.store(in: &publishers) + + client.pairingPingResponsePublisher.sink { topic in + self.onPairingPing?(topic) + }.store(in: &publishers) } } diff --git a/Example/IntegrationTests/Sign/SignClientTests.swift b/Example/IntegrationTests/Sign/SignClientTests.swift index 3dd004d78..8a30c72cc 100644 --- a/Example/IntegrationTests/Sign/SignClientTests.swift +++ b/Example/IntegrationTests/Sign/SignClientTests.swift @@ -137,20 +137,54 @@ final class SignClientTests: XCTestCase { wait(for: [sessionDeleteExpectation], timeout: defaultTimeout) } - func testNewPairingPing() async throws { + func testPairingPing() async throws { let pongResponseExpectation = expectation(description: "Ping sender receives a pong response") let uri = try await dapp.client.connect(requiredNamespaces: ProposalNamespace.stubRequired())! try await wallet.client.pair(uri: uri) let pairing = wallet.client.getPairings().first! - wallet.client.ping(topic: pairing.topic) { result in - if case .failure = result { XCTFail() } + + wallet.onPairingPing = { topic in + XCTAssertEqual(topic, pairing.topic) pongResponseExpectation.fulfill() } + + try await wallet.client.ping(topic: pairing.topic) + wait(for: [pongResponseExpectation], timeout: defaultTimeout) } + func testSessionPing() async throws { + let expectation = expectation(description: "Proposer receives ping response") + + let requiredNamespaces = ProposalNamespace.stubRequired() + let sessionNamespaces = SessionNamespace.make(toRespond: requiredNamespaces) + + wallet.onSessionProposal = { proposal in + Task(priority: .high) { + try! await self.wallet.client.approve(proposalId: proposal.id, namespaces: sessionNamespaces) + } + } + + dapp.onSessionSettled = { sessionSettled in + Task(priority: .high) { + try! await self.dapp.client.ping(topic: sessionSettled.topic) + } + } + + dapp.onSessionPing = { topic in + let session = self.wallet.client.getSessions().first! + XCTAssertEqual(topic, session.topic) + expectation.fulfill() + } + + let uri = try await dapp.client.connect(requiredNamespaces: requiredNamespaces)! + try await wallet.client.pair(uri: uri) + + wait(for: [expectation], timeout: .infinity) + } + func testSessionRequest() async throws { let requestExpectation = expectation(description: "Wallet expects to receive a request") let responseExpectation = expectation(description: "Dapp expects to receive a response") @@ -242,7 +276,6 @@ final class SignClientTests: XCTestCase { wait(for: [expectation], timeout: defaultTimeout) } - func testNewSessionOnExistingPairing() async { let dappSettlementExpectation = expectation(description: "Dapp settles session") dappSettlementExpectation.expectedFulfillmentCount = 2 diff --git a/Sources/WalletConnectPairing/Services/PairingPingService.swift b/Sources/WalletConnectPairing/Services/PairingPingService.swift index 6baef98e0..f435ec72b 100644 --- a/Sources/WalletConnectPairing/Services/PairingPingService.swift +++ b/Sources/WalletConnectPairing/Services/PairingPingService.swift @@ -1,8 +1,9 @@ -import WalletConnectUtils import Foundation +import WalletConnectUtils import WalletConnectNetworking public class PairingPingService { + private let pairingStorage: WCPairingStorage private let pingRequester: PingRequester private let pingResponder: PingResponder private let pingResponseSubscriber: PingResponseSubscriber @@ -20,13 +21,14 @@ public class PairingPingService { pairingStorage: WCPairingStorage, networkingInteractor: NetworkInteracting, logger: ConsoleLogging) { - pingRequester = PingRequester(pairingStorage: pairingStorage, networkingInteractor: networkingInteractor) - pingResponder = PingResponder(networkingInteractor: networkingInteractor, logger: logger) - pingResponseSubscriber = PingResponseSubscriber(networkingInteractor: networkingInteractor, logger: logger) + self.pairingStorage = pairingStorage + self.pingRequester = PingRequester(networkingInteractor: networkingInteractor, method: PairingProtocolMethod.ping) + self.pingResponder = PingResponder(networkingInteractor: networkingInteractor, method: PairingProtocolMethod.ping, logger: logger) + self.pingResponseSubscriber = PingResponseSubscriber(networkingInteractor: networkingInteractor, method: PairingProtocolMethod.ping, logger: logger) } public func ping(topic: String) async throws { + guard pairingStorage.hasPairing(forTopic: topic) else { return } try await pingRequester.ping(topic: topic) } - } diff --git a/Sources/WalletConnectPairing/Services/PingRequester.swift b/Sources/WalletConnectPairing/Services/PingRequester.swift index f936a8c9c..9def19d28 100644 --- a/Sources/WalletConnectPairing/Services/PingRequester.swift +++ b/Sources/WalletConnectPairing/Services/PingRequester.swift @@ -1,19 +1,18 @@ import Foundation -import WalletConnectNetworking import JSONRPC +import WalletConnectNetworking -class PingRequester { - private let pairingStorage: WCPairingStorage +public class PingRequester { + private let method: ProtocolMethod private let networkingInteractor: NetworkInteracting - init(pairingStorage: WCPairingStorage, networkingInteractor: NetworkInteracting) { - self.pairingStorage = pairingStorage + public init(networkingInteractor: NetworkInteracting, method: ProtocolMethod) { + self.method = method self.networkingInteractor = networkingInteractor } - func ping(topic: String) async throws { - guard pairingStorage.hasPairing(forTopic: topic) else { return } - let request = RPCRequest(method: PairingProtocolMethod.ping.rawValue, params: PairingPingParams()) - try await networkingInteractor.request(request, topic: topic, tag: PairingProtocolMethod.ping.requestTag) + public func ping(topic: String) async throws { + let request = RPCRequest(method: method.method, params: PairingPingParams()) + try await networkingInteractor.request(request, topic: topic, tag: method.requestTag) } } diff --git a/Sources/WalletConnectPairing/Services/PingResponder.swift b/Sources/WalletConnectPairing/Services/PingResponder.swift index 9ed8c0806..7718f6128 100644 --- a/Sources/WalletConnectPairing/Services/PingResponder.swift +++ b/Sources/WalletConnectPairing/Services/PingResponder.swift @@ -2,24 +2,27 @@ import Combine import WalletConnectUtils import WalletConnectNetworking -class PingResponder { +public class PingResponder { private let networkingInteractor: NetworkInteracting + private let method: ProtocolMethod private let logger: ConsoleLogging private var publishers = [AnyCancellable]() - init(networkingInteractor: NetworkInteracting, + public init(networkingInteractor: NetworkInteracting, + method: ProtocolMethod, logger: ConsoleLogging) { self.networkingInteractor = networkingInteractor + self.method = method self.logger = logger subscribePingRequests() } private func subscribePingRequests() { - networkingInteractor.requestSubscription(on: PairingProtocolMethod.ping) + networkingInteractor.requestSubscription(on: method) .sink { [unowned self] (payload: RequestSubscriptionPayload) in logger.debug("Responding for pairing ping") Task(priority: .high) { - try? await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, tag: PairingProtocolMethod.ping.responseTag) + try? await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, tag: method.responseTag) } } .store(in: &publishers) diff --git a/Sources/WalletConnectPairing/Services/PingResponseSubscriber.swift b/Sources/WalletConnectPairing/Services/PingResponseSubscriber.swift index d2ce74a89..e473bd3e4 100644 --- a/Sources/WalletConnectPairing/Services/PingResponseSubscriber.swift +++ b/Sources/WalletConnectPairing/Services/PingResponseSubscriber.swift @@ -2,22 +2,25 @@ import Combine import WalletConnectUtils import WalletConnectNetworking -class PingResponseSubscriber { +public class PingResponseSubscriber { private let networkingInteractor: NetworkInteracting + private let method: ProtocolMethod private let logger: ConsoleLogging private var publishers = [AnyCancellable]() - var onResponse: ((String)->())? + public var onResponse: ((String)->())? - init(networkingInteractor: NetworkInteracting, + public init(networkingInteractor: NetworkInteracting, + method: ProtocolMethod, logger: ConsoleLogging) { self.networkingInteractor = networkingInteractor + self.method = method self.logger = logger subscribePingResponses() } private func subscribePingResponses() { - networkingInteractor.responseSubscription(on: PairingProtocolMethod.ping) + networkingInteractor.responseSubscription(on: method) .sink { [unowned self] (payload: ResponseSubscriptionPayload) in onResponse?(payload.topic) } diff --git a/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift b/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift index 0561e6446..7c75d9876 100644 --- a/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift @@ -75,23 +75,6 @@ final class PairingEngine { let request = RPCRequest(method: SignProtocolMethod.sessionPropose.method, params: proposal) try await networkingInteractor.request(request, topic: pairingTopic, tag: SignProtocolMethod.sessionPropose.requestTag) } - - func ping(topic: String, completion: @escaping ((Result) -> Void)) { - guard pairingStore.hasPairing(forTopic: topic) else { - logger.debug("Could not find pairing to ping for topic \(topic)") - return - } -// TODO: Ping disabled -// networkingInteractor.requestPeerResponse(.wcPairingPing, onTopic: topic) { [unowned self] result in -// switch result { -// case .success: -// logger.debug("Did receive ping response") -// completion(.success(())) -// case .failure(let error): -// logger.debug("error: \(error)") -// } -// } - } } // MARK: Private diff --git a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift index 0905d4f3d..60435dc0e 100644 --- a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift @@ -47,23 +47,6 @@ final class SessionEngine { sessionStore.getAll().map {$0.publicRepresentation()} } - func ping(topic: String, completion: @escaping (Result) -> Void) { - guard sessionStore.hasSession(forTopic: topic) else { - logger.debug("Could not find session to ping for topic \(topic)") - return - } -// TODO: Ping disabled -// networkingInteractor.requestPeerResponse(.wcSessionPing, onTopic: topic) { [unowned self] result in -// switch result { -// case .success: -// logger.debug("Did receive ping response") -// completion(.success(())) -// case .failure(let error): -// logger.debug("error: \(error)") -// } -// } - } - func request(_ request: Request) async throws { logger.debug("will request on session topic: \(request.topic)") guard let session = sessionStore.getSession(forTopic: request.topic), session.acknowledged else { diff --git a/Sources/WalletConnectSign/Services/SessionPingService.swift b/Sources/WalletConnectSign/Services/SessionPingService.swift new file mode 100644 index 000000000..697dcab89 --- /dev/null +++ b/Sources/WalletConnectSign/Services/SessionPingService.swift @@ -0,0 +1,35 @@ +import Foundation +import WalletConnectPairing +import WalletConnectUtils +import WalletConnectNetworking + +class SessionPingService { + private let sessionStorage: WCSessionStorage + private let pingRequester: PingRequester + private let pingResponder: PingResponder + private let pingResponseSubscriber: PingResponseSubscriber + + var onResponse: ((String)->())? { + get { + return pingResponseSubscriber.onResponse + } + set { + pingResponseSubscriber.onResponse = newValue + } + } + + init( + sessionStorage: WCSessionStorage, + networkingInteractor: NetworkInteracting, + logger: ConsoleLogging) { + self.sessionStorage = sessionStorage + self.pingRequester = PingRequester(networkingInteractor: networkingInteractor, method: SignProtocolMethod.sessionPing) + self.pingResponder = PingResponder(networkingInteractor: networkingInteractor, method: SignProtocolMethod.sessionPing, logger: logger) + self.pingResponseSubscriber = PingResponseSubscriber(networkingInteractor: networkingInteractor, method: SignProtocolMethod.sessionPing, logger: logger) + } + + func ping(topic: String) async throws { + guard sessionStorage.hasSession(forTopic: topic) else { return } + try await pingRequester.ping(topic: topic) + } +} diff --git a/Sources/WalletConnectSign/Sign/SignClient.swift b/Sources/WalletConnectSign/Sign/SignClient.swift index 0d774c529..4d343153a 100644 --- a/Sources/WalletConnectSign/Sign/SignClient.swift +++ b/Sources/WalletConnectSign/Sign/SignClient.swift @@ -5,6 +5,7 @@ import WalletConnectRelay import WalletConnectUtils import WalletConnectKMS import WalletConnectNetworking +import WalletConnectPairing /// WalletConnect Sign Client /// @@ -83,6 +84,20 @@ public final class SignClient { sessionExtendPublisherSubject.eraseToAnyPublisher() } + /// Publisher that sends session topic when session ping received + /// + /// Event will be emited on controller and non-controller clients. + public var sessionPingResponsePublisher: AnyPublisher { + sessionPingResponsePublisherSubject.eraseToAnyPublisher() + } + + /// Publisher that sends pairing topic when pairing ping received + /// + /// Event will be emited on controller and non-controller clients. + public var pairingPingResponsePublisher: AnyPublisher { + pairingPingResponsePublisherSubject.eraseToAnyPublisher() + } + /// An object that loggs SDK's errors and info messages public let logger: ConsoleLogging @@ -94,6 +109,8 @@ public final class SignClient { private let sessionEngine: SessionEngine private let approveEngine: ApproveEngine private let disconnectService: DisconnectService + private let pairingPingService: PairingPingService + private let sessionPingService: SessionPingService private let nonControllerSessionStateMachine: NonControllerSessionStateMachine private let controllerSessionStateMachine: ControllerSessionStateMachine private let history: RPCHistory @@ -109,6 +126,8 @@ public final class SignClient { private let sessionUpdatePublisherSubject = PassthroughSubject<(sessionTopic: String, namespaces: [String: SessionNamespace]), Never>() private let sessionEventPublisherSubject = PassthroughSubject<(event: Session.Event, sessionTopic: String, chainId: Blockchain?), Never>() private let sessionExtendPublisherSubject = PassthroughSubject<(sessionTopic: String, date: Date), Never>() + private let sessionPingResponsePublisherSubject = PassthroughSubject() + private let pairingPingResponsePublisherSubject = PassthroughSubject() private var publishers = Set() @@ -120,6 +139,8 @@ public final class SignClient { pairEngine: PairEngine, sessionEngine: SessionEngine, approveEngine: ApproveEngine, + pairingPingService: PairingPingService, + sessionPingService: SessionPingService, nonControllerSessionStateMachine: NonControllerSessionStateMachine, controllerSessionStateMachine: ControllerSessionStateMachine, disconnectService: DisconnectService, @@ -132,6 +153,8 @@ public final class SignClient { self.pairEngine = pairEngine self.sessionEngine = sessionEngine self.approveEngine = approveEngine + self.pairingPingService = pairingPingService + self.sessionPingService = sessionPingService self.nonControllerSessionStateMachine = nonControllerSessionStateMachine self.controllerSessionStateMachine = controllerSessionStateMachine self.history = history @@ -238,15 +261,11 @@ public final class SignClient { /// - Parameters: /// - topic: Topic of a session or a pairing /// - completion: Result will be success on response or an error - public func ping(topic: String, completion: @escaping ((Result) -> Void)) { + public func ping(topic: String) async throws { if pairingEngine.hasPairing(for: topic) { - pairingEngine.ping(topic: topic) { result in - completion(result) - } + try await pairingPingService.ping(topic: topic) } else if sessionEngine.hasSession(for: topic) { - sessionEngine.ping(topic: topic) { result in - completion(result) - } + try await sessionPingService.ping(topic: topic) } } @@ -359,6 +378,12 @@ public final class SignClient { sessionEngine.onSessionResponse = { [unowned self] response in sessionResponsePublisherSubject.send(response) } + pairingPingService.onResponse = { [unowned self] topic in + pairingPingResponsePublisherSubject.send(topic) + } + sessionPingService.onResponse = { [unowned self] topic in + sessionPingResponsePublisherSubject.send(topic) + } } private func setUpConnectionObserving() { diff --git a/Sources/WalletConnectSign/Sign/SignClientFactory.swift b/Sources/WalletConnectSign/Sign/SignClientFactory.swift index 3ca457f06..694b68c48 100644 --- a/Sources/WalletConnectSign/Sign/SignClientFactory.swift +++ b/Sources/WalletConnectSign/Sign/SignClientFactory.swift @@ -42,6 +42,8 @@ public struct SignClientFactory { let deletePairingService = DeletePairingService(networkingInteractor: networkingInteractor, kms: kms, pairingStorage: pairingStore, logger: logger) let deleteSessionService = DeleteSessionService(networkingInteractor: networkingInteractor, kms: kms, sessionStore: sessionStore, logger: logger) let disconnectService = DisconnectService(deletePairingService: deletePairingService, deleteSessionService: deleteSessionService, pairingStorage: pairingStore, sessionStorage: sessionStore) + let sessionPingService = SessionPingService(sessionStorage: sessionStore, networkingInteractor: networkingInteractor, logger: logger) + let pairingPingService = PairingPingService(pairingStorage: pairingStore, networkingInteractor: networkingInteractor, logger: logger) let client = SignClient( logger: logger, @@ -50,6 +52,8 @@ public struct SignClientFactory { pairEngine: pairEngine, sessionEngine: sessionEngine, approveEngine: approveEngine, + pairingPingService: pairingPingService, + sessionPingService: sessionPingService, nonControllerSessionStateMachine: nonControllerSessionStateMachine, controllerSessionStateMachine: controllerSessionStateMachine, disconnectService: disconnectService, history: rpcHistory, From 3a734eac9f22585a1c611cd8edba9821d33c5a7a Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 8 Sep 2022 22:53:25 +0300 Subject: [PATCH 008/175] Cleanup --- .../NetworkInteractor/NetworkInteractor.swift | 259 ------------------ .../NetworkInteractor/NetworkRelaying.swift | 19 -- 2 files changed, 278 deletions(-) delete mode 100644 Sources/WalletConnectSign/NetworkInteractor/NetworkInteractor.swift delete mode 100644 Sources/WalletConnectSign/NetworkInteractor/NetworkRelaying.swift diff --git a/Sources/WalletConnectSign/NetworkInteractor/NetworkInteractor.swift b/Sources/WalletConnectSign/NetworkInteractor/NetworkInteractor.swift deleted file mode 100644 index f23cc922f..000000000 --- a/Sources/WalletConnectSign/NetworkInteractor/NetworkInteractor.swift +++ /dev/null @@ -1,259 +0,0 @@ -//import Foundation -//import Combine -//import WalletConnectUtils -//import WalletConnectKMS -// -//protocol NetworkInteracting: AnyObject { -// var transportConnectionPublisher: AnyPublisher {get} -// var wcRequestPublisher: AnyPublisher {get} -// var responsePublisher: AnyPublisher {get} -// /// Completes when request sent from a networking client -// func request(_ wcMethod: WCMethod, onTopic topic: String) async throws -// /// Completes with an acknowledgement from the relay network -// func requestNetworkAck(_ wcMethod: WCMethod, onTopic topic: String, completion: @escaping ((Error?) -> Void)) -// /// Completes with a peer response -// func requestPeerResponse(_ wcMethod: WCMethod, onTopic topic: String, completion: ((Result, JSONRPCErrorResponse>) -> Void)?) -// func respond(topic: String, response: JsonRpcResult, tag: Int) async throws -// func respondSuccess(payload: WCRequestSubscriptionPayload) async throws -// func respondSuccess(for payload: WCRequestSubscriptionPayload) -// func respondError(payload: WCRequestSubscriptionPayload, reason: ReasonCode) async throws -// func subscribe(topic: String) async throws -// func unsubscribe(topic: String) -//} -// -//extension NetworkInteracting { -// func request(_ wcMethod: WCMethod, onTopic topic: String) { -// requestPeerResponse(wcMethod, onTopic: topic, completion: nil) -// } -//} -// -//class NetworkInteractor: NetworkInteracting { -// -// private var publishers = Set() -// -// private var relayClient: NetworkRelaying -// private let serializer: Serializing -// private let jsonRpcHistory: JsonRpcHistoryRecording -// -// private let transportConnectionPublisherSubject = PassthroughSubject() -// private let responsePublisherSubject = PassthroughSubject() -// private let wcRequestPublisherSubject = PassthroughSubject() -// -// var transportConnectionPublisher: AnyPublisher { -// transportConnectionPublisherSubject.eraseToAnyPublisher() -// } -// var wcRequestPublisher: AnyPublisher { -// wcRequestPublisherSubject.eraseToAnyPublisher() -// } -// var responsePublisher: AnyPublisher { -// responsePublisherSubject.eraseToAnyPublisher() -// } -// -// let logger: ConsoleLogging -// -// init(relayClient: NetworkRelaying, -// serializer: Serializing, -// logger: ConsoleLogging, -// jsonRpcHistory: JsonRpcHistoryRecording) { -// self.relayClient = relayClient -// self.serializer = serializer -// self.logger = logger -// self.jsonRpcHistory = jsonRpcHistory -// setUpPublishers() -// } -// -// func request(_ wcMethod: WCMethod, onTopic topic: String) async throws { -// try await request(topic: topic, payload: wcMethod.asRequest()) -// } -// -// /// Completes when networking client sends a request -// func request(topic: String, payload: WCRequest) async throws { -// try jsonRpcHistory.set(topic: topic, request: payload, chainId: getChainId(payload)) -// let message = try serializer.serialize(topic: topic, encodable: payload) -// let prompt = shouldPrompt(payload.method) -// try await relayClient.publish(topic: topic, payload: message, tag: payload.tag, prompt: prompt) -// } -// -// func requestPeerResponse(_ wcMethod: WCMethod, onTopic topic: String, completion: ((Result, JSONRPCErrorResponse>) -> Void)?) { -// let payload = wcMethod.asRequest() -// do { -// try jsonRpcHistory.set(topic: topic, request: payload, chainId: getChainId(payload)) -// let message = try serializer.serialize(topic: topic, encodable: payload) -// let prompt = shouldPrompt(payload.method) -// relayClient.publish(topic: topic, payload: message, tag: payload.tag, prompt: prompt) { [weak self] error in -// guard let self = self else {return} -// if let error = error { -// self.logger.error(error) -// } else { -// var cancellable: AnyCancellable! -// cancellable = self.responsePublisher -// .filter {$0.result.id == payload.id} -// .sink { (response) in -// cancellable.cancel() -// self.logger.debug("WC Relay - received response on topic: \(topic)") -// switch response.result { -// case .response(let response): -// completion?(.success(response)) -// case .error(let error): -// self.logger.debug("Request error: \(error)") -// completion?(.failure(error)) -// } -// } -// } -// } -// } catch WalletConnectError.internal(.jsonRpcDuplicateDetected) { -// logger.info("Info: Json Rpc Duplicate Detected") -// } catch { -// logger.error(error) -// } -// } -// -// /// Completes with an acknowledgement from the relay network. -// /// completes with error if networking client was not able to send a message -// func requestNetworkAck(_ wcMethod: WCMethod, onTopic topic: String, completion: @escaping ((Error?) -> Void)) { -// do { -// let payload = wcMethod.asRequest() -// try jsonRpcHistory.set(topic: topic, request: payload, chainId: getChainId(payload)) -// let message = try serializer.serialize(topic: topic, encodable: payload) -// let prompt = shouldPrompt(payload.method) -// relayClient.publish(topic: topic, payload: message, tag: payload.tag, prompt: prompt) { error in -// completion(error) -// } -// } catch WalletConnectError.internal(.jsonRpcDuplicateDetected) { -// logger.info("Info: Json Rpc Duplicate Detected") -// } catch { -// logger.error(error) -// } -// } -// -// func respond(topic: String, response: JsonRpcResult, tag: Int) async throws { -// _ = try jsonRpcHistory.resolve(response: response) -// -// let message = try serializer.serialize(topic: topic, encodable: response.value) -// logger.debug("Responding....topic: \(topic)") -// -// do { -// try await relayClient.publish(topic: topic, payload: message, tag: tag, prompt: false) -// } catch WalletConnectError.internal(.jsonRpcDuplicateDetected) { -// logger.info("Info: Json Rpc Duplicate Detected") -// } -// } -// -// func respondSuccess(payload: WCRequestSubscriptionPayload) async throws { -// let response = JSONRPCResponse(id: payload.wcRequest.id, result: AnyCodable(true)) -// try await respond(topic: payload.topic, response: JsonRpcResult.response(response), tag: payload.wcRequest.responseTag) -// } -// -// func respondError(payload: WCRequestSubscriptionPayload, reason: ReasonCode) async throws { -// let response = JSONRPCErrorResponse(id: payload.wcRequest.id, error: JSONRPCErrorResponse.Error(code: reason.code, message: reason.message)) -// try await respond(topic: payload.topic, response: JsonRpcResult.error(response), tag: payload.wcRequest.responseTag) -// } -// -// // TODO: Move to async -// func respondSuccess(for payload: WCRequestSubscriptionPayload) { -// Task(priority: .background) { -// do { -// try await respondSuccess(payload: payload) -// } catch { -// self.logger.error("Respond Success failed with: \(error.localizedDescription)") -// } -// } -// } -// -// func subscribe(topic: String) async throws { -// try await relayClient.subscribe(topic: topic) -// } -// -// func unsubscribe(topic: String) { -// relayClient.unsubscribe(topic: topic) { [weak self] error in -// if let error = error { -// self?.logger.error(error) -// } else { -// self?.jsonRpcHistory.delete(topic: topic) -// } -// } -// } -// -// // MARK: - Private -// -// private func setUpPublishers() { -// relayClient.socketConnectionStatusPublisher.sink { [weak self] status in -// if status == .connected { -// self?.transportConnectionPublisherSubject.send() -// } -// }.store(in: &publishers) -// -// relayClient.messagePublisher.sink { [weak self] (topic, message) in -// self?.manageSubscription(topic, message) -// } -// .store(in: &publishers) -// } -// -// private func manageSubscription(_ topic: String, _ encodedEnvelope: String) { -// if let deserializedJsonRpcRequest: WCRequest = serializer.tryDeserialize(topic: topic, encodedEnvelope: encodedEnvelope) { -// handleWCRequest(topic: topic, request: deserializedJsonRpcRequest) -// } else if let deserializedJsonRpcResponse: JSONRPCResponse = serializer.tryDeserialize(topic: topic, encodedEnvelope: encodedEnvelope) { -// handleJsonRpcResponse(response: deserializedJsonRpcResponse) -// } else if let deserializedJsonRpcError: JSONRPCErrorResponse = serializer.tryDeserialize(topic: topic, encodedEnvelope: encodedEnvelope) { -// handleJsonRpcErrorResponse(response: deserializedJsonRpcError) -// } else { -// logger.warn("Warning: Networking Interactor - Received unknown object type from networking relay") -// } -// } -// -// private func handleWCRequest(topic: String, request: WCRequest) { -// do { -// try jsonRpcHistory.set(topic: topic, request: request, chainId: getChainId(request)) -// let payload = WCRequestSubscriptionPayload(topic: topic, wcRequest: request) -// wcRequestPublisherSubject.send(payload) -// } catch WalletConnectError.internal(.jsonRpcDuplicateDetected) { -// logger.info("Info: Json Rpc Duplicate Detected") -// } catch { -// logger.error(error) -// } -// } -// -// private func handleJsonRpcResponse(response: JSONRPCResponse) { -// do { -// let record = try jsonRpcHistory.resolve(response: JsonRpcResult.response(response)) -// let wcResponse = WCResponse( -// topic: record.topic, -// chainId: record.chainId, -// requestMethod: record.request.method, -// requestParams: record.request.params, -// result: JsonRpcResult.response(response)) -// responsePublisherSubject.send(wcResponse) -// } catch { -// logger.info("Info: \(error.localizedDescription)") -// } -// } -// -// private func handleJsonRpcErrorResponse(response: JSONRPCErrorResponse) { -// do { -// let record = try jsonRpcHistory.resolve(response: JsonRpcResult.error(response)) -// let wcResponse = WCResponse( -// topic: record.topic, -// chainId: record.chainId, -// requestMethod: record.request.method, -// requestParams: record.request.params, -// result: JsonRpcResult.error(response)) -// responsePublisherSubject.send(wcResponse) -// } catch { -// logger.info("Info: \(error.localizedDescription)") -// } -// } -// -// private func shouldPrompt(_ method: WCRequest.Method) -> Bool { -// switch method { -// case .sessionRequest: -// return true -// default: -// return false -// } -// } -// -// func getChainId(_ request: WCRequest) -> String? { -// guard case let .sessionRequest(payload) = request.params else {return nil} -// return payload.chainId.absoluteString -// } -//} diff --git a/Sources/WalletConnectSign/NetworkInteractor/NetworkRelaying.swift b/Sources/WalletConnectSign/NetworkInteractor/NetworkRelaying.swift deleted file mode 100644 index fec9c6ed8..000000000 --- a/Sources/WalletConnectSign/NetworkInteractor/NetworkRelaying.swift +++ /dev/null @@ -1,19 +0,0 @@ -//import Foundation -//import WalletConnectRelay -//import Combine -// -//extension RelayClient: NetworkRelaying {} -// -//protocol NetworkRelaying { -// var messagePublisher: AnyPublisher<(topic: String, message: String), Never> { get } -// var socketConnectionStatusPublisher: AnyPublisher { get } -// func connect() throws -// func disconnect(closeCode: URLSessionWebSocketTask.CloseCode) throws -// func publish(topic: String, payload: String, tag: Int, prompt: Bool) async throws -// /// - returns: request id -// func publish(topic: String, payload: String, tag: Int, prompt: Bool, onNetworkAcknowledge: @escaping ((Error?) -> Void)) -// func subscribe(topic: String, completion: @escaping (Error?) -> Void) -// func subscribe(topic: String) async throws -// /// - returns: request id -// func unsubscribe(topic: String, completion: @escaping ((Error?) -> Void)) -//} From e19148db4a98908e7bf1bb2537fa9bd59d94d75f Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Fri, 9 Sep 2022 02:54:08 +0300 Subject: [PATCH 009/175] Unit tests fixed --- Package.swift | 2 +- .../Engine/Common/ApproveEngine.swift | 2 +- .../Engine/Common/SessionEngine.swift | 4 +- .../NonControllerSessionStateMachine.swift | 2 +- .../NetworkingInteractorMock.swift | 36 ++++- .../ApproveEngineTests.swift | 43 +++--- .../Helpers/WCRequest+Extension.swift | 9 -- .../JsonRpcHistoryTests.swift | 84 ---------- .../Mocks/MockedRelayClient.swift | 78 +++++----- .../Mocks/NetworkingInteractorMock.swift | 102 ------------ .../Mocks/SerializerMock.swift | 34 ---- ...onControllerSessionStateMachineTests.swift | 16 +- .../PairEngineTests.swift | 6 +- .../PairingEngineTests.swift | 26 ++-- Tests/WalletConnectSignTests/Stub/Stubs.swift | 36 ++--- .../WalletConnectSignTests/WCRelayTests.swift | 145 +++++++++--------- .../WCResponseTests.swift | 16 +- 17 files changed, 206 insertions(+), 435 deletions(-) delete mode 100644 Tests/WalletConnectSignTests/Helpers/WCRequest+Extension.swift delete mode 100644 Tests/WalletConnectSignTests/JsonRpcHistoryTests.swift delete mode 100644 Tests/WalletConnectSignTests/Mocks/NetworkingInteractorMock.swift delete mode 100644 Tests/WalletConnectSignTests/Mocks/SerializerMock.swift diff --git a/Package.swift b/Package.swift index a991ee8bf..4c925ebf6 100644 --- a/Package.swift +++ b/Package.swift @@ -52,7 +52,7 @@ let package = Package( path: "Sources/WalletConnectKMS"), .target( name: "WalletConnectPairing", - dependencies: ["WalletConnectUtils", "WalletConnectNetworking", "JSONRPC"]), + dependencies: ["WalletConnectNetworking"]), .target( name: "WalletConnectUtils", dependencies: ["Commons", "JSONRPC"]), diff --git a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift index e29813c63..398e1566a 100644 --- a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift @@ -184,7 +184,7 @@ private extension ApproveEngine { } func respondError(payload: SubscriptionPayload, reason: ReasonCode, tag: Int) { - Task { + Task(priority: .high) { do { try await networkingInteractor.respondError(topic: payload.topic, requestId: payload.id, tag: tag, reason: reason) } catch { diff --git a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift index 60435dc0e..1372c8fe6 100644 --- a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift @@ -94,7 +94,7 @@ private extension SessionEngine { guard status == .connected else { return } sessionStore.getAll() .forEach { session in - Task { try await networkingInteractor.subscribe(topic: session.topic) } + Task(priority: .high) { try await networkingInteractor.subscribe(topic: session.topic) } } } .store(in: &publishers) @@ -142,7 +142,7 @@ private extension SessionEngine { } func respondError(payload: SubscriptionPayload, reason: ReasonCode, tag: Int) { - Task { + Task(priority: .high) { do { try await networkingInteractor.respondError(topic: payload.topic, requestId: payload.id, tag: tag, reason: reason) } catch { diff --git a/Sources/WalletConnectSign/Engine/NonController/NonControllerSessionStateMachine.swift b/Sources/WalletConnectSign/Engine/NonController/NonControllerSessionStateMachine.swift index 46ab1c516..7cca1066b 100644 --- a/Sources/WalletConnectSign/Engine/NonController/NonControllerSessionStateMachine.swift +++ b/Sources/WalletConnectSign/Engine/NonController/NonControllerSessionStateMachine.swift @@ -39,7 +39,7 @@ final class NonControllerSessionStateMachine { } private func respondError(payload: SubscriptionPayload, reason: ReasonCode, tag: Int) { - Task { + Task(priority: .high) { do { try await networkingInteractor.respondError(topic: payload.topic, requestId: payload.id, tag: tag, reason: reason) } catch { diff --git a/Tests/TestingUtils/NetworkingInteractorMock.swift b/Tests/TestingUtils/NetworkingInteractorMock.swift index 7942fdac1..286390803 100644 --- a/Tests/TestingUtils/NetworkingInteractorMock.swift +++ b/Tests/TestingUtils/NetworkingInteractorMock.swift @@ -8,6 +8,19 @@ import WalletConnectNetworking public class NetworkingInteractorMock: NetworkInteracting { private(set) var subscriptions: [String] = [] + private(set) var unsubscriptions: [String] = [] + + private(set) var requests: [(topic: String, request: RPCRequest)] = [] + + private(set) var didRespondSuccess = false + private(set) var didRespondError = false + private(set) var didCallSubscribe = false + private(set) var didCallUnsubscribe = false + private(set) var didRespondOnTopic: String? + private(set) var lastErrorCode = -1 + + private(set) var requestCallCount = 0 + var didCallRequest: Bool { requestCallCount > 0 } public let socketConnectionStatusPublisherSubject = PassthroughSubject() public var socketConnectionStatusPublisher: AnyPublisher { @@ -67,33 +80,42 @@ public class NetworkingInteractorMock: NetworkInteracting { public func subscribe(topic: String) async throws { subscriptions.append(topic) + didCallSubscribe = true } func didSubscribe(to topic: String) -> Bool { - subscriptions.contains { $0 == topic } + subscriptions.contains { $0 == topic } } - public func unsubscribe(topic: String) { + func didUnsubscribe(to topic: String) -> Bool { + unsubscriptions.contains { $0 == topic } + } + public func unsubscribe(topic: String) { + unsubscriptions.append(topic) + didCallUnsubscribe = true } public func request(_ request: RPCRequest, topic: String, tag: Int, envelopeType: Envelope.EnvelopeType) async throws { - + requestCallCount += 1 + requests.append((topic, request)) } public func respond(topic: String, response: RPCResponse, tag: Int, envelopeType: Envelope.EnvelopeType) async throws { - + didRespondOnTopic = topic } public func respondSuccess(topic: String, requestId: RPCID, tag: Int, envelopeType: Envelope.EnvelopeType) async throws { - + didRespondSuccess = true } public func respondError(topic: String, requestId: RPCID, tag: Int, reason: Reason, envelopeType: Envelope.EnvelopeType) async throws { - + lastErrorCode = reason.code + didRespondError = true } public func requestNetworkAck(_ request: RPCRequest, topic: String, tag: Int) async throws { - + requestCallCount += 1 + requests.append((topic, request)) } } diff --git a/Tests/WalletConnectSignTests/ApproveEngineTests.swift b/Tests/WalletConnectSignTests/ApproveEngineTests.swift index 88e7a541c..77fc29075 100644 --- a/Tests/WalletConnectSignTests/ApproveEngineTests.swift +++ b/Tests/WalletConnectSignTests/ApproveEngineTests.swift @@ -1,7 +1,9 @@ import XCTest import Combine +import JSONRPC import WalletConnectUtils import WalletConnectPairing +import WalletConnectNetworking @testable import WalletConnectSign @testable import TestingUtils @testable import WalletConnectKMS @@ -14,7 +16,7 @@ final class ApproveEngineTests: XCTestCase { var cryptoMock: KeyManagementServiceMock! var pairingStorageMock: WCPairingStorageMock! var sessionStorageMock: WCSessionStorageMock! - var proposalPayloadsStore: CodableStore! + var proposalPayloadsStore: CodableStore>! var publishers = Set() @@ -24,7 +26,7 @@ final class ApproveEngineTests: XCTestCase { cryptoMock = KeyManagementServiceMock() pairingStorageMock = WCPairingStorageMock() sessionStorageMock = WCSessionStorageMock() - proposalPayloadsStore = CodableStore(defaults: RuntimeKeyValueStorage(), identifier: "") + proposalPayloadsStore = CodableStore>(defaults: RuntimeKeyValueStorage(), identifier: "") engine = ApproveEngine( networkingInteractor: networkingInteractor, proposalPayloadsStore: proposalPayloadsStore, @@ -52,9 +54,8 @@ final class ApproveEngineTests: XCTestCase { pairingStorageMock.setPairing(pairing) let proposerPubKey = AgreementPrivateKey().publicKey.hexRepresentation let proposal = SessionProposal.stub(proposerPubKey: proposerPubKey) - let request = WCRequest(method: .sessionPropose, params: .sessionPropose(proposal)) - let payload = WCRequestSubscriptionPayload(topic: topicA, wcRequest: request) - networkingInteractor.wcRequestPublisherSubject.send(payload) + let request = RPCRequest(method: SignProtocolMethod.sessionPropose.method, params: proposal) + networkingInteractor.requestPublisherSubject.send((topicA, request)) try await engine.approveProposal(proposerPubKey: proposal.proposer.publicKey, validating: SessionNamespace.stubDictionary()) @@ -74,14 +75,13 @@ final class ApproveEngineTests: XCTestCase { var sessionProposed = false let proposerPubKey = AgreementPrivateKey().publicKey.hexRepresentation let proposal = SessionProposal.stub(proposerPubKey: proposerPubKey) - let request = WCRequest(method: .sessionPropose, params: .sessionPropose(proposal)) - let payload = WCRequestSubscriptionPayload(topic: topicA, wcRequest: request) + let request = RPCRequest(method: SignProtocolMethod.sessionPropose.method, params: proposal) engine.onSessionProposal = { _ in sessionProposed = true } - networkingInteractor.wcRequestPublisherSubject.send(payload) + networkingInteractor.requestPublisherSubject.send((topicA, request)) XCTAssertNotNil(try! proposalPayloadsStore.get(key: proposal.proposer.publicKey), "Proposer must store proposal payload") XCTAssertTrue(sessionProposed) } @@ -106,7 +106,9 @@ final class ApproveEngineTests: XCTestCase { } engine.settlingProposal = SessionProposal.stub() - networkingInteractor.wcRequestPublisherSubject.send(WCRequestSubscriptionPayload.stubSettle(topic: sessionTopic)) + networkingInteractor.requestPublisherSubject.send((sessionTopic, RPCRequest.stubSettle())) + + usleep(100) XCTAssertTrue(sessionStorageMock.getSession(forTopic: sessionTopic)!.acknowledged, "Proposer must store acknowledged session on topic B") XCTAssertTrue(networkingInteractor.didRespondSuccess, "Proposer must send acknowledge on settle request") @@ -117,14 +119,10 @@ final class ApproveEngineTests: XCTestCase { let session = WCSession.stub(isSelfController: true, acknowledged: false) sessionStorageMock.setSession(session) - let settleResponse = JSONRPCResponse(id: 1, result: AnyCodable(true)) - let response = WCResponse( - topic: session.topic, - chainId: nil, - requestMethod: .sessionSettle, - requestParams: .sessionSettle(SessionType.SettleParams.stub()), - result: .response(settleResponse)) - networkingInteractor.responsePublisherSubject.send(response) + let request = RPCRequest(method: SignProtocolMethod.sessionSettle.method, params: SessionType.SettleParams.stub()) + let response = RPCResponse(matchingRequest: request, result: RPCResult.response(AnyCodable(true))) + + networkingInteractor.responsePublisherSubject.send((session.topic, request, response)) XCTAssertTrue(sessionStorageMock.getSession(forTopic: session.topic)!.acknowledged, "Responder must acknowledged session") } @@ -136,13 +134,10 @@ final class ApproveEngineTests: XCTestCase { cryptoMock.setAgreementSecret(AgreementKeys.stub(), topic: session.topic) try! cryptoMock.setPrivateKey(privateKey) - let response = WCResponse( - topic: session.topic, - chainId: nil, - requestMethod: .sessionSettle, - requestParams: .sessionSettle(SessionType.SettleParams.stub()), - result: .error(JSONRPCErrorResponse(id: 1, error: JSONRPCErrorResponse.Error(code: 0, message: "")))) - networkingInteractor.responsePublisherSubject.send(response) + let request = RPCRequest(method: SignProtocolMethod.sessionSettle.method, params: SessionType.SettleParams.stub()) + let response = RPCResponse(matchingRequest: request, result: RPCResult.error(JSONRPCError(code: 0, message: ""))) + + networkingInteractor.responsePublisherSubject.send((session.topic, request, response)) XCTAssertNil(sessionStorageMock.getSession(forTopic: session.topic), "Responder must remove session") XCTAssertTrue(networkingInteractor.didUnsubscribe(to: session.topic), "Responder must unsubscribe topic B") diff --git a/Tests/WalletConnectSignTests/Helpers/WCRequest+Extension.swift b/Tests/WalletConnectSignTests/Helpers/WCRequest+Extension.swift deleted file mode 100644 index d3616f365..000000000 --- a/Tests/WalletConnectSignTests/Helpers/WCRequest+Extension.swift +++ /dev/null @@ -1,9 +0,0 @@ -@testable import WalletConnectSign - -extension WCRequest { - - var sessionProposal: SessionProposal? { - guard case .sessionPropose(let proposal) = self.params else { return nil } - return proposal - } -} diff --git a/Tests/WalletConnectSignTests/JsonRpcHistoryTests.swift b/Tests/WalletConnectSignTests/JsonRpcHistoryTests.swift deleted file mode 100644 index dd98115f7..000000000 --- a/Tests/WalletConnectSignTests/JsonRpcHistoryTests.swift +++ /dev/null @@ -1,84 +0,0 @@ -import Foundation -import XCTest -import TestingUtils -import WalletConnectUtils -import WalletConnectPairing -@testable import WalletConnectSign - -final class JsonRpcHistoryTests: XCTestCase { - - var sut: WalletConnectSign.JsonRpcHistory! - - override func setUp() { - sut = JsonRpcHistory(logger: ConsoleLoggerMock(), keyValueStore: CodableStore(defaults: RuntimeKeyValueStorage(), identifier: "")) - } - - override func tearDown() { - sut = nil - } - - func testSetRecord() { - let recordinput = getTestJsonRpcRecordInput() - XCTAssertFalse(sut.exist(id: recordinput.request.id)) - try! sut.set(topic: recordinput.topic, request: recordinput.request) - XCTAssertTrue(sut.exist(id: recordinput.request.id)) - } - - func testGetRecord() { - let recordinput = getTestJsonRpcRecordInput() - XCTAssertNil(sut.get(id: recordinput.request.id)) - try! sut.set(topic: recordinput.topic, request: recordinput.request) - XCTAssertNotNil(sut.get(id: recordinput.request.id)) - } - - func testResolve() { - let recordinput = getTestJsonRpcRecordInput() - try! sut.set(topic: recordinput.topic, request: recordinput.request) - XCTAssertNil(sut.get(id: recordinput.request.id)?.response) - let jsonRpcResponse = JSONRPCResponse(id: recordinput.request.id, result: AnyCodable("")) - let response = JsonRpcResult.response(jsonRpcResponse) - _ = try! sut.resolve(response: response) - XCTAssertNotNil(sut.get(id: jsonRpcResponse.id)?.response) - } - - func testThrowsOnResolveDuplicate() { - let recordinput = getTestJsonRpcRecordInput() - try! sut.set(topic: recordinput.topic, request: recordinput.request) - let jsonRpcResponse = JSONRPCResponse(id: recordinput.request.id, result: AnyCodable("")) - let response = JsonRpcResult.response(jsonRpcResponse) - _ = try! sut.resolve(response: response) - XCTAssertThrowsError(try sut.resolve(response: response)) - } - - func testThrowsOnSetDuplicate() { - let recordinput = getTestJsonRpcRecordInput() - try! sut.set(topic: recordinput.topic, request: recordinput.request) - XCTAssertThrowsError(try sut.set(topic: recordinput.topic, request: recordinput.request)) - } - - func testDelete() { - let recordinput = getTestJsonRpcRecordInput() - try! sut.set(topic: recordinput.topic, request: recordinput.request) - XCTAssertNotNil(sut.get(id: recordinput.request.id)) - sut.delete(topic: testTopic) - XCTAssertNil(sut.get(id: recordinput.request.id)) - } - - func testGetPending() { - let recordinput1 = getTestJsonRpcRecordInput(id: 1) - let recordinput2 = getTestJsonRpcRecordInput(id: 2) - try! sut.set(topic: recordinput1.topic, request: recordinput1.request) - try! sut.set(topic: recordinput2.topic, request: recordinput2.request) - XCTAssertEqual(sut.getPending().count, 2) - let jsonRpcResponse = JSONRPCResponse(id: recordinput1.request.id, result: AnyCodable("")) - let response = JsonRpcResult.response(jsonRpcResponse) - _ = try! sut.resolve(response: response) - XCTAssertEqual(sut.getPending().count, 1) - } -} - -private let testTopic = "test_topic" -private func getTestJsonRpcRecordInput(id: Int64 = 0) -> (topic: String, request: WCRequest) { - let request = WCRequest(id: id, jsonrpc: "2.0", method: .pairingPing, params: WCRequest.Params.pairingPing(PairingType.PingParams())) - return (topic: testTopic, request: request) -} diff --git a/Tests/WalletConnectSignTests/Mocks/MockedRelayClient.swift b/Tests/WalletConnectSignTests/Mocks/MockedRelayClient.swift index eac4dacb5..a371a1e79 100644 --- a/Tests/WalletConnectSignTests/Mocks/MockedRelayClient.swift +++ b/Tests/WalletConnectSignTests/Mocks/MockedRelayClient.swift @@ -3,42 +3,42 @@ import Foundation @testable import WalletConnectRelay @testable import WalletConnectSign -class MockedRelayClient: NetworkRelaying { - - var messagePublisherSubject = PassthroughSubject<(topic: String, message: String), Never>() - var messagePublisher: AnyPublisher<(topic: String, message: String), Never> { - messagePublisherSubject.eraseToAnyPublisher() - } - - var socketConnectionStatusPublisherSubject = PassthroughSubject() - var socketConnectionStatusPublisher: AnyPublisher { - socketConnectionStatusPublisherSubject.eraseToAnyPublisher() - } - - var error: Error? - var prompt = false - - func publish(topic: String, payload: String, tag: Int, prompt: Bool) async throws { - self.prompt = prompt - } - - func publish(topic: String, payload: String, tag: Int, prompt: Bool, onNetworkAcknowledge: @escaping ((Error?) -> Void)) { - self.prompt = prompt - onNetworkAcknowledge(error) - } - - func subscribe(topic: String) async throws {} - - func subscribe(topic: String, completion: @escaping (Error?) -> Void) { - } - - func unsubscribe(topic: String, completion: @escaping ((Error?) -> Void)) { - } - - func connect() { - } - - func disconnect(closeCode: URLSessionWebSocketTask.CloseCode) { - } - -} +//class MockedRelayClient: NetworkRelaying { +// +// var messagePublisherSubject = PassthroughSubject<(topic: String, message: String), Never>() +// var messagePublisher: AnyPublisher<(topic: String, message: String), Never> { +// messagePublisherSubject.eraseToAnyPublisher() +// } +// +// var socketConnectionStatusPublisherSubject = PassthroughSubject() +// var socketConnectionStatusPublisher: AnyPublisher { +// socketConnectionStatusPublisherSubject.eraseToAnyPublisher() +// } +// +// var error: Error? +// var prompt = false +// +// func publish(topic: String, payload: String, tag: Int, prompt: Bool) async throws { +// self.prompt = prompt +// } +// +// func publish(topic: String, payload: String, tag: Int, prompt: Bool, onNetworkAcknowledge: @escaping ((Error?) -> Void)) { +// self.prompt = prompt +// onNetworkAcknowledge(error) +// } +// +// func subscribe(topic: String) async throws {} +// +// func subscribe(topic: String, completion: @escaping (Error?) -> Void) { +// } +// +// func unsubscribe(topic: String, completion: @escaping ((Error?) -> Void)) { +// } +// +// func connect() { +// } +// +// func disconnect(closeCode: URLSessionWebSocketTask.CloseCode) { +// } +// +//} diff --git a/Tests/WalletConnectSignTests/Mocks/NetworkingInteractorMock.swift b/Tests/WalletConnectSignTests/Mocks/NetworkingInteractorMock.swift deleted file mode 100644 index a76d6ef91..000000000 --- a/Tests/WalletConnectSignTests/Mocks/NetworkingInteractorMock.swift +++ /dev/null @@ -1,102 +0,0 @@ -import Foundation -import Combine -import WalletConnectUtils -import WalletConnectPairing -@testable import WalletConnectSign -@testable import TestingUtils - -class NetworkingInteractorMock: NetworkInteracting { - - private(set) var subscriptions: [String] = [] - private(set) var unsubscriptions: [String] = [] - - let transportConnectionPublisherSubject = PassthroughSubject() - let responsePublisherSubject = PassthroughSubject() - let wcRequestPublisherSubject = PassthroughSubject() - - var transportConnectionPublisher: AnyPublisher { - transportConnectionPublisherSubject.eraseToAnyPublisher() - } - var wcRequestPublisher: AnyPublisher { - wcRequestPublisherSubject.eraseToAnyPublisher() - } - var responsePublisher: AnyPublisher { - responsePublisherSubject.eraseToAnyPublisher() - } - - var didCallSubscribe = false - var didRespondOnTopic: String? - var didCallUnsubscribe = false - var didRespondSuccess = false - var didRespondError = false - var lastErrorCode = -1 - var error: Error? - - private(set) var requestCallCount = 0 - var didCallRequest: Bool { requestCallCount > 0 } - - private(set) var requests: [(topic: String, request: WCRequest)] = [] - - func request(topic: String, payload: WCRequest) async throws { - requestCallCount += 1 - requests.append((topic, payload)) - } - - func requestNetworkAck(_ wcMethod: WCMethod, onTopic topic: String, completion: @escaping ((Error?) -> Void)) { - requestCallCount += 1 - requests.append((topic, wcMethod.asRequest())) - completion(nil) - } - - func requestPeerResponse(_ wcMethod: WCMethod, onTopic topic: String, completion: ((Result, JSONRPCErrorResponse>) -> Void)?) { - requestCallCount += 1 - requests.append((topic, wcMethod.asRequest())) - } - - func respond(topic: String, response: JsonRpcResult, completion: @escaping ((Error?) -> Void)) { - didRespondOnTopic = topic - completion(error) - } - - func respond(topic: String, response: JsonRpcResult, tag: Int) async throws { - didRespondOnTopic = topic - } - - func respondSuccess(payload: WCRequestSubscriptionPayload) async throws { - respondSuccess(for: payload) - } - - func respondError(payload: WCRequestSubscriptionPayload, reason: ReasonCode) async throws { - lastErrorCode = reason.code - didRespondError = true - } - - func respondSuccess(for payload: WCRequestSubscriptionPayload) { - didRespondSuccess = true - } - - func subscribe(topic: String) { - subscriptions.append(topic) - didCallSubscribe = true - } - - func unsubscribe(topic: String) { - unsubscriptions.append(topic) - didCallUnsubscribe = true - } - - func sendSubscriptionPayloadOn(topic: String) { - let payload = WCRequestSubscriptionPayload(topic: topic, wcRequest: pingRequest) - wcRequestPublisherSubject.send(payload) - } - - func didSubscribe(to topic: String) -> Bool { - subscriptions.contains { $0 == topic } - } - - func didUnsubscribe(to topic: String) -> Bool { - unsubscriptions.contains { $0 == topic } - } -} - -private let pingRequest = WCRequest(id: 1, jsonrpc: "2.0", method: .pairingPing, params: WCRequest.Params.pairingPing(PairingType.PingParams())) diff --git a/Tests/WalletConnectSignTests/Mocks/SerializerMock.swift b/Tests/WalletConnectSignTests/Mocks/SerializerMock.swift deleted file mode 100644 index 339da3ff8..000000000 --- a/Tests/WalletConnectSignTests/Mocks/SerializerMock.swift +++ /dev/null @@ -1,34 +0,0 @@ -// - -import Foundation -import WalletConnectUtils -@testable import WalletConnectKMS -@testable import WalletConnectSign - -class SerializerMock: Serializing { - var deserialized: Any! - var serialized: String = "" - - func serialize(topic: String, encodable: Encodable, envelopeType: Envelope.EnvelopeType) throws -> String { - try serialize(json: try encodable.json(), agreementKeys: AgreementKeys.stub()) - } - func deserialize(topic: String, encodedEnvelope: String) throws -> T { - return try deserialize(message: encodedEnvelope, symmetricKey: Data()) - } - func deserializeJsonRpc(topic: String, message: String) throws -> Result, JSONRPCErrorResponse> { - .success(try deserialize(message: message, symmetricKey: Data())) - } - - func deserialize(message: String, symmetricKey: Data) throws -> T where T: Codable { - if let deserializedModel = deserialized as? T { - return deserializedModel - } else { - throw NSError.mock() - } - } - - func serialize(json: String, agreementKeys: AgreementKeys) throws -> String { - return serialized - } - -} diff --git a/Tests/WalletConnectSignTests/NonControllerSessionStateMachineTests.swift b/Tests/WalletConnectSignTests/NonControllerSessionStateMachineTests.swift index 47937bd42..8107cfd0d 100644 --- a/Tests/WalletConnectSignTests/NonControllerSessionStateMachineTests.swift +++ b/Tests/WalletConnectSignTests/NonControllerSessionStateMachineTests.swift @@ -2,6 +2,7 @@ import XCTest import WalletConnectUtils @testable import TestingUtils import WalletConnectKMS +import JSONRPC @testable import WalletConnectSign class NonControllerSessionStateMachineTests: XCTestCase { @@ -34,8 +35,9 @@ class NonControllerSessionStateMachineTests: XCTestCase { didCallbackUpdatMethods = true XCTAssertEqual(topic, session.topic) } - networkingInteractor.wcRequestPublisherSubject.send(WCRequestSubscriptionPayload.stubUpdateNamespaces(topic: session.topic)) + networkingInteractor.requestPublisherSubject.send((session.topic, RPCRequest.stubUpdateNamespaces())) XCTAssertTrue(didCallbackUpdatMethods) + usleep(100) XCTAssertTrue(networkingInteractor.didRespondSuccess) } @@ -49,7 +51,7 @@ class NonControllerSessionStateMachineTests: XCTestCase { // } func testUpdateMethodPeerErrorSessionNotFound() { - networkingInteractor.wcRequestPublisherSubject.send(WCRequestSubscriptionPayload.stubUpdateNamespaces(topic: "")) + networkingInteractor.requestPublisherSubject.send(("", RPCRequest.stubUpdateNamespaces())) usleep(100) XCTAssertFalse(networkingInteractor.didRespondSuccess) XCTAssertEqual(networkingInteractor.lastErrorCode, 7001) @@ -58,7 +60,7 @@ class NonControllerSessionStateMachineTests: XCTestCase { func testUpdateMethodPeerErrorUnauthorized() { let session = WCSession.stub(isSelfController: true) // Peer is not a controller storageMock.setSession(session) - networkingInteractor.wcRequestPublisherSubject.send(WCRequestSubscriptionPayload.stubUpdateNamespaces(topic: session.topic)) + networkingInteractor.requestPublisherSubject.send((session.topic, RPCRequest.stubUpdateNamespaces())) usleep(100) XCTAssertFalse(networkingInteractor.didRespondSuccess) XCTAssertEqual(networkingInteractor.lastErrorCode, 3003) @@ -72,7 +74,7 @@ class NonControllerSessionStateMachineTests: XCTestCase { storageMock.setSession(session) let twoDaysFromNowTimestamp = Int64(TimeTraveler.dateByAdding(days: 2).timeIntervalSince1970) - networkingInteractor.wcRequestPublisherSubject.send(WCRequestSubscriptionPayload.stubUpdateExpiry(topic: session.topic, expiry: twoDaysFromNowTimestamp)) + networkingInteractor.requestPublisherSubject.send((session.topic, RPCRequest.stubUpdateExpiry(expiry: twoDaysFromNowTimestamp))) let extendedSession = storageMock.getAll().first {$0.topic == session.topic}! print(extendedSession.expiryDate) @@ -85,7 +87,7 @@ class NonControllerSessionStateMachineTests: XCTestCase { storageMock.setSession(session) let twoDaysFromNowTimestamp = Int64(TimeTraveler.dateByAdding(days: 2).timeIntervalSince1970) - networkingInteractor.wcRequestPublisherSubject.send(WCRequestSubscriptionPayload.stubUpdateExpiry(topic: session.topic, expiry: twoDaysFromNowTimestamp)) + networkingInteractor.requestPublisherSubject.send((session.topic, RPCRequest.stubUpdateExpiry(expiry: twoDaysFromNowTimestamp))) let potentiallyExtendedSession = storageMock.getAll().first {$0.topic == session.topic}! XCTAssertEqual(potentiallyExtendedSession.expiryDate.timeIntervalSinceReferenceDate, tomorrow.timeIntervalSinceReferenceDate, accuracy: 1, "expiry date has been extended for peer non controller request ") @@ -96,7 +98,7 @@ class NonControllerSessionStateMachineTests: XCTestCase { let session = WCSession.stub(isSelfController: false, expiryDate: tomorrow) storageMock.setSession(session) let tenDaysFromNowTimestamp = Int64(TimeTraveler.dateByAdding(days: 10).timeIntervalSince1970) - networkingInteractor.wcRequestPublisherSubject.send(WCRequestSubscriptionPayload.stubUpdateExpiry(topic: session.topic, expiry: tenDaysFromNowTimestamp)) + networkingInteractor.requestPublisherSubject.send((session.topic, RPCRequest.stubUpdateExpiry(expiry: tenDaysFromNowTimestamp))) let potentaillyExtendedSession = storageMock.getAll().first {$0.topic == session.topic}! XCTAssertEqual(potentaillyExtendedSession.expiryDate.timeIntervalSinceReferenceDate, tomorrow.timeIntervalSinceReferenceDate, accuracy: 1, "expiry date has been extended despite ttl to high") @@ -108,7 +110,7 @@ class NonControllerSessionStateMachineTests: XCTestCase { storageMock.setSession(session) let oneDayFromNowTimestamp = Int64(TimeTraveler.dateByAdding(days: 10).timeIntervalSince1970) - networkingInteractor.wcRequestPublisherSubject.send(WCRequestSubscriptionPayload.stubUpdateExpiry(topic: session.topic, expiry: oneDayFromNowTimestamp)) + networkingInteractor.requestPublisherSubject.send((session.topic, RPCRequest.stubUpdateExpiry(expiry: oneDayFromNowTimestamp))) let potentaillyExtendedSession = storageMock.getAll().first {$0.topic == session.topic}! XCTAssertEqual(potentaillyExtendedSession.expiryDate.timeIntervalSinceReferenceDate, tomorrow.timeIntervalSinceReferenceDate, accuracy: 1, "expiry date has been extended despite ttl to low") } diff --git a/Tests/WalletConnectSignTests/PairEngineTests.swift b/Tests/WalletConnectSignTests/PairEngineTests.swift index 9b581672d..66018ff7b 100644 --- a/Tests/WalletConnectSignTests/PairEngineTests.swift +++ b/Tests/WalletConnectSignTests/PairEngineTests.swift @@ -3,6 +3,7 @@ import XCTest @testable import TestingUtils @testable import WalletConnectKMS import WalletConnectUtils +import WalletConnectNetworking final class PairEngineTests: XCTestCase { @@ -11,16 +12,11 @@ final class PairEngineTests: XCTestCase { var networkingInteractor: NetworkingInteractorMock! var storageMock: WCPairingStorageMock! var cryptoMock: KeyManagementServiceMock! - var proposalPayloadsStore: CodableStore! - - var topicGenerator: TopicGenerator! override func setUp() { networkingInteractor = NetworkingInteractorMock() storageMock = WCPairingStorageMock() cryptoMock = KeyManagementServiceMock() - topicGenerator = TopicGenerator() - proposalPayloadsStore = CodableStore(defaults: RuntimeKeyValueStorage(), identifier: "") setupEngine() } diff --git a/Tests/WalletConnectSignTests/PairingEngineTests.swift b/Tests/WalletConnectSignTests/PairingEngineTests.swift index 785be6dc2..6bd6538cc 100644 --- a/Tests/WalletConnectSignTests/PairingEngineTests.swift +++ b/Tests/WalletConnectSignTests/PairingEngineTests.swift @@ -1,5 +1,6 @@ import XCTest import Combine +import JSONRPC @testable import WalletConnectSign @testable import TestingUtils @testable import WalletConnectKMS @@ -78,7 +79,7 @@ final class PairingEngineTests: XCTestCase { try! await engine.propose(pairingTopic: pairing.topic, namespaces: ProposalNamespace.stubDictionary(), relay: relayOptions) guard let publishTopic = networkingInteractor.requests.first?.topic, - let proposal = networkingInteractor.requests.first?.request.sessionProposal else { + let proposal = try? networkingInteractor.requests.first?.request.params?.get(SessionType.ProposeParams.self) else { XCTFail("Proposer must publish a proposal request."); return } XCTAssert(cryptoMock.hasPrivateKey(for: proposal.proposer.publicKey), "Proposer must store the private key matching the public key sent through the proposal.") @@ -96,7 +97,7 @@ final class PairingEngineTests: XCTestCase { try! await engine.propose(pairingTopic: pairing.topic, namespaces: ProposalNamespace.stubDictionary(), relay: relayOptions) guard let request = networkingInteractor.requests.first?.request, - let proposal = networkingInteractor.requests.first?.request.sessionProposal else { + let proposal = try? networkingInteractor.requests.first?.request.params?.get(SessionType.ProposeParams.self) else { XCTFail("Proposer must publish session proposal request"); return } @@ -104,14 +105,9 @@ final class PairingEngineTests: XCTestCase { let responder = Participant.stub() let proposalResponse = SessionType.ProposeResponse(relay: relayOptions, responderPublicKey: responder.publicKey) - let jsonRpcResponse = JSONRPCResponse(id: request.id, result: AnyCodable.decoded(proposalResponse)) - let response = WCResponse(topic: topicA, - chainId: nil, - requestMethod: request.method, - requestParams: request.params, - result: .response(jsonRpcResponse)) + let response = RPCResponse(id: request.id!, result: RPCResult.response(AnyCodable(proposalResponse))) - networkingInteractor.responsePublisherSubject.send(response) + networkingInteractor.responsePublisherSubject.send((topicA, request, response)) let privateKey = try! cryptoMock.getPrivateKey(for: proposal.proposer.publicKey)! let topicB = deriveTopic(publicKey: responder.publicKey, privateKey: privateKey) let storedPairing = storageMock.getPairing(forTopic: topicA)! @@ -133,12 +129,12 @@ final class PairingEngineTests: XCTestCase { try! await engine.propose(pairingTopic: pairing.topic, namespaces: ProposalNamespace.stubDictionary(), relay: relayOptions) guard let request = networkingInteractor.requests.first?.request, - let proposal = networkingInteractor.requests.first?.request.sessionProposal else { + let proposal = try? networkingInteractor.requests.first?.request.params?.get(SessionType.ProposeParams.self) else { XCTFail("Proposer must publish session proposal request"); return } - let response = WCResponse.stubError(forRequest: request, topic: topicA) - networkingInteractor.responsePublisherSubject.send(response) + let response = RPCResponse.stubError(forRequest: request) + networkingInteractor.responsePublisherSubject.send((topicA, request, response)) XCTAssert(networkingInteractor.didUnsubscribe(to: pairing.topic), "Proposer must unsubscribe if pairing is inactive.") XCTAssertFalse(storageMock.hasPairing(forTopic: pairing.topic), "Proposer must delete an inactive pairing.") @@ -157,7 +153,7 @@ final class PairingEngineTests: XCTestCase { try? await engine.propose(pairingTopic: pairing.topic, namespaces: ProposalNamespace.stubDictionary(), relay: relayOptions) guard let request = networkingInteractor.requests.first?.request, - let proposal = networkingInteractor.requests.first?.request.sessionProposal else { + let proposal = try? networkingInteractor.requests.first?.request.params?.get(SessionType.ProposeParams.self) else { XCTFail("Proposer must publish session proposal request"); return } @@ -165,8 +161,8 @@ final class PairingEngineTests: XCTestCase { storedPairing.activate() storageMock.setPairing(storedPairing) - let response = WCResponse.stubError(forRequest: request, topic: topicA) - networkingInteractor.responsePublisherSubject.send(response) + let response = RPCResponse.stubError(forRequest: request) + networkingInteractor.responsePublisherSubject.send((topicA, request, response)) XCTAssertFalse(networkingInteractor.didUnsubscribe(to: pairing.topic), "Proposer must not unsubscribe if pairing is active.") XCTAssert(storageMock.hasPairing(forTopic: pairing.topic), "Proposer must not delete an active pairing.") diff --git a/Tests/WalletConnectSignTests/Stub/Stubs.swift b/Tests/WalletConnectSignTests/Stub/Stubs.swift index a17ad98a8..96ea5eb3b 100644 --- a/Tests/WalletConnectSignTests/Stub/Stubs.swift +++ b/Tests/WalletConnectSignTests/Stub/Stubs.swift @@ -1,5 +1,6 @@ @testable import WalletConnectSign import Foundation +import JSONRPC import WalletConnectKMS import WalletConnectUtils import TestingUtils @@ -59,29 +60,25 @@ extension AgreementPeer { } } -extension WCRequestSubscriptionPayload { +extension RPCRequest { - static func stubUpdateNamespaces(topic: String, namespaces: [String: SessionNamespace] = SessionNamespace.stubDictionary()) -> WCRequestSubscriptionPayload { - let updateMethod = WCMethod.wcSessionUpdate(SessionType.UpdateParams(namespaces: namespaces)).asRequest() - return WCRequestSubscriptionPayload(topic: topic, wcRequest: updateMethod) + static func stubUpdateNamespaces(namespaces: [String: SessionNamespace] = SessionNamespace.stubDictionary()) -> RPCRequest { + return RPCRequest(method: SignProtocolMethod.sessionUpdate.method, params: SessionType.UpdateParams(namespaces: namespaces)) } - static func stubUpdateExpiry(topic: String, expiry: Int64) -> WCRequestSubscriptionPayload { - let updateExpiryMethod = WCMethod.wcSessionExtend(SessionType.UpdateExpiryParams(expiry: expiry)).asRequest() - return WCRequestSubscriptionPayload(topic: topic, wcRequest: updateExpiryMethod) + static func stubUpdateExpiry(expiry: Int64) -> RPCRequest { + return RPCRequest(method: SignProtocolMethod.sessionExtend.method, params: SessionType.UpdateExpiryParams(expiry: expiry)) } - static func stubSettle(topic: String) -> WCRequestSubscriptionPayload { - let method = WCMethod.wcSessionSettle(SessionType.SettleParams.stub()) - return WCRequestSubscriptionPayload(topic: topic, wcRequest: method.asRequest()) + static func stubSettle() -> RPCRequest { + return RPCRequest(method: SignProtocolMethod.sessionSettle.method, params: SessionType.SettleParams.stub()) } - static func stubRequest(topic: String, method: String, chainId: Blockchain) -> WCRequestSubscriptionPayload { + static func stubRequest(method: String, chainId: Blockchain) -> RPCRequest { let params = SessionType.RequestParams( request: SessionType.RequestParams.Request(method: method, params: AnyCodable(EmptyCodable())), chainId: chainId) - let request = WCRequest(method: .sessionRequest, params: .sessionRequest(params)) - return WCRequestSubscriptionPayload(topic: topic, wcRequest: request) + return RPCRequest(method: SignProtocolMethod.sessionRequest.method, params: params) } } @@ -95,15 +92,8 @@ extension SessionProposal { } } -extension WCResponse { - static func stubError(forRequest request: WCRequest, topic: String) -> WCResponse { - let errorResponse = JSONRPCErrorResponse(id: request.id, error: JSONRPCErrorResponse.Error(code: 0, message: "")) - return WCResponse( - topic: topic, - chainId: nil, - requestMethod: request.method, - requestParams: request.params, - result: .error(errorResponse) - ) +extension RPCResponse { + static func stubError(forRequest request: RPCRequest) -> RPCResponse { + return RPCResponse(matchingRequest: request, result: RPCResult.error(JSONRPCError(code: 0, message: ""))) } } diff --git a/Tests/WalletConnectSignTests/WCRelayTests.swift b/Tests/WalletConnectSignTests/WCRelayTests.swift index a42fbd328..aea8037c9 100644 --- a/Tests/WalletConnectSignTests/WCRelayTests.swift +++ b/Tests/WalletConnectSignTests/WCRelayTests.swift @@ -1,72 +1,73 @@ -import Foundation -import Combine -import XCTest -import WalletConnectUtils -import WalletConnectPairing -@testable import TestingUtils -@testable import WalletConnectSign - -class NetworkingInteractorTests: XCTestCase { - var networkingInteractor: NetworkInteractor! - var relayClient: MockedRelayClient! - var serializer: SerializerMock! - - private var publishers = [AnyCancellable]() - - override func setUp() { - let logger = ConsoleLoggerMock() - serializer = SerializerMock() - relayClient = MockedRelayClient() - networkingInteractor = NetworkInteractor(relayClient: relayClient, serializer: serializer, logger: logger, jsonRpcHistory: JsonRpcHistory(logger: logger, keyValueStore: CodableStore(defaults: RuntimeKeyValueStorage(), identifier: ""))) - } - - override func tearDown() { - networkingInteractor = nil - relayClient = nil - serializer = nil - } - - func testNotifiesOnEncryptedWCJsonRpcRequest() { - let requestExpectation = expectation(description: "notifies with request") - let topic = "fefc3dc39cacbc562ed58f92b296e2d65a6b07ef08992b93db5b3cb86280635a" - networkingInteractor.wcRequestPublisher.sink { (_) in - requestExpectation.fulfill() - }.store(in: &publishers) - serializer.deserialized = request - relayClient.messagePublisherSubject.send((topic, testPayload)) - waitForExpectations(timeout: 1.001, handler: nil) - } - - func testPromptOnSessionRequest() async { - let topic = "fefc3dc39cacbc562ed58f92b296e2d65a6b07ef08992b93db5b3cb86280635a" - let method = getWCSessionMethod() - relayClient.prompt = false - try! await networkingInteractor.request(topic: topic, payload: method.asRequest()) - XCTAssertTrue(relayClient.prompt) - } -} - -extension NetworkingInteractorTests { - func getWCSessionMethod() -> WCMethod { - let sessionRequestParams = SessionType.RequestParams(request: SessionType.RequestParams.Request(method: "method", params: AnyCodable("params")), chainId: Blockchain("eip155:1")!) - return .wcSessionRequest(sessionRequestParams) - } -} - -private let testPayload = -""" -{ - "id":1630300527198334, - "jsonrpc":"2.0", - "method":"irn_subscription", - "params":{ - "id":"0847f4e1dd19cf03a43dc7525f39896b630e9da33e4683c8efbc92ea671b5e07", - "data":{ - "topic":"fefc3dc39cacbc562ed58f92b296e2d65a6b07ef08992b93db5b3cb86280635a", - "message":"7b226964223a313633303330303532383030302c226a736f6e727063223a22322e30222c22726573756c74223a747275657d" - } - } -} -""" -// TODO - change for different request -private let request = WCRequest(id: 1, jsonrpc: "2.0", method: .pairingPing, params: WCRequest.Params.pairingPing(PairingType.PingParams())) +//import Foundation +//import Combine +//import XCTest +//import WalletConnectUtils +//import WalletConnectPairing +//import WalletConnectNetworking +//@testable import TestingUtils +//@testable import WalletConnectSign +// +//class NetworkingInteractorTests: XCTestCase { +// var networkingInteractor: NetworkingInteractor! +// var relayClient: MockedRelayClient! +// var serializer: SerializerMock! +// +// private var publishers = [AnyCancellable]() +// +// override func setUp() { +// let logger = ConsoleLoggerMock() +// serializer = SerializerMock() +// relayClient = MockedRelayClient() +// networkingInteractor = NetworkingInteractor(relayClient: relayClient, serializer: serializer, logger: logger, rpcHistory: RPCHistory(logger: logger, keyValueStore: CodableStore(defaults: RuntimeKeyValueStorage(), identifier: ""))) +// } +// +// override func tearDown() { +// networkingInteractor = nil +// relayClient = nil +// serializer = nil +// } +// +// func testNotifiesOnEncryptedWCJsonRpcRequest() { +// let requestExpectation = expectation(description: "notifies with request") +// let topic = "fefc3dc39cacbc562ed58f92b296e2d65a6b07ef08992b93db5b3cb86280635a" +// networkingInteractor.wcRequestPublisher.sink { (_) in +// requestExpectation.fulfill() +// }.store(in: &publishers) +// serializer.deserialized = request +// relayClient.messagePublisherSubject.send((topic, testPayload)) +// waitForExpectations(timeout: 1.001, handler: nil) +// } +// +// func testPromptOnSessionRequest() async { +// let topic = "fefc3dc39cacbc562ed58f92b296e2d65a6b07ef08992b93db5b3cb86280635a" +// let method = getWCSessionMethod() +// relayClient.prompt = false +// try! await networkingInteractor.request(topic: topic, payload: method.asRequest()) +// XCTAssertTrue(relayClient.prompt) +// } +//} +// +//extension NetworkingInteractorTests { +// func getWCSessionMethod() -> WCMethod { +// let sessionRequestParams = SessionType.RequestParams(request: SessionType.RequestParams.Request(method: "method", params: AnyCodable("params")), chainId: Blockchain("eip155:1")!) +// return .wcSessionRequest(sessionRequestParams) +// } +//} +// +//private let testPayload = +//""" +//{ +// "id":1630300527198334, +// "jsonrpc":"2.0", +// "method":"irn_subscription", +// "params":{ +// "id":"0847f4e1dd19cf03a43dc7525f39896b630e9da33e4683c8efbc92ea671b5e07", +// "data":{ +// "topic":"fefc3dc39cacbc562ed58f92b296e2d65a6b07ef08992b93db5b3cb86280635a", +// "message":"7b226964223a313633303330303532383030302c226a736f6e727063223a22322e30222c22726573756c74223a747275657d" +// } +// } +//} +//""" +//// TODO - change for different request +//private let request = WCRequest(id: 1, jsonrpc: "2.0", method: .pairingPing, params: WCRequest.Params.pairingPing(PairingType.PingParams())) diff --git a/Tests/WalletConnectSignTests/WCResponseTests.swift b/Tests/WalletConnectSignTests/WCResponseTests.swift index 24729924b..b4d814d9e 100644 --- a/Tests/WalletConnectSignTests/WCResponseTests.swift +++ b/Tests/WalletConnectSignTests/WCResponseTests.swift @@ -1,17 +1,15 @@ import XCTest +import JSONRPC @testable import WalletConnectSign -final class WCResponseTests: XCTestCase { +final class RPCIDTests: XCTestCase { func testTimestamp() { - let request = WCRequest( - method: .pairingPing, - params: .pairingPing(.init()) - ) - let response = WCResponse.stubError(forRequest: request, topic: "topic") - let timestamp = Date(timeIntervalSince1970: TimeInterval(request.id / 1000 / 1000)) + let request = RPCRequest(method: "method") + let response = RPCResponse(matchingRequest: request, error: JSONRPCError(code: 0, message: "message")) + let timestamp = Date(timeIntervalSince1970: TimeInterval(request.id!.right! / 1000 / 1000)) - XCTAssertEqual(response.timestamp, timestamp) - XCTAssertTrue(Calendar.current.isDateInToday(response.timestamp)) + XCTAssertEqual(response.id!.timestamp, timestamp) + XCTAssertTrue(Calendar.current.isDateInToday(response.id!.timestamp)) } } From 12587e4282a77d53c40123236573d88224e0bfcd Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Fri, 9 Sep 2022 14:15:16 +0300 Subject: [PATCH 010/175] Sample apps build errors fixed --- .../DApp/Sign/ResponseViewController.swift | 10 ++++----- .../SessionDetailViewController.swift | 13 +++++------ .../SessionDetailViewModel.swift | 20 +++++++++++------ .../Wallet/WalletViewController.swift | 13 +++++------ .../Sign/Helpers/ClientDelegate.swift | 11 +++------- .../Sign/SignClientTests.swift | 4 ++-- .../Engine/Common/SessionEngine.swift | 1 + Sources/WalletConnectSign/Response.swift | 1 + .../WalletConnectSign/Sign/SignClient.swift | 22 ++++++------------- 9 files changed, 42 insertions(+), 53 deletions(-) diff --git a/Example/DApp/Sign/ResponseViewController.swift b/Example/DApp/Sign/ResponseViewController.swift index 4f8d1c0b3..eec81065f 100644 --- a/Example/DApp/Sign/ResponseViewController.swift +++ b/Example/DApp/Sign/ResponseViewController.swift @@ -23,14 +23,14 @@ class ResponseViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - let record = Sign.instance.getSessionRequestRecord(id: response.result.id)! + let record = Sign.instance.getSessionRequestRecord(id: response.id)! switch response.result { case .response(let response): - responseView.nameLabel.text = "Received Response\n\(record.request.method)" - responseView.descriptionLabel.text = try! response.result.get(String.self).description + responseView.nameLabel.text = "Received Response\n\(record.method)" + responseView.descriptionLabel.text = try! response.get(String.self).description case .error(let error): - responseView.nameLabel.text = "Received Error\n\(record.request.method)" - responseView.descriptionLabel.text = error.error.message + responseView.nameLabel.text = "Received Error\n\(record.method)" + responseView.descriptionLabel.text = error.message } responseView.dismissButton.addTarget(self, action: #selector(dismissSelf), for: .touchUpInside) } diff --git a/Example/ExampleApp/SessionDetails/SessionDetailViewController.swift b/Example/ExampleApp/SessionDetails/SessionDetailViewController.swift index 4d7d6b39e..f1b2a05bc 100644 --- a/Example/ExampleApp/SessionDetails/SessionDetailViewController.swift +++ b/Example/ExampleApp/SessionDetails/SessionDetailViewController.swift @@ -24,8 +24,7 @@ final class SessionDetailViewController: UIHostingController let viewController = RequestViewController(request) viewController.onSign = { [unowned self] in let result = Signer.signEth(request: request) - let response = JSONRPCResponse(id: request.id, result: result) - respondOnSign(request: request, response: response) + respondOnSign(request: request, response: result) reload() } viewController.onReject = { [unowned self] in @@ -35,11 +34,11 @@ final class SessionDetailViewController: UIHostingController present(viewController, animated: true) } - private func respondOnSign(request: Request, response: JSONRPCResponse) { + private func respondOnSign(request: Request, response: AnyCodable) { print("[WALLET] Respond on Sign") Task { do { - try await Sign.instance.respond(topic: request.topic, response: .response(response)) + try await Sign.instance.respond(topic: request.topic, requestId: request.id, response: .response(response)) } catch { print("[DAPP] Respond Error: \(error.localizedDescription)") } @@ -52,10 +51,8 @@ final class SessionDetailViewController: UIHostingController do { try await Sign.instance.respond( topic: request.topic, - response: .error(JSONRPCErrorResponse( - id: request.id, - error: JSONRPCErrorResponse.Error(code: 0, message: "")) - ) + requestId: request.id, + response: .error(.init(code: 0, message: "")) ) } catch { print("[DAPP] Respond Error: \(error.localizedDescription)") diff --git a/Example/ExampleApp/SessionDetails/SessionDetailViewModel.swift b/Example/ExampleApp/SessionDetails/SessionDetailViewModel.swift index f29dea255..88fe381b1 100644 --- a/Example/ExampleApp/SessionDetails/SessionDetailViewModel.swift +++ b/Example/ExampleApp/SessionDetails/SessionDetailViewModel.swift @@ -7,6 +7,8 @@ final class SessionDetailViewModel: ObservableObject { private let session: Session private let client: SignClient + private var publishers = Set() + enum Fields { case accounts case methods @@ -22,6 +24,8 @@ final class SessionDetailViewModel: ObservableObject { self.session = session self.client = client self.namespaces = session.namespaces + + setupSubscriptions() } var peerName: String { @@ -81,13 +85,8 @@ final class SessionDetailViewModel: ObservableObject { } func ping() { - client.ping(topic: session.topic) { result in - switch result { - case .success: - self.pingSuccess = true - case .failure: - self.pingFailed = true - } + Task(priority: .userInitiated) { + try await client.ping(topic: session.topic) } } @@ -98,6 +97,13 @@ final class SessionDetailViewModel: ObservableObject { private extension SessionDetailViewModel { + func setupSubscriptions() { + client.pingResponsePublisher.sink { _ in + self.pingSuccess = true + } + .store(in: &publishers) + } + func addTestAccount(for chain: String) { guard let viewModel = namespace(for: chain) else { return } diff --git a/Example/ExampleApp/Wallet/WalletViewController.swift b/Example/ExampleApp/Wallet/WalletViewController.swift index 9d574ea34..c3bbacdae 100644 --- a/Example/ExampleApp/Wallet/WalletViewController.swift +++ b/Example/ExampleApp/Wallet/WalletViewController.swift @@ -71,8 +71,7 @@ final class WalletViewController: UIViewController { let requestVC = RequestViewController(request) requestVC.onSign = { [unowned self] in let result = Signer.signEth(request: request) - let response = JSONRPCResponse(id: request.id, result: result) - respondOnSign(request: request, response: response) + respondOnSign(request: request, response: result) reloadSessionDetailsIfNeeded() } requestVC.onReject = { [unowned self] in @@ -90,11 +89,11 @@ final class WalletViewController: UIViewController { } @MainActor - private func respondOnSign(request: Request, response: JSONRPCResponse) { + private func respondOnSign(request: Request, response: AnyCodable) { print("[WALLET] Respond on Sign") Task { do { - try await Sign.instance.respond(topic: request.topic, response: .response(response)) + try await Sign.instance.respond(topic: request.topic, requestId: request.id, response: .response(response)) } catch { print("[DAPP] Respond Error: \(error.localizedDescription)") } @@ -108,10 +107,8 @@ final class WalletViewController: UIViewController { do { try await Sign.instance.respond( topic: request.topic, - response: .error(JSONRPCErrorResponse( - id: request.id, - error: JSONRPCErrorResponse.Error(code: 0, message: "")) - ) + requestId: request.id, + response: .error(.init(code: 0, message: "")) ) } catch { print("[DAPP] Respond Error: \(error.localizedDescription)") diff --git a/Example/IntegrationTests/Sign/Helpers/ClientDelegate.swift b/Example/IntegrationTests/Sign/Helpers/ClientDelegate.swift index ab7b43e11..e299d0316 100644 --- a/Example/IntegrationTests/Sign/Helpers/ClientDelegate.swift +++ b/Example/IntegrationTests/Sign/Helpers/ClientDelegate.swift @@ -14,8 +14,7 @@ class ClientDelegate { var onSessionDelete: (() -> Void)? var onSessionUpdateNamespaces: ((String, [String: SessionNamespace]) -> Void)? var onSessionExtend: ((String, Date) -> Void)? - var onSessionPing: ((String) -> Void)? - var onPairingPing: ((String) -> Void)? + var onPing: ((String) -> Void)? var onEventReceived: ((Session.Event, String) -> Void)? private var publishers = Set() @@ -66,12 +65,8 @@ class ClientDelegate { self.onSessionExtend?(topic, date) }.store(in: &publishers) - client.sessionPingResponsePublisher.sink { topic in - self.onSessionPing?(topic) - }.store(in: &publishers) - - client.pairingPingResponsePublisher.sink { topic in - self.onPairingPing?(topic) + client.pingResponsePublisher.sink { topic in + self.onPing?(topic) }.store(in: &publishers) } } diff --git a/Example/IntegrationTests/Sign/SignClientTests.swift b/Example/IntegrationTests/Sign/SignClientTests.swift index 8a30c72cc..17524d541 100644 --- a/Example/IntegrationTests/Sign/SignClientTests.swift +++ b/Example/IntegrationTests/Sign/SignClientTests.swift @@ -145,7 +145,7 @@ final class SignClientTests: XCTestCase { let pairing = wallet.client.getPairings().first! - wallet.onPairingPing = { topic in + wallet.onPing = { topic in XCTAssertEqual(topic, pairing.topic) pongResponseExpectation.fulfill() } @@ -173,7 +173,7 @@ final class SignClientTests: XCTestCase { } } - dapp.onSessionPing = { topic in + dapp.onPing = { topic in let session = self.wallet.client.getSessions().first! XCTAssertEqual(topic, session.topic) expectation.fulfill() diff --git a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift index 1372c8fe6..e210a3444 100644 --- a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift @@ -126,6 +126,7 @@ private extension SessionEngine { networkingInteractor.responseSubscription(on: SignProtocolMethod.sessionRequest) .sink { [unowned self] (payload: ResponseSubscriptionPayload) in onSessionResponse?(Response( + id: payload.id, topic: payload.topic, chainId: payload.request.chainId.absoluteString, result: payload.response diff --git a/Sources/WalletConnectSign/Response.swift b/Sources/WalletConnectSign/Response.swift index c01e438e5..aaaaf02fa 100644 --- a/Sources/WalletConnectSign/Response.swift +++ b/Sources/WalletConnectSign/Response.swift @@ -3,6 +3,7 @@ import JSONRPC import WalletConnectUtils public struct Response: Codable { + public let id: RPCID public let topic: String public let chainId: String? public let result: RPCResult diff --git a/Sources/WalletConnectSign/Sign/SignClient.swift b/Sources/WalletConnectSign/Sign/SignClient.swift index 4d343153a..83ba5cbb0 100644 --- a/Sources/WalletConnectSign/Sign/SignClient.swift +++ b/Sources/WalletConnectSign/Sign/SignClient.swift @@ -87,15 +87,8 @@ public final class SignClient { /// Publisher that sends session topic when session ping received /// /// Event will be emited on controller and non-controller clients. - public var sessionPingResponsePublisher: AnyPublisher { - sessionPingResponsePublisherSubject.eraseToAnyPublisher() - } - - /// Publisher that sends pairing topic when pairing ping received - /// - /// Event will be emited on controller and non-controller clients. - public var pairingPingResponsePublisher: AnyPublisher { - pairingPingResponsePublisherSubject.eraseToAnyPublisher() + public var pingResponsePublisher: AnyPublisher { + pingResponsePublisherSubject.eraseToAnyPublisher() } /// An object that loggs SDK's errors and info messages @@ -126,8 +119,7 @@ public final class SignClient { private let sessionUpdatePublisherSubject = PassthroughSubject<(sessionTopic: String, namespaces: [String: SessionNamespace]), Never>() private let sessionEventPublisherSubject = PassthroughSubject<(event: Session.Event, sessionTopic: String, chainId: Blockchain?), Never>() private let sessionExtendPublisherSubject = PassthroughSubject<(sessionTopic: String, date: Date), Never>() - private let sessionPingResponsePublisherSubject = PassthroughSubject() - private let pairingPingResponsePublisherSubject = PassthroughSubject() + private let pingResponsePublisherSubject = PassthroughSubject() private var publishers = Set() @@ -324,9 +316,9 @@ public final class SignClient { /// - Parameter id: id of a wc_sessionRequest jsonrpc request /// - Returns: json rpc record object for given id or nil if record for give id does not exits - public func getSessionRequestRecord(id: Int64) -> Request? { + public func getSessionRequestRecord(id: RPCID) -> Request? { guard - let record = history.get(recordId: RPCID(id)), + let record = history.get(recordId: id), let request = try? record.request.params?.get(SessionType.RequestParams.self) else { return nil } @@ -379,10 +371,10 @@ public final class SignClient { sessionResponsePublisherSubject.send(response) } pairingPingService.onResponse = { [unowned self] topic in - pairingPingResponsePublisherSubject.send(topic) + pingResponsePublisherSubject.send(topic) } sessionPingService.onResponse = { [unowned self] topic in - sessionPingResponsePublisherSubject.send(topic) + pingResponsePublisherSubject.send(topic) } } From fb9805bb49379b3c705bad3d6ccdf7ec98486fbd Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Mon, 12 Sep 2022 14:10:06 +0300 Subject: [PATCH 011/175] Sample apps and UI tests repaired --- .../SelectChain/SelectChainViewController.swift | 4 ---- Example/DApp/Sign/SignCoordinator.swift | 10 +++++++--- Example/ExampleApp/SceneDelegate.swift | 17 ++++++++++++++++- .../SessionDetails/SessionDetailViewModel.swift | 10 ++++++---- .../SessionDetailsViewController.swift | 8 ++++---- .../Wallet/WalletViewController.swift | 4 +--- .../Engine/Common/PairingEngine.swift | 2 +- 7 files changed, 35 insertions(+), 20 deletions(-) diff --git a/Example/DApp/Sign/SelectChain/SelectChainViewController.swift b/Example/DApp/Sign/SelectChain/SelectChainViewController.swift index 74210f740..3a78d7efc 100644 --- a/Example/DApp/Sign/SelectChain/SelectChainViewController.swift +++ b/Example/DApp/Sign/SelectChain/SelectChainViewController.swift @@ -15,16 +15,12 @@ class SelectChainViewController: UIViewController, UITableViewDataSource { private var publishers = [AnyCancellable]() let chains = [Chain(name: "Ethereum", id: "eip155:1"), Chain(name: "Polygon", id: "eip155:137")] - var onSessionSettled: ((Session) -> Void)? override func viewDidLoad() { super.viewDidLoad() navigationItem.title = "Available Chains" selectChainView.tableView.dataSource = self selectChainView.connectButton.addTarget(self, action: #selector(connect), for: .touchUpInside) selectChainView.openWallet.addTarget(self, action: #selector(openWallet), for: .touchUpInside) - Sign.instance.sessionSettlePublisher.sink {[unowned self] session in - onSessionSettled?(session) - }.store(in: &publishers) } override func loadView() { diff --git a/Example/DApp/Sign/SignCoordinator.swift b/Example/DApp/Sign/SignCoordinator.swift index 1373e4c30..eec11715c 100644 --- a/Example/DApp/Sign/SignCoordinator.swift +++ b/Example/DApp/Sign/SignCoordinator.swift @@ -44,6 +44,12 @@ final class SignCoordinator { presentResponse(for: response) }.store(in: &publishers) + Sign.instance.sessionSettlePublisher + .receive(on: DispatchQueue.main) + .sink { [unowned self] session in + showAccountsScreen(session) + }.store(in: &publishers) + if let session = Sign.instance.getSessions().first { showAccountsScreen(session) } else { @@ -53,9 +59,6 @@ final class SignCoordinator { private func showSelectChainScreen() { let controller = SelectChainViewController() - controller.onSessionSettled = { [unowned self] session in - showAccountsScreen(session) - } navigationController.viewControllers = [controller] } @@ -64,6 +67,7 @@ final class SignCoordinator { controller.onDisconnect = { [unowned self] in showSelectChainScreen() } + navigationController.presentedViewController?.dismiss(animated: true) navigationController.viewControllers = [controller] } diff --git a/Example/ExampleApp/SceneDelegate.swift b/Example/ExampleApp/SceneDelegate.swift index ba57b9ff4..6cd39be9a 100644 --- a/Example/ExampleApp/SceneDelegate.swift +++ b/Example/ExampleApp/SceneDelegate.swift @@ -1,4 +1,5 @@ import UIKit +import Combine import WalletConnectSign import WalletConnectRelay import Starscream @@ -15,6 +16,10 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? + private var publishers: Set = [] + private var onConnected: (() -> Void)? + private var connectionStatus: SocketConnectionStatus = .disconnected + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { let metadata = AppMetadata( @@ -35,6 +40,16 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { window = UIWindow(windowScene: windowScene) window?.rootViewController = UITabBarController.createExampleApp() window?.makeKeyAndVisible() + + Sign.instance.socketConnectionStatusPublisher + .receive(on: DispatchQueue.main) + .sink { [unowned self] status in + self.connectionStatus = status + if status == .connected { + self.onConnected?() + self.onConnected = nil + } + }.store(in: &publishers) } func scene(_ scene: UIScene, continue userActivity: NSUserActivity) { @@ -44,7 +59,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { } let wcUri = incomingURL.absoluteString.deletingPrefix("https://walletconnect.com/wc?uri=") let vc = ((window!.rootViewController as! UINavigationController).viewControllers[0] as! WalletViewController) - Task(priority: .high) {try? await Sign.instance.pair(uri: WalletConnectURI(string: wcUri)!)} + Task(priority: .high) { try? await Sign.instance.pair(uri: WalletConnectURI(string: wcUri)!) } vc.onClientConnected = { Task(priority: .high) { do { diff --git a/Example/ExampleApp/SessionDetails/SessionDetailViewModel.swift b/Example/ExampleApp/SessionDetails/SessionDetailViewModel.swift index 88fe381b1..d43a5ce3d 100644 --- a/Example/ExampleApp/SessionDetails/SessionDetailViewModel.swift +++ b/Example/ExampleApp/SessionDetails/SessionDetailViewModel.swift @@ -98,10 +98,12 @@ final class SessionDetailViewModel: ObservableObject { private extension SessionDetailViewModel { func setupSubscriptions() { - client.pingResponsePublisher.sink { _ in - self.pingSuccess = true - } - .store(in: &publishers) + client.pingResponsePublisher + .receive(on: DispatchQueue.main) + .sink { _ in + self.pingSuccess = true + } + .store(in: &publishers) } func addTestAccount(for chain: String) { diff --git a/Example/ExampleApp/SessionDetails/SessionDetailsViewController.swift b/Example/ExampleApp/SessionDetails/SessionDetailsViewController.swift index f53ecbdef..bb6d21be6 100644 --- a/Example/ExampleApp/SessionDetails/SessionDetailsViewController.swift +++ b/Example/ExampleApp/SessionDetails/SessionDetailsViewController.swift @@ -49,11 +49,11 @@ final class SessionDetailsViewController: UIViewController, UITableViewDelegate, @objc private func ping() { - Sign.instance.ping(topic: session.topic) { result in - switch result { - case .success: + Task(priority: .userInitiated) { @MainActor in + do { + try await Sign.instance.ping(topic: session.topic) print("received ping response") - case .failure(let error): + } catch { print(error) } } diff --git a/Example/ExampleApp/Wallet/WalletViewController.swift b/Example/ExampleApp/Wallet/WalletViewController.swift index c3bbacdae..fbc5322db 100644 --- a/Example/ExampleApp/Wallet/WalletViewController.swift +++ b/Example/ExampleApp/Wallet/WalletViewController.swift @@ -10,7 +10,6 @@ final class WalletViewController: UIViewController { lazy var account = Signer.privateKey.address.hex(eip55: true) var sessionItems: [ActiveSessionItem] = [] var currentProposal: Session.Proposal? - var onClientConnected: (() -> Void)? private var publishers = [AnyCancellable]() private let walletView: WalletView = { @@ -236,9 +235,8 @@ extension WalletViewController { func setUpAuthSubscribing() { Sign.instance.socketConnectionStatusPublisher .receive(on: DispatchQueue.main) - .sink { [weak self] status in + .sink { status in if status == .connected { - self?.onClientConnected?() print("Client connected") } }.store(in: &publishers) diff --git a/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift b/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift index 7c75d9876..cac8290f8 100644 --- a/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift @@ -73,7 +73,7 @@ final class PairingEngine { requiredNamespaces: namespaces) let request = RPCRequest(method: SignProtocolMethod.sessionPropose.method, params: proposal) - try await networkingInteractor.request(request, topic: pairingTopic, tag: SignProtocolMethod.sessionPropose.requestTag) + try await networkingInteractor.requestNetworkAck(request, topic: pairingTopic, tag: SignProtocolMethod.sessionPropose.requestTag) } } From f2dc91eb4edbd3c85972955937fcdb9250bb0ccc Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Mon, 12 Sep 2022 21:08:51 +0300 Subject: [PATCH 012/175] testSessionPing duplicate removed --- .../Sign/SignClientTests.swift | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/Example/IntegrationTests/Sign/SignClientTests.swift b/Example/IntegrationTests/Sign/SignClientTests.swift index 17524d541..a62321a05 100644 --- a/Example/IntegrationTests/Sign/SignClientTests.swift +++ b/Example/IntegrationTests/Sign/SignClientTests.swift @@ -314,27 +314,6 @@ final class SignClientTests: XCTestCase { } - func testSessionPing() async { - let expectation = expectation(description: "Dapp receives ping response") - let requiredNamespaces = ProposalNamespace.stubRequired() - let sessionNamespaces = SessionNamespace.make(toRespond: requiredNamespaces) - - wallet.onSessionProposal = { [unowned self] proposal in - Task(priority: .high) { - try await wallet.client.approve(proposalId: proposal.id, namespaces: sessionNamespaces) - } - } - dapp.onSessionSettled = { [unowned self] settledSession in - dapp.client.ping(topic: settledSession.topic) {_ in - expectation.fulfill() - } - } - let uri = try! await dapp.client.connect(requiredNamespaces: requiredNamespaces) - try! await wallet.client.pair(uri: uri!) - wait(for: [expectation], timeout: defaultTimeout) - } - - func testSuccessfulSessionUpdateNamespaces() async { let expectation = expectation(description: "Dapp updates namespaces") let requiredNamespaces = ProposalNamespace.stubRequired() From dc469c250c774cbece96949b36e0a0fe0ae5221c Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Tue, 13 Sep 2022 02:35:07 +0300 Subject: [PATCH 013/175] Wallet sample app fixed --- Example/DApp/Sign/SignCoordinator.swift | 2 +- Example/ExampleApp/SceneDelegate.swift | 15 +-------------- .../Engine/Common/PairingEngine.swift | 1 - .../Engine/Common/SessionEngine.swift | 1 - 4 files changed, 2 insertions(+), 17 deletions(-) diff --git a/Example/DApp/Sign/SignCoordinator.swift b/Example/DApp/Sign/SignCoordinator.swift index eec11715c..8470ad200 100644 --- a/Example/DApp/Sign/SignCoordinator.swift +++ b/Example/DApp/Sign/SignCoordinator.swift @@ -67,7 +67,7 @@ final class SignCoordinator { controller.onDisconnect = { [unowned self] in showSelectChainScreen() } - navigationController.presentedViewController?.dismiss(animated: true) + navigationController.presentedViewController?.dismiss(animated: false) navigationController.viewControllers = [controller] } diff --git a/Example/ExampleApp/SceneDelegate.swift b/Example/ExampleApp/SceneDelegate.swift index 6cd39be9a..d469490bc 100644 --- a/Example/ExampleApp/SceneDelegate.swift +++ b/Example/ExampleApp/SceneDelegate.swift @@ -1,4 +1,5 @@ import UIKit +import Foundation import Combine import WalletConnectSign import WalletConnectRelay @@ -16,10 +17,6 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? - private var publishers: Set = [] - private var onConnected: (() -> Void)? - private var connectionStatus: SocketConnectionStatus = .disconnected - func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { let metadata = AppMetadata( @@ -40,16 +37,6 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { window = UIWindow(windowScene: windowScene) window?.rootViewController = UITabBarController.createExampleApp() window?.makeKeyAndVisible() - - Sign.instance.socketConnectionStatusPublisher - .receive(on: DispatchQueue.main) - .sink { [unowned self] status in - self.connectionStatus = status - if status == .connected { - self.onConnected?() - self.onConnected = nil - } - }.store(in: &publishers) } func scene(_ scene: UIScene, continue userActivity: NSUserActivity) { diff --git a/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift b/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift index cac8290f8..d6a012de0 100644 --- a/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift @@ -84,7 +84,6 @@ private extension PairingEngine { func setupNetworkingSubscriptions() { networkingInteractor.socketConnectionStatusPublisher .sink { [unowned self] status in - guard status == .connected else { return } pairingStore.getAll() .forEach { pairing in Task(priority: .high) { try await networkingInteractor.subscribe(topic: pairing.topic) } diff --git a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift index e210a3444..2c9c2be07 100644 --- a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift @@ -91,7 +91,6 @@ private extension SessionEngine { func setupConnectionSubscriptions() { networkingInteractor.socketConnectionStatusPublisher .sink { [unowned self] status in - guard status == .connected else { return } sessionStore.getAll() .forEach { session in Task(priority: .high) { try await networkingInteractor.subscribe(topic: session.topic) } From be2641ed93993e63d06d9b58cbd28879262070d0 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Tue, 13 Sep 2022 16:06:32 +0300 Subject: [PATCH 014/175] UI Tests improvements --- .../ExampleApp/Wallet/WalletViewController.swift | 5 ++++- Example/UITests/Engine/WalletEngine.swift | 8 ++++++++ Example/UITests/Regression/RegressionTests.swift | 15 ++++++++------- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/Example/ExampleApp/Wallet/WalletViewController.swift b/Example/ExampleApp/Wallet/WalletViewController.swift index fbc5322db..d53343480 100644 --- a/Example/ExampleApp/Wallet/WalletViewController.swift +++ b/Example/ExampleApp/Wallet/WalletViewController.swift @@ -12,6 +12,8 @@ final class WalletViewController: UIViewController { var currentProposal: Session.Proposal? private var publishers = [AnyCancellable]() + var onClientConnected: (() -> Void)? + private let walletView: WalletView = { WalletView() }() @@ -235,9 +237,10 @@ extension WalletViewController { func setUpAuthSubscribing() { Sign.instance.socketConnectionStatusPublisher .receive(on: DispatchQueue.main) - .sink { status in + .sink { [weak self] status in if status == .connected { print("Client connected") + self?.onClientConnected?() } }.store(in: &publishers) diff --git a/Example/UITests/Engine/WalletEngine.swift b/Example/UITests/Engine/WalletEngine.swift index 536e45443..2255d87b8 100644 --- a/Example/UITests/Engine/WalletEngine.swift +++ b/Example/UITests/Engine/WalletEngine.swift @@ -45,7 +45,15 @@ struct WalletEngine { instance.buttons["Ping"] } + var okButton: XCUIElement { + instance.buttons["OK"] + } + var pingAlert: XCUIElement { instance.alerts.element.staticTexts["Received ping response"] } + + func swipeDismiss() { + instance.swipeDown(velocity: .fast) + } } diff --git a/Example/UITests/Regression/RegressionTests.swift b/Example/UITests/Regression/RegressionTests.swift index 716ba8702..4d3016b60 100644 --- a/Example/UITests/Regression/RegressionTests.swift +++ b/Example/UITests/Regression/RegressionTests.swift @@ -4,15 +4,13 @@ class PairingTests: XCTestCase { private let engine: Engine = Engine() - private static var cleanLaunch: Bool = true - - override func setUp() { - engine.routing.launch(app: .dapp, clean: PairingTests.cleanLaunch) - engine.routing.launch(app: .wallet, clean: PairingTests.cleanLaunch) - - PairingTests.cleanLaunch = false + override class func setUp() { + let engine: Engine = Engine() + engine.routing.launch(app: .dapp, clean: true) + engine.routing.launch(app: .wallet, clean: true) } + /// Check pairing proposal approval via QR code or uri /// - TU001 func test01PairingCreation() { @@ -45,6 +43,9 @@ class PairingTests: XCTestCase { engine.wallet.pingButton.waitTap() XCTAssertTrue(engine.wallet.pingAlert.waitExists()) + + engine.wallet.okButton.waitTap() + engine.wallet.swipeDismiss() } /// Approve session on existing pairing From 48d3adcdbd49e6c9ab4564a59cb6e0d9749120ae Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Wed, 14 Sep 2022 21:18:11 +0300 Subject: [PATCH 015/175] Approve Engine RPCResult replaced --- .../Engine/Common/ApproveEngine.swift | 116 ++++++++++-------- 1 file changed, 62 insertions(+), 54 deletions(-) diff --git a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift index 398e1566a..5a23eb079 100644 --- a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift @@ -12,6 +12,7 @@ final class ApproveEngine { case relayNotFound case proposalPayloadsNotFound case pairingNotFound + case sessionNotFound case agreementMissingOrInvalid } @@ -53,6 +54,7 @@ final class ApproveEngine { setupRequestSubscriptions() setupResponseSubscriptions() + setupResponseErrorSubscriptions() } func approveProposal(proposerPubKey: String, validating sessionNamespaces: [String: SessionNamespace]) async throws { @@ -165,21 +167,25 @@ private extension ApproveEngine { func setupResponseSubscriptions() { networkingInteractor.responseSubscription(on: SignProtocolMethod.sessionPropose) - .sink { [unowned self] (payload: ResponseSubscriptionPayload) in + .sink { [unowned self] (payload: ResponseSubscriptionPayload) in handleSessionProposeResponse(payload: payload) }.store(in: &publishers) networkingInteractor.responseSubscription(on: SignProtocolMethod.sessionSettle) - .sink { [unowned self] (payload: ResponseSubscriptionPayload) in + .sink { [unowned self] (payload: ResponseSubscriptionPayload) in handleSessionSettleResponse(payload: payload) }.store(in: &publishers) + } + func setupResponseErrorSubscriptions() { networkingInteractor.responseErrorSubscription(on: SignProtocolMethod.sessionPropose) .sink { [unowned self] (payload: ResponseSubscriptionErrorPayload) in - onSessionRejected?( - payload.request.publicRepresentation(), - SessionType.Reason(code: payload.error.code, message: payload.error.message) - ) + handleSessionProposeResponseError(payload: payload) + }.store(in: &publishers) + + networkingInteractor.responseErrorSubscription(on: SignProtocolMethod.sessionSettle) + .sink { [unowned self] (payload: ResponseSubscriptionErrorPayload) in + handleSessionSettleResponseError(payload: payload) }.store(in: &publishers) } @@ -201,32 +207,14 @@ private extension ApproveEngine { // MARK: SessionProposeResponse // TODO: Move to Non-Controller SettleEngine - func handleSessionProposeResponse(payload: ResponseSubscriptionPayload) { + func handleSessionProposeResponse(payload: ResponseSubscriptionPayload) { do { - let sessionTopic = try handleProposeResponse( - pairingTopic: payload.topic, - proposal: payload.request, - result: payload.response - ) - settlingProposal = payload.request + let pairingTopic = payload.topic - Task(priority: .high) { - try await networkingInteractor.subscribe(topic: sessionTopic) - } - } catch { - guard let error = error as? Reason else { - return logger.debug(error.localizedDescription) + guard var pairing = pairingStore.getPairing(forTopic: pairingTopic) else { + throw Errors.pairingNotFound } - onSessionRejected?(payload.request.publicRepresentation(), SessionType.Reason(code: error.code, message: error.message)) - } - } - func handleProposeResponse(pairingTopic: String, proposal: SessionProposal, result: RPCResult) throws -> String { - guard var pairing = pairingStore.getPairing(forTopic: pairingTopic) - else { throw Errors.pairingNotFound } - - switch result { - case .response(let response): // Activate the pairing if !pairing.active { pairing.activate() @@ -236,9 +224,8 @@ private extension ApproveEngine { pairingStore.setPairing(pairing) - let selfPublicKey = try AgreementPublicKey(hex: proposal.proposer.publicKey) - let proposeResponse = try response.get(SessionType.ProposeResponse.self) - let agreementKeys = try kms.performKeyAgreement(selfPublicKey: selfPublicKey, peerPublicKey: proposeResponse.responderPublicKey) + let selfPublicKey = try AgreementPublicKey(hex: payload.request.proposer.publicKey) + let agreementKeys = try kms.performKeyAgreement(selfPublicKey: selfPublicKey, peerPublicKey: payload.response.responderPublicKey) let sessionTopic = agreementKeys.derivedTopic() logger.debug("Received Session Proposal response") @@ -246,37 +233,57 @@ private extension ApproveEngine { try kms.setAgreementSecret(agreementKeys, topic: sessionTopic) sessionToPairingTopic.set(pairingTopic, forKey: sessionTopic) - return sessionTopic + settlingProposal = payload.request - case .error(let error): - if !pairing.active { - kms.deleteSymmetricKey(for: pairing.topic) - networkingInteractor.unsubscribe(topic: pairing.topic) - pairingStore.delete(topic: pairingTopic) + Task(priority: .high) { + try await networkingInteractor.subscribe(topic: sessionTopic) } - logger.debug("Session Proposal has been rejected") - kms.deletePrivateKey(for: proposal.proposer.publicKey) - throw error + } catch { + return logger.debug(error.localizedDescription) + } + } + + func handleSessionProposeResponseError(payload: ResponseSubscriptionErrorPayload) { + guard let pairing = pairingStore.getPairing(forTopic: payload.topic) else { + return logger.debug(Errors.pairingNotFound.localizedDescription) } + + if !pairing.active { + kms.deleteSymmetricKey(for: pairing.topic) + networkingInteractor.unsubscribe(topic: pairing.topic) + pairingStore.delete(topic: payload.topic) + } + logger.debug("Session Proposal has been rejected") + kms.deletePrivateKey(for: payload.request.proposer.publicKey) + + onSessionRejected?( + payload.request.publicRepresentation(), + SessionType.Reason(code: payload.error.code, message: payload.error.message) + ) } // MARK: SessionSettleResponse - func handleSessionSettleResponse(payload: ResponseSubscriptionPayload) { - guard let session = sessionStore.getSession(forTopic: payload.topic) else { return } - switch payload.response { - case .response: - logger.debug("Received session settle response") - guard var session = sessionStore.getSession(forTopic: payload.topic) else { return } - session.acknowledge() - sessionStore.setSession(session) - case .error(let error): - logger.error("Error - session rejected, Reason: \(error)") - networkingInteractor.unsubscribe(topic: payload.topic) - sessionStore.delete(topic: payload.topic) - kms.deleteAgreementSecret(for: payload.topic) - kms.deletePrivateKey(for: session.publicKey!) + func handleSessionSettleResponse(payload: ResponseSubscriptionPayload) { + guard var session = sessionStore.getSession(forTopic: payload.topic) else { + return logger.debug(Errors.sessionNotFound.localizedDescription) } + + logger.debug("Received session settle response") + session.acknowledge() + sessionStore.setSession(session) + } + + func handleSessionSettleResponseError(payload: ResponseSubscriptionErrorPayload) { + guard let session = sessionStore.getSession(forTopic: payload.topic) else { + return logger.debug(Errors.sessionNotFound.localizedDescription) + } + + logger.error("Error - session rejected, Reason: \(payload.error)") + networkingInteractor.unsubscribe(topic: payload.topic) + sessionStore.delete(topic: payload.topic) + kms.deleteAgreementSecret(for: payload.topic) + kms.deletePrivateKey(for: session.publicKey!) } // MARK: SessionProposeRequest @@ -292,6 +299,7 @@ private extension ApproveEngine { } // MARK: SessionSettleRequest + func handleSessionSettleRequest(payload: RequestSubscriptionPayload) { logger.debug("Did receive session settle request") From 707d335a4ee6993caab201c00858eeec1bce64a6 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Wed, 14 Sep 2022 21:25:35 +0300 Subject: [PATCH 016/175] Prompt --- .../WalletConnectNetworking/NetworkInteractor.swift | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Sources/WalletConnectNetworking/NetworkInteractor.swift b/Sources/WalletConnectNetworking/NetworkInteractor.swift index 405533ef1..111d0003f 100644 --- a/Sources/WalletConnectNetworking/NetworkInteractor.swift +++ b/Sources/WalletConnectNetworking/NetworkInteractor.swift @@ -96,7 +96,7 @@ public class NetworkingInteractor: NetworkInteracting { public func request(_ request: RPCRequest, topic: String, tag: Int, envelopeType: Envelope.EnvelopeType) async throws { try rpcHistory.set(request, forTopic: topic, emmitedBy: .local) let message = try! serializer.serialize(topic: topic, encodable: request, envelopeType: envelopeType) - try await relayClient.publish(topic: topic, payload: message, tag: tag) + try await relayClient.publish(topic: topic, payload: message, tag: tag, prompt: shouldPrompt(method: request.method)) } /// Completes with an acknowledgement from the relay network. @@ -107,7 +107,7 @@ public class NetworkingInteractor: NetworkInteracting { try rpcHistory.set(request, forTopic: topic, emmitedBy: .local) let message = try serializer.serialize(topic: topic, encodable: request) return try await withCheckedThrowingContinuation { continuation in - relayClient.publish(topic: topic, payload: message, tag: tag) { error in + relayClient.publish(topic: topic, payload: message, tag: tag, prompt: shouldPrompt(method: request.method)) { error in if let error = error { continuation.resume(throwing: error) } else { @@ -165,4 +165,13 @@ public class NetworkingInteractor: NetworkInteracting { logger.debug("Handle json rpc response error: \(error)") } } + + private func shouldPrompt(method: String) -> Bool { + switch method { + case "wc_sessionRequest": // TODO: Include promt in ProtocolMethod + return true + default: + return false + } + } } From f79460583131cf91793b42a42c2d4449c1cc5432 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Wed, 14 Sep 2022 23:38:34 +0300 Subject: [PATCH 017/175] Unit test fixed --- Tests/WalletConnectSignTests/ApproveEngineTests.swift | 2 +- Tests/WalletConnectSignTests/Stub/Stubs.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/WalletConnectSignTests/ApproveEngineTests.swift b/Tests/WalletConnectSignTests/ApproveEngineTests.swift index 77fc29075..3f2c39da8 100644 --- a/Tests/WalletConnectSignTests/ApproveEngineTests.swift +++ b/Tests/WalletConnectSignTests/ApproveEngineTests.swift @@ -135,7 +135,7 @@ final class ApproveEngineTests: XCTestCase { try! cryptoMock.setPrivateKey(privateKey) let request = RPCRequest(method: SignProtocolMethod.sessionSettle.method, params: SessionType.SettleParams.stub()) - let response = RPCResponse(matchingRequest: request, result: RPCResult.error(JSONRPCError(code: 0, message: ""))) + let response = RPCResponse.stubError(forRequest: request) networkingInteractor.responsePublisherSubject.send((session.topic, request, response)) diff --git a/Tests/WalletConnectSignTests/Stub/Stubs.swift b/Tests/WalletConnectSignTests/Stub/Stubs.swift index 96ea5eb3b..f0b87a080 100644 --- a/Tests/WalletConnectSignTests/Stub/Stubs.swift +++ b/Tests/WalletConnectSignTests/Stub/Stubs.swift @@ -94,6 +94,6 @@ extension SessionProposal { extension RPCResponse { static func stubError(forRequest request: RPCRequest) -> RPCResponse { - return RPCResponse(matchingRequest: request, result: RPCResult.error(JSONRPCError(code: 0, message: ""))) + return RPCResponse(matchingRequest: request, error: JSONRPCError(code: 0, message: "")) } } From 51a0bcd45bfa7fc602688a0aa170e121674262b2 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 15 Sep 2022 02:38:49 +0300 Subject: [PATCH 018/175] RelayClient Retry --- Example/ExampleApp/SceneDelegate.swift | 12 +----- Sources/WalletConnectRelay/Dispatching.swift | 27 ------------ Sources/WalletConnectRelay/RelayClient.swift | 45 +++++++++++++++++--- 3 files changed, 41 insertions(+), 43 deletions(-) diff --git a/Example/ExampleApp/SceneDelegate.swift b/Example/ExampleApp/SceneDelegate.swift index ba57b9ff4..1a394d02a 100644 --- a/Example/ExampleApp/SceneDelegate.swift +++ b/Example/ExampleApp/SceneDelegate.swift @@ -43,16 +43,8 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { return } let wcUri = incomingURL.absoluteString.deletingPrefix("https://walletconnect.com/wc?uri=") - let vc = ((window!.rootViewController as! UINavigationController).viewControllers[0] as! WalletViewController) - Task(priority: .high) {try? await Sign.instance.pair(uri: WalletConnectURI(string: wcUri)!)} - vc.onClientConnected = { - Task(priority: .high) { - do { - try await Sign.instance.pair(uri: WalletConnectURI(string: wcUri)!) - } catch { - print(error) - } - } + Task(priority: .high) { + try! await Sign.instance.pair(uri: WalletConnectURI(string: wcUri)!) } } } diff --git a/Sources/WalletConnectRelay/Dispatching.swift b/Sources/WalletConnectRelay/Dispatching.swift index 2d8be9ba5..1f7a4b1db 100644 --- a/Sources/WalletConnectRelay/Dispatching.swift +++ b/Sources/WalletConnectRelay/Dispatching.swift @@ -5,7 +5,6 @@ protocol Dispatching { var onConnect: (() -> Void)? {get set} var onDisconnect: (() -> Void)? {get set} var onMessage: ((String) -> Void)? {get set} - func send(_ string: String) async throws func send(_ string: String, completion: @escaping (Error?) -> Void) func connect() throws func disconnect(closeCode: URLSessionWebSocketTask.CloseCode) throws @@ -31,28 +30,13 @@ final class Dispatcher: NSObject, Dispatching { setUpSocketConnectionObserving() } - func send(_ string: String) async throws { - return try await withCheckedThrowingContinuation { continuation in - if socket.isConnected { - socket.write(string: string) { - continuation.resume(returning: ()) - } - } else { - continuation.resume(throwing: NetworkError.webSocketNotConnected) - } - } - } - func send(_ string: String, completion: @escaping (Error?) -> Void) { - // TODO - add policy for retry and "single try" if socket.isConnected { self.socket.write(string: string) { completion(nil) } - // TODO - enqueue if fails } else { completion(NetworkError.webSocketNotConnected) -// textFramesQueue.enqueue(string) } } @@ -72,21 +56,10 @@ final class Dispatcher: NSObject, Dispatching { private func setUpSocketConnectionObserving() { socket.onConnect = { [unowned self] in - self.dequeuePendingTextFrames() self.onConnect?() } socket.onDisconnect = { [unowned self] _ in self.onDisconnect?() } } - - private func dequeuePendingTextFrames() { - while let frame = textFramesQueue.dequeue() { - send(frame) { [unowned self] error in - if let error = error { - self.logger.error(error.localizedDescription) - } - } - } - } } diff --git a/Sources/WalletConnectRelay/RelayClient.swift b/Sources/WalletConnectRelay/RelayClient.swift index 94ab8ceab..886c7293c 100644 --- a/Sources/WalletConnectRelay/RelayClient.swift +++ b/Sources/WalletConnectRelay/RelayClient.swift @@ -34,7 +34,7 @@ public final class RelayClient { } private let messagePublisherSubject = PassthroughSubject<(topic: String, message: String), Never>() - private let socketConnectionStatusPublisherSubject = PassthroughSubject() + private let socketConnectionStatusPublisherSubject = CurrentValueSubject(.disconnected) private let subscriptionResponsePublisherSubject = PassthroughSubject<(RPCID?, String), Never>() private var subscriptionResponsePublisher: AnyPublisher<(RPCID?, String), Never> { @@ -71,6 +71,9 @@ public final class RelayClient { dispatcher.onConnect = { [unowned self] in self.socketConnectionStatusPublisherSubject.send(.connected) } + dispatcher.onDisconnect = { + self.socketConnectionStatusPublisherSubject.send(.disconnected) + } } /// Instantiates Relay Client @@ -132,7 +135,7 @@ public final class RelayClient { .asRPCRequest() let message = try request.asJSONEncodedString() logger.debug("Publishing payload on topic: \(topic)") - try await dispatcher.send(message) + try await protectedSend(message) } /// Completes with an acknowledgement from the relay network. @@ -156,7 +159,7 @@ public final class RelayClient { cancellable?.cancel() onNetworkAcknowledge(nil) } - dispatcher.send(message) { [weak self] error in + protectedSend(message) { [weak self] error in if let error = error { self?.logger.debug("Failed to Publish Payload, error: \(error)") cancellable?.cancel() @@ -183,7 +186,7 @@ public final class RelayClient { } completion(nil) } - dispatcher.send(message) { [weak self] error in + protectedSend(message) { [weak self] error in if let error = error { self?.logger.debug("Failed to subscribe to topic \(error)") cancellable?.cancel() @@ -223,7 +226,7 @@ public final class RelayClient { cancellable?.cancel() completion(nil) } - dispatcher.send(message) { [weak self] error in + protectedSend(message) { [weak self] error in if let error = error { self?.logger.debug("Failed to unsubscribe from topic") cancellable?.cancel() @@ -279,7 +282,7 @@ public final class RelayClient { private func acknowledgeRequest(_ request: RPCRequest) throws { let response = RPCResponse(matchingRequest: request, result: true) let message = try response.asJSONEncodedString() - dispatcher.send(message) { [unowned self] in + protectedSend(message) { [unowned self] in if let error = $0 { logger.debug("Failed to dispatch response: \(response), error: \(error)") } else { @@ -291,4 +294,34 @@ public final class RelayClient { } } } + + private func protectedSend(_ string: String) async throws { + return try await withCheckedThrowingContinuation { continuation in + protectedSend(string) { error in + if let error = error { + continuation.resume(throwing: error) + } else { + continuation.resume(returning: ()) + } + } + } + } + + private func protectedSend(_ string: String, timeout: Int = 5, completion: @escaping (Error?) -> Void) { + guard socketConnectionStatusPublisherSubject.value == .disconnected else { + return dispatcher.send(string, completion: completion) + } + + var cancellable: AnyCancellable? + cancellable = socketConnectionStatusPublisher.sink { [unowned self] status in + guard status == .connected else { return } + defer { cancellable?.cancel() } + dispatcher.send(string, completion: completion) + } + + concurrentQueue.asyncAfter(deadline: .now() + .seconds(timeout)) { + completion(NetworkError.webSocketNotConnected) + cancellable?.cancel() + } + } } From e8a27621309ac05b86aff326bab09a8e7bd6e113 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 15 Sep 2022 19:55:50 +0300 Subject: [PATCH 019/175] PR suggetsions --- Sources/WalletConnectRelay/Dispatching.swift | 55 ++++++++++++++++--- Sources/WalletConnectRelay/RelayClient.swift | 49 ++--------------- Tests/RelayerTests/DispatcherTests.swift | 15 +++-- Tests/RelayerTests/Mocks/DispatcherMock.swift | 28 ++++++++-- 4 files changed, 84 insertions(+), 63 deletions(-) diff --git a/Sources/WalletConnectRelay/Dispatching.swift b/Sources/WalletConnectRelay/Dispatching.swift index 1f7a4b1db..49a112422 100644 --- a/Sources/WalletConnectRelay/Dispatching.swift +++ b/Sources/WalletConnectRelay/Dispatching.swift @@ -1,24 +1,31 @@ import Foundation +import Combine import WalletConnectUtils protocol Dispatching { - var onConnect: (() -> Void)? {get set} - var onDisconnect: (() -> Void)? {get set} - var onMessage: ((String) -> Void)? {get set} + var onMessage: ((String) -> Void)? { get set } + var socketConnectionStatusPublisher: AnyPublisher { get } func send(_ string: String, completion: @escaping (Error?) -> Void) + func protectedSend(_ string: String, completion: @escaping (Error?) -> Void) + func protectedSend(_ string: String) async throws func connect() throws func disconnect(closeCode: URLSessionWebSocketTask.CloseCode) throws } final class Dispatcher: NSObject, Dispatching { - var onConnect: (() -> Void)? - var onDisconnect: (() -> Void)? var onMessage: ((String) -> Void)? - private var textFramesQueue = Queue() - private let logger: ConsoleLogging var socket: WebSocketConnecting var socketConnectionHandler: SocketConnectionHandler + private let logger: ConsoleLogging + private let defaultTimeout: Int = 5 + + private let socketConnectionStatusPublisherSubject = CurrentValueSubject(.disconnected) + + var socketConnectionStatusPublisher: AnyPublisher { + socketConnectionStatusPublisherSubject.eraseToAnyPublisher() + } + init(socket: WebSocketConnecting, socketConnectionHandler: SocketConnectionHandler, logger: ConsoleLogging) { @@ -40,6 +47,36 @@ final class Dispatcher: NSObject, Dispatching { } } + func protectedSend(_ string: String, completion: @escaping (Error?) -> Void) { + guard socketConnectionStatusPublisherSubject.value == .disconnected else { + return send(string, completion: completion) + } + + var cancellable: AnyCancellable? + cancellable = socketConnectionStatusPublisher.sink { [unowned self] status in + guard status == .connected else { return } + defer { cancellable?.cancel() } + send(string, completion: completion) + } + + DispatchQueue.global().asyncAfter(deadline: .now() + .seconds(defaultTimeout)) { + completion(NetworkError.webSocketNotConnected) + cancellable?.cancel() + } + } + + func protectedSend(_ string: String) async throws { + return try await withCheckedThrowingContinuation { continuation in + protectedSend(string) { error in + if let error = error { + continuation.resume(throwing: error) + } else { + continuation.resume(returning: ()) + } + } + } + } + func connect() throws { try socketConnectionHandler.handleConnect() } @@ -56,10 +93,10 @@ final class Dispatcher: NSObject, Dispatching { private func setUpSocketConnectionObserving() { socket.onConnect = { [unowned self] in - self.onConnect?() + self.socketConnectionStatusPublisherSubject.send(.connected) } socket.onDisconnect = { [unowned self] _ in - self.onDisconnect?() + self.socketConnectionStatusPublisherSubject.send(.disconnected) } } } diff --git a/Sources/WalletConnectRelay/RelayClient.swift b/Sources/WalletConnectRelay/RelayClient.swift index 886c7293c..f68ad459e 100644 --- a/Sources/WalletConnectRelay/RelayClient.swift +++ b/Sources/WalletConnectRelay/RelayClient.swift @@ -30,11 +30,10 @@ public final class RelayClient { } public var socketConnectionStatusPublisher: AnyPublisher { - socketConnectionStatusPublisherSubject.eraseToAnyPublisher() + dispatcher.socketConnectionStatusPublisher } private let messagePublisherSubject = PassthroughSubject<(topic: String, message: String), Never>() - private let socketConnectionStatusPublisherSubject = CurrentValueSubject(.disconnected) private let subscriptionResponsePublisherSubject = PassthroughSubject<(RPCID?, String), Never>() private var subscriptionResponsePublisher: AnyPublisher<(RPCID?, String), Never> { @@ -68,12 +67,6 @@ public final class RelayClient { dispatcher.onMessage = { [weak self] payload in self?.handlePayloadMessage(payload) } - dispatcher.onConnect = { [unowned self] in - self.socketConnectionStatusPublisherSubject.send(.connected) - } - dispatcher.onDisconnect = { - self.socketConnectionStatusPublisherSubject.send(.disconnected) - } } /// Instantiates Relay Client @@ -135,7 +128,7 @@ public final class RelayClient { .asRPCRequest() let message = try request.asJSONEncodedString() logger.debug("Publishing payload on topic: \(topic)") - try await protectedSend(message) + try await dispatcher.protectedSend(message) } /// Completes with an acknowledgement from the relay network. @@ -159,7 +152,7 @@ public final class RelayClient { cancellable?.cancel() onNetworkAcknowledge(nil) } - protectedSend(message) { [weak self] error in + dispatcher.protectedSend(message) { [weak self] error in if let error = error { self?.logger.debug("Failed to Publish Payload, error: \(error)") cancellable?.cancel() @@ -186,7 +179,7 @@ public final class RelayClient { } completion(nil) } - protectedSend(message) { [weak self] error in + dispatcher.protectedSend(message) { [weak self] error in if let error = error { self?.logger.debug("Failed to subscribe to topic \(error)") cancellable?.cancel() @@ -226,7 +219,7 @@ public final class RelayClient { cancellable?.cancel() completion(nil) } - protectedSend(message) { [weak self] error in + dispatcher.protectedSend(message) { [weak self] error in if let error = error { self?.logger.debug("Failed to unsubscribe from topic") cancellable?.cancel() @@ -282,7 +275,7 @@ public final class RelayClient { private func acknowledgeRequest(_ request: RPCRequest) throws { let response = RPCResponse(matchingRequest: request, result: true) let message = try response.asJSONEncodedString() - protectedSend(message) { [unowned self] in + dispatcher.protectedSend(message) { [unowned self] in if let error = $0 { logger.debug("Failed to dispatch response: \(response), error: \(error)") } else { @@ -294,34 +287,4 @@ public final class RelayClient { } } } - - private func protectedSend(_ string: String) async throws { - return try await withCheckedThrowingContinuation { continuation in - protectedSend(string) { error in - if let error = error { - continuation.resume(throwing: error) - } else { - continuation.resume(returning: ()) - } - } - } - } - - private func protectedSend(_ string: String, timeout: Int = 5, completion: @escaping (Error?) -> Void) { - guard socketConnectionStatusPublisherSubject.value == .disconnected else { - return dispatcher.send(string, completion: completion) - } - - var cancellable: AnyCancellable? - cancellable = socketConnectionStatusPublisher.sink { [unowned self] status in - guard status == .connected else { return } - defer { cancellable?.cancel() } - dispatcher.send(string, completion: completion) - } - - concurrentQueue.asyncAfter(deadline: .now() + .seconds(timeout)) { - completion(NetworkError.webSocketNotConnected) - cancellable?.cancel() - } - } } diff --git a/Tests/RelayerTests/DispatcherTests.swift b/Tests/RelayerTests/DispatcherTests.swift index 78103a30c..9d32b380d 100644 --- a/Tests/RelayerTests/DispatcherTests.swift +++ b/Tests/RelayerTests/DispatcherTests.swift @@ -1,5 +1,6 @@ import Foundation import XCTest +import Combine @testable import WalletConnectRelay import TestingUtils import Combine @@ -29,6 +30,7 @@ class WebSocketMock: WebSocketConnecting { } final class DispatcherTests: XCTestCase { + var publishers = Set() var sut: Dispatcher! var webSocket: WebSocketMock! var networkMonitor: NetworkMonitoringMock! @@ -66,18 +68,21 @@ final class DispatcherTests: XCTestCase { func testOnConnect() { let expectation = expectation(description: "on connect") - sut.onConnect = { + sut.socketConnectionStatusPublisher.sink { status in + guard status == .connected else { return } expectation.fulfill() - } + }.store(in: &publishers) webSocket.onConnect?() waitForExpectations(timeout: 0.001) } - func testOnDisconnect() { + func testOnDisconnect() throws { let expectation = expectation(description: "on disconnect") - sut.onDisconnect = { + try sut.connect() + sut.socketConnectionStatusPublisher.sink { status in + guard status == .disconnected else { return } expectation.fulfill() - } + }.store(in: &publishers) webSocket.onDisconnect?(nil) waitForExpectations(timeout: 0.001) } diff --git a/Tests/RelayerTests/Mocks/DispatcherMock.swift b/Tests/RelayerTests/Mocks/DispatcherMock.swift index 97ddac5dc..d5088bf61 100644 --- a/Tests/RelayerTests/Mocks/DispatcherMock.swift +++ b/Tests/RelayerTests/Mocks/DispatcherMock.swift @@ -1,18 +1,34 @@ import Foundation import JSONRPC +import Combine @testable import WalletConnectRelay class DispatcherMock: Dispatching { + private var publishers = Set() + private let socketConnectionStatusPublisherSubject = CurrentValueSubject(.disconnected) + var socketConnectionStatusPublisher: AnyPublisher { + return socketConnectionStatusPublisherSubject.eraseToAnyPublisher() + } - var onConnect: (() -> Void)? - var onDisconnect: (() -> Void)? + var sent = false + var lastMessage: String = "" var onMessage: ((String) -> Void)? - func connect() {} - func disconnect(closeCode: URLSessionWebSocketTask.CloseCode) {} + func protectedSend(_ string: String, completion: @escaping (Error?) -> Void) { + send(string, completion: completion) + } - var sent = false - var lastMessage: String = "" + func protectedSend(_ string: String) async throws { + try await send(string) + } + + func connect() { + socketConnectionStatusPublisherSubject.send(.connected) + } + + func disconnect(closeCode: URLSessionWebSocketTask.CloseCode) { + socketConnectionStatusPublisherSubject.send(.disconnected) + } func send(_ string: String, completion: @escaping (Error?) -> Void) { sent = true From 355e63b32316d755c40029025444939dfb18a552 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Mon, 19 Sep 2022 15:12:10 +0300 Subject: [PATCH 020/175] CurrentValueSubject replaced --- Sources/WalletConnectRelay/Dispatching.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/WalletConnectRelay/Dispatching.swift b/Sources/WalletConnectRelay/Dispatching.swift index 49a112422..6df8a56f4 100644 --- a/Sources/WalletConnectRelay/Dispatching.swift +++ b/Sources/WalletConnectRelay/Dispatching.swift @@ -20,7 +20,7 @@ final class Dispatcher: NSObject, Dispatching { private let logger: ConsoleLogging private let defaultTimeout: Int = 5 - private let socketConnectionStatusPublisherSubject = CurrentValueSubject(.disconnected) + private let socketConnectionStatusPublisherSubject = PassthroughSubject() var socketConnectionStatusPublisher: AnyPublisher { socketConnectionStatusPublisherSubject.eraseToAnyPublisher() @@ -48,14 +48,14 @@ final class Dispatcher: NSObject, Dispatching { } func protectedSend(_ string: String, completion: @escaping (Error?) -> Void) { - guard socketConnectionStatusPublisherSubject.value == .disconnected else { + guard !socket.isConnected else { return send(string, completion: completion) } var cancellable: AnyCancellable? cancellable = socketConnectionStatusPublisher.sink { [unowned self] status in guard status == .connected else { return } - defer { cancellable?.cancel() } + cancellable?.cancel() send(string, completion: completion) } From ffbfc0d7041770b4ad42abfcd4d93934db9f397c Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 19 Sep 2022 15:44:55 +0200 Subject: [PATCH 021/175] pass pairing api integration test --- Example/ExampleApp.xcodeproj/project.pbxproj | 12 +++ .../Pairing/PairingTests.swift | 85 +++++++++++++++++++ .../Push/PushClient.swift | 5 +- .../Push/PushRequestSubscriber.swift | 4 +- 4 files changed, 103 insertions(+), 3 deletions(-) create mode 100644 Example/IntegrationTests/Pairing/PairingTests.swift diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index e23637099..645d75745 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -44,6 +44,7 @@ 84CE644E279ED2FF00142511 /* SelectChainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CE644D279ED2FF00142511 /* SelectChainView.swift */; }; 84CE6452279ED42B00142511 /* ConnectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CE6451279ED42B00142511 /* ConnectView.swift */; }; 84CE645527A29D4D00142511 /* ResponseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CE645427A29D4C00142511 /* ResponseViewController.swift */; }; + 84CEC64628D89D6B00D081A8 /* PairingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CEC64528D89D6B00D081A8 /* PairingTests.swift */; }; 84D2A66628A4F51E0088AE09 /* AuthTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84D2A66528A4F51E0088AE09 /* AuthTests.swift */; }; 84DDB4ED28ABB663003D66ED /* WalletConnectAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 84DDB4EC28ABB663003D66ED /* WalletConnectAuth */; }; 84F568C2279582D200D0A289 /* Signer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F568C1279582D200D0A289 /* Signer.swift */; }; @@ -235,6 +236,7 @@ 84CE6451279ED42B00142511 /* ConnectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectView.swift; sourceTree = ""; }; 84CE6453279FFE1100142511 /* Wallet.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Wallet.entitlements; sourceTree = ""; }; 84CE645427A29D4C00142511 /* ResponseViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResponseViewController.swift; sourceTree = ""; }; + 84CEC64528D89D6B00D081A8 /* PairingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PairingTests.swift; sourceTree = ""; }; 84D2A66528A4F51E0088AE09 /* AuthTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthTests.swift; sourceTree = ""; }; 84F568C1279582D200D0A289 /* Signer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Signer.swift; sourceTree = ""; }; 84F568C32795832A00D0A289 /* EthereumTransaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EthereumTransaction.swift; sourceTree = ""; }; @@ -581,6 +583,14 @@ path = Connect; sourceTree = ""; }; + 84CEC64728D8A98900D081A8 /* Pairing */ = { + isa = PBXGroup; + children = ( + 84CEC64528D89D6B00D081A8 /* PairingTests.swift */, + ); + path = Pairing; + sourceTree = ""; + }; 84D2A66728A4F5260088AE09 /* Auth */ = { isa = PBXGroup; children = ( @@ -1019,6 +1029,7 @@ A5E03DEE286464DB00888481 /* IntegrationTests */ = { isa = PBXGroup; children = ( + 84CEC64728D8A98900D081A8 /* Pairing */, A5E03E0B28646AA500888481 /* Relay */, A5E03E0A28646A8A00888481 /* Stubs */, A5E03E0928646A8100888481 /* Sign */, @@ -1455,6 +1466,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 84CEC64628D89D6B00D081A8 /* PairingTests.swift in Sources */, 767DC83528997F8E00080FA9 /* EthSendTransaction.swift in Sources */, A5E03E03286466F400888481 /* ChatTests.swift in Sources */, 84D2A66628A4F51E0088AE09 /* AuthTests.swift in Sources */, diff --git a/Example/IntegrationTests/Pairing/PairingTests.swift b/Example/IntegrationTests/Pairing/PairingTests.swift new file mode 100644 index 000000000..fb4123393 --- /dev/null +++ b/Example/IntegrationTests/Pairing/PairingTests.swift @@ -0,0 +1,85 @@ +import Foundation +import XCTest +import WalletConnectUtils +@testable import WalletConnectKMS +import WalletConnectRelay +import Combine +import WalletConnectNetworking +import WalletConnectPairing + + +final class PairingTests: XCTestCase { + var appPairingClient: PairingClient! + var walletPairingClient: PairingClient! + + private var publishers = [AnyCancellable]() + + override func setUp() { + appPairingClient = makeClient(prefix: "👻 App") + walletPairingClient = makeClient(prefix: "🤑 Wallet") + + let expectation = expectation(description: "Wait Clients Connected") + expectation.expectedFulfillmentCount = 2 + + appPairingClient.socketConnectionStatusPublisher.sink { status in + if status == .connected { + expectation.fulfill() + } + }.store(in: &publishers) + + walletPairingClient.socketConnectionStatusPublisher.sink { status in + if status == .connected { + expectation.fulfill() + } + }.store(in: &publishers) + + wait(for: [expectation], timeout: 5) + } + + + func makeClient(prefix: String) -> PairingClient { + let logger = ConsoleLogger(suffix: prefix, loggingLevel: .debug) + let projectId = "3ca2919724fbfa5456a25194e369a8b4" + let keychain = KeychainStorageMock() + let relayClient = RelayClient(relayHost: URLConfig.relayHost, projectId: projectId, keychainStorage: keychain, socketFactory: SocketFactory(), logger: logger) + + let pairingClient = PairingClientFactory.create(logger: logger, keyValueStorage: RuntimeKeyValueStorage(), keychainStorage: keychain, relayClient: relayClient) + return pairingClient + } + + func makePushClient(suffix: String) -> PushClient { + let logger = ConsoleLogger(suffix: suffix, loggingLevel: .debug) + let projectId = "3ca2919724fbfa5456a25194e369a8b4" + let keychain = KeychainStorageMock() + let relayClient = RelayClient(relayHost: URLConfig.relayHost, projectId: projectId, keychainStorage: keychain, socketFactory: SocketFactory(), logger: logger) + return PushClientFactory.create(logger: logger, keyValueStorage: RuntimeKeyValueStorage(), keychainStorage: keychain, relayClient: relayClient) + } + + func testProposePushOnPairing() async { + let exp = expectation(description: "") + + let appPushClient = makePushClient(suffix: "👻 App") + let walletPushClient = makePushClient(suffix: "🤑 Wallet") + + walletPushClient.proposalPublisher.sink { _ in + exp.fulfill() + }.store(in: &publishers) + + appPairingClient.configure(with: [appPushClient]) + + walletPairingClient.configure(with: [walletPushClient]) + + let uri = try! await appPairingClient.create() + + try! await walletPairingClient.pair(uri: uri) + + try! await appPushClient.propose(topic: uri.topic) + + + + wait(for: [exp], timeout: 2) + + } + +} + diff --git a/Sources/WalletConnectPairing/Push/PushClient.swift b/Sources/WalletConnectPairing/Push/PushClient.swift index 15164aa79..6bf0fa2da 100644 --- a/Sources/WalletConnectPairing/Push/PushClient.swift +++ b/Sources/WalletConnectPairing/Push/PushClient.swift @@ -4,6 +4,8 @@ import WalletConnectUtils import WalletConnectNetworking import Combine +struct ProposalParams: Codable {} + public class PushClient: Paringable { public var protocolMethod: ProtocolMethod @@ -33,10 +35,11 @@ public class PushClient: Paringable { func handleProposal() { pairingRequestSubscriber.onRequest = { [unowned self] _ in logger.debug("Push: received proposal") + proposalPublisherSubject.send("done") } } public func propose(topic: String) async throws { - try await pairingRequester.request(topic: topic) + try await pairingRequester.request(topic: topic, params: AnyCodable(PushRequestParams())) } } diff --git a/Sources/WalletConnectPairing/Push/PushRequestSubscriber.swift b/Sources/WalletConnectPairing/Push/PushRequestSubscriber.swift index d0f04445f..6d9681e7b 100644 --- a/Sources/WalletConnectPairing/Push/PushRequestSubscriber.swift +++ b/Sources/WalletConnectPairing/Push/PushRequestSubscriber.swift @@ -53,8 +53,8 @@ public class PairingRequester { self.protocolMethod = protocolMethod } - func request(topic: String) async throws { - let request = RPCRequest(method: protocolMethod.method, params: AnyCodable("")) + func request(topic: String, params: AnyCodable) async throws { + let request = RPCRequest(method: protocolMethod.method, params: params) try await networkingInteractor.requestNetworkAck(request, topic: topic, tag: PushProtocolMethod.propose.requestTag) } From c2ba26c6546e28f11c30ebeb1ea320226267e95d Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 20 Sep 2022 09:05:20 +0200 Subject: [PATCH 022/175] refactor pairing --- .../Pairing/PairingTests.swift | 4 +- .../WalletConnectPairing/PairingClient.swift | 15 ++++--- .../PairingClientFactory.swift | 27 +++++++++++ .../Push/PairingClientFactory.swift | 45 ------------------- .../Push/PushClient.swift | 2 - .../Push/PushClientFactory.swift | 22 +++++++++ .../PairingRequestSubscriber.swift} | 28 ------------ .../Common/Paringable/PairingRequester.swift | 30 +++++++++++++ .../Common/Paringable/Paringable.swift | 9 ++++ .../Ping}/PairingPingService.swift | 0 .../{ => Common/Ping}/PingRequester.swift | 0 .../{ => Common/Ping}/PingResponder.swift | 0 .../Ping}/PingResponseSubscriber.swift | 0 .../{ => Types}/PairingProtocolMethod.swift | 0 14 files changed, 100 insertions(+), 82 deletions(-) create mode 100644 Sources/WalletConnectPairing/PairingClientFactory.swift delete mode 100644 Sources/WalletConnectPairing/Push/PairingClientFactory.swift create mode 100644 Sources/WalletConnectPairing/Push/PushClientFactory.swift rename Sources/WalletConnectPairing/{Push/PushRequestSubscriber.swift => Services/Common/Paringable/PairingRequestSubscriber.swift} (50%) create mode 100644 Sources/WalletConnectPairing/Services/Common/Paringable/PairingRequester.swift create mode 100644 Sources/WalletConnectPairing/Services/Common/Paringable/Paringable.swift rename Sources/WalletConnectPairing/Services/{ => Common/Ping}/PairingPingService.swift (100%) rename Sources/WalletConnectPairing/Services/{ => Common/Ping}/PingRequester.swift (100%) rename Sources/WalletConnectPairing/Services/{ => Common/Ping}/PingResponder.swift (100%) rename Sources/WalletConnectPairing/Services/{ => Common/Ping}/PingResponseSubscriber.swift (100%) rename Sources/WalletConnectPairing/{ => Types}/PairingProtocolMethod.swift (100%) diff --git a/Example/IntegrationTests/Pairing/PairingTests.swift b/Example/IntegrationTests/Pairing/PairingTests.swift index fb4123393..3887c7ca8 100644 --- a/Example/IntegrationTests/Pairing/PairingTests.swift +++ b/Example/IntegrationTests/Pairing/PairingTests.swift @@ -65,9 +65,9 @@ final class PairingTests: XCTestCase { exp.fulfill() }.store(in: &publishers) - appPairingClient.configure(with: [appPushClient]) + appPairingClient.configureProtocols(with: [appPushClient]) - walletPairingClient.configure(with: [walletPushClient]) + walletPairingClient.configureProtocols(with: [walletPushClient]) let uri = try! await appPairingClient.create() diff --git a/Sources/WalletConnectPairing/PairingClient.swift b/Sources/WalletConnectPairing/PairingClient.swift index 10a760784..2d3f21e6c 100644 --- a/Sources/WalletConnectPairing/PairingClient.swift +++ b/Sources/WalletConnectPairing/PairingClient.swift @@ -1,6 +1,7 @@ import Foundation import WalletConnectUtils import WalletConnectRelay +import WalletConnectNetworking import Combine public class PairingClient { @@ -8,13 +9,17 @@ public class PairingClient { private let appPairService: AppPairService public let socketConnectionStatusPublisher: AnyPublisher let logger: ConsoleLogging + private let networkingInteractor: NetworkInteracting + init(appPairService: AppPairService, + networkingInteractor: NetworkInteracting, logger: ConsoleLogging, walletPairService: WalletPairService, socketConnectionStatusPublisher: AnyPublisher ) { self.appPairService = appPairService self.walletPairService = walletPairService + self.networkingInteractor = networkingInteractor self.socketConnectionStatusPublisher = socketConnectionStatusPublisher self.logger = logger } @@ -33,13 +38,13 @@ public class PairingClient { return try await appPairService.create() } - public func configure(with paringables: [Paringable]) { - var p = paringables.first! - - p.pairingRequestSubscriber = PairingRequestSubscriber(networkingInteractor: walletPairService.networkingInteractor, logger: logger, kms: walletPairService.kms, protocolMethod: p.protocolMethod) + public func configureProtocols(with paringables: [Paringable]) { + paringables.forEach { + $0.pairingRequestSubscriber = PairingRequestSubscriber(networkingInteractor: walletPairService.networkingInteractor, logger: logger, kms: walletPairService.kms, protocolMethod: $0.protocolMethod) + $0.pairingRequester = PairingRequester(networkingInteractor: walletPairService.networkingInteractor, kms: walletPairService.kms, logger: logger, protocolMethod: $0.protocolMethod) + } - p.pairingRequester = PairingRequester(networkingInteractor: walletPairService.networkingInteractor, kms: walletPairService.kms, logger: logger, protocolMethod: p.protocolMethod) } diff --git a/Sources/WalletConnectPairing/PairingClientFactory.swift b/Sources/WalletConnectPairing/PairingClientFactory.swift new file mode 100644 index 000000000..efccb6579 --- /dev/null +++ b/Sources/WalletConnectPairing/PairingClientFactory.swift @@ -0,0 +1,27 @@ +import Foundation +import WalletConnectRelay +import WalletConnectUtils +import WalletConnectKMS +import WalletConnectNetworking + +public struct PairingClientFactory { + public static func create(logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, relayClient: RelayClient) -> PairingClient { + let kms = KeyManagementService(keychain: keychainStorage) + let serializer = Serializer(kms: kms) + let kv = RuntimeKeyValueStorage() + let historyStorage = CodableStore(defaults: kv, identifier: "") + let history = RPCHistory(keyValueStore: historyStorage) + + + let networkingInteractor = NetworkingInteractor(relayClient: relayClient, serializer: serializer, logger: logger, rpcHistory: history) + let pairingStore = PairingStorage(storage: SequenceStore(store: .init(defaults: kv, identifier: ""))) + + + let appPairService = AppPairService(networkingInteractor: networkingInteractor, kms: kms, pairingStorage: pairingStore) + + let walletPaS = WalletPairService(networkingInteractor: networkingInteractor, kms: kms, pairingStorage: pairingStore) + + return PairingClient(appPairService: appPairService, networkingInteractor: networkingInteractor, logger: logger, walletPairService: walletPaS, socketConnectionStatusPublisher: relayClient.socketConnectionStatusPublisher) + } +} + diff --git a/Sources/WalletConnectPairing/Push/PairingClientFactory.swift b/Sources/WalletConnectPairing/Push/PairingClientFactory.swift deleted file mode 100644 index 8410ee0a3..000000000 --- a/Sources/WalletConnectPairing/Push/PairingClientFactory.swift +++ /dev/null @@ -1,45 +0,0 @@ -import Foundation -import WalletConnectRelay -import WalletConnectUtils -import WalletConnectKMS -import WalletConnectNetworking - -public struct PairingClientFactory { - public static func create(logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, relayClient: RelayClient) -> PairingClient { - let kms = KeyManagementService(keychain: keychainStorage) - let serializer = Serializer(kms: kms) - let kv = RuntimeKeyValueStorage() - let historyStorage = CodableStore(defaults: kv, identifier: "") - let history = RPCHistory(keyValueStore: historyStorage) - - - let networkingInt = NetworkingInteractor(relayClient: relayClient, serializer: serializer, logger: logger, rpcHistory: history) - let pairingStore = PairingStorage(storage: SequenceStore(store: .init(defaults: kv, identifier: ""))) - - - let appPairService = AppPairService(networkingInteractor: networkingInt, kms: kms, pairingStorage: pairingStore) - - let walletPaS = WalletPairService(networkingInteractor: networkingInt, kms: kms, pairingStorage: pairingStore) - - return PairingClient(appPairService: appPairService, logger: logger, walletPairService: walletPaS, socketConnectionStatusPublisher: relayClient.socketConnectionStatusPublisher) - } -} - - - -public struct PushClientFactory { - public static func create(logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, relayClient: RelayClient) -> PushClient { - let kms = KeyManagementService(keychain: keychainStorage) - let serializer = Serializer(kms: kms) - let kv = RuntimeKeyValueStorage() - let historyStorage = CodableStore(defaults: kv, identifier: "") - let history = RPCHistory(keyValueStore: historyStorage) - - - let networkingInt = NetworkingInteractor(relayClient: relayClient, serializer: serializer, logger: logger, rpcHistory: history) - - - - return PushClient(networkingInteractor: networkingInt, logger: logger, kms: kms) - } -} diff --git a/Sources/WalletConnectPairing/Push/PushClient.swift b/Sources/WalletConnectPairing/Push/PushClient.swift index 6bf0fa2da..4f5da3438 100644 --- a/Sources/WalletConnectPairing/Push/PushClient.swift +++ b/Sources/WalletConnectPairing/Push/PushClient.swift @@ -4,8 +4,6 @@ import WalletConnectUtils import WalletConnectNetworking import Combine -struct ProposalParams: Codable {} - public class PushClient: Paringable { public var protocolMethod: ProtocolMethod diff --git a/Sources/WalletConnectPairing/Push/PushClientFactory.swift b/Sources/WalletConnectPairing/Push/PushClientFactory.swift new file mode 100644 index 000000000..0348185ba --- /dev/null +++ b/Sources/WalletConnectPairing/Push/PushClientFactory.swift @@ -0,0 +1,22 @@ +import Foundation +import WalletConnectRelay +import WalletConnectUtils +import WalletConnectKMS +import WalletConnectNetworking + +public struct PushClientFactory { + public static func create(logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, relayClient: RelayClient) -> PushClient { + let kms = KeyManagementService(keychain: keychainStorage) + let serializer = Serializer(kms: kms) + let kv = RuntimeKeyValueStorage() + let historyStorage = CodableStore(defaults: kv, identifier: "") + let history = RPCHistory(keyValueStore: historyStorage) + + + let networkingInt = NetworkingInteractor(relayClient: relayClient, serializer: serializer, logger: logger, rpcHistory: history) + + + + return PushClient(networkingInteractor: networkingInt, logger: logger, kms: kms) + } +} diff --git a/Sources/WalletConnectPairing/Push/PushRequestSubscriber.swift b/Sources/WalletConnectPairing/Services/Common/Paringable/PairingRequestSubscriber.swift similarity index 50% rename from Sources/WalletConnectPairing/Push/PushRequestSubscriber.swift rename to Sources/WalletConnectPairing/Services/Common/Paringable/PairingRequestSubscriber.swift index 6d9681e7b..ad5eac2f7 100644 --- a/Sources/WalletConnectPairing/Push/PushRequestSubscriber.swift +++ b/Sources/WalletConnectPairing/Services/Common/Paringable/PairingRequestSubscriber.swift @@ -5,11 +5,6 @@ import WalletConnectUtils import WalletConnectKMS import WalletConnectNetworking -public protocol Paringable { - var protocolMethod: ProtocolMethod { get set } - var pairingRequestSubscriber: PairingRequestSubscriber! {get set} - var pairingRequester: PairingRequester! {get set} -} public class PairingRequestSubscriber { private let networkingInteractor: NetworkInteracting @@ -36,26 +31,3 @@ public class PairingRequestSubscriber { }.store(in: &publishers) } } - -public class PairingRequester { - private let networkingInteractor: NetworkInteracting - private let kms: KeyManagementServiceProtocol - private let logger: ConsoleLogging - let protocolMethod: ProtocolMethod - - init(networkingInteractor: NetworkInteracting, - kms: KeyManagementServiceProtocol, - logger: ConsoleLogging, - protocolMethod: ProtocolMethod) { - self.networkingInteractor = networkingInteractor - self.kms = kms - self.logger = logger - self.protocolMethod = protocolMethod - } - - func request(topic: String, params: AnyCodable) async throws { - let request = RPCRequest(method: protocolMethod.method, params: params) - - try await networkingInteractor.requestNetworkAck(request, topic: topic, tag: PushProtocolMethod.propose.requestTag) - } -} diff --git a/Sources/WalletConnectPairing/Services/Common/Paringable/PairingRequester.swift b/Sources/WalletConnectPairing/Services/Common/Paringable/PairingRequester.swift new file mode 100644 index 000000000..c92a64d73 --- /dev/null +++ b/Sources/WalletConnectPairing/Services/Common/Paringable/PairingRequester.swift @@ -0,0 +1,30 @@ +import Foundation +import Combine +import JSONRPC +import WalletConnectUtils +import WalletConnectKMS +import WalletConnectNetworking + + +public class PairingRequester { + private let networkingInteractor: NetworkInteracting + private let kms: KeyManagementServiceProtocol + private let logger: ConsoleLogging + let protocolMethod: ProtocolMethod + + init(networkingInteractor: NetworkInteracting, + kms: KeyManagementServiceProtocol, + logger: ConsoleLogging, + protocolMethod: ProtocolMethod) { + self.networkingInteractor = networkingInteractor + self.kms = kms + self.logger = logger + self.protocolMethod = protocolMethod + } + + func request(topic: String, params: AnyCodable) async throws { + let request = RPCRequest(method: protocolMethod.method, params: params) + + try await networkingInteractor.requestNetworkAck(request, topic: topic, tag: PushProtocolMethod.propose.requestTag) + } +} diff --git a/Sources/WalletConnectPairing/Services/Common/Paringable/Paringable.swift b/Sources/WalletConnectPairing/Services/Common/Paringable/Paringable.swift new file mode 100644 index 000000000..c1580e37d --- /dev/null +++ b/Sources/WalletConnectPairing/Services/Common/Paringable/Paringable.swift @@ -0,0 +1,9 @@ + +import Foundation +import WalletConnectNetworking + +public protocol Paringable: AnyObject { + var protocolMethod: ProtocolMethod { get set } + var pairingRequestSubscriber: PairingRequestSubscriber! {get set} + var pairingRequester: PairingRequester! {get set} +} diff --git a/Sources/WalletConnectPairing/Services/PairingPingService.swift b/Sources/WalletConnectPairing/Services/Common/Ping/PairingPingService.swift similarity index 100% rename from Sources/WalletConnectPairing/Services/PairingPingService.swift rename to Sources/WalletConnectPairing/Services/Common/Ping/PairingPingService.swift diff --git a/Sources/WalletConnectPairing/Services/PingRequester.swift b/Sources/WalletConnectPairing/Services/Common/Ping/PingRequester.swift similarity index 100% rename from Sources/WalletConnectPairing/Services/PingRequester.swift rename to Sources/WalletConnectPairing/Services/Common/Ping/PingRequester.swift diff --git a/Sources/WalletConnectPairing/Services/PingResponder.swift b/Sources/WalletConnectPairing/Services/Common/Ping/PingResponder.swift similarity index 100% rename from Sources/WalletConnectPairing/Services/PingResponder.swift rename to Sources/WalletConnectPairing/Services/Common/Ping/PingResponder.swift diff --git a/Sources/WalletConnectPairing/Services/PingResponseSubscriber.swift b/Sources/WalletConnectPairing/Services/Common/Ping/PingResponseSubscriber.swift similarity index 100% rename from Sources/WalletConnectPairing/Services/PingResponseSubscriber.swift rename to Sources/WalletConnectPairing/Services/Common/Ping/PingResponseSubscriber.swift diff --git a/Sources/WalletConnectPairing/PairingProtocolMethod.swift b/Sources/WalletConnectPairing/Types/PairingProtocolMethod.swift similarity index 100% rename from Sources/WalletConnectPairing/PairingProtocolMethod.swift rename to Sources/WalletConnectPairing/Types/PairingProtocolMethod.swift From f50a8162bd5c873718ed321f510ff807d54735de Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 20 Sep 2022 10:17:39 +0200 Subject: [PATCH 023/175] update pairingable protocol --- .../NetworkInteracting.swift | 4 ++ .../NetworkInteractor.swift | 4 ++ .../WalletConnectPairing/PairingClient.swift | 69 +++++++++++++++++-- .../PairingRequester.swift | 2 +- .../Push/PushClient.swift | 2 +- .../Paringable/PairingRequestSubscriber.swift | 33 --------- .../Common/Paringable/Pairingable.swift | 10 +++ .../Common/Paringable/Paringable.swift | 9 --- 8 files changed, 83 insertions(+), 50 deletions(-) rename Sources/WalletConnectPairing/{Services/Common/Paringable => Push}/PairingRequester.swift (96%) delete mode 100644 Sources/WalletConnectPairing/Services/Common/Paringable/PairingRequestSubscriber.swift create mode 100644 Sources/WalletConnectPairing/Services/Common/Paringable/Pairingable.swift delete mode 100644 Sources/WalletConnectPairing/Services/Common/Paringable/Paringable.swift diff --git a/Sources/WalletConnectNetworking/NetworkInteracting.swift b/Sources/WalletConnectNetworking/NetworkInteracting.swift index 85ca9177e..9e033cc3c 100644 --- a/Sources/WalletConnectNetworking/NetworkInteracting.swift +++ b/Sources/WalletConnectNetworking/NetworkInteracting.swift @@ -18,6 +18,10 @@ public protocol NetworkInteracting { on request: ProtocolMethod ) -> AnyPublisher, Never> + func requestSubscription( + on requests: [ProtocolMethod] + ) -> AnyPublisher, Never> + func responseSubscription( on request: ProtocolMethod ) -> AnyPublisher, Never> diff --git a/Sources/WalletConnectNetworking/NetworkInteractor.swift b/Sources/WalletConnectNetworking/NetworkInteractor.swift index 111d0003f..42b3e71d9 100644 --- a/Sources/WalletConnectNetworking/NetworkInteractor.swift +++ b/Sources/WalletConnectNetworking/NetworkInteractor.swift @@ -68,6 +68,10 @@ public class NetworkingInteractor: NetworkInteracting { .eraseToAnyPublisher() } + public func requestSubscription(on requests: [ProtocolMethod]) -> AnyPublisher, Never> where Request : Decodable, Request : Encodable { + <#code#> + } + public func responseSubscription(on request: ProtocolMethod) -> AnyPublisher, Never> { return responsePublisher .filter { rpcRequest in diff --git a/Sources/WalletConnectPairing/PairingClient.swift b/Sources/WalletConnectPairing/PairingClient.swift index 2d3f21e6c..3bc9c5420 100644 --- a/Sources/WalletConnectPairing/PairingClient.swift +++ b/Sources/WalletConnectPairing/PairingClient.swift @@ -38,15 +38,72 @@ public class PairingClient { return try await appPairService.create() } - public func configureProtocols(with paringables: [Paringable]) { - paringables.forEach { - $0.pairingRequestSubscriber = PairingRequestSubscriber(networkingInteractor: walletPairService.networkingInteractor, logger: logger, kms: walletPairService.kms, protocolMethod: $0.protocolMethod) + public func configureProtocols(with paringables: [Pairingable]) { - $0.pairingRequester = PairingRequester(networkingInteractor: walletPairService.networkingInteractor, kms: walletPairService.kms, logger: logger, protocolMethod: $0.protocolMethod) - } + } +} + +import Foundation +import Combine +import JSONRPC +import WalletConnectUtils +import WalletConnectKMS +import WalletConnectNetworking + + +public class PairingRequestsSubscriber { + private let networkingInteractor: NetworkInteracting + private let kms: KeyManagementServiceProtocol + private var publishers = [AnyCancellable]() + var onRequest: ((RequestSubscriptionPayload) -> Void)? + let protocolMethod: ProtocolMethod + var pairingables = [Pairingable]() + init(networkingInteractor: NetworkInteracting, + logger: ConsoleLogging, + kms: KeyManagementServiceProtocol, + protocolMethod: ProtocolMethod) { + self.networkingInteractor = networkingInteractor + self.kms = kms + self.protocolMethod = protocolMethod } - + func setPairingables(_ pairingables: [Pairingable]) { + self.pairingables = pairingables + let methods = pairingables.map{$0.protocolMethod} + subscribeForRequests(methods: methods) + + } + + private func subscribeForRequests(methods: [ProtocolMethod]) { + // TODO - spec tag + let tag = 123456 + networkingInteractor.requestSubscription(on: methods) + .sink { [unowned self] (payload: RequestSubscriptionPayload) in + guard let pairingable = pairingables + .first(where: { p in + p.protocolMethod.method == payload.request.method + }) else { + Task { try await networkingInteractor.respondError(topic: payload.topic, requestId: payload.id, tag: tag, reason: PairError.methodUnsupported) } + return + } + pairingable.requestPublisherSubject.send((topic: payload.topic, request: payload.request)) + + }.store(in: &publishers) + } + +} +public enum PairError: Codable, Equatable, Error, Reason { + case methodUnsupported + + public var code: Int { + //TODO - spec code + return 44444 + } + + //TODO - spec message + public var message: String { + return "Method Unsupported" + } } diff --git a/Sources/WalletConnectPairing/Services/Common/Paringable/PairingRequester.swift b/Sources/WalletConnectPairing/Push/PairingRequester.swift similarity index 96% rename from Sources/WalletConnectPairing/Services/Common/Paringable/PairingRequester.swift rename to Sources/WalletConnectPairing/Push/PairingRequester.swift index c92a64d73..8c273fb9c 100644 --- a/Sources/WalletConnectPairing/Services/Common/Paringable/PairingRequester.swift +++ b/Sources/WalletConnectPairing/Push/PairingRequester.swift @@ -6,7 +6,7 @@ import WalletConnectKMS import WalletConnectNetworking -public class PairingRequester { +public class PushRequester { private let networkingInteractor: NetworkInteracting private let kms: KeyManagementServiceProtocol private let logger: ConsoleLogging diff --git a/Sources/WalletConnectPairing/Push/PushClient.swift b/Sources/WalletConnectPairing/Push/PushClient.swift index 4f5da3438..a5fb74f06 100644 --- a/Sources/WalletConnectPairing/Push/PushClient.swift +++ b/Sources/WalletConnectPairing/Push/PushClient.swift @@ -4,7 +4,7 @@ import WalletConnectUtils import WalletConnectNetworking import Combine -public class PushClient: Paringable { +public class PushClient: Pairingable { public var protocolMethod: ProtocolMethod public var proposalPublisher: AnyPublisher { diff --git a/Sources/WalletConnectPairing/Services/Common/Paringable/PairingRequestSubscriber.swift b/Sources/WalletConnectPairing/Services/Common/Paringable/PairingRequestSubscriber.swift deleted file mode 100644 index ad5eac2f7..000000000 --- a/Sources/WalletConnectPairing/Services/Common/Paringable/PairingRequestSubscriber.swift +++ /dev/null @@ -1,33 +0,0 @@ -import Foundation -import Combine -import JSONRPC -import WalletConnectUtils -import WalletConnectKMS -import WalletConnectNetworking - - -public class PairingRequestSubscriber { - private let networkingInteractor: NetworkInteracting - private let kms: KeyManagementServiceProtocol - private var publishers = [AnyCancellable]() - var onRequest: ((RequestSubscriptionPayload) -> Void)? - let protocolMethod: ProtocolMethod - - init(networkingInteractor: NetworkInteracting, - logger: ConsoleLogging, - kms: KeyManagementServiceProtocol, - protocolMethod: ProtocolMethod) { - self.networkingInteractor = networkingInteractor - self.kms = kms - self.protocolMethod = protocolMethod - subscribeForRequest() - } - - func subscribeForRequest() { - - networkingInteractor.requestSubscription(on: protocolMethod) - .sink { [unowned self] (payload: RequestSubscriptionPayload) in - onRequest?(payload) - }.store(in: &publishers) - } -} diff --git a/Sources/WalletConnectPairing/Services/Common/Paringable/Pairingable.swift b/Sources/WalletConnectPairing/Services/Common/Paringable/Pairingable.swift new file mode 100644 index 000000000..858cf90e3 --- /dev/null +++ b/Sources/WalletConnectPairing/Services/Common/Paringable/Pairingable.swift @@ -0,0 +1,10 @@ + +import Foundation +import Combine +import WalletConnectNetworking +import JSONRPC + +public protocol Pairingable: AnyObject { + var protocolMethod: ProtocolMethod { get set } + var requestPublisherSubject: PassthroughSubject<(topic: String, request: RPCRequest), Never> {get} +} diff --git a/Sources/WalletConnectPairing/Services/Common/Paringable/Paringable.swift b/Sources/WalletConnectPairing/Services/Common/Paringable/Paringable.swift deleted file mode 100644 index c1580e37d..000000000 --- a/Sources/WalletConnectPairing/Services/Common/Paringable/Paringable.swift +++ /dev/null @@ -1,9 +0,0 @@ - -import Foundation -import WalletConnectNetworking - -public protocol Paringable: AnyObject { - var protocolMethod: ProtocolMethod { get set } - var pairingRequestSubscriber: PairingRequestSubscriber! {get set} - var pairingRequester: PairingRequester! {get set} -} From 70b7cda6523aae3cd5e03eb9119ec768f3e1124d Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 20 Sep 2022 10:40:55 +0200 Subject: [PATCH 024/175] add requestSubscription on requests to networking --- .../NetworkInteracting.swift | 4 +--- .../NetworkInteractor.swift | 14 ++++++++++---- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/Sources/WalletConnectNetworking/NetworkInteracting.swift b/Sources/WalletConnectNetworking/NetworkInteracting.swift index 9e033cc3c..e0bc1cb3e 100644 --- a/Sources/WalletConnectNetworking/NetworkInteracting.swift +++ b/Sources/WalletConnectNetworking/NetworkInteracting.swift @@ -18,9 +18,7 @@ public protocol NetworkInteracting { on request: ProtocolMethod ) -> AnyPublisher, Never> - func requestSubscription( - on requests: [ProtocolMethod] - ) -> AnyPublisher, Never> + func requestSubscription(on requests: [ProtocolMethod]) -> AnyPublisher<(topic: String, request: RPCRequest), Never> func responseSubscription( on request: ProtocolMethod diff --git a/Sources/WalletConnectNetworking/NetworkInteractor.swift b/Sources/WalletConnectNetworking/NetworkInteractor.swift index 42b3e71d9..b7151eeb2 100644 --- a/Sources/WalletConnectNetworking/NetworkInteractor.swift +++ b/Sources/WalletConnectNetworking/NetworkInteractor.swift @@ -56,20 +56,26 @@ public class NetworkingInteractor: NetworkInteracting { } } - public func requestSubscription(on request: ProtocolMethod) -> AnyPublisher, Never> { + public func requestSubscription(on request: ProtocolMethod) -> AnyPublisher, Never> { return requestPublisher .filter { rpcRequest in return rpcRequest.request.method == request.method } .compactMap { topic, rpcRequest in - guard let id = rpcRequest.id, let request = try? rpcRequest.params?.get(Request.self) else { return nil } + guard let id = rpcRequest.id, let request = try? rpcRequest.params?.get(RequestParams.self) else { return nil } return RequestSubscriptionPayload(id: id, topic: topic, request: request) } .eraseToAnyPublisher() } - public func requestSubscription(on requests: [ProtocolMethod]) -> AnyPublisher, Never> where Request : Decodable, Request : Encodable { - <#code#> + public func requestSubscription(on requests: [ProtocolMethod]) -> AnyPublisher<(topic: String, request: RPCRequest), Never> { + return requestPublisher + .filter { rpcRequest in + return requests.contains { protocolMethod in + protocolMethod.method == rpcRequest.request.method + } + } + .eraseToAnyPublisher() } public func responseSubscription(on request: ProtocolMethod) -> AnyPublisher, Never> { From b8ca28b6509d2c7f4b5f3170fd0346466091eea8 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 20 Sep 2022 11:07:00 +0200 Subject: [PATCH 025/175] savepoint --- .../WalletConnectPairing/PairingClient.swift | 69 ++----------------- .../PairingClientFactory.swift | 4 +- .../Common/Paringable => }/Pairingable.swift | 0 .../Push/PairingRequester.swift | 2 +- .../Push/PushClient.swift | 31 +++------ .../Push/PushClientFactory.swift | 8 +-- .../Common/PairingRequestsSubscriber.swift | 48 +++++++++++++ .../Types/PairError.swift | 16 +++++ 8 files changed, 87 insertions(+), 91 deletions(-) rename Sources/WalletConnectPairing/{Services/Common/Paringable => }/Pairingable.swift (100%) create mode 100644 Sources/WalletConnectPairing/Services/Common/PairingRequestsSubscriber.swift create mode 100644 Sources/WalletConnectPairing/Types/PairError.swift diff --git a/Sources/WalletConnectPairing/PairingClient.swift b/Sources/WalletConnectPairing/PairingClient.swift index 3bc9c5420..6fbcecd13 100644 --- a/Sources/WalletConnectPairing/PairingClient.swift +++ b/Sources/WalletConnectPairing/PairingClient.swift @@ -10,11 +10,13 @@ public class PairingClient { public let socketConnectionStatusPublisher: AnyPublisher let logger: ConsoleLogging private let networkingInteractor: NetworkInteracting + private let pairingRequestsSubscriber: PairingRequestsSubscriber init(appPairService: AppPairService, networkingInteractor: NetworkInteracting, logger: ConsoleLogging, walletPairService: WalletPairService, + pairingRequestsSubscriber: PairingRequestsSubscriber, socketConnectionStatusPublisher: AnyPublisher ) { self.appPairService = appPairService @@ -22,6 +24,7 @@ public class PairingClient { self.networkingInteractor = networkingInteractor self.socketConnectionStatusPublisher = socketConnectionStatusPublisher self.logger = logger + self.pairingRequestsSubscriber = pairingRequestsSubscriber } /// For wallet to establish a pairing and receive an authentication request /// Wallet should call this function in order to accept peer's pairing proposal and be able to subscribe for future authentication request. @@ -39,71 +42,7 @@ public class PairingClient { } public func configureProtocols(with paringables: [Pairingable]) { - - } -} - -import Foundation -import Combine -import JSONRPC -import WalletConnectUtils -import WalletConnectKMS -import WalletConnectNetworking - - -public class PairingRequestsSubscriber { - private let networkingInteractor: NetworkInteracting - private let kms: KeyManagementServiceProtocol - private var publishers = [AnyCancellable]() - var onRequest: ((RequestSubscriptionPayload) -> Void)? - let protocolMethod: ProtocolMethod - var pairingables = [Pairingable]() - - init(networkingInteractor: NetworkInteracting, - logger: ConsoleLogging, - kms: KeyManagementServiceProtocol, - protocolMethod: ProtocolMethod) { - self.networkingInteractor = networkingInteractor - self.kms = kms - self.protocolMethod = protocolMethod - } - - func setPairingables(_ pairingables: [Pairingable]) { - self.pairingables = pairingables - let methods = pairingables.map{$0.protocolMethod} - subscribeForRequests(methods: methods) - - } - - private func subscribeForRequests(methods: [ProtocolMethod]) { - // TODO - spec tag - let tag = 123456 - networkingInteractor.requestSubscription(on: methods) - .sink { [unowned self] (payload: RequestSubscriptionPayload) in - guard let pairingable = pairingables - .first(where: { p in - p.protocolMethod.method == payload.request.method - }) else { - Task { try await networkingInteractor.respondError(topic: payload.topic, requestId: payload.id, tag: tag, reason: PairError.methodUnsupported) } - return - } - pairingable.requestPublisherSubject.send((topic: payload.topic, request: payload.request)) - - }.store(in: &publishers) + pairingRequestsSubscriber.setPairingables(paringables) } - } -public enum PairError: Codable, Equatable, Error, Reason { - case methodUnsupported - - public var code: Int { - //TODO - spec code - return 44444 - } - //TODO - spec message - public var message: String { - return "Method Unsupported" - } - -} diff --git a/Sources/WalletConnectPairing/PairingClientFactory.swift b/Sources/WalletConnectPairing/PairingClientFactory.swift index efccb6579..1b1647ca7 100644 --- a/Sources/WalletConnectPairing/PairingClientFactory.swift +++ b/Sources/WalletConnectPairing/PairingClientFactory.swift @@ -21,7 +21,9 @@ public struct PairingClientFactory { let walletPaS = WalletPairService(networkingInteractor: networkingInteractor, kms: kms, pairingStorage: pairingStore) - return PairingClient(appPairService: appPairService, networkingInteractor: networkingInteractor, logger: logger, walletPairService: walletPaS, socketConnectionStatusPublisher: relayClient.socketConnectionStatusPublisher) + let pairingRequestsSubscriber = PairingRequestsSubscriber(networkingInteractor: networkingInteractor, logger: logger, kms: kms) + + return PairingClient(appPairService: appPairService, networkingInteractor: networkingInteractor, logger: logger, walletPairService: walletPaS, pairingRequestsSubscriber: pairingRequestsSubscriber, socketConnectionStatusPublisher: relayClient.socketConnectionStatusPublisher) } } diff --git a/Sources/WalletConnectPairing/Services/Common/Paringable/Pairingable.swift b/Sources/WalletConnectPairing/Pairingable.swift similarity index 100% rename from Sources/WalletConnectPairing/Services/Common/Paringable/Pairingable.swift rename to Sources/WalletConnectPairing/Pairingable.swift diff --git a/Sources/WalletConnectPairing/Push/PairingRequester.swift b/Sources/WalletConnectPairing/Push/PairingRequester.swift index 8c273fb9c..ba584d443 100644 --- a/Sources/WalletConnectPairing/Push/PairingRequester.swift +++ b/Sources/WalletConnectPairing/Push/PairingRequester.swift @@ -6,7 +6,7 @@ import WalletConnectKMS import WalletConnectNetworking -public class PushRequester { +public class PushProposer { private let networkingInteractor: NetworkInteracting private let kms: KeyManagementServiceProtocol private let logger: ConsoleLogging diff --git a/Sources/WalletConnectPairing/Push/PushClient.swift b/Sources/WalletConnectPairing/Push/PushClient.swift index a5fb74f06..80b2645d6 100644 --- a/Sources/WalletConnectPairing/Push/PushClient.swift +++ b/Sources/WalletConnectPairing/Push/PushClient.swift @@ -2,42 +2,33 @@ import Foundation import WalletConnectKMS import WalletConnectUtils import WalletConnectNetworking +import JSONRPC import Combine public class PushClient: Pairingable { + public var requestPublisherSubject = PassthroughSubject<(topic: String, request: RPCRequest), Never>() public var protocolMethod: ProtocolMethod - public var proposalPublisher: AnyPublisher { - proposalPublisherSubject.eraseToAnyPublisher() - } - private let proposalPublisherSubject = PassthroughSubject() - public var pairingRequestSubscriber: PairingRequestSubscriber! { - didSet { - handleProposal() - } + public var proposalPublisher: AnyPublisher<(topic: String, request: RPCRequest), Never> { + requestPublisherSubject.eraseToAnyPublisher() } - public var pairingRequester: PairingRequester! + private let pushProposer: PushProposer public let logger: ConsoleLogging init(networkingInteractor: NetworkInteracting, logger: ConsoleLogging, - kms: KeyManagementServiceProtocol) { + kms: KeyManagementServiceProtocol, + protocolMethod: ProtocolMethod, + pushProposer: PushProposer) { self.logger = logger - - protocolMethod = PushProtocolMethod.propose - } - - func handleProposal() { - pairingRequestSubscriber.onRequest = { [unowned self] _ in - logger.debug("Push: received proposal") - proposalPublisherSubject.send("done") - } + self.protocolMethod = protocolMethod + self.pushProposer = pushProposer } public func propose(topic: String) async throws { - try await pairingRequester.request(topic: topic, params: AnyCodable(PushRequestParams())) + try await pushProposer.request(topic: topic, params: AnyCodable(PushRequestParams())) } } diff --git a/Sources/WalletConnectPairing/Push/PushClientFactory.swift b/Sources/WalletConnectPairing/Push/PushClientFactory.swift index 0348185ba..63d420e24 100644 --- a/Sources/WalletConnectPairing/Push/PushClientFactory.swift +++ b/Sources/WalletConnectPairing/Push/PushClientFactory.swift @@ -13,10 +13,10 @@ public struct PushClientFactory { let history = RPCHistory(keyValueStore: historyStorage) - let networkingInt = NetworkingInteractor(relayClient: relayClient, serializer: serializer, logger: logger, rpcHistory: history) + let networkingInteractor = NetworkingInteractor(relayClient: relayClient, serializer: serializer, logger: logger, rpcHistory: history) - - - return PushClient(networkingInteractor: networkingInt, logger: logger, kms: kms) + let protocolMethod = PushProtocolMethod.propose + let pushProposer = PushProposer(networkingInteractor: networkingInteractor, kms: kms, logger: logger, protocolMethod: protocolMethod) + return PushClient(networkingInteractor: networkingInteractor, logger: logger, kms: kms, protocolMethod: protocolMethod, pushProposer: pushProposer) } } diff --git a/Sources/WalletConnectPairing/Services/Common/PairingRequestsSubscriber.swift b/Sources/WalletConnectPairing/Services/Common/PairingRequestsSubscriber.swift new file mode 100644 index 000000000..05947f8d7 --- /dev/null +++ b/Sources/WalletConnectPairing/Services/Common/PairingRequestsSubscriber.swift @@ -0,0 +1,48 @@ + +import Foundation +import Combine +import JSONRPC +import WalletConnectUtils +import WalletConnectKMS +import WalletConnectNetworking + + +public class PairingRequestsSubscriber { + private let networkingInteractor: NetworkInteracting + private let kms: KeyManagementServiceProtocol + private var publishers = [AnyCancellable]() + var onRequest: ((RequestSubscriptionPayload) -> Void)? + var pairingables = [Pairingable]() + + init(networkingInteractor: NetworkInteracting, + logger: ConsoleLogging, + kms: KeyManagementServiceProtocol) { + self.networkingInteractor = networkingInteractor + self.kms = kms + } + + func setPairingables(_ pairingables: [Pairingable]) { + self.pairingables = pairingables + let methods = pairingables.map{$0.protocolMethod} + subscribeForRequests(methods: methods) + + } + + private func subscribeForRequests(methods: [ProtocolMethod]) { + // TODO - spec tag + let tag = 123456 + networkingInteractor.requestSubscription(on: methods) + .sink { [unowned self] topic, request in + guard let pairingable = pairingables + .first(where: { p in + p.protocolMethod.method == request.method + }) else { + Task { try await networkingInteractor.respondError(topic: topic, requestId: request.id!, tag: tag, reason: PairError.methodUnsupported) } + return + } + pairingable.requestPublisherSubject.send((topic: topic, request: request)) + + }.store(in: &publishers) + } + +} diff --git a/Sources/WalletConnectPairing/Types/PairError.swift b/Sources/WalletConnectPairing/Types/PairError.swift new file mode 100644 index 000000000..99e93f336 --- /dev/null +++ b/Sources/WalletConnectPairing/Types/PairError.swift @@ -0,0 +1,16 @@ +import WalletConnectNetworking + +public enum PairError: Codable, Equatable, Error, Reason { + case methodUnsupported + + public var code: Int { + //TODO - spec code + return 44444 + } + + //TODO - spec message + public var message: String { + return "Method Unsupported" + } + +} From 2901a006e1b40f077a7a6fe60b0f28d4df5a655a Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 20 Sep 2022 13:03:35 +0200 Subject: [PATCH 026/175] savepoint --- .../Pairing/PairingTests.swift | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/Example/IntegrationTests/Pairing/PairingTests.swift b/Example/IntegrationTests/Pairing/PairingTests.swift index 3887c7ca8..578e6a7cd 100644 --- a/Example/IntegrationTests/Pairing/PairingTests.swift +++ b/Example/IntegrationTests/Pairing/PairingTests.swift @@ -15,8 +15,8 @@ final class PairingTests: XCTestCase { private var publishers = [AnyCancellable]() override func setUp() { - appPairingClient = makeClient(prefix: "👻 App") - walletPairingClient = makeClient(prefix: "🤑 Wallet") + appPairingClient = makeClient(prefix: "👻 App", keychain: appKeychain) + walletPairingClient = makeClient(prefix: "🤑 Wallet", keychain: walletKeychain) let expectation = expectation(description: "Wait Clients Connected") expectation.expectedFulfillmentCount = 2 @@ -36,21 +36,21 @@ final class PairingTests: XCTestCase { wait(for: [expectation], timeout: 5) } - - func makeClient(prefix: String) -> PairingClient { + func makeClient(prefix: String, keychain: KeychainStorageMock) -> PairingClient { let logger = ConsoleLogger(suffix: prefix, loggingLevel: .debug) let projectId = "3ca2919724fbfa5456a25194e369a8b4" - let keychain = KeychainStorageMock() let relayClient = RelayClient(relayHost: URLConfig.relayHost, projectId: projectId, keychainStorage: keychain, socketFactory: SocketFactory(), logger: logger) let pairingClient = PairingClientFactory.create(logger: logger, keyValueStorage: RuntimeKeyValueStorage(), keychainStorage: keychain, relayClient: relayClient) return pairingClient } - func makePushClient(suffix: String) -> PushClient { + let appKeychain = KeychainStorageMock() + let walletKeychain = KeychainStorageMock() + + func makePushClient(suffix: String, keychain: KeychainStorageMock) -> PushClient { let logger = ConsoleLogger(suffix: suffix, loggingLevel: .debug) let projectId = "3ca2919724fbfa5456a25194e369a8b4" - let keychain = KeychainStorageMock() let relayClient = RelayClient(relayHost: URLConfig.relayHost, projectId: projectId, keychainStorage: keychain, socketFactory: SocketFactory(), logger: logger) return PushClientFactory.create(logger: logger, keyValueStorage: RuntimeKeyValueStorage(), keychainStorage: keychain, relayClient: relayClient) } @@ -58,8 +58,8 @@ final class PairingTests: XCTestCase { func testProposePushOnPairing() async { let exp = expectation(description: "") - let appPushClient = makePushClient(suffix: "👻 App") - let walletPushClient = makePushClient(suffix: "🤑 Wallet") + let appPushClient = makePushClient(suffix: "👻 App", keychain: appKeychain) + let walletPushClient = makePushClient(suffix: "🤑 Wallet", keychain: walletKeychain) walletPushClient.proposalPublisher.sink { _ in exp.fulfill() @@ -75,8 +75,6 @@ final class PairingTests: XCTestCase { try! await appPushClient.propose(topic: uri.topic) - - wait(for: [exp], timeout: 2) } From 36f52c5461124e7559df2fac7717bf7918ca0a0b Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Tue, 20 Sep 2022 16:11:49 +0300 Subject: [PATCH 027/175] RPCHistory factory --- Sources/Auth/AuthClientFactory.swift | 3 +-- Sources/Chat/ChatClientFactory.swift | 2 +- Sources/WalletConnectRelay/RelayClient.swift | 4 +--- .../{ => RPCHistory}/RPCHistory.swift | 2 +- .../RPCHistory/RPCHistoryFactory.swift | 20 +++++++++++++++++++ .../AuthTests/AppRespondSubscriberTests.swift | 4 ++-- .../WalletRequestSubscriberTests.swift | 4 ++-- 7 files changed, 28 insertions(+), 11 deletions(-) rename Sources/WalletConnectUtils/{ => RPCHistory}/RPCHistory.swift (97%) create mode 100644 Sources/WalletConnectUtils/RPCHistory/RPCHistoryFactory.swift diff --git a/Sources/Auth/AuthClientFactory.swift b/Sources/Auth/AuthClientFactory.swift index e0fe3f586..6d7c8926a 100644 --- a/Sources/Auth/AuthClientFactory.swift +++ b/Sources/Auth/AuthClientFactory.swift @@ -15,11 +15,10 @@ public struct AuthClientFactory { } static func create(metadata: AppMetadata, account: Account?, logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, relayClient: RelayClient) -> AuthClient { - let historyStorage = CodableStore(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.jsonRpcHistory.rawValue) let pairingStore = PairingStorage(storage: SequenceStore(store: .init(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.pairings.rawValue))) let kms = KeyManagementService(keychain: keychainStorage) let serializer = Serializer(kms: kms) - let history = RPCHistory(keyValueStore: historyStorage) + let history = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage) let networkingInteractor = NetworkingInteractor(relayClient: relayClient, serializer: serializer, logger: logger, rpcHistory: history) let messageFormatter = SIWEMessageFormatter() let appPairService = AppPairService(networkingInteractor: networkingInteractor, kms: kms, pairingStorage: pairingStore) diff --git a/Sources/Chat/ChatClientFactory.swift b/Sources/Chat/ChatClientFactory.swift index 9918dbc70..b5af0d7b2 100644 --- a/Sources/Chat/ChatClientFactory.swift +++ b/Sources/Chat/ChatClientFactory.swift @@ -15,7 +15,7 @@ public struct ChatClientFactory { ) -> ChatClient { let topicToRegistryRecordStore = CodableStore(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.topicToInvitationPubKey.rawValue) let serialiser = Serializer(kms: kms) - let rpcHistory = RPCHistory(keyValueStore: CodableStore(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.jsonRpcHistory.rawValue)) + let rpcHistory = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage) let networkingInteractor = NetworkingInteractor(relayClient: relayClient, serializer: serialiser, logger: logger, rpcHistory: rpcHistory) let invitePayloadStore = CodableStore>(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.invite.rawValue) let registryService = RegistryService(registry: registry, networkingInteractor: networkingInteractor, kms: kms, logger: logger, topicToRegistryRecordStore: topicToRegistryRecordStore) diff --git a/Sources/WalletConnectRelay/RelayClient.swift b/Sources/WalletConnectRelay/RelayClient.swift index f68ad459e..8ac2c7ea0 100644 --- a/Sources/WalletConnectRelay/RelayClient.swift +++ b/Sources/WalletConnectRelay/RelayClient.swift @@ -20,8 +20,6 @@ public final class RelayClient { case subscriptionIdNotFound } - static let historyIdentifier = "com.walletconnect.sdk.relayer_client.subscription_json_rpc_record" - let defaultTtl = 6*Time.hour var subscriptions: [String: String] = [:] @@ -59,7 +57,7 @@ public final class RelayClient { ) { self.logger = logger self.dispatcher = dispatcher - self.rpcHistory = RPCHistory(keyValueStore: CodableStore(defaults: keyValueStorage, identifier: Self.historyIdentifier)) + self.rpcHistory = RPCHistoryFactory.createForRelay(keyValueStorage: keyValueStorage) setUpBindings() } diff --git a/Sources/WalletConnectUtils/RPCHistory.swift b/Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift similarity index 97% rename from Sources/WalletConnectUtils/RPCHistory.swift rename to Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift index 255801b24..c7f382c1b 100644 --- a/Sources/WalletConnectUtils/RPCHistory.swift +++ b/Sources/WalletConnectUtils/RPCHistory/RPCHistory.swift @@ -24,7 +24,7 @@ public final class RPCHistory { private let storage: CodableStore - public init(keyValueStore: CodableStore) { + init(keyValueStore: CodableStore) { self.storage = keyValueStore } diff --git a/Sources/WalletConnectUtils/RPCHistory/RPCHistoryFactory.swift b/Sources/WalletConnectUtils/RPCHistory/RPCHistoryFactory.swift new file mode 100644 index 000000000..817ca2d17 --- /dev/null +++ b/Sources/WalletConnectUtils/RPCHistory/RPCHistoryFactory.swift @@ -0,0 +1,20 @@ +import Foundation + +public struct RPCHistoryFactory { + + private static let networkIdentifier = "com.walletconnect.sdk.wc_jsonRpcHistoryRecord" + private static let relayIdentifier = "com.walletconnect.sdk.relayer_client.subscription_json_rpc_record" + + public static func createForNetwork(keyValueStorage: KeyValueStorage) -> RPCHistory { + return RPCHistoryFactory.create(keyValueStorage: keyValueStorage, identifier: RPCHistoryFactory.networkIdentifier) + } + + public static func createForRelay(keyValueStorage: KeyValueStorage) -> RPCHistory { + return RPCHistoryFactory.create(keyValueStorage: keyValueStorage, identifier: RPCHistoryFactory.relayIdentifier) + } + + static func create(keyValueStorage: KeyValueStorage, identifier: String) -> RPCHistory { + let keyValueStore = CodableStore(defaults: keyValueStorage, identifier: identifier) + return RPCHistory(keyValueStore: keyValueStore) + } +} diff --git a/Tests/AuthTests/AppRespondSubscriberTests.swift b/Tests/AuthTests/AppRespondSubscriberTests.swift index 98b8f47dd..4043fbab5 100644 --- a/Tests/AuthTests/AppRespondSubscriberTests.swift +++ b/Tests/AuthTests/AppRespondSubscriberTests.swift @@ -1,8 +1,8 @@ import Foundation import XCTest @testable import Auth -import WalletConnectUtils -import WalletConnectNetworking +@testable import WalletConnectUtils +@testable import WalletConnectNetworking @testable import WalletConnectKMS @testable import TestingUtils import JSONRPC diff --git a/Tests/AuthTests/WalletRequestSubscriberTests.swift b/Tests/AuthTests/WalletRequestSubscriberTests.swift index 0fb214242..5f30d166e 100644 --- a/Tests/AuthTests/WalletRequestSubscriberTests.swift +++ b/Tests/AuthTests/WalletRequestSubscriberTests.swift @@ -1,8 +1,8 @@ import Foundation import XCTest import JSONRPC -import WalletConnectUtils -import WalletConnectNetworking +@testable import WalletConnectUtils +@testable import WalletConnectNetworking @testable import Auth @testable import WalletConnectKMS @testable import TestingUtils From 1bc9ce3f47697d5031044a0bf3db5d77b2e68a3f Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 20 Sep 2022 15:17:17 +0200 Subject: [PATCH 028/175] savepoint --- Example/IntegrationTests/Pairing/PairingTests.swift | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Example/IntegrationTests/Pairing/PairingTests.swift b/Example/IntegrationTests/Pairing/PairingTests.swift index 578e6a7cd..229323460 100644 --- a/Example/IntegrationTests/Pairing/PairingTests.swift +++ b/Example/IntegrationTests/Pairing/PairingTests.swift @@ -55,6 +55,13 @@ final class PairingTests: XCTestCase { return PushClientFactory.create(logger: logger, keyValueStorage: RuntimeKeyValueStorage(), keychainStorage: keychain, relayClient: relayClient) } + func makeAppClients() -> (PairingClient, PushClient) { + + } + + func makeWalletClient() -> (PairingClient, PushClient) { + } + func testProposePushOnPairing() async { let exp = expectation(description: "") From 84770b8f75ac49817a0cd8dd836ffd68fdf71376 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Tue, 6 Sep 2022 23:16:50 +0300 Subject: [PATCH 029/175] SessionEngine --- Package.swift | 2 +- .../NetworkInteracting.swift | 4 +- .../NetworkInteractor.swift | 14 +- .../RequestSubscriptionPayload.swift | 2 +- .../ResponseSubscriptionPayload.swift | 2 +- .../SubscriptionPayload.swift | 7 + .../Engine/Common/ApproveEngine.swift | 5 +- .../Engine/Common/SessionEngine.swift | 171 +++--- .../NetworkInteractor/NetworkInteractor.swift | 518 +++++++++--------- .../NetworkInteractor/NetworkRelaying.swift | 38 +- Sources/WalletConnectSign/Reason.swift | 11 - Sources/WalletConnectSign/Request.swift | 9 +- .../WCRequestSubscriptionPayload.swift | 11 - .../WalletConnectSign/Types/ReasonCode.swift | 4 +- .../WalletConnectSign/Types/WCMethod.swift | 37 -- .../WalletConnectSign/Types/WCRequest.swift | 236 +++----- .../WalletConnectSign/Types/WCResponse.swift | 14 - 17 files changed, 473 insertions(+), 612 deletions(-) create mode 100644 Sources/WalletConnectNetworking/SubscriptionPayload.swift delete mode 100644 Sources/WalletConnectSign/Reason.swift delete mode 100644 Sources/WalletConnectSign/Subscription/WCRequestSubscriptionPayload.swift delete mode 100644 Sources/WalletConnectSign/Types/WCMethod.swift delete mode 100644 Sources/WalletConnectSign/Types/WCResponse.swift diff --git a/Package.swift b/Package.swift index 7640db6ad..a991ee8bf 100644 --- a/Package.swift +++ b/Package.swift @@ -32,7 +32,7 @@ let package = Package( targets: [ .target( name: "WalletConnectSign", - dependencies: ["WalletConnectRelay", "WalletConnectUtils", "WalletConnectKMS", "WalletConnectPairing"], + dependencies: ["WalletConnectNetworking", "WalletConnectPairing"], path: "Sources/WalletConnectSign"), .target( name: "Chat", diff --git a/Sources/WalletConnectNetworking/NetworkInteracting.swift b/Sources/WalletConnectNetworking/NetworkInteracting.swift index 8dd02d382..67d5e9673 100644 --- a/Sources/WalletConnectNetworking/NetworkInteracting.swift +++ b/Sources/WalletConnectNetworking/NetworkInteracting.swift @@ -15,11 +15,11 @@ public protocol NetworkInteracting { func respondError(topic: String, requestId: RPCID, tag: Int, reason: Reason, envelopeType: Envelope.EnvelopeType) async throws func requestSubscription( - on request: ProtocolMethod + on request: ProtocolMethod? ) -> AnyPublisher, Never> func responseSubscription( - on request: ProtocolMethod + on request: ProtocolMethod? ) -> AnyPublisher, Never> func responseErrorSubscription(on request: ProtocolMethod) -> AnyPublisher diff --git a/Sources/WalletConnectNetworking/NetworkInteractor.swift b/Sources/WalletConnectNetworking/NetworkInteractor.swift index 13859ead6..f5ed34273 100644 --- a/Sources/WalletConnectNetworking/NetworkInteractor.swift +++ b/Sources/WalletConnectNetworking/NetworkInteractor.swift @@ -56,9 +56,12 @@ public class NetworkingInteractor: NetworkInteracting { } } - public func requestSubscription(on request: ProtocolMethod) -> AnyPublisher, Never> { + public func requestSubscription(on request: ProtocolMethod?) -> AnyPublisher, Never> { return requestPublisher - .filter { $0.request.method == request.method } + .filter { rpcRequest in + guard let request = request else { return true } + return rpcRequest.request.method == request.method + } .compactMap { topic, rpcRequest in guard let id = rpcRequest.id, let request = try? rpcRequest.params?.get(Request.self) else { return nil } return RequestSubscriptionPayload(id: id, topic: topic, request: request) @@ -66,9 +69,12 @@ public class NetworkingInteractor: NetworkInteracting { .eraseToAnyPublisher() } - public func responseSubscription(on request: ProtocolMethod) -> AnyPublisher, Never> { + public func responseSubscription(on request: ProtocolMethod?) -> AnyPublisher, Never> { return responsePublisher - .filter { $0.request.method == request.method } + .filter { rpcRequest in + guard let request = request else { return true } + return rpcRequest.request.method == request.method + } .compactMap { topic, rpcRequest, rpcResponse in guard let id = rpcRequest.id, diff --git a/Sources/WalletConnectNetworking/RequestSubscriptionPayload.swift b/Sources/WalletConnectNetworking/RequestSubscriptionPayload.swift index fa6b0e8db..d8767d692 100644 --- a/Sources/WalletConnectNetworking/RequestSubscriptionPayload.swift +++ b/Sources/WalletConnectNetworking/RequestSubscriptionPayload.swift @@ -1,7 +1,7 @@ import Foundation import JSONRPC -public struct RequestSubscriptionPayload: Codable { +public struct RequestSubscriptionPayload: Codable, SubscriptionPayload { public let id: RPCID public let topic: String public let request: Request diff --git a/Sources/WalletConnectNetworking/ResponseSubscriptionPayload.swift b/Sources/WalletConnectNetworking/ResponseSubscriptionPayload.swift index 21043eb9d..93b21538a 100644 --- a/Sources/WalletConnectNetworking/ResponseSubscriptionPayload.swift +++ b/Sources/WalletConnectNetworking/ResponseSubscriptionPayload.swift @@ -1,7 +1,7 @@ import Foundation import JSONRPC -public struct ResponseSubscriptionPayload { +public struct ResponseSubscriptionPayload: SubscriptionPayload { public let id: RPCID public let topic: String public let request: Request diff --git a/Sources/WalletConnectNetworking/SubscriptionPayload.swift b/Sources/WalletConnectNetworking/SubscriptionPayload.swift new file mode 100644 index 000000000..ad5e60eab --- /dev/null +++ b/Sources/WalletConnectNetworking/SubscriptionPayload.swift @@ -0,0 +1,7 @@ +import Foundation +import JSONRPC + +public protocol SubscriptionPayload { + var id: RPCID { get } + var topic: String { get } +} diff --git a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift index 229e2e698..a5a310a1d 100644 --- a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift @@ -3,6 +3,7 @@ import Combine import WalletConnectUtils import WalletConnectKMS import WalletConnectPairing +import WalletConnectNetworking final class ApproveEngine { enum Errors: Error { @@ -180,10 +181,10 @@ private extension ApproveEngine { }.store(in: &publishers) } - func respondError(payload: WCRequestSubscriptionPayload, reason: ReasonCode) { + func respondError(payload: SubscriptionPayload, reason: ReasonCode) { Task { do { - try await networkingInteractor.respondError(payload: payload, reason: reason) + try await networkingInteractor.respondError(topic: payload.topic, requestId: payload.id, tag: <#T##Int#>, reason: reason) } catch { logger.error("Respond Error failed with: \(error.localizedDescription)") } diff --git a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift index a1197c8e1..df321e01a 100644 --- a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift @@ -1,11 +1,13 @@ import Foundation import Combine +import JSONRPC import WalletConnectUtils import WalletConnectKMS +import WalletConnectNetworking final class SessionEngine { enum Errors: Error { - case respondError(payload: WCRequestSubscriptionPayload, reason: ReasonCode) + case respondError(payload: SubscriptionPayload, reason: ReasonCode) case sessionNotFound(topic: String) } @@ -49,15 +51,16 @@ final class SessionEngine { logger.debug("Could not find session to ping for topic \(topic)") return } - networkingInteractor.requestPeerResponse(.wcSessionPing, onTopic: topic) { [unowned self] result in - switch result { - case .success: - logger.debug("Did receive ping response") - completion(.success(())) - case .failure(let error): - logger.debug("error: \(error)") - } - } +// TODO: Ping disabled +// networkingInteractor.requestPeerResponse(.wcSessionPing, onTopic: topic) { [unowned self] result in +// switch result { +// case .success: +// logger.debug("Did receive ping response") +// completion(.success(())) +// case .failure(let error): +// logger.debug("error: \(error)") +// } +// } } func request(_ request: Request) async throws { @@ -71,14 +74,18 @@ final class SessionEngine { } let chainRequest = SessionType.RequestParams.Request(method: request.method, params: request.params) let sessionRequestParams = SessionType.RequestParams(request: chainRequest, chainId: request.chainId) - try await networkingInteractor.request(.wcSessionRequest(sessionRequestParams), onTopic: request.topic) + + let payload = WCRequest.sessionRequest(sessionRequestParams) + let rpcRequest = RPCRequest(method: WCRequest.Method.sessionRequest.method, params: payload) + try await networkingInteractor.request(rpcRequest, topic: request.topic, tag: WCRequest.Method.sessionRequest.requestTag) } func respondSessionRequest(topic: String, response: JsonRpcResult) async throws { guard sessionStore.hasSession(forTopic: topic) else { throw Errors.sessionNotFound(topic: topic) } - try await networkingInteractor.respond(topic: topic, response: response, tag: 1109) // FIXME: Hardcoded tag + // TODO: ??? +// try await networkingInteractor.respond(topic: topic, response: response, tag: 1109) // FIXME: Hardcoded tag } func emit(topic: String, event: SessionType.EventParams.Event, chainId: Blockchain) async throws { @@ -90,7 +97,8 @@ final class SessionEngine { throw WalletConnectError.invalidEvent } let params = SessionType.EventParams(event: event, chainId: chainId) - try await networkingInteractor.request(.wcSessionEvent(params), onTopic: topic) + let rpcRequest = RPCRequest(method: WCRequest.Method.sessionEvent.method, params: params) + try await networkingInteractor.request(rpcRequest, topic: topic, tag: WCRequest.Method.sessionEvent.requestTag) } } @@ -99,100 +107,113 @@ final class SessionEngine { private extension SessionEngine { func setupNetworkingSubscriptions() { - networkingInteractor.wcRequestPublisher.sink { [unowned self] subscriptionPayload in - do { - switch subscriptionPayload.wcRequest.params { - case .sessionDelete(let deleteParams): - try onSessionDelete(subscriptionPayload, deleteParams: deleteParams) - case .sessionRequest(let sessionRequestParams): - try onSessionRequest(subscriptionPayload, payloadParams: sessionRequestParams) + networkingInteractor.socketConnectionStatusPublisher + .sink { [unowned self] status in + guard status == .connected else { return } + sessionStore.getAll() + .forEach { session in + Task { try await networkingInteractor.subscribe(topic: session.topic) } + } + } + .store(in: &publishers) + + networkingInteractor.requestSubscription(on: nil) + .sink { [unowned self] (payload: RequestSubscriptionPayload) in + switch payload.request { + case .sessionDelete(let params): + onSessionDelete(payload, params: params) + case .sessionRequest(let params): + onSessionRequest(payload, params: params) case .sessionPing: - onSessionPing(subscriptionPayload) - case .sessionEvent(let eventParams): - try onSessionEvent(subscriptionPayload, eventParams: eventParams) + onSessionPing(payload) + case .sessionEvent(let params): + onSessionEvent(payload, params: params) default: return } - } catch Errors.respondError(let payload, let reason) { - respondError(payload: payload, reason: reason) - } catch { - logger.error("Unexpected Error: \(error.localizedDescription)") } - }.store(in: &publishers) - - networkingInteractor.transportConnectionPublisher - .sink { [unowned self] (_) in - let topics = sessionStore.getAll().map {$0.topic} - topics.forEach { topic in Task { try? await networkingInteractor.subscribe(topic: topic) } } - }.store(in: &publishers) - - networkingInteractor.responsePublisher - .sink { [unowned self] response in - self.handleResponse(response) - }.store(in: &publishers) + .store(in: &publishers) + + networkingInteractor.responseSubscription(on: nil) + .sink { [unowned self] (payload: ResponseSubscriptionPayload) in + switch payload.request { + case .sessionRequest(let params): + // TODO: ??? Chain ID from request is ok? + // Need to check absolute string + let response = Response(topic: payload.topic, chainId: params.chainId.absoluteString, result: payload.response) + onSessionResponse?(response) + default: + break + } + } + .store(in: &publishers) } - func respondError(payload: WCRequestSubscriptionPayload, reason: ReasonCode) { + func respondError(payload: SubscriptionPayload, reason: ReasonCode, tag: Int) { Task { do { - try await networkingInteractor.respondError(payload: payload, reason: reason) + try await networkingInteractor.respondError(topic: payload.topic, requestId: payload.id, tag: tag, reason: reason) } catch { logger.error("Respond Error failed with: \(error.localizedDescription)") } } } - func onSessionDelete(_ payload: WCRequestSubscriptionPayload, deleteParams: SessionType.DeleteParams) throws { + func onSessionDelete(_ payload: SubscriptionPayload, params: SessionType.DeleteParams) { + let tag = WCRequest.Method.sessionDelete.responseTag let topic = payload.topic guard sessionStore.hasSession(forTopic: topic) else { - throw Errors.respondError(payload: payload, reason: .noSessionForTopic) + return respondError(payload: payload, reason: .noSessionForTopic, tag: tag) } sessionStore.delete(topic: topic) networkingInteractor.unsubscribe(topic: topic) - networkingInteractor.respondSuccess(for: payload) - onSessionDelete?(topic, deleteParams) + Task(priority: .high) { + try await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, tag: tag) + } + onSessionDelete?(topic, params) } - func onSessionRequest(_ payload: WCRequestSubscriptionPayload, payloadParams: SessionType.RequestParams) throws { + func onSessionRequest(_ payload: SubscriptionPayload, params: SessionType.RequestParams) { + let tag = WCRequest.Method.sessionRequest.responseTag let topic = payload.topic - let jsonRpcRequest = JSONRPCRequest(id: payload.wcRequest.id, method: payloadParams.request.method, params: payloadParams.request.params) let request = Request( - id: jsonRpcRequest.id, - topic: topic, - method: jsonRpcRequest.method, - params: jsonRpcRequest.params, - chainId: payloadParams.chainId) + id: payload.id, + topic: payload.topic, + method: WCRequest.Method.sessionRequest.method, + params: params, + chainId: params.chainId) guard let session = sessionStore.getSession(forTopic: topic) else { - throw Errors.respondError(payload: payload, reason: .noSessionForTopic) + return respondError(payload: payload, reason: .noSessionForTopic, tag: tag) } - let chain = request.chainId - guard session.hasNamespace(for: chain) else { - throw Errors.respondError(payload: payload, reason: .unauthorizedChain) + guard session.hasNamespace(for: request.chainId) else { + return respondError(payload: payload, reason: .unauthorizedChain, tag: tag) } - guard session.hasPermission(forMethod: request.method, onChain: chain) else { - throw Errors.respondError(payload: payload, reason: .unauthorizedMethod(request.method)) + guard session.hasPermission(forMethod: request.method, onChain: request.chainId) else { + return respondError(payload: payload, reason: .unauthorizedMethod(request.method), tag: tag) } onSessionRequest?(request) } - func onSessionPing(_ payload: WCRequestSubscriptionPayload) { - networkingInteractor.respondSuccess(for: payload) + func onSessionPing(_ payload: SubscriptionPayload) { + Task(priority: .high) { + try await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, tag: WCRequest.Method.sessionPing.responseTag) + } } - func onSessionEvent(_ payload: WCRequestSubscriptionPayload, eventParams: SessionType.EventParams) throws { - let event = eventParams.event + func onSessionEvent(_ payload: SubscriptionPayload, params: SessionType.EventParams) { + let tag = WCRequest.Method.sessionEvent.responseTag + let event = params.event let topic = payload.topic guard let session = sessionStore.getSession(forTopic: topic) else { - throw Errors.respondError(payload: payload, reason: .noSessionForTopic) + return respondError(payload: payload, reason: .noSessionForTopic, tag: tag) + } + guard session.peerIsController, session.hasPermission(forEvent: event.name, onChain: params.chainId) else { + return respondError(payload: payload, reason: .unauthorizedEvent(event.name), tag: tag) } - guard - session.peerIsController, - session.hasPermission(forEvent: event.name, onChain: eventParams.chainId) - else { - throw Errors.respondError(payload: payload, reason: .unauthorizedEvent(event.name)) + Task(priority: .high) { + try await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, tag: tag) } - networkingInteractor.respondSuccess(for: payload) - onEventReceived?(topic, event.publicRepresentation(), eventParams.chainId) + onEventReceived?(topic, event.publicRepresentation(), params.chainId) } func setupExpirationSubscriptions() { @@ -201,14 +222,4 @@ private extension SessionEngine { self?.kms.deleteAgreementSecret(for: session.topic) } } - - func handleResponse(_ response: WCResponse) { - switch response.requestParams { - case .sessionRequest: - let response = Response(topic: response.topic, chainId: response.chainId, result: response.result) - onSessionResponse?(response) - default: - break - } - } } diff --git a/Sources/WalletConnectSign/NetworkInteractor/NetworkInteractor.swift b/Sources/WalletConnectSign/NetworkInteractor/NetworkInteractor.swift index 3389771b3..f23cc922f 100644 --- a/Sources/WalletConnectSign/NetworkInteractor/NetworkInteractor.swift +++ b/Sources/WalletConnectSign/NetworkInteractor/NetworkInteractor.swift @@ -1,259 +1,259 @@ -import Foundation -import Combine -import WalletConnectUtils -import WalletConnectKMS - -protocol NetworkInteracting: AnyObject { - var transportConnectionPublisher: AnyPublisher {get} - var wcRequestPublisher: AnyPublisher {get} - var responsePublisher: AnyPublisher {get} - /// Completes when request sent from a networking client - func request(_ wcMethod: WCMethod, onTopic topic: String) async throws - /// Completes with an acknowledgement from the relay network - func requestNetworkAck(_ wcMethod: WCMethod, onTopic topic: String, completion: @escaping ((Error?) -> Void)) - /// Completes with a peer response - func requestPeerResponse(_ wcMethod: WCMethod, onTopic topic: String, completion: ((Result, JSONRPCErrorResponse>) -> Void)?) - func respond(topic: String, response: JsonRpcResult, tag: Int) async throws - func respondSuccess(payload: WCRequestSubscriptionPayload) async throws - func respondSuccess(for payload: WCRequestSubscriptionPayload) - func respondError(payload: WCRequestSubscriptionPayload, reason: ReasonCode) async throws - func subscribe(topic: String) async throws - func unsubscribe(topic: String) -} - -extension NetworkInteracting { - func request(_ wcMethod: WCMethod, onTopic topic: String) { - requestPeerResponse(wcMethod, onTopic: topic, completion: nil) - } -} - -class NetworkInteractor: NetworkInteracting { - - private var publishers = Set() - - private var relayClient: NetworkRelaying - private let serializer: Serializing - private let jsonRpcHistory: JsonRpcHistoryRecording - - private let transportConnectionPublisherSubject = PassthroughSubject() - private let responsePublisherSubject = PassthroughSubject() - private let wcRequestPublisherSubject = PassthroughSubject() - - var transportConnectionPublisher: AnyPublisher { - transportConnectionPublisherSubject.eraseToAnyPublisher() - } - var wcRequestPublisher: AnyPublisher { - wcRequestPublisherSubject.eraseToAnyPublisher() - } - var responsePublisher: AnyPublisher { - responsePublisherSubject.eraseToAnyPublisher() - } - - let logger: ConsoleLogging - - init(relayClient: NetworkRelaying, - serializer: Serializing, - logger: ConsoleLogging, - jsonRpcHistory: JsonRpcHistoryRecording) { - self.relayClient = relayClient - self.serializer = serializer - self.logger = logger - self.jsonRpcHistory = jsonRpcHistory - setUpPublishers() - } - - func request(_ wcMethod: WCMethod, onTopic topic: String) async throws { - try await request(topic: topic, payload: wcMethod.asRequest()) - } - - /// Completes when networking client sends a request - func request(topic: String, payload: WCRequest) async throws { - try jsonRpcHistory.set(topic: topic, request: payload, chainId: getChainId(payload)) - let message = try serializer.serialize(topic: topic, encodable: payload) - let prompt = shouldPrompt(payload.method) - try await relayClient.publish(topic: topic, payload: message, tag: payload.tag, prompt: prompt) - } - - func requestPeerResponse(_ wcMethod: WCMethod, onTopic topic: String, completion: ((Result, JSONRPCErrorResponse>) -> Void)?) { - let payload = wcMethod.asRequest() - do { - try jsonRpcHistory.set(topic: topic, request: payload, chainId: getChainId(payload)) - let message = try serializer.serialize(topic: topic, encodable: payload) - let prompt = shouldPrompt(payload.method) - relayClient.publish(topic: topic, payload: message, tag: payload.tag, prompt: prompt) { [weak self] error in - guard let self = self else {return} - if let error = error { - self.logger.error(error) - } else { - var cancellable: AnyCancellable! - cancellable = self.responsePublisher - .filter {$0.result.id == payload.id} - .sink { (response) in - cancellable.cancel() - self.logger.debug("WC Relay - received response on topic: \(topic)") - switch response.result { - case .response(let response): - completion?(.success(response)) - case .error(let error): - self.logger.debug("Request error: \(error)") - completion?(.failure(error)) - } - } - } - } - } catch WalletConnectError.internal(.jsonRpcDuplicateDetected) { - logger.info("Info: Json Rpc Duplicate Detected") - } catch { - logger.error(error) - } - } - - /// Completes with an acknowledgement from the relay network. - /// completes with error if networking client was not able to send a message - func requestNetworkAck(_ wcMethod: WCMethod, onTopic topic: String, completion: @escaping ((Error?) -> Void)) { - do { - let payload = wcMethod.asRequest() - try jsonRpcHistory.set(topic: topic, request: payload, chainId: getChainId(payload)) - let message = try serializer.serialize(topic: topic, encodable: payload) - let prompt = shouldPrompt(payload.method) - relayClient.publish(topic: topic, payload: message, tag: payload.tag, prompt: prompt) { error in - completion(error) - } - } catch WalletConnectError.internal(.jsonRpcDuplicateDetected) { - logger.info("Info: Json Rpc Duplicate Detected") - } catch { - logger.error(error) - } - } - - func respond(topic: String, response: JsonRpcResult, tag: Int) async throws { - _ = try jsonRpcHistory.resolve(response: response) - - let message = try serializer.serialize(topic: topic, encodable: response.value) - logger.debug("Responding....topic: \(topic)") - - do { - try await relayClient.publish(topic: topic, payload: message, tag: tag, prompt: false) - } catch WalletConnectError.internal(.jsonRpcDuplicateDetected) { - logger.info("Info: Json Rpc Duplicate Detected") - } - } - - func respondSuccess(payload: WCRequestSubscriptionPayload) async throws { - let response = JSONRPCResponse(id: payload.wcRequest.id, result: AnyCodable(true)) - try await respond(topic: payload.topic, response: JsonRpcResult.response(response), tag: payload.wcRequest.responseTag) - } - - func respondError(payload: WCRequestSubscriptionPayload, reason: ReasonCode) async throws { - let response = JSONRPCErrorResponse(id: payload.wcRequest.id, error: JSONRPCErrorResponse.Error(code: reason.code, message: reason.message)) - try await respond(topic: payload.topic, response: JsonRpcResult.error(response), tag: payload.wcRequest.responseTag) - } - - // TODO: Move to async - func respondSuccess(for payload: WCRequestSubscriptionPayload) { - Task(priority: .background) { - do { - try await respondSuccess(payload: payload) - } catch { - self.logger.error("Respond Success failed with: \(error.localizedDescription)") - } - } - } - - func subscribe(topic: String) async throws { - try await relayClient.subscribe(topic: topic) - } - - func unsubscribe(topic: String) { - relayClient.unsubscribe(topic: topic) { [weak self] error in - if let error = error { - self?.logger.error(error) - } else { - self?.jsonRpcHistory.delete(topic: topic) - } - } - } - - // MARK: - Private - - private func setUpPublishers() { - relayClient.socketConnectionStatusPublisher.sink { [weak self] status in - if status == .connected { - self?.transportConnectionPublisherSubject.send() - } - }.store(in: &publishers) - - relayClient.messagePublisher.sink { [weak self] (topic, message) in - self?.manageSubscription(topic, message) - } - .store(in: &publishers) - } - - private func manageSubscription(_ topic: String, _ encodedEnvelope: String) { - if let deserializedJsonRpcRequest: WCRequest = serializer.tryDeserialize(topic: topic, encodedEnvelope: encodedEnvelope) { - handleWCRequest(topic: topic, request: deserializedJsonRpcRequest) - } else if let deserializedJsonRpcResponse: JSONRPCResponse = serializer.tryDeserialize(topic: topic, encodedEnvelope: encodedEnvelope) { - handleJsonRpcResponse(response: deserializedJsonRpcResponse) - } else if let deserializedJsonRpcError: JSONRPCErrorResponse = serializer.tryDeserialize(topic: topic, encodedEnvelope: encodedEnvelope) { - handleJsonRpcErrorResponse(response: deserializedJsonRpcError) - } else { - logger.warn("Warning: Networking Interactor - Received unknown object type from networking relay") - } - } - - private func handleWCRequest(topic: String, request: WCRequest) { - do { - try jsonRpcHistory.set(topic: topic, request: request, chainId: getChainId(request)) - let payload = WCRequestSubscriptionPayload(topic: topic, wcRequest: request) - wcRequestPublisherSubject.send(payload) - } catch WalletConnectError.internal(.jsonRpcDuplicateDetected) { - logger.info("Info: Json Rpc Duplicate Detected") - } catch { - logger.error(error) - } - } - - private func handleJsonRpcResponse(response: JSONRPCResponse) { - do { - let record = try jsonRpcHistory.resolve(response: JsonRpcResult.response(response)) - let wcResponse = WCResponse( - topic: record.topic, - chainId: record.chainId, - requestMethod: record.request.method, - requestParams: record.request.params, - result: JsonRpcResult.response(response)) - responsePublisherSubject.send(wcResponse) - } catch { - logger.info("Info: \(error.localizedDescription)") - } - } - - private func handleJsonRpcErrorResponse(response: JSONRPCErrorResponse) { - do { - let record = try jsonRpcHistory.resolve(response: JsonRpcResult.error(response)) - let wcResponse = WCResponse( - topic: record.topic, - chainId: record.chainId, - requestMethod: record.request.method, - requestParams: record.request.params, - result: JsonRpcResult.error(response)) - responsePublisherSubject.send(wcResponse) - } catch { - logger.info("Info: \(error.localizedDescription)") - } - } - - private func shouldPrompt(_ method: WCRequest.Method) -> Bool { - switch method { - case .sessionRequest: - return true - default: - return false - } - } - - func getChainId(_ request: WCRequest) -> String? { - guard case let .sessionRequest(payload) = request.params else {return nil} - return payload.chainId.absoluteString - } -} +//import Foundation +//import Combine +//import WalletConnectUtils +//import WalletConnectKMS +// +//protocol NetworkInteracting: AnyObject { +// var transportConnectionPublisher: AnyPublisher {get} +// var wcRequestPublisher: AnyPublisher {get} +// var responsePublisher: AnyPublisher {get} +// /// Completes when request sent from a networking client +// func request(_ wcMethod: WCMethod, onTopic topic: String) async throws +// /// Completes with an acknowledgement from the relay network +// func requestNetworkAck(_ wcMethod: WCMethod, onTopic topic: String, completion: @escaping ((Error?) -> Void)) +// /// Completes with a peer response +// func requestPeerResponse(_ wcMethod: WCMethod, onTopic topic: String, completion: ((Result, JSONRPCErrorResponse>) -> Void)?) +// func respond(topic: String, response: JsonRpcResult, tag: Int) async throws +// func respondSuccess(payload: WCRequestSubscriptionPayload) async throws +// func respondSuccess(for payload: WCRequestSubscriptionPayload) +// func respondError(payload: WCRequestSubscriptionPayload, reason: ReasonCode) async throws +// func subscribe(topic: String) async throws +// func unsubscribe(topic: String) +//} +// +//extension NetworkInteracting { +// func request(_ wcMethod: WCMethod, onTopic topic: String) { +// requestPeerResponse(wcMethod, onTopic: topic, completion: nil) +// } +//} +// +//class NetworkInteractor: NetworkInteracting { +// +// private var publishers = Set() +// +// private var relayClient: NetworkRelaying +// private let serializer: Serializing +// private let jsonRpcHistory: JsonRpcHistoryRecording +// +// private let transportConnectionPublisherSubject = PassthroughSubject() +// private let responsePublisherSubject = PassthroughSubject() +// private let wcRequestPublisherSubject = PassthroughSubject() +// +// var transportConnectionPublisher: AnyPublisher { +// transportConnectionPublisherSubject.eraseToAnyPublisher() +// } +// var wcRequestPublisher: AnyPublisher { +// wcRequestPublisherSubject.eraseToAnyPublisher() +// } +// var responsePublisher: AnyPublisher { +// responsePublisherSubject.eraseToAnyPublisher() +// } +// +// let logger: ConsoleLogging +// +// init(relayClient: NetworkRelaying, +// serializer: Serializing, +// logger: ConsoleLogging, +// jsonRpcHistory: JsonRpcHistoryRecording) { +// self.relayClient = relayClient +// self.serializer = serializer +// self.logger = logger +// self.jsonRpcHistory = jsonRpcHistory +// setUpPublishers() +// } +// +// func request(_ wcMethod: WCMethod, onTopic topic: String) async throws { +// try await request(topic: topic, payload: wcMethod.asRequest()) +// } +// +// /// Completes when networking client sends a request +// func request(topic: String, payload: WCRequest) async throws { +// try jsonRpcHistory.set(topic: topic, request: payload, chainId: getChainId(payload)) +// let message = try serializer.serialize(topic: topic, encodable: payload) +// let prompt = shouldPrompt(payload.method) +// try await relayClient.publish(topic: topic, payload: message, tag: payload.tag, prompt: prompt) +// } +// +// func requestPeerResponse(_ wcMethod: WCMethod, onTopic topic: String, completion: ((Result, JSONRPCErrorResponse>) -> Void)?) { +// let payload = wcMethod.asRequest() +// do { +// try jsonRpcHistory.set(topic: topic, request: payload, chainId: getChainId(payload)) +// let message = try serializer.serialize(topic: topic, encodable: payload) +// let prompt = shouldPrompt(payload.method) +// relayClient.publish(topic: topic, payload: message, tag: payload.tag, prompt: prompt) { [weak self] error in +// guard let self = self else {return} +// if let error = error { +// self.logger.error(error) +// } else { +// var cancellable: AnyCancellable! +// cancellable = self.responsePublisher +// .filter {$0.result.id == payload.id} +// .sink { (response) in +// cancellable.cancel() +// self.logger.debug("WC Relay - received response on topic: \(topic)") +// switch response.result { +// case .response(let response): +// completion?(.success(response)) +// case .error(let error): +// self.logger.debug("Request error: \(error)") +// completion?(.failure(error)) +// } +// } +// } +// } +// } catch WalletConnectError.internal(.jsonRpcDuplicateDetected) { +// logger.info("Info: Json Rpc Duplicate Detected") +// } catch { +// logger.error(error) +// } +// } +// +// /// Completes with an acknowledgement from the relay network. +// /// completes with error if networking client was not able to send a message +// func requestNetworkAck(_ wcMethod: WCMethod, onTopic topic: String, completion: @escaping ((Error?) -> Void)) { +// do { +// let payload = wcMethod.asRequest() +// try jsonRpcHistory.set(topic: topic, request: payload, chainId: getChainId(payload)) +// let message = try serializer.serialize(topic: topic, encodable: payload) +// let prompt = shouldPrompt(payload.method) +// relayClient.publish(topic: topic, payload: message, tag: payload.tag, prompt: prompt) { error in +// completion(error) +// } +// } catch WalletConnectError.internal(.jsonRpcDuplicateDetected) { +// logger.info("Info: Json Rpc Duplicate Detected") +// } catch { +// logger.error(error) +// } +// } +// +// func respond(topic: String, response: JsonRpcResult, tag: Int) async throws { +// _ = try jsonRpcHistory.resolve(response: response) +// +// let message = try serializer.serialize(topic: topic, encodable: response.value) +// logger.debug("Responding....topic: \(topic)") +// +// do { +// try await relayClient.publish(topic: topic, payload: message, tag: tag, prompt: false) +// } catch WalletConnectError.internal(.jsonRpcDuplicateDetected) { +// logger.info("Info: Json Rpc Duplicate Detected") +// } +// } +// +// func respondSuccess(payload: WCRequestSubscriptionPayload) async throws { +// let response = JSONRPCResponse(id: payload.wcRequest.id, result: AnyCodable(true)) +// try await respond(topic: payload.topic, response: JsonRpcResult.response(response), tag: payload.wcRequest.responseTag) +// } +// +// func respondError(payload: WCRequestSubscriptionPayload, reason: ReasonCode) async throws { +// let response = JSONRPCErrorResponse(id: payload.wcRequest.id, error: JSONRPCErrorResponse.Error(code: reason.code, message: reason.message)) +// try await respond(topic: payload.topic, response: JsonRpcResult.error(response), tag: payload.wcRequest.responseTag) +// } +// +// // TODO: Move to async +// func respondSuccess(for payload: WCRequestSubscriptionPayload) { +// Task(priority: .background) { +// do { +// try await respondSuccess(payload: payload) +// } catch { +// self.logger.error("Respond Success failed with: \(error.localizedDescription)") +// } +// } +// } +// +// func subscribe(topic: String) async throws { +// try await relayClient.subscribe(topic: topic) +// } +// +// func unsubscribe(topic: String) { +// relayClient.unsubscribe(topic: topic) { [weak self] error in +// if let error = error { +// self?.logger.error(error) +// } else { +// self?.jsonRpcHistory.delete(topic: topic) +// } +// } +// } +// +// // MARK: - Private +// +// private func setUpPublishers() { +// relayClient.socketConnectionStatusPublisher.sink { [weak self] status in +// if status == .connected { +// self?.transportConnectionPublisherSubject.send() +// } +// }.store(in: &publishers) +// +// relayClient.messagePublisher.sink { [weak self] (topic, message) in +// self?.manageSubscription(topic, message) +// } +// .store(in: &publishers) +// } +// +// private func manageSubscription(_ topic: String, _ encodedEnvelope: String) { +// if let deserializedJsonRpcRequest: WCRequest = serializer.tryDeserialize(topic: topic, encodedEnvelope: encodedEnvelope) { +// handleWCRequest(topic: topic, request: deserializedJsonRpcRequest) +// } else if let deserializedJsonRpcResponse: JSONRPCResponse = serializer.tryDeserialize(topic: topic, encodedEnvelope: encodedEnvelope) { +// handleJsonRpcResponse(response: deserializedJsonRpcResponse) +// } else if let deserializedJsonRpcError: JSONRPCErrorResponse = serializer.tryDeserialize(topic: topic, encodedEnvelope: encodedEnvelope) { +// handleJsonRpcErrorResponse(response: deserializedJsonRpcError) +// } else { +// logger.warn("Warning: Networking Interactor - Received unknown object type from networking relay") +// } +// } +// +// private func handleWCRequest(topic: String, request: WCRequest) { +// do { +// try jsonRpcHistory.set(topic: topic, request: request, chainId: getChainId(request)) +// let payload = WCRequestSubscriptionPayload(topic: topic, wcRequest: request) +// wcRequestPublisherSubject.send(payload) +// } catch WalletConnectError.internal(.jsonRpcDuplicateDetected) { +// logger.info("Info: Json Rpc Duplicate Detected") +// } catch { +// logger.error(error) +// } +// } +// +// private func handleJsonRpcResponse(response: JSONRPCResponse) { +// do { +// let record = try jsonRpcHistory.resolve(response: JsonRpcResult.response(response)) +// let wcResponse = WCResponse( +// topic: record.topic, +// chainId: record.chainId, +// requestMethod: record.request.method, +// requestParams: record.request.params, +// result: JsonRpcResult.response(response)) +// responsePublisherSubject.send(wcResponse) +// } catch { +// logger.info("Info: \(error.localizedDescription)") +// } +// } +// +// private func handleJsonRpcErrorResponse(response: JSONRPCErrorResponse) { +// do { +// let record = try jsonRpcHistory.resolve(response: JsonRpcResult.error(response)) +// let wcResponse = WCResponse( +// topic: record.topic, +// chainId: record.chainId, +// requestMethod: record.request.method, +// requestParams: record.request.params, +// result: JsonRpcResult.error(response)) +// responsePublisherSubject.send(wcResponse) +// } catch { +// logger.info("Info: \(error.localizedDescription)") +// } +// } +// +// private func shouldPrompt(_ method: WCRequest.Method) -> Bool { +// switch method { +// case .sessionRequest: +// return true +// default: +// return false +// } +// } +// +// func getChainId(_ request: WCRequest) -> String? { +// guard case let .sessionRequest(payload) = request.params else {return nil} +// return payload.chainId.absoluteString +// } +//} diff --git a/Sources/WalletConnectSign/NetworkInteractor/NetworkRelaying.swift b/Sources/WalletConnectSign/NetworkInteractor/NetworkRelaying.swift index 5574cf826..fec9c6ed8 100644 --- a/Sources/WalletConnectSign/NetworkInteractor/NetworkRelaying.swift +++ b/Sources/WalletConnectSign/NetworkInteractor/NetworkRelaying.swift @@ -1,19 +1,19 @@ -import Foundation -import WalletConnectRelay -import Combine - -extension RelayClient: NetworkRelaying {} - -protocol NetworkRelaying { - var messagePublisher: AnyPublisher<(topic: String, message: String), Never> { get } - var socketConnectionStatusPublisher: AnyPublisher { get } - func connect() throws - func disconnect(closeCode: URLSessionWebSocketTask.CloseCode) throws - func publish(topic: String, payload: String, tag: Int, prompt: Bool) async throws - /// - returns: request id - func publish(topic: String, payload: String, tag: Int, prompt: Bool, onNetworkAcknowledge: @escaping ((Error?) -> Void)) - func subscribe(topic: String, completion: @escaping (Error?) -> Void) - func subscribe(topic: String) async throws - /// - returns: request id - func unsubscribe(topic: String, completion: @escaping ((Error?) -> Void)) -} +//import Foundation +//import WalletConnectRelay +//import Combine +// +//extension RelayClient: NetworkRelaying {} +// +//protocol NetworkRelaying { +// var messagePublisher: AnyPublisher<(topic: String, message: String), Never> { get } +// var socketConnectionStatusPublisher: AnyPublisher { get } +// func connect() throws +// func disconnect(closeCode: URLSessionWebSocketTask.CloseCode) throws +// func publish(topic: String, payload: String, tag: Int, prompt: Bool) async throws +// /// - returns: request id +// func publish(topic: String, payload: String, tag: Int, prompt: Bool, onNetworkAcknowledge: @escaping ((Error?) -> Void)) +// func subscribe(topic: String, completion: @escaping (Error?) -> Void) +// func subscribe(topic: String) async throws +// /// - returns: request id +// func unsubscribe(topic: String, completion: @escaping ((Error?) -> Void)) +//} diff --git a/Sources/WalletConnectSign/Reason.swift b/Sources/WalletConnectSign/Reason.swift deleted file mode 100644 index d49aab825..000000000 --- a/Sources/WalletConnectSign/Reason.swift +++ /dev/null @@ -1,11 +0,0 @@ -// TODO: Refactor into codes. Reference: https://docs.walletconnect.com/2.0/protocol/reason-codes -public struct Reason { - - public let code: Int - public let message: String - - public init(code: Int, message: String) { - self.code = code - self.message = message - } -} diff --git a/Sources/WalletConnectSign/Request.swift b/Sources/WalletConnectSign/Request.swift index da55722b2..5cca66797 100644 --- a/Sources/WalletConnectSign/Request.swift +++ b/Sources/WalletConnectSign/Request.swift @@ -1,14 +1,15 @@ import Foundation +import JSONRPC import WalletConnectUtils public struct Request: Codable, Equatable { - public let id: Int64 + public let id: RPCID public let topic: String public let method: String public let params: AnyCodable public let chainId: Blockchain - internal init(id: Int64, topic: String, method: String, params: AnyCodable, chainId: Blockchain) { + internal init(id: RPCID, topic: String, method: String, params: AnyCodable, chainId: Blockchain) { self.id = id self.topic = topic self.method = method @@ -17,10 +18,10 @@ public struct Request: Codable, Equatable { } public init(topic: String, method: String, params: AnyCodable, chainId: Blockchain) { - self.init(id: JsonRpcID.generate(), topic: topic, method: method, params: params, chainId: chainId) + self.init(id: RPCID(JsonRpcID.generate()), topic: topic, method: method, params: params, chainId: chainId) } - internal init(id: Int64, topic: String, method: String, params: C, chainId: Blockchain) where C: Codable { + internal init(id: RPCID, topic: String, method: String, params: C, chainId: Blockchain) where C: Codable { self.init(id: id, topic: topic, method: method, params: AnyCodable(params), chainId: chainId) } } diff --git a/Sources/WalletConnectSign/Subscription/WCRequestSubscriptionPayload.swift b/Sources/WalletConnectSign/Subscription/WCRequestSubscriptionPayload.swift deleted file mode 100644 index 91efa9654..000000000 --- a/Sources/WalletConnectSign/Subscription/WCRequestSubscriptionPayload.swift +++ /dev/null @@ -1,11 +0,0 @@ -import Foundation -import WalletConnectUtils - -struct WCRequestSubscriptionPayload: Codable { - let topic: String - let wcRequest: WCRequest - - var timestamp: Date { - return JsonRpcID.timestamp(from: wcRequest.id) - } -} diff --git a/Sources/WalletConnectSign/Types/ReasonCode.swift b/Sources/WalletConnectSign/Types/ReasonCode.swift index cdc4f8db5..8ca23bd4c 100644 --- a/Sources/WalletConnectSign/Types/ReasonCode.swift +++ b/Sources/WalletConnectSign/Types/ReasonCode.swift @@ -1,4 +1,6 @@ -enum ReasonCode: Codable, Equatable { +import WalletConnectNetworking + +enum ReasonCode: Reason, Codable, Equatable { enum Context: String, Codable { case pairing = "pairing" diff --git a/Sources/WalletConnectSign/Types/WCMethod.swift b/Sources/WalletConnectSign/Types/WCMethod.swift deleted file mode 100644 index e2002188a..000000000 --- a/Sources/WalletConnectSign/Types/WCMethod.swift +++ /dev/null @@ -1,37 +0,0 @@ -import Foundation -import WalletConnectPairing - -enum WCMethod { - case wcPairingPing - case wcSessionPropose(SessionType.ProposeParams) - case wcSessionSettle(SessionType.SettleParams) - case wcSessionUpdate(SessionType.UpdateParams) - case wcSessionExtend(SessionType.UpdateExpiryParams) - case wcSessionDelete(SessionType.DeleteParams) - case wcSessionRequest(SessionType.RequestParams) - case wcSessionPing - case wcSessionEvent(SessionType.EventParams) - - func asRequest() -> WCRequest { - switch self { - case .wcPairingPing: - return WCRequest(method: .pairingPing, params: .pairingPing(PairingType.PingParams())) - case .wcSessionPropose(let proposalParams): - return WCRequest(method: .sessionPropose, params: .sessionPropose(proposalParams)) - case .wcSessionSettle(let settleParams): - return WCRequest(method: .sessionSettle, params: .sessionSettle(settleParams)) - case .wcSessionUpdate(let updateParams): - return WCRequest(method: .sessionUpdate, params: .sessionUpdate(updateParams)) - case .wcSessionExtend(let updateExpiryParams): - return WCRequest(method: .sessionExtend, params: .sessionExtend(updateExpiryParams)) - case .wcSessionDelete(let deleteParams): - return WCRequest(method: .sessionDelete, params: .sessionDelete(deleteParams)) - case .wcSessionRequest(let payloadParams): - return WCRequest(method: .sessionRequest, params: .sessionRequest(payloadParams)) - case .wcSessionPing: - return WCRequest(method: .sessionPing, params: .sessionPing(SessionType.PingParams())) - case .wcSessionEvent(let eventParams): - return WCRequest(method: .sessionEvent, params: .sessionEvent(eventParams)) - } - } -} diff --git a/Sources/WalletConnectSign/Types/WCRequest.swift b/Sources/WalletConnectSign/Types/WCRequest.swift index ec9edfdf8..a4a7e44df 100644 --- a/Sources/WalletConnectSign/Types/WCRequest.swift +++ b/Sources/WalletConnectSign/Types/WCRequest.swift @@ -1,179 +1,85 @@ import Foundation +import JSONRPC import WalletConnectPairing import WalletConnectUtils +import WalletConnectNetworking -struct WCRequest: Codable { - let id: Int64 - let jsonrpc: String - let method: Method - let params: Params +enum WCRequest: Codable { + case pairingDelete(PairingType.DeleteParams) + case pairingPing(PairingType.PingParams) + case sessionPropose(SessionType.ProposeParams) + case sessionSettle(SessionType.SettleParams) + case sessionUpdate(SessionType.UpdateParams) + case sessionExtend(SessionType.UpdateExpiryParams) + case sessionDelete(SessionType.DeleteParams) + case sessionRequest(SessionType.RequestParams) + case sessionPing(SessionType.PingParams) + case sessionEvent(SessionType.EventParams) - enum CodingKeys: CodingKey { - case id - case jsonrpc - case method - case params - } - - internal init(id: Int64 = JsonRpcID.generate(), jsonrpc: String = "2.0", method: Method, params: Params) { - self.id = id - self.jsonrpc = jsonrpc - self.method = method - self.params = params - } + enum Method: ProtocolMethod { + case pairingDelete + case pairingPing + case sessionPropose + case sessionSettle + case sessionUpdate + case sessionExtend + case sessionDelete + case sessionRequest + case sessionPing + case sessionEvent - init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - id = try container.decode(Int64.self, forKey: .id) - jsonrpc = try container.decode(String.self, forKey: .jsonrpc) - method = try container.decode(Method.self, forKey: .method) - switch method { - case .pairingDelete: - let paramsValue = try container.decode(PairingType.DeleteParams.self, forKey: .params) - params = .pairingDelete(paramsValue) - case .pairingPing: - let paramsValue = try container.decode(PairingType.PingParams.self, forKey: .params) - params = .pairingPing(paramsValue) - case .sessionPropose: - let paramsValue = try container.decode(SessionType.ProposeParams.self, forKey: .params) - params = .sessionPropose(paramsValue) - case .sessionSettle: - let paramsValue = try container.decode(SessionType.SettleParams.self, forKey: .params) - params = .sessionSettle(paramsValue) - case .sessionUpdate: - let paramsValue = try container.decode(SessionType.UpdateParams.self, forKey: .params) - params = .sessionUpdate(paramsValue) - case .sessionDelete: - let paramsValue = try container.decode(SessionType.DeleteParams.self, forKey: .params) - params = .sessionDelete(paramsValue) - case .sessionRequest: - let paramsValue = try container.decode(SessionType.RequestParams.self, forKey: .params) - params = .sessionRequest(paramsValue) - case .sessionPing: - let paramsValue = try container.decode(SessionType.PingParams.self, forKey: .params) - params = .sessionPing(paramsValue) - case .sessionExtend: - let paramsValue = try container.decode(SessionType.UpdateExpiryParams.self, forKey: .params) - params = .sessionExtend(paramsValue) - case .sessionEvent: - let paramsValue = try container.decode(SessionType.EventParams.self, forKey: .params) - params = .sessionEvent(paramsValue) - } - } - - func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(id, forKey: .id) - try container.encode(jsonrpc, forKey: .jsonrpc) - try container.encode(method.rawValue, forKey: .method) - switch params { - case .pairingDelete(let params): - try container.encode(params, forKey: .params) - case .pairingPing(let params): - try container.encode(params, forKey: .params) - case .sessionPropose(let params): - try container.encode(params, forKey: .params) - case .sessionSettle(let params): - try container.encode(params, forKey: .params) - case .sessionUpdate(let params): - try container.encode(params, forKey: .params) - case .sessionExtend(let params): - try container.encode(params, forKey: .params) - case .sessionDelete(let params): - try container.encode(params, forKey: .params) - case .sessionRequest(let params): - try container.encode(params, forKey: .params) - case .sessionPing(let params): - try container.encode(params, forKey: .params) - case .sessionEvent(let params): - try container.encode(params, forKey: .params) + var method: String { + switch self { + case .pairingDelete: + return "wc_pairingDelete" + case .pairingPing: + return "wc_pairingPing" + case .sessionPropose: + return "wc_sessionPropose" + case .sessionSettle: + return "wc_sessionSettle" + case .sessionUpdate: + return "wc_sessionUpdate" + case .sessionExtend: + return "wc_sessionExtend" + case .sessionDelete: + return "wc_sessionDelete" + case .sessionRequest: + return "wc_sessionRequest" + case .sessionPing: + return "wc_sessionPing" + case .sessionEvent: + return "wc_sessionEvent" + } } - } -} -extension WCRequest { - enum Method: String, Codable { - case pairingDelete = "wc_pairingDelete" - case pairingPing = "wc_pairingPing" - case sessionPropose = "wc_sessionPropose" - case sessionSettle = "wc_sessionSettle" - case sessionUpdate = "wc_sessionUpdate" - case sessionExtend = "wc_sessionExtend" - case sessionDelete = "wc_sessionDelete" - case sessionRequest = "wc_sessionRequest" - case sessionPing = "wc_sessionPing" - case sessionEvent = "wc_sessionEvent" - } -} - -extension WCRequest { - enum Params: Codable, Equatable { - case pairingDelete(PairingType.DeleteParams) - case pairingPing(PairingType.PingParams) - case sessionPropose(SessionType.ProposeParams) - case sessionSettle(SessionType.SettleParams) - case sessionUpdate(SessionType.UpdateParams) - case sessionExtend(SessionType.UpdateExpiryParams) - case sessionDelete(SessionType.DeleteParams) - case sessionRequest(SessionType.RequestParams) - case sessionPing(SessionType.PingParams) - case sessionEvent(SessionType.EventParams) - - static func == (lhs: Params, rhs: Params) -> Bool { - switch (lhs, rhs) { - case (.pairingDelete(let lhsParam), pairingDelete(let rhsParam)): - return lhsParam == rhsParam - case (.sessionPropose(let lhsParam), sessionPropose(let rhsParam)): - return lhsParam == rhsParam - case (.sessionSettle(let lhsParam), sessionSettle(let rhsParam)): - return lhsParam == rhsParam - case (.sessionUpdate(let lhsParam), sessionUpdate(let rhsParam)): - return lhsParam == rhsParam - case (.sessionExtend(let lhsParam), sessionExtend(let rhsParams)): - return lhsParam == rhsParams - case (.sessionDelete(let lhsParam), sessionDelete(let rhsParam)): - return lhsParam == rhsParam - case (.sessionRequest(let lhsParam), sessionRequest(let rhsParam)): - return lhsParam == rhsParam - case (.sessionPing(let lhsParam), sessionPing(let rhsParam)): - return lhsParam == rhsParam - case (.sessionEvent(let lhsParam), sessionEvent(let rhsParam)): - return lhsParam == rhsParam - default: - return false + var requestTag: Int { + switch self { + case .pairingDelete: + return 1000 + case .pairingPing: + return 1002 + case .sessionPropose: + return 1100 + case .sessionSettle: + return 1102 + case .sessionUpdate: + return 1104 + case .sessionExtend: + return 1106 + case .sessionDelete: + return 1112 + case .sessionRequest: + return 1108 + case .sessionPing: + return 1114 + case .sessionEvent: + return 1110 } } - } -} - -extension WCRequest { - var tag: Int { - switch method { - case .pairingDelete: - return 1000 - case .pairingPing: - return 1002 - case .sessionPropose: - return 1100 - case .sessionSettle: - return 1102 - case .sessionUpdate: - return 1104 - case .sessionExtend: - return 1106 - case .sessionDelete: - return 1112 - case .sessionRequest: - return 1108 - case .sessionPing: - return 1114 - case .sessionEvent: - return 1110 + var responseTag: Int { + return requestTag + 1 } } - - var responseTag: Int { - return tag + 1 - } } diff --git a/Sources/WalletConnectSign/Types/WCResponse.swift b/Sources/WalletConnectSign/Types/WCResponse.swift deleted file mode 100644 index b457b9ed0..000000000 --- a/Sources/WalletConnectSign/Types/WCResponse.swift +++ /dev/null @@ -1,14 +0,0 @@ -import Foundation -import WalletConnectUtils - -struct WCResponse: Codable { - let topic: String - let chainId: String? - let requestMethod: WCRequest.Method - let requestParams: WCRequest.Params - let result: JsonRpcResult - - var timestamp: Date { - return JsonRpcID.timestamp(from: result.id) - } -} From 498c6b1e132d752a22f3e4d9715b684de6b2ba35 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Wed, 7 Sep 2022 00:02:10 +0300 Subject: [PATCH 030/175] ApproveEngine --- .../Engine/Common/ApproveEngine.swift | 107 +++++++++--------- .../Engine/Common/PairingEngine.swift | 1 + .../Engine/Common/SessionEngine.swift | 1 - .../Engine/Controller/PairEngine.swift | 1 + .../NetworkingInteractorMock.swift | 16 ++- 5 files changed, 69 insertions(+), 57 deletions(-) diff --git a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift index a5a310a1d..e75cc8bde 100644 --- a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift @@ -1,5 +1,6 @@ import Foundation import Combine +import JSONRPC import WalletConnectUtils import WalletConnectKMS import WalletConnectPairing @@ -12,7 +13,6 @@ final class ApproveEngine { case proposalPayloadsNotFound case pairingNotFound case agreementMissingOrInvalid - case respondError(payload: WCRequestSubscriptionPayload, reason: ReasonCode) } var onSessionProposal: ((Session.Proposal) -> Void)? @@ -24,7 +24,7 @@ final class ApproveEngine { private let networkingInteractor: NetworkInteracting private let pairingStore: WCPairingStorage private let sessionStore: WCSessionStorage - private let proposalPayloadsStore: CodableStore + private let proposalPayloadsStore: CodableStore> private let sessionToPairingTopic: CodableStore private let metadata: AppMetadata private let kms: KeyManagementServiceProtocol @@ -34,7 +34,7 @@ final class ApproveEngine { init( networkingInteractor: NetworkInteracting, - proposalPayloadsStore: CodableStore, + proposalPayloadsStore: CodableStore>, sessionToPairingTopic: CodableStore, metadata: AppMetadata, kms: KeyManagementServiceProtocol, @@ -57,7 +57,7 @@ final class ApproveEngine { func approveProposal(proposerPubKey: String, validating sessionNamespaces: [String: SessionNamespace]) async throws { let payload = try proposalPayloadsStore.get(key: proposerPubKey) - guard let payload = payload, case .sessionPropose(let proposal) = payload.wcRequest.params else { + guard let payload = payload, case .sessionPropose(let proposal) = payload.request else { throw Errors.wrongRequestParams } @@ -80,14 +80,13 @@ final class ApproveEngine { throw Errors.relayNotFound } - let proposeResponse = SessionType.ProposeResponse(relay: relay, responderPublicKey: selfPublicKey.hexRepresentation) - let response = JSONRPCResponse(id: payload.wcRequest.id, result: AnyCodable(proposeResponse)) - guard var pairing = pairingStore.getPairing(forTopic: payload.topic) else { throw Errors.pairingNotFound } - try await networkingInteractor.respond(topic: payload.topic, response: .response(response), tag: payload.wcRequest.responseTag) + let result = SessionType.ProposeResponse(relay: relay, responderPublicKey: selfPublicKey.hexRepresentation) + let response = RPCResponse(id: payload.id, result: result) + try await networkingInteractor.respond(topic: payload.topic, response: response, tag: WCRequest.Method.sessionPropose.responseTag) try pairing.updateExpiry() pairingStore.setPairing(pairing) @@ -100,7 +99,7 @@ final class ApproveEngine { throw Errors.proposalPayloadsNotFound } proposalPayloadsStore.delete(forKey: proposerPubKey) - try await networkingInteractor.respondError(payload: payload, reason: reason) + try await networkingInteractor.respondError(topic: payload.topic, requestId: payload.id, tag: WCRequest.Method.sessionPropose.responseTag, reason: reason) // TODO: Delete pairing if inactive } @@ -141,7 +140,8 @@ final class ApproveEngine { try await networkingInteractor.subscribe(topic: topic) sessionStore.setSession(session) - try await networkingInteractor.request(.wcSessionSettle(settleParams), onTopic: topic) + let request = RPCRequest(method: WCRequest.Method.sessionSettle.method, params: WCRequest.sessionSettle(settleParams)) + try await networkingInteractor.request(request, topic: topic, tag: WCRequest.Method.sessionSettle.requestTag) onSessionSettle?(session.publicRepresentation()) } } @@ -151,40 +151,34 @@ final class ApproveEngine { private extension ApproveEngine { func setupNetworkingSubscriptions() { - networkingInteractor.responsePublisher - .sink { [unowned self] response in - switch response.requestParams { + networkingInteractor.responseSubscription(on: nil) + .sink { [unowned self] (payload: ResponseSubscriptionPayload) in + switch payload.request { case .sessionPropose(let proposal): - handleSessionProposeResponse(response: response, proposal: proposal) + handleSessionProposeResponse(payload: payload, proposal: proposal) case .sessionSettle: - handleSessionSettleResponse(response: response) + handleSessionSettleResponse(payload: payload) default: break } }.store(in: &publishers) - networkingInteractor.wcRequestPublisher - .sink { [unowned self] subscriptionPayload in - do { - switch subscriptionPayload.wcRequest.params { - case .sessionPropose(let proposal): - try handleSessionProposeRequest(payload: subscriptionPayload, proposal: proposal) - case .sessionSettle(let settleParams): - try handleSessionSettleRequest(payload: subscriptionPayload, settleParams: settleParams) - default: return - } - } catch Errors.respondError(let payload, let reason) { - respondError(payload: payload, reason: reason) - } catch { - logger.error("Unexpected Error: \(error.localizedDescription)") + networkingInteractor.requestSubscription(on: nil) + .sink { [unowned self] (payload: RequestSubscriptionPayload) in + switch payload.request { + case .sessionPropose(let proposal): + handleSessionProposeRequest(payload: payload, proposal: proposal) + case .sessionSettle(let params): + handleSessionSettleRequest(payload: payload, params: params) + default: return } }.store(in: &publishers) } - func respondError(payload: SubscriptionPayload, reason: ReasonCode) { + func respondError(payload: SubscriptionPayload, reason: ReasonCode, tag: Int) { Task { do { - try await networkingInteractor.respondError(topic: payload.topic, requestId: payload.id, tag: <#T##Int#>, reason: reason) + try await networkingInteractor.respondError(topic: payload.topic, requestId: payload.id, tag: tag, reason: reason) } catch { logger.error("Respond Error failed with: \(error.localizedDescription)") } @@ -199,12 +193,12 @@ private extension ApproveEngine { // MARK: SessionProposeResponse // TODO: Move to Non-Controller SettleEngine - func handleSessionProposeResponse(response: WCResponse, proposal: SessionType.ProposeParams) { + func handleSessionProposeResponse(payload: ResponseSubscriptionPayload, proposal: SessionType.ProposeParams) { do { let sessionTopic = try handleProposeResponse( - pairingTopic: response.topic, + pairingTopic: payload.topic, proposal: proposal, - result: response.result + result: payload.response ) settlingProposal = proposal @@ -260,48 +254,55 @@ private extension ApproveEngine { // MARK: SessionSettleResponse - func handleSessionSettleResponse(response: WCResponse) { - guard let session = sessionStore.getSession(forTopic: response.topic) else { return } - switch response.result { + func handleSessionSettleResponse(payload: ResponseSubscriptionPayload) { + guard let session = sessionStore.getSession(forTopic: payload.topic) else { return } + switch payload.response { case .response: logger.debug("Received session settle response") - guard var session = sessionStore.getSession(forTopic: response.topic) else { return } + guard var session = sessionStore.getSession(forTopic: payload.topic) else { return } session.acknowledge() sessionStore.setSession(session) case .error(let error): logger.error("Error - session rejected, Reason: \(error)") - networkingInteractor.unsubscribe(topic: response.topic) - sessionStore.delete(topic: response.topic) - kms.deleteAgreementSecret(for: response.topic) + networkingInteractor.unsubscribe(topic: payload.topic) + sessionStore.delete(topic: payload.topic) + kms.deleteAgreementSecret(for: payload.topic) kms.deletePrivateKey(for: session.publicKey!) } } // MARK: SessionProposeRequest - func handleSessionProposeRequest(payload: WCRequestSubscriptionPayload, proposal: SessionType.ProposeParams) throws { + func handleSessionProposeRequest(payload: RequestSubscriptionPayload, proposal: SessionType.ProposeParams) { logger.debug("Received Session Proposal") - do { try Namespace.validate(proposal.requiredNamespaces) } catch { throw Errors.respondError(payload: payload, reason: .invalidUpdateRequest) } + do { try Namespace.validate(proposal.requiredNamespaces) } catch { + return respondError(payload: payload, reason: .invalidUpdateRequest, tag: WCRequest.Method.sessionPropose.responseTag) + } proposalPayloadsStore.set(payload, forKey: proposal.proposer.publicKey) onSessionProposal?(proposal.publicRepresentation()) } // MARK: SessionSettleRequest - func handleSessionSettleRequest(payload: WCRequestSubscriptionPayload, settleParams: SessionType.SettleParams) throws { + func handleSessionSettleRequest(payload: RequestSubscriptionPayload, params: SessionType.SettleParams) { logger.debug("Did receive session settle request") - guard let proposedNamespaces = settlingProposal?.requiredNamespaces - else { throw Errors.respondError(payload: payload, reason: .invalidUpdateRequest) } + let tag = WCRequest.Method.sessionSettle.responseTag + + guard let proposedNamespaces = settlingProposal?.requiredNamespaces else { + return respondError(payload: payload, reason: .invalidUpdateRequest, tag: tag) + } settlingProposal = nil - let sessionNamespaces = settleParams.namespaces + let sessionNamespaces = params.namespaces do { try Namespace.validate(sessionNamespaces) try Namespace.validateApproved(sessionNamespaces, against: proposedNamespaces) } catch WalletConnectError.unsupportedNamespace(let reason) { - throw Errors.respondError(payload: payload, reason: reason) + return respondError(payload: payload, reason: reason, tag: tag) + } catch { + return respondError(payload: payload, reason: .invalidUpdateRequest, tag: tag) } let topic = payload.topic @@ -311,20 +312,22 @@ private extension ApproveEngine { metadata: metadata ) if let pairingTopic = try? sessionToPairingTopic.get(key: topic) { - updatePairingMetadata(topic: pairingTopic, metadata: settleParams.controller.metadata) + updatePairingMetadata(topic: pairingTopic, metadata: params.controller.metadata) } let session = WCSession( topic: topic, timestamp: Date(), selfParticipant: selfParticipant, - peerParticipant: settleParams.controller, - settleParams: settleParams, + peerParticipant: params.controller, + settleParams: params, requiredNamespaces: proposedNamespaces, acknowledged: true ) sessionStore.setSession(session) - networkingInteractor.respondSuccess(for: payload) + Task(priority: .high) { + try await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, tag: tag) + } onSessionSettle?(session.publicRepresentation()) } } diff --git a/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift b/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift index d9ec3ac3c..43a1b4b63 100644 --- a/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift @@ -3,6 +3,7 @@ import Combine import WalletConnectPairing import WalletConnectUtils import WalletConnectKMS +import WalletConnectNetworking final class PairingEngine { diff --git a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift index df321e01a..35934cf24 100644 --- a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift @@ -7,7 +7,6 @@ import WalletConnectNetworking final class SessionEngine { enum Errors: Error { - case respondError(payload: SubscriptionPayload, reason: ReasonCode) case sessionNotFound(topic: String) } diff --git a/Sources/WalletConnectSign/Engine/Controller/PairEngine.swift b/Sources/WalletConnectSign/Engine/Controller/PairEngine.swift index 9ad8956b7..81b4465d1 100644 --- a/Sources/WalletConnectSign/Engine/Controller/PairEngine.swift +++ b/Sources/WalletConnectSign/Engine/Controller/PairEngine.swift @@ -1,6 +1,7 @@ import Foundation import WalletConnectKMS import WalletConnectPairing +import WalletConnectNetworking actor PairEngine { private let networkingInteractor: NetworkInteracting diff --git a/Tests/TestingUtils/NetworkingInteractorMock.swift b/Tests/TestingUtils/NetworkingInteractorMock.swift index 10f42fc08..0ddc96adc 100644 --- a/Tests/TestingUtils/NetworkingInteractorMock.swift +++ b/Tests/TestingUtils/NetworkingInteractorMock.swift @@ -25,9 +25,13 @@ public class NetworkingInteractorMock: NetworkInteracting { responsePublisherSubject.eraseToAnyPublisher() } - public func requestSubscription(on request: ProtocolMethod) -> AnyPublisher, Never> { + // TODO: Avoid copy paste from NetworkInteractor + public func requestSubscription(on request: ProtocolMethod?) -> AnyPublisher, Never> { return requestPublisher - .filter { $0.request.method == request.method } + .filter { rpcRequest in + guard let request = request else { return true } + return rpcRequest.request.method == request.method + } .compactMap { topic, rpcRequest in guard let id = rpcRequest.id, let request = try? rpcRequest.params?.get(Request.self) else { return nil } return RequestSubscriptionPayload(id: id, topic: topic, request: request) @@ -35,9 +39,13 @@ public class NetworkingInteractorMock: NetworkInteracting { .eraseToAnyPublisher() } - public func responseSubscription(on request: ProtocolMethod) -> AnyPublisher, Never> { + // TODO: Avoid copy paste from NetworkInteractor + public func responseSubscription(on request: ProtocolMethod?) -> AnyPublisher, Never> { return responsePublisher - .filter { $0.request.method == request.method } + .filter { rpcRequest in + guard let request = request else { return true } + return rpcRequest.request.method == request.method + } .compactMap { topic, rpcRequest, rpcResponse in guard let id = rpcRequest.id, From 17fbd90c454adc200bdae47b92a4a5636f0b6f83 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 8 Sep 2022 04:00:32 +0300 Subject: [PATCH 031/175] Sign: Networking package imported --- .../Sign/SignClientTests.swift | 20 +-- .../Services/App/AppRespondSubscriber.swift | 2 +- Sources/JSONRPC/RPCID.swift | 10 ++ Sources/JSONRPC/RPCResponse.swift | 38 +++--- Sources/JSONRPC/RPCResult.swift | 39 ++++++ .../NetworkInteracting.swift | 8 +- .../NetworkInteractor.swift | 14 +- .../ResponseSubscriptionErrorPayload.swift | 8 +- Sources/WalletConnectRelay/RelayClient.swift | 4 +- .../Engine/Common/ApproveEngine.swift | 98 +++++++------- .../Engine/Common/DeletePairingService.swift | 5 +- .../Engine/Common/DeleteSessionService.swift | 5 +- .../Engine/Common/PairingEngine.swift | 65 +++++----- .../Engine/Common/SessionEngine.swift | 122 +++++++++--------- .../ControllerSessionStateMachine.swift | 52 ++++---- .../NonControllerSessionStateMachine.swift | 67 +++++----- .../JsonRpcHistory/JsonRpcHistory.swift | 63 --------- .../JsonRpcHistory/JsonRpcRecord.swift | 15 --- Sources/WalletConnectSign/Response.swift | 3 +- Sources/WalletConnectSign/Sign/Sign.swift | 6 +- .../WalletConnectSign/Sign/SignClient.swift | 34 ++--- .../Sign/SignClientFactory.swift | 9 +- .../Types/Session/SessionType.swift | 15 +-- .../Types/SignProtocolMethod.swift | 72 +++++++++++ .../WalletConnectSign/Types/WCRequest.swift | 85 ------------ .../JSONRPC/JSONRPCErrorResponse.swift | 27 ---- .../JSONRPC/JSONRPCRequest.swift | 23 ---- .../JSONRPC/JSONRPCResponse.swift | 18 --- .../JSONRPC/JsonRpcResult.swift | 24 ---- .../WalletConnectUtils/JsonRpcHistory.swift | 58 --------- .../WalletConnectUtils/JsonRpcRecord.swift | 27 ---- .../NetworkingInteractorMock.swift | 15 +-- 32 files changed, 426 insertions(+), 625 deletions(-) create mode 100644 Sources/JSONRPC/RPCResult.swift delete mode 100644 Sources/WalletConnectSign/JsonRpcHistory/JsonRpcHistory.swift delete mode 100644 Sources/WalletConnectSign/JsonRpcHistory/JsonRpcRecord.swift create mode 100644 Sources/WalletConnectSign/Types/SignProtocolMethod.swift delete mode 100644 Sources/WalletConnectSign/Types/WCRequest.swift delete mode 100644 Sources/WalletConnectUtils/JSONRPC/JSONRPCErrorResponse.swift delete mode 100644 Sources/WalletConnectUtils/JSONRPC/JSONRPCRequest.swift delete mode 100644 Sources/WalletConnectUtils/JSONRPC/JSONRPCResponse.swift delete mode 100644 Sources/WalletConnectUtils/JSONRPC/JsonRpcResult.swift delete mode 100644 Sources/WalletConnectUtils/JsonRpcHistory.swift delete mode 100644 Sources/WalletConnectUtils/JsonRpcRecord.swift diff --git a/Example/IntegrationTests/Sign/SignClientTests.swift b/Example/IntegrationTests/Sign/SignClientTests.swift index e00529b0b..3dd004d78 100644 --- a/Example/IntegrationTests/Sign/SignClientTests.swift +++ b/Example/IntegrationTests/Sign/SignClientTests.swift @@ -1,5 +1,6 @@ import XCTest import WalletConnectUtils +import JSONRPC @testable import WalletConnectKMS @testable import WalletConnectSign @testable import WalletConnectRelay @@ -171,7 +172,7 @@ final class SignClientTests: XCTestCase { } dapp.onSessionSettled = { [unowned self] settledSession in Task(priority: .high) { - let request = Request(id: 0, topic: settledSession.topic, method: requestMethod, params: requestParams, chainId: chain) + let request = Request(id: RPCID(0), topic: settledSession.topic, method: requestMethod, params: requestParams, chainId: chain) try await dapp.client.request(params: request) } } @@ -181,14 +182,13 @@ final class SignClientTests: XCTestCase { XCTAssertEqual(sessionRequest.method, requestMethod) requestExpectation.fulfill() Task(priority: .high) { - let jsonrpcResponse = JSONRPCResponse(id: sessionRequest.id, result: AnyCodable(responseParams)) - try await wallet.client.respond(topic: sessionRequest.topic, response: .response(jsonrpcResponse)) + try await wallet.client.respond(topic: sessionRequest.topic, requestId: sessionRequest.id, response: .response(AnyCodable(responseParams))) } } dapp.onSessionResponse = { response in switch response.result { case .response(let response): - XCTAssertEqual(try! response.result.get(String.self), responseParams) + XCTAssertEqual(try! response.get(String.self), responseParams) case .error: XCTFail() } @@ -207,7 +207,8 @@ final class SignClientTests: XCTestCase { let requestMethod = "eth_sendTransaction" let requestParams = [EthSendTransaction.stub()] - let error = JSONRPCErrorResponse.Error(code: 0, message: "error") + let error = JSONRPCError(code: 0, message: "error") + let chain = Blockchain("eip155:1")! wallet.onSessionProposal = { [unowned self] proposal in @@ -217,22 +218,21 @@ final class SignClientTests: XCTestCase { } dapp.onSessionSettled = { [unowned self] settledSession in Task(priority: .high) { - let request = Request(id: 0, topic: settledSession.topic, method: requestMethod, params: requestParams, chainId: chain) + let request = Request(id: RPCID(0), topic: settledSession.topic, method: requestMethod, params: requestParams, chainId: chain) try await dapp.client.request(params: request) } } wallet.onSessionRequest = { [unowned self] sessionRequest in Task(priority: .high) { - let response = JSONRPCErrorResponse(id: sessionRequest.id, error: error) - try await wallet.client.respond(topic: sessionRequest.topic, response: .error(response)) + try await wallet.client.respond(topic: sessionRequest.topic, requestId: sessionRequest.id, response: .error(error)) } } dapp.onSessionResponse = { response in switch response.result { case .response: XCTFail() - case .error(let errorResponse): - XCTAssertEqual(error, errorResponse.error) + case .error(let receivedError): + XCTAssertEqual(error, receivedError) } expectation.fulfill() } diff --git a/Sources/Auth/Services/App/AppRespondSubscriber.swift b/Sources/Auth/Services/App/AppRespondSubscriber.swift index 2b3b353fa..a0e9f8c40 100644 --- a/Sources/Auth/Services/App/AppRespondSubscriber.swift +++ b/Sources/Auth/Services/App/AppRespondSubscriber.swift @@ -33,7 +33,7 @@ class AppRespondSubscriber { private func subscribeForResponse() { networkingInteractor.responseErrorSubscription(on: AuthProtocolMethod.authRequest) - .sink { [unowned self] payload in + .sink { [unowned self] (payload: ResponseSubscriptionErrorPayload) in guard let error = AuthError(code: payload.error.code) else { return } onResponse?(payload.id, .failure(error)) }.store(in: &publishers) diff --git a/Sources/JSONRPC/RPCID.swift b/Sources/JSONRPC/RPCID.swift index ff0ef3c83..21dcfb682 100644 --- a/Sources/JSONRPC/RPCID.swift +++ b/Sources/JSONRPC/RPCID.swift @@ -1,3 +1,4 @@ +import Foundation import Commons import Foundation @@ -15,3 +16,12 @@ struct IntIdentifierGenerator: IdentifierGenerator { return RPCID(timestamp + random) } } + +extension RPCID { + + public var timestamp: Date { + guard let id = self.right else { return .distantPast } + let interval = TimeInterval(id / 1000 / 1000) + return Date(timeIntervalSince1970: interval) + } +} diff --git a/Sources/JSONRPC/RPCResponse.swift b/Sources/JSONRPC/RPCResponse.swift index ad6ba9ca6..d38ef9c49 100644 --- a/Sources/JSONRPC/RPCResponse.swift +++ b/Sources/JSONRPC/RPCResponse.swift @@ -10,65 +10,65 @@ public struct RPCResponse: Equatable { public let id: RPCID? public var result: AnyCodable? { - if case .success(let value) = outcome { return value } + if case .response(let value) = outcome { return value } return nil } public var error: JSONRPCError? { - if case .failure(let error) = outcome { return error } + if case .error(let error) = outcome { return error } return nil } - public let outcome: Result + public let outcome: RPCResult - internal init(id: RPCID?, outcome: Result) { + internal init(id: RPCID?, outcome: RPCResult) { self.jsonrpc = "2.0" self.id = id self.outcome = outcome } public init(matchingRequest: RPCRequest, result: C) where C: Codable { - self.init(id: matchingRequest.id, outcome: .success(AnyCodable(result))) + self.init(id: matchingRequest.id, outcome: .response(AnyCodable(result))) } public init(matchingRequest: RPCRequest, error: JSONRPCError) { - self.init(id: matchingRequest.id, outcome: .failure(error)) + self.init(id: matchingRequest.id, outcome: .error(error)) } public init(id: Int64, result: C) where C: Codable { - self.init(id: RPCID(id), outcome: .success(AnyCodable(result))) + self.init(id: RPCID(id), outcome: .response(AnyCodable(result))) } public init(id: String, result: C) where C: Codable { - self.init(id: RPCID(id), outcome: .success(AnyCodable(result))) + self.init(id: RPCID(id), outcome: .response(AnyCodable(result))) } public init(id: RPCID, result: C) where C: Codable { - self.init(id: id, outcome: .success(AnyCodable(result))) + self.init(id: id, outcome: .response(AnyCodable(result))) } public init(id: RPCID?, error: JSONRPCError) { - self.init(id: id, outcome: .failure(error)) + self.init(id: id, outcome: .error(error)) } public init(id: Int64, error: JSONRPCError) { - self.init(id: RPCID(id), outcome: .failure(error)) + self.init(id: RPCID(id), outcome: .error(error)) } public init(id: String, error: JSONRPCError) { - self.init(id: RPCID(id), outcome: .failure(error)) + self.init(id: RPCID(id), outcome: .error(error)) } public init(id: Int64, errorCode: Int, message: String, associatedData: AnyCodable? = nil) { - self.init(id: RPCID(id), outcome: .failure(JSONRPCError(code: errorCode, message: message, data: associatedData))) + self.init(id: RPCID(id), outcome: .error(JSONRPCError(code: errorCode, message: message, data: associatedData))) } public init(id: String, errorCode: Int, message: String, associatedData: AnyCodable? = nil) { - self.init(id: RPCID(id), outcome: .failure(JSONRPCError(code: errorCode, message: message, data: associatedData))) + self.init(id: RPCID(id), outcome: .error(JSONRPCError(code: errorCode, message: message, data: associatedData))) } public init(errorWithoutID: JSONRPCError) { - self.init(id: nil, outcome: .failure(errorWithoutID)) + self.init(id: nil, outcome: .error(errorWithoutID)) } } @@ -104,9 +104,9 @@ extension RPCResponse: Codable { codingPath: [CodingKeys.result, CodingKeys.id], debugDescription: "A success response must have a valid `id`.")) } - outcome = .success(result) + outcome = .response(result) } else if let error = error { - outcome = .failure(error) + outcome = .error(error) } else { throw DecodingError.dataCorrupted(.init( codingPath: [CodingKeys.result, CodingKeys.error], @@ -119,9 +119,9 @@ extension RPCResponse: Codable { try container.encode(jsonrpc, forKey: .jsonrpc) try container.encode(id, forKey: .id) switch outcome { - case .success(let anyCodable): + case .response(let anyCodable): try container.encode(anyCodable, forKey: .result) - case .failure(let rpcError): + case .error(let rpcError): try container.encode(rpcError, forKey: .error) } } diff --git a/Sources/JSONRPC/RPCResult.swift b/Sources/JSONRPC/RPCResult.swift new file mode 100644 index 000000000..a3b81e094 --- /dev/null +++ b/Sources/JSONRPC/RPCResult.swift @@ -0,0 +1,39 @@ +import Foundation +import Commons + +public enum RPCResult: Codable, Equatable { + enum Errors: Error { + case decoding + } + + case response(AnyCodable) + case error(JSONRPCError) + + public var value: Codable { + switch self { + case .response(let value): + return value + case .error(let value): + return value + } + } + + public init(from decoder: Decoder) throws { + if let value = try? JSONRPCError(from: decoder) { + self = .error(value) + } else if let value = try? AnyCodable(from: decoder) { + self = .response(value) + } else { + throw Errors.decoding + } + } + + public func encode(to encoder: Encoder) throws { + switch self { + case .error(let value): + try value.encode(to: encoder) + case .response(let value): + try value.encode(to: encoder) + } + } +} diff --git a/Sources/WalletConnectNetworking/NetworkInteracting.swift b/Sources/WalletConnectNetworking/NetworkInteracting.swift index 67d5e9673..85ca9177e 100644 --- a/Sources/WalletConnectNetworking/NetworkInteracting.swift +++ b/Sources/WalletConnectNetworking/NetworkInteracting.swift @@ -15,14 +15,16 @@ public protocol NetworkInteracting { func respondError(topic: String, requestId: RPCID, tag: Int, reason: Reason, envelopeType: Envelope.EnvelopeType) async throws func requestSubscription( - on request: ProtocolMethod? + on request: ProtocolMethod ) -> AnyPublisher, Never> func responseSubscription( - on request: ProtocolMethod? + on request: ProtocolMethod ) -> AnyPublisher, Never> - func responseErrorSubscription(on request: ProtocolMethod) -> AnyPublisher + func responseErrorSubscription( + on request: ProtocolMethod + ) -> AnyPublisher, Never> } extension NetworkInteracting { diff --git a/Sources/WalletConnectNetworking/NetworkInteractor.swift b/Sources/WalletConnectNetworking/NetworkInteractor.swift index f5ed34273..405533ef1 100644 --- a/Sources/WalletConnectNetworking/NetworkInteractor.swift +++ b/Sources/WalletConnectNetworking/NetworkInteractor.swift @@ -56,10 +56,9 @@ public class NetworkingInteractor: NetworkInteracting { } } - public func requestSubscription(on request: ProtocolMethod?) -> AnyPublisher, Never> { + public func requestSubscription(on request: ProtocolMethod) -> AnyPublisher, Never> { return requestPublisher .filter { rpcRequest in - guard let request = request else { return true } return rpcRequest.request.method == request.method } .compactMap { topic, rpcRequest in @@ -69,10 +68,9 @@ public class NetworkingInteractor: NetworkInteracting { .eraseToAnyPublisher() } - public func responseSubscription(on request: ProtocolMethod?) -> AnyPublisher, Never> { + public func responseSubscription(on request: ProtocolMethod) -> AnyPublisher, Never> { return responsePublisher .filter { rpcRequest in - guard let request = request else { return true } return rpcRequest.request.method == request.method } .compactMap { topic, rpcRequest, rpcResponse in @@ -85,12 +83,12 @@ public class NetworkingInteractor: NetworkInteracting { .eraseToAnyPublisher() } - public func responseErrorSubscription(on request: ProtocolMethod) -> AnyPublisher { + public func responseErrorSubscription(on request: ProtocolMethod) -> AnyPublisher, Never> { return responsePublisher .filter { $0.request.method == request.method } - .compactMap { (_, _, rpcResponse) in - guard let id = rpcResponse.id, let error = rpcResponse.error else { return nil } - return ResponseSubscriptionErrorPayload(id: id, error: error) + .compactMap { (topic, rpcRequest, rpcResponse) in + guard let id = rpcResponse.id, let request = try? rpcRequest.params?.get(Request.self), let error = rpcResponse.error else { return nil } + return ResponseSubscriptionErrorPayload(id: id, topic: topic, request: request, error: error) } .eraseToAnyPublisher() } diff --git a/Sources/WalletConnectNetworking/ResponseSubscriptionErrorPayload.swift b/Sources/WalletConnectNetworking/ResponseSubscriptionErrorPayload.swift index 8b38df244..589ebbcc2 100644 --- a/Sources/WalletConnectNetworking/ResponseSubscriptionErrorPayload.swift +++ b/Sources/WalletConnectNetworking/ResponseSubscriptionErrorPayload.swift @@ -1,12 +1,16 @@ import Foundation import JSONRPC -public struct ResponseSubscriptionErrorPayload { +public struct ResponseSubscriptionErrorPayload: Codable, SubscriptionPayload { public let id: RPCID + public let topic: String + public let request: Request public let error: JSONRPCError - public init(id: RPCID, error: JSONRPCError) { + public init(id: RPCID, topic: String, request: Request, error: JSONRPCError) { self.id = id + self.topic = topic + self.request = request self.error = error } } diff --git a/Sources/WalletConnectRelay/RelayClient.swift b/Sources/WalletConnectRelay/RelayClient.swift index 8ac2c7ea0..9aa2ba8a4 100644 --- a/Sources/WalletConnectRelay/RelayClient.swift +++ b/Sources/WalletConnectRelay/RelayClient.swift @@ -247,13 +247,13 @@ public final class RelayClient { } } else if let response = tryDecode(RPCResponse.self, from: payload) { switch response.outcome { - case .success(let anyCodable): + case .response(let anyCodable): if let _ = try? anyCodable.get(Bool.self) { // TODO: Handle success vs. error requestAcknowledgePublisherSubject.send(response.id) } else if let subscriptionId = try? anyCodable.get(String.self) { subscriptionResponsePublisherSubject.send((response.id, subscriptionId)) } - case .failure(let rpcError): + case .error(let rpcError): logger.error("Received RPC error from relay network: \(rpcError)") } } else { diff --git a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift index e75cc8bde..e29813c63 100644 --- a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift @@ -16,7 +16,7 @@ final class ApproveEngine { } var onSessionProposal: ((Session.Proposal) -> Void)? - var onSessionRejected: ((Session.Proposal, SessionType.Reason) -> Void)? + var onSessionRejected: ((Session.Proposal, Reason) -> Void)? var onSessionSettle: ((Session) -> Void)? var settlingProposal: SessionProposal? @@ -24,7 +24,7 @@ final class ApproveEngine { private let networkingInteractor: NetworkInteracting private let pairingStore: WCPairingStorage private let sessionStore: WCSessionStorage - private let proposalPayloadsStore: CodableStore> + private let proposalPayloadsStore: CodableStore> private let sessionToPairingTopic: CodableStore private let metadata: AppMetadata private let kms: KeyManagementServiceProtocol @@ -34,7 +34,7 @@ final class ApproveEngine { init( networkingInteractor: NetworkInteracting, - proposalPayloadsStore: CodableStore>, + proposalPayloadsStore: CodableStore>, sessionToPairingTopic: CodableStore, metadata: AppMetadata, kms: KeyManagementServiceProtocol, @@ -51,16 +51,17 @@ final class ApproveEngine { self.pairingStore = pairingStore self.sessionStore = sessionStore - setupNetworkingSubscriptions() + setupRequestSubscriptions() + setupResponseSubscriptions() } func approveProposal(proposerPubKey: String, validating sessionNamespaces: [String: SessionNamespace]) async throws { - let payload = try proposalPayloadsStore.get(key: proposerPubKey) - - guard let payload = payload, case .sessionPropose(let proposal) = payload.request else { + guard let payload = try proposalPayloadsStore.get(key: proposerPubKey) else { throw Errors.wrongRequestParams } + let proposal = payload.request + proposalPayloadsStore.delete(forKey: proposerPubKey) try Namespace.validate(sessionNamespaces) @@ -86,7 +87,7 @@ final class ApproveEngine { let result = SessionType.ProposeResponse(relay: relay, responderPublicKey: selfPublicKey.hexRepresentation) let response = RPCResponse(id: payload.id, result: result) - try await networkingInteractor.respond(topic: payload.topic, response: response, tag: WCRequest.Method.sessionPropose.responseTag) + try await networkingInteractor.respond(topic: payload.topic, response: response, tag: SignProtocolMethod.sessionPropose.responseTag) try pairing.updateExpiry() pairingStore.setPairing(pairing) @@ -99,7 +100,7 @@ final class ApproveEngine { throw Errors.proposalPayloadsNotFound } proposalPayloadsStore.delete(forKey: proposerPubKey) - try await networkingInteractor.respondError(topic: payload.topic, requestId: payload.id, tag: WCRequest.Method.sessionPropose.responseTag, reason: reason) + try await networkingInteractor.respondError(topic: payload.topic, requestId: payload.id, tag: SignProtocolMethod.sessionPropose.responseTag, reason: reason) // TODO: Delete pairing if inactive } @@ -140,8 +141,8 @@ final class ApproveEngine { try await networkingInteractor.subscribe(topic: topic) sessionStore.setSession(session) - let request = RPCRequest(method: WCRequest.Method.sessionSettle.method, params: WCRequest.sessionSettle(settleParams)) - try await networkingInteractor.request(request, topic: topic, tag: WCRequest.Method.sessionSettle.requestTag) + let request = RPCRequest(method: SignProtocolMethod.sessionSettle.method, params: settleParams) + try await networkingInteractor.request(request, topic: topic, tag: SignProtocolMethod.sessionSettle.requestTag) onSessionSettle?(session.publicRepresentation()) } } @@ -150,28 +151,35 @@ final class ApproveEngine { private extension ApproveEngine { - func setupNetworkingSubscriptions() { - networkingInteractor.responseSubscription(on: nil) - .sink { [unowned self] (payload: ResponseSubscriptionPayload) in - switch payload.request { - case .sessionPropose(let proposal): - handleSessionProposeResponse(payload: payload, proposal: proposal) - case .sessionSettle: - handleSessionSettleResponse(payload: payload) - default: - break - } + func setupRequestSubscriptions() { + networkingInteractor.requestSubscription(on: SignProtocolMethod.sessionPropose) + .sink { [unowned self] (payload: RequestSubscriptionPayload) in + handleSessionProposeRequest(payload: payload) + }.store(in: &publishers) + + networkingInteractor.requestSubscription(on: SignProtocolMethod.sessionSettle) + .sink { [unowned self] (payload: RequestSubscriptionPayload) in + handleSessionSettleRequest(payload: payload) + }.store(in: &publishers) + } + + func setupResponseSubscriptions() { + networkingInteractor.responseSubscription(on: SignProtocolMethod.sessionPropose) + .sink { [unowned self] (payload: ResponseSubscriptionPayload) in + handleSessionProposeResponse(payload: payload) + }.store(in: &publishers) + + networkingInteractor.responseSubscription(on: SignProtocolMethod.sessionSettle) + .sink { [unowned self] (payload: ResponseSubscriptionPayload) in + handleSessionSettleResponse(payload: payload) }.store(in: &publishers) - networkingInteractor.requestSubscription(on: nil) - .sink { [unowned self] (payload: RequestSubscriptionPayload) in - switch payload.request { - case .sessionPropose(let proposal): - handleSessionProposeRequest(payload: payload, proposal: proposal) - case .sessionSettle(let params): - handleSessionSettleRequest(payload: payload, params: params) - default: return - } + networkingInteractor.responseErrorSubscription(on: SignProtocolMethod.sessionPropose) + .sink { [unowned self] (payload: ResponseSubscriptionErrorPayload) in + onSessionRejected?( + payload.request.publicRepresentation(), + SessionType.Reason(code: payload.error.code, message: payload.error.message) + ) }.store(in: &publishers) } @@ -193,27 +201,27 @@ private extension ApproveEngine { // MARK: SessionProposeResponse // TODO: Move to Non-Controller SettleEngine - func handleSessionProposeResponse(payload: ResponseSubscriptionPayload, proposal: SessionType.ProposeParams) { + func handleSessionProposeResponse(payload: ResponseSubscriptionPayload) { do { let sessionTopic = try handleProposeResponse( pairingTopic: payload.topic, - proposal: proposal, + proposal: payload.request, result: payload.response ) - settlingProposal = proposal + settlingProposal = payload.request Task(priority: .high) { - try? await networkingInteractor.subscribe(topic: sessionTopic) + try await networkingInteractor.subscribe(topic: sessionTopic) } } catch { - guard let error = error as? JSONRPCErrorResponse else { + guard let error = error as? Reason else { return logger.debug(error.localizedDescription) } - onSessionRejected?(proposal.publicRepresentation(), SessionType.Reason(code: error.error.code, message: error.error.message)) + onSessionRejected?(payload.request.publicRepresentation(), SessionType.Reason(code: error.code, message: error.message)) } } - func handleProposeResponse(pairingTopic: String, proposal: SessionProposal, result: JsonRpcResult) throws -> String { + func handleProposeResponse(pairingTopic: String, proposal: SessionProposal, result: RPCResult) throws -> String { guard var pairing = pairingStore.getPairing(forTopic: pairingTopic) else { throw Errors.pairingNotFound } @@ -229,7 +237,7 @@ private extension ApproveEngine { pairingStore.setPairing(pairing) let selfPublicKey = try AgreementPublicKey(hex: proposal.proposer.publicKey) - let proposeResponse = try response.result.get(SessionType.ProposeResponse.self) + let proposeResponse = try response.get(SessionType.ProposeResponse.self) let agreementKeys = try kms.performKeyAgreement(selfPublicKey: selfPublicKey, peerPublicKey: proposeResponse.responderPublicKey) let sessionTopic = agreementKeys.derivedTopic() @@ -254,7 +262,7 @@ private extension ApproveEngine { // MARK: SessionSettleResponse - func handleSessionSettleResponse(payload: ResponseSubscriptionPayload) { + func handleSessionSettleResponse(payload: ResponseSubscriptionPayload) { guard let session = sessionStore.getSession(forTopic: payload.topic) else { return } switch payload.response { case .response: @@ -273,20 +281,21 @@ private extension ApproveEngine { // MARK: SessionProposeRequest - func handleSessionProposeRequest(payload: RequestSubscriptionPayload, proposal: SessionType.ProposeParams) { + func handleSessionProposeRequest(payload: RequestSubscriptionPayload) { logger.debug("Received Session Proposal") + let proposal = payload.request do { try Namespace.validate(proposal.requiredNamespaces) } catch { - return respondError(payload: payload, reason: .invalidUpdateRequest, tag: WCRequest.Method.sessionPropose.responseTag) + return respondError(payload: payload, reason: .invalidUpdateRequest, tag: SignProtocolMethod.sessionPropose.responseTag) } proposalPayloadsStore.set(payload, forKey: proposal.proposer.publicKey) onSessionProposal?(proposal.publicRepresentation()) } // MARK: SessionSettleRequest - func handleSessionSettleRequest(payload: RequestSubscriptionPayload, params: SessionType.SettleParams) { + func handleSessionSettleRequest(payload: RequestSubscriptionPayload) { logger.debug("Did receive session settle request") - let tag = WCRequest.Method.sessionSettle.responseTag + let tag = SignProtocolMethod.sessionSettle.responseTag guard let proposedNamespaces = settlingProposal?.requiredNamespaces else { return respondError(payload: payload, reason: .invalidUpdateRequest, tag: tag) @@ -294,6 +303,7 @@ private extension ApproveEngine { settlingProposal = nil + let params = payload.request let sessionNamespaces = params.namespaces do { diff --git a/Sources/WalletConnectSign/Engine/Common/DeletePairingService.swift b/Sources/WalletConnectSign/Engine/Common/DeletePairingService.swift index a6324481e..2dbbede9d 100644 --- a/Sources/WalletConnectSign/Engine/Common/DeletePairingService.swift +++ b/Sources/WalletConnectSign/Engine/Common/DeletePairingService.swift @@ -1,7 +1,9 @@ import Foundation +import JSONRPC import WalletConnectKMS import WalletConnectUtils import WalletConnectPairing +import WalletConnectNetworking class DeletePairingService { private let networkingInteractor: NetworkInteracting @@ -23,7 +25,8 @@ class DeletePairingService { let reasonCode = ReasonCode.userDisconnected let reason = SessionType.Reason(code: reasonCode.code, message: reasonCode.message) logger.debug("Will delete pairing for reason: message: \(reason.message) code: \(reason.code)") - try await networkingInteractor.request(.wcSessionDelete(reason), onTopic: topic) + let request = RPCRequest(method: SignProtocolMethod.sessionDelete.method, params: reason) + try await networkingInteractor.request(request, topic: topic, tag: SignProtocolMethod.sessionDelete.requestTag) pairingStorage.delete(topic: topic) kms.deleteSymmetricKey(for: topic) networkingInteractor.unsubscribe(topic: topic) diff --git a/Sources/WalletConnectSign/Engine/Common/DeleteSessionService.swift b/Sources/WalletConnectSign/Engine/Common/DeleteSessionService.swift index 561af3507..f2ff1442d 100644 --- a/Sources/WalletConnectSign/Engine/Common/DeleteSessionService.swift +++ b/Sources/WalletConnectSign/Engine/Common/DeleteSessionService.swift @@ -1,6 +1,8 @@ import Foundation +import JSONRPC import WalletConnectKMS import WalletConnectUtils +import WalletConnectNetworking class DeleteSessionService { private let networkingInteractor: NetworkInteracting @@ -22,7 +24,8 @@ class DeleteSessionService { let reasonCode = ReasonCode.userDisconnected let reason = SessionType.Reason(code: reasonCode.code, message: reasonCode.message) logger.debug("Will delete session for reason: message: \(reason.message) code: \(reason.code)") - try await networkingInteractor.request(.wcSessionDelete(reason), onTopic: topic) + let request = RPCRequest(method: SignProtocolMethod.sessionDelete.method, params: reason) + try await networkingInteractor.request(request, topic: topic, tag: SignProtocolMethod.sessionDelete.requestTag) sessionStore.delete(topic: topic) kms.deleteSymmetricKey(for: topic) networkingInteractor.unsubscribe(topic: topic) diff --git a/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift b/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift index 43a1b4b63..0561e6446 100644 --- a/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift @@ -1,5 +1,6 @@ import Foundation import Combine +import JSONRPC import WalletConnectPairing import WalletConnectUtils import WalletConnectKMS @@ -70,15 +71,9 @@ final class PairingEngine { relays: [relay], proposer: proposer, requiredNamespaces: namespaces) - return try await withCheckedThrowingContinuation { continuation in - networkingInteractor.requestNetworkAck(.wcSessionPropose(proposal), onTopic: pairingTopic) { error in - if let error = error { - continuation.resume(throwing: error) - } else { - continuation.resume() - } - } - } + + let request = RPCRequest(method: SignProtocolMethod.sessionPropose.method, params: proposal) + try await networkingInteractor.request(request, topic: pairingTopic, tag: SignProtocolMethod.sessionPropose.requestTag) } func ping(topic: String, completion: @escaping ((Result) -> Void)) { @@ -86,15 +81,16 @@ final class PairingEngine { logger.debug("Could not find pairing to ping for topic \(topic)") return } - networkingInteractor.requestPeerResponse(.wcPairingPing, onTopic: topic) { [unowned self] result in - switch result { - case .success: - logger.debug("Did receive ping response") - completion(.success(())) - case .failure(let error): - logger.debug("error: \(error)") - } - } +// TODO: Ping disabled +// networkingInteractor.requestPeerResponse(.wcPairingPing, onTopic: topic) { [unowned self] result in +// switch result { +// case .success: +// logger.debug("Did receive ping response") +// completion(.success(())) +// case .failure(let error): +// logger.debug("error: \(error)") +// } +// } } } @@ -103,26 +99,23 @@ final class PairingEngine { private extension PairingEngine { func setupNetworkingSubscriptions() { - networkingInteractor.transportConnectionPublisher - .sink { [unowned self] (_) in - let topics = pairingStore.getAll() - .map {$0.topic} - topics.forEach { topic in Task {try? await networkingInteractor.subscribe(topic: topic)}} - }.store(in: &publishers) + networkingInteractor.socketConnectionStatusPublisher + .sink { [unowned self] status in + guard status == .connected else { return } + pairingStore.getAll() + .forEach { pairing in + Task(priority: .high) { try await networkingInteractor.subscribe(topic: pairing.topic) } + } + } + .store(in: &publishers) - networkingInteractor.wcRequestPublisher - .sink { [unowned self] subscriptionPayload in - switch subscriptionPayload.wcRequest.params { - case .pairingPing: - wcPairingPing(subscriptionPayload) - default: - return + networkingInteractor.requestSubscription(on: SignProtocolMethod.pairingPing) + .sink { [unowned self] (payload: RequestSubscriptionPayload) in + Task(priority: .high) { + try await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, tag: SignProtocolMethod.pairingPing.responseTag) } - }.store(in: &publishers) - } - - func wcPairingPing(_ payload: WCRequestSubscriptionPayload) { - networkingInteractor.respondSuccess(for: payload) + } + .store(in: &publishers) } func setupExpirationHandling() { diff --git a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift index 35934cf24..0905d4f3d 100644 --- a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift @@ -33,7 +33,9 @@ final class SessionEngine { self.sessionStore = sessionStore self.logger = logger - setupNetworkingSubscriptions() + setupConnectionSubscriptions() + setupRequestSubscriptions() + setupResponseSubscriptions() setupExpirationSubscriptions() } @@ -74,17 +76,16 @@ final class SessionEngine { let chainRequest = SessionType.RequestParams.Request(method: request.method, params: request.params) let sessionRequestParams = SessionType.RequestParams(request: chainRequest, chainId: request.chainId) - let payload = WCRequest.sessionRequest(sessionRequestParams) - let rpcRequest = RPCRequest(method: WCRequest.Method.sessionRequest.method, params: payload) - try await networkingInteractor.request(rpcRequest, topic: request.topic, tag: WCRequest.Method.sessionRequest.requestTag) + let rpcRequest = RPCRequest(method: SignProtocolMethod.sessionRequest.method, params: sessionRequestParams) + try await networkingInteractor.request(rpcRequest, topic: request.topic, tag: SignProtocolMethod.sessionRequest.requestTag) } - func respondSessionRequest(topic: String, response: JsonRpcResult) async throws { + func respondSessionRequest(topic: String, requestId: RPCID, response: RPCResult) async throws { guard sessionStore.hasSession(forTopic: topic) else { throw Errors.sessionNotFound(topic: topic) } - // TODO: ??? -// try await networkingInteractor.respond(topic: topic, response: response, tag: 1109) // FIXME: Hardcoded tag + let response = RPCResponse(id: requestId, result: response) + try await networkingInteractor.respond(topic: topic, response: response, tag: 1109) // FIXME: Hardcoded tag } func emit(topic: String, event: SessionType.EventParams.Event, chainId: Blockchain) async throws { @@ -95,9 +96,8 @@ final class SessionEngine { guard session.hasPermission(forEvent: event.name, onChain: chainId) else { throw WalletConnectError.invalidEvent } - let params = SessionType.EventParams(event: event, chainId: chainId) - let rpcRequest = RPCRequest(method: WCRequest.Method.sessionEvent.method, params: params) - try await networkingInteractor.request(rpcRequest, topic: topic, tag: WCRequest.Method.sessionEvent.requestTag) + let rpcRequest = RPCRequest(method: SignProtocolMethod.sessionEvent.method, params: SessionType.EventParams(event: event, chainId: chainId)) + try await networkingInteractor.request(rpcRequest, topic: topic, tag: SignProtocolMethod.sessionEvent.requestTag) } } @@ -105,7 +105,7 @@ final class SessionEngine { private extension SessionEngine { - func setupNetworkingSubscriptions() { + func setupConnectionSubscriptions() { networkingInteractor.socketConnectionStatusPublisher .sink { [unowned self] status in guard status == .connected else { return } @@ -115,38 +115,49 @@ private extension SessionEngine { } } .store(in: &publishers) + } - networkingInteractor.requestSubscription(on: nil) - .sink { [unowned self] (payload: RequestSubscriptionPayload) in - switch payload.request { - case .sessionDelete(let params): - onSessionDelete(payload, params: params) - case .sessionRequest(let params): - onSessionRequest(payload, params: params) - case .sessionPing: - onSessionPing(payload) - case .sessionEvent(let params): - onSessionEvent(payload, params: params) - default: return - } - } - .store(in: &publishers) + func setupRequestSubscriptions() { + networkingInteractor.requestSubscription(on: SignProtocolMethod.sessionDelete) + .sink { [unowned self] (payload: RequestSubscriptionPayload) in + onSessionDelete(payload: payload) + }.store(in: &publishers) + + networkingInteractor.requestSubscription(on: SignProtocolMethod.sessionRequest) + .sink { [unowned self] (payload: RequestSubscriptionPayload) in + onSessionRequest(payload: payload) + }.store(in: &publishers) + + networkingInteractor.requestSubscription(on: SignProtocolMethod.sessionPing) + .sink { [unowned self] (payload: RequestSubscriptionPayload) in + onSessionPing(payload: payload) + }.store(in: &publishers) + + networkingInteractor.requestSubscription(on: SignProtocolMethod.sessionEvent) + .sink { [unowned self] (payload: RequestSubscriptionPayload) in + onSessionEvent(payload: payload) + }.store(in: &publishers) + } - networkingInteractor.responseSubscription(on: nil) - .sink { [unowned self] (payload: ResponseSubscriptionPayload) in - switch payload.request { - case .sessionRequest(let params): - // TODO: ??? Chain ID from request is ok? - // Need to check absolute string - let response = Response(topic: payload.topic, chainId: params.chainId.absoluteString, result: payload.response) - onSessionResponse?(response) - default: - break - } + func setupResponseSubscriptions() { + networkingInteractor.responseSubscription(on: SignProtocolMethod.sessionRequest) + .sink { [unowned self] (payload: ResponseSubscriptionPayload) in + onSessionResponse?(Response( + topic: payload.topic, + chainId: payload.request.chainId.absoluteString, + result: payload.response + )) } .store(in: &publishers) } + func setupExpirationSubscriptions() { + sessionStore.onSessionExpiration = { [weak self] session in + self?.kms.deletePrivateKey(for: session.selfParticipant.publicKey) + self?.kms.deleteAgreementSecret(for: session.topic) + } + } + func respondError(payload: SubscriptionPayload, reason: ReasonCode, tag: Int) { Task { do { @@ -157,8 +168,8 @@ private extension SessionEngine { } } - func onSessionDelete(_ payload: SubscriptionPayload, params: SessionType.DeleteParams) { - let tag = WCRequest.Method.sessionDelete.responseTag + func onSessionDelete(payload: RequestSubscriptionPayload) { + let tag = SignProtocolMethod.sessionDelete.responseTag let topic = payload.topic guard sessionStore.hasSession(forTopic: topic) else { return respondError(payload: payload, reason: .noSessionForTopic, tag: tag) @@ -168,18 +179,18 @@ private extension SessionEngine { Task(priority: .high) { try await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, tag: tag) } - onSessionDelete?(topic, params) + onSessionDelete?(topic, payload.request) } - func onSessionRequest(_ payload: SubscriptionPayload, params: SessionType.RequestParams) { - let tag = WCRequest.Method.sessionRequest.responseTag + func onSessionRequest(payload: RequestSubscriptionPayload) { + let tag = SignProtocolMethod.sessionRequest.responseTag let topic = payload.topic let request = Request( id: payload.id, topic: payload.topic, - method: WCRequest.Method.sessionRequest.method, - params: params, - chainId: params.chainId) + method: payload.request.request.method, + params: payload.request.request.params, + chainId: payload.request.chainId) guard let session = sessionStore.getSession(forTopic: topic) else { return respondError(payload: payload, reason: .noSessionForTopic, tag: tag) @@ -193,32 +204,25 @@ private extension SessionEngine { onSessionRequest?(request) } - func onSessionPing(_ payload: SubscriptionPayload) { + func onSessionPing(payload: SubscriptionPayload) { Task(priority: .high) { - try await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, tag: WCRequest.Method.sessionPing.responseTag) + try await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, tag: SignProtocolMethod.sessionPing.responseTag) } } - func onSessionEvent(_ payload: SubscriptionPayload, params: SessionType.EventParams) { - let tag = WCRequest.Method.sessionEvent.responseTag - let event = params.event + func onSessionEvent(payload: RequestSubscriptionPayload) { + let tag = SignProtocolMethod.sessionEvent.responseTag + let event = payload.request.event let topic = payload.topic guard let session = sessionStore.getSession(forTopic: topic) else { return respondError(payload: payload, reason: .noSessionForTopic, tag: tag) } - guard session.peerIsController, session.hasPermission(forEvent: event.name, onChain: params.chainId) else { + guard session.peerIsController, session.hasPermission(forEvent: event.name, onChain: payload.request.chainId) else { return respondError(payload: payload, reason: .unauthorizedEvent(event.name), tag: tag) } Task(priority: .high) { try await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, tag: tag) } - onEventReceived?(topic, event.publicRepresentation(), params.chainId) - } - - func setupExpirationSubscriptions() { - sessionStore.onSessionExpiration = { [weak self] session in - self?.kms.deletePrivateKey(for: session.selfParticipant.publicKey) - self?.kms.deleteAgreementSecret(for: session.topic) - } + onEventReceived?(topic, event.publicRepresentation(), payload.request.chainId) } } diff --git a/Sources/WalletConnectSign/Engine/Controller/ControllerSessionStateMachine.swift b/Sources/WalletConnectSign/Engine/Controller/ControllerSessionStateMachine.swift index b76b63d50..be4733984 100644 --- a/Sources/WalletConnectSign/Engine/Controller/ControllerSessionStateMachine.swift +++ b/Sources/WalletConnectSign/Engine/Controller/ControllerSessionStateMachine.swift @@ -1,7 +1,9 @@ import Foundation +import Combine +import JSONRPC import WalletConnectUtils import WalletConnectKMS -import Combine +import WalletConnectNetworking final class ControllerSessionStateMachine { @@ -22,9 +24,8 @@ final class ControllerSessionStateMachine { self.kms = kms self.sessionStore = sessionStore self.logger = logger - networkingInteractor.responsePublisher.sink { [unowned self] response in - handleResponse(response) - }.store(in: &publishers) + + setupSubscriptions() } func update(topic: String, namespaces: [String: SessionNamespace]) async throws { @@ -33,7 +34,8 @@ final class ControllerSessionStateMachine { try Namespace.validate(namespaces) logger.debug("Controller will update methods") sessionStore.setSession(session) - try await networkingInteractor.request(.wcSessionUpdate(SessionType.UpdateParams(namespaces: namespaces)), onTopic: topic) + let request = RPCRequest(method: SignProtocolMethod.sessionUpdate.method, params: SessionType.UpdateParams(namespaces: namespaces)) + try await networkingInteractor.request(request, topic: topic, tag: SignProtocolMethod.sessionUpdate.requestTag) } func extend(topic: String, by ttl: Int64) async throws { @@ -42,28 +44,32 @@ final class ControllerSessionStateMachine { try session.updateExpiry(by: ttl) let newExpiry = Int64(session.expiryDate.timeIntervalSince1970 ) sessionStore.setSession(session) - try await networkingInteractor.request(.wcSessionExtend(SessionType.UpdateExpiryParams(expiry: newExpiry)), onTopic: topic) + let request = RPCRequest(method: SignProtocolMethod.sessionExtend.method, params: SessionType.UpdateExpiryParams(expiry: newExpiry)) + try await networkingInteractor.request(request, topic: topic, tag: SignProtocolMethod.sessionExtend.requestTag) } // MARK: - Handle Response - private func handleResponse(_ response: WCResponse) { - switch response.requestParams { - case .sessionUpdate(let payload): - handleUpdateResponse(response: response, payload: payload) - case .sessionExtend(let payload): - handleUpdateExpiryResponse(response: response, payload: payload) - default: - break - } + private func setupSubscriptions() { + networkingInteractor.responseSubscription(on: SignProtocolMethod.sessionUpdate) + .sink { [unowned self] (payload: ResponseSubscriptionPayload) in + handleUpdateResponse(payload: payload) + } + .store(in: &publishers) + + networkingInteractor.responseSubscription(on: SignProtocolMethod.sessionExtend) + .sink { [unowned self] (payload: ResponseSubscriptionPayload) in + handleUpdateExpiryResponse(payload: payload) + } + .store(in: &publishers) } - private func handleUpdateResponse(response: WCResponse, payload: SessionType.UpdateParams) { - guard var session = sessionStore.getSession(forTopic: response.topic) else { return } - switch response.result { + private func handleUpdateResponse(payload: ResponseSubscriptionPayload) { + guard var session = sessionStore.getSession(forTopic: payload.topic) else { return } + switch payload.response { case .response: do { - try session.updateNamespaces(payload.namespaces, timestamp: response.timestamp) + try session.updateNamespaces(payload.request.namespaces, timestamp: payload.id.timestamp) if sessionStore.setSessionIfNewer(session) { onNamespacesUpdate?(session.topic, session.namespaces) @@ -76,12 +82,12 @@ final class ControllerSessionStateMachine { } } - private func handleUpdateExpiryResponse(response: WCResponse, payload: SessionType.UpdateExpiryParams) { - guard var session = sessionStore.getSession(forTopic: response.topic) else { return } - switch response.result { + private func handleUpdateExpiryResponse(payload: ResponseSubscriptionPayload) { + guard var session = sessionStore.getSession(forTopic: payload.topic) else { return } + switch payload.response { case .response: do { - try session.updateExpiry(to: payload.expiry) + try session.updateExpiry(to: payload.request.expiry) sessionStore.setSession(session) onExtend?(session.topic, session.expiryDate) } catch { diff --git a/Sources/WalletConnectSign/Engine/NonController/NonControllerSessionStateMachine.swift b/Sources/WalletConnectSign/Engine/NonController/NonControllerSessionStateMachine.swift index 0e4e22f66..46ab1c516 100644 --- a/Sources/WalletConnectSign/Engine/NonController/NonControllerSessionStateMachine.swift +++ b/Sources/WalletConnectSign/Engine/NonController/NonControllerSessionStateMachine.swift @@ -1,12 +1,10 @@ import Foundation import WalletConnectUtils import WalletConnectKMS +import WalletConnectNetworking import Combine final class NonControllerSessionStateMachine { - enum Errors: Error { - case respondError(payload: WCRequestSubscriptionPayload, reason: ReasonCode) - } var onNamespacesUpdate: ((String, [String: SessionNamespace]) -> Void)? var onExtend: ((String, Date) -> Void)? @@ -25,32 +23,25 @@ final class NonControllerSessionStateMachine { self.kms = kms self.sessionStore = sessionStore self.logger = logger - setUpWCRequestHandling() + setupSubscriptions() } - private func setUpWCRequestHandling() { - networkingInteractor.wcRequestPublisher - .sink { [unowned self] subscriptionPayload in - do { - switch subscriptionPayload.wcRequest.params { - case .sessionUpdate(let updateParams): - try onSessionUpdateNamespacesRequest(payload: subscriptionPayload, updateParams: updateParams) - case .sessionExtend(let updateExpiryParams): - try onSessionUpdateExpiry(subscriptionPayload, updateExpiryParams: updateExpiryParams) - default: return - } - } catch Errors.respondError(let payload, let reason) { - respondError(payload: payload, reason: reason) - } catch { - logger.error("Unexpected Error: \(error.localizedDescription)") - } + private func setupSubscriptions() { + networkingInteractor.requestSubscription(on: SignProtocolMethod.sessionUpdate) + .sink { [unowned self] (payload: RequestSubscriptionPayload) in + onSessionUpdateNamespacesRequest(payload: payload, updateParams: payload.request) + }.store(in: &publishers) + + networkingInteractor.requestSubscription(on: SignProtocolMethod.sessionExtend) + .sink { [unowned self] (payload: RequestSubscriptionPayload) in + onSessionUpdateExpiry(payload: payload, updateExpiryParams: payload.request) }.store(in: &publishers) } - private func respondError(payload: WCRequestSubscriptionPayload, reason: ReasonCode) { + private func respondError(payload: SubscriptionPayload, reason: ReasonCode, tag: Int) { Task { do { - try await networkingInteractor.respondError(payload: payload, reason: reason) + try await networkingInteractor.respondError(topic: payload.topic, requestId: payload.id, tag: tag, reason: reason) } catch { logger.error("Respond Error failed with: \(error.localizedDescription)") } @@ -58,43 +49,51 @@ final class NonControllerSessionStateMachine { } // TODO: Update stored session namespaces - private func onSessionUpdateNamespacesRequest(payload: WCRequestSubscriptionPayload, updateParams: SessionType.UpdateParams) throws { + private func onSessionUpdateNamespacesRequest(payload: SubscriptionPayload, updateParams: SessionType.UpdateParams) { do { try Namespace.validate(updateParams.namespaces) } catch { - throw Errors.respondError(payload: payload, reason: .invalidUpdateRequest) + return respondError(payload: payload, reason: .invalidUpdateRequest, tag: SignProtocolMethod.sessionUpdate.responseTag) } guard var session = sessionStore.getSession(forTopic: payload.topic) else { - throw Errors.respondError(payload: payload, reason: .noSessionForTopic) + return respondError(payload: payload, reason: .noSessionForTopic, tag: SignProtocolMethod.sessionUpdate.responseTag) } guard session.peerIsController else { - throw Errors.respondError(payload: payload, reason: .unauthorizedUpdateRequest) + return respondError(payload: payload, reason: .unauthorizedUpdateRequest, tag: SignProtocolMethod.sessionUpdate.responseTag) } do { - try session.updateNamespaces(updateParams.namespaces, timestamp: payload.timestamp) + try session.updateNamespaces(updateParams.namespaces, timestamp: payload.id.timestamp) } catch { - throw Errors.respondError(payload: payload, reason: .invalidUpdateRequest) + return respondError(payload: payload, reason: .invalidUpdateRequest, tag: SignProtocolMethod.sessionUpdate.responseTag) } sessionStore.setSession(session) - networkingInteractor.respondSuccess(for: payload) + + Task(priority: .high) { + try await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, tag: SignProtocolMethod.sessionUpdate.responseTag) + } + onNamespacesUpdate?(session.topic, updateParams.namespaces) } - private func onSessionUpdateExpiry(_ payload: WCRequestSubscriptionPayload, updateExpiryParams: SessionType.UpdateExpiryParams) throws { + private func onSessionUpdateExpiry(payload: SubscriptionPayload, updateExpiryParams: SessionType.UpdateExpiryParams) { let topic = payload.topic guard var session = sessionStore.getSession(forTopic: topic) else { - throw Errors.respondError(payload: payload, reason: .noSessionForTopic) + return respondError(payload: payload, reason: .noSessionForTopic, tag: SignProtocolMethod.sessionExtend.responseTag) } guard session.peerIsController else { - throw Errors.respondError(payload: payload, reason: .unauthorizedExtendRequest) + return respondError(payload: payload, reason: .unauthorizedExtendRequest, tag: SignProtocolMethod.sessionExtend.responseTag) } do { try session.updateExpiry(to: updateExpiryParams.expiry) } catch { - throw Errors.respondError(payload: payload, reason: .invalidExtendRequest) + return respondError(payload: payload, reason: .invalidExtendRequest, tag: SignProtocolMethod.sessionExtend.responseTag) } sessionStore.setSession(session) - networkingInteractor.respondSuccess(for: payload) + + Task(priority: .high) { + try await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, tag: SignProtocolMethod.sessionExtend.responseTag) + } + onExtend?(session.topic, session.expiryDate) } } diff --git a/Sources/WalletConnectSign/JsonRpcHistory/JsonRpcHistory.swift b/Sources/WalletConnectSign/JsonRpcHistory/JsonRpcHistory.swift deleted file mode 100644 index 321bb5a62..000000000 --- a/Sources/WalletConnectSign/JsonRpcHistory/JsonRpcHistory.swift +++ /dev/null @@ -1,63 +0,0 @@ -import Foundation -import WalletConnectUtils - -protocol JsonRpcHistoryRecording { - func get(id: Int64) -> JsonRpcRecord? - func set(topic: String, request: WCRequest, chainId: String?) throws - func delete(topic: String) - func resolve(response: JsonRpcResult) throws -> JsonRpcRecord - func exist(id: Int64) -> Bool -} -// TODO -remove and use jsonrpc history only from utils -class JsonRpcHistory: JsonRpcHistoryRecording { - let storage: CodableStore - let logger: ConsoleLogging - - init(logger: ConsoleLogging, keyValueStore: CodableStore) { - self.logger = logger - self.storage = keyValueStore - } - - func get(id: Int64) -> JsonRpcRecord? { - try? storage.get(key: "\(id)") - } - - func set(topic: String, request: WCRequest, chainId: String? = nil) throws { - guard !exist(id: request.id) else { - throw WalletConnectError.internal(.jsonRpcDuplicateDetected) - } - logger.debug("Setting JSON-RPC request history record - ID: \(request.id)") - let record = JsonRpcRecord(id: request.id, topic: topic, request: JsonRpcRecord.Request(method: request.method, params: request.params), response: nil, chainId: chainId) - storage.set(record, forKey: "\(request.id)") - } - - func delete(topic: String) { - storage.getAll().forEach { record in - if record.topic == topic { - storage.delete(forKey: "\(record.id)") - } - } - } - - func resolve(response: JsonRpcResult) throws -> JsonRpcRecord { - logger.debug("Resolving JSON-RPC response - ID: \(response.id)") - guard var record = try? storage.get(key: "\(response.id)") else { - throw WalletConnectError.internal(.noJsonRpcRequestMatchingResponse) - } - if record.response != nil { - throw WalletConnectError.internal(.jsonRpcDuplicateDetected) - } else { - record.response = response - storage.set(record, forKey: "\(record.id)") - return record - } - } - - func exist(id: Int64) -> Bool { - return (try? storage.get(key: "\(id)")) != nil - } - - public func getPending() -> [JsonRpcRecord] { - storage.getAll().filter {$0.response == nil} - } -} diff --git a/Sources/WalletConnectSign/JsonRpcHistory/JsonRpcRecord.swift b/Sources/WalletConnectSign/JsonRpcHistory/JsonRpcRecord.swift deleted file mode 100644 index edfb14dc8..000000000 --- a/Sources/WalletConnectSign/JsonRpcHistory/JsonRpcRecord.swift +++ /dev/null @@ -1,15 +0,0 @@ -import Foundation -import WalletConnectUtils - -struct JsonRpcRecord: Codable { - let id: Int64 - let topic: String - let request: Request - var response: JsonRpcResult? - let chainId: String? - - struct Request: Codable { - let method: WCRequest.Method - let params: WCRequest.Params - } -} diff --git a/Sources/WalletConnectSign/Response.swift b/Sources/WalletConnectSign/Response.swift index 8dd4a0f0f..c01e438e5 100644 --- a/Sources/WalletConnectSign/Response.swift +++ b/Sources/WalletConnectSign/Response.swift @@ -1,8 +1,9 @@ import Foundation +import JSONRPC import WalletConnectUtils public struct Response: Codable { public let topic: String public let chainId: String? - public let result: JsonRpcResult + public let result: RPCResult } diff --git a/Sources/WalletConnectSign/Sign/Sign.swift b/Sources/WalletConnectSign/Sign/Sign.swift index 42685d660..1005410cc 100644 --- a/Sources/WalletConnectSign/Sign/Sign.swift +++ b/Sources/WalletConnectSign/Sign/Sign.swift @@ -1,10 +1,14 @@ import Foundation +import Combine +import JSONRPC import WalletConnectUtils import WalletConnectRelay -import Combine +import WalletConnectNetworking public typealias Account = WalletConnectUtils.Account public typealias Blockchain = WalletConnectUtils.Blockchain +public typealias Reason = WalletConnectNetworking.Reason +public typealias RPCID = JSONRPC.RPCID /// Sign instatnce wrapper /// diff --git a/Sources/WalletConnectSign/Sign/SignClient.swift b/Sources/WalletConnectSign/Sign/SignClient.swift index befbca947..0d774c529 100644 --- a/Sources/WalletConnectSign/Sign/SignClient.swift +++ b/Sources/WalletConnectSign/Sign/SignClient.swift @@ -1,8 +1,10 @@ import Foundation +import Combine +import JSONRPC import WalletConnectRelay import WalletConnectUtils import WalletConnectKMS -import Combine +import WalletConnectNetworking /// WalletConnect Sign Client /// @@ -94,7 +96,7 @@ public final class SignClient { private let disconnectService: DisconnectService private let nonControllerSessionStateMachine: NonControllerSessionStateMachine private let controllerSessionStateMachine: ControllerSessionStateMachine - private let history: JsonRpcHistory + private let history: RPCHistory private let cleanupService: CleanupService private let sessionProposalPublisherSubject = PassthroughSubject() @@ -121,7 +123,7 @@ public final class SignClient { nonControllerSessionStateMachine: NonControllerSessionStateMachine, controllerSessionStateMachine: ControllerSessionStateMachine, disconnectService: DisconnectService, - history: JsonRpcHistory, + history: RPCHistory, cleanupService: CleanupService ) { self.logger = logger @@ -221,9 +223,10 @@ public final class SignClient { /// For the wallet to respond on pending dApp's JSON-RPC request /// - Parameters: /// - topic: Topic of the session for which the request was received. + /// - requestId: RPC request ID /// - response: Your JSON RPC response or an error. - public func respond(topic: String, response: JsonRpcResult) async throws { - try await sessionEngine.respondSessionRequest(topic: topic, response: response) + public func respond(topic: String, requestId: RPCID, response: RPCResult) async throws { + try await sessionEngine.respondSessionRequest(topic: topic, requestId: requestId, response: response) } /// Ping method allows to check if peer client is online and is subscribing for given topic @@ -289,10 +292,9 @@ public final class SignClient { /// - Parameter topic: topic representing session for which you want to get pending requests. If nil, you will receive pending requests for all active sessions. public func getPendingRequests(topic: String? = nil) -> [Request] { let pendingRequests: [Request] = history.getPending() - .filter {$0.request.method == .sessionRequest} .compactMap { - guard case let .sessionRequest(payloadRequest) = $0.request.params else {return nil} - return Request(id: $0.id, topic: $0.topic, method: payloadRequest.request.method, params: payloadRequest.request.params, chainId: payloadRequest.chainId) + guard let request = try? $0.request.params?.get(SessionType.RequestParams.self) else { return nil } + return Request(id: $0.id, topic: $0.topic, method: request.request.method, params: request.request.params, chainId: request.chainId) } if let topic = topic { return pendingRequests.filter {$0.topic == topic} @@ -303,11 +305,13 @@ public final class SignClient { /// - Parameter id: id of a wc_sessionRequest jsonrpc request /// - Returns: json rpc record object for given id or nil if record for give id does not exits - public func getSessionRequestRecord(id: Int64) -> WalletConnectUtils.JsonRpcRecord? { - guard let record = history.get(id: id), - case .sessionRequest(let payload) = record.request.params else {return nil} - let request = WalletConnectUtils.JsonRpcRecord.Request(method: payload.request.method, params: payload.request.params) - return WalletConnectUtils.JsonRpcRecord(id: record.id, topic: record.topic, request: request, response: record.response, chainId: record.chainId) + public func getSessionRequestRecord(id: Int64) -> Request? { + guard + let record = history.get(recordId: RPCID(id)), + let request = try? record.request.params?.get(SessionType.RequestParams.self) + else { return nil } + + return Request(id: record.id, topic: record.topic, method: record.request.method, params: request, chainId: request.chainId) } #if DEBUG @@ -326,7 +330,7 @@ public final class SignClient { sessionProposalPublisherSubject.send(proposal) } approveEngine.onSessionRejected = { [unowned self] proposal, reason in - sessionRejectionPublisherSubject.send((proposal, reason.publicRepresentation())) + sessionRejectionPublisherSubject.send((proposal, reason)) } approveEngine.onSessionSettle = { [unowned self] settledSession in sessionSettlePublisherSubject.send(settledSession) @@ -335,7 +339,7 @@ public final class SignClient { sessionRequestPublisherSubject.send(sessionRequest) } sessionEngine.onSessionDelete = { [unowned self] topic, reason in - sessionDeletePublisherSubject.send((topic, reason.publicRepresentation())) + sessionDeletePublisherSubject.send((topic, reason)) } controllerSessionStateMachine.onNamespacesUpdate = { [unowned self] topic, namespaces in sessionUpdatePublisherSubject.send((topic, namespaces)) diff --git a/Sources/WalletConnectSign/Sign/SignClientFactory.swift b/Sources/WalletConnectSign/Sign/SignClientFactory.swift index 9a0412ad4..3ca457f06 100644 --- a/Sources/WalletConnectSign/Sign/SignClientFactory.swift +++ b/Sources/WalletConnectSign/Sign/SignClientFactory.swift @@ -3,6 +3,7 @@ import WalletConnectRelay import WalletConnectUtils import WalletConnectKMS import WalletConnectPairing +import WalletConnectNetworking public struct SignClientFactory { @@ -25,12 +26,12 @@ public struct SignClientFactory { static func create(metadata: AppMetadata, logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, relayClient: RelayClient) -> SignClient { let kms = KeyManagementService(keychain: keychainStorage) let serializer = Serializer(kms: kms) - let history = JsonRpcHistory(logger: logger, keyValueStore: CodableStore(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.jsonRpcHistory.rawValue)) - let networkingInteractor = NetworkInteractor(relayClient: relayClient, serializer: serializer, logger: logger, jsonRpcHistory: history) + let rpcHistory = RPCHistory(keyValueStore: CodableStore(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.jsonRpcHistory.rawValue)) + let networkingInteractor = NetworkingInteractor(relayClient: relayClient, serializer: serializer, logger: logger, rpcHistory: rpcHistory) let pairingStore = PairingStorage(storage: SequenceStore(store: .init(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.pairings.rawValue))) let sessionStore = SessionStorage(storage: SequenceStore(store: .init(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.sessions.rawValue))) let sessionToPairingTopic = CodableStore(defaults: RuntimeKeyValueStorage(), identifier: StorageDomainIdentifiers.sessionToPairingTopic.rawValue) - let proposalPayloadsStore = CodableStore(defaults: RuntimeKeyValueStorage(), identifier: StorageDomainIdentifiers.proposals.rawValue) + let proposalPayloadsStore = CodableStore>(defaults: RuntimeKeyValueStorage(), identifier: StorageDomainIdentifiers.proposals.rawValue) let pairingEngine = PairingEngine(networkingInteractor: networkingInteractor, kms: kms, pairingStore: pairingStore, metadata: metadata, logger: logger) let sessionEngine = SessionEngine(networkingInteractor: networkingInteractor, kms: kms, sessionStore: sessionStore, logger: logger) let nonControllerSessionStateMachine = NonControllerSessionStateMachine(networkingInteractor: networkingInteractor, kms: kms, sessionStore: sessionStore, logger: logger) @@ -51,7 +52,7 @@ public struct SignClientFactory { approveEngine: approveEngine, nonControllerSessionStateMachine: nonControllerSessionStateMachine, controllerSessionStateMachine: controllerSessionStateMachine, disconnectService: disconnectService, - history: history, + history: rpcHistory, cleanupService: cleanupService ) return client diff --git a/Sources/WalletConnectSign/Types/Session/SessionType.swift b/Sources/WalletConnectSign/Types/Session/SessionType.swift index 054704eae..f450e94aa 100644 --- a/Sources/WalletConnectSign/Types/Session/SessionType.swift +++ b/Sources/WalletConnectSign/Types/Session/SessionType.swift @@ -1,5 +1,6 @@ import Foundation import WalletConnectUtils +import WalletConnectNetworking // Internal namespace for session payloads. internal enum SessionType { @@ -24,7 +25,7 @@ internal enum SessionType { typealias DeleteParams = SessionType.Reason - struct Reason: Codable, Equatable { + struct Reason: Codable, Equatable, WalletConnectNetworking.Reason { let code: Int let message: String @@ -64,15 +65,3 @@ internal enum SessionType { let expiry: Int64 } } - -internal extension Reason { - func internalRepresentation() -> SessionType.Reason { - SessionType.Reason(code: self.code, message: self.message) - } -} - -extension SessionType.Reason { - func publicRepresentation() -> Reason { - Reason(code: self.code, message: self.message) - } -} diff --git a/Sources/WalletConnectSign/Types/SignProtocolMethod.swift b/Sources/WalletConnectSign/Types/SignProtocolMethod.swift new file mode 100644 index 000000000..90d4a0c54 --- /dev/null +++ b/Sources/WalletConnectSign/Types/SignProtocolMethod.swift @@ -0,0 +1,72 @@ +import Foundation +import JSONRPC +import WalletConnectPairing +import WalletConnectUtils +import WalletConnectNetworking + +enum SignProtocolMethod: ProtocolMethod { + case pairingDelete + case pairingPing + case sessionPropose + case sessionSettle + case sessionUpdate + case sessionExtend + case sessionDelete + case sessionRequest + case sessionPing + case sessionEvent + + var method: String { + switch self { + case .pairingDelete: + return "wc_pairingDelete" + case .pairingPing: + return "wc_pairingPing" + case .sessionPropose: + return "wc_sessionPropose" + case .sessionSettle: + return "wc_sessionSettle" + case .sessionUpdate: + return "wc_sessionUpdate" + case .sessionExtend: + return "wc_sessionExtend" + case .sessionDelete: + return "wc_sessionDelete" + case .sessionRequest: + return "wc_sessionRequest" + case .sessionPing: + return "wc_sessionPing" + case .sessionEvent: + return "wc_sessionEvent" + } + } + + var requestTag: Int { + switch self { + case .pairingDelete: + return 1000 + case .pairingPing: + return 1002 + case .sessionPropose: + return 1100 + case .sessionSettle: + return 1102 + case .sessionUpdate: + return 1104 + case .sessionExtend: + return 1106 + case .sessionDelete: + return 1112 + case .sessionRequest: + return 1108 + case .sessionPing: + return 1114 + case .sessionEvent: + return 1110 + } + } + + var responseTag: Int { + return requestTag + 1 + } +} diff --git a/Sources/WalletConnectSign/Types/WCRequest.swift b/Sources/WalletConnectSign/Types/WCRequest.swift deleted file mode 100644 index a4a7e44df..000000000 --- a/Sources/WalletConnectSign/Types/WCRequest.swift +++ /dev/null @@ -1,85 +0,0 @@ -import Foundation -import JSONRPC -import WalletConnectPairing -import WalletConnectUtils -import WalletConnectNetworking - -enum WCRequest: Codable { - case pairingDelete(PairingType.DeleteParams) - case pairingPing(PairingType.PingParams) - case sessionPropose(SessionType.ProposeParams) - case sessionSettle(SessionType.SettleParams) - case sessionUpdate(SessionType.UpdateParams) - case sessionExtend(SessionType.UpdateExpiryParams) - case sessionDelete(SessionType.DeleteParams) - case sessionRequest(SessionType.RequestParams) - case sessionPing(SessionType.PingParams) - case sessionEvent(SessionType.EventParams) - - enum Method: ProtocolMethod { - case pairingDelete - case pairingPing - case sessionPropose - case sessionSettle - case sessionUpdate - case sessionExtend - case sessionDelete - case sessionRequest - case sessionPing - case sessionEvent - - var method: String { - switch self { - case .pairingDelete: - return "wc_pairingDelete" - case .pairingPing: - return "wc_pairingPing" - case .sessionPropose: - return "wc_sessionPropose" - case .sessionSettle: - return "wc_sessionSettle" - case .sessionUpdate: - return "wc_sessionUpdate" - case .sessionExtend: - return "wc_sessionExtend" - case .sessionDelete: - return "wc_sessionDelete" - case .sessionRequest: - return "wc_sessionRequest" - case .sessionPing: - return "wc_sessionPing" - case .sessionEvent: - return "wc_sessionEvent" - } - } - - var requestTag: Int { - switch self { - case .pairingDelete: - return 1000 - case .pairingPing: - return 1002 - case .sessionPropose: - return 1100 - case .sessionSettle: - return 1102 - case .sessionUpdate: - return 1104 - case .sessionExtend: - return 1106 - case .sessionDelete: - return 1112 - case .sessionRequest: - return 1108 - case .sessionPing: - return 1114 - case .sessionEvent: - return 1110 - } - } - - var responseTag: Int { - return requestTag + 1 - } - } -} diff --git a/Sources/WalletConnectUtils/JSONRPC/JSONRPCErrorResponse.swift b/Sources/WalletConnectUtils/JSONRPC/JSONRPCErrorResponse.swift deleted file mode 100644 index 15f2c3de1..000000000 --- a/Sources/WalletConnectUtils/JSONRPC/JSONRPCErrorResponse.swift +++ /dev/null @@ -1,27 +0,0 @@ -import Foundation - -public struct JSONRPCErrorResponse: Error, Equatable, Codable { - public let jsonrpc = "2.0" - public let id: Int64 - public let error: JSONRPCErrorResponse.Error - - enum CodingKeys: String, CodingKey { - case jsonrpc - case id - case error - } - - public init(id: Int64, error: JSONRPCErrorResponse.Error) { - self.id = id - self.error = error - } - - public struct Error: Codable, Equatable { - public let code: Int - public let message: String - public init(code: Int, message: String) { - self.code = code - self.message = message - } - } -} diff --git a/Sources/WalletConnectUtils/JSONRPC/JSONRPCRequest.swift b/Sources/WalletConnectUtils/JSONRPC/JSONRPCRequest.swift deleted file mode 100644 index 2c3f57bb9..000000000 --- a/Sources/WalletConnectUtils/JSONRPC/JSONRPCRequest.swift +++ /dev/null @@ -1,23 +0,0 @@ -import Foundation - -public struct JSONRPCRequest: Codable, Equatable { - - public let id: Int64 - public let jsonrpc: String - public let method: String - public let params: T - - public enum CodingKeys: CodingKey { - case id - case jsonrpc - case method - case params - } - - public init(id: Int64 = JsonRpcID.generate(), method: String, params: T) { - self.id = id - self.jsonrpc = "2.0" - self.method = method - self.params = params - } -} diff --git a/Sources/WalletConnectUtils/JSONRPC/JSONRPCResponse.swift b/Sources/WalletConnectUtils/JSONRPC/JSONRPCResponse.swift deleted file mode 100644 index e7dd48923..000000000 --- a/Sources/WalletConnectUtils/JSONRPC/JSONRPCResponse.swift +++ /dev/null @@ -1,18 +0,0 @@ -import Foundation - -public struct JSONRPCResponse: Codable, Equatable { - public let jsonrpc = "2.0" - public let id: Int64 - public let result: T - - enum CodingKeys: String, CodingKey { - case jsonrpc - case id - case result - } - - public init(id: Int64, result: T) { - self.id = id - self.result = result - } -} diff --git a/Sources/WalletConnectUtils/JSONRPC/JsonRpcResult.swift b/Sources/WalletConnectUtils/JSONRPC/JsonRpcResult.swift deleted file mode 100644 index 611712500..000000000 --- a/Sources/WalletConnectUtils/JSONRPC/JsonRpcResult.swift +++ /dev/null @@ -1,24 +0,0 @@ -import Foundation - -public enum JsonRpcResult: Codable { - case error(JSONRPCErrorResponse) - case response(JSONRPCResponse) - - public var id: Int64 { - switch self { - case .error(let value): - return value.id - case .response(let value): - return value.id - } - } - - public var value: Codable { - switch self { - case .error(let value): - return value - case .response(let value): - return value - } - } -} diff --git a/Sources/WalletConnectUtils/JsonRpcHistory.swift b/Sources/WalletConnectUtils/JsonRpcHistory.swift deleted file mode 100644 index 9616b794b..000000000 --- a/Sources/WalletConnectUtils/JsonRpcHistory.swift +++ /dev/null @@ -1,58 +0,0 @@ -import Foundation - -public class JsonRpcHistory where T: Codable&Equatable { - enum RecordingError: Error { - case jsonRpcDuplicateDetected - case noJsonRpcRequestMatchingResponse - } - private let storage: CodableStore - private let logger: ConsoleLogging - - public init(logger: ConsoleLogging, keyValueStore: CodableStore) { - self.logger = logger - self.storage = keyValueStore - } - - public func get(id: Int64) -> JsonRpcRecord? { - try? storage.get(key: "\(id)") - } - - public func set(topic: String, request: JSONRPCRequest, chainId: String? = nil) throws { - guard !exist(id: request.id) else { - throw RecordingError.jsonRpcDuplicateDetected - } - logger.debug("Setting JSON-RPC request history record") - let record = JsonRpcRecord(id: request.id, topic: topic, request: JsonRpcRecord.Request(method: request.method, params: AnyCodable(request.params)), response: nil, chainId: chainId) - storage.set(record, forKey: "\(request.id)") - } - - public func delete(topic: String) { - storage.getAll().forEach { record in - if record.topic == topic { - storage.delete(forKey: "\(record.id)") - } - } - } - - public func resolve(response: JsonRpcResult) throws -> JsonRpcRecord { - logger.debug("Resolving JSON-RPC response - ID: \(response.id)") - guard var record = try? storage.get(key: "\(response.id)") else { - throw RecordingError.noJsonRpcRequestMatchingResponse - } - if record.response != nil { - throw RecordingError.jsonRpcDuplicateDetected - } else { - record.response = response - storage.set(record, forKey: "\(record.id)") - return record - } - } - - public func exist(id: Int64) -> Bool { - return (try? storage.get(key: "\(id)")) != nil - } - - public func getPending() -> [JsonRpcRecord] { - storage.getAll().filter {$0.response == nil} - } -} diff --git a/Sources/WalletConnectUtils/JsonRpcRecord.swift b/Sources/WalletConnectUtils/JsonRpcRecord.swift deleted file mode 100644 index 48013221f..000000000 --- a/Sources/WalletConnectUtils/JsonRpcRecord.swift +++ /dev/null @@ -1,27 +0,0 @@ -import Foundation - -public struct JsonRpcRecord: Codable { - public let id: Int64 - public let topic: String - public let request: Request - public var response: JsonRpcResult? - public let chainId: String? - - public init(id: Int64, topic: String, request: JsonRpcRecord.Request, response: JsonRpcResult? = nil, chainId: String?) { - self.id = id - self.topic = topic - self.request = request - self.response = response - self.chainId = chainId - } - - public struct Request: Codable { - public let method: String - public let params: AnyCodable - - public init(method: String, params: AnyCodable) { - self.method = method - self.params = params - } - } -} diff --git a/Tests/TestingUtils/NetworkingInteractorMock.swift b/Tests/TestingUtils/NetworkingInteractorMock.swift index 0ddc96adc..7942fdac1 100644 --- a/Tests/TestingUtils/NetworkingInteractorMock.swift +++ b/Tests/TestingUtils/NetworkingInteractorMock.swift @@ -26,10 +26,9 @@ public class NetworkingInteractorMock: NetworkInteracting { } // TODO: Avoid copy paste from NetworkInteractor - public func requestSubscription(on request: ProtocolMethod?) -> AnyPublisher, Never> { + public func requestSubscription(on request: ProtocolMethod) -> AnyPublisher, Never> { return requestPublisher .filter { rpcRequest in - guard let request = request else { return true } return rpcRequest.request.method == request.method } .compactMap { topic, rpcRequest in @@ -40,10 +39,9 @@ public class NetworkingInteractorMock: NetworkInteracting { } // TODO: Avoid copy paste from NetworkInteractor - public func responseSubscription(on request: ProtocolMethod?) -> AnyPublisher, Never> { + public func responseSubscription(on request: ProtocolMethod) -> AnyPublisher, Never> { return responsePublisher .filter { rpcRequest in - guard let request = request else { return true } return rpcRequest.request.method == request.method } .compactMap { topic, rpcRequest, rpcResponse in @@ -56,12 +54,13 @@ public class NetworkingInteractorMock: NetworkInteracting { .eraseToAnyPublisher() } - public func responseErrorSubscription(on request: ProtocolMethod) -> AnyPublisher { + // TODO: Avoid copy paste from NetworkInteractor + public func responseErrorSubscription(on request: ProtocolMethod) -> AnyPublisher, Never> { return responsePublisher .filter { $0.request.method == request.method } - .compactMap { (_, _, rpcResponse) in - guard let id = rpcResponse.id, let error = rpcResponse.error else { return nil } - return ResponseSubscriptionErrorPayload(id: id, error: error) + .compactMap { (topic, rpcRequest, rpcResponse) in + guard let id = rpcResponse.id, let request = try? rpcRequest.params?.get(Request.self), let error = rpcResponse.error else { return nil } + return ResponseSubscriptionErrorPayload(id: id, topic: topic, request: request, error: error) } .eraseToAnyPublisher() } From e6cde543eda08ac76cd3198608e80ab9ee02ae3f Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 8 Sep 2022 22:51:54 +0300 Subject: [PATCH 032/175] Sign new ping --- .../Sign/Helpers/ClientDelegate.swift | 10 +++++ .../Sign/SignClientTests.swift | 41 +++++++++++++++++-- .../Services/PairingPingService.swift | 12 +++--- .../Services/PingRequester.swift | 17 ++++---- .../Services/PingResponder.swift | 11 +++-- .../Services/PingResponseSubscriber.swift | 11 +++-- .../Engine/Common/PairingEngine.swift | 17 -------- .../Engine/Common/SessionEngine.swift | 17 -------- .../Services/SessionPingService.swift | 35 ++++++++++++++++ .../WalletConnectSign/Sign/SignClient.swift | 39 ++++++++++++++---- .../Sign/SignClientFactory.swift | 4 ++ 11 files changed, 147 insertions(+), 67 deletions(-) create mode 100644 Sources/WalletConnectSign/Services/SessionPingService.swift diff --git a/Example/IntegrationTests/Sign/Helpers/ClientDelegate.swift b/Example/IntegrationTests/Sign/Helpers/ClientDelegate.swift index 71ac99ab3..ab7b43e11 100644 --- a/Example/IntegrationTests/Sign/Helpers/ClientDelegate.swift +++ b/Example/IntegrationTests/Sign/Helpers/ClientDelegate.swift @@ -14,6 +14,8 @@ class ClientDelegate { var onSessionDelete: (() -> Void)? var onSessionUpdateNamespaces: ((String, [String: SessionNamespace]) -> Void)? var onSessionExtend: ((String, Date) -> Void)? + var onSessionPing: ((String) -> Void)? + var onPairingPing: ((String) -> Void)? var onEventReceived: ((Session.Event, String) -> Void)? private var publishers = Set() @@ -63,5 +65,13 @@ class ClientDelegate { client.sessionExtendPublisher.sink { (topic, date) in self.onSessionExtend?(topic, date) }.store(in: &publishers) + + client.sessionPingResponsePublisher.sink { topic in + self.onSessionPing?(topic) + }.store(in: &publishers) + + client.pairingPingResponsePublisher.sink { topic in + self.onPairingPing?(topic) + }.store(in: &publishers) } } diff --git a/Example/IntegrationTests/Sign/SignClientTests.swift b/Example/IntegrationTests/Sign/SignClientTests.swift index 3dd004d78..8a30c72cc 100644 --- a/Example/IntegrationTests/Sign/SignClientTests.swift +++ b/Example/IntegrationTests/Sign/SignClientTests.swift @@ -137,20 +137,54 @@ final class SignClientTests: XCTestCase { wait(for: [sessionDeleteExpectation], timeout: defaultTimeout) } - func testNewPairingPing() async throws { + func testPairingPing() async throws { let pongResponseExpectation = expectation(description: "Ping sender receives a pong response") let uri = try await dapp.client.connect(requiredNamespaces: ProposalNamespace.stubRequired())! try await wallet.client.pair(uri: uri) let pairing = wallet.client.getPairings().first! - wallet.client.ping(topic: pairing.topic) { result in - if case .failure = result { XCTFail() } + + wallet.onPairingPing = { topic in + XCTAssertEqual(topic, pairing.topic) pongResponseExpectation.fulfill() } + + try await wallet.client.ping(topic: pairing.topic) + wait(for: [pongResponseExpectation], timeout: defaultTimeout) } + func testSessionPing() async throws { + let expectation = expectation(description: "Proposer receives ping response") + + let requiredNamespaces = ProposalNamespace.stubRequired() + let sessionNamespaces = SessionNamespace.make(toRespond: requiredNamespaces) + + wallet.onSessionProposal = { proposal in + Task(priority: .high) { + try! await self.wallet.client.approve(proposalId: proposal.id, namespaces: sessionNamespaces) + } + } + + dapp.onSessionSettled = { sessionSettled in + Task(priority: .high) { + try! await self.dapp.client.ping(topic: sessionSettled.topic) + } + } + + dapp.onSessionPing = { topic in + let session = self.wallet.client.getSessions().first! + XCTAssertEqual(topic, session.topic) + expectation.fulfill() + } + + let uri = try await dapp.client.connect(requiredNamespaces: requiredNamespaces)! + try await wallet.client.pair(uri: uri) + + wait(for: [expectation], timeout: .infinity) + } + func testSessionRequest() async throws { let requestExpectation = expectation(description: "Wallet expects to receive a request") let responseExpectation = expectation(description: "Dapp expects to receive a response") @@ -242,7 +276,6 @@ final class SignClientTests: XCTestCase { wait(for: [expectation], timeout: defaultTimeout) } - func testNewSessionOnExistingPairing() async { let dappSettlementExpectation = expectation(description: "Dapp settles session") dappSettlementExpectation.expectedFulfillmentCount = 2 diff --git a/Sources/WalletConnectPairing/Services/PairingPingService.swift b/Sources/WalletConnectPairing/Services/PairingPingService.swift index 6baef98e0..f435ec72b 100644 --- a/Sources/WalletConnectPairing/Services/PairingPingService.swift +++ b/Sources/WalletConnectPairing/Services/PairingPingService.swift @@ -1,8 +1,9 @@ -import WalletConnectUtils import Foundation +import WalletConnectUtils import WalletConnectNetworking public class PairingPingService { + private let pairingStorage: WCPairingStorage private let pingRequester: PingRequester private let pingResponder: PingResponder private let pingResponseSubscriber: PingResponseSubscriber @@ -20,13 +21,14 @@ public class PairingPingService { pairingStorage: WCPairingStorage, networkingInteractor: NetworkInteracting, logger: ConsoleLogging) { - pingRequester = PingRequester(pairingStorage: pairingStorage, networkingInteractor: networkingInteractor) - pingResponder = PingResponder(networkingInteractor: networkingInteractor, logger: logger) - pingResponseSubscriber = PingResponseSubscriber(networkingInteractor: networkingInteractor, logger: logger) + self.pairingStorage = pairingStorage + self.pingRequester = PingRequester(networkingInteractor: networkingInteractor, method: PairingProtocolMethod.ping) + self.pingResponder = PingResponder(networkingInteractor: networkingInteractor, method: PairingProtocolMethod.ping, logger: logger) + self.pingResponseSubscriber = PingResponseSubscriber(networkingInteractor: networkingInteractor, method: PairingProtocolMethod.ping, logger: logger) } public func ping(topic: String) async throws { + guard pairingStorage.hasPairing(forTopic: topic) else { return } try await pingRequester.ping(topic: topic) } - } diff --git a/Sources/WalletConnectPairing/Services/PingRequester.swift b/Sources/WalletConnectPairing/Services/PingRequester.swift index f936a8c9c..9def19d28 100644 --- a/Sources/WalletConnectPairing/Services/PingRequester.swift +++ b/Sources/WalletConnectPairing/Services/PingRequester.swift @@ -1,19 +1,18 @@ import Foundation -import WalletConnectNetworking import JSONRPC +import WalletConnectNetworking -class PingRequester { - private let pairingStorage: WCPairingStorage +public class PingRequester { + private let method: ProtocolMethod private let networkingInteractor: NetworkInteracting - init(pairingStorage: WCPairingStorage, networkingInteractor: NetworkInteracting) { - self.pairingStorage = pairingStorage + public init(networkingInteractor: NetworkInteracting, method: ProtocolMethod) { + self.method = method self.networkingInteractor = networkingInteractor } - func ping(topic: String) async throws { - guard pairingStorage.hasPairing(forTopic: topic) else { return } - let request = RPCRequest(method: PairingProtocolMethod.ping.rawValue, params: PairingPingParams()) - try await networkingInteractor.request(request, topic: topic, tag: PairingProtocolMethod.ping.requestTag) + public func ping(topic: String) async throws { + let request = RPCRequest(method: method.method, params: PairingPingParams()) + try await networkingInteractor.request(request, topic: topic, tag: method.requestTag) } } diff --git a/Sources/WalletConnectPairing/Services/PingResponder.swift b/Sources/WalletConnectPairing/Services/PingResponder.swift index 9ed8c0806..7718f6128 100644 --- a/Sources/WalletConnectPairing/Services/PingResponder.swift +++ b/Sources/WalletConnectPairing/Services/PingResponder.swift @@ -2,24 +2,27 @@ import Combine import WalletConnectUtils import WalletConnectNetworking -class PingResponder { +public class PingResponder { private let networkingInteractor: NetworkInteracting + private let method: ProtocolMethod private let logger: ConsoleLogging private var publishers = [AnyCancellable]() - init(networkingInteractor: NetworkInteracting, + public init(networkingInteractor: NetworkInteracting, + method: ProtocolMethod, logger: ConsoleLogging) { self.networkingInteractor = networkingInteractor + self.method = method self.logger = logger subscribePingRequests() } private func subscribePingRequests() { - networkingInteractor.requestSubscription(on: PairingProtocolMethod.ping) + networkingInteractor.requestSubscription(on: method) .sink { [unowned self] (payload: RequestSubscriptionPayload) in logger.debug("Responding for pairing ping") Task(priority: .high) { - try? await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, tag: PairingProtocolMethod.ping.responseTag) + try? await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, tag: method.responseTag) } } .store(in: &publishers) diff --git a/Sources/WalletConnectPairing/Services/PingResponseSubscriber.swift b/Sources/WalletConnectPairing/Services/PingResponseSubscriber.swift index d2ce74a89..e473bd3e4 100644 --- a/Sources/WalletConnectPairing/Services/PingResponseSubscriber.swift +++ b/Sources/WalletConnectPairing/Services/PingResponseSubscriber.swift @@ -2,22 +2,25 @@ import Combine import WalletConnectUtils import WalletConnectNetworking -class PingResponseSubscriber { +public class PingResponseSubscriber { private let networkingInteractor: NetworkInteracting + private let method: ProtocolMethod private let logger: ConsoleLogging private var publishers = [AnyCancellable]() - var onResponse: ((String)->())? + public var onResponse: ((String)->())? - init(networkingInteractor: NetworkInteracting, + public init(networkingInteractor: NetworkInteracting, + method: ProtocolMethod, logger: ConsoleLogging) { self.networkingInteractor = networkingInteractor + self.method = method self.logger = logger subscribePingResponses() } private func subscribePingResponses() { - networkingInteractor.responseSubscription(on: PairingProtocolMethod.ping) + networkingInteractor.responseSubscription(on: method) .sink { [unowned self] (payload: ResponseSubscriptionPayload) in onResponse?(payload.topic) } diff --git a/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift b/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift index 0561e6446..7c75d9876 100644 --- a/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift @@ -75,23 +75,6 @@ final class PairingEngine { let request = RPCRequest(method: SignProtocolMethod.sessionPropose.method, params: proposal) try await networkingInteractor.request(request, topic: pairingTopic, tag: SignProtocolMethod.sessionPropose.requestTag) } - - func ping(topic: String, completion: @escaping ((Result) -> Void)) { - guard pairingStore.hasPairing(forTopic: topic) else { - logger.debug("Could not find pairing to ping for topic \(topic)") - return - } -// TODO: Ping disabled -// networkingInteractor.requestPeerResponse(.wcPairingPing, onTopic: topic) { [unowned self] result in -// switch result { -// case .success: -// logger.debug("Did receive ping response") -// completion(.success(())) -// case .failure(let error): -// logger.debug("error: \(error)") -// } -// } - } } // MARK: Private diff --git a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift index 0905d4f3d..60435dc0e 100644 --- a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift @@ -47,23 +47,6 @@ final class SessionEngine { sessionStore.getAll().map {$0.publicRepresentation()} } - func ping(topic: String, completion: @escaping (Result) -> Void) { - guard sessionStore.hasSession(forTopic: topic) else { - logger.debug("Could not find session to ping for topic \(topic)") - return - } -// TODO: Ping disabled -// networkingInteractor.requestPeerResponse(.wcSessionPing, onTopic: topic) { [unowned self] result in -// switch result { -// case .success: -// logger.debug("Did receive ping response") -// completion(.success(())) -// case .failure(let error): -// logger.debug("error: \(error)") -// } -// } - } - func request(_ request: Request) async throws { logger.debug("will request on session topic: \(request.topic)") guard let session = sessionStore.getSession(forTopic: request.topic), session.acknowledged else { diff --git a/Sources/WalletConnectSign/Services/SessionPingService.swift b/Sources/WalletConnectSign/Services/SessionPingService.swift new file mode 100644 index 000000000..697dcab89 --- /dev/null +++ b/Sources/WalletConnectSign/Services/SessionPingService.swift @@ -0,0 +1,35 @@ +import Foundation +import WalletConnectPairing +import WalletConnectUtils +import WalletConnectNetworking + +class SessionPingService { + private let sessionStorage: WCSessionStorage + private let pingRequester: PingRequester + private let pingResponder: PingResponder + private let pingResponseSubscriber: PingResponseSubscriber + + var onResponse: ((String)->())? { + get { + return pingResponseSubscriber.onResponse + } + set { + pingResponseSubscriber.onResponse = newValue + } + } + + init( + sessionStorage: WCSessionStorage, + networkingInteractor: NetworkInteracting, + logger: ConsoleLogging) { + self.sessionStorage = sessionStorage + self.pingRequester = PingRequester(networkingInteractor: networkingInteractor, method: SignProtocolMethod.sessionPing) + self.pingResponder = PingResponder(networkingInteractor: networkingInteractor, method: SignProtocolMethod.sessionPing, logger: logger) + self.pingResponseSubscriber = PingResponseSubscriber(networkingInteractor: networkingInteractor, method: SignProtocolMethod.sessionPing, logger: logger) + } + + func ping(topic: String) async throws { + guard sessionStorage.hasSession(forTopic: topic) else { return } + try await pingRequester.ping(topic: topic) + } +} diff --git a/Sources/WalletConnectSign/Sign/SignClient.swift b/Sources/WalletConnectSign/Sign/SignClient.swift index 0d774c529..4d343153a 100644 --- a/Sources/WalletConnectSign/Sign/SignClient.swift +++ b/Sources/WalletConnectSign/Sign/SignClient.swift @@ -5,6 +5,7 @@ import WalletConnectRelay import WalletConnectUtils import WalletConnectKMS import WalletConnectNetworking +import WalletConnectPairing /// WalletConnect Sign Client /// @@ -83,6 +84,20 @@ public final class SignClient { sessionExtendPublisherSubject.eraseToAnyPublisher() } + /// Publisher that sends session topic when session ping received + /// + /// Event will be emited on controller and non-controller clients. + public var sessionPingResponsePublisher: AnyPublisher { + sessionPingResponsePublisherSubject.eraseToAnyPublisher() + } + + /// Publisher that sends pairing topic when pairing ping received + /// + /// Event will be emited on controller and non-controller clients. + public var pairingPingResponsePublisher: AnyPublisher { + pairingPingResponsePublisherSubject.eraseToAnyPublisher() + } + /// An object that loggs SDK's errors and info messages public let logger: ConsoleLogging @@ -94,6 +109,8 @@ public final class SignClient { private let sessionEngine: SessionEngine private let approveEngine: ApproveEngine private let disconnectService: DisconnectService + private let pairingPingService: PairingPingService + private let sessionPingService: SessionPingService private let nonControllerSessionStateMachine: NonControllerSessionStateMachine private let controllerSessionStateMachine: ControllerSessionStateMachine private let history: RPCHistory @@ -109,6 +126,8 @@ public final class SignClient { private let sessionUpdatePublisherSubject = PassthroughSubject<(sessionTopic: String, namespaces: [String: SessionNamespace]), Never>() private let sessionEventPublisherSubject = PassthroughSubject<(event: Session.Event, sessionTopic: String, chainId: Blockchain?), Never>() private let sessionExtendPublisherSubject = PassthroughSubject<(sessionTopic: String, date: Date), Never>() + private let sessionPingResponsePublisherSubject = PassthroughSubject() + private let pairingPingResponsePublisherSubject = PassthroughSubject() private var publishers = Set() @@ -120,6 +139,8 @@ public final class SignClient { pairEngine: PairEngine, sessionEngine: SessionEngine, approveEngine: ApproveEngine, + pairingPingService: PairingPingService, + sessionPingService: SessionPingService, nonControllerSessionStateMachine: NonControllerSessionStateMachine, controllerSessionStateMachine: ControllerSessionStateMachine, disconnectService: DisconnectService, @@ -132,6 +153,8 @@ public final class SignClient { self.pairEngine = pairEngine self.sessionEngine = sessionEngine self.approveEngine = approveEngine + self.pairingPingService = pairingPingService + self.sessionPingService = sessionPingService self.nonControllerSessionStateMachine = nonControllerSessionStateMachine self.controllerSessionStateMachine = controllerSessionStateMachine self.history = history @@ -238,15 +261,11 @@ public final class SignClient { /// - Parameters: /// - topic: Topic of a session or a pairing /// - completion: Result will be success on response or an error - public func ping(topic: String, completion: @escaping ((Result) -> Void)) { + public func ping(topic: String) async throws { if pairingEngine.hasPairing(for: topic) { - pairingEngine.ping(topic: topic) { result in - completion(result) - } + try await pairingPingService.ping(topic: topic) } else if sessionEngine.hasSession(for: topic) { - sessionEngine.ping(topic: topic) { result in - completion(result) - } + try await sessionPingService.ping(topic: topic) } } @@ -359,6 +378,12 @@ public final class SignClient { sessionEngine.onSessionResponse = { [unowned self] response in sessionResponsePublisherSubject.send(response) } + pairingPingService.onResponse = { [unowned self] topic in + pairingPingResponsePublisherSubject.send(topic) + } + sessionPingService.onResponse = { [unowned self] topic in + sessionPingResponsePublisherSubject.send(topic) + } } private func setUpConnectionObserving() { diff --git a/Sources/WalletConnectSign/Sign/SignClientFactory.swift b/Sources/WalletConnectSign/Sign/SignClientFactory.swift index 3ca457f06..694b68c48 100644 --- a/Sources/WalletConnectSign/Sign/SignClientFactory.swift +++ b/Sources/WalletConnectSign/Sign/SignClientFactory.swift @@ -42,6 +42,8 @@ public struct SignClientFactory { let deletePairingService = DeletePairingService(networkingInteractor: networkingInteractor, kms: kms, pairingStorage: pairingStore, logger: logger) let deleteSessionService = DeleteSessionService(networkingInteractor: networkingInteractor, kms: kms, sessionStore: sessionStore, logger: logger) let disconnectService = DisconnectService(deletePairingService: deletePairingService, deleteSessionService: deleteSessionService, pairingStorage: pairingStore, sessionStorage: sessionStore) + let sessionPingService = SessionPingService(sessionStorage: sessionStore, networkingInteractor: networkingInteractor, logger: logger) + let pairingPingService = PairingPingService(pairingStorage: pairingStore, networkingInteractor: networkingInteractor, logger: logger) let client = SignClient( logger: logger, @@ -50,6 +52,8 @@ public struct SignClientFactory { pairEngine: pairEngine, sessionEngine: sessionEngine, approveEngine: approveEngine, + pairingPingService: pairingPingService, + sessionPingService: sessionPingService, nonControllerSessionStateMachine: nonControllerSessionStateMachine, controllerSessionStateMachine: controllerSessionStateMachine, disconnectService: disconnectService, history: rpcHistory, From 1b25f0632d7e2397f91fd65c180074af442b007b Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 8 Sep 2022 22:53:25 +0300 Subject: [PATCH 033/175] Cleanup --- .../NetworkInteractor/NetworkInteractor.swift | 259 ------------------ .../NetworkInteractor/NetworkRelaying.swift | 19 -- 2 files changed, 278 deletions(-) delete mode 100644 Sources/WalletConnectSign/NetworkInteractor/NetworkInteractor.swift delete mode 100644 Sources/WalletConnectSign/NetworkInteractor/NetworkRelaying.swift diff --git a/Sources/WalletConnectSign/NetworkInteractor/NetworkInteractor.swift b/Sources/WalletConnectSign/NetworkInteractor/NetworkInteractor.swift deleted file mode 100644 index f23cc922f..000000000 --- a/Sources/WalletConnectSign/NetworkInteractor/NetworkInteractor.swift +++ /dev/null @@ -1,259 +0,0 @@ -//import Foundation -//import Combine -//import WalletConnectUtils -//import WalletConnectKMS -// -//protocol NetworkInteracting: AnyObject { -// var transportConnectionPublisher: AnyPublisher {get} -// var wcRequestPublisher: AnyPublisher {get} -// var responsePublisher: AnyPublisher {get} -// /// Completes when request sent from a networking client -// func request(_ wcMethod: WCMethod, onTopic topic: String) async throws -// /// Completes with an acknowledgement from the relay network -// func requestNetworkAck(_ wcMethod: WCMethod, onTopic topic: String, completion: @escaping ((Error?) -> Void)) -// /// Completes with a peer response -// func requestPeerResponse(_ wcMethod: WCMethod, onTopic topic: String, completion: ((Result, JSONRPCErrorResponse>) -> Void)?) -// func respond(topic: String, response: JsonRpcResult, tag: Int) async throws -// func respondSuccess(payload: WCRequestSubscriptionPayload) async throws -// func respondSuccess(for payload: WCRequestSubscriptionPayload) -// func respondError(payload: WCRequestSubscriptionPayload, reason: ReasonCode) async throws -// func subscribe(topic: String) async throws -// func unsubscribe(topic: String) -//} -// -//extension NetworkInteracting { -// func request(_ wcMethod: WCMethod, onTopic topic: String) { -// requestPeerResponse(wcMethod, onTopic: topic, completion: nil) -// } -//} -// -//class NetworkInteractor: NetworkInteracting { -// -// private var publishers = Set() -// -// private var relayClient: NetworkRelaying -// private let serializer: Serializing -// private let jsonRpcHistory: JsonRpcHistoryRecording -// -// private let transportConnectionPublisherSubject = PassthroughSubject() -// private let responsePublisherSubject = PassthroughSubject() -// private let wcRequestPublisherSubject = PassthroughSubject() -// -// var transportConnectionPublisher: AnyPublisher { -// transportConnectionPublisherSubject.eraseToAnyPublisher() -// } -// var wcRequestPublisher: AnyPublisher { -// wcRequestPublisherSubject.eraseToAnyPublisher() -// } -// var responsePublisher: AnyPublisher { -// responsePublisherSubject.eraseToAnyPublisher() -// } -// -// let logger: ConsoleLogging -// -// init(relayClient: NetworkRelaying, -// serializer: Serializing, -// logger: ConsoleLogging, -// jsonRpcHistory: JsonRpcHistoryRecording) { -// self.relayClient = relayClient -// self.serializer = serializer -// self.logger = logger -// self.jsonRpcHistory = jsonRpcHistory -// setUpPublishers() -// } -// -// func request(_ wcMethod: WCMethod, onTopic topic: String) async throws { -// try await request(topic: topic, payload: wcMethod.asRequest()) -// } -// -// /// Completes when networking client sends a request -// func request(topic: String, payload: WCRequest) async throws { -// try jsonRpcHistory.set(topic: topic, request: payload, chainId: getChainId(payload)) -// let message = try serializer.serialize(topic: topic, encodable: payload) -// let prompt = shouldPrompt(payload.method) -// try await relayClient.publish(topic: topic, payload: message, tag: payload.tag, prompt: prompt) -// } -// -// func requestPeerResponse(_ wcMethod: WCMethod, onTopic topic: String, completion: ((Result, JSONRPCErrorResponse>) -> Void)?) { -// let payload = wcMethod.asRequest() -// do { -// try jsonRpcHistory.set(topic: topic, request: payload, chainId: getChainId(payload)) -// let message = try serializer.serialize(topic: topic, encodable: payload) -// let prompt = shouldPrompt(payload.method) -// relayClient.publish(topic: topic, payload: message, tag: payload.tag, prompt: prompt) { [weak self] error in -// guard let self = self else {return} -// if let error = error { -// self.logger.error(error) -// } else { -// var cancellable: AnyCancellable! -// cancellable = self.responsePublisher -// .filter {$0.result.id == payload.id} -// .sink { (response) in -// cancellable.cancel() -// self.logger.debug("WC Relay - received response on topic: \(topic)") -// switch response.result { -// case .response(let response): -// completion?(.success(response)) -// case .error(let error): -// self.logger.debug("Request error: \(error)") -// completion?(.failure(error)) -// } -// } -// } -// } -// } catch WalletConnectError.internal(.jsonRpcDuplicateDetected) { -// logger.info("Info: Json Rpc Duplicate Detected") -// } catch { -// logger.error(error) -// } -// } -// -// /// Completes with an acknowledgement from the relay network. -// /// completes with error if networking client was not able to send a message -// func requestNetworkAck(_ wcMethod: WCMethod, onTopic topic: String, completion: @escaping ((Error?) -> Void)) { -// do { -// let payload = wcMethod.asRequest() -// try jsonRpcHistory.set(topic: topic, request: payload, chainId: getChainId(payload)) -// let message = try serializer.serialize(topic: topic, encodable: payload) -// let prompt = shouldPrompt(payload.method) -// relayClient.publish(topic: topic, payload: message, tag: payload.tag, prompt: prompt) { error in -// completion(error) -// } -// } catch WalletConnectError.internal(.jsonRpcDuplicateDetected) { -// logger.info("Info: Json Rpc Duplicate Detected") -// } catch { -// logger.error(error) -// } -// } -// -// func respond(topic: String, response: JsonRpcResult, tag: Int) async throws { -// _ = try jsonRpcHistory.resolve(response: response) -// -// let message = try serializer.serialize(topic: topic, encodable: response.value) -// logger.debug("Responding....topic: \(topic)") -// -// do { -// try await relayClient.publish(topic: topic, payload: message, tag: tag, prompt: false) -// } catch WalletConnectError.internal(.jsonRpcDuplicateDetected) { -// logger.info("Info: Json Rpc Duplicate Detected") -// } -// } -// -// func respondSuccess(payload: WCRequestSubscriptionPayload) async throws { -// let response = JSONRPCResponse(id: payload.wcRequest.id, result: AnyCodable(true)) -// try await respond(topic: payload.topic, response: JsonRpcResult.response(response), tag: payload.wcRequest.responseTag) -// } -// -// func respondError(payload: WCRequestSubscriptionPayload, reason: ReasonCode) async throws { -// let response = JSONRPCErrorResponse(id: payload.wcRequest.id, error: JSONRPCErrorResponse.Error(code: reason.code, message: reason.message)) -// try await respond(topic: payload.topic, response: JsonRpcResult.error(response), tag: payload.wcRequest.responseTag) -// } -// -// // TODO: Move to async -// func respondSuccess(for payload: WCRequestSubscriptionPayload) { -// Task(priority: .background) { -// do { -// try await respondSuccess(payload: payload) -// } catch { -// self.logger.error("Respond Success failed with: \(error.localizedDescription)") -// } -// } -// } -// -// func subscribe(topic: String) async throws { -// try await relayClient.subscribe(topic: topic) -// } -// -// func unsubscribe(topic: String) { -// relayClient.unsubscribe(topic: topic) { [weak self] error in -// if let error = error { -// self?.logger.error(error) -// } else { -// self?.jsonRpcHistory.delete(topic: topic) -// } -// } -// } -// -// // MARK: - Private -// -// private func setUpPublishers() { -// relayClient.socketConnectionStatusPublisher.sink { [weak self] status in -// if status == .connected { -// self?.transportConnectionPublisherSubject.send() -// } -// }.store(in: &publishers) -// -// relayClient.messagePublisher.sink { [weak self] (topic, message) in -// self?.manageSubscription(topic, message) -// } -// .store(in: &publishers) -// } -// -// private func manageSubscription(_ topic: String, _ encodedEnvelope: String) { -// if let deserializedJsonRpcRequest: WCRequest = serializer.tryDeserialize(topic: topic, encodedEnvelope: encodedEnvelope) { -// handleWCRequest(topic: topic, request: deserializedJsonRpcRequest) -// } else if let deserializedJsonRpcResponse: JSONRPCResponse = serializer.tryDeserialize(topic: topic, encodedEnvelope: encodedEnvelope) { -// handleJsonRpcResponse(response: deserializedJsonRpcResponse) -// } else if let deserializedJsonRpcError: JSONRPCErrorResponse = serializer.tryDeserialize(topic: topic, encodedEnvelope: encodedEnvelope) { -// handleJsonRpcErrorResponse(response: deserializedJsonRpcError) -// } else { -// logger.warn("Warning: Networking Interactor - Received unknown object type from networking relay") -// } -// } -// -// private func handleWCRequest(topic: String, request: WCRequest) { -// do { -// try jsonRpcHistory.set(topic: topic, request: request, chainId: getChainId(request)) -// let payload = WCRequestSubscriptionPayload(topic: topic, wcRequest: request) -// wcRequestPublisherSubject.send(payload) -// } catch WalletConnectError.internal(.jsonRpcDuplicateDetected) { -// logger.info("Info: Json Rpc Duplicate Detected") -// } catch { -// logger.error(error) -// } -// } -// -// private func handleJsonRpcResponse(response: JSONRPCResponse) { -// do { -// let record = try jsonRpcHistory.resolve(response: JsonRpcResult.response(response)) -// let wcResponse = WCResponse( -// topic: record.topic, -// chainId: record.chainId, -// requestMethod: record.request.method, -// requestParams: record.request.params, -// result: JsonRpcResult.response(response)) -// responsePublisherSubject.send(wcResponse) -// } catch { -// logger.info("Info: \(error.localizedDescription)") -// } -// } -// -// private func handleJsonRpcErrorResponse(response: JSONRPCErrorResponse) { -// do { -// let record = try jsonRpcHistory.resolve(response: JsonRpcResult.error(response)) -// let wcResponse = WCResponse( -// topic: record.topic, -// chainId: record.chainId, -// requestMethod: record.request.method, -// requestParams: record.request.params, -// result: JsonRpcResult.error(response)) -// responsePublisherSubject.send(wcResponse) -// } catch { -// logger.info("Info: \(error.localizedDescription)") -// } -// } -// -// private func shouldPrompt(_ method: WCRequest.Method) -> Bool { -// switch method { -// case .sessionRequest: -// return true -// default: -// return false -// } -// } -// -// func getChainId(_ request: WCRequest) -> String? { -// guard case let .sessionRequest(payload) = request.params else {return nil} -// return payload.chainId.absoluteString -// } -//} diff --git a/Sources/WalletConnectSign/NetworkInteractor/NetworkRelaying.swift b/Sources/WalletConnectSign/NetworkInteractor/NetworkRelaying.swift deleted file mode 100644 index fec9c6ed8..000000000 --- a/Sources/WalletConnectSign/NetworkInteractor/NetworkRelaying.swift +++ /dev/null @@ -1,19 +0,0 @@ -//import Foundation -//import WalletConnectRelay -//import Combine -// -//extension RelayClient: NetworkRelaying {} -// -//protocol NetworkRelaying { -// var messagePublisher: AnyPublisher<(topic: String, message: String), Never> { get } -// var socketConnectionStatusPublisher: AnyPublisher { get } -// func connect() throws -// func disconnect(closeCode: URLSessionWebSocketTask.CloseCode) throws -// func publish(topic: String, payload: String, tag: Int, prompt: Bool) async throws -// /// - returns: request id -// func publish(topic: String, payload: String, tag: Int, prompt: Bool, onNetworkAcknowledge: @escaping ((Error?) -> Void)) -// func subscribe(topic: String, completion: @escaping (Error?) -> Void) -// func subscribe(topic: String) async throws -// /// - returns: request id -// func unsubscribe(topic: String, completion: @escaping ((Error?) -> Void)) -//} From c1d9ef581b95aa3c20ec3f6838a4c5b4ff207c20 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Fri, 9 Sep 2022 02:54:08 +0300 Subject: [PATCH 034/175] Unit tests fixed --- Package.swift | 2 +- .../Engine/Common/ApproveEngine.swift | 2 +- .../Engine/Common/SessionEngine.swift | 4 +- .../NonControllerSessionStateMachine.swift | 2 +- .../NetworkingInteractorMock.swift | 36 ++++- .../ApproveEngineTests.swift | 43 +++--- .../Helpers/WCRequest+Extension.swift | 9 -- .../JsonRpcHistoryTests.swift | 84 ---------- .../Mocks/MockedRelayClient.swift | 78 +++++----- .../Mocks/NetworkingInteractorMock.swift | 102 ------------ .../Mocks/SerializerMock.swift | 34 ---- ...onControllerSessionStateMachineTests.swift | 16 +- .../PairEngineTests.swift | 6 +- .../PairingEngineTests.swift | 26 ++-- Tests/WalletConnectSignTests/Stub/Stubs.swift | 36 ++--- .../WalletConnectSignTests/WCRelayTests.swift | 145 +++++++++--------- .../WCResponseTests.swift | 16 +- 17 files changed, 206 insertions(+), 435 deletions(-) delete mode 100644 Tests/WalletConnectSignTests/Helpers/WCRequest+Extension.swift delete mode 100644 Tests/WalletConnectSignTests/JsonRpcHistoryTests.swift delete mode 100644 Tests/WalletConnectSignTests/Mocks/NetworkingInteractorMock.swift delete mode 100644 Tests/WalletConnectSignTests/Mocks/SerializerMock.swift diff --git a/Package.swift b/Package.swift index a991ee8bf..4c925ebf6 100644 --- a/Package.swift +++ b/Package.swift @@ -52,7 +52,7 @@ let package = Package( path: "Sources/WalletConnectKMS"), .target( name: "WalletConnectPairing", - dependencies: ["WalletConnectUtils", "WalletConnectNetworking", "JSONRPC"]), + dependencies: ["WalletConnectNetworking"]), .target( name: "WalletConnectUtils", dependencies: ["Commons", "JSONRPC"]), diff --git a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift index e29813c63..398e1566a 100644 --- a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift @@ -184,7 +184,7 @@ private extension ApproveEngine { } func respondError(payload: SubscriptionPayload, reason: ReasonCode, tag: Int) { - Task { + Task(priority: .high) { do { try await networkingInteractor.respondError(topic: payload.topic, requestId: payload.id, tag: tag, reason: reason) } catch { diff --git a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift index 60435dc0e..1372c8fe6 100644 --- a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift @@ -94,7 +94,7 @@ private extension SessionEngine { guard status == .connected else { return } sessionStore.getAll() .forEach { session in - Task { try await networkingInteractor.subscribe(topic: session.topic) } + Task(priority: .high) { try await networkingInteractor.subscribe(topic: session.topic) } } } .store(in: &publishers) @@ -142,7 +142,7 @@ private extension SessionEngine { } func respondError(payload: SubscriptionPayload, reason: ReasonCode, tag: Int) { - Task { + Task(priority: .high) { do { try await networkingInteractor.respondError(topic: payload.topic, requestId: payload.id, tag: tag, reason: reason) } catch { diff --git a/Sources/WalletConnectSign/Engine/NonController/NonControllerSessionStateMachine.swift b/Sources/WalletConnectSign/Engine/NonController/NonControllerSessionStateMachine.swift index 46ab1c516..7cca1066b 100644 --- a/Sources/WalletConnectSign/Engine/NonController/NonControllerSessionStateMachine.swift +++ b/Sources/WalletConnectSign/Engine/NonController/NonControllerSessionStateMachine.swift @@ -39,7 +39,7 @@ final class NonControllerSessionStateMachine { } private func respondError(payload: SubscriptionPayload, reason: ReasonCode, tag: Int) { - Task { + Task(priority: .high) { do { try await networkingInteractor.respondError(topic: payload.topic, requestId: payload.id, tag: tag, reason: reason) } catch { diff --git a/Tests/TestingUtils/NetworkingInteractorMock.swift b/Tests/TestingUtils/NetworkingInteractorMock.swift index 7942fdac1..286390803 100644 --- a/Tests/TestingUtils/NetworkingInteractorMock.swift +++ b/Tests/TestingUtils/NetworkingInteractorMock.swift @@ -8,6 +8,19 @@ import WalletConnectNetworking public class NetworkingInteractorMock: NetworkInteracting { private(set) var subscriptions: [String] = [] + private(set) var unsubscriptions: [String] = [] + + private(set) var requests: [(topic: String, request: RPCRequest)] = [] + + private(set) var didRespondSuccess = false + private(set) var didRespondError = false + private(set) var didCallSubscribe = false + private(set) var didCallUnsubscribe = false + private(set) var didRespondOnTopic: String? + private(set) var lastErrorCode = -1 + + private(set) var requestCallCount = 0 + var didCallRequest: Bool { requestCallCount > 0 } public let socketConnectionStatusPublisherSubject = PassthroughSubject() public var socketConnectionStatusPublisher: AnyPublisher { @@ -67,33 +80,42 @@ public class NetworkingInteractorMock: NetworkInteracting { public func subscribe(topic: String) async throws { subscriptions.append(topic) + didCallSubscribe = true } func didSubscribe(to topic: String) -> Bool { - subscriptions.contains { $0 == topic } + subscriptions.contains { $0 == topic } } - public func unsubscribe(topic: String) { + func didUnsubscribe(to topic: String) -> Bool { + unsubscriptions.contains { $0 == topic } + } + public func unsubscribe(topic: String) { + unsubscriptions.append(topic) + didCallUnsubscribe = true } public func request(_ request: RPCRequest, topic: String, tag: Int, envelopeType: Envelope.EnvelopeType) async throws { - + requestCallCount += 1 + requests.append((topic, request)) } public func respond(topic: String, response: RPCResponse, tag: Int, envelopeType: Envelope.EnvelopeType) async throws { - + didRespondOnTopic = topic } public func respondSuccess(topic: String, requestId: RPCID, tag: Int, envelopeType: Envelope.EnvelopeType) async throws { - + didRespondSuccess = true } public func respondError(topic: String, requestId: RPCID, tag: Int, reason: Reason, envelopeType: Envelope.EnvelopeType) async throws { - + lastErrorCode = reason.code + didRespondError = true } public func requestNetworkAck(_ request: RPCRequest, topic: String, tag: Int) async throws { - + requestCallCount += 1 + requests.append((topic, request)) } } diff --git a/Tests/WalletConnectSignTests/ApproveEngineTests.swift b/Tests/WalletConnectSignTests/ApproveEngineTests.swift index 88e7a541c..77fc29075 100644 --- a/Tests/WalletConnectSignTests/ApproveEngineTests.swift +++ b/Tests/WalletConnectSignTests/ApproveEngineTests.swift @@ -1,7 +1,9 @@ import XCTest import Combine +import JSONRPC import WalletConnectUtils import WalletConnectPairing +import WalletConnectNetworking @testable import WalletConnectSign @testable import TestingUtils @testable import WalletConnectKMS @@ -14,7 +16,7 @@ final class ApproveEngineTests: XCTestCase { var cryptoMock: KeyManagementServiceMock! var pairingStorageMock: WCPairingStorageMock! var sessionStorageMock: WCSessionStorageMock! - var proposalPayloadsStore: CodableStore! + var proposalPayloadsStore: CodableStore>! var publishers = Set() @@ -24,7 +26,7 @@ final class ApproveEngineTests: XCTestCase { cryptoMock = KeyManagementServiceMock() pairingStorageMock = WCPairingStorageMock() sessionStorageMock = WCSessionStorageMock() - proposalPayloadsStore = CodableStore(defaults: RuntimeKeyValueStorage(), identifier: "") + proposalPayloadsStore = CodableStore>(defaults: RuntimeKeyValueStorage(), identifier: "") engine = ApproveEngine( networkingInteractor: networkingInteractor, proposalPayloadsStore: proposalPayloadsStore, @@ -52,9 +54,8 @@ final class ApproveEngineTests: XCTestCase { pairingStorageMock.setPairing(pairing) let proposerPubKey = AgreementPrivateKey().publicKey.hexRepresentation let proposal = SessionProposal.stub(proposerPubKey: proposerPubKey) - let request = WCRequest(method: .sessionPropose, params: .sessionPropose(proposal)) - let payload = WCRequestSubscriptionPayload(topic: topicA, wcRequest: request) - networkingInteractor.wcRequestPublisherSubject.send(payload) + let request = RPCRequest(method: SignProtocolMethod.sessionPropose.method, params: proposal) + networkingInteractor.requestPublisherSubject.send((topicA, request)) try await engine.approveProposal(proposerPubKey: proposal.proposer.publicKey, validating: SessionNamespace.stubDictionary()) @@ -74,14 +75,13 @@ final class ApproveEngineTests: XCTestCase { var sessionProposed = false let proposerPubKey = AgreementPrivateKey().publicKey.hexRepresentation let proposal = SessionProposal.stub(proposerPubKey: proposerPubKey) - let request = WCRequest(method: .sessionPropose, params: .sessionPropose(proposal)) - let payload = WCRequestSubscriptionPayload(topic: topicA, wcRequest: request) + let request = RPCRequest(method: SignProtocolMethod.sessionPropose.method, params: proposal) engine.onSessionProposal = { _ in sessionProposed = true } - networkingInteractor.wcRequestPublisherSubject.send(payload) + networkingInteractor.requestPublisherSubject.send((topicA, request)) XCTAssertNotNil(try! proposalPayloadsStore.get(key: proposal.proposer.publicKey), "Proposer must store proposal payload") XCTAssertTrue(sessionProposed) } @@ -106,7 +106,9 @@ final class ApproveEngineTests: XCTestCase { } engine.settlingProposal = SessionProposal.stub() - networkingInteractor.wcRequestPublisherSubject.send(WCRequestSubscriptionPayload.stubSettle(topic: sessionTopic)) + networkingInteractor.requestPublisherSubject.send((sessionTopic, RPCRequest.stubSettle())) + + usleep(100) XCTAssertTrue(sessionStorageMock.getSession(forTopic: sessionTopic)!.acknowledged, "Proposer must store acknowledged session on topic B") XCTAssertTrue(networkingInteractor.didRespondSuccess, "Proposer must send acknowledge on settle request") @@ -117,14 +119,10 @@ final class ApproveEngineTests: XCTestCase { let session = WCSession.stub(isSelfController: true, acknowledged: false) sessionStorageMock.setSession(session) - let settleResponse = JSONRPCResponse(id: 1, result: AnyCodable(true)) - let response = WCResponse( - topic: session.topic, - chainId: nil, - requestMethod: .sessionSettle, - requestParams: .sessionSettle(SessionType.SettleParams.stub()), - result: .response(settleResponse)) - networkingInteractor.responsePublisherSubject.send(response) + let request = RPCRequest(method: SignProtocolMethod.sessionSettle.method, params: SessionType.SettleParams.stub()) + let response = RPCResponse(matchingRequest: request, result: RPCResult.response(AnyCodable(true))) + + networkingInteractor.responsePublisherSubject.send((session.topic, request, response)) XCTAssertTrue(sessionStorageMock.getSession(forTopic: session.topic)!.acknowledged, "Responder must acknowledged session") } @@ -136,13 +134,10 @@ final class ApproveEngineTests: XCTestCase { cryptoMock.setAgreementSecret(AgreementKeys.stub(), topic: session.topic) try! cryptoMock.setPrivateKey(privateKey) - let response = WCResponse( - topic: session.topic, - chainId: nil, - requestMethod: .sessionSettle, - requestParams: .sessionSettle(SessionType.SettleParams.stub()), - result: .error(JSONRPCErrorResponse(id: 1, error: JSONRPCErrorResponse.Error(code: 0, message: "")))) - networkingInteractor.responsePublisherSubject.send(response) + let request = RPCRequest(method: SignProtocolMethod.sessionSettle.method, params: SessionType.SettleParams.stub()) + let response = RPCResponse(matchingRequest: request, result: RPCResult.error(JSONRPCError(code: 0, message: ""))) + + networkingInteractor.responsePublisherSubject.send((session.topic, request, response)) XCTAssertNil(sessionStorageMock.getSession(forTopic: session.topic), "Responder must remove session") XCTAssertTrue(networkingInteractor.didUnsubscribe(to: session.topic), "Responder must unsubscribe topic B") diff --git a/Tests/WalletConnectSignTests/Helpers/WCRequest+Extension.swift b/Tests/WalletConnectSignTests/Helpers/WCRequest+Extension.swift deleted file mode 100644 index d3616f365..000000000 --- a/Tests/WalletConnectSignTests/Helpers/WCRequest+Extension.swift +++ /dev/null @@ -1,9 +0,0 @@ -@testable import WalletConnectSign - -extension WCRequest { - - var sessionProposal: SessionProposal? { - guard case .sessionPropose(let proposal) = self.params else { return nil } - return proposal - } -} diff --git a/Tests/WalletConnectSignTests/JsonRpcHistoryTests.swift b/Tests/WalletConnectSignTests/JsonRpcHistoryTests.swift deleted file mode 100644 index dd98115f7..000000000 --- a/Tests/WalletConnectSignTests/JsonRpcHistoryTests.swift +++ /dev/null @@ -1,84 +0,0 @@ -import Foundation -import XCTest -import TestingUtils -import WalletConnectUtils -import WalletConnectPairing -@testable import WalletConnectSign - -final class JsonRpcHistoryTests: XCTestCase { - - var sut: WalletConnectSign.JsonRpcHistory! - - override func setUp() { - sut = JsonRpcHistory(logger: ConsoleLoggerMock(), keyValueStore: CodableStore(defaults: RuntimeKeyValueStorage(), identifier: "")) - } - - override func tearDown() { - sut = nil - } - - func testSetRecord() { - let recordinput = getTestJsonRpcRecordInput() - XCTAssertFalse(sut.exist(id: recordinput.request.id)) - try! sut.set(topic: recordinput.topic, request: recordinput.request) - XCTAssertTrue(sut.exist(id: recordinput.request.id)) - } - - func testGetRecord() { - let recordinput = getTestJsonRpcRecordInput() - XCTAssertNil(sut.get(id: recordinput.request.id)) - try! sut.set(topic: recordinput.topic, request: recordinput.request) - XCTAssertNotNil(sut.get(id: recordinput.request.id)) - } - - func testResolve() { - let recordinput = getTestJsonRpcRecordInput() - try! sut.set(topic: recordinput.topic, request: recordinput.request) - XCTAssertNil(sut.get(id: recordinput.request.id)?.response) - let jsonRpcResponse = JSONRPCResponse(id: recordinput.request.id, result: AnyCodable("")) - let response = JsonRpcResult.response(jsonRpcResponse) - _ = try! sut.resolve(response: response) - XCTAssertNotNil(sut.get(id: jsonRpcResponse.id)?.response) - } - - func testThrowsOnResolveDuplicate() { - let recordinput = getTestJsonRpcRecordInput() - try! sut.set(topic: recordinput.topic, request: recordinput.request) - let jsonRpcResponse = JSONRPCResponse(id: recordinput.request.id, result: AnyCodable("")) - let response = JsonRpcResult.response(jsonRpcResponse) - _ = try! sut.resolve(response: response) - XCTAssertThrowsError(try sut.resolve(response: response)) - } - - func testThrowsOnSetDuplicate() { - let recordinput = getTestJsonRpcRecordInput() - try! sut.set(topic: recordinput.topic, request: recordinput.request) - XCTAssertThrowsError(try sut.set(topic: recordinput.topic, request: recordinput.request)) - } - - func testDelete() { - let recordinput = getTestJsonRpcRecordInput() - try! sut.set(topic: recordinput.topic, request: recordinput.request) - XCTAssertNotNil(sut.get(id: recordinput.request.id)) - sut.delete(topic: testTopic) - XCTAssertNil(sut.get(id: recordinput.request.id)) - } - - func testGetPending() { - let recordinput1 = getTestJsonRpcRecordInput(id: 1) - let recordinput2 = getTestJsonRpcRecordInput(id: 2) - try! sut.set(topic: recordinput1.topic, request: recordinput1.request) - try! sut.set(topic: recordinput2.topic, request: recordinput2.request) - XCTAssertEqual(sut.getPending().count, 2) - let jsonRpcResponse = JSONRPCResponse(id: recordinput1.request.id, result: AnyCodable("")) - let response = JsonRpcResult.response(jsonRpcResponse) - _ = try! sut.resolve(response: response) - XCTAssertEqual(sut.getPending().count, 1) - } -} - -private let testTopic = "test_topic" -private func getTestJsonRpcRecordInput(id: Int64 = 0) -> (topic: String, request: WCRequest) { - let request = WCRequest(id: id, jsonrpc: "2.0", method: .pairingPing, params: WCRequest.Params.pairingPing(PairingType.PingParams())) - return (topic: testTopic, request: request) -} diff --git a/Tests/WalletConnectSignTests/Mocks/MockedRelayClient.swift b/Tests/WalletConnectSignTests/Mocks/MockedRelayClient.swift index eac4dacb5..a371a1e79 100644 --- a/Tests/WalletConnectSignTests/Mocks/MockedRelayClient.swift +++ b/Tests/WalletConnectSignTests/Mocks/MockedRelayClient.swift @@ -3,42 +3,42 @@ import Foundation @testable import WalletConnectRelay @testable import WalletConnectSign -class MockedRelayClient: NetworkRelaying { - - var messagePublisherSubject = PassthroughSubject<(topic: String, message: String), Never>() - var messagePublisher: AnyPublisher<(topic: String, message: String), Never> { - messagePublisherSubject.eraseToAnyPublisher() - } - - var socketConnectionStatusPublisherSubject = PassthroughSubject() - var socketConnectionStatusPublisher: AnyPublisher { - socketConnectionStatusPublisherSubject.eraseToAnyPublisher() - } - - var error: Error? - var prompt = false - - func publish(topic: String, payload: String, tag: Int, prompt: Bool) async throws { - self.prompt = prompt - } - - func publish(topic: String, payload: String, tag: Int, prompt: Bool, onNetworkAcknowledge: @escaping ((Error?) -> Void)) { - self.prompt = prompt - onNetworkAcknowledge(error) - } - - func subscribe(topic: String) async throws {} - - func subscribe(topic: String, completion: @escaping (Error?) -> Void) { - } - - func unsubscribe(topic: String, completion: @escaping ((Error?) -> Void)) { - } - - func connect() { - } - - func disconnect(closeCode: URLSessionWebSocketTask.CloseCode) { - } - -} +//class MockedRelayClient: NetworkRelaying { +// +// var messagePublisherSubject = PassthroughSubject<(topic: String, message: String), Never>() +// var messagePublisher: AnyPublisher<(topic: String, message: String), Never> { +// messagePublisherSubject.eraseToAnyPublisher() +// } +// +// var socketConnectionStatusPublisherSubject = PassthroughSubject() +// var socketConnectionStatusPublisher: AnyPublisher { +// socketConnectionStatusPublisherSubject.eraseToAnyPublisher() +// } +// +// var error: Error? +// var prompt = false +// +// func publish(topic: String, payload: String, tag: Int, prompt: Bool) async throws { +// self.prompt = prompt +// } +// +// func publish(topic: String, payload: String, tag: Int, prompt: Bool, onNetworkAcknowledge: @escaping ((Error?) -> Void)) { +// self.prompt = prompt +// onNetworkAcknowledge(error) +// } +// +// func subscribe(topic: String) async throws {} +// +// func subscribe(topic: String, completion: @escaping (Error?) -> Void) { +// } +// +// func unsubscribe(topic: String, completion: @escaping ((Error?) -> Void)) { +// } +// +// func connect() { +// } +// +// func disconnect(closeCode: URLSessionWebSocketTask.CloseCode) { +// } +// +//} diff --git a/Tests/WalletConnectSignTests/Mocks/NetworkingInteractorMock.swift b/Tests/WalletConnectSignTests/Mocks/NetworkingInteractorMock.swift deleted file mode 100644 index a76d6ef91..000000000 --- a/Tests/WalletConnectSignTests/Mocks/NetworkingInteractorMock.swift +++ /dev/null @@ -1,102 +0,0 @@ -import Foundation -import Combine -import WalletConnectUtils -import WalletConnectPairing -@testable import WalletConnectSign -@testable import TestingUtils - -class NetworkingInteractorMock: NetworkInteracting { - - private(set) var subscriptions: [String] = [] - private(set) var unsubscriptions: [String] = [] - - let transportConnectionPublisherSubject = PassthroughSubject() - let responsePublisherSubject = PassthroughSubject() - let wcRequestPublisherSubject = PassthroughSubject() - - var transportConnectionPublisher: AnyPublisher { - transportConnectionPublisherSubject.eraseToAnyPublisher() - } - var wcRequestPublisher: AnyPublisher { - wcRequestPublisherSubject.eraseToAnyPublisher() - } - var responsePublisher: AnyPublisher { - responsePublisherSubject.eraseToAnyPublisher() - } - - var didCallSubscribe = false - var didRespondOnTopic: String? - var didCallUnsubscribe = false - var didRespondSuccess = false - var didRespondError = false - var lastErrorCode = -1 - var error: Error? - - private(set) var requestCallCount = 0 - var didCallRequest: Bool { requestCallCount > 0 } - - private(set) var requests: [(topic: String, request: WCRequest)] = [] - - func request(topic: String, payload: WCRequest) async throws { - requestCallCount += 1 - requests.append((topic, payload)) - } - - func requestNetworkAck(_ wcMethod: WCMethod, onTopic topic: String, completion: @escaping ((Error?) -> Void)) { - requestCallCount += 1 - requests.append((topic, wcMethod.asRequest())) - completion(nil) - } - - func requestPeerResponse(_ wcMethod: WCMethod, onTopic topic: String, completion: ((Result, JSONRPCErrorResponse>) -> Void)?) { - requestCallCount += 1 - requests.append((topic, wcMethod.asRequest())) - } - - func respond(topic: String, response: JsonRpcResult, completion: @escaping ((Error?) -> Void)) { - didRespondOnTopic = topic - completion(error) - } - - func respond(topic: String, response: JsonRpcResult, tag: Int) async throws { - didRespondOnTopic = topic - } - - func respondSuccess(payload: WCRequestSubscriptionPayload) async throws { - respondSuccess(for: payload) - } - - func respondError(payload: WCRequestSubscriptionPayload, reason: ReasonCode) async throws { - lastErrorCode = reason.code - didRespondError = true - } - - func respondSuccess(for payload: WCRequestSubscriptionPayload) { - didRespondSuccess = true - } - - func subscribe(topic: String) { - subscriptions.append(topic) - didCallSubscribe = true - } - - func unsubscribe(topic: String) { - unsubscriptions.append(topic) - didCallUnsubscribe = true - } - - func sendSubscriptionPayloadOn(topic: String) { - let payload = WCRequestSubscriptionPayload(topic: topic, wcRequest: pingRequest) - wcRequestPublisherSubject.send(payload) - } - - func didSubscribe(to topic: String) -> Bool { - subscriptions.contains { $0 == topic } - } - - func didUnsubscribe(to topic: String) -> Bool { - unsubscriptions.contains { $0 == topic } - } -} - -private let pingRequest = WCRequest(id: 1, jsonrpc: "2.0", method: .pairingPing, params: WCRequest.Params.pairingPing(PairingType.PingParams())) diff --git a/Tests/WalletConnectSignTests/Mocks/SerializerMock.swift b/Tests/WalletConnectSignTests/Mocks/SerializerMock.swift deleted file mode 100644 index 339da3ff8..000000000 --- a/Tests/WalletConnectSignTests/Mocks/SerializerMock.swift +++ /dev/null @@ -1,34 +0,0 @@ -// - -import Foundation -import WalletConnectUtils -@testable import WalletConnectKMS -@testable import WalletConnectSign - -class SerializerMock: Serializing { - var deserialized: Any! - var serialized: String = "" - - func serialize(topic: String, encodable: Encodable, envelopeType: Envelope.EnvelopeType) throws -> String { - try serialize(json: try encodable.json(), agreementKeys: AgreementKeys.stub()) - } - func deserialize(topic: String, encodedEnvelope: String) throws -> T { - return try deserialize(message: encodedEnvelope, symmetricKey: Data()) - } - func deserializeJsonRpc(topic: String, message: String) throws -> Result, JSONRPCErrorResponse> { - .success(try deserialize(message: message, symmetricKey: Data())) - } - - func deserialize(message: String, symmetricKey: Data) throws -> T where T: Codable { - if let deserializedModel = deserialized as? T { - return deserializedModel - } else { - throw NSError.mock() - } - } - - func serialize(json: String, agreementKeys: AgreementKeys) throws -> String { - return serialized - } - -} diff --git a/Tests/WalletConnectSignTests/NonControllerSessionStateMachineTests.swift b/Tests/WalletConnectSignTests/NonControllerSessionStateMachineTests.swift index 47937bd42..8107cfd0d 100644 --- a/Tests/WalletConnectSignTests/NonControllerSessionStateMachineTests.swift +++ b/Tests/WalletConnectSignTests/NonControllerSessionStateMachineTests.swift @@ -2,6 +2,7 @@ import XCTest import WalletConnectUtils @testable import TestingUtils import WalletConnectKMS +import JSONRPC @testable import WalletConnectSign class NonControllerSessionStateMachineTests: XCTestCase { @@ -34,8 +35,9 @@ class NonControllerSessionStateMachineTests: XCTestCase { didCallbackUpdatMethods = true XCTAssertEqual(topic, session.topic) } - networkingInteractor.wcRequestPublisherSubject.send(WCRequestSubscriptionPayload.stubUpdateNamespaces(topic: session.topic)) + networkingInteractor.requestPublisherSubject.send((session.topic, RPCRequest.stubUpdateNamespaces())) XCTAssertTrue(didCallbackUpdatMethods) + usleep(100) XCTAssertTrue(networkingInteractor.didRespondSuccess) } @@ -49,7 +51,7 @@ class NonControllerSessionStateMachineTests: XCTestCase { // } func testUpdateMethodPeerErrorSessionNotFound() { - networkingInteractor.wcRequestPublisherSubject.send(WCRequestSubscriptionPayload.stubUpdateNamespaces(topic: "")) + networkingInteractor.requestPublisherSubject.send(("", RPCRequest.stubUpdateNamespaces())) usleep(100) XCTAssertFalse(networkingInteractor.didRespondSuccess) XCTAssertEqual(networkingInteractor.lastErrorCode, 7001) @@ -58,7 +60,7 @@ class NonControllerSessionStateMachineTests: XCTestCase { func testUpdateMethodPeerErrorUnauthorized() { let session = WCSession.stub(isSelfController: true) // Peer is not a controller storageMock.setSession(session) - networkingInteractor.wcRequestPublisherSubject.send(WCRequestSubscriptionPayload.stubUpdateNamespaces(topic: session.topic)) + networkingInteractor.requestPublisherSubject.send((session.topic, RPCRequest.stubUpdateNamespaces())) usleep(100) XCTAssertFalse(networkingInteractor.didRespondSuccess) XCTAssertEqual(networkingInteractor.lastErrorCode, 3003) @@ -72,7 +74,7 @@ class NonControllerSessionStateMachineTests: XCTestCase { storageMock.setSession(session) let twoDaysFromNowTimestamp = Int64(TimeTraveler.dateByAdding(days: 2).timeIntervalSince1970) - networkingInteractor.wcRequestPublisherSubject.send(WCRequestSubscriptionPayload.stubUpdateExpiry(topic: session.topic, expiry: twoDaysFromNowTimestamp)) + networkingInteractor.requestPublisherSubject.send((session.topic, RPCRequest.stubUpdateExpiry(expiry: twoDaysFromNowTimestamp))) let extendedSession = storageMock.getAll().first {$0.topic == session.topic}! print(extendedSession.expiryDate) @@ -85,7 +87,7 @@ class NonControllerSessionStateMachineTests: XCTestCase { storageMock.setSession(session) let twoDaysFromNowTimestamp = Int64(TimeTraveler.dateByAdding(days: 2).timeIntervalSince1970) - networkingInteractor.wcRequestPublisherSubject.send(WCRequestSubscriptionPayload.stubUpdateExpiry(topic: session.topic, expiry: twoDaysFromNowTimestamp)) + networkingInteractor.requestPublisherSubject.send((session.topic, RPCRequest.stubUpdateExpiry(expiry: twoDaysFromNowTimestamp))) let potentiallyExtendedSession = storageMock.getAll().first {$0.topic == session.topic}! XCTAssertEqual(potentiallyExtendedSession.expiryDate.timeIntervalSinceReferenceDate, tomorrow.timeIntervalSinceReferenceDate, accuracy: 1, "expiry date has been extended for peer non controller request ") @@ -96,7 +98,7 @@ class NonControllerSessionStateMachineTests: XCTestCase { let session = WCSession.stub(isSelfController: false, expiryDate: tomorrow) storageMock.setSession(session) let tenDaysFromNowTimestamp = Int64(TimeTraveler.dateByAdding(days: 10).timeIntervalSince1970) - networkingInteractor.wcRequestPublisherSubject.send(WCRequestSubscriptionPayload.stubUpdateExpiry(topic: session.topic, expiry: tenDaysFromNowTimestamp)) + networkingInteractor.requestPublisherSubject.send((session.topic, RPCRequest.stubUpdateExpiry(expiry: tenDaysFromNowTimestamp))) let potentaillyExtendedSession = storageMock.getAll().first {$0.topic == session.topic}! XCTAssertEqual(potentaillyExtendedSession.expiryDate.timeIntervalSinceReferenceDate, tomorrow.timeIntervalSinceReferenceDate, accuracy: 1, "expiry date has been extended despite ttl to high") @@ -108,7 +110,7 @@ class NonControllerSessionStateMachineTests: XCTestCase { storageMock.setSession(session) let oneDayFromNowTimestamp = Int64(TimeTraveler.dateByAdding(days: 10).timeIntervalSince1970) - networkingInteractor.wcRequestPublisherSubject.send(WCRequestSubscriptionPayload.stubUpdateExpiry(topic: session.topic, expiry: oneDayFromNowTimestamp)) + networkingInteractor.requestPublisherSubject.send((session.topic, RPCRequest.stubUpdateExpiry(expiry: oneDayFromNowTimestamp))) let potentaillyExtendedSession = storageMock.getAll().first {$0.topic == session.topic}! XCTAssertEqual(potentaillyExtendedSession.expiryDate.timeIntervalSinceReferenceDate, tomorrow.timeIntervalSinceReferenceDate, accuracy: 1, "expiry date has been extended despite ttl to low") } diff --git a/Tests/WalletConnectSignTests/PairEngineTests.swift b/Tests/WalletConnectSignTests/PairEngineTests.swift index 9b581672d..66018ff7b 100644 --- a/Tests/WalletConnectSignTests/PairEngineTests.swift +++ b/Tests/WalletConnectSignTests/PairEngineTests.swift @@ -3,6 +3,7 @@ import XCTest @testable import TestingUtils @testable import WalletConnectKMS import WalletConnectUtils +import WalletConnectNetworking final class PairEngineTests: XCTestCase { @@ -11,16 +12,11 @@ final class PairEngineTests: XCTestCase { var networkingInteractor: NetworkingInteractorMock! var storageMock: WCPairingStorageMock! var cryptoMock: KeyManagementServiceMock! - var proposalPayloadsStore: CodableStore! - - var topicGenerator: TopicGenerator! override func setUp() { networkingInteractor = NetworkingInteractorMock() storageMock = WCPairingStorageMock() cryptoMock = KeyManagementServiceMock() - topicGenerator = TopicGenerator() - proposalPayloadsStore = CodableStore(defaults: RuntimeKeyValueStorage(), identifier: "") setupEngine() } diff --git a/Tests/WalletConnectSignTests/PairingEngineTests.swift b/Tests/WalletConnectSignTests/PairingEngineTests.swift index 785be6dc2..6bd6538cc 100644 --- a/Tests/WalletConnectSignTests/PairingEngineTests.swift +++ b/Tests/WalletConnectSignTests/PairingEngineTests.swift @@ -1,5 +1,6 @@ import XCTest import Combine +import JSONRPC @testable import WalletConnectSign @testable import TestingUtils @testable import WalletConnectKMS @@ -78,7 +79,7 @@ final class PairingEngineTests: XCTestCase { try! await engine.propose(pairingTopic: pairing.topic, namespaces: ProposalNamespace.stubDictionary(), relay: relayOptions) guard let publishTopic = networkingInteractor.requests.first?.topic, - let proposal = networkingInteractor.requests.first?.request.sessionProposal else { + let proposal = try? networkingInteractor.requests.first?.request.params?.get(SessionType.ProposeParams.self) else { XCTFail("Proposer must publish a proposal request."); return } XCTAssert(cryptoMock.hasPrivateKey(for: proposal.proposer.publicKey), "Proposer must store the private key matching the public key sent through the proposal.") @@ -96,7 +97,7 @@ final class PairingEngineTests: XCTestCase { try! await engine.propose(pairingTopic: pairing.topic, namespaces: ProposalNamespace.stubDictionary(), relay: relayOptions) guard let request = networkingInteractor.requests.first?.request, - let proposal = networkingInteractor.requests.first?.request.sessionProposal else { + let proposal = try? networkingInteractor.requests.first?.request.params?.get(SessionType.ProposeParams.self) else { XCTFail("Proposer must publish session proposal request"); return } @@ -104,14 +105,9 @@ final class PairingEngineTests: XCTestCase { let responder = Participant.stub() let proposalResponse = SessionType.ProposeResponse(relay: relayOptions, responderPublicKey: responder.publicKey) - let jsonRpcResponse = JSONRPCResponse(id: request.id, result: AnyCodable.decoded(proposalResponse)) - let response = WCResponse(topic: topicA, - chainId: nil, - requestMethod: request.method, - requestParams: request.params, - result: .response(jsonRpcResponse)) + let response = RPCResponse(id: request.id!, result: RPCResult.response(AnyCodable(proposalResponse))) - networkingInteractor.responsePublisherSubject.send(response) + networkingInteractor.responsePublisherSubject.send((topicA, request, response)) let privateKey = try! cryptoMock.getPrivateKey(for: proposal.proposer.publicKey)! let topicB = deriveTopic(publicKey: responder.publicKey, privateKey: privateKey) let storedPairing = storageMock.getPairing(forTopic: topicA)! @@ -133,12 +129,12 @@ final class PairingEngineTests: XCTestCase { try! await engine.propose(pairingTopic: pairing.topic, namespaces: ProposalNamespace.stubDictionary(), relay: relayOptions) guard let request = networkingInteractor.requests.first?.request, - let proposal = networkingInteractor.requests.first?.request.sessionProposal else { + let proposal = try? networkingInteractor.requests.first?.request.params?.get(SessionType.ProposeParams.self) else { XCTFail("Proposer must publish session proposal request"); return } - let response = WCResponse.stubError(forRequest: request, topic: topicA) - networkingInteractor.responsePublisherSubject.send(response) + let response = RPCResponse.stubError(forRequest: request) + networkingInteractor.responsePublisherSubject.send((topicA, request, response)) XCTAssert(networkingInteractor.didUnsubscribe(to: pairing.topic), "Proposer must unsubscribe if pairing is inactive.") XCTAssertFalse(storageMock.hasPairing(forTopic: pairing.topic), "Proposer must delete an inactive pairing.") @@ -157,7 +153,7 @@ final class PairingEngineTests: XCTestCase { try? await engine.propose(pairingTopic: pairing.topic, namespaces: ProposalNamespace.stubDictionary(), relay: relayOptions) guard let request = networkingInteractor.requests.first?.request, - let proposal = networkingInteractor.requests.first?.request.sessionProposal else { + let proposal = try? networkingInteractor.requests.first?.request.params?.get(SessionType.ProposeParams.self) else { XCTFail("Proposer must publish session proposal request"); return } @@ -165,8 +161,8 @@ final class PairingEngineTests: XCTestCase { storedPairing.activate() storageMock.setPairing(storedPairing) - let response = WCResponse.stubError(forRequest: request, topic: topicA) - networkingInteractor.responsePublisherSubject.send(response) + let response = RPCResponse.stubError(forRequest: request) + networkingInteractor.responsePublisherSubject.send((topicA, request, response)) XCTAssertFalse(networkingInteractor.didUnsubscribe(to: pairing.topic), "Proposer must not unsubscribe if pairing is active.") XCTAssert(storageMock.hasPairing(forTopic: pairing.topic), "Proposer must not delete an active pairing.") diff --git a/Tests/WalletConnectSignTests/Stub/Stubs.swift b/Tests/WalletConnectSignTests/Stub/Stubs.swift index a17ad98a8..96ea5eb3b 100644 --- a/Tests/WalletConnectSignTests/Stub/Stubs.swift +++ b/Tests/WalletConnectSignTests/Stub/Stubs.swift @@ -1,5 +1,6 @@ @testable import WalletConnectSign import Foundation +import JSONRPC import WalletConnectKMS import WalletConnectUtils import TestingUtils @@ -59,29 +60,25 @@ extension AgreementPeer { } } -extension WCRequestSubscriptionPayload { +extension RPCRequest { - static func stubUpdateNamespaces(topic: String, namespaces: [String: SessionNamespace] = SessionNamespace.stubDictionary()) -> WCRequestSubscriptionPayload { - let updateMethod = WCMethod.wcSessionUpdate(SessionType.UpdateParams(namespaces: namespaces)).asRequest() - return WCRequestSubscriptionPayload(topic: topic, wcRequest: updateMethod) + static func stubUpdateNamespaces(namespaces: [String: SessionNamespace] = SessionNamespace.stubDictionary()) -> RPCRequest { + return RPCRequest(method: SignProtocolMethod.sessionUpdate.method, params: SessionType.UpdateParams(namespaces: namespaces)) } - static func stubUpdateExpiry(topic: String, expiry: Int64) -> WCRequestSubscriptionPayload { - let updateExpiryMethod = WCMethod.wcSessionExtend(SessionType.UpdateExpiryParams(expiry: expiry)).asRequest() - return WCRequestSubscriptionPayload(topic: topic, wcRequest: updateExpiryMethod) + static func stubUpdateExpiry(expiry: Int64) -> RPCRequest { + return RPCRequest(method: SignProtocolMethod.sessionExtend.method, params: SessionType.UpdateExpiryParams(expiry: expiry)) } - static func stubSettle(topic: String) -> WCRequestSubscriptionPayload { - let method = WCMethod.wcSessionSettle(SessionType.SettleParams.stub()) - return WCRequestSubscriptionPayload(topic: topic, wcRequest: method.asRequest()) + static func stubSettle() -> RPCRequest { + return RPCRequest(method: SignProtocolMethod.sessionSettle.method, params: SessionType.SettleParams.stub()) } - static func stubRequest(topic: String, method: String, chainId: Blockchain) -> WCRequestSubscriptionPayload { + static func stubRequest(method: String, chainId: Blockchain) -> RPCRequest { let params = SessionType.RequestParams( request: SessionType.RequestParams.Request(method: method, params: AnyCodable(EmptyCodable())), chainId: chainId) - let request = WCRequest(method: .sessionRequest, params: .sessionRequest(params)) - return WCRequestSubscriptionPayload(topic: topic, wcRequest: request) + return RPCRequest(method: SignProtocolMethod.sessionRequest.method, params: params) } } @@ -95,15 +92,8 @@ extension SessionProposal { } } -extension WCResponse { - static func stubError(forRequest request: WCRequest, topic: String) -> WCResponse { - let errorResponse = JSONRPCErrorResponse(id: request.id, error: JSONRPCErrorResponse.Error(code: 0, message: "")) - return WCResponse( - topic: topic, - chainId: nil, - requestMethod: request.method, - requestParams: request.params, - result: .error(errorResponse) - ) +extension RPCResponse { + static func stubError(forRequest request: RPCRequest) -> RPCResponse { + return RPCResponse(matchingRequest: request, result: RPCResult.error(JSONRPCError(code: 0, message: ""))) } } diff --git a/Tests/WalletConnectSignTests/WCRelayTests.swift b/Tests/WalletConnectSignTests/WCRelayTests.swift index a42fbd328..aea8037c9 100644 --- a/Tests/WalletConnectSignTests/WCRelayTests.swift +++ b/Tests/WalletConnectSignTests/WCRelayTests.swift @@ -1,72 +1,73 @@ -import Foundation -import Combine -import XCTest -import WalletConnectUtils -import WalletConnectPairing -@testable import TestingUtils -@testable import WalletConnectSign - -class NetworkingInteractorTests: XCTestCase { - var networkingInteractor: NetworkInteractor! - var relayClient: MockedRelayClient! - var serializer: SerializerMock! - - private var publishers = [AnyCancellable]() - - override func setUp() { - let logger = ConsoleLoggerMock() - serializer = SerializerMock() - relayClient = MockedRelayClient() - networkingInteractor = NetworkInteractor(relayClient: relayClient, serializer: serializer, logger: logger, jsonRpcHistory: JsonRpcHistory(logger: logger, keyValueStore: CodableStore(defaults: RuntimeKeyValueStorage(), identifier: ""))) - } - - override func tearDown() { - networkingInteractor = nil - relayClient = nil - serializer = nil - } - - func testNotifiesOnEncryptedWCJsonRpcRequest() { - let requestExpectation = expectation(description: "notifies with request") - let topic = "fefc3dc39cacbc562ed58f92b296e2d65a6b07ef08992b93db5b3cb86280635a" - networkingInteractor.wcRequestPublisher.sink { (_) in - requestExpectation.fulfill() - }.store(in: &publishers) - serializer.deserialized = request - relayClient.messagePublisherSubject.send((topic, testPayload)) - waitForExpectations(timeout: 1.001, handler: nil) - } - - func testPromptOnSessionRequest() async { - let topic = "fefc3dc39cacbc562ed58f92b296e2d65a6b07ef08992b93db5b3cb86280635a" - let method = getWCSessionMethod() - relayClient.prompt = false - try! await networkingInteractor.request(topic: topic, payload: method.asRequest()) - XCTAssertTrue(relayClient.prompt) - } -} - -extension NetworkingInteractorTests { - func getWCSessionMethod() -> WCMethod { - let sessionRequestParams = SessionType.RequestParams(request: SessionType.RequestParams.Request(method: "method", params: AnyCodable("params")), chainId: Blockchain("eip155:1")!) - return .wcSessionRequest(sessionRequestParams) - } -} - -private let testPayload = -""" -{ - "id":1630300527198334, - "jsonrpc":"2.0", - "method":"irn_subscription", - "params":{ - "id":"0847f4e1dd19cf03a43dc7525f39896b630e9da33e4683c8efbc92ea671b5e07", - "data":{ - "topic":"fefc3dc39cacbc562ed58f92b296e2d65a6b07ef08992b93db5b3cb86280635a", - "message":"7b226964223a313633303330303532383030302c226a736f6e727063223a22322e30222c22726573756c74223a747275657d" - } - } -} -""" -// TODO - change for different request -private let request = WCRequest(id: 1, jsonrpc: "2.0", method: .pairingPing, params: WCRequest.Params.pairingPing(PairingType.PingParams())) +//import Foundation +//import Combine +//import XCTest +//import WalletConnectUtils +//import WalletConnectPairing +//import WalletConnectNetworking +//@testable import TestingUtils +//@testable import WalletConnectSign +// +//class NetworkingInteractorTests: XCTestCase { +// var networkingInteractor: NetworkingInteractor! +// var relayClient: MockedRelayClient! +// var serializer: SerializerMock! +// +// private var publishers = [AnyCancellable]() +// +// override func setUp() { +// let logger = ConsoleLoggerMock() +// serializer = SerializerMock() +// relayClient = MockedRelayClient() +// networkingInteractor = NetworkingInteractor(relayClient: relayClient, serializer: serializer, logger: logger, rpcHistory: RPCHistory(logger: logger, keyValueStore: CodableStore(defaults: RuntimeKeyValueStorage(), identifier: ""))) +// } +// +// override func tearDown() { +// networkingInteractor = nil +// relayClient = nil +// serializer = nil +// } +// +// func testNotifiesOnEncryptedWCJsonRpcRequest() { +// let requestExpectation = expectation(description: "notifies with request") +// let topic = "fefc3dc39cacbc562ed58f92b296e2d65a6b07ef08992b93db5b3cb86280635a" +// networkingInteractor.wcRequestPublisher.sink { (_) in +// requestExpectation.fulfill() +// }.store(in: &publishers) +// serializer.deserialized = request +// relayClient.messagePublisherSubject.send((topic, testPayload)) +// waitForExpectations(timeout: 1.001, handler: nil) +// } +// +// func testPromptOnSessionRequest() async { +// let topic = "fefc3dc39cacbc562ed58f92b296e2d65a6b07ef08992b93db5b3cb86280635a" +// let method = getWCSessionMethod() +// relayClient.prompt = false +// try! await networkingInteractor.request(topic: topic, payload: method.asRequest()) +// XCTAssertTrue(relayClient.prompt) +// } +//} +// +//extension NetworkingInteractorTests { +// func getWCSessionMethod() -> WCMethod { +// let sessionRequestParams = SessionType.RequestParams(request: SessionType.RequestParams.Request(method: "method", params: AnyCodable("params")), chainId: Blockchain("eip155:1")!) +// return .wcSessionRequest(sessionRequestParams) +// } +//} +// +//private let testPayload = +//""" +//{ +// "id":1630300527198334, +// "jsonrpc":"2.0", +// "method":"irn_subscription", +// "params":{ +// "id":"0847f4e1dd19cf03a43dc7525f39896b630e9da33e4683c8efbc92ea671b5e07", +// "data":{ +// "topic":"fefc3dc39cacbc562ed58f92b296e2d65a6b07ef08992b93db5b3cb86280635a", +// "message":"7b226964223a313633303330303532383030302c226a736f6e727063223a22322e30222c22726573756c74223a747275657d" +// } +// } +//} +//""" +//// TODO - change for different request +//private let request = WCRequest(id: 1, jsonrpc: "2.0", method: .pairingPing, params: WCRequest.Params.pairingPing(PairingType.PingParams())) diff --git a/Tests/WalletConnectSignTests/WCResponseTests.swift b/Tests/WalletConnectSignTests/WCResponseTests.swift index 24729924b..b4d814d9e 100644 --- a/Tests/WalletConnectSignTests/WCResponseTests.swift +++ b/Tests/WalletConnectSignTests/WCResponseTests.swift @@ -1,17 +1,15 @@ import XCTest +import JSONRPC @testable import WalletConnectSign -final class WCResponseTests: XCTestCase { +final class RPCIDTests: XCTestCase { func testTimestamp() { - let request = WCRequest( - method: .pairingPing, - params: .pairingPing(.init()) - ) - let response = WCResponse.stubError(forRequest: request, topic: "topic") - let timestamp = Date(timeIntervalSince1970: TimeInterval(request.id / 1000 / 1000)) + let request = RPCRequest(method: "method") + let response = RPCResponse(matchingRequest: request, error: JSONRPCError(code: 0, message: "message")) + let timestamp = Date(timeIntervalSince1970: TimeInterval(request.id!.right! / 1000 / 1000)) - XCTAssertEqual(response.timestamp, timestamp) - XCTAssertTrue(Calendar.current.isDateInToday(response.timestamp)) + XCTAssertEqual(response.id!.timestamp, timestamp) + XCTAssertTrue(Calendar.current.isDateInToday(response.id!.timestamp)) } } From 3a72916eaf9aac2138f088fd7ec6cbc160b1eab2 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Fri, 9 Sep 2022 14:15:16 +0300 Subject: [PATCH 035/175] Sample apps build errors fixed --- .../DApp/Sign/ResponseViewController.swift | 10 ++++----- .../SessionDetailViewController.swift | 13 +++++------ .../SessionDetailViewModel.swift | 20 +++++++++++------ .../Wallet/WalletViewController.swift | 13 +++++------ .../Sign/Helpers/ClientDelegate.swift | 11 +++------- .../Sign/SignClientTests.swift | 4 ++-- .../Engine/Common/SessionEngine.swift | 1 + Sources/WalletConnectSign/Response.swift | 1 + .../WalletConnectSign/Sign/SignClient.swift | 22 ++++++------------- 9 files changed, 42 insertions(+), 53 deletions(-) diff --git a/Example/DApp/Sign/ResponseViewController.swift b/Example/DApp/Sign/ResponseViewController.swift index 4f8d1c0b3..eec81065f 100644 --- a/Example/DApp/Sign/ResponseViewController.swift +++ b/Example/DApp/Sign/ResponseViewController.swift @@ -23,14 +23,14 @@ class ResponseViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - let record = Sign.instance.getSessionRequestRecord(id: response.result.id)! + let record = Sign.instance.getSessionRequestRecord(id: response.id)! switch response.result { case .response(let response): - responseView.nameLabel.text = "Received Response\n\(record.request.method)" - responseView.descriptionLabel.text = try! response.result.get(String.self).description + responseView.nameLabel.text = "Received Response\n\(record.method)" + responseView.descriptionLabel.text = try! response.get(String.self).description case .error(let error): - responseView.nameLabel.text = "Received Error\n\(record.request.method)" - responseView.descriptionLabel.text = error.error.message + responseView.nameLabel.text = "Received Error\n\(record.method)" + responseView.descriptionLabel.text = error.message } responseView.dismissButton.addTarget(self, action: #selector(dismissSelf), for: .touchUpInside) } diff --git a/Example/ExampleApp/SessionDetails/SessionDetailViewController.swift b/Example/ExampleApp/SessionDetails/SessionDetailViewController.swift index 4d7d6b39e..f1b2a05bc 100644 --- a/Example/ExampleApp/SessionDetails/SessionDetailViewController.swift +++ b/Example/ExampleApp/SessionDetails/SessionDetailViewController.swift @@ -24,8 +24,7 @@ final class SessionDetailViewController: UIHostingController let viewController = RequestViewController(request) viewController.onSign = { [unowned self] in let result = Signer.signEth(request: request) - let response = JSONRPCResponse(id: request.id, result: result) - respondOnSign(request: request, response: response) + respondOnSign(request: request, response: result) reload() } viewController.onReject = { [unowned self] in @@ -35,11 +34,11 @@ final class SessionDetailViewController: UIHostingController present(viewController, animated: true) } - private func respondOnSign(request: Request, response: JSONRPCResponse) { + private func respondOnSign(request: Request, response: AnyCodable) { print("[WALLET] Respond on Sign") Task { do { - try await Sign.instance.respond(topic: request.topic, response: .response(response)) + try await Sign.instance.respond(topic: request.topic, requestId: request.id, response: .response(response)) } catch { print("[DAPP] Respond Error: \(error.localizedDescription)") } @@ -52,10 +51,8 @@ final class SessionDetailViewController: UIHostingController do { try await Sign.instance.respond( topic: request.topic, - response: .error(JSONRPCErrorResponse( - id: request.id, - error: JSONRPCErrorResponse.Error(code: 0, message: "")) - ) + requestId: request.id, + response: .error(.init(code: 0, message: "")) ) } catch { print("[DAPP] Respond Error: \(error.localizedDescription)") diff --git a/Example/ExampleApp/SessionDetails/SessionDetailViewModel.swift b/Example/ExampleApp/SessionDetails/SessionDetailViewModel.swift index f29dea255..88fe381b1 100644 --- a/Example/ExampleApp/SessionDetails/SessionDetailViewModel.swift +++ b/Example/ExampleApp/SessionDetails/SessionDetailViewModel.swift @@ -7,6 +7,8 @@ final class SessionDetailViewModel: ObservableObject { private let session: Session private let client: SignClient + private var publishers = Set() + enum Fields { case accounts case methods @@ -22,6 +24,8 @@ final class SessionDetailViewModel: ObservableObject { self.session = session self.client = client self.namespaces = session.namespaces + + setupSubscriptions() } var peerName: String { @@ -81,13 +85,8 @@ final class SessionDetailViewModel: ObservableObject { } func ping() { - client.ping(topic: session.topic) { result in - switch result { - case .success: - self.pingSuccess = true - case .failure: - self.pingFailed = true - } + Task(priority: .userInitiated) { + try await client.ping(topic: session.topic) } } @@ -98,6 +97,13 @@ final class SessionDetailViewModel: ObservableObject { private extension SessionDetailViewModel { + func setupSubscriptions() { + client.pingResponsePublisher.sink { _ in + self.pingSuccess = true + } + .store(in: &publishers) + } + func addTestAccount(for chain: String) { guard let viewModel = namespace(for: chain) else { return } diff --git a/Example/ExampleApp/Wallet/WalletViewController.swift b/Example/ExampleApp/Wallet/WalletViewController.swift index 9d574ea34..c3bbacdae 100644 --- a/Example/ExampleApp/Wallet/WalletViewController.swift +++ b/Example/ExampleApp/Wallet/WalletViewController.swift @@ -71,8 +71,7 @@ final class WalletViewController: UIViewController { let requestVC = RequestViewController(request) requestVC.onSign = { [unowned self] in let result = Signer.signEth(request: request) - let response = JSONRPCResponse(id: request.id, result: result) - respondOnSign(request: request, response: response) + respondOnSign(request: request, response: result) reloadSessionDetailsIfNeeded() } requestVC.onReject = { [unowned self] in @@ -90,11 +89,11 @@ final class WalletViewController: UIViewController { } @MainActor - private func respondOnSign(request: Request, response: JSONRPCResponse) { + private func respondOnSign(request: Request, response: AnyCodable) { print("[WALLET] Respond on Sign") Task { do { - try await Sign.instance.respond(topic: request.topic, response: .response(response)) + try await Sign.instance.respond(topic: request.topic, requestId: request.id, response: .response(response)) } catch { print("[DAPP] Respond Error: \(error.localizedDescription)") } @@ -108,10 +107,8 @@ final class WalletViewController: UIViewController { do { try await Sign.instance.respond( topic: request.topic, - response: .error(JSONRPCErrorResponse( - id: request.id, - error: JSONRPCErrorResponse.Error(code: 0, message: "")) - ) + requestId: request.id, + response: .error(.init(code: 0, message: "")) ) } catch { print("[DAPP] Respond Error: \(error.localizedDescription)") diff --git a/Example/IntegrationTests/Sign/Helpers/ClientDelegate.swift b/Example/IntegrationTests/Sign/Helpers/ClientDelegate.swift index ab7b43e11..e299d0316 100644 --- a/Example/IntegrationTests/Sign/Helpers/ClientDelegate.swift +++ b/Example/IntegrationTests/Sign/Helpers/ClientDelegate.swift @@ -14,8 +14,7 @@ class ClientDelegate { var onSessionDelete: (() -> Void)? var onSessionUpdateNamespaces: ((String, [String: SessionNamespace]) -> Void)? var onSessionExtend: ((String, Date) -> Void)? - var onSessionPing: ((String) -> Void)? - var onPairingPing: ((String) -> Void)? + var onPing: ((String) -> Void)? var onEventReceived: ((Session.Event, String) -> Void)? private var publishers = Set() @@ -66,12 +65,8 @@ class ClientDelegate { self.onSessionExtend?(topic, date) }.store(in: &publishers) - client.sessionPingResponsePublisher.sink { topic in - self.onSessionPing?(topic) - }.store(in: &publishers) - - client.pairingPingResponsePublisher.sink { topic in - self.onPairingPing?(topic) + client.pingResponsePublisher.sink { topic in + self.onPing?(topic) }.store(in: &publishers) } } diff --git a/Example/IntegrationTests/Sign/SignClientTests.swift b/Example/IntegrationTests/Sign/SignClientTests.swift index 8a30c72cc..17524d541 100644 --- a/Example/IntegrationTests/Sign/SignClientTests.swift +++ b/Example/IntegrationTests/Sign/SignClientTests.swift @@ -145,7 +145,7 @@ final class SignClientTests: XCTestCase { let pairing = wallet.client.getPairings().first! - wallet.onPairingPing = { topic in + wallet.onPing = { topic in XCTAssertEqual(topic, pairing.topic) pongResponseExpectation.fulfill() } @@ -173,7 +173,7 @@ final class SignClientTests: XCTestCase { } } - dapp.onSessionPing = { topic in + dapp.onPing = { topic in let session = self.wallet.client.getSessions().first! XCTAssertEqual(topic, session.topic) expectation.fulfill() diff --git a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift index 1372c8fe6..e210a3444 100644 --- a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift @@ -126,6 +126,7 @@ private extension SessionEngine { networkingInteractor.responseSubscription(on: SignProtocolMethod.sessionRequest) .sink { [unowned self] (payload: ResponseSubscriptionPayload) in onSessionResponse?(Response( + id: payload.id, topic: payload.topic, chainId: payload.request.chainId.absoluteString, result: payload.response diff --git a/Sources/WalletConnectSign/Response.swift b/Sources/WalletConnectSign/Response.swift index c01e438e5..aaaaf02fa 100644 --- a/Sources/WalletConnectSign/Response.swift +++ b/Sources/WalletConnectSign/Response.swift @@ -3,6 +3,7 @@ import JSONRPC import WalletConnectUtils public struct Response: Codable { + public let id: RPCID public let topic: String public let chainId: String? public let result: RPCResult diff --git a/Sources/WalletConnectSign/Sign/SignClient.swift b/Sources/WalletConnectSign/Sign/SignClient.swift index 4d343153a..83ba5cbb0 100644 --- a/Sources/WalletConnectSign/Sign/SignClient.swift +++ b/Sources/WalletConnectSign/Sign/SignClient.swift @@ -87,15 +87,8 @@ public final class SignClient { /// Publisher that sends session topic when session ping received /// /// Event will be emited on controller and non-controller clients. - public var sessionPingResponsePublisher: AnyPublisher { - sessionPingResponsePublisherSubject.eraseToAnyPublisher() - } - - /// Publisher that sends pairing topic when pairing ping received - /// - /// Event will be emited on controller and non-controller clients. - public var pairingPingResponsePublisher: AnyPublisher { - pairingPingResponsePublisherSubject.eraseToAnyPublisher() + public var pingResponsePublisher: AnyPublisher { + pingResponsePublisherSubject.eraseToAnyPublisher() } /// An object that loggs SDK's errors and info messages @@ -126,8 +119,7 @@ public final class SignClient { private let sessionUpdatePublisherSubject = PassthroughSubject<(sessionTopic: String, namespaces: [String: SessionNamespace]), Never>() private let sessionEventPublisherSubject = PassthroughSubject<(event: Session.Event, sessionTopic: String, chainId: Blockchain?), Never>() private let sessionExtendPublisherSubject = PassthroughSubject<(sessionTopic: String, date: Date), Never>() - private let sessionPingResponsePublisherSubject = PassthroughSubject() - private let pairingPingResponsePublisherSubject = PassthroughSubject() + private let pingResponsePublisherSubject = PassthroughSubject() private var publishers = Set() @@ -324,9 +316,9 @@ public final class SignClient { /// - Parameter id: id of a wc_sessionRequest jsonrpc request /// - Returns: json rpc record object for given id or nil if record for give id does not exits - public func getSessionRequestRecord(id: Int64) -> Request? { + public func getSessionRequestRecord(id: RPCID) -> Request? { guard - let record = history.get(recordId: RPCID(id)), + let record = history.get(recordId: id), let request = try? record.request.params?.get(SessionType.RequestParams.self) else { return nil } @@ -379,10 +371,10 @@ public final class SignClient { sessionResponsePublisherSubject.send(response) } pairingPingService.onResponse = { [unowned self] topic in - pairingPingResponsePublisherSubject.send(topic) + pingResponsePublisherSubject.send(topic) } sessionPingService.onResponse = { [unowned self] topic in - sessionPingResponsePublisherSubject.send(topic) + pingResponsePublisherSubject.send(topic) } } From 68afd51fd272740d00d0d58b729abcb65cff7ce8 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Mon, 12 Sep 2022 14:10:06 +0300 Subject: [PATCH 036/175] Sample apps and UI tests repaired --- .../SelectChain/SelectChainViewController.swift | 4 ---- Example/DApp/Sign/SignCoordinator.swift | 10 +++++++--- Example/ExampleApp/SceneDelegate.swift | 15 +++++++++++++++ .../SessionDetails/SessionDetailViewModel.swift | 10 ++++++---- .../SessionDetailsViewController.swift | 8 ++++---- .../ExampleApp/Wallet/WalletViewController.swift | 4 +--- .../Engine/Common/PairingEngine.swift | 2 +- 7 files changed, 34 insertions(+), 19 deletions(-) diff --git a/Example/DApp/Sign/SelectChain/SelectChainViewController.swift b/Example/DApp/Sign/SelectChain/SelectChainViewController.swift index 74210f740..3a78d7efc 100644 --- a/Example/DApp/Sign/SelectChain/SelectChainViewController.swift +++ b/Example/DApp/Sign/SelectChain/SelectChainViewController.swift @@ -15,16 +15,12 @@ class SelectChainViewController: UIViewController, UITableViewDataSource { private var publishers = [AnyCancellable]() let chains = [Chain(name: "Ethereum", id: "eip155:1"), Chain(name: "Polygon", id: "eip155:137")] - var onSessionSettled: ((Session) -> Void)? override func viewDidLoad() { super.viewDidLoad() navigationItem.title = "Available Chains" selectChainView.tableView.dataSource = self selectChainView.connectButton.addTarget(self, action: #selector(connect), for: .touchUpInside) selectChainView.openWallet.addTarget(self, action: #selector(openWallet), for: .touchUpInside) - Sign.instance.sessionSettlePublisher.sink {[unowned self] session in - onSessionSettled?(session) - }.store(in: &publishers) } override func loadView() { diff --git a/Example/DApp/Sign/SignCoordinator.swift b/Example/DApp/Sign/SignCoordinator.swift index 1373e4c30..eec11715c 100644 --- a/Example/DApp/Sign/SignCoordinator.swift +++ b/Example/DApp/Sign/SignCoordinator.swift @@ -44,6 +44,12 @@ final class SignCoordinator { presentResponse(for: response) }.store(in: &publishers) + Sign.instance.sessionSettlePublisher + .receive(on: DispatchQueue.main) + .sink { [unowned self] session in + showAccountsScreen(session) + }.store(in: &publishers) + if let session = Sign.instance.getSessions().first { showAccountsScreen(session) } else { @@ -53,9 +59,6 @@ final class SignCoordinator { private func showSelectChainScreen() { let controller = SelectChainViewController() - controller.onSessionSettled = { [unowned self] session in - showAccountsScreen(session) - } navigationController.viewControllers = [controller] } @@ -64,6 +67,7 @@ final class SignCoordinator { controller.onDisconnect = { [unowned self] in showSelectChainScreen() } + navigationController.presentedViewController?.dismiss(animated: true) navigationController.viewControllers = [controller] } diff --git a/Example/ExampleApp/SceneDelegate.swift b/Example/ExampleApp/SceneDelegate.swift index 1a394d02a..c8cb4731d 100644 --- a/Example/ExampleApp/SceneDelegate.swift +++ b/Example/ExampleApp/SceneDelegate.swift @@ -1,4 +1,5 @@ import UIKit +import Combine import WalletConnectSign import WalletConnectRelay import Starscream @@ -15,6 +16,10 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? + private var publishers: Set = [] + private var onConnected: (() -> Void)? + private var connectionStatus: SocketConnectionStatus = .disconnected + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { let metadata = AppMetadata( @@ -35,6 +40,16 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { window = UIWindow(windowScene: windowScene) window?.rootViewController = UITabBarController.createExampleApp() window?.makeKeyAndVisible() + + Sign.instance.socketConnectionStatusPublisher + .receive(on: DispatchQueue.main) + .sink { [unowned self] status in + self.connectionStatus = status + if status == .connected { + self.onConnected?() + self.onConnected = nil + } + }.store(in: &publishers) } func scene(_ scene: UIScene, continue userActivity: NSUserActivity) { diff --git a/Example/ExampleApp/SessionDetails/SessionDetailViewModel.swift b/Example/ExampleApp/SessionDetails/SessionDetailViewModel.swift index 88fe381b1..d43a5ce3d 100644 --- a/Example/ExampleApp/SessionDetails/SessionDetailViewModel.swift +++ b/Example/ExampleApp/SessionDetails/SessionDetailViewModel.swift @@ -98,10 +98,12 @@ final class SessionDetailViewModel: ObservableObject { private extension SessionDetailViewModel { func setupSubscriptions() { - client.pingResponsePublisher.sink { _ in - self.pingSuccess = true - } - .store(in: &publishers) + client.pingResponsePublisher + .receive(on: DispatchQueue.main) + .sink { _ in + self.pingSuccess = true + } + .store(in: &publishers) } func addTestAccount(for chain: String) { diff --git a/Example/ExampleApp/SessionDetails/SessionDetailsViewController.swift b/Example/ExampleApp/SessionDetails/SessionDetailsViewController.swift index f53ecbdef..bb6d21be6 100644 --- a/Example/ExampleApp/SessionDetails/SessionDetailsViewController.swift +++ b/Example/ExampleApp/SessionDetails/SessionDetailsViewController.swift @@ -49,11 +49,11 @@ final class SessionDetailsViewController: UIViewController, UITableViewDelegate, @objc private func ping() { - Sign.instance.ping(topic: session.topic) { result in - switch result { - case .success: + Task(priority: .userInitiated) { @MainActor in + do { + try await Sign.instance.ping(topic: session.topic) print("received ping response") - case .failure(let error): + } catch { print(error) } } diff --git a/Example/ExampleApp/Wallet/WalletViewController.swift b/Example/ExampleApp/Wallet/WalletViewController.swift index c3bbacdae..fbc5322db 100644 --- a/Example/ExampleApp/Wallet/WalletViewController.swift +++ b/Example/ExampleApp/Wallet/WalletViewController.swift @@ -10,7 +10,6 @@ final class WalletViewController: UIViewController { lazy var account = Signer.privateKey.address.hex(eip55: true) var sessionItems: [ActiveSessionItem] = [] var currentProposal: Session.Proposal? - var onClientConnected: (() -> Void)? private var publishers = [AnyCancellable]() private let walletView: WalletView = { @@ -236,9 +235,8 @@ extension WalletViewController { func setUpAuthSubscribing() { Sign.instance.socketConnectionStatusPublisher .receive(on: DispatchQueue.main) - .sink { [weak self] status in + .sink { status in if status == .connected { - self?.onClientConnected?() print("Client connected") } }.store(in: &publishers) diff --git a/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift b/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift index 7c75d9876..cac8290f8 100644 --- a/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift @@ -73,7 +73,7 @@ final class PairingEngine { requiredNamespaces: namespaces) let request = RPCRequest(method: SignProtocolMethod.sessionPropose.method, params: proposal) - try await networkingInteractor.request(request, topic: pairingTopic, tag: SignProtocolMethod.sessionPropose.requestTag) + try await networkingInteractor.requestNetworkAck(request, topic: pairingTopic, tag: SignProtocolMethod.sessionPropose.requestTag) } } From 774b0fc789ff545bbdede3586193191e906629c9 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Mon, 12 Sep 2022 21:08:51 +0300 Subject: [PATCH 037/175] testSessionPing duplicate removed --- .../Sign/SignClientTests.swift | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/Example/IntegrationTests/Sign/SignClientTests.swift b/Example/IntegrationTests/Sign/SignClientTests.swift index 17524d541..a62321a05 100644 --- a/Example/IntegrationTests/Sign/SignClientTests.swift +++ b/Example/IntegrationTests/Sign/SignClientTests.swift @@ -314,27 +314,6 @@ final class SignClientTests: XCTestCase { } - func testSessionPing() async { - let expectation = expectation(description: "Dapp receives ping response") - let requiredNamespaces = ProposalNamespace.stubRequired() - let sessionNamespaces = SessionNamespace.make(toRespond: requiredNamespaces) - - wallet.onSessionProposal = { [unowned self] proposal in - Task(priority: .high) { - try await wallet.client.approve(proposalId: proposal.id, namespaces: sessionNamespaces) - } - } - dapp.onSessionSettled = { [unowned self] settledSession in - dapp.client.ping(topic: settledSession.topic) {_ in - expectation.fulfill() - } - } - let uri = try! await dapp.client.connect(requiredNamespaces: requiredNamespaces) - try! await wallet.client.pair(uri: uri!) - wait(for: [expectation], timeout: defaultTimeout) - } - - func testSuccessfulSessionUpdateNamespaces() async { let expectation = expectation(description: "Dapp updates namespaces") let requiredNamespaces = ProposalNamespace.stubRequired() From 5f1bd7d88f3888475a3fba59940c76e6d4800ff1 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Tue, 13 Sep 2022 02:35:07 +0300 Subject: [PATCH 038/175] Wallet sample app fixed --- Example/DApp/Sign/SignCoordinator.swift | 2 +- Example/ExampleApp/SceneDelegate.swift | 15 +-------------- .../Engine/Common/PairingEngine.swift | 1 - .../Engine/Common/SessionEngine.swift | 1 - 4 files changed, 2 insertions(+), 17 deletions(-) diff --git a/Example/DApp/Sign/SignCoordinator.swift b/Example/DApp/Sign/SignCoordinator.swift index eec11715c..8470ad200 100644 --- a/Example/DApp/Sign/SignCoordinator.swift +++ b/Example/DApp/Sign/SignCoordinator.swift @@ -67,7 +67,7 @@ final class SignCoordinator { controller.onDisconnect = { [unowned self] in showSelectChainScreen() } - navigationController.presentedViewController?.dismiss(animated: true) + navigationController.presentedViewController?.dismiss(animated: false) navigationController.viewControllers = [controller] } diff --git a/Example/ExampleApp/SceneDelegate.swift b/Example/ExampleApp/SceneDelegate.swift index c8cb4731d..f52c84ae6 100644 --- a/Example/ExampleApp/SceneDelegate.swift +++ b/Example/ExampleApp/SceneDelegate.swift @@ -1,4 +1,5 @@ import UIKit +import Foundation import Combine import WalletConnectSign import WalletConnectRelay @@ -16,10 +17,6 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? - private var publishers: Set = [] - private var onConnected: (() -> Void)? - private var connectionStatus: SocketConnectionStatus = .disconnected - func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { let metadata = AppMetadata( @@ -40,16 +37,6 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { window = UIWindow(windowScene: windowScene) window?.rootViewController = UITabBarController.createExampleApp() window?.makeKeyAndVisible() - - Sign.instance.socketConnectionStatusPublisher - .receive(on: DispatchQueue.main) - .sink { [unowned self] status in - self.connectionStatus = status - if status == .connected { - self.onConnected?() - self.onConnected = nil - } - }.store(in: &publishers) } func scene(_ scene: UIScene, continue userActivity: NSUserActivity) { diff --git a/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift b/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift index cac8290f8..d6a012de0 100644 --- a/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift @@ -84,7 +84,6 @@ private extension PairingEngine { func setupNetworkingSubscriptions() { networkingInteractor.socketConnectionStatusPublisher .sink { [unowned self] status in - guard status == .connected else { return } pairingStore.getAll() .forEach { pairing in Task(priority: .high) { try await networkingInteractor.subscribe(topic: pairing.topic) } diff --git a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift index e210a3444..2c9c2be07 100644 --- a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift @@ -91,7 +91,6 @@ private extension SessionEngine { func setupConnectionSubscriptions() { networkingInteractor.socketConnectionStatusPublisher .sink { [unowned self] status in - guard status == .connected else { return } sessionStore.getAll() .forEach { session in Task(priority: .high) { try await networkingInteractor.subscribe(topic: session.topic) } From f91bcca558bd722c1c1c4cb59890892c7e49b95c Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Tue, 13 Sep 2022 16:06:32 +0300 Subject: [PATCH 039/175] UI Tests improvements --- .../ExampleApp/Wallet/WalletViewController.swift | 5 ++++- Example/UITests/Engine/WalletEngine.swift | 8 ++++++++ Example/UITests/Regression/RegressionTests.swift | 15 ++++++++------- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/Example/ExampleApp/Wallet/WalletViewController.swift b/Example/ExampleApp/Wallet/WalletViewController.swift index fbc5322db..d53343480 100644 --- a/Example/ExampleApp/Wallet/WalletViewController.swift +++ b/Example/ExampleApp/Wallet/WalletViewController.swift @@ -12,6 +12,8 @@ final class WalletViewController: UIViewController { var currentProposal: Session.Proposal? private var publishers = [AnyCancellable]() + var onClientConnected: (() -> Void)? + private let walletView: WalletView = { WalletView() }() @@ -235,9 +237,10 @@ extension WalletViewController { func setUpAuthSubscribing() { Sign.instance.socketConnectionStatusPublisher .receive(on: DispatchQueue.main) - .sink { status in + .sink { [weak self] status in if status == .connected { print("Client connected") + self?.onClientConnected?() } }.store(in: &publishers) diff --git a/Example/UITests/Engine/WalletEngine.swift b/Example/UITests/Engine/WalletEngine.swift index 536e45443..2255d87b8 100644 --- a/Example/UITests/Engine/WalletEngine.swift +++ b/Example/UITests/Engine/WalletEngine.swift @@ -45,7 +45,15 @@ struct WalletEngine { instance.buttons["Ping"] } + var okButton: XCUIElement { + instance.buttons["OK"] + } + var pingAlert: XCUIElement { instance.alerts.element.staticTexts["Received ping response"] } + + func swipeDismiss() { + instance.swipeDown(velocity: .fast) + } } diff --git a/Example/UITests/Regression/RegressionTests.swift b/Example/UITests/Regression/RegressionTests.swift index 716ba8702..4d3016b60 100644 --- a/Example/UITests/Regression/RegressionTests.swift +++ b/Example/UITests/Regression/RegressionTests.swift @@ -4,15 +4,13 @@ class PairingTests: XCTestCase { private let engine: Engine = Engine() - private static var cleanLaunch: Bool = true - - override func setUp() { - engine.routing.launch(app: .dapp, clean: PairingTests.cleanLaunch) - engine.routing.launch(app: .wallet, clean: PairingTests.cleanLaunch) - - PairingTests.cleanLaunch = false + override class func setUp() { + let engine: Engine = Engine() + engine.routing.launch(app: .dapp, clean: true) + engine.routing.launch(app: .wallet, clean: true) } + /// Check pairing proposal approval via QR code or uri /// - TU001 func test01PairingCreation() { @@ -45,6 +43,9 @@ class PairingTests: XCTestCase { engine.wallet.pingButton.waitTap() XCTAssertTrue(engine.wallet.pingAlert.waitExists()) + + engine.wallet.okButton.waitTap() + engine.wallet.swipeDismiss() } /// Approve session on existing pairing From 003111479837e8abc93e5407076b1328b643c325 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Wed, 14 Sep 2022 21:18:11 +0300 Subject: [PATCH 040/175] Approve Engine RPCResult replaced --- .../Engine/Common/ApproveEngine.swift | 116 ++++++++++-------- 1 file changed, 62 insertions(+), 54 deletions(-) diff --git a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift index 398e1566a..5a23eb079 100644 --- a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift @@ -12,6 +12,7 @@ final class ApproveEngine { case relayNotFound case proposalPayloadsNotFound case pairingNotFound + case sessionNotFound case agreementMissingOrInvalid } @@ -53,6 +54,7 @@ final class ApproveEngine { setupRequestSubscriptions() setupResponseSubscriptions() + setupResponseErrorSubscriptions() } func approveProposal(proposerPubKey: String, validating sessionNamespaces: [String: SessionNamespace]) async throws { @@ -165,21 +167,25 @@ private extension ApproveEngine { func setupResponseSubscriptions() { networkingInteractor.responseSubscription(on: SignProtocolMethod.sessionPropose) - .sink { [unowned self] (payload: ResponseSubscriptionPayload) in + .sink { [unowned self] (payload: ResponseSubscriptionPayload) in handleSessionProposeResponse(payload: payload) }.store(in: &publishers) networkingInteractor.responseSubscription(on: SignProtocolMethod.sessionSettle) - .sink { [unowned self] (payload: ResponseSubscriptionPayload) in + .sink { [unowned self] (payload: ResponseSubscriptionPayload) in handleSessionSettleResponse(payload: payload) }.store(in: &publishers) + } + func setupResponseErrorSubscriptions() { networkingInteractor.responseErrorSubscription(on: SignProtocolMethod.sessionPropose) .sink { [unowned self] (payload: ResponseSubscriptionErrorPayload) in - onSessionRejected?( - payload.request.publicRepresentation(), - SessionType.Reason(code: payload.error.code, message: payload.error.message) - ) + handleSessionProposeResponseError(payload: payload) + }.store(in: &publishers) + + networkingInteractor.responseErrorSubscription(on: SignProtocolMethod.sessionSettle) + .sink { [unowned self] (payload: ResponseSubscriptionErrorPayload) in + handleSessionSettleResponseError(payload: payload) }.store(in: &publishers) } @@ -201,32 +207,14 @@ private extension ApproveEngine { // MARK: SessionProposeResponse // TODO: Move to Non-Controller SettleEngine - func handleSessionProposeResponse(payload: ResponseSubscriptionPayload) { + func handleSessionProposeResponse(payload: ResponseSubscriptionPayload) { do { - let sessionTopic = try handleProposeResponse( - pairingTopic: payload.topic, - proposal: payload.request, - result: payload.response - ) - settlingProposal = payload.request + let pairingTopic = payload.topic - Task(priority: .high) { - try await networkingInteractor.subscribe(topic: sessionTopic) - } - } catch { - guard let error = error as? Reason else { - return logger.debug(error.localizedDescription) + guard var pairing = pairingStore.getPairing(forTopic: pairingTopic) else { + throw Errors.pairingNotFound } - onSessionRejected?(payload.request.publicRepresentation(), SessionType.Reason(code: error.code, message: error.message)) - } - } - func handleProposeResponse(pairingTopic: String, proposal: SessionProposal, result: RPCResult) throws -> String { - guard var pairing = pairingStore.getPairing(forTopic: pairingTopic) - else { throw Errors.pairingNotFound } - - switch result { - case .response(let response): // Activate the pairing if !pairing.active { pairing.activate() @@ -236,9 +224,8 @@ private extension ApproveEngine { pairingStore.setPairing(pairing) - let selfPublicKey = try AgreementPublicKey(hex: proposal.proposer.publicKey) - let proposeResponse = try response.get(SessionType.ProposeResponse.self) - let agreementKeys = try kms.performKeyAgreement(selfPublicKey: selfPublicKey, peerPublicKey: proposeResponse.responderPublicKey) + let selfPublicKey = try AgreementPublicKey(hex: payload.request.proposer.publicKey) + let agreementKeys = try kms.performKeyAgreement(selfPublicKey: selfPublicKey, peerPublicKey: payload.response.responderPublicKey) let sessionTopic = agreementKeys.derivedTopic() logger.debug("Received Session Proposal response") @@ -246,37 +233,57 @@ private extension ApproveEngine { try kms.setAgreementSecret(agreementKeys, topic: sessionTopic) sessionToPairingTopic.set(pairingTopic, forKey: sessionTopic) - return sessionTopic + settlingProposal = payload.request - case .error(let error): - if !pairing.active { - kms.deleteSymmetricKey(for: pairing.topic) - networkingInteractor.unsubscribe(topic: pairing.topic) - pairingStore.delete(topic: pairingTopic) + Task(priority: .high) { + try await networkingInteractor.subscribe(topic: sessionTopic) } - logger.debug("Session Proposal has been rejected") - kms.deletePrivateKey(for: proposal.proposer.publicKey) - throw error + } catch { + return logger.debug(error.localizedDescription) + } + } + + func handleSessionProposeResponseError(payload: ResponseSubscriptionErrorPayload) { + guard let pairing = pairingStore.getPairing(forTopic: payload.topic) else { + return logger.debug(Errors.pairingNotFound.localizedDescription) } + + if !pairing.active { + kms.deleteSymmetricKey(for: pairing.topic) + networkingInteractor.unsubscribe(topic: pairing.topic) + pairingStore.delete(topic: payload.topic) + } + logger.debug("Session Proposal has been rejected") + kms.deletePrivateKey(for: payload.request.proposer.publicKey) + + onSessionRejected?( + payload.request.publicRepresentation(), + SessionType.Reason(code: payload.error.code, message: payload.error.message) + ) } // MARK: SessionSettleResponse - func handleSessionSettleResponse(payload: ResponseSubscriptionPayload) { - guard let session = sessionStore.getSession(forTopic: payload.topic) else { return } - switch payload.response { - case .response: - logger.debug("Received session settle response") - guard var session = sessionStore.getSession(forTopic: payload.topic) else { return } - session.acknowledge() - sessionStore.setSession(session) - case .error(let error): - logger.error("Error - session rejected, Reason: \(error)") - networkingInteractor.unsubscribe(topic: payload.topic) - sessionStore.delete(topic: payload.topic) - kms.deleteAgreementSecret(for: payload.topic) - kms.deletePrivateKey(for: session.publicKey!) + func handleSessionSettleResponse(payload: ResponseSubscriptionPayload) { + guard var session = sessionStore.getSession(forTopic: payload.topic) else { + return logger.debug(Errors.sessionNotFound.localizedDescription) } + + logger.debug("Received session settle response") + session.acknowledge() + sessionStore.setSession(session) + } + + func handleSessionSettleResponseError(payload: ResponseSubscriptionErrorPayload) { + guard let session = sessionStore.getSession(forTopic: payload.topic) else { + return logger.debug(Errors.sessionNotFound.localizedDescription) + } + + logger.error("Error - session rejected, Reason: \(payload.error)") + networkingInteractor.unsubscribe(topic: payload.topic) + sessionStore.delete(topic: payload.topic) + kms.deleteAgreementSecret(for: payload.topic) + kms.deletePrivateKey(for: session.publicKey!) } // MARK: SessionProposeRequest @@ -292,6 +299,7 @@ private extension ApproveEngine { } // MARK: SessionSettleRequest + func handleSessionSettleRequest(payload: RequestSubscriptionPayload) { logger.debug("Did receive session settle request") From c4cca76796966aaa653cf40ca169badf17a01269 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Wed, 14 Sep 2022 21:25:35 +0300 Subject: [PATCH 041/175] Prompt --- .../WalletConnectNetworking/NetworkInteractor.swift | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Sources/WalletConnectNetworking/NetworkInteractor.swift b/Sources/WalletConnectNetworking/NetworkInteractor.swift index 405533ef1..111d0003f 100644 --- a/Sources/WalletConnectNetworking/NetworkInteractor.swift +++ b/Sources/WalletConnectNetworking/NetworkInteractor.swift @@ -96,7 +96,7 @@ public class NetworkingInteractor: NetworkInteracting { public func request(_ request: RPCRequest, topic: String, tag: Int, envelopeType: Envelope.EnvelopeType) async throws { try rpcHistory.set(request, forTopic: topic, emmitedBy: .local) let message = try! serializer.serialize(topic: topic, encodable: request, envelopeType: envelopeType) - try await relayClient.publish(topic: topic, payload: message, tag: tag) + try await relayClient.publish(topic: topic, payload: message, tag: tag, prompt: shouldPrompt(method: request.method)) } /// Completes with an acknowledgement from the relay network. @@ -107,7 +107,7 @@ public class NetworkingInteractor: NetworkInteracting { try rpcHistory.set(request, forTopic: topic, emmitedBy: .local) let message = try serializer.serialize(topic: topic, encodable: request) return try await withCheckedThrowingContinuation { continuation in - relayClient.publish(topic: topic, payload: message, tag: tag) { error in + relayClient.publish(topic: topic, payload: message, tag: tag, prompt: shouldPrompt(method: request.method)) { error in if let error = error { continuation.resume(throwing: error) } else { @@ -165,4 +165,13 @@ public class NetworkingInteractor: NetworkInteracting { logger.debug("Handle json rpc response error: \(error)") } } + + private func shouldPrompt(method: String) -> Bool { + switch method { + case "wc_sessionRequest": // TODO: Include promt in ProtocolMethod + return true + default: + return false + } + } } From 13c0e5f0fbd4a5f2157d3082303f08e2e2fe772a Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Wed, 14 Sep 2022 23:38:34 +0300 Subject: [PATCH 042/175] Unit test fixed --- Tests/WalletConnectSignTests/ApproveEngineTests.swift | 2 +- Tests/WalletConnectSignTests/Stub/Stubs.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/WalletConnectSignTests/ApproveEngineTests.swift b/Tests/WalletConnectSignTests/ApproveEngineTests.swift index 77fc29075..3f2c39da8 100644 --- a/Tests/WalletConnectSignTests/ApproveEngineTests.swift +++ b/Tests/WalletConnectSignTests/ApproveEngineTests.swift @@ -135,7 +135,7 @@ final class ApproveEngineTests: XCTestCase { try! cryptoMock.setPrivateKey(privateKey) let request = RPCRequest(method: SignProtocolMethod.sessionSettle.method, params: SessionType.SettleParams.stub()) - let response = RPCResponse(matchingRequest: request, result: RPCResult.error(JSONRPCError(code: 0, message: ""))) + let response = RPCResponse.stubError(forRequest: request) networkingInteractor.responsePublisherSubject.send((session.topic, request, response)) diff --git a/Tests/WalletConnectSignTests/Stub/Stubs.swift b/Tests/WalletConnectSignTests/Stub/Stubs.swift index 96ea5eb3b..f0b87a080 100644 --- a/Tests/WalletConnectSignTests/Stub/Stubs.swift +++ b/Tests/WalletConnectSignTests/Stub/Stubs.swift @@ -94,6 +94,6 @@ extension SessionProposal { extension RPCResponse { static func stubError(forRequest request: RPCRequest) -> RPCResponse { - return RPCResponse(matchingRequest: request, result: RPCResult.error(JSONRPCError(code: 0, message: ""))) + return RPCResponse(matchingRequest: request, error: JSONRPCError(code: 0, message: "")) } } From 41f491bfab736e0be469c52d044693fbe957900b Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Wed, 21 Sep 2022 02:29:04 +0300 Subject: [PATCH 043/175] Sign RPCHistoryFactory --- Sources/Auth/StorageDomainIdentifiers.swift | 1 - Sources/WalletConnectSign/Sign/SignClientFactory.swift | 2 +- Sources/WalletConnectSign/StorageDomainIdentifiers.swift | 1 - Tests/AuthTests/AppRespondSubscriberTests.swift | 3 +-- 4 files changed, 2 insertions(+), 5 deletions(-) diff --git a/Sources/Auth/StorageDomainIdentifiers.swift b/Sources/Auth/StorageDomainIdentifiers.swift index ed7156c67..c328999c6 100644 --- a/Sources/Auth/StorageDomainIdentifiers.swift +++ b/Sources/Auth/StorageDomainIdentifiers.swift @@ -1,6 +1,5 @@ import Foundation enum StorageDomainIdentifiers: String { - case jsonRpcHistory = "com.walletconnect.sdk.wc_jsonRpcHistoryRecord" case pairings = "com.walletconnect.sdk.pairingSequences" } diff --git a/Sources/WalletConnectSign/Sign/SignClientFactory.swift b/Sources/WalletConnectSign/Sign/SignClientFactory.swift index 694b68c48..b26989d46 100644 --- a/Sources/WalletConnectSign/Sign/SignClientFactory.swift +++ b/Sources/WalletConnectSign/Sign/SignClientFactory.swift @@ -26,7 +26,7 @@ public struct SignClientFactory { static func create(metadata: AppMetadata, logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, relayClient: RelayClient) -> SignClient { let kms = KeyManagementService(keychain: keychainStorage) let serializer = Serializer(kms: kms) - let rpcHistory = RPCHistory(keyValueStore: CodableStore(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.jsonRpcHistory.rawValue)) + let rpcHistory = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage) let networkingInteractor = NetworkingInteractor(relayClient: relayClient, serializer: serializer, logger: logger, rpcHistory: rpcHistory) let pairingStore = PairingStorage(storage: SequenceStore(store: .init(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.pairings.rawValue))) let sessionStore = SessionStorage(storage: SequenceStore(store: .init(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.sessions.rawValue))) diff --git a/Sources/WalletConnectSign/StorageDomainIdentifiers.swift b/Sources/WalletConnectSign/StorageDomainIdentifiers.swift index 1e0c2a799..89a0b52a1 100644 --- a/Sources/WalletConnectSign/StorageDomainIdentifiers.swift +++ b/Sources/WalletConnectSign/StorageDomainIdentifiers.swift @@ -1,7 +1,6 @@ import Foundation enum StorageDomainIdentifiers: String { - case jsonRpcHistory = "com.walletconnect.sdk.wc_jsonRpcHistoryRecord" case pairings = "com.walletconnect.sdk.pairingSequences" case sessions = "com.walletconnect.sdk.sessionSequences" case proposals = "com.walletconnect.sdk.sessionProposals" diff --git a/Tests/AuthTests/AppRespondSubscriberTests.swift b/Tests/AuthTests/AppRespondSubscriberTests.swift index 4043fbab5..aac4a12ea 100644 --- a/Tests/AuthTests/AppRespondSubscriberTests.swift +++ b/Tests/AuthTests/AppRespondSubscriberTests.swift @@ -22,8 +22,7 @@ class AppRespondSubscriberTests: XCTestCase { networkingInteractor = NetworkingInteractorMock() messageFormatter = SIWEMessageFormatter() messageSigner = MessageSigner() - let historyStorage = CodableStore(defaults: RuntimeKeyValueStorage(), identifier: StorageDomainIdentifiers.jsonRpcHistory.rawValue) - rpcHistory = RPCHistory(keyValueStore: historyStorage) + rpcHistory = RPCHistoryFactory.createForNetwork(keyValueStorage: RuntimeKeyValueStorage()) pairingStorage = WCPairingStorageMock() sut = AppRespondSubscriber( networkingInteractor: networkingInteractor, From 99ef0f1fe75cf80f1d0e8fd03a99e73fbea43084 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Wed, 21 Sep 2022 02:40:53 +0300 Subject: [PATCH 044/175] requestNetworkAck removed --- .../Auth/Services/App/AppRequestService.swift | 2 +- .../NetworkInteracting.swift | 1 - .../NetworkInteractor.swift | 21 ------------------- .../Engine/Common/PairingEngine.swift | 2 +- .../NetworkingInteractorMock.swift | 5 ----- 5 files changed, 2 insertions(+), 29 deletions(-) diff --git a/Sources/Auth/Services/App/AppRequestService.swift b/Sources/Auth/Services/App/AppRequestService.swift index da88c993a..e932503d8 100644 --- a/Sources/Auth/Services/App/AppRequestService.swift +++ b/Sources/Auth/Services/App/AppRequestService.swift @@ -30,7 +30,7 @@ actor AppRequestService { let request = RPCRequest(method: "wc_authRequest", params: params) try kms.setPublicKey(publicKey: pubKey, for: responseTopic) logger.debug("AppRequestService: Subscribibg for response topic: \(responseTopic)") - try await networkingInteractor.requestNetworkAck(request, topic: topic, tag: AuthProtocolMethod.authRequest.responseTag) + try await networkingInteractor.request(request, topic: topic, tag: AuthProtocolMethod.authRequest.responseTag) try await networkingInteractor.subscribe(topic: responseTopic) } } diff --git a/Sources/WalletConnectNetworking/NetworkInteracting.swift b/Sources/WalletConnectNetworking/NetworkInteracting.swift index 85ca9177e..c47e7dd5b 100644 --- a/Sources/WalletConnectNetworking/NetworkInteracting.swift +++ b/Sources/WalletConnectNetworking/NetworkInteracting.swift @@ -9,7 +9,6 @@ public protocol NetworkInteracting { func subscribe(topic: String) async throws func unsubscribe(topic: String) func request(_ request: RPCRequest, topic: String, tag: Int, envelopeType: Envelope.EnvelopeType) async throws - func requestNetworkAck(_ request: RPCRequest, topic: String, tag: Int) async throws func respond(topic: String, response: RPCResponse, tag: Int, envelopeType: Envelope.EnvelopeType) async throws func respondSuccess(topic: String, requestId: RPCID, tag: Int, envelopeType: Envelope.EnvelopeType) async throws func respondError(topic: String, requestId: RPCID, tag: Int, reason: Reason, envelopeType: Envelope.EnvelopeType) async throws diff --git a/Sources/WalletConnectNetworking/NetworkInteractor.swift b/Sources/WalletConnectNetworking/NetworkInteractor.swift index 111d0003f..ed2acb617 100644 --- a/Sources/WalletConnectNetworking/NetworkInteractor.swift +++ b/Sources/WalletConnectNetworking/NetworkInteractor.swift @@ -99,27 +99,6 @@ public class NetworkingInteractor: NetworkInteracting { try await relayClient.publish(topic: topic, payload: message, tag: tag, prompt: shouldPrompt(method: request.method)) } - /// Completes with an acknowledgement from the relay network. - /// completes with error if networking client was not able to send a message - /// TODO - relay client should provide async function - continualion should be removed from here - public func requestNetworkAck(_ request: RPCRequest, topic: String, tag: Int) async throws { - do { - try rpcHistory.set(request, forTopic: topic, emmitedBy: .local) - let message = try serializer.serialize(topic: topic, encodable: request) - return try await withCheckedThrowingContinuation { continuation in - relayClient.publish(topic: topic, payload: message, tag: tag, prompt: shouldPrompt(method: request.method)) { error in - if let error = error { - continuation.resume(throwing: error) - } else { - continuation.resume() - } - } - } - } catch { - logger.error(error) - } - } - public func respond(topic: String, response: RPCResponse, tag: Int, envelopeType: Envelope.EnvelopeType) async throws { try rpcHistory.resolve(response) let message = try! serializer.serialize(topic: topic, encodable: response, envelopeType: envelopeType) diff --git a/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift b/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift index d6a012de0..076a345fc 100644 --- a/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift @@ -73,7 +73,7 @@ final class PairingEngine { requiredNamespaces: namespaces) let request = RPCRequest(method: SignProtocolMethod.sessionPropose.method, params: proposal) - try await networkingInteractor.requestNetworkAck(request, topic: pairingTopic, tag: SignProtocolMethod.sessionPropose.requestTag) + try await networkingInteractor.request(request, topic: pairingTopic, tag: SignProtocolMethod.sessionPropose.requestTag) } } diff --git a/Tests/TestingUtils/NetworkingInteractorMock.swift b/Tests/TestingUtils/NetworkingInteractorMock.swift index 286390803..321cb40f5 100644 --- a/Tests/TestingUtils/NetworkingInteractorMock.swift +++ b/Tests/TestingUtils/NetworkingInteractorMock.swift @@ -113,9 +113,4 @@ public class NetworkingInteractorMock: NetworkInteracting { lastErrorCode = reason.code didRespondError = true } - - public func requestNetworkAck(_ request: RPCRequest, topic: String, tag: Int) async throws { - requestCallCount += 1 - requests.append((topic, request)) - } } From 82eafa79396644df14a1566d2f9cce758c743a48 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Wed, 21 Sep 2022 02:42:59 +0300 Subject: [PATCH 045/175] Removed old WCRelay tests --- .../WalletConnectSignTests/WCRelayTests.swift | 73 ------------------- 1 file changed, 73 deletions(-) delete mode 100644 Tests/WalletConnectSignTests/WCRelayTests.swift diff --git a/Tests/WalletConnectSignTests/WCRelayTests.swift b/Tests/WalletConnectSignTests/WCRelayTests.swift deleted file mode 100644 index aea8037c9..000000000 --- a/Tests/WalletConnectSignTests/WCRelayTests.swift +++ /dev/null @@ -1,73 +0,0 @@ -//import Foundation -//import Combine -//import XCTest -//import WalletConnectUtils -//import WalletConnectPairing -//import WalletConnectNetworking -//@testable import TestingUtils -//@testable import WalletConnectSign -// -//class NetworkingInteractorTests: XCTestCase { -// var networkingInteractor: NetworkingInteractor! -// var relayClient: MockedRelayClient! -// var serializer: SerializerMock! -// -// private var publishers = [AnyCancellable]() -// -// override func setUp() { -// let logger = ConsoleLoggerMock() -// serializer = SerializerMock() -// relayClient = MockedRelayClient() -// networkingInteractor = NetworkingInteractor(relayClient: relayClient, serializer: serializer, logger: logger, rpcHistory: RPCHistory(logger: logger, keyValueStore: CodableStore(defaults: RuntimeKeyValueStorage(), identifier: ""))) -// } -// -// override func tearDown() { -// networkingInteractor = nil -// relayClient = nil -// serializer = nil -// } -// -// func testNotifiesOnEncryptedWCJsonRpcRequest() { -// let requestExpectation = expectation(description: "notifies with request") -// let topic = "fefc3dc39cacbc562ed58f92b296e2d65a6b07ef08992b93db5b3cb86280635a" -// networkingInteractor.wcRequestPublisher.sink { (_) in -// requestExpectation.fulfill() -// }.store(in: &publishers) -// serializer.deserialized = request -// relayClient.messagePublisherSubject.send((topic, testPayload)) -// waitForExpectations(timeout: 1.001, handler: nil) -// } -// -// func testPromptOnSessionRequest() async { -// let topic = "fefc3dc39cacbc562ed58f92b296e2d65a6b07ef08992b93db5b3cb86280635a" -// let method = getWCSessionMethod() -// relayClient.prompt = false -// try! await networkingInteractor.request(topic: topic, payload: method.asRequest()) -// XCTAssertTrue(relayClient.prompt) -// } -//} -// -//extension NetworkingInteractorTests { -// func getWCSessionMethod() -> WCMethod { -// let sessionRequestParams = SessionType.RequestParams(request: SessionType.RequestParams.Request(method: "method", params: AnyCodable("params")), chainId: Blockchain("eip155:1")!) -// return .wcSessionRequest(sessionRequestParams) -// } -//} -// -//private let testPayload = -//""" -//{ -// "id":1630300527198334, -// "jsonrpc":"2.0", -// "method":"irn_subscription", -// "params":{ -// "id":"0847f4e1dd19cf03a43dc7525f39896b630e9da33e4683c8efbc92ea671b5e07", -// "data":{ -// "topic":"fefc3dc39cacbc562ed58f92b296e2d65a6b07ef08992b93db5b3cb86280635a", -// "message":"7b226964223a313633303330303532383030302c226a736f6e727063223a22322e30222c22726573756c74223a747275657d" -// } -// } -//} -//""" -//// TODO - change for different request -//private let request = WCRequest(id: 1, jsonrpc: "2.0", method: .pairingPing, params: WCRequest.Params.pairingPing(PairingType.PingParams())) From 8f210ef3a7ea551dc607972a50f4cc862d1fcc48 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Wed, 21 Sep 2022 03:22:45 +0300 Subject: [PATCH 046/175] Revert "requestNetworkAck removed" This reverts commit 99ef0f1fe75cf80f1d0e8fd03a99e73fbea43084. --- .../Auth/Services/App/AppRequestService.swift | 2 +- .../NetworkInteracting.swift | 1 + .../NetworkInteractor.swift | 21 +++++++++++++++++++ .../Engine/Common/PairingEngine.swift | 2 +- .../NetworkingInteractorMock.swift | 5 +++++ 5 files changed, 29 insertions(+), 2 deletions(-) diff --git a/Sources/Auth/Services/App/AppRequestService.swift b/Sources/Auth/Services/App/AppRequestService.swift index e932503d8..da88c993a 100644 --- a/Sources/Auth/Services/App/AppRequestService.swift +++ b/Sources/Auth/Services/App/AppRequestService.swift @@ -30,7 +30,7 @@ actor AppRequestService { let request = RPCRequest(method: "wc_authRequest", params: params) try kms.setPublicKey(publicKey: pubKey, for: responseTopic) logger.debug("AppRequestService: Subscribibg for response topic: \(responseTopic)") - try await networkingInteractor.request(request, topic: topic, tag: AuthProtocolMethod.authRequest.responseTag) + try await networkingInteractor.requestNetworkAck(request, topic: topic, tag: AuthProtocolMethod.authRequest.responseTag) try await networkingInteractor.subscribe(topic: responseTopic) } } diff --git a/Sources/WalletConnectNetworking/NetworkInteracting.swift b/Sources/WalletConnectNetworking/NetworkInteracting.swift index c47e7dd5b..85ca9177e 100644 --- a/Sources/WalletConnectNetworking/NetworkInteracting.swift +++ b/Sources/WalletConnectNetworking/NetworkInteracting.swift @@ -9,6 +9,7 @@ public protocol NetworkInteracting { func subscribe(topic: String) async throws func unsubscribe(topic: String) func request(_ request: RPCRequest, topic: String, tag: Int, envelopeType: Envelope.EnvelopeType) async throws + func requestNetworkAck(_ request: RPCRequest, topic: String, tag: Int) async throws func respond(topic: String, response: RPCResponse, tag: Int, envelopeType: Envelope.EnvelopeType) async throws func respondSuccess(topic: String, requestId: RPCID, tag: Int, envelopeType: Envelope.EnvelopeType) async throws func respondError(topic: String, requestId: RPCID, tag: Int, reason: Reason, envelopeType: Envelope.EnvelopeType) async throws diff --git a/Sources/WalletConnectNetworking/NetworkInteractor.swift b/Sources/WalletConnectNetworking/NetworkInteractor.swift index ed2acb617..111d0003f 100644 --- a/Sources/WalletConnectNetworking/NetworkInteractor.swift +++ b/Sources/WalletConnectNetworking/NetworkInteractor.swift @@ -99,6 +99,27 @@ public class NetworkingInteractor: NetworkInteracting { try await relayClient.publish(topic: topic, payload: message, tag: tag, prompt: shouldPrompt(method: request.method)) } + /// Completes with an acknowledgement from the relay network. + /// completes with error if networking client was not able to send a message + /// TODO - relay client should provide async function - continualion should be removed from here + public func requestNetworkAck(_ request: RPCRequest, topic: String, tag: Int) async throws { + do { + try rpcHistory.set(request, forTopic: topic, emmitedBy: .local) + let message = try serializer.serialize(topic: topic, encodable: request) + return try await withCheckedThrowingContinuation { continuation in + relayClient.publish(topic: topic, payload: message, tag: tag, prompt: shouldPrompt(method: request.method)) { error in + if let error = error { + continuation.resume(throwing: error) + } else { + continuation.resume() + } + } + } + } catch { + logger.error(error) + } + } + public func respond(topic: String, response: RPCResponse, tag: Int, envelopeType: Envelope.EnvelopeType) async throws { try rpcHistory.resolve(response) let message = try! serializer.serialize(topic: topic, encodable: response, envelopeType: envelopeType) diff --git a/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift b/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift index 076a345fc..d6a012de0 100644 --- a/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift @@ -73,7 +73,7 @@ final class PairingEngine { requiredNamespaces: namespaces) let request = RPCRequest(method: SignProtocolMethod.sessionPropose.method, params: proposal) - try await networkingInteractor.request(request, topic: pairingTopic, tag: SignProtocolMethod.sessionPropose.requestTag) + try await networkingInteractor.requestNetworkAck(request, topic: pairingTopic, tag: SignProtocolMethod.sessionPropose.requestTag) } } diff --git a/Tests/TestingUtils/NetworkingInteractorMock.swift b/Tests/TestingUtils/NetworkingInteractorMock.swift index 321cb40f5..286390803 100644 --- a/Tests/TestingUtils/NetworkingInteractorMock.swift +++ b/Tests/TestingUtils/NetworkingInteractorMock.swift @@ -113,4 +113,9 @@ public class NetworkingInteractorMock: NetworkInteracting { lastErrorCode = reason.code didRespondError = true } + + public func requestNetworkAck(_ request: RPCRequest, topic: String, tag: Int) async throws { + requestCallCount += 1 + requests.append((topic, request)) + } } From 1e1646e72a637622b845ff0963fde301bf14c772 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 21 Sep 2022 07:54:01 +0200 Subject: [PATCH 047/175] remove socket connection observing from integration tests --- Example/IntegrationTests/Auth/AuthTests.swift | 17 --------------- Example/IntegrationTests/Chat/ChatTests.swift | 21 ------------------- .../Pairing/PairingTests.swift | 19 +---------------- 3 files changed, 1 insertion(+), 56 deletions(-) diff --git a/Example/IntegrationTests/Auth/AuthTests.swift b/Example/IntegrationTests/Auth/AuthTests.swift index 6abe13983..4ee2bf33d 100644 --- a/Example/IntegrationTests/Auth/AuthTests.swift +++ b/Example/IntegrationTests/Auth/AuthTests.swift @@ -16,23 +16,6 @@ final class AuthTests: XCTestCase { app = makeClient(prefix: "👻 App") let walletAccount = Account(chainIdentifier: "eip155:1", address: "0x724d0D2DaD3fbB0C168f947B87Fa5DBe36F1A8bf")! wallet = makeClient(prefix: "🤑 Wallet", account: walletAccount) - - let expectation = expectation(description: "Wait Clients Connected") - expectation.expectedFulfillmentCount = 2 - - app.socketConnectionStatusPublisher.sink { status in - if status == .connected { - expectation.fulfill() - } - }.store(in: &publishers) - - wallet.socketConnectionStatusPublisher.sink { status in - if status == .connected { - expectation.fulfill() - } - }.store(in: &publishers) - - wait(for: [expectation], timeout: 5) } func makeClient(prefix: String, account: Account? = nil) -> AuthClient { diff --git a/Example/IntegrationTests/Chat/ChatTests.swift b/Example/IntegrationTests/Chat/ChatTests.swift index 191ea398f..c4863b8af 100644 --- a/Example/IntegrationTests/Chat/ChatTests.swift +++ b/Example/IntegrationTests/Chat/ChatTests.swift @@ -16,27 +16,6 @@ final class ChatTests: XCTestCase { registry = KeyValueRegistry() invitee = makeClient(prefix: "🦖 Registered") inviter = makeClient(prefix: "🍄 Inviter") - - waitClientsConnected() - } - - private func waitClientsConnected() { - let expectation = expectation(description: "Wait Clients Connected") - expectation.expectedFulfillmentCount = 2 - - invitee.socketConnectionStatusPublisher.sink { status in - if status == .connected { - expectation.fulfill() - } - }.store(in: &publishers) - - inviter.socketConnectionStatusPublisher.sink { status in - if status == .connected { - expectation.fulfill() - } - }.store(in: &publishers) - - wait(for: [expectation], timeout: 5) } func makeClient(prefix: String) -> ChatClient { diff --git a/Example/IntegrationTests/Pairing/PairingTests.swift b/Example/IntegrationTests/Pairing/PairingTests.swift index e77efa3e4..4b320a1c2 100644 --- a/Example/IntegrationTests/Pairing/PairingTests.swift +++ b/Example/IntegrationTests/Pairing/PairingTests.swift @@ -20,23 +20,6 @@ final class PairingTests: XCTestCase { override func setUp() { (appPairingClient, appPushClient) = makeClients(prefix: "👻 App") (walletPairingClient, walletPushClient) = makeClients(prefix: "🤑 Wallet") - - let expectation = expectation(description: "Wait Clients Connected") - expectation.expectedFulfillmentCount = 2 - - appPairingClient.socketConnectionStatusPublisher.sink { status in - if status == .connected { - expectation.fulfill() - } - }.store(in: &publishers) - - walletPairingClient.socketConnectionStatusPublisher.sink { status in - if status == .connected { - expectation.fulfill() - } - }.store(in: &publishers) - - wait(for: [expectation], timeout: 5) } func makeClients(prefix: String) -> (PairingClient, PushClient) { @@ -54,7 +37,7 @@ final class PairingTests: XCTestCase { func testProposePushOnPairing() async { let exp = expectation(description: "") - + walletPushClient.proposalPublisher.sink { _ in exp.fulfill() }.store(in: &publishers) From 67e3b156e08a03976f1184c6edb010f46ceb9fe3 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 21 Sep 2022 08:25:32 +0200 Subject: [PATCH 048/175] restore on connection publisher --- Example/IntegrationTests/Auth/AuthTests.swift | 17 +++++++++++++++ Example/IntegrationTests/Chat/ChatTests.swift | 21 +++++++++++++++++++ .../NetworkInteracting.swift | 3 +-- .../NetworkInteractor.swift | 12 +---------- .../Common/PairingRequestsSubscriber.swift | 2 +- 5 files changed, 41 insertions(+), 14 deletions(-) diff --git a/Example/IntegrationTests/Auth/AuthTests.swift b/Example/IntegrationTests/Auth/AuthTests.swift index 4ee2bf33d..6abe13983 100644 --- a/Example/IntegrationTests/Auth/AuthTests.swift +++ b/Example/IntegrationTests/Auth/AuthTests.swift @@ -16,6 +16,23 @@ final class AuthTests: XCTestCase { app = makeClient(prefix: "👻 App") let walletAccount = Account(chainIdentifier: "eip155:1", address: "0x724d0D2DaD3fbB0C168f947B87Fa5DBe36F1A8bf")! wallet = makeClient(prefix: "🤑 Wallet", account: walletAccount) + + let expectation = expectation(description: "Wait Clients Connected") + expectation.expectedFulfillmentCount = 2 + + app.socketConnectionStatusPublisher.sink { status in + if status == .connected { + expectation.fulfill() + } + }.store(in: &publishers) + + wallet.socketConnectionStatusPublisher.sink { status in + if status == .connected { + expectation.fulfill() + } + }.store(in: &publishers) + + wait(for: [expectation], timeout: 5) } func makeClient(prefix: String, account: Account? = nil) -> AuthClient { diff --git a/Example/IntegrationTests/Chat/ChatTests.swift b/Example/IntegrationTests/Chat/ChatTests.swift index c4863b8af..191ea398f 100644 --- a/Example/IntegrationTests/Chat/ChatTests.swift +++ b/Example/IntegrationTests/Chat/ChatTests.swift @@ -16,6 +16,27 @@ final class ChatTests: XCTestCase { registry = KeyValueRegistry() invitee = makeClient(prefix: "🦖 Registered") inviter = makeClient(prefix: "🍄 Inviter") + + waitClientsConnected() + } + + private func waitClientsConnected() { + let expectation = expectation(description: "Wait Clients Connected") + expectation.expectedFulfillmentCount = 2 + + invitee.socketConnectionStatusPublisher.sink { status in + if status == .connected { + expectation.fulfill() + } + }.store(in: &publishers) + + inviter.socketConnectionStatusPublisher.sink { status in + if status == .connected { + expectation.fulfill() + } + }.store(in: &publishers) + + wait(for: [expectation], timeout: 5) } func makeClient(prefix: String) -> ChatClient { diff --git a/Sources/WalletConnectNetworking/NetworkInteracting.swift b/Sources/WalletConnectNetworking/NetworkInteracting.swift index e0bc1cb3e..56b81caae 100644 --- a/Sources/WalletConnectNetworking/NetworkInteracting.swift +++ b/Sources/WalletConnectNetworking/NetworkInteracting.swift @@ -6,6 +6,7 @@ import WalletConnectRelay public protocol NetworkInteracting { var socketConnectionStatusPublisher: AnyPublisher { get } + var requestPublisher: AnyPublisher<(topic: String, request: RPCRequest), Never> { get } func subscribe(topic: String) async throws func unsubscribe(topic: String) func request(_ request: RPCRequest, topic: String, tag: Int, envelopeType: Envelope.EnvelopeType) async throws @@ -18,8 +19,6 @@ public protocol NetworkInteracting { on request: ProtocolMethod ) -> AnyPublisher, Never> - func requestSubscription(on requests: [ProtocolMethod]) -> AnyPublisher<(topic: String, request: RPCRequest), Never> - func responseSubscription( on request: ProtocolMethod ) -> AnyPublisher, Never> diff --git a/Sources/WalletConnectNetworking/NetworkInteractor.swift b/Sources/WalletConnectNetworking/NetworkInteractor.swift index b7151eeb2..c6a265b50 100644 --- a/Sources/WalletConnectNetworking/NetworkInteractor.swift +++ b/Sources/WalletConnectNetworking/NetworkInteractor.swift @@ -15,7 +15,7 @@ public class NetworkingInteractor: NetworkInteracting { private let requestPublisherSubject = PassthroughSubject<(topic: String, request: RPCRequest), Never>() private let responsePublisherSubject = PassthroughSubject<(topic: String, request: RPCRequest, response: RPCResponse), Never>() - private var requestPublisher: AnyPublisher<(topic: String, request: RPCRequest), Never> { + public var requestPublisher: AnyPublisher<(topic: String, request: RPCRequest), Never> { requestPublisherSubject.eraseToAnyPublisher() } @@ -68,16 +68,6 @@ public class NetworkingInteractor: NetworkInteracting { .eraseToAnyPublisher() } - public func requestSubscription(on requests: [ProtocolMethod]) -> AnyPublisher<(topic: String, request: RPCRequest), Never> { - return requestPublisher - .filter { rpcRequest in - return requests.contains { protocolMethod in - protocolMethod.method == rpcRequest.request.method - } - } - .eraseToAnyPublisher() - } - public func responseSubscription(on request: ProtocolMethod) -> AnyPublisher, Never> { return responsePublisher .filter { rpcRequest in diff --git a/Sources/WalletConnectPairing/Services/Common/PairingRequestsSubscriber.swift b/Sources/WalletConnectPairing/Services/Common/PairingRequestsSubscriber.swift index 05947f8d7..6295ed66e 100644 --- a/Sources/WalletConnectPairing/Services/Common/PairingRequestsSubscriber.swift +++ b/Sources/WalletConnectPairing/Services/Common/PairingRequestsSubscriber.swift @@ -31,7 +31,7 @@ public class PairingRequestsSubscriber { private func subscribeForRequests(methods: [ProtocolMethod]) { // TODO - spec tag let tag = 123456 - networkingInteractor.requestSubscription(on: methods) + networkingInteractor.requestPublisher .sink { [unowned self] topic, request in guard let pairingable = pairingables .first(where: { p in From 1395b231938511f79d0f0f8b9bba83ee2eceec85 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 21 Sep 2022 08:42:30 +0200 Subject: [PATCH 049/175] remove duplicated file --- .../IntegrationTests/Auth/PairingTest.swift | 83 ------------------- .../Pairing/PairingTests.swift | 17 ++++ 2 files changed, 17 insertions(+), 83 deletions(-) delete mode 100644 Example/IntegrationTests/Auth/PairingTest.swift diff --git a/Example/IntegrationTests/Auth/PairingTest.swift b/Example/IntegrationTests/Auth/PairingTest.swift deleted file mode 100644 index 96a092b41..000000000 --- a/Example/IntegrationTests/Auth/PairingTest.swift +++ /dev/null @@ -1,83 +0,0 @@ -import Foundation -import XCTest -import WalletConnectUtils -@testable import WalletConnectKMS -import WalletConnectRelay -import Combine -import WalletConnectNetworking -import WalletConnectPairing - - -final class Pairingtests: XCTestCase { - var appPairingClient: PairingClient! - var walletPairingClient: PairingClient! - - private var publishers = [AnyCancellable]() - - override func setUp() { - appPairingClient = makeClient(prefix: "👻 App") - walletPairingClient = makeClient(prefix: "🤑 Wallet") - - let expectation = expectation(description: "Wait Clients Connected") - expectation.expectedFulfillmentCount = 2 - - appPairingClient.socketConnectionStatusPublisher.sink { status in - if status == .connected { - expectation.fulfill() - } - }.store(in: &publishers) - - walletPairingClient.socketConnectionStatusPublisher.sink { status in - if status == .connected { - expectation.fulfill() - } - }.store(in: &publishers) - - wait(for: [expectation], timeout: 5) - } - - - func makeClient(prefix: String) -> PairingClient { - let logger = ConsoleLogger(suffix: prefix, loggingLevel: .debug) - let projectId = "3ca2919724fbfa5456a25194e369a8b4" - let keychain = KeychainStorageMock() - let relayClient = RelayClient(relayHost: URLConfig.relayHost, projectId: projectId, keychainStorage: keychain, socketFactory: SocketFactory(), logger: logger) - - let pairingClient = PairingClientFactory.create(logger: logger, keyValueStorage: RuntimeKeyValueStorage(), keychainStorage: keychain, relayClient: relayClient) - return pairingClient - } - - func makePushClient() -> PushClient { - let logger = ConsoleLogger(suffix: "", loggingLevel: .debug) - let projectId = "3ca2919724fbfa5456a25194e369a8b4" - let keychain = KeychainStorageMock() - let relayClient = RelayClient(relayHost: URLConfig.relayHost, projectId: projectId, keychainStorage: keychain, socketFactory: SocketFactory(), logger: logger) - return PushClientFactory.create(logger: logger, keyValueStorage: RuntimeKeyValueStorage(), keychainStorage: keychain, relayClient: relayClient) - } - - func testProposePushOnPairing() async { - let exp = expectation(description: "") - let appPushClient = makePushClient() - let walletPushClient = makePushClient() - - appPairingClient.configure(with: [appPushClient]) - - walletPairingClient.configure(with: [walletPushClient, ]) - - let uri = try! await appPairingClient.create() - - try! await walletPairingClient.pair(uri: uri) - - try! await appPushClient.propose(topic: uri.topic) - - walletPushClient.proposalPublisher.sink { _ in - exp.fulfill() - print("received push proposal") - }.store(in: &publishers) - - wait(for: [exp], timeout: 2) - - } - -} - diff --git a/Example/IntegrationTests/Pairing/PairingTests.swift b/Example/IntegrationTests/Pairing/PairingTests.swift index 4b320a1c2..7594348ef 100644 --- a/Example/IntegrationTests/Pairing/PairingTests.swift +++ b/Example/IntegrationTests/Pairing/PairingTests.swift @@ -20,6 +20,23 @@ final class PairingTests: XCTestCase { override func setUp() { (appPairingClient, appPushClient) = makeClients(prefix: "👻 App") (walletPairingClient, walletPushClient) = makeClients(prefix: "🤑 Wallet") + + let expectation = expectation(description: "Wait Clients Connected") + expectation.expectedFulfillmentCount = 2 + + appPairingClient.socketConnectionStatusPublisher.sink { status in + if status == .connected { + expectation.fulfill() + } + }.store(in: &publishers) + + walletPairingClient.socketConnectionStatusPublisher.sink { status in + if status == .connected { + expectation.fulfill() + } + }.store(in: &publishers) + + wait(for: [expectation], timeout: 5) } func makeClients(prefix: String) -> (PairingClient, PushClient) { From 227ac545e32c7517fc20b05415b667286c1349f1 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 21 Sep 2022 11:29:38 +0200 Subject: [PATCH 050/175] self review changes --- Sources/WalletConnectPairing/PairingClient.swift | 4 ++-- Sources/WalletConnectPairing/PairingClientFactory.swift | 7 ++----- Sources/WalletConnectPairing/Push/PushClientFactory.swift | 4 +--- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/Sources/WalletConnectPairing/PairingClient.swift b/Sources/WalletConnectPairing/PairingClient.swift index 6fbcecd13..eb7797381 100644 --- a/Sources/WalletConnectPairing/PairingClient.swift +++ b/Sources/WalletConnectPairing/PairingClient.swift @@ -26,8 +26,8 @@ public class PairingClient { self.logger = logger self.pairingRequestsSubscriber = pairingRequestsSubscriber } - /// For wallet to establish a pairing and receive an authentication request - /// Wallet should call this function in order to accept peer's pairing proposal and be able to subscribe for future authentication request. + /// For wallet to establish a pairing + /// Wallet should call this function in order to accept peer's pairing proposal and be able to subscribe for future requests. /// - Parameter uri: Pairing URI that is commonly presented as a QR code by a dapp or delivered with universal linking. /// /// Throws Error: diff --git a/Sources/WalletConnectPairing/PairingClientFactory.swift b/Sources/WalletConnectPairing/PairingClientFactory.swift index 34ca7077c..a957e2d0f 100644 --- a/Sources/WalletConnectPairing/PairingClientFactory.swift +++ b/Sources/WalletConnectPairing/PairingClientFactory.swift @@ -8,13 +8,10 @@ public struct PairingClientFactory { public static func create(logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, relayClient: RelayClient) -> PairingClient { let kms = KeyManagementService(keychain: keychainStorage) let serializer = Serializer(kms: kms) - let kv = RuntimeKeyValueStorage() - let history = RPCHistoryFactory.createForNetwork(keyValueStorage: kv) - + let history = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage) let networkingInteractor = NetworkingInteractor(relayClient: relayClient, serializer: serializer, logger: logger, rpcHistory: history) - let pairingStore = PairingStorage(storage: SequenceStore(store: .init(defaults: kv, identifier: ""))) - + let pairingStore = PairingStorage(storage: SequenceStore(store: .init(defaults: keyValueStorage, identifier: ""))) let appPairService = AppPairService(networkingInteractor: networkingInteractor, kms: kms, pairingStorage: pairingStore) diff --git a/Sources/WalletConnectPairing/Push/PushClientFactory.swift b/Sources/WalletConnectPairing/Push/PushClientFactory.swift index 6e0574950..89c11ea80 100644 --- a/Sources/WalletConnectPairing/Push/PushClientFactory.swift +++ b/Sources/WalletConnectPairing/Push/PushClientFactory.swift @@ -8,9 +8,7 @@ public struct PushClientFactory { public static func create(logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, relayClient: RelayClient) -> PushClient { let kms = KeyManagementService(keychain: keychainStorage) let serializer = Serializer(kms: kms) - let kv = RuntimeKeyValueStorage() - let history = RPCHistoryFactory.createForNetwork(keyValueStorage: kv) - + let history = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage) let networkingInteractor = NetworkingInteractor(relayClient: relayClient, serializer: serializer, logger: logger, rpcHistory: history) From 6aab69c551c775da4952a55e70f7c1a0d8c5f3be Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 22 Sep 2022 16:09:09 +0200 Subject: [PATCH 051/175] savepoint --- Sources/WalletConnectPairing/PairingClient.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/WalletConnectPairing/PairingClient.swift b/Sources/WalletConnectPairing/PairingClient.swift index eb7797381..4b108addf 100644 --- a/Sources/WalletConnectPairing/PairingClient.swift +++ b/Sources/WalletConnectPairing/PairingClient.swift @@ -8,7 +8,7 @@ public class PairingClient { private let walletPairService: WalletPairService private let appPairService: AppPairService public let socketConnectionStatusPublisher: AnyPublisher - let logger: ConsoleLogging + private let logger: ConsoleLogging private let networkingInteractor: NetworkInteracting private let pairingRequestsSubscriber: PairingRequestsSubscriber From be56aab76e7cf3626ff0a60af1aae1d853f2bd08 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 22 Sep 2022 18:05:32 +0300 Subject: [PATCH 052/175] Pairingable removed --- .../Pairing/PairingTests.swift | 8 +--- .../WalletConnectPairing/PairingClient.swift | 7 --- .../PairingClientFactory.swift | 4 +- .../WalletConnectPairing/Pairingable.swift | 10 ---- .../Push/PairingRequester.swift | 8 +--- .../Push/PushClient.swift | 35 +++++++++----- .../Push/PushClientFactory.swift | 5 +- .../Common/PairingRequestsSubscriber.swift | 48 ------------------- 8 files changed, 30 insertions(+), 95 deletions(-) delete mode 100644 Sources/WalletConnectPairing/Pairingable.swift delete mode 100644 Sources/WalletConnectPairing/Services/Common/PairingRequestsSubscriber.swift diff --git a/Example/IntegrationTests/Pairing/PairingTests.swift b/Example/IntegrationTests/Pairing/PairingTests.swift index 7594348ef..304d9dc76 100644 --- a/Example/IntegrationTests/Pairing/PairingTests.swift +++ b/Example/IntegrationTests/Pairing/PairingTests.swift @@ -5,7 +5,7 @@ import WalletConnectUtils import WalletConnectRelay import Combine import WalletConnectNetworking -import WalletConnectPairing +@testable import WalletConnectPairing final class PairingTests: XCTestCase { @@ -59,10 +59,6 @@ final class PairingTests: XCTestCase { exp.fulfill() }.store(in: &publishers) - appPairingClient.configureProtocols(with: [appPushClient]) - - walletPairingClient.configureProtocols(with: [walletPushClient]) - let uri = try! await appPairingClient.create() try! await walletPairingClient.pair(uri: uri) @@ -70,8 +66,6 @@ final class PairingTests: XCTestCase { try! await appPushClient.propose(topic: uri.topic) wait(for: [exp], timeout: 2) - } - } diff --git a/Sources/WalletConnectPairing/PairingClient.swift b/Sources/WalletConnectPairing/PairingClient.swift index 4b108addf..bf64bcab0 100644 --- a/Sources/WalletConnectPairing/PairingClient.swift +++ b/Sources/WalletConnectPairing/PairingClient.swift @@ -10,13 +10,11 @@ public class PairingClient { public let socketConnectionStatusPublisher: AnyPublisher private let logger: ConsoleLogging private let networkingInteractor: NetworkInteracting - private let pairingRequestsSubscriber: PairingRequestsSubscriber init(appPairService: AppPairService, networkingInteractor: NetworkInteracting, logger: ConsoleLogging, walletPairService: WalletPairService, - pairingRequestsSubscriber: PairingRequestsSubscriber, socketConnectionStatusPublisher: AnyPublisher ) { self.appPairService = appPairService @@ -24,7 +22,6 @@ public class PairingClient { self.networkingInteractor = networkingInteractor self.socketConnectionStatusPublisher = socketConnectionStatusPublisher self.logger = logger - self.pairingRequestsSubscriber = pairingRequestsSubscriber } /// For wallet to establish a pairing /// Wallet should call this function in order to accept peer's pairing proposal and be able to subscribe for future requests. @@ -40,9 +37,5 @@ public class PairingClient { public func create() async throws -> WalletConnectURI { return try await appPairService.create() } - - public func configureProtocols(with paringables: [Pairingable]) { - pairingRequestsSubscriber.setPairingables(paringables) - } } diff --git a/Sources/WalletConnectPairing/PairingClientFactory.swift b/Sources/WalletConnectPairing/PairingClientFactory.swift index a957e2d0f..7f4cc3bfb 100644 --- a/Sources/WalletConnectPairing/PairingClientFactory.swift +++ b/Sources/WalletConnectPairing/PairingClientFactory.swift @@ -17,9 +17,7 @@ public struct PairingClientFactory { let walletPaS = WalletPairService(networkingInteractor: networkingInteractor, kms: kms, pairingStorage: pairingStore) - let pairingRequestsSubscriber = PairingRequestsSubscriber(networkingInteractor: networkingInteractor, logger: logger, kms: kms) - - return PairingClient(appPairService: appPairService, networkingInteractor: networkingInteractor, logger: logger, walletPairService: walletPaS, pairingRequestsSubscriber: pairingRequestsSubscriber, socketConnectionStatusPublisher: relayClient.socketConnectionStatusPublisher) + return PairingClient(appPairService: appPairService, networkingInteractor: networkingInteractor, logger: logger, walletPairService: walletPaS, socketConnectionStatusPublisher: relayClient.socketConnectionStatusPublisher) } } diff --git a/Sources/WalletConnectPairing/Pairingable.swift b/Sources/WalletConnectPairing/Pairingable.swift deleted file mode 100644 index 858cf90e3..000000000 --- a/Sources/WalletConnectPairing/Pairingable.swift +++ /dev/null @@ -1,10 +0,0 @@ - -import Foundation -import Combine -import WalletConnectNetworking -import JSONRPC - -public protocol Pairingable: AnyObject { - var protocolMethod: ProtocolMethod { get set } - var requestPublisherSubject: PassthroughSubject<(topic: String, request: RPCRequest), Never> {get} -} diff --git a/Sources/WalletConnectPairing/Push/PairingRequester.swift b/Sources/WalletConnectPairing/Push/PairingRequester.swift index ba584d443..bdc89d2dc 100644 --- a/Sources/WalletConnectPairing/Push/PairingRequester.swift +++ b/Sources/WalletConnectPairing/Push/PairingRequester.swift @@ -10,21 +10,17 @@ public class PushProposer { private let networkingInteractor: NetworkInteracting private let kms: KeyManagementServiceProtocol private let logger: ConsoleLogging - let protocolMethod: ProtocolMethod init(networkingInteractor: NetworkInteracting, kms: KeyManagementServiceProtocol, - logger: ConsoleLogging, - protocolMethod: ProtocolMethod) { + logger: ConsoleLogging) { self.networkingInteractor = networkingInteractor self.kms = kms self.logger = logger - self.protocolMethod = protocolMethod } func request(topic: String, params: AnyCodable) async throws { - let request = RPCRequest(method: protocolMethod.method, params: params) - + let request = RPCRequest(method: PushProtocolMethod.propose.method, params: params) try await networkingInteractor.requestNetworkAck(request, topic: topic, tag: PushProtocolMethod.propose.requestTag) } } diff --git a/Sources/WalletConnectPairing/Push/PushClient.swift b/Sources/WalletConnectPairing/Push/PushClient.swift index 80b2645d6..0a75896ea 100644 --- a/Sources/WalletConnectPairing/Push/PushClient.swift +++ b/Sources/WalletConnectPairing/Push/PushClient.swift @@ -1,34 +1,47 @@ import Foundation +import JSONRPC +import Combine import WalletConnectKMS import WalletConnectUtils import WalletConnectNetworking -import JSONRPC -import Combine -public class PushClient: Pairingable { - public var requestPublisherSubject = PassthroughSubject<(topic: String, request: RPCRequest), Never>() +public class PushClient { - public var protocolMethod: ProtocolMethod + private var publishers = Set() - public var proposalPublisher: AnyPublisher<(topic: String, request: RPCRequest), Never> { + let requestPublisherSubject = PassthroughSubject<(topic: String, params: PushRequestParams), Never>() + + var proposalPublisher: AnyPublisher<(topic: String, params: PushRequestParams), Never> { requestPublisherSubject.eraseToAnyPublisher() } - private let pushProposer: PushProposer - public let logger: ConsoleLogging - init(networkingInteractor: NetworkInteracting, + private let pushProposer: PushProposer + private let networkInteractor: NetworkInteracting + + init(networkInteractor: NetworkInteracting, logger: ConsoleLogging, kms: KeyManagementServiceProtocol, - protocolMethod: ProtocolMethod, pushProposer: PushProposer) { + self.networkInteractor = networkInteractor self.logger = logger - self.protocolMethod = protocolMethod self.pushProposer = pushProposer + + setupPairingSubscriptions() } public func propose(topic: String) async throws { try await pushProposer.request(topic: topic, params: AnyCodable(PushRequestParams())) } } + +private extension PushClient { + + func setupPairingSubscriptions() { + networkInteractor.requestSubscription(on: PushProtocolMethod.propose) + .sink { [unowned self] (payload: RequestSubscriptionPayload) in + requestPublisherSubject.send((payload.topic, payload.request)) + }.store(in: &publishers) + } +} diff --git a/Sources/WalletConnectPairing/Push/PushClientFactory.swift b/Sources/WalletConnectPairing/Push/PushClientFactory.swift index 89c11ea80..ead91fe59 100644 --- a/Sources/WalletConnectPairing/Push/PushClientFactory.swift +++ b/Sources/WalletConnectPairing/Push/PushClientFactory.swift @@ -12,8 +12,7 @@ public struct PushClientFactory { let networkingInteractor = NetworkingInteractor(relayClient: relayClient, serializer: serializer, logger: logger, rpcHistory: history) - let protocolMethod = PushProtocolMethod.propose - let pushProposer = PushProposer(networkingInteractor: networkingInteractor, kms: kms, logger: logger, protocolMethod: protocolMethod) - return PushClient(networkingInteractor: networkingInteractor, logger: logger, kms: kms, protocolMethod: protocolMethod, pushProposer: pushProposer) + let pushProposer = PushProposer(networkingInteractor: networkingInteractor, kms: kms, logger: logger) + return PushClient(networkInteractor: networkingInteractor, logger: logger, kms: kms, pushProposer: pushProposer) } } diff --git a/Sources/WalletConnectPairing/Services/Common/PairingRequestsSubscriber.swift b/Sources/WalletConnectPairing/Services/Common/PairingRequestsSubscriber.swift deleted file mode 100644 index 6295ed66e..000000000 --- a/Sources/WalletConnectPairing/Services/Common/PairingRequestsSubscriber.swift +++ /dev/null @@ -1,48 +0,0 @@ - -import Foundation -import Combine -import JSONRPC -import WalletConnectUtils -import WalletConnectKMS -import WalletConnectNetworking - - -public class PairingRequestsSubscriber { - private let networkingInteractor: NetworkInteracting - private let kms: KeyManagementServiceProtocol - private var publishers = [AnyCancellable]() - var onRequest: ((RequestSubscriptionPayload) -> Void)? - var pairingables = [Pairingable]() - - init(networkingInteractor: NetworkInteracting, - logger: ConsoleLogging, - kms: KeyManagementServiceProtocol) { - self.networkingInteractor = networkingInteractor - self.kms = kms - } - - func setPairingables(_ pairingables: [Pairingable]) { - self.pairingables = pairingables - let methods = pairingables.map{$0.protocolMethod} - subscribeForRequests(methods: methods) - - } - - private func subscribeForRequests(methods: [ProtocolMethod]) { - // TODO - spec tag - let tag = 123456 - networkingInteractor.requestPublisher - .sink { [unowned self] topic, request in - guard let pairingable = pairingables - .first(where: { p in - p.protocolMethod.method == request.method - }) else { - Task { try await networkingInteractor.respondError(topic: topic, requestId: request.id!, tag: tag, reason: PairError.methodUnsupported) } - return - } - pairingable.requestPublisherSubject.send((topic: topic, request: request)) - - }.store(in: &publishers) - } - -} From 1a3af64263bde67be8e2f080a623e7d724c4d14c Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 22 Sep 2022 22:42:27 +0300 Subject: [PATCH 053/175] PairingRegisterer --- .../Pairing/PairingTests.swift | 14 ++++---- .../WalletConnectPairing/PairingClient.swift | 9 ++++- .../PairingClientFactory.swift | 25 +++++++++---- .../PairingRegisterer.swift | 6 ++++ .../{Push => }/PairingRequester.swift | 0 .../PairingRequestsSubscriber.swift | 36 +++++++++++++++++++ .../Push/PushClient.swift | 7 +++- .../Push/PushClientFactory.swift | 14 +++++--- 8 files changed, 93 insertions(+), 18 deletions(-) create mode 100644 Sources/WalletConnectPairing/PairingRegisterer.swift rename Sources/WalletConnectPairing/{Push => }/PairingRequester.swift (100%) create mode 100644 Sources/WalletConnectPairing/PairingRequestsSubscriber.swift diff --git a/Example/IntegrationTests/Pairing/PairingTests.swift b/Example/IntegrationTests/Pairing/PairingTests.swift index 304d9dc76..bc9aa7338 100644 --- a/Example/IntegrationTests/Pairing/PairingTests.swift +++ b/Example/IntegrationTests/Pairing/PairingTests.swift @@ -15,6 +15,8 @@ final class PairingTests: XCTestCase { var appPushClient: PushClient! var walletPushClient: PushClient! + var pairingStorage: PairingStorage! + private var publishers = [AnyCancellable]() override func setUp() { @@ -47,23 +49,23 @@ final class PairingTests: XCTestCase { let pairingClient = PairingClientFactory.create(logger: logger, keyValueStorage: RuntimeKeyValueStorage(), keychainStorage: keychain, relayClient: relayClient) - let pushClient = PushClientFactory.create(logger: logger, keyValueStorage: RuntimeKeyValueStorage(), keychainStorage: keychain, relayClient: relayClient) + let pushClient = PushClientFactory.create(logger: logger, keyValueStorage: RuntimeKeyValueStorage(), keychainStorage: keychain, relayClient: relayClient, pairingClient: pairingClient) return (pairingClient, pushClient) } - func testProposePushOnPairing() async { - let exp = expectation(description: "") + func testProposePushOnPairing() async throws { + let exp = expectation(description: "testProposePushOnPairing") walletPushClient.proposalPublisher.sink { _ in exp.fulfill() }.store(in: &publishers) - let uri = try! await appPairingClient.create() + let uri = try await appPairingClient.create() - try! await walletPairingClient.pair(uri: uri) + try await walletPairingClient.pair(uri: uri) - try! await appPushClient.propose(topic: uri.topic) + try await appPushClient.propose(topic: uri.topic) wait(for: [exp], timeout: 2) } diff --git a/Sources/WalletConnectPairing/PairingClient.swift b/Sources/WalletConnectPairing/PairingClient.swift index bf64bcab0..de16ec03b 100644 --- a/Sources/WalletConnectPairing/PairingClient.swift +++ b/Sources/WalletConnectPairing/PairingClient.swift @@ -4,17 +4,19 @@ import WalletConnectRelay import WalletConnectNetworking import Combine -public class PairingClient { +public class PairingClient: PairingRegisterer { private let walletPairService: WalletPairService private let appPairService: AppPairService public let socketConnectionStatusPublisher: AnyPublisher private let logger: ConsoleLogging private let networkingInteractor: NetworkInteracting + private let pairingRequestsSubscriber: PairingRequestsSubscriber init(appPairService: AppPairService, networkingInteractor: NetworkInteracting, logger: ConsoleLogging, walletPairService: WalletPairService, + pairingRequestsSubscriber: PairingRequestsSubscriber, socketConnectionStatusPublisher: AnyPublisher ) { self.appPairService = appPairService @@ -22,6 +24,7 @@ public class PairingClient { self.networkingInteractor = networkingInteractor self.socketConnectionStatusPublisher = socketConnectionStatusPublisher self.logger = logger + self.pairingRequestsSubscriber = pairingRequestsSubscriber } /// For wallet to establish a pairing /// Wallet should call this function in order to accept peer's pairing proposal and be able to subscribe for future requests. @@ -37,5 +40,9 @@ public class PairingClient { public func create() async throws -> WalletConnectURI { return try await appPairService.create() } + + public func register(method: ProtocolMethod) { + pairingRequestsSubscriber.subscribeForRequest(method) + } } diff --git a/Sources/WalletConnectPairing/PairingClientFactory.swift b/Sources/WalletConnectPairing/PairingClientFactory.swift index 7f4cc3bfb..63a447038 100644 --- a/Sources/WalletConnectPairing/PairingClientFactory.swift +++ b/Sources/WalletConnectPairing/PairingClientFactory.swift @@ -5,19 +5,32 @@ import WalletConnectKMS import WalletConnectNetworking public struct PairingClientFactory { - public static func create(logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, relayClient: RelayClient) -> PairingClient { + + public static func create(relayClient: RelayClient) -> PairingClient { + let logger = ConsoleLogger(loggingLevel: .off) + let keyValueStorage = UserDefaults.standard + let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk") + return PairingClientFactory.create(logger: logger, keyValueStorage: keyValueStorage, keychainStorage: keychainStorage, relayClient: relayClient) + } + + static func create(logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, relayClient: RelayClient) -> PairingClient { let kms = KeyManagementService(keychain: keychainStorage) let serializer = Serializer(kms: kms) let history = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage) - let networkingInteractor = NetworkingInteractor(relayClient: relayClient, serializer: serializer, logger: logger, rpcHistory: history) let pairingStore = PairingStorage(storage: SequenceStore(store: .init(defaults: keyValueStorage, identifier: ""))) - let appPairService = AppPairService(networkingInteractor: networkingInteractor, kms: kms, pairingStorage: pairingStore) + let walletPairService = WalletPairService(networkingInteractor: networkingInteractor, kms: kms, pairingStorage: pairingStore) + let pairingRequestsSubscriber = PairingRequestsSubscriber(networkingInteractor: networkingInteractor, pairingStorage: pairingStore, logger: logger) - let walletPaS = WalletPairService(networkingInteractor: networkingInteractor, kms: kms, pairingStorage: pairingStore) - - return PairingClient(appPairService: appPairService, networkingInteractor: networkingInteractor, logger: logger, walletPairService: walletPaS, socketConnectionStatusPublisher: relayClient.socketConnectionStatusPublisher) + return PairingClient( + appPairService: appPairService, + networkingInteractor: networkingInteractor, + logger: logger, + walletPairService: walletPairService, + pairingRequestsSubscriber: pairingRequestsSubscriber, + socketConnectionStatusPublisher: relayClient.socketConnectionStatusPublisher + ) } } diff --git a/Sources/WalletConnectPairing/PairingRegisterer.swift b/Sources/WalletConnectPairing/PairingRegisterer.swift new file mode 100644 index 000000000..6eafa7b1e --- /dev/null +++ b/Sources/WalletConnectPairing/PairingRegisterer.swift @@ -0,0 +1,6 @@ +import Foundation +import WalletConnectNetworking + +public protocol PairingRegisterer { + func register(method: ProtocolMethod) +} diff --git a/Sources/WalletConnectPairing/Push/PairingRequester.swift b/Sources/WalletConnectPairing/PairingRequester.swift similarity index 100% rename from Sources/WalletConnectPairing/Push/PairingRequester.swift rename to Sources/WalletConnectPairing/PairingRequester.swift diff --git a/Sources/WalletConnectPairing/PairingRequestsSubscriber.swift b/Sources/WalletConnectPairing/PairingRequestsSubscriber.swift new file mode 100644 index 000000000..f997b886f --- /dev/null +++ b/Sources/WalletConnectPairing/PairingRequestsSubscriber.swift @@ -0,0 +1,36 @@ +import Foundation +import Combine +import WalletConnectUtils +import WalletConnectNetworking + +public class PairingRequestsSubscriber { + private let networkingInteractor: NetworkInteracting + private let pairingStorage: PairingStorage + private var publishers = Set() + + init(networkingInteractor: NetworkInteracting, pairingStorage: PairingStorage, logger: ConsoleLogging) { + self.networkingInteractor = networkingInteractor + self.pairingStorage = pairingStorage + } + + func subscribeForRequest(_ protocolMethod: ProtocolMethod) { + networkingInteractor.requestPublisher + // Pairing requests only + .filter { [unowned self] payload in + return pairingStorage.hasPairing(forTopic: payload.topic) + } + // Wrong method + .filter { payload in + return payload.request.method != protocolMethod.method + } + // Respond error + .sink { [unowned self] topic, request in + Task(priority: .high) { + // TODO - spec tag + try await networkingInteractor.respondError(topic: topic, requestId: request.id!, tag: 123456, reason: PairError.methodUnsupported) + } + + }.store(in: &publishers) + } + +} diff --git a/Sources/WalletConnectPairing/Push/PushClient.swift b/Sources/WalletConnectPairing/Push/PushClient.swift index 0a75896ea..6df4561ed 100644 --- a/Sources/WalletConnectPairing/Push/PushClient.swift +++ b/Sources/WalletConnectPairing/Push/PushClient.swift @@ -19,14 +19,17 @@ public class PushClient { private let pushProposer: PushProposer private let networkInteractor: NetworkInteracting + private let pairingRegisterer: PairingRegisterer init(networkInteractor: NetworkInteracting, logger: ConsoleLogging, kms: KeyManagementServiceProtocol, - pushProposer: PushProposer) { + pushProposer: PushProposer, + pairingRegisterer: PairingRegisterer) { self.networkInteractor = networkInteractor self.logger = logger self.pushProposer = pushProposer + self.pairingRegisterer = pairingRegisterer setupPairingSubscriptions() } @@ -39,6 +42,8 @@ public class PushClient { private extension PushClient { func setupPairingSubscriptions() { + pairingRegisterer.register(method: PushProtocolMethod.propose) + networkInteractor.requestSubscription(on: PushProtocolMethod.propose) .sink { [unowned self] (payload: RequestSubscriptionPayload) in requestPublisherSubject.send((payload.topic, payload.request)) diff --git a/Sources/WalletConnectPairing/Push/PushClientFactory.swift b/Sources/WalletConnectPairing/Push/PushClientFactory.swift index ead91fe59..21c4cf74c 100644 --- a/Sources/WalletConnectPairing/Push/PushClientFactory.swift +++ b/Sources/WalletConnectPairing/Push/PushClientFactory.swift @@ -5,14 +5,20 @@ import WalletConnectKMS import WalletConnectNetworking public struct PushClientFactory { - public static func create(logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, relayClient: RelayClient) -> PushClient { + + static func create(logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, relayClient: RelayClient, pairingClient: PairingClient) -> PushClient { let kms = KeyManagementService(keychain: keychainStorage) let serializer = Serializer(kms: kms) let history = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage) - let networkingInteractor = NetworkingInteractor(relayClient: relayClient, serializer: serializer, logger: logger, rpcHistory: history) - let pushProposer = PushProposer(networkingInteractor: networkingInteractor, kms: kms, logger: logger) - return PushClient(networkInteractor: networkingInteractor, logger: logger, kms: kms, pushProposer: pushProposer) + + return PushClient( + networkInteractor: networkingInteractor, + logger: logger, + kms: kms, + pushProposer: pushProposer, + pairingRegisterer: pairingClient + ) } } From 87006a75645ee13cad8373c9c0113895a2b44f46 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Fri, 23 Sep 2022 15:44:52 +0300 Subject: [PATCH 054/175] Push propose error publisher --- Sources/WalletConnectPairing/Push/PushClient.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Sources/WalletConnectPairing/Push/PushClient.swift b/Sources/WalletConnectPairing/Push/PushClient.swift index 6df4561ed..5b232688b 100644 --- a/Sources/WalletConnectPairing/Push/PushClient.swift +++ b/Sources/WalletConnectPairing/Push/PushClient.swift @@ -44,6 +44,11 @@ private extension PushClient { func setupPairingSubscriptions() { pairingRegisterer.register(method: PushProtocolMethod.propose) + networkInteractor.responseErrorSubscription(on: PushProtocolMethod.propose) + .sink { [unowned self] (payload: ResponseSubscriptionErrorPayload) in + print("error") + }.store(in: &publishers) + networkInteractor.requestSubscription(on: PushProtocolMethod.propose) .sink { [unowned self] (payload: RequestSubscriptionPayload) in requestPublisherSubject.send((payload.topic, payload.request)) From eb1ce87585412f61b25ebb69a07e7478c2220b57 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Fri, 23 Sep 2022 15:46:38 +0300 Subject: [PATCH 055/175] Build errors --- Example/IntegrationTests/Pairing/PairingTests.swift | 2 +- Sources/WalletConnectPairing/Push/PushClient.swift | 2 +- Tests/TestingUtils/NetworkingInteractorMock.swift | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Example/IntegrationTests/Pairing/PairingTests.swift b/Example/IntegrationTests/Pairing/PairingTests.swift index bc9aa7338..5a581a215 100644 --- a/Example/IntegrationTests/Pairing/PairingTests.swift +++ b/Example/IntegrationTests/Pairing/PairingTests.swift @@ -55,7 +55,7 @@ final class PairingTests: XCTestCase { } func testProposePushOnPairing() async throws { - let exp = expectation(description: "testProposePushOnPairing") + let exp = expectation(description: "testProposePushOnPairing") walletPushClient.proposalPublisher.sink { _ in exp.fulfill() diff --git a/Sources/WalletConnectPairing/Push/PushClient.swift b/Sources/WalletConnectPairing/Push/PushClient.swift index 5b232688b..4739593e5 100644 --- a/Sources/WalletConnectPairing/Push/PushClient.swift +++ b/Sources/WalletConnectPairing/Push/PushClient.swift @@ -46,7 +46,7 @@ private extension PushClient { networkInteractor.responseErrorSubscription(on: PushProtocolMethod.propose) .sink { [unowned self] (payload: ResponseSubscriptionErrorPayload) in - print("error") + logger.error(payload.error.localizedDescription) }.store(in: &publishers) networkInteractor.requestSubscription(on: PushProtocolMethod.propose) diff --git a/Tests/TestingUtils/NetworkingInteractorMock.swift b/Tests/TestingUtils/NetworkingInteractorMock.swift index 286390803..0ba467bab 100644 --- a/Tests/TestingUtils/NetworkingInteractorMock.swift +++ b/Tests/TestingUtils/NetworkingInteractorMock.swift @@ -30,7 +30,7 @@ public class NetworkingInteractorMock: NetworkInteracting { public let requestPublisherSubject = PassthroughSubject<(topic: String, request: RPCRequest), Never>() public let responsePublisherSubject = PassthroughSubject<(topic: String, request: RPCRequest, response: RPCResponse), Never>() - private var requestPublisher: AnyPublisher<(topic: String, request: RPCRequest), Never> { + public var requestPublisher: AnyPublisher<(topic: String, request: RPCRequest), Never> { requestPublisherSubject.eraseToAnyPublisher() } From d6df9c05401a98bc184770305e2e5765c733ceaf Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 26 Sep 2022 10:26:34 +0200 Subject: [PATCH 056/175] refactor request method --- Sources/Auth/Services/Common/DeletePairingService.swift | 2 +- Sources/Chat/ProtocolServices/Common/MessagingService.swift | 2 +- Sources/Chat/ProtocolServices/Inviter/InviteService.swift | 2 +- Sources/WalletConnectNetworking/NetworkInteracting.swift | 6 +++--- Sources/WalletConnectNetworking/NetworkInteractor.swift | 4 ++-- Sources/WalletConnectPairing/Services/PingRequester.swift | 2 +- Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift | 2 +- .../Engine/Common/DeletePairingService.swift | 2 +- .../Engine/Common/DeleteSessionService.swift | 2 +- Sources/WalletConnectSign/Engine/Common/SessionEngine.swift | 4 ++-- .../Engine/Controller/ControllerSessionStateMachine.swift | 4 ++-- Tests/TestingUtils/NetworkingInteractorMock.swift | 2 +- 12 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Sources/Auth/Services/Common/DeletePairingService.swift b/Sources/Auth/Services/Common/DeletePairingService.swift index 721bdc81f..4ec44f20e 100644 --- a/Sources/Auth/Services/Common/DeletePairingService.swift +++ b/Sources/Auth/Services/Common/DeletePairingService.swift @@ -29,7 +29,7 @@ class DeletePairingService { let reason = AuthError.userDisconnected logger.debug("Will delete pairing for reason: message: \(reason.message) code: \(reason.code)") let request = RPCRequest(method: AuthProtocolMethod.pairingDelete.rawValue, params: reason) - try await networkingInteractor.request(request, topic: topic, tag: AuthProtocolMethod.pairingDelete.requestTag) + try await networkingInteractor.request(request, topic: topic, protocolMethod: AuthProtocolMethod.pairingDelete) pairingStorage.delete(topic: topic) kms.deleteSymmetricKey(for: topic) networkingInteractor.unsubscribe(topic: topic) diff --git a/Sources/Chat/ProtocolServices/Common/MessagingService.swift b/Sources/Chat/ProtocolServices/Common/MessagingService.swift index 20edfb6b5..3e29ab505 100644 --- a/Sources/Chat/ProtocolServices/Common/MessagingService.swift +++ b/Sources/Chat/ProtocolServices/Common/MessagingService.swift @@ -34,7 +34,7 @@ class MessagingService { let timestamp = Int64(Date().timeIntervalSince1970 * 1000) let message = Message(topic: topic, message: messageString, authorAccount: authorAccount, timestamp: timestamp) let request = RPCRequest(method: ChatProtocolMethod.message.method, params: message) - try await networkingInteractor.request(request, topic: topic, tag: ChatProtocolMethod.message.requestTag) + try await networkingInteractor.request(request, topic: topic, protocolMethod: ChatProtocolMethod.message) Task(priority: .background) { await messagesStore.add(message) onMessage?(message) diff --git a/Sources/Chat/ProtocolServices/Inviter/InviteService.swift b/Sources/Chat/ProtocolServices/Inviter/InviteService.swift index 514138042..706baddc1 100644 --- a/Sources/Chat/ProtocolServices/Inviter/InviteService.swift +++ b/Sources/Chat/ProtocolServices/Inviter/InviteService.swift @@ -50,7 +50,7 @@ class InviteService { try kms.setSymmetricKey(symKeyI.sharedKey, for: responseTopic) try await networkingInteractor.subscribe(topic: responseTopic) - try await networkingInteractor.request(request, topic: inviteTopic, tag: ChatProtocolMethod.invite.requestTag, envelopeType: .type1(pubKey: selfPubKeyY.rawRepresentation)) + try await networkingInteractor.request(request, topic: inviteTopic, protocolMethod: ChatProtocolMethod.invite, envelopeType: .type1(pubKey: selfPubKeyY.rawRepresentation)) logger.debug("invite sent on topic: \(inviteTopic)") } diff --git a/Sources/WalletConnectNetworking/NetworkInteracting.swift b/Sources/WalletConnectNetworking/NetworkInteracting.swift index 85ca9177e..576887da0 100644 --- a/Sources/WalletConnectNetworking/NetworkInteracting.swift +++ b/Sources/WalletConnectNetworking/NetworkInteracting.swift @@ -8,7 +8,7 @@ public protocol NetworkInteracting { var socketConnectionStatusPublisher: AnyPublisher { get } func subscribe(topic: String) async throws func unsubscribe(topic: String) - func request(_ request: RPCRequest, topic: String, tag: Int, envelopeType: Envelope.EnvelopeType) async throws + func request(_ request: RPCRequest, topic: String, protocolMethod: ProtocolMethod, envelopeType: Envelope.EnvelopeType) async throws func requestNetworkAck(_ request: RPCRequest, topic: String, tag: Int) async throws func respond(topic: String, response: RPCResponse, tag: Int, envelopeType: Envelope.EnvelopeType) async throws func respondSuccess(topic: String, requestId: RPCID, tag: Int, envelopeType: Envelope.EnvelopeType) async throws @@ -28,8 +28,8 @@ public protocol NetworkInteracting { } extension NetworkInteracting { - public func request(_ request: RPCRequest, topic: String, tag: Int) async throws { - try await self.request(request, topic: topic, tag: tag, envelopeType: .type0) + public func request(_ request: RPCRequest, topic: String, protocolMethod: ProtocolMethod) async throws { + try await self.request(request, topic: topic, protocolMethod: protocolMethod, envelopeType: .type0) } public func respond(topic: String, response: RPCResponse, tag: Int) async throws { diff --git a/Sources/WalletConnectNetworking/NetworkInteractor.swift b/Sources/WalletConnectNetworking/NetworkInteractor.swift index 111d0003f..b8a5d8b9b 100644 --- a/Sources/WalletConnectNetworking/NetworkInteractor.swift +++ b/Sources/WalletConnectNetworking/NetworkInteractor.swift @@ -93,10 +93,10 @@ public class NetworkingInteractor: NetworkInteracting { .eraseToAnyPublisher() } - public func request(_ request: RPCRequest, topic: String, tag: Int, envelopeType: Envelope.EnvelopeType) async throws { + public func request(_ request: RPCRequest, topic: String, protocolMethod: ProtocolMethod, envelopeType: Envelope.EnvelopeType) async throws { try rpcHistory.set(request, forTopic: topic, emmitedBy: .local) let message = try! serializer.serialize(topic: topic, encodable: request, envelopeType: envelopeType) - try await relayClient.publish(topic: topic, payload: message, tag: tag, prompt: shouldPrompt(method: request.method)) + try await relayClient.publish(topic: topic, payload: message, tag: protocolMethod.requestTag, prompt: shouldPrompt(method: request.method)) } /// Completes with an acknowledgement from the relay network. diff --git a/Sources/WalletConnectPairing/Services/PingRequester.swift b/Sources/WalletConnectPairing/Services/PingRequester.swift index 9def19d28..f5007c9c6 100644 --- a/Sources/WalletConnectPairing/Services/PingRequester.swift +++ b/Sources/WalletConnectPairing/Services/PingRequester.swift @@ -13,6 +13,6 @@ public class PingRequester { public func ping(topic: String) async throws { let request = RPCRequest(method: method.method, params: PairingPingParams()) - try await networkingInteractor.request(request, topic: topic, tag: method.requestTag) + try await networkingInteractor.request(request, topic: topic, protocolMethod: method) } } diff --git a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift index 5a23eb079..cfe3506cc 100644 --- a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift @@ -144,7 +144,7 @@ final class ApproveEngine { sessionStore.setSession(session) let request = RPCRequest(method: SignProtocolMethod.sessionSettle.method, params: settleParams) - try await networkingInteractor.request(request, topic: topic, tag: SignProtocolMethod.sessionSettle.requestTag) + try await networkingInteractor.request(request, topic: topic, protocolMethod: SignProtocolMethod.sessionSettle) onSessionSettle?(session.publicRepresentation()) } } diff --git a/Sources/WalletConnectSign/Engine/Common/DeletePairingService.swift b/Sources/WalletConnectSign/Engine/Common/DeletePairingService.swift index 2dbbede9d..1e6aa3d19 100644 --- a/Sources/WalletConnectSign/Engine/Common/DeletePairingService.swift +++ b/Sources/WalletConnectSign/Engine/Common/DeletePairingService.swift @@ -26,7 +26,7 @@ class DeletePairingService { let reason = SessionType.Reason(code: reasonCode.code, message: reasonCode.message) logger.debug("Will delete pairing for reason: message: \(reason.message) code: \(reason.code)") let request = RPCRequest(method: SignProtocolMethod.sessionDelete.method, params: reason) - try await networkingInteractor.request(request, topic: topic, tag: SignProtocolMethod.sessionDelete.requestTag) + try await networkingInteractor.request(request, topic: topic, protocolMethod: SignProtocolMethod.sessionDelete) pairingStorage.delete(topic: topic) kms.deleteSymmetricKey(for: topic) networkingInteractor.unsubscribe(topic: topic) diff --git a/Sources/WalletConnectSign/Engine/Common/DeleteSessionService.swift b/Sources/WalletConnectSign/Engine/Common/DeleteSessionService.swift index f2ff1442d..eda023715 100644 --- a/Sources/WalletConnectSign/Engine/Common/DeleteSessionService.swift +++ b/Sources/WalletConnectSign/Engine/Common/DeleteSessionService.swift @@ -25,7 +25,7 @@ class DeleteSessionService { let reason = SessionType.Reason(code: reasonCode.code, message: reasonCode.message) logger.debug("Will delete session for reason: message: \(reason.message) code: \(reason.code)") let request = RPCRequest(method: SignProtocolMethod.sessionDelete.method, params: reason) - try await networkingInteractor.request(request, topic: topic, tag: SignProtocolMethod.sessionDelete.requestTag) + try await networkingInteractor.request(request, topic: topic, protocolMethod: SignProtocolMethod.sessionDelete) sessionStore.delete(topic: topic) kms.deleteSymmetricKey(for: topic) networkingInteractor.unsubscribe(topic: topic) diff --git a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift index 2c9c2be07..4ed7a78bd 100644 --- a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift @@ -60,7 +60,7 @@ final class SessionEngine { let sessionRequestParams = SessionType.RequestParams(request: chainRequest, chainId: request.chainId) let rpcRequest = RPCRequest(method: SignProtocolMethod.sessionRequest.method, params: sessionRequestParams) - try await networkingInteractor.request(rpcRequest, topic: request.topic, tag: SignProtocolMethod.sessionRequest.requestTag) + try await networkingInteractor.request(rpcRequest, topic: request.topic, protocolMethod: SignProtocolMethod.sessionRequest) } func respondSessionRequest(topic: String, requestId: RPCID, response: RPCResult) async throws { @@ -80,7 +80,7 @@ final class SessionEngine { throw WalletConnectError.invalidEvent } let rpcRequest = RPCRequest(method: SignProtocolMethod.sessionEvent.method, params: SessionType.EventParams(event: event, chainId: chainId)) - try await networkingInteractor.request(rpcRequest, topic: topic, tag: SignProtocolMethod.sessionEvent.requestTag) + try await networkingInteractor.request(rpcRequest, topic: topic, protocolMethod: SignProtocolMethod.sessionEvent) } } diff --git a/Sources/WalletConnectSign/Engine/Controller/ControllerSessionStateMachine.swift b/Sources/WalletConnectSign/Engine/Controller/ControllerSessionStateMachine.swift index be4733984..c7755e08e 100644 --- a/Sources/WalletConnectSign/Engine/Controller/ControllerSessionStateMachine.swift +++ b/Sources/WalletConnectSign/Engine/Controller/ControllerSessionStateMachine.swift @@ -35,7 +35,7 @@ final class ControllerSessionStateMachine { logger.debug("Controller will update methods") sessionStore.setSession(session) let request = RPCRequest(method: SignProtocolMethod.sessionUpdate.method, params: SessionType.UpdateParams(namespaces: namespaces)) - try await networkingInteractor.request(request, topic: topic, tag: SignProtocolMethod.sessionUpdate.requestTag) + try await networkingInteractor.request(request, topic: topic, protocolMethod: SignProtocolMethod.sessionUpdate) } func extend(topic: String, by ttl: Int64) async throws { @@ -45,7 +45,7 @@ final class ControllerSessionStateMachine { let newExpiry = Int64(session.expiryDate.timeIntervalSince1970 ) sessionStore.setSession(session) let request = RPCRequest(method: SignProtocolMethod.sessionExtend.method, params: SessionType.UpdateExpiryParams(expiry: newExpiry)) - try await networkingInteractor.request(request, topic: topic, tag: SignProtocolMethod.sessionExtend.requestTag) + try await networkingInteractor.request(request, topic: topic, protocolMethod: SignProtocolMethod.sessionExtend) } // MARK: - Handle Response diff --git a/Tests/TestingUtils/NetworkingInteractorMock.swift b/Tests/TestingUtils/NetworkingInteractorMock.swift index 286390803..6f7fff08a 100644 --- a/Tests/TestingUtils/NetworkingInteractorMock.swift +++ b/Tests/TestingUtils/NetworkingInteractorMock.swift @@ -96,7 +96,7 @@ public class NetworkingInteractorMock: NetworkInteracting { didCallUnsubscribe = true } - public func request(_ request: RPCRequest, topic: String, tag: Int, envelopeType: Envelope.EnvelopeType) async throws { + public func request(_ request: RPCRequest, topic: String, protocolMethod: ProtocolMethod, envelopeType: Envelope.EnvelopeType) async throws { requestCallCount += 1 requests.append((topic, request)) } From 3128d0986e8d2dca28a276998ab8fc734a4b07f6 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 26 Sep 2022 11:05:29 +0200 Subject: [PATCH 057/175] refactor respond method --- .../Wallet/WalletErrorResponder.swift | 3 +- .../Wallet/WalletRespondService.swift | 2 +- .../Common/MessagingService.swift | 2 +- .../Invitee/InvitationHandlingService.swift | 4 +-- .../NetworkInteracting.swift | 18 +++++------ .../NetworkInteractor.swift | 12 ++++---- .../Services/PingResponder.swift | 2 +- .../Engine/Common/ApproveEngine.swift | 20 ++++++------- .../Engine/Common/PairingEngine.swift | 2 +- .../Engine/Common/SessionEngine.swift | 30 +++++++++---------- .../NonControllerSessionStateMachine.swift | 24 ++++++++------- 11 files changed, 60 insertions(+), 59 deletions(-) diff --git a/Sources/Auth/Services/Wallet/WalletErrorResponder.swift b/Sources/Auth/Services/Wallet/WalletErrorResponder.swift index 10e318c24..b774c3865 100644 --- a/Sources/Auth/Services/Wallet/WalletErrorResponder.swift +++ b/Sources/Auth/Services/Wallet/WalletErrorResponder.swift @@ -32,9 +32,8 @@ actor WalletErrorResponder { try kms.setAgreementSecret(keys, topic: topic) - let tag = AuthProtocolMethod.authRequest.responseTag let envelopeType = Envelope.EnvelopeType.type1(pubKey: keys.publicKey.rawRepresentation) - try await networkingInteractor.respondError(topic: topic, requestId: requestId, tag: tag, reason: error, envelopeType: envelopeType) + try await networkingInteractor.respondError(topic: topic, requestId: requestId, protocolMethod: AuthProtocolMethod.authRequest, reason: error, envelopeType: envelopeType) } private func getAuthRequestParams(requestId: RPCID) throws -> AuthRequestParams { diff --git a/Sources/Auth/Services/Wallet/WalletRespondService.swift b/Sources/Auth/Services/Wallet/WalletRespondService.swift index f9a53b10d..c9529c197 100644 --- a/Sources/Auth/Services/Wallet/WalletRespondService.swift +++ b/Sources/Auth/Services/Wallet/WalletRespondService.swift @@ -39,7 +39,7 @@ actor WalletRespondService { let responseParams = AuthResponseParams(h: header, p: payload, s: signature) let response = RPCResponse(id: requestId, result: responseParams) - try await networkingInteractor.respond(topic: topic, response: response, tag: AuthProtocolMethod.authRequest.responseTag, envelopeType: .type1(pubKey: keys.publicKey.rawRepresentation)) + try await networkingInteractor.respond(topic: topic, response: response, protocolMethod: AuthProtocolMethod.authRequest, envelopeType: .type1(pubKey: keys.publicKey.rawRepresentation)) } func respondError(requestId: RPCID) async throws { diff --git a/Sources/Chat/ProtocolServices/Common/MessagingService.swift b/Sources/Chat/ProtocolServices/Common/MessagingService.swift index 3e29ab505..027afc5d8 100644 --- a/Sources/Chat/ProtocolServices/Common/MessagingService.swift +++ b/Sources/Chat/ProtocolServices/Common/MessagingService.swift @@ -59,7 +59,7 @@ class MessagingService { private func handleMessage(_ message: Message, topic: String, requestId: RPCID) { Task(priority: .background) { - try await networkingInteractor.respondSuccess(topic: topic, requestId: requestId, tag: ChatProtocolMethod.message.responseTag) + try await networkingInteractor.respondSuccess(topic: topic, requestId: requestId, protocolMethod: ChatProtocolMethod.message) await messagesStore.add(message) logger.debug("Received message") onMessage?(message) diff --git a/Sources/Chat/ProtocolServices/Invitee/InvitationHandlingService.swift b/Sources/Chat/ProtocolServices/Invitee/InvitationHandlingService.swift index c6dd9aede..1c07311b0 100644 --- a/Sources/Chat/ProtocolServices/Invitee/InvitationHandlingService.swift +++ b/Sources/Chat/ProtocolServices/Invitee/InvitationHandlingService.swift @@ -47,7 +47,7 @@ class InvitationHandlingService { let response = RPCResponse(id: payload.id, result: inviteResponse) let responseTopic = try getInviteResponseTopic(requestTopic: payload.topic, invite: payload.request) - try await networkingInteractor.respond(topic: responseTopic, response: response, tag: ChatProtocolMethod.invite.responseTag) + try await networkingInteractor.respond(topic: responseTopic, response: response, protocolMethod: ChatProtocolMethod.invite) let threadAgreementKeys = try kms.performKeyAgreement(selfPublicKey: selfThreadPubKey, peerPublicKey: payload.request.publicKey) let threadTopic = threadAgreementKeys.derivedTopic() @@ -71,7 +71,7 @@ class InvitationHandlingService { let responseTopic = try getInviteResponseTopic(requestTopic: payload.topic, invite: payload.request) - try await networkingInteractor.respondError(topic: responseTopic, requestId: payload.id, tag: ChatProtocolMethod.invite.responseTag, reason: ChatError.userRejected) + try await networkingInteractor.respondError(topic: responseTopic, requestId: payload.id, protocolMethod: ChatProtocolMethod.invite, reason: ChatError.userRejected) invitePayloadStore.delete(forKey: inviteId) } diff --git a/Sources/WalletConnectNetworking/NetworkInteracting.swift b/Sources/WalletConnectNetworking/NetworkInteracting.swift index 576887da0..33d5cea2d 100644 --- a/Sources/WalletConnectNetworking/NetworkInteracting.swift +++ b/Sources/WalletConnectNetworking/NetworkInteracting.swift @@ -10,9 +10,9 @@ public protocol NetworkInteracting { func unsubscribe(topic: String) func request(_ request: RPCRequest, topic: String, protocolMethod: ProtocolMethod, envelopeType: Envelope.EnvelopeType) async throws func requestNetworkAck(_ request: RPCRequest, topic: String, tag: Int) async throws - func respond(topic: String, response: RPCResponse, tag: Int, envelopeType: Envelope.EnvelopeType) async throws - func respondSuccess(topic: String, requestId: RPCID, tag: Int, envelopeType: Envelope.EnvelopeType) async throws - func respondError(topic: String, requestId: RPCID, tag: Int, reason: Reason, envelopeType: Envelope.EnvelopeType) async throws + func respond(topic: String, response: RPCResponse, protocolMethod: ProtocolMethod, envelopeType: Envelope.EnvelopeType) async throws + func respondSuccess(topic: String, requestId: RPCID, protocolMethod: ProtocolMethod, envelopeType: Envelope.EnvelopeType) async throws + func respondError(topic: String, requestId: RPCID, protocolMethod: ProtocolMethod, reason: Reason, envelopeType: Envelope.EnvelopeType) async throws func requestSubscription( on request: ProtocolMethod @@ -32,15 +32,15 @@ extension NetworkInteracting { try await self.request(request, topic: topic, protocolMethod: protocolMethod, envelopeType: .type0) } - public func respond(topic: String, response: RPCResponse, tag: Int) async throws { - try await self.respond(topic: topic, response: response, tag: tag, envelopeType: .type0) + public func respond(topic: String, response: RPCResponse, protocolMethod: ProtocolMethod) async throws { + try await self.respond(topic: topic, response: response, protocolMethod: protocolMethod, envelopeType: .type0) } - public func respondSuccess(topic: String, requestId: RPCID, tag: Int) async throws { - try await self.respondSuccess(topic: topic, requestId: requestId, tag: tag, envelopeType: .type0) + public func respondSuccess(topic: String, requestId: RPCID, protocolMethod: ProtocolMethod) async throws { + try await self.respondSuccess(topic: topic, requestId: requestId, protocolMethod: protocolMethod, envelopeType: .type0) } - public func respondError(topic: String, requestId: RPCID, tag: Int, reason: Reason) async throws { - try await self.respondError(topic: topic, requestId: requestId, tag: tag, reason: reason, envelopeType: .type0) + public func respondError(topic: String, requestId: RPCID, protocolMethod: ProtocolMethod, reason: Reason) async throws { + try await self.respondError(topic: topic, requestId: requestId, protocolMethod: protocolMethod, reason: reason, envelopeType: .type0) } } diff --git a/Sources/WalletConnectNetworking/NetworkInteractor.swift b/Sources/WalletConnectNetworking/NetworkInteractor.swift index b8a5d8b9b..a781b2049 100644 --- a/Sources/WalletConnectNetworking/NetworkInteractor.swift +++ b/Sources/WalletConnectNetworking/NetworkInteractor.swift @@ -120,21 +120,21 @@ public class NetworkingInteractor: NetworkInteracting { } } - public func respond(topic: String, response: RPCResponse, tag: Int, envelopeType: Envelope.EnvelopeType) async throws { + public func respond(topic: String, response: RPCResponse, protocolMethod: ProtocolMethod, envelopeType: Envelope.EnvelopeType) async throws { try rpcHistory.resolve(response) let message = try! serializer.serialize(topic: topic, encodable: response, envelopeType: envelopeType) - try await relayClient.publish(topic: topic, payload: message, tag: tag) + try await relayClient.publish(topic: topic, payload: message, tag: protocolMethod.responseTag) } - public func respondSuccess(topic: String, requestId: RPCID, tag: Int, envelopeType: Envelope.EnvelopeType) async throws { + public func respondSuccess(topic: String, requestId: RPCID, protocolMethod: ProtocolMethod, envelopeType: Envelope.EnvelopeType) async throws { let response = RPCResponse(id: requestId, result: true) - try await respond(topic: topic, response: response, tag: tag, envelopeType: envelopeType) + try await respond(topic: topic, response: response, protocolMethod: protocolMethod, envelopeType: envelopeType) } - public func respondError(topic: String, requestId: RPCID, tag: Int, reason: Reason, envelopeType: Envelope.EnvelopeType) async throws { + public func respondError(topic: String, requestId: RPCID, protocolMethod: ProtocolMethod, reason: Reason, envelopeType: Envelope.EnvelopeType) async throws { let error = JSONRPCError(code: reason.code, message: reason.message) let response = RPCResponse(id: requestId, error: error) - try await respond(topic: topic, response: response, tag: tag, envelopeType: envelopeType) + try await respond(topic: topic, response: response, protocolMethod: protocolMethod, envelopeType: envelopeType) } private func manageSubscription(_ topic: String, _ encodedEnvelope: String) { diff --git a/Sources/WalletConnectPairing/Services/PingResponder.swift b/Sources/WalletConnectPairing/Services/PingResponder.swift index 7718f6128..60835c29c 100644 --- a/Sources/WalletConnectPairing/Services/PingResponder.swift +++ b/Sources/WalletConnectPairing/Services/PingResponder.swift @@ -22,7 +22,7 @@ public class PingResponder { .sink { [unowned self] (payload: RequestSubscriptionPayload) in logger.debug("Responding for pairing ping") Task(priority: .high) { - try? await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, tag: method.responseTag) + try? await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, protocolMethod: method) } } .store(in: &publishers) diff --git a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift index cfe3506cc..126ea595b 100644 --- a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift @@ -89,7 +89,7 @@ final class ApproveEngine { let result = SessionType.ProposeResponse(relay: relay, responderPublicKey: selfPublicKey.hexRepresentation) let response = RPCResponse(id: payload.id, result: result) - try await networkingInteractor.respond(topic: payload.topic, response: response, tag: SignProtocolMethod.sessionPropose.responseTag) + try await networkingInteractor.respond(topic: payload.topic, response: response, protocolMethod: SignProtocolMethod.sessionPropose) try pairing.updateExpiry() pairingStore.setPairing(pairing) @@ -102,7 +102,7 @@ final class ApproveEngine { throw Errors.proposalPayloadsNotFound } proposalPayloadsStore.delete(forKey: proposerPubKey) - try await networkingInteractor.respondError(topic: payload.topic, requestId: payload.id, tag: SignProtocolMethod.sessionPropose.responseTag, reason: reason) + try await networkingInteractor.respondError(topic: payload.topic, requestId: payload.id, protocolMethod: SignProtocolMethod.sessionPropose, reason: reason) // TODO: Delete pairing if inactive } @@ -189,10 +189,10 @@ private extension ApproveEngine { }.store(in: &publishers) } - func respondError(payload: SubscriptionPayload, reason: ReasonCode, tag: Int) { + func respondError(payload: SubscriptionPayload, reason: ReasonCode, protocolMethod: ProtocolMethod) { Task(priority: .high) { do { - try await networkingInteractor.respondError(topic: payload.topic, requestId: payload.id, tag: tag, reason: reason) + try await networkingInteractor.respondError(topic: payload.topic, requestId: payload.id, protocolMethod: protocolMethod, reason: reason) } catch { logger.error("Respond Error failed with: \(error.localizedDescription)") } @@ -292,7 +292,7 @@ private extension ApproveEngine { logger.debug("Received Session Proposal") let proposal = payload.request do { try Namespace.validate(proposal.requiredNamespaces) } catch { - return respondError(payload: payload, reason: .invalidUpdateRequest, tag: SignProtocolMethod.sessionPropose.responseTag) + return respondError(payload: payload, reason: .invalidUpdateRequest, protocolMethod: SignProtocolMethod.sessionPropose) } proposalPayloadsStore.set(payload, forKey: proposal.proposer.publicKey) onSessionProposal?(proposal.publicRepresentation()) @@ -303,10 +303,10 @@ private extension ApproveEngine { func handleSessionSettleRequest(payload: RequestSubscriptionPayload) { logger.debug("Did receive session settle request") - let tag = SignProtocolMethod.sessionSettle.responseTag + let protocolMethod = SignProtocolMethod.sessionSettle guard let proposedNamespaces = settlingProposal?.requiredNamespaces else { - return respondError(payload: payload, reason: .invalidUpdateRequest, tag: tag) + return respondError(payload: payload, reason: .invalidUpdateRequest, protocolMethod: protocolMethod) } settlingProposal = nil @@ -318,9 +318,9 @@ private extension ApproveEngine { try Namespace.validate(sessionNamespaces) try Namespace.validateApproved(sessionNamespaces, against: proposedNamespaces) } catch WalletConnectError.unsupportedNamespace(let reason) { - return respondError(payload: payload, reason: reason, tag: tag) + return respondError(payload: payload, reason: reason, protocolMethod: protocolMethod) } catch { - return respondError(payload: payload, reason: .invalidUpdateRequest, tag: tag) + return respondError(payload: payload, reason: .invalidUpdateRequest, protocolMethod: protocolMethod) } let topic = payload.topic @@ -344,7 +344,7 @@ private extension ApproveEngine { ) sessionStore.setSession(session) Task(priority: .high) { - try await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, tag: tag) + try await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, protocolMethod: protocolMethod) } onSessionSettle?(session.publicRepresentation()) } diff --git a/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift b/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift index d6a012de0..6a9b7cae3 100644 --- a/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift @@ -94,7 +94,7 @@ private extension PairingEngine { networkingInteractor.requestSubscription(on: SignProtocolMethod.pairingPing) .sink { [unowned self] (payload: RequestSubscriptionPayload) in Task(priority: .high) { - try await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, tag: SignProtocolMethod.pairingPing.responseTag) + try await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, protocolMethod: SignProtocolMethod.pairingPing) } } .store(in: &publishers) diff --git a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift index 4ed7a78bd..ec8c1911c 100644 --- a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift @@ -68,7 +68,7 @@ final class SessionEngine { throw Errors.sessionNotFound(topic: topic) } let response = RPCResponse(id: requestId, result: response) - try await networkingInteractor.respond(topic: topic, response: response, tag: 1109) // FIXME: Hardcoded tag + try await networkingInteractor.respond(topic: topic, response: response, protocolMethod: SignProtocolMethod.sessionRequest) } func emit(topic: String, event: SessionType.EventParams.Event, chainId: Blockchain) async throws { @@ -141,10 +141,10 @@ private extension SessionEngine { } } - func respondError(payload: SubscriptionPayload, reason: ReasonCode, tag: Int) { + func respondError(payload: SubscriptionPayload, reason: ReasonCode, protocolMethod: ProtocolMethod) { Task(priority: .high) { do { - try await networkingInteractor.respondError(topic: payload.topic, requestId: payload.id, tag: tag, reason: reason) + try await networkingInteractor.respondError(topic: payload.topic, requestId: payload.id, protocolMethod: protocolMethod, reason: reason) } catch { logger.error("Respond Error failed with: \(error.localizedDescription)") } @@ -152,21 +152,21 @@ private extension SessionEngine { } func onSessionDelete(payload: RequestSubscriptionPayload) { - let tag = SignProtocolMethod.sessionDelete.responseTag + let protocolMethod = SignProtocolMethod.sessionDelete let topic = payload.topic guard sessionStore.hasSession(forTopic: topic) else { - return respondError(payload: payload, reason: .noSessionForTopic, tag: tag) + return respondError(payload: payload, reason: .noSessionForTopic, protocolMethod: protocolMethod) } sessionStore.delete(topic: topic) networkingInteractor.unsubscribe(topic: topic) Task(priority: .high) { - try await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, tag: tag) + try await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, protocolMethod: protocolMethod) } onSessionDelete?(topic, payload.request) } func onSessionRequest(payload: RequestSubscriptionPayload) { - let tag = SignProtocolMethod.sessionRequest.responseTag + let protocolMethod = SignProtocolMethod.sessionRequest let topic = payload.topic let request = Request( id: payload.id, @@ -176,35 +176,35 @@ private extension SessionEngine { chainId: payload.request.chainId) guard let session = sessionStore.getSession(forTopic: topic) else { - return respondError(payload: payload, reason: .noSessionForTopic, tag: tag) + return respondError(payload: payload, reason: .noSessionForTopic, protocolMethod: protocolMethod) } guard session.hasNamespace(for: request.chainId) else { - return respondError(payload: payload, reason: .unauthorizedChain, tag: tag) + return respondError(payload: payload, reason: .unauthorizedChain, protocolMethod: protocolMethod) } guard session.hasPermission(forMethod: request.method, onChain: request.chainId) else { - return respondError(payload: payload, reason: .unauthorizedMethod(request.method), tag: tag) + return respondError(payload: payload, reason: .unauthorizedMethod(request.method), protocolMethod: protocolMethod) } onSessionRequest?(request) } func onSessionPing(payload: SubscriptionPayload) { Task(priority: .high) { - try await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, tag: SignProtocolMethod.sessionPing.responseTag) + try await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, protocolMethod: SignProtocolMethod.sessionPing) } } func onSessionEvent(payload: RequestSubscriptionPayload) { - let tag = SignProtocolMethod.sessionEvent.responseTag + let protocolMethod = SignProtocolMethod.sessionEvent let event = payload.request.event let topic = payload.topic guard let session = sessionStore.getSession(forTopic: topic) else { - return respondError(payload: payload, reason: .noSessionForTopic, tag: tag) + return respondError(payload: payload, reason: .noSessionForTopic, protocolMethod: protocolMethod) } guard session.peerIsController, session.hasPermission(forEvent: event.name, onChain: payload.request.chainId) else { - return respondError(payload: payload, reason: .unauthorizedEvent(event.name), tag: tag) + return respondError(payload: payload, reason: .unauthorizedEvent(event.name), protocolMethod: protocolMethod) } Task(priority: .high) { - try await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, tag: tag) + try await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, protocolMethod: protocolMethod) } onEventReceived?(topic, event.publicRepresentation(), payload.request.chainId) } diff --git a/Sources/WalletConnectSign/Engine/NonController/NonControllerSessionStateMachine.swift b/Sources/WalletConnectSign/Engine/NonController/NonControllerSessionStateMachine.swift index 7cca1066b..a2af79032 100644 --- a/Sources/WalletConnectSign/Engine/NonController/NonControllerSessionStateMachine.swift +++ b/Sources/WalletConnectSign/Engine/NonController/NonControllerSessionStateMachine.swift @@ -38,10 +38,10 @@ final class NonControllerSessionStateMachine { }.store(in: &publishers) } - private func respondError(payload: SubscriptionPayload, reason: ReasonCode, tag: Int) { + private func respondError(payload: SubscriptionPayload, reason: ReasonCode, protocolMethod: ProtocolMethod) { Task(priority: .high) { do { - try await networkingInteractor.respondError(topic: payload.topic, requestId: payload.id, tag: tag, reason: reason) + try await networkingInteractor.respondError(topic: payload.topic, requestId: payload.id, protocolMethod: protocolMethod, reason: reason) } catch { logger.error("Respond Error failed with: \(error.localizedDescription)") } @@ -50,48 +50,50 @@ final class NonControllerSessionStateMachine { // TODO: Update stored session namespaces private func onSessionUpdateNamespacesRequest(payload: SubscriptionPayload, updateParams: SessionType.UpdateParams) { + let protocolMethod = SignProtocolMethod.sessionUpdate do { try Namespace.validate(updateParams.namespaces) } catch { - return respondError(payload: payload, reason: .invalidUpdateRequest, tag: SignProtocolMethod.sessionUpdate.responseTag) + return respondError(payload: payload, reason: .invalidUpdateRequest, protocolMethod: protocolMethod) } guard var session = sessionStore.getSession(forTopic: payload.topic) else { - return respondError(payload: payload, reason: .noSessionForTopic, tag: SignProtocolMethod.sessionUpdate.responseTag) + return respondError(payload: payload, reason: .noSessionForTopic, protocolMethod: protocolMethod) } guard session.peerIsController else { - return respondError(payload: payload, reason: .unauthorizedUpdateRequest, tag: SignProtocolMethod.sessionUpdate.responseTag) + return respondError(payload: payload, reason: .unauthorizedUpdateRequest, protocolMethod: protocolMethod) } do { try session.updateNamespaces(updateParams.namespaces, timestamp: payload.id.timestamp) } catch { - return respondError(payload: payload, reason: .invalidUpdateRequest, tag: SignProtocolMethod.sessionUpdate.responseTag) + return respondError(payload: payload, reason: .invalidUpdateRequest, protocolMethod: protocolMethod) } sessionStore.setSession(session) Task(priority: .high) { - try await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, tag: SignProtocolMethod.sessionUpdate.responseTag) + try await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, protocolMethod: protocolMethod) } onNamespacesUpdate?(session.topic, updateParams.namespaces) } private func onSessionUpdateExpiry(payload: SubscriptionPayload, updateExpiryParams: SessionType.UpdateExpiryParams) { + let protocolMethod = SignProtocolMethod.sessionExtend let topic = payload.topic guard var session = sessionStore.getSession(forTopic: topic) else { - return respondError(payload: payload, reason: .noSessionForTopic, tag: SignProtocolMethod.sessionExtend.responseTag) + return respondError(payload: payload, reason: .noSessionForTopic, protocolMethod: protocolMethod) } guard session.peerIsController else { - return respondError(payload: payload, reason: .unauthorizedExtendRequest, tag: SignProtocolMethod.sessionExtend.responseTag) + return respondError(payload: payload, reason: .unauthorizedExtendRequest, protocolMethod: protocolMethod) } do { try session.updateExpiry(to: updateExpiryParams.expiry) } catch { - return respondError(payload: payload, reason: .invalidExtendRequest, tag: SignProtocolMethod.sessionExtend.responseTag) + return respondError(payload: payload, reason: .invalidExtendRequest, protocolMethod: protocolMethod) } sessionStore.setSession(session) Task(priority: .high) { - try await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, tag: SignProtocolMethod.sessionExtend.responseTag) + try await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, protocolMethod: SignProtocolMethod.sessionExtend) } onExtend?(session.topic, session.expiryDate) From 8a04461ad381fca904f684204f0f4b5bb2c738b5 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 26 Sep 2022 12:05:14 +0200 Subject: [PATCH 058/175] refactor protocol methods in auth and chat --- Sources/Auth/AuthProtocolMethod.swift | 54 +++++++++---------- .../Auth/Services/App/AppRequestService.swift | 2 +- .../Services/App/AppRespondSubscriber.swift | 4 +- .../Common/DeletePairingService.swift | 5 +- .../Wallet/WalletErrorResponder.swift | 2 +- .../Wallet/WalletRequestSubscriber.swift | 2 +- .../Wallet/WalletRespondService.swift | 2 +- .../Common/MessagingService.swift | 11 ++-- .../Invitee/InvitationHandlingService.swift | 8 +-- .../Inviter/InviteService.swift | 7 +-- Sources/Chat/Types/ChatProtocolMethod.swift | 46 ++++++---------- .../NetworkInteracting.swift | 2 +- .../NetworkInteractor.swift | 17 ++---- .../ProtocolMethod.swift | 14 ++++- .../PairingProtocolMethod.swift | 16 ++---- .../Services/PairingPingService.swift | 7 +-- .../Engine/Common/PairingEngine.swift | 2 +- .../NetworkingInteractorMock.swift | 2 +- 18 files changed, 91 insertions(+), 112 deletions(-) diff --git a/Sources/Auth/AuthProtocolMethod.swift b/Sources/Auth/AuthProtocolMethod.swift index c3df72dae..c15cc76ef 100644 --- a/Sources/Auth/AuthProtocolMethod.swift +++ b/Sources/Auth/AuthProtocolMethod.swift @@ -1,34 +1,28 @@ import Foundation import WalletConnectNetworking -enum AuthProtocolMethod: String, ProtocolMethod { - case authRequest = "wc_authRequest" - case pairingDelete = "wc_pairingDelete" - case pairingPing = "wc_pairingPing" - - var method: String { - return self.rawValue - } - - var requestTag: Int { - switch self { - case .authRequest: - return 3000 - case .pairingDelete: - return 1000 - case .pairingPing: - return 1002 - } - } - - var responseTag: Int { - switch self { - case .authRequest: - return 3001 - case .pairingDelete: - return 1001 - case .pairingPing: - return 1003 - } - } +struct AuthRequestProtocolMethod: ProtocolMethod { + var method: String = "wc_authRequest" + + var request = RelayConfigrable(tag: 3000, prompt: true) + + var response = RelayConfigrable(tag: 3001, prompt: false) +} + + +struct PairingPingProtocolMethod: ProtocolMethod { + var method: String = "wc_pairingPing" + + var request = RelayConfigrable(tag: 1002, prompt: false) + + var response = RelayConfigrable(tag: 1003, prompt: false) +} + + +struct PairingDeleteProtocolMethod: ProtocolMethod { + var method: String = "wc_pairingDelete" + + var request = RelayConfigrable(tag: 1000, prompt: false) + + var response = RelayConfigrable(tag: 1001, prompt: false) } diff --git a/Sources/Auth/Services/App/AppRequestService.swift b/Sources/Auth/Services/App/AppRequestService.swift index da88c993a..54c492e8a 100644 --- a/Sources/Auth/Services/App/AppRequestService.swift +++ b/Sources/Auth/Services/App/AppRequestService.swift @@ -30,7 +30,7 @@ actor AppRequestService { let request = RPCRequest(method: "wc_authRequest", params: params) try kms.setPublicKey(publicKey: pubKey, for: responseTopic) logger.debug("AppRequestService: Subscribibg for response topic: \(responseTopic)") - try await networkingInteractor.requestNetworkAck(request, topic: topic, tag: AuthProtocolMethod.authRequest.responseTag) + try await networkingInteractor.requestNetworkAck(request, topic: topic, protocolMethod: AuthRequestProtocolMethod()) try await networkingInteractor.subscribe(topic: responseTopic) } } diff --git a/Sources/Auth/Services/App/AppRespondSubscriber.swift b/Sources/Auth/Services/App/AppRespondSubscriber.swift index a0e9f8c40..542d4e27c 100644 --- a/Sources/Auth/Services/App/AppRespondSubscriber.swift +++ b/Sources/Auth/Services/App/AppRespondSubscriber.swift @@ -32,13 +32,13 @@ class AppRespondSubscriber { } private func subscribeForResponse() { - networkingInteractor.responseErrorSubscription(on: AuthProtocolMethod.authRequest) + networkingInteractor.responseErrorSubscription(on: AuthRequestProtocolMethod()) .sink { [unowned self] (payload: ResponseSubscriptionErrorPayload) in guard let error = AuthError(code: payload.error.code) else { return } onResponse?(payload.id, .failure(error)) }.store(in: &publishers) - networkingInteractor.responseSubscription(on: AuthProtocolMethod.authRequest) + networkingInteractor.responseSubscription(on: AuthRequestProtocolMethod()) .sink { [unowned self] (payload: ResponseSubscriptionPayload) in activatePairingIfNeeded(id: payload.id) diff --git a/Sources/Auth/Services/Common/DeletePairingService.swift b/Sources/Auth/Services/Common/DeletePairingService.swift index 4ec44f20e..414118348 100644 --- a/Sources/Auth/Services/Common/DeletePairingService.swift +++ b/Sources/Auth/Services/Common/DeletePairingService.swift @@ -26,10 +26,11 @@ class DeletePairingService { func delete(topic: String) async throws { guard pairingStorage.hasPairing(forTopic: topic) else { throw Errors.pairingNotFound} + let protocolMethod = PairingDeleteProtocolMethod() let reason = AuthError.userDisconnected logger.debug("Will delete pairing for reason: message: \(reason.message) code: \(reason.code)") - let request = RPCRequest(method: AuthProtocolMethod.pairingDelete.rawValue, params: reason) - try await networkingInteractor.request(request, topic: topic, protocolMethod: AuthProtocolMethod.pairingDelete) + let request = RPCRequest(method: protocolMethod.method, params: reason) + try await networkingInteractor.request(request, topic: topic, protocolMethod: protocolMethod) pairingStorage.delete(topic: topic) kms.deleteSymmetricKey(for: topic) networkingInteractor.unsubscribe(topic: topic) diff --git a/Sources/Auth/Services/Wallet/WalletErrorResponder.swift b/Sources/Auth/Services/Wallet/WalletErrorResponder.swift index b774c3865..491cf73ad 100644 --- a/Sources/Auth/Services/Wallet/WalletErrorResponder.swift +++ b/Sources/Auth/Services/Wallet/WalletErrorResponder.swift @@ -33,7 +33,7 @@ actor WalletErrorResponder { try kms.setAgreementSecret(keys, topic: topic) let envelopeType = Envelope.EnvelopeType.type1(pubKey: keys.publicKey.rawRepresentation) - try await networkingInteractor.respondError(topic: topic, requestId: requestId, protocolMethod: AuthProtocolMethod.authRequest, reason: error, envelopeType: envelopeType) + try await networkingInteractor.respondError(topic: topic, requestId: requestId, protocolMethod: AuthRequestProtocolMethod(), reason: error, envelopeType: envelopeType) } private func getAuthRequestParams(requestId: RPCID) throws -> AuthRequestParams { diff --git a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift index c204cc526..05d028fe1 100644 --- a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift +++ b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift @@ -33,7 +33,7 @@ class WalletRequestSubscriber { private func subscribeForRequest() { guard let address = address else { return } - networkingInteractor.requestSubscription(on: AuthProtocolMethod.authRequest) + networkingInteractor.requestSubscription(on: AuthRequestProtocolMethod()) .sink { [unowned self] (payload: RequestSubscriptionPayload) in logger.debug("WalletRequestSubscriber: Received request") guard let message = messageFormatter.formatMessage(from: payload.request.payloadParams, address: address) else { diff --git a/Sources/Auth/Services/Wallet/WalletRespondService.swift b/Sources/Auth/Services/Wallet/WalletRespondService.swift index c9529c197..32090f956 100644 --- a/Sources/Auth/Services/Wallet/WalletRespondService.swift +++ b/Sources/Auth/Services/Wallet/WalletRespondService.swift @@ -39,7 +39,7 @@ actor WalletRespondService { let responseParams = AuthResponseParams(h: header, p: payload, s: signature) let response = RPCResponse(id: requestId, result: responseParams) - try await networkingInteractor.respond(topic: topic, response: response, protocolMethod: AuthProtocolMethod.authRequest, envelopeType: .type1(pubKey: keys.publicKey.rawRepresentation)) + try await networkingInteractor.respond(topic: topic, response: response, protocolMethod: AuthRequestProtocolMethod(), envelopeType: .type1(pubKey: keys.publicKey.rawRepresentation)) } func respondError(requestId: RPCID) async throws { diff --git a/Sources/Chat/ProtocolServices/Common/MessagingService.swift b/Sources/Chat/ProtocolServices/Common/MessagingService.swift index 027afc5d8..ea13faca4 100644 --- a/Sources/Chat/ProtocolServices/Common/MessagingService.swift +++ b/Sources/Chat/ProtocolServices/Common/MessagingService.swift @@ -29,12 +29,13 @@ class MessagingService { func send(topic: String, messageString: String) async throws { // TODO - manage author account + let protocolMethod = ChatMessageProtocolMethod() let thread = await threadStore.first {$0.topic == topic} guard let authorAccount = thread?.selfAccount else { throw Errors.threadDoNotExist} let timestamp = Int64(Date().timeIntervalSince1970 * 1000) let message = Message(topic: topic, message: messageString, authorAccount: authorAccount, timestamp: timestamp) - let request = RPCRequest(method: ChatProtocolMethod.message.method, params: message) - try await networkingInteractor.request(request, topic: topic, protocolMethod: ChatProtocolMethod.message) + let request = RPCRequest(method: protocolMethod.method, params: message) + try await networkingInteractor.request(request, topic: topic, protocolMethod: protocolMethod) Task(priority: .background) { await messagesStore.add(message) onMessage?(message) @@ -42,14 +43,14 @@ class MessagingService { } private func setUpResponseHandling() { - networkingInteractor.responseSubscription(on: ChatProtocolMethod.message) + networkingInteractor.responseSubscription(on: ChatMessageProtocolMethod()) .sink { [unowned self] (payload: ResponseSubscriptionPayload) in logger.debug("Received Message response") }.store(in: &publishers) } private func setUpRequestHandling() { - networkingInteractor.requestSubscription(on: ChatProtocolMethod.message) + networkingInteractor.requestSubscription(on: ChatMessageProtocolMethod()) .sink { [unowned self] (payload: RequestSubscriptionPayload) in var message = payload.request message.topic = payload.topic @@ -59,7 +60,7 @@ class MessagingService { private func handleMessage(_ message: Message, topic: String, requestId: RPCID) { Task(priority: .background) { - try await networkingInteractor.respondSuccess(topic: topic, requestId: requestId, protocolMethod: ChatProtocolMethod.message) + try await networkingInteractor.respondSuccess(topic: topic, requestId: requestId, protocolMethod: ChatMessageProtocolMethod()) await messagesStore.add(message) logger.debug("Received message") onMessage?(message) diff --git a/Sources/Chat/ProtocolServices/Invitee/InvitationHandlingService.swift b/Sources/Chat/ProtocolServices/Invitee/InvitationHandlingService.swift index 1c07311b0..e3e30028c 100644 --- a/Sources/Chat/ProtocolServices/Invitee/InvitationHandlingService.swift +++ b/Sources/Chat/ProtocolServices/Invitee/InvitationHandlingService.swift @@ -39,6 +39,8 @@ class InvitationHandlingService { } func accept(inviteId: String) async throws { + let protocolMethod = ChatInviteProtocolMethod() + guard let payload = try invitePayloadStore.get(key: inviteId) else { throw Error.inviteForIdNotFound } let selfThreadPubKey = try kms.createX25519KeyPair() @@ -47,7 +49,7 @@ class InvitationHandlingService { let response = RPCResponse(id: payload.id, result: inviteResponse) let responseTopic = try getInviteResponseTopic(requestTopic: payload.topic, invite: payload.request) - try await networkingInteractor.respond(topic: responseTopic, response: response, protocolMethod: ChatProtocolMethod.invite) + try await networkingInteractor.respond(topic: responseTopic, response: response, protocolMethod: protocolMethod) let threadAgreementKeys = try kms.performKeyAgreement(selfPublicKey: selfThreadPubKey, peerPublicKey: payload.request.publicKey) let threadTopic = threadAgreementKeys.derivedTopic() @@ -71,13 +73,13 @@ class InvitationHandlingService { let responseTopic = try getInviteResponseTopic(requestTopic: payload.topic, invite: payload.request) - try await networkingInteractor.respondError(topic: responseTopic, requestId: payload.id, protocolMethod: ChatProtocolMethod.invite, reason: ChatError.userRejected) + try await networkingInteractor.respondError(topic: responseTopic, requestId: payload.id, protocolMethod: ChatInviteProtocolMethod(), reason: ChatError.userRejected) invitePayloadStore.delete(forKey: inviteId) } private func setUpRequestHandling() { - networkingInteractor.requestSubscription(on: ChatProtocolMethod.invite) + networkingInteractor.requestSubscription(on: ChatInviteProtocolMethod()) .sink { [unowned self] (payload: RequestSubscriptionPayload) in logger.debug("did receive an invite") invitePayloadStore.set(payload, forKey: payload.request.publicKey) diff --git a/Sources/Chat/ProtocolServices/Inviter/InviteService.swift b/Sources/Chat/ProtocolServices/Inviter/InviteService.swift index 706baddc1..9568bfac4 100644 --- a/Sources/Chat/ProtocolServices/Inviter/InviteService.swift +++ b/Sources/Chat/ProtocolServices/Inviter/InviteService.swift @@ -33,6 +33,7 @@ class InviteService { func invite(peerPubKey: String, peerAccount: Account, openingMessage: String, account: Account) async throws { // TODO ad storage + let protocolMethod = ChatInviteProtocolMethod() self.peerAccount = peerAccount let selfPubKeyY = try kms.createX25519KeyPair() let invite = Invite(message: openingMessage, account: account, publicKey: selfPubKeyY.hexRepresentation) @@ -42,7 +43,7 @@ class InviteService { // overrides on invite toipic try kms.setSymmetricKey(symKeyI.sharedKey, for: inviteTopic) - let request = RPCRequest(method: ChatProtocolMethod.invite.method, params: invite) + let request = RPCRequest(method: protocolMethod.method, params: invite) // 2. Proposer subscribes to topic R which is the hash of the derived symKey let responseTopic = symKeyI.derivedTopic() @@ -50,13 +51,13 @@ class InviteService { try kms.setSymmetricKey(symKeyI.sharedKey, for: responseTopic) try await networkingInteractor.subscribe(topic: responseTopic) - try await networkingInteractor.request(request, topic: inviteTopic, protocolMethod: ChatProtocolMethod.invite, envelopeType: .type1(pubKey: selfPubKeyY.rawRepresentation)) + try await networkingInteractor.request(request, topic: inviteTopic, protocolMethod: protocolMethod, envelopeType: .type1(pubKey: selfPubKeyY.rawRepresentation)) logger.debug("invite sent on topic: \(inviteTopic)") } private func setUpResponseHandling() { - networkingInteractor.responseSubscription(on: ChatProtocolMethod.invite) + networkingInteractor.responseSubscription(on: ChatInviteProtocolMethod()) .sink { [unowned self] (payload: ResponseSubscriptionPayload) in logger.debug("Invite has been accepted") diff --git a/Sources/Chat/Types/ChatProtocolMethod.swift b/Sources/Chat/Types/ChatProtocolMethod.swift index a32e5d4bf..6e5197f60 100644 --- a/Sources/Chat/Types/ChatProtocolMethod.swift +++ b/Sources/Chat/Types/ChatProtocolMethod.swift @@ -1,34 +1,20 @@ import Foundation import WalletConnectNetworking -enum ChatProtocolMethod: ProtocolMethod { - case invite - case message - - var requestTag: Int { - switch self { - case .invite: - return 2000 - case .message: - return 2002 - } - } - - var responseTag: Int { - switch self { - case .invite: - return 2001 - case .message: - return 2003 - } - } - - var method: String { - switch self { - case .invite: - return "wc_chatInvite" - case .message: - return "wc_chatMessage" - } - } +struct ChatInviteProtocolMethod: ProtocolMethod { + var method: String = "wc_chatInvite" + + var request = RelayConfigrable(tag: 2000, prompt: true) + + var response = RelayConfigrable(tag: 2001, prompt: false) + +} + +struct ChatMessageProtocolMethod: ProtocolMethod { + var method: String = "wc_chatMessage" + + var request = RelayConfigrable(tag: 2002, prompt: true) + + var response = RelayConfigrable(tag: 2003, prompt: false) + } diff --git a/Sources/WalletConnectNetworking/NetworkInteracting.swift b/Sources/WalletConnectNetworking/NetworkInteracting.swift index 33d5cea2d..665834735 100644 --- a/Sources/WalletConnectNetworking/NetworkInteracting.swift +++ b/Sources/WalletConnectNetworking/NetworkInteracting.swift @@ -9,7 +9,7 @@ public protocol NetworkInteracting { func subscribe(topic: String) async throws func unsubscribe(topic: String) func request(_ request: RPCRequest, topic: String, protocolMethod: ProtocolMethod, envelopeType: Envelope.EnvelopeType) async throws - func requestNetworkAck(_ request: RPCRequest, topic: String, tag: Int) async throws + func requestNetworkAck(_ request: RPCRequest, topic: String, protocolMethod: ProtocolMethod) async throws func respond(topic: String, response: RPCResponse, protocolMethod: ProtocolMethod, envelopeType: Envelope.EnvelopeType) async throws func respondSuccess(topic: String, requestId: RPCID, protocolMethod: ProtocolMethod, envelopeType: Envelope.EnvelopeType) async throws func respondError(topic: String, requestId: RPCID, protocolMethod: ProtocolMethod, reason: Reason, envelopeType: Envelope.EnvelopeType) async throws diff --git a/Sources/WalletConnectNetworking/NetworkInteractor.swift b/Sources/WalletConnectNetworking/NetworkInteractor.swift index a781b2049..339c87656 100644 --- a/Sources/WalletConnectNetworking/NetworkInteractor.swift +++ b/Sources/WalletConnectNetworking/NetworkInteractor.swift @@ -96,18 +96,18 @@ public class NetworkingInteractor: NetworkInteracting { public func request(_ request: RPCRequest, topic: String, protocolMethod: ProtocolMethod, envelopeType: Envelope.EnvelopeType) async throws { try rpcHistory.set(request, forTopic: topic, emmitedBy: .local) let message = try! serializer.serialize(topic: topic, encodable: request, envelopeType: envelopeType) - try await relayClient.publish(topic: topic, payload: message, tag: protocolMethod.requestTag, prompt: shouldPrompt(method: request.method)) + try await relayClient.publish(topic: topic, payload: message, tag: protocolMethod.request.tag, prompt: protocolMethod.request.prompt) } /// Completes with an acknowledgement from the relay network. /// completes with error if networking client was not able to send a message /// TODO - relay client should provide async function - continualion should be removed from here - public func requestNetworkAck(_ request: RPCRequest, topic: String, tag: Int) async throws { + public func requestNetworkAck(_ request: RPCRequest, topic: String, protocolMethod: ProtocolMethod) async throws { do { try rpcHistory.set(request, forTopic: topic, emmitedBy: .local) let message = try serializer.serialize(topic: topic, encodable: request) return try await withCheckedThrowingContinuation { continuation in - relayClient.publish(topic: topic, payload: message, tag: tag, prompt: shouldPrompt(method: request.method)) { error in + relayClient.publish(topic: topic, payload: message, tag: protocolMethod.request.tag, prompt: protocolMethod.request.prompt) { error in if let error = error { continuation.resume(throwing: error) } else { @@ -123,7 +123,7 @@ public class NetworkingInteractor: NetworkInteracting { public func respond(topic: String, response: RPCResponse, protocolMethod: ProtocolMethod, envelopeType: Envelope.EnvelopeType) async throws { try rpcHistory.resolve(response) let message = try! serializer.serialize(topic: topic, encodable: response, envelopeType: envelopeType) - try await relayClient.publish(topic: topic, payload: message, tag: protocolMethod.responseTag) + try await relayClient.publish(topic: topic, payload: message, tag: protocolMethod.response.tag) } public func respondSuccess(topic: String, requestId: RPCID, protocolMethod: ProtocolMethod, envelopeType: Envelope.EnvelopeType) async throws { @@ -165,13 +165,4 @@ public class NetworkingInteractor: NetworkInteracting { logger.debug("Handle json rpc response error: \(error)") } } - - private func shouldPrompt(method: String) -> Bool { - switch method { - case "wc_sessionRequest": // TODO: Include promt in ProtocolMethod - return true - default: - return false - } - } } diff --git a/Sources/WalletConnectNetworking/ProtocolMethod.swift b/Sources/WalletConnectNetworking/ProtocolMethod.swift index dea8bd255..10a8c5e7b 100644 --- a/Sources/WalletConnectNetworking/ProtocolMethod.swift +++ b/Sources/WalletConnectNetworking/ProtocolMethod.swift @@ -2,6 +2,16 @@ import Foundation public protocol ProtocolMethod { var method: String { get } - var requestTag: Int { get } - var responseTag: Int { get } + var request: RelayConfigrable { get } + var response: RelayConfigrable { get } +} + +public struct RelayConfigrable { + var tag: Int + var prompt: Bool + + public init(tag: Int, prompt: Bool) { + self.tag = tag + self.prompt = prompt + } } diff --git a/Sources/WalletConnectPairing/PairingProtocolMethod.swift b/Sources/WalletConnectPairing/PairingProtocolMethod.swift index a6f9d14cf..640517ff6 100644 --- a/Sources/WalletConnectPairing/PairingProtocolMethod.swift +++ b/Sources/WalletConnectPairing/PairingProtocolMethod.swift @@ -1,18 +1,10 @@ import Foundation import WalletConnectNetworking -enum PairingProtocolMethod: String, ProtocolMethod { - case ping = "wc_pairingPing" +struct PairingPingProtocolMethod: ProtocolMethod { + var method: String = "wc_pairingPing" - var method: String { - return self.rawValue - } + var request = RelayConfigrable(tag: 1002, prompt: false) - var requestTag: Int { - return 1002 - } - - var responseTag: Int { - return 1003 - } + var response = RelayConfigrable(tag: 1003, prompt: false) } diff --git a/Sources/WalletConnectPairing/Services/PairingPingService.swift b/Sources/WalletConnectPairing/Services/PairingPingService.swift index f435ec72b..053461717 100644 --- a/Sources/WalletConnectPairing/Services/PairingPingService.swift +++ b/Sources/WalletConnectPairing/Services/PairingPingService.swift @@ -21,10 +21,11 @@ public class PairingPingService { pairingStorage: WCPairingStorage, networkingInteractor: NetworkInteracting, logger: ConsoleLogging) { + let protocolMethod = PairingPingProtocolMethod() self.pairingStorage = pairingStorage - self.pingRequester = PingRequester(networkingInteractor: networkingInteractor, method: PairingProtocolMethod.ping) - self.pingResponder = PingResponder(networkingInteractor: networkingInteractor, method: PairingProtocolMethod.ping, logger: logger) - self.pingResponseSubscriber = PingResponseSubscriber(networkingInteractor: networkingInteractor, method: PairingProtocolMethod.ping, logger: logger) + self.pingRequester = PingRequester(networkingInteractor: networkingInteractor, method: protocolMethod) + self.pingResponder = PingResponder(networkingInteractor: networkingInteractor, method: protocolMethod, logger: logger) + self.pingResponseSubscriber = PingResponseSubscriber(networkingInteractor: networkingInteractor, method: protocolMethod, logger: logger) } public func ping(topic: String) async throws { diff --git a/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift b/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift index 6a9b7cae3..56a572022 100644 --- a/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift @@ -73,7 +73,7 @@ final class PairingEngine { requiredNamespaces: namespaces) let request = RPCRequest(method: SignProtocolMethod.sessionPropose.method, params: proposal) - try await networkingInteractor.requestNetworkAck(request, topic: pairingTopic, tag: SignProtocolMethod.sessionPropose.requestTag) + try await networkingInteractor.requestNetworkAck(request, topic: pairingTopic, protocolMethod: SignProtocolMethod.sessionPropose) } } diff --git a/Tests/TestingUtils/NetworkingInteractorMock.swift b/Tests/TestingUtils/NetworkingInteractorMock.swift index 6f7fff08a..b3e6714bb 100644 --- a/Tests/TestingUtils/NetworkingInteractorMock.swift +++ b/Tests/TestingUtils/NetworkingInteractorMock.swift @@ -114,7 +114,7 @@ public class NetworkingInteractorMock: NetworkInteracting { didRespondError = true } - public func requestNetworkAck(_ request: RPCRequest, topic: String, tag: Int) async throws { + public func requestNetworkAck(_ request: RPCRequest, topic: String, protocolMethod: ProtocolMethod) async throws { requestCallCount += 1 requests.append((topic, request)) } From 5f0601f036e1e30e40cf7a5ae84bdfe391b9e661 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 26 Sep 2022 12:19:36 +0200 Subject: [PATCH 059/175] refactor sign protocol methods --- Sources/Auth/AuthProtocolMethod.swift | 12 +- Sources/Chat/Types/ChatProtocolMethod.swift | 8 +- .../ProtocolMethod.swift | 6 +- .../PairingProtocolMethod.swift | 4 +- .../Types/SignProtocolMethod.swift | 145 ++++++++++-------- 5 files changed, 95 insertions(+), 80 deletions(-) diff --git a/Sources/Auth/AuthProtocolMethod.swift b/Sources/Auth/AuthProtocolMethod.swift index c15cc76ef..4de009c42 100644 --- a/Sources/Auth/AuthProtocolMethod.swift +++ b/Sources/Auth/AuthProtocolMethod.swift @@ -4,25 +4,25 @@ import WalletConnectNetworking struct AuthRequestProtocolMethod: ProtocolMethod { var method: String = "wc_authRequest" - var request = RelayConfigrable(tag: 3000, prompt: true) + var request = RelayConfig(tag: 3000, prompt: true) - var response = RelayConfigrable(tag: 3001, prompt: false) + var response = RelayConfig(tag: 3001, prompt: false) } struct PairingPingProtocolMethod: ProtocolMethod { var method: String = "wc_pairingPing" - var request = RelayConfigrable(tag: 1002, prompt: false) + var request = RelayConfig(tag: 1002, prompt: false) - var response = RelayConfigrable(tag: 1003, prompt: false) + var response = RelayConfig(tag: 1003, prompt: false) } struct PairingDeleteProtocolMethod: ProtocolMethod { var method: String = "wc_pairingDelete" - var request = RelayConfigrable(tag: 1000, prompt: false) + var request = RelayConfig(tag: 1000, prompt: false) - var response = RelayConfigrable(tag: 1001, prompt: false) + var response = RelayConfig(tag: 1001, prompt: false) } diff --git a/Sources/Chat/Types/ChatProtocolMethod.swift b/Sources/Chat/Types/ChatProtocolMethod.swift index 6e5197f60..c6ca16dfe 100644 --- a/Sources/Chat/Types/ChatProtocolMethod.swift +++ b/Sources/Chat/Types/ChatProtocolMethod.swift @@ -4,17 +4,17 @@ import WalletConnectNetworking struct ChatInviteProtocolMethod: ProtocolMethod { var method: String = "wc_chatInvite" - var request = RelayConfigrable(tag: 2000, prompt: true) + var request = RelayConfig(tag: 2000, prompt: true) - var response = RelayConfigrable(tag: 2001, prompt: false) + var response = RelayConfig(tag: 2001, prompt: false) } struct ChatMessageProtocolMethod: ProtocolMethod { var method: String = "wc_chatMessage" - var request = RelayConfigrable(tag: 2002, prompt: true) + var request = RelayConfig(tag: 2002, prompt: true) - var response = RelayConfigrable(tag: 2003, prompt: false) + var response = RelayConfig(tag: 2003, prompt: false) } diff --git a/Sources/WalletConnectNetworking/ProtocolMethod.swift b/Sources/WalletConnectNetworking/ProtocolMethod.swift index 10a8c5e7b..21c8ad1b5 100644 --- a/Sources/WalletConnectNetworking/ProtocolMethod.swift +++ b/Sources/WalletConnectNetworking/ProtocolMethod.swift @@ -2,11 +2,11 @@ import Foundation public protocol ProtocolMethod { var method: String { get } - var request: RelayConfigrable { get } - var response: RelayConfigrable { get } + var request: RelayConfig { get } + var response: RelayConfig { get } } -public struct RelayConfigrable { +public struct RelayConfig { var tag: Int var prompt: Bool diff --git a/Sources/WalletConnectPairing/PairingProtocolMethod.swift b/Sources/WalletConnectPairing/PairingProtocolMethod.swift index 640517ff6..b4231dade 100644 --- a/Sources/WalletConnectPairing/PairingProtocolMethod.swift +++ b/Sources/WalletConnectPairing/PairingProtocolMethod.swift @@ -4,7 +4,7 @@ import WalletConnectNetworking struct PairingPingProtocolMethod: ProtocolMethod { var method: String = "wc_pairingPing" - var request = RelayConfigrable(tag: 1002, prompt: false) + var request = RelayConfig(tag: 1002, prompt: false) - var response = RelayConfigrable(tag: 1003, prompt: false) + var response = RelayConfig(tag: 1003, prompt: false) } diff --git a/Sources/WalletConnectSign/Types/SignProtocolMethod.swift b/Sources/WalletConnectSign/Types/SignProtocolMethod.swift index 90d4a0c54..87f727441 100644 --- a/Sources/WalletConnectSign/Types/SignProtocolMethod.swift +++ b/Sources/WalletConnectSign/Types/SignProtocolMethod.swift @@ -4,69 +4,84 @@ import WalletConnectPairing import WalletConnectUtils import WalletConnectNetworking -enum SignProtocolMethod: ProtocolMethod { - case pairingDelete - case pairingPing - case sessionPropose - case sessionSettle - case sessionUpdate - case sessionExtend - case sessionDelete - case sessionRequest - case sessionPing - case sessionEvent - - var method: String { - switch self { - case .pairingDelete: - return "wc_pairingDelete" - case .pairingPing: - return "wc_pairingPing" - case .sessionPropose: - return "wc_sessionPropose" - case .sessionSettle: - return "wc_sessionSettle" - case .sessionUpdate: - return "wc_sessionUpdate" - case .sessionExtend: - return "wc_sessionExtend" - case .sessionDelete: - return "wc_sessionDelete" - case .sessionRequest: - return "wc_sessionRequest" - case .sessionPing: - return "wc_sessionPing" - case .sessionEvent: - return "wc_sessionEvent" - } - } - - var requestTag: Int { - switch self { - case .pairingDelete: - return 1000 - case .pairingPing: - return 1002 - case .sessionPropose: - return 1100 - case .sessionSettle: - return 1102 - case .sessionUpdate: - return 1104 - case .sessionExtend: - return 1106 - case .sessionDelete: - return 1112 - case .sessionRequest: - return 1108 - case .sessionPing: - return 1114 - case .sessionEvent: - return 1110 - } - } - - var responseTag: Int { - return requestTag + 1 - } +struct PairingPingProtocolMethod: ProtocolMethod { + var method: String = "wc_pairingPing" + + var request = RelayConfig(tag: 1002, prompt: false) + + var response = RelayConfig(tag: 1003, prompt: false) +} + +struct PairingDeleteProtocolMethod: ProtocolMethod { + var method: String = "wc_pairingDelete" + + var request = RelayConfig(tag: 1000, prompt: false) + + var response = RelayConfig(tag: 1001, prompt: false) +} + + + + +struct SessionProposeProtocolMethod: ProtocolMethod { + var method: String = "wc_sessionPropose" + + var request = RelayConfig(tag: 1100, prompt: true) + + var response = RelayConfig(tag: 1101, prompt: false) +} + +struct SessionSettleProtocolMethod: ProtocolMethod { + var method: String = "wc_sessionSettle" + + var request = RelayConfig(tag: 1102, prompt: false) + + var response = RelayConfig(tag: 1103, prompt: false) +} + +struct SessionUpdateProtocolMethod: ProtocolMethod { + var method: String = "wc_sessionUpdate" + + var request = RelayConfig(tag: 1104, prompt: false) + + var response = RelayConfig(tag: 1105, prompt: false) +} + +struct SessionExtendProtocolMethod: ProtocolMethod { + var method: String = "wc_sessionExtend" + + var request = RelayConfig(tag: 1106, prompt: false) + + var response = RelayConfig(tag: 1107, prompt: false) +} + +struct SessionDeleteProtocolMethod: ProtocolMethod { + var method: String = "wc_sessionDelete" + + var request = RelayConfig(tag: 1112, prompt: false) + + var response = RelayConfig(tag: 1113, prompt: false) +} + + +struct SessionRequestProtocolMethod: ProtocolMethod { + var method: String = "wc_sessionRequest" + + var request = RelayConfig(tag: 1108, prompt: true) + + var response = RelayConfig(tag: 1109, prompt: false) +} +struct SessionPingProtocolMethod: ProtocolMethod { + var method: String = "wc_sessionPing" + + var request = RelayConfig(tag: 1114, prompt: false) + + var response = RelayConfig(tag: 1115, prompt: false) +} +struct SessionEventProtocolMethod: ProtocolMethod { + var method: String = "wc_sessionEvent" + + var request = RelayConfig(tag: 1110, prompt: true) + + var response = RelayConfig(tag: 1111, prompt: false) } From 4a91d0cd57136bed1da7e72c8dd781008b424dac Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 26 Sep 2022 12:33:30 +0200 Subject: [PATCH 060/175] refactor sign protocol methods --- .../Engine/Common/ApproveEngine.swift | 25 ++++++++-------- .../Engine/Common/DeletePairingService.swift | 5 ++-- .../Engine/Common/DeleteSessionService.swift | 5 ++-- .../Engine/Common/PairingEngine.swift | 10 ++++--- .../Engine/Common/SessionEngine.swift | 29 ++++++++++--------- .../ControllerSessionStateMachine.swift | 14 +++++---- .../NonControllerSessionStateMachine.swift | 10 +++---- .../Services/SessionPingService.swift | 7 +++-- .../SignProtocolMethod.swift | 8 ++--- 9 files changed, 59 insertions(+), 54 deletions(-) rename Sources/WalletConnectSign/Types/{ => ProtocolMethods}/SignProtocolMethod.swift (96%) diff --git a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift index 126ea595b..7dbc9f476 100644 --- a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift @@ -89,7 +89,7 @@ final class ApproveEngine { let result = SessionType.ProposeResponse(relay: relay, responderPublicKey: selfPublicKey.hexRepresentation) let response = RPCResponse(id: payload.id, result: result) - try await networkingInteractor.respond(topic: payload.topic, response: response, protocolMethod: SignProtocolMethod.sessionPropose) + try await networkingInteractor.respond(topic: payload.topic, response: response, protocolMethod: SessionProposeProtocolMethod()) try pairing.updateExpiry() pairingStore.setPairing(pairing) @@ -102,7 +102,7 @@ final class ApproveEngine { throw Errors.proposalPayloadsNotFound } proposalPayloadsStore.delete(forKey: proposerPubKey) - try await networkingInteractor.respondError(topic: payload.topic, requestId: payload.id, protocolMethod: SignProtocolMethod.sessionPropose, reason: reason) + try await networkingInteractor.respondError(topic: payload.topic, requestId: payload.id, protocolMethod: SessionProposeProtocolMethod(), reason: reason) // TODO: Delete pairing if inactive } @@ -143,8 +143,9 @@ final class ApproveEngine { try await networkingInteractor.subscribe(topic: topic) sessionStore.setSession(session) - let request = RPCRequest(method: SignProtocolMethod.sessionSettle.method, params: settleParams) - try await networkingInteractor.request(request, topic: topic, protocolMethod: SignProtocolMethod.sessionSettle) + let protocolMethod = SessionSettleProtocolMethod() + let request = RPCRequest(method: protocolMethod.method, params: settleParams) + try await networkingInteractor.request(request, topic: topic, protocolMethod: protocolMethod) onSessionSettle?(session.publicRepresentation()) } } @@ -154,36 +155,36 @@ final class ApproveEngine { private extension ApproveEngine { func setupRequestSubscriptions() { - networkingInteractor.requestSubscription(on: SignProtocolMethod.sessionPropose) + networkingInteractor.requestSubscription(on: SessionProposeProtocolMethod()) .sink { [unowned self] (payload: RequestSubscriptionPayload) in handleSessionProposeRequest(payload: payload) }.store(in: &publishers) - networkingInteractor.requestSubscription(on: SignProtocolMethod.sessionSettle) + networkingInteractor.requestSubscription(on: SessionSettleProtocolMethod()) .sink { [unowned self] (payload: RequestSubscriptionPayload) in handleSessionSettleRequest(payload: payload) }.store(in: &publishers) } func setupResponseSubscriptions() { - networkingInteractor.responseSubscription(on: SignProtocolMethod.sessionPropose) + networkingInteractor.responseSubscription(on: SessionProposeProtocolMethod()) .sink { [unowned self] (payload: ResponseSubscriptionPayload) in handleSessionProposeResponse(payload: payload) }.store(in: &publishers) - networkingInteractor.responseSubscription(on: SignProtocolMethod.sessionSettle) + networkingInteractor.responseSubscription(on: SessionSettleProtocolMethod()) .sink { [unowned self] (payload: ResponseSubscriptionPayload) in handleSessionSettleResponse(payload: payload) }.store(in: &publishers) } func setupResponseErrorSubscriptions() { - networkingInteractor.responseErrorSubscription(on: SignProtocolMethod.sessionPropose) + networkingInteractor.responseErrorSubscription(on: SessionProposeProtocolMethod()) .sink { [unowned self] (payload: ResponseSubscriptionErrorPayload) in handleSessionProposeResponseError(payload: payload) }.store(in: &publishers) - networkingInteractor.responseErrorSubscription(on: SignProtocolMethod.sessionSettle) + networkingInteractor.responseErrorSubscription(on: SessionSettleProtocolMethod()) .sink { [unowned self] (payload: ResponseSubscriptionErrorPayload) in handleSessionSettleResponseError(payload: payload) }.store(in: &publishers) @@ -292,7 +293,7 @@ private extension ApproveEngine { logger.debug("Received Session Proposal") let proposal = payload.request do { try Namespace.validate(proposal.requiredNamespaces) } catch { - return respondError(payload: payload, reason: .invalidUpdateRequest, protocolMethod: SignProtocolMethod.sessionPropose) + return respondError(payload: payload, reason: .invalidUpdateRequest, protocolMethod: SessionProposeProtocolMethod()) } proposalPayloadsStore.set(payload, forKey: proposal.proposer.publicKey) onSessionProposal?(proposal.publicRepresentation()) @@ -303,7 +304,7 @@ private extension ApproveEngine { func handleSessionSettleRequest(payload: RequestSubscriptionPayload) { logger.debug("Did receive session settle request") - let protocolMethod = SignProtocolMethod.sessionSettle + let protocolMethod = SessionSettleProtocolMethod() guard let proposedNamespaces = settlingProposal?.requiredNamespaces else { return respondError(payload: payload, reason: .invalidUpdateRequest, protocolMethod: protocolMethod) diff --git a/Sources/WalletConnectSign/Engine/Common/DeletePairingService.swift b/Sources/WalletConnectSign/Engine/Common/DeletePairingService.swift index 1e6aa3d19..8928013b5 100644 --- a/Sources/WalletConnectSign/Engine/Common/DeletePairingService.swift +++ b/Sources/WalletConnectSign/Engine/Common/DeletePairingService.swift @@ -23,10 +23,11 @@ class DeletePairingService { func delete(topic: String) async throws { let reasonCode = ReasonCode.userDisconnected + let protocolMethod = PairingDeleteProtocolMethod() let reason = SessionType.Reason(code: reasonCode.code, message: reasonCode.message) logger.debug("Will delete pairing for reason: message: \(reason.message) code: \(reason.code)") - let request = RPCRequest(method: SignProtocolMethod.sessionDelete.method, params: reason) - try await networkingInteractor.request(request, topic: topic, protocolMethod: SignProtocolMethod.sessionDelete) + let request = RPCRequest(method: protocolMethod.method, params: reason) + try await networkingInteractor.request(request, topic: topic, protocolMethod: protocolMethod) pairingStorage.delete(topic: topic) kms.deleteSymmetricKey(for: topic) networkingInteractor.unsubscribe(topic: topic) diff --git a/Sources/WalletConnectSign/Engine/Common/DeleteSessionService.swift b/Sources/WalletConnectSign/Engine/Common/DeleteSessionService.swift index eda023715..be2be4c42 100644 --- a/Sources/WalletConnectSign/Engine/Common/DeleteSessionService.swift +++ b/Sources/WalletConnectSign/Engine/Common/DeleteSessionService.swift @@ -22,10 +22,11 @@ class DeleteSessionService { func delete(topic: String) async throws { let reasonCode = ReasonCode.userDisconnected + let protocolMethod = SessionDeleteProtocolMethod() let reason = SessionType.Reason(code: reasonCode.code, message: reasonCode.message) logger.debug("Will delete session for reason: message: \(reason.message) code: \(reason.code)") - let request = RPCRequest(method: SignProtocolMethod.sessionDelete.method, params: reason) - try await networkingInteractor.request(request, topic: topic, protocolMethod: SignProtocolMethod.sessionDelete) + let request = RPCRequest(method: protocolMethod.method, params: reason) + try await networkingInteractor.request(request, topic: topic, protocolMethod: protocolMethod) sessionStore.delete(topic: topic) kms.deleteSymmetricKey(for: topic) networkingInteractor.unsubscribe(topic: topic) diff --git a/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift b/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift index 56a572022..deee4888b 100644 --- a/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift @@ -63,6 +63,7 @@ final class PairingEngine { func propose(pairingTopic: String, namespaces: [String: ProposalNamespace], relay: RelayProtocolOptions) async throws { logger.debug("Propose Session on topic: \(pairingTopic)") try Namespace.validate(namespaces) + let protocolMethod = SessionProposeProtocolMethod() let publicKey = try! kms.createX25519KeyPair() let proposer = Participant( publicKey: publicKey.hexRepresentation, @@ -72,8 +73,8 @@ final class PairingEngine { proposer: proposer, requiredNamespaces: namespaces) - let request = RPCRequest(method: SignProtocolMethod.sessionPropose.method, params: proposal) - try await networkingInteractor.requestNetworkAck(request, topic: pairingTopic, protocolMethod: SignProtocolMethod.sessionPropose) + let request = RPCRequest(method: protocolMethod.method, params: proposal) + try await networkingInteractor.requestNetworkAck(request, topic: pairingTopic, protocolMethod: protocolMethod) } } @@ -90,11 +91,12 @@ private extension PairingEngine { } } .store(in: &publishers) + let protocolMethod = PairingPingProtocolMethod() - networkingInteractor.requestSubscription(on: SignProtocolMethod.pairingPing) + networkingInteractor.requestSubscription(on: protocolMethod) .sink { [unowned self] (payload: RequestSubscriptionPayload) in Task(priority: .high) { - try await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, protocolMethod: SignProtocolMethod.pairingPing) + try await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, protocolMethod: protocolMethod) } } .store(in: &publishers) diff --git a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift index ec8c1911c..2ca824ff1 100644 --- a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift @@ -59,8 +59,8 @@ final class SessionEngine { let chainRequest = SessionType.RequestParams.Request(method: request.method, params: request.params) let sessionRequestParams = SessionType.RequestParams(request: chainRequest, chainId: request.chainId) - let rpcRequest = RPCRequest(method: SignProtocolMethod.sessionRequest.method, params: sessionRequestParams) - try await networkingInteractor.request(rpcRequest, topic: request.topic, protocolMethod: SignProtocolMethod.sessionRequest) + let rpcRequest = RPCRequest(method: SessionRequestProtocolMethod().method, params: sessionRequestParams) + try await networkingInteractor.request(rpcRequest, topic: request.topic, protocolMethod: SessionRequestProtocolMethod()) } func respondSessionRequest(topic: String, requestId: RPCID, response: RPCResult) async throws { @@ -68,10 +68,11 @@ final class SessionEngine { throw Errors.sessionNotFound(topic: topic) } let response = RPCResponse(id: requestId, result: response) - try await networkingInteractor.respond(topic: topic, response: response, protocolMethod: SignProtocolMethod.sessionRequest) + try await networkingInteractor.respond(topic: topic, response: response, protocolMethod: SessionRequestProtocolMethod()) } func emit(topic: String, event: SessionType.EventParams.Event, chainId: Blockchain) async throws { + let protocolMethod = SessionEventProtocolMethod() guard let session = sessionStore.getSession(forTopic: topic) else { logger.debug("Could not find session for topic \(topic)") return @@ -79,8 +80,8 @@ final class SessionEngine { guard session.hasPermission(forEvent: event.name, onChain: chainId) else { throw WalletConnectError.invalidEvent } - let rpcRequest = RPCRequest(method: SignProtocolMethod.sessionEvent.method, params: SessionType.EventParams(event: event, chainId: chainId)) - try await networkingInteractor.request(rpcRequest, topic: topic, protocolMethod: SignProtocolMethod.sessionEvent) + let rpcRequest = RPCRequest(method: protocolMethod.method, params: SessionType.EventParams(event: event, chainId: chainId)) + try await networkingInteractor.request(rpcRequest, topic: topic, protocolMethod: protocolMethod) } } @@ -100,29 +101,29 @@ private extension SessionEngine { } func setupRequestSubscriptions() { - networkingInteractor.requestSubscription(on: SignProtocolMethod.sessionDelete) + networkingInteractor.requestSubscription(on: SessionDeleteProtocolMethod()) .sink { [unowned self] (payload: RequestSubscriptionPayload) in onSessionDelete(payload: payload) }.store(in: &publishers) - networkingInteractor.requestSubscription(on: SignProtocolMethod.sessionRequest) + networkingInteractor.requestSubscription(on: SessionRequestProtocolMethod()) .sink { [unowned self] (payload: RequestSubscriptionPayload) in onSessionRequest(payload: payload) }.store(in: &publishers) - networkingInteractor.requestSubscription(on: SignProtocolMethod.sessionPing) + networkingInteractor.requestSubscription(on: SessionPingProtocolMethod()) .sink { [unowned self] (payload: RequestSubscriptionPayload) in onSessionPing(payload: payload) }.store(in: &publishers) - networkingInteractor.requestSubscription(on: SignProtocolMethod.sessionEvent) + networkingInteractor.requestSubscription(on: SessionEventProtocolMethod()) .sink { [unowned self] (payload: RequestSubscriptionPayload) in onSessionEvent(payload: payload) }.store(in: &publishers) } func setupResponseSubscriptions() { - networkingInteractor.responseSubscription(on: SignProtocolMethod.sessionRequest) + networkingInteractor.responseSubscription(on: SessionRequestProtocolMethod()) .sink { [unowned self] (payload: ResponseSubscriptionPayload) in onSessionResponse?(Response( id: payload.id, @@ -152,7 +153,7 @@ private extension SessionEngine { } func onSessionDelete(payload: RequestSubscriptionPayload) { - let protocolMethod = SignProtocolMethod.sessionDelete + let protocolMethod = SessionDeleteProtocolMethod() let topic = payload.topic guard sessionStore.hasSession(forTopic: topic) else { return respondError(payload: payload, reason: .noSessionForTopic, protocolMethod: protocolMethod) @@ -166,7 +167,7 @@ private extension SessionEngine { } func onSessionRequest(payload: RequestSubscriptionPayload) { - let protocolMethod = SignProtocolMethod.sessionRequest + let protocolMethod = SessionRequestProtocolMethod() let topic = payload.topic let request = Request( id: payload.id, @@ -189,12 +190,12 @@ private extension SessionEngine { func onSessionPing(payload: SubscriptionPayload) { Task(priority: .high) { - try await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, protocolMethod: SignProtocolMethod.sessionPing) + try await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, protocolMethod: SessionPingProtocolMethod()) } } func onSessionEvent(payload: RequestSubscriptionPayload) { - let protocolMethod = SignProtocolMethod.sessionEvent + let protocolMethod = SessionEventProtocolMethod() let event = payload.request.event let topic = payload.topic guard let session = sessionStore.getSession(forTopic: topic) else { diff --git a/Sources/WalletConnectSign/Engine/Controller/ControllerSessionStateMachine.swift b/Sources/WalletConnectSign/Engine/Controller/ControllerSessionStateMachine.swift index c7755e08e..748ae7713 100644 --- a/Sources/WalletConnectSign/Engine/Controller/ControllerSessionStateMachine.swift +++ b/Sources/WalletConnectSign/Engine/Controller/ControllerSessionStateMachine.swift @@ -30,34 +30,36 @@ final class ControllerSessionStateMachine { func update(topic: String, namespaces: [String: SessionNamespace]) async throws { let session = try getSession(for: topic) + let protocolMethod = SessionUpdateProtocolMethod() try validateController(session) try Namespace.validate(namespaces) logger.debug("Controller will update methods") sessionStore.setSession(session) - let request = RPCRequest(method: SignProtocolMethod.sessionUpdate.method, params: SessionType.UpdateParams(namespaces: namespaces)) - try await networkingInteractor.request(request, topic: topic, protocolMethod: SignProtocolMethod.sessionUpdate) + let request = RPCRequest(method: protocolMethod.method, params: SessionType.UpdateParams(namespaces: namespaces)) + try await networkingInteractor.request(request, topic: topic, protocolMethod: protocolMethod) } func extend(topic: String, by ttl: Int64) async throws { var session = try getSession(for: topic) + let protocolMethod = SessionExtendProtocolMethod() try validateController(session) try session.updateExpiry(by: ttl) let newExpiry = Int64(session.expiryDate.timeIntervalSince1970 ) sessionStore.setSession(session) - let request = RPCRequest(method: SignProtocolMethod.sessionExtend.method, params: SessionType.UpdateExpiryParams(expiry: newExpiry)) - try await networkingInteractor.request(request, topic: topic, protocolMethod: SignProtocolMethod.sessionExtend) + let request = RPCRequest(method: protocolMethod.method, params: SessionType.UpdateExpiryParams(expiry: newExpiry)) + try await networkingInteractor.request(request, topic: topic, protocolMethod: protocolMethod) } // MARK: - Handle Response private func setupSubscriptions() { - networkingInteractor.responseSubscription(on: SignProtocolMethod.sessionUpdate) + networkingInteractor.responseSubscription(on: SessionUpdateProtocolMethod()) .sink { [unowned self] (payload: ResponseSubscriptionPayload) in handleUpdateResponse(payload: payload) } .store(in: &publishers) - networkingInteractor.responseSubscription(on: SignProtocolMethod.sessionExtend) + networkingInteractor.responseSubscription(on: SessionExtendProtocolMethod()) .sink { [unowned self] (payload: ResponseSubscriptionPayload) in handleUpdateExpiryResponse(payload: payload) } diff --git a/Sources/WalletConnectSign/Engine/NonController/NonControllerSessionStateMachine.swift b/Sources/WalletConnectSign/Engine/NonController/NonControllerSessionStateMachine.swift index a2af79032..dbde21388 100644 --- a/Sources/WalletConnectSign/Engine/NonController/NonControllerSessionStateMachine.swift +++ b/Sources/WalletConnectSign/Engine/NonController/NonControllerSessionStateMachine.swift @@ -27,12 +27,12 @@ final class NonControllerSessionStateMachine { } private func setupSubscriptions() { - networkingInteractor.requestSubscription(on: SignProtocolMethod.sessionUpdate) + networkingInteractor.requestSubscription(on: SessionUpdateProtocolMethod()) .sink { [unowned self] (payload: RequestSubscriptionPayload) in onSessionUpdateNamespacesRequest(payload: payload, updateParams: payload.request) }.store(in: &publishers) - networkingInteractor.requestSubscription(on: SignProtocolMethod.sessionExtend) + networkingInteractor.requestSubscription(on: SessionExtendProtocolMethod()) .sink { [unowned self] (payload: RequestSubscriptionPayload) in onSessionUpdateExpiry(payload: payload, updateExpiryParams: payload.request) }.store(in: &publishers) @@ -50,7 +50,7 @@ final class NonControllerSessionStateMachine { // TODO: Update stored session namespaces private func onSessionUpdateNamespacesRequest(payload: SubscriptionPayload, updateParams: SessionType.UpdateParams) { - let protocolMethod = SignProtocolMethod.sessionUpdate + let protocolMethod = SessionUpdateProtocolMethod() do { try Namespace.validate(updateParams.namespaces) } catch { @@ -77,7 +77,7 @@ final class NonControllerSessionStateMachine { } private func onSessionUpdateExpiry(payload: SubscriptionPayload, updateExpiryParams: SessionType.UpdateExpiryParams) { - let protocolMethod = SignProtocolMethod.sessionExtend + let protocolMethod = SessionExtendProtocolMethod() let topic = payload.topic guard var session = sessionStore.getSession(forTopic: topic) else { return respondError(payload: payload, reason: .noSessionForTopic, protocolMethod: protocolMethod) @@ -93,7 +93,7 @@ final class NonControllerSessionStateMachine { sessionStore.setSession(session) Task(priority: .high) { - try await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, protocolMethod: SignProtocolMethod.sessionExtend) + try await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, protocolMethod: protocolMethod) } onExtend?(session.topic, session.expiryDate) diff --git a/Sources/WalletConnectSign/Services/SessionPingService.swift b/Sources/WalletConnectSign/Services/SessionPingService.swift index 697dcab89..1d10e2105 100644 --- a/Sources/WalletConnectSign/Services/SessionPingService.swift +++ b/Sources/WalletConnectSign/Services/SessionPingService.swift @@ -22,10 +22,11 @@ class SessionPingService { sessionStorage: WCSessionStorage, networkingInteractor: NetworkInteracting, logger: ConsoleLogging) { + let protocolMethod = SessionPingProtocolMethod() self.sessionStorage = sessionStorage - self.pingRequester = PingRequester(networkingInteractor: networkingInteractor, method: SignProtocolMethod.sessionPing) - self.pingResponder = PingResponder(networkingInteractor: networkingInteractor, method: SignProtocolMethod.sessionPing, logger: logger) - self.pingResponseSubscriber = PingResponseSubscriber(networkingInteractor: networkingInteractor, method: SignProtocolMethod.sessionPing, logger: logger) + self.pingRequester = PingRequester(networkingInteractor: networkingInteractor, method: protocolMethod) + self.pingResponder = PingResponder(networkingInteractor: networkingInteractor, method: protocolMethod, logger: logger) + self.pingResponseSubscriber = PingResponseSubscriber(networkingInteractor: networkingInteractor, method: protocolMethod, logger: logger) } func ping(topic: String) async throws { diff --git a/Sources/WalletConnectSign/Types/SignProtocolMethod.swift b/Sources/WalletConnectSign/Types/ProtocolMethods/SignProtocolMethod.swift similarity index 96% rename from Sources/WalletConnectSign/Types/SignProtocolMethod.swift rename to Sources/WalletConnectSign/Types/ProtocolMethods/SignProtocolMethod.swift index 87f727441..ca2a263d4 100644 --- a/Sources/WalletConnectSign/Types/SignProtocolMethod.swift +++ b/Sources/WalletConnectSign/Types/ProtocolMethods/SignProtocolMethod.swift @@ -1,7 +1,4 @@ import Foundation -import JSONRPC -import WalletConnectPairing -import WalletConnectUtils import WalletConnectNetworking struct PairingPingProtocolMethod: ProtocolMethod { @@ -20,9 +17,6 @@ struct PairingDeleteProtocolMethod: ProtocolMethod { var response = RelayConfig(tag: 1001, prompt: false) } - - - struct SessionProposeProtocolMethod: ProtocolMethod { var method: String = "wc_sessionPropose" @@ -71,6 +65,7 @@ struct SessionRequestProtocolMethod: ProtocolMethod { var response = RelayConfig(tag: 1109, prompt: false) } + struct SessionPingProtocolMethod: ProtocolMethod { var method: String = "wc_sessionPing" @@ -78,6 +73,7 @@ struct SessionPingProtocolMethod: ProtocolMethod { var response = RelayConfig(tag: 1115, prompt: false) } + struct SessionEventProtocolMethod: ProtocolMethod { var method: String = "wc_sessionEvent" From a466d2999c6fd79533d2b12207b88409e1d7f705 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 26 Sep 2022 12:34:52 +0200 Subject: [PATCH 061/175] rename properties --- Sources/Auth/AuthProtocolMethod.swift | 12 +++--- Sources/Chat/Types/ChatProtocolMethod.swift | 8 ++-- .../NetworkInteractor.swift | 6 +-- .../ProtocolMethod.swift | 4 +- .../PairingProtocolMethod.swift | 4 +- .../ProtocolMethods/SignProtocolMethod.swift | 40 +++++++++---------- 6 files changed, 37 insertions(+), 37 deletions(-) diff --git a/Sources/Auth/AuthProtocolMethod.swift b/Sources/Auth/AuthProtocolMethod.swift index 4de009c42..df6e982be 100644 --- a/Sources/Auth/AuthProtocolMethod.swift +++ b/Sources/Auth/AuthProtocolMethod.swift @@ -4,25 +4,25 @@ import WalletConnectNetworking struct AuthRequestProtocolMethod: ProtocolMethod { var method: String = "wc_authRequest" - var request = RelayConfig(tag: 3000, prompt: true) + var requestConfig = RelayConfig(tag: 3000, prompt: true) - var response = RelayConfig(tag: 3001, prompt: false) + var responseConfig = RelayConfig(tag: 3001, prompt: false) } struct PairingPingProtocolMethod: ProtocolMethod { var method: String = "wc_pairingPing" - var request = RelayConfig(tag: 1002, prompt: false) + var requestConfig = RelayConfig(tag: 1002, prompt: false) - var response = RelayConfig(tag: 1003, prompt: false) + var responseConfig = RelayConfig(tag: 1003, prompt: false) } struct PairingDeleteProtocolMethod: ProtocolMethod { var method: String = "wc_pairingDelete" - var request = RelayConfig(tag: 1000, prompt: false) + var requestConfig = RelayConfig(tag: 1000, prompt: false) - var response = RelayConfig(tag: 1001, prompt: false) + var responseConfig = RelayConfig(tag: 1001, prompt: false) } diff --git a/Sources/Chat/Types/ChatProtocolMethod.swift b/Sources/Chat/Types/ChatProtocolMethod.swift index c6ca16dfe..96a5dd9af 100644 --- a/Sources/Chat/Types/ChatProtocolMethod.swift +++ b/Sources/Chat/Types/ChatProtocolMethod.swift @@ -4,17 +4,17 @@ import WalletConnectNetworking struct ChatInviteProtocolMethod: ProtocolMethod { var method: String = "wc_chatInvite" - var request = RelayConfig(tag: 2000, prompt: true) + var requestConfig = RelayConfig(tag: 2000, prompt: true) - var response = RelayConfig(tag: 2001, prompt: false) + var responseConfig = RelayConfig(tag: 2001, prompt: false) } struct ChatMessageProtocolMethod: ProtocolMethod { var method: String = "wc_chatMessage" - var request = RelayConfig(tag: 2002, prompt: true) + var requestConfig = RelayConfig(tag: 2002, prompt: true) - var response = RelayConfig(tag: 2003, prompt: false) + var responseConfig = RelayConfig(tag: 2003, prompt: false) } diff --git a/Sources/WalletConnectNetworking/NetworkInteractor.swift b/Sources/WalletConnectNetworking/NetworkInteractor.swift index 339c87656..b13026a39 100644 --- a/Sources/WalletConnectNetworking/NetworkInteractor.swift +++ b/Sources/WalletConnectNetworking/NetworkInteractor.swift @@ -96,7 +96,7 @@ public class NetworkingInteractor: NetworkInteracting { public func request(_ request: RPCRequest, topic: String, protocolMethod: ProtocolMethod, envelopeType: Envelope.EnvelopeType) async throws { try rpcHistory.set(request, forTopic: topic, emmitedBy: .local) let message = try! serializer.serialize(topic: topic, encodable: request, envelopeType: envelopeType) - try await relayClient.publish(topic: topic, payload: message, tag: protocolMethod.request.tag, prompt: protocolMethod.request.prompt) + try await relayClient.publish(topic: topic, payload: message, tag: protocolMethod.requestConfig.tag, prompt: protocolMethod.requestConfig.prompt) } /// Completes with an acknowledgement from the relay network. @@ -107,7 +107,7 @@ public class NetworkingInteractor: NetworkInteracting { try rpcHistory.set(request, forTopic: topic, emmitedBy: .local) let message = try serializer.serialize(topic: topic, encodable: request) return try await withCheckedThrowingContinuation { continuation in - relayClient.publish(topic: topic, payload: message, tag: protocolMethod.request.tag, prompt: protocolMethod.request.prompt) { error in + relayClient.publish(topic: topic, payload: message, tag: protocolMethod.requestConfig.tag, prompt: protocolMethod.requestConfig.prompt) { error in if let error = error { continuation.resume(throwing: error) } else { @@ -123,7 +123,7 @@ public class NetworkingInteractor: NetworkInteracting { public func respond(topic: String, response: RPCResponse, protocolMethod: ProtocolMethod, envelopeType: Envelope.EnvelopeType) async throws { try rpcHistory.resolve(response) let message = try! serializer.serialize(topic: topic, encodable: response, envelopeType: envelopeType) - try await relayClient.publish(topic: topic, payload: message, tag: protocolMethod.response.tag) + try await relayClient.publish(topic: topic, payload: message, tag: protocolMethod.responseConfig.tag) } public func respondSuccess(topic: String, requestId: RPCID, protocolMethod: ProtocolMethod, envelopeType: Envelope.EnvelopeType) async throws { diff --git a/Sources/WalletConnectNetworking/ProtocolMethod.swift b/Sources/WalletConnectNetworking/ProtocolMethod.swift index 21c8ad1b5..b482f09c1 100644 --- a/Sources/WalletConnectNetworking/ProtocolMethod.swift +++ b/Sources/WalletConnectNetworking/ProtocolMethod.swift @@ -2,8 +2,8 @@ import Foundation public protocol ProtocolMethod { var method: String { get } - var request: RelayConfig { get } - var response: RelayConfig { get } + var requestConfig: RelayConfig { get } + var responseConfig: RelayConfig { get } } public struct RelayConfig { diff --git a/Sources/WalletConnectPairing/PairingProtocolMethod.swift b/Sources/WalletConnectPairing/PairingProtocolMethod.swift index b4231dade..c223a51c4 100644 --- a/Sources/WalletConnectPairing/PairingProtocolMethod.swift +++ b/Sources/WalletConnectPairing/PairingProtocolMethod.swift @@ -4,7 +4,7 @@ import WalletConnectNetworking struct PairingPingProtocolMethod: ProtocolMethod { var method: String = "wc_pairingPing" - var request = RelayConfig(tag: 1002, prompt: false) + var requestConfig = RelayConfig(tag: 1002, prompt: false) - var response = RelayConfig(tag: 1003, prompt: false) + var responseConfig = RelayConfig(tag: 1003, prompt: false) } diff --git a/Sources/WalletConnectSign/Types/ProtocolMethods/SignProtocolMethod.swift b/Sources/WalletConnectSign/Types/ProtocolMethods/SignProtocolMethod.swift index ca2a263d4..2058a0044 100644 --- a/Sources/WalletConnectSign/Types/ProtocolMethods/SignProtocolMethod.swift +++ b/Sources/WalletConnectSign/Types/ProtocolMethods/SignProtocolMethod.swift @@ -4,80 +4,80 @@ import WalletConnectNetworking struct PairingPingProtocolMethod: ProtocolMethod { var method: String = "wc_pairingPing" - var request = RelayConfig(tag: 1002, prompt: false) + var requestConfig = RelayConfig(tag: 1002, prompt: false) - var response = RelayConfig(tag: 1003, prompt: false) + var responseConfig = RelayConfig(tag: 1003, prompt: false) } struct PairingDeleteProtocolMethod: ProtocolMethod { var method: String = "wc_pairingDelete" - var request = RelayConfig(tag: 1000, prompt: false) + var requestConfig = RelayConfig(tag: 1000, prompt: false) - var response = RelayConfig(tag: 1001, prompt: false) + var responseConfig = RelayConfig(tag: 1001, prompt: false) } struct SessionProposeProtocolMethod: ProtocolMethod { var method: String = "wc_sessionPropose" - var request = RelayConfig(tag: 1100, prompt: true) + var requestConfig = RelayConfig(tag: 1100, prompt: true) - var response = RelayConfig(tag: 1101, prompt: false) + var responseConfig = RelayConfig(tag: 1101, prompt: false) } struct SessionSettleProtocolMethod: ProtocolMethod { var method: String = "wc_sessionSettle" - var request = RelayConfig(tag: 1102, prompt: false) + var requestConfig = RelayConfig(tag: 1102, prompt: false) - var response = RelayConfig(tag: 1103, prompt: false) + var responseConfig = RelayConfig(tag: 1103, prompt: false) } struct SessionUpdateProtocolMethod: ProtocolMethod { var method: String = "wc_sessionUpdate" - var request = RelayConfig(tag: 1104, prompt: false) + var requestConfig = RelayConfig(tag: 1104, prompt: false) - var response = RelayConfig(tag: 1105, prompt: false) + var responseConfig = RelayConfig(tag: 1105, prompt: false) } struct SessionExtendProtocolMethod: ProtocolMethod { var method: String = "wc_sessionExtend" - var request = RelayConfig(tag: 1106, prompt: false) + var requestConfig = RelayConfig(tag: 1106, prompt: false) - var response = RelayConfig(tag: 1107, prompt: false) + var responseConfig = RelayConfig(tag: 1107, prompt: false) } struct SessionDeleteProtocolMethod: ProtocolMethod { var method: String = "wc_sessionDelete" - var request = RelayConfig(tag: 1112, prompt: false) + var requestConfig = RelayConfig(tag: 1112, prompt: false) - var response = RelayConfig(tag: 1113, prompt: false) + var responseConfig = RelayConfig(tag: 1113, prompt: false) } struct SessionRequestProtocolMethod: ProtocolMethod { var method: String = "wc_sessionRequest" - var request = RelayConfig(tag: 1108, prompt: true) + var requestConfig = RelayConfig(tag: 1108, prompt: true) - var response = RelayConfig(tag: 1109, prompt: false) + var responseConfig = RelayConfig(tag: 1109, prompt: false) } struct SessionPingProtocolMethod: ProtocolMethod { var method: String = "wc_sessionPing" - var request = RelayConfig(tag: 1114, prompt: false) + var requestConfig = RelayConfig(tag: 1114, prompt: false) - var response = RelayConfig(tag: 1115, prompt: false) + var responseConfig = RelayConfig(tag: 1115, prompt: false) } struct SessionEventProtocolMethod: ProtocolMethod { var method: String = "wc_sessionEvent" - var request = RelayConfig(tag: 1110, prompt: true) + var requestConfig = RelayConfig(tag: 1110, prompt: true) - var response = RelayConfig(tag: 1111, prompt: false) + var responseConfig = RelayConfig(tag: 1111, prompt: false) } From 9ab2979b9178616b44f7f60358f6c5eb5fe5a748 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 26 Sep 2022 12:39:01 +0200 Subject: [PATCH 062/175] savepoint --- .../WalletConnectNetworking/NetworkInteractor.swift | 2 +- Sources/WalletConnectNetworking/ProtocolMethod.swift | 8 +++++--- Sources/WalletConnectRelay/RelayClient.swift | 10 +++++----- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/Sources/WalletConnectNetworking/NetworkInteractor.swift b/Sources/WalletConnectNetworking/NetworkInteractor.swift index b13026a39..10250f025 100644 --- a/Sources/WalletConnectNetworking/NetworkInteractor.swift +++ b/Sources/WalletConnectNetworking/NetworkInteractor.swift @@ -123,7 +123,7 @@ public class NetworkingInteractor: NetworkInteracting { public func respond(topic: String, response: RPCResponse, protocolMethod: ProtocolMethod, envelopeType: Envelope.EnvelopeType) async throws { try rpcHistory.resolve(response) let message = try! serializer.serialize(topic: topic, encodable: response, envelopeType: envelopeType) - try await relayClient.publish(topic: topic, payload: message, tag: protocolMethod.responseConfig.tag) + try await relayClient.publish(topic: topic, payload: message, tag: protocolMethod.responseConfig.tag, prompt: protocolMethod.responseConfig.prompt, ttl: protocolMethod.responseConfig.ttl) } public func respondSuccess(topic: String, requestId: RPCID, protocolMethod: ProtocolMethod, envelopeType: Envelope.EnvelopeType) async throws { diff --git a/Sources/WalletConnectNetworking/ProtocolMethod.swift b/Sources/WalletConnectNetworking/ProtocolMethod.swift index b482f09c1..fa061623f 100644 --- a/Sources/WalletConnectNetworking/ProtocolMethod.swift +++ b/Sources/WalletConnectNetworking/ProtocolMethod.swift @@ -7,11 +7,13 @@ public protocol ProtocolMethod { } public struct RelayConfig { - var tag: Int - var prompt: Bool + let tag: Int + let prompt: Bool + let ttl: Int - public init(tag: Int, prompt: Bool) { + public init(tag: Int, prompt: Bool, ttl: Int) { self.tag = tag self.prompt = prompt + self.ttl = ttl } } diff --git a/Sources/WalletConnectRelay/RelayClient.swift b/Sources/WalletConnectRelay/RelayClient.swift index 9aa2ba8a4..c038cb0dd 100644 --- a/Sources/WalletConnectRelay/RelayClient.swift +++ b/Sources/WalletConnectRelay/RelayClient.swift @@ -20,7 +20,6 @@ public final class RelayClient { case subscriptionIdNotFound } - let defaultTtl = 6*Time.hour var subscriptions: [String: String] = [:] public var messagePublisher: AnyPublisher<(topic: String, message: String), Never> { @@ -120,8 +119,8 @@ public final class RelayClient { } /// Completes when networking client sends a request, error if it fails on client side - public func publish(topic: String, payload: String, tag: Int, prompt: Bool = false) async throws { - let request = Publish(params: .init(topic: topic, message: payload, ttl: defaultTtl, prompt: prompt, tag: tag)) + public func publish(topic: String, payload: String, tag: Int, prompt: Bool, ttl: Int) async throws { + let request = Publish(params: .init(topic: topic, message: payload, ttl: ttl, prompt: prompt, tag: tag)) .wrapToIRN() .asRPCRequest() let message = try request.asJSONEncodedString() @@ -134,10 +133,11 @@ public final class RelayClient { topic: String, payload: String, tag: Int, - prompt: Bool = false, + prompt: Bool, + ttl: Int, onNetworkAcknowledge: @escaping ((Error?) -> Void) ) { - let rpc = Publish(params: .init(topic: topic, message: payload, ttl: defaultTtl, prompt: prompt, tag: tag)) + let rpc = Publish(params: .init(topic: topic, message: payload, ttl: ttl, prompt: prompt, tag: tag)) let request = rpc .wrapToIRN() .asRPCRequest() From 5a23be7d9efb46c7f3ff65e6788c9fda430c3bfc Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 26 Sep 2022 12:49:10 +0200 Subject: [PATCH 063/175] restructure files --- .../Relay/RelayClientEndToEndTests.swift | 4 +- Sources/Auth/AuthProtocolMethod.swift | 12 +-- Sources/Chat/Types/ChatProtocolMethod.swift | 8 +- .../NetworkInteractor.swift | 4 +- .../PairingProtocolMethod.swift | 4 +- Sources/WalletConnectRelay/Misc/Time.swift | 7 -- .../PairingProtocolMethods.swift | 18 ++++ .../SessionDeleteProtocolMethod.swift | 11 +++ .../SessionEventProtocolMethod.swift | 10 +++ .../SessionExtendProtocolMethod.swift | 10 +++ .../SessionPingProtocolMethod.swift | 11 +++ .../SessionProposeProtocolMethod.swift | 10 +++ .../SessionRequestProtocolMethod.swift | 10 +++ .../SessionSettleProtocolMethod.swift | 10 +++ .../SessionUpdateProtocolMethod.swift | 11 +++ .../ProtocolMethods/SignProtocolMethod.swift | 83 ------------------- 16 files changed, 117 insertions(+), 106 deletions(-) delete mode 100644 Sources/WalletConnectRelay/Misc/Time.swift create mode 100644 Sources/WalletConnectSign/Types/ProtocolMethods/PairingProtocolMethods.swift create mode 100644 Sources/WalletConnectSign/Types/ProtocolMethods/SessionDeleteProtocolMethod.swift create mode 100644 Sources/WalletConnectSign/Types/ProtocolMethods/SessionEventProtocolMethod.swift create mode 100644 Sources/WalletConnectSign/Types/ProtocolMethods/SessionExtendProtocolMethod.swift create mode 100644 Sources/WalletConnectSign/Types/ProtocolMethods/SessionPingProtocolMethod.swift create mode 100644 Sources/WalletConnectSign/Types/ProtocolMethods/SessionProposeProtocolMethod.swift create mode 100644 Sources/WalletConnectSign/Types/ProtocolMethods/SessionRequestProtocolMethod.swift create mode 100644 Sources/WalletConnectSign/Types/ProtocolMethods/SessionSettleProtocolMethod.swift create mode 100644 Sources/WalletConnectSign/Types/ProtocolMethods/SessionUpdateProtocolMethod.swift delete mode 100644 Sources/WalletConnectSign/Types/ProtocolMethods/SignProtocolMethod.swift diff --git a/Example/IntegrationTests/Relay/RelayClientEndToEndTests.swift b/Example/IntegrationTests/Relay/RelayClientEndToEndTests.swift index d456d69dc..5c1843eb3 100644 --- a/Example/IntegrationTests/Relay/RelayClientEndToEndTests.swift +++ b/Example/IntegrationTests/Relay/RelayClientEndToEndTests.swift @@ -77,7 +77,7 @@ final class RelayClientEndToEndTests: XCTestCase { }.store(in: &publishers) relayA.socketConnectionStatusPublisher.sink { _ in - relayA.publish(topic: randomTopic, payload: payloadA, tag: 0, onNetworkAcknowledge: { error in + relayA.publish(topic: randomTopic, payload: payloadA, tag: 0, prompt: false, ttl: 60, onNetworkAcknowledge: { error in XCTAssertNil(error) }) relayA.subscribe(topic: randomTopic) { error in @@ -85,7 +85,7 @@ final class RelayClientEndToEndTests: XCTestCase { } }.store(in: &publishers) relayB.socketConnectionStatusPublisher.sink { _ in - relayB.publish(topic: randomTopic, payload: payloadB, tag: 0, onNetworkAcknowledge: { error in + relayB.publish(topic: randomTopic, payload: payloadB, tag: 0, prompt: false, ttl: 60, onNetworkAcknowledge: { error in XCTAssertNil(error) }) relayB.subscribe(topic: randomTopic) { error in diff --git a/Sources/Auth/AuthProtocolMethod.swift b/Sources/Auth/AuthProtocolMethod.swift index df6e982be..e54889755 100644 --- a/Sources/Auth/AuthProtocolMethod.swift +++ b/Sources/Auth/AuthProtocolMethod.swift @@ -4,25 +4,25 @@ import WalletConnectNetworking struct AuthRequestProtocolMethod: ProtocolMethod { var method: String = "wc_authRequest" - var requestConfig = RelayConfig(tag: 3000, prompt: true) + var requestConfig = RelayConfig(tag: 3000, prompt: true, ttl: 86400) - var responseConfig = RelayConfig(tag: 3001, prompt: false) + var responseConfig = RelayConfig(tag: 3001, prompt: false, ttl: 86400) } struct PairingPingProtocolMethod: ProtocolMethod { var method: String = "wc_pairingPing" - var requestConfig = RelayConfig(tag: 1002, prompt: false) + var requestConfig = RelayConfig(tag: 1002, prompt: false, ttl: 30) - var responseConfig = RelayConfig(tag: 1003, prompt: false) + var responseConfig = RelayConfig(tag: 1003, prompt: false, ttl: 30) } struct PairingDeleteProtocolMethod: ProtocolMethod { var method: String = "wc_pairingDelete" - var requestConfig = RelayConfig(tag: 1000, prompt: false) + var requestConfig = RelayConfig(tag: 1000, prompt: false, ttl: 86400) - var responseConfig = RelayConfig(tag: 1001, prompt: false) + var responseConfig = RelayConfig(tag: 1001, prompt: false, ttl: 86400) } diff --git a/Sources/Chat/Types/ChatProtocolMethod.swift b/Sources/Chat/Types/ChatProtocolMethod.swift index 96a5dd9af..82dcd15dc 100644 --- a/Sources/Chat/Types/ChatProtocolMethod.swift +++ b/Sources/Chat/Types/ChatProtocolMethod.swift @@ -4,17 +4,17 @@ import WalletConnectNetworking struct ChatInviteProtocolMethod: ProtocolMethod { var method: String = "wc_chatInvite" - var requestConfig = RelayConfig(tag: 2000, prompt: true) + var requestConfig = RelayConfig(tag: 2000, prompt: true, ttl: 86400) - var responseConfig = RelayConfig(tag: 2001, prompt: false) + var responseConfig = RelayConfig(tag: 2001, prompt: false, ttl: 86400) } struct ChatMessageProtocolMethod: ProtocolMethod { var method: String = "wc_chatMessage" - var requestConfig = RelayConfig(tag: 2002, prompt: true) + var requestConfig = RelayConfig(tag: 2002, prompt: true, ttl: 86400) - var responseConfig = RelayConfig(tag: 2003, prompt: false) + var responseConfig = RelayConfig(tag: 2003, prompt: false, ttl: 86400) } diff --git a/Sources/WalletConnectNetworking/NetworkInteractor.swift b/Sources/WalletConnectNetworking/NetworkInteractor.swift index 10250f025..6b8cdecbd 100644 --- a/Sources/WalletConnectNetworking/NetworkInteractor.swift +++ b/Sources/WalletConnectNetworking/NetworkInteractor.swift @@ -96,7 +96,7 @@ public class NetworkingInteractor: NetworkInteracting { public func request(_ request: RPCRequest, topic: String, protocolMethod: ProtocolMethod, envelopeType: Envelope.EnvelopeType) async throws { try rpcHistory.set(request, forTopic: topic, emmitedBy: .local) let message = try! serializer.serialize(topic: topic, encodable: request, envelopeType: envelopeType) - try await relayClient.publish(topic: topic, payload: message, tag: protocolMethod.requestConfig.tag, prompt: protocolMethod.requestConfig.prompt) + try await relayClient.publish(topic: topic, payload: message, tag: protocolMethod.requestConfig.tag, prompt: protocolMethod.requestConfig.prompt, ttl: protocolMethod.requestConfig.ttl) } /// Completes with an acknowledgement from the relay network. @@ -107,7 +107,7 @@ public class NetworkingInteractor: NetworkInteracting { try rpcHistory.set(request, forTopic: topic, emmitedBy: .local) let message = try serializer.serialize(topic: topic, encodable: request) return try await withCheckedThrowingContinuation { continuation in - relayClient.publish(topic: topic, payload: message, tag: protocolMethod.requestConfig.tag, prompt: protocolMethod.requestConfig.prompt) { error in + relayClient.publish(topic: topic, payload: message, tag: protocolMethod.requestConfig.tag, prompt: protocolMethod.requestConfig.prompt, ttl: protocolMethod.requestConfig.ttl) { error in if let error = error { continuation.resume(throwing: error) } else { diff --git a/Sources/WalletConnectPairing/PairingProtocolMethod.swift b/Sources/WalletConnectPairing/PairingProtocolMethod.swift index c223a51c4..2cd6ac361 100644 --- a/Sources/WalletConnectPairing/PairingProtocolMethod.swift +++ b/Sources/WalletConnectPairing/PairingProtocolMethod.swift @@ -4,7 +4,7 @@ import WalletConnectNetworking struct PairingPingProtocolMethod: ProtocolMethod { var method: String = "wc_pairingPing" - var requestConfig = RelayConfig(tag: 1002, prompt: false) + var requestConfig = RelayConfig(tag: 1002, prompt: false, ttl: 30) - var responseConfig = RelayConfig(tag: 1003, prompt: false) + var responseConfig = RelayConfig(tag: 1003, prompt: false, ttl: 30) } diff --git a/Sources/WalletConnectRelay/Misc/Time.swift b/Sources/WalletConnectRelay/Misc/Time.swift deleted file mode 100644 index 5aaaafb87..000000000 --- a/Sources/WalletConnectRelay/Misc/Time.swift +++ /dev/null @@ -1,7 +0,0 @@ -// - -import Foundation - -enum Time { - static let hour = 3600 -} diff --git a/Sources/WalletConnectSign/Types/ProtocolMethods/PairingProtocolMethods.swift b/Sources/WalletConnectSign/Types/ProtocolMethods/PairingProtocolMethods.swift new file mode 100644 index 000000000..cfd3bd53c --- /dev/null +++ b/Sources/WalletConnectSign/Types/ProtocolMethods/PairingProtocolMethods.swift @@ -0,0 +1,18 @@ +import Foundation +import WalletConnectNetworking + +struct PairingPingProtocolMethod: ProtocolMethod { + var method: String = "wc_pairingPing" + + var requestConfig = RelayConfig(tag: 1002, prompt: false, ttl: 30) + + var responseConfig = RelayConfig(tag: 1003, prompt: false, ttl: 30) +} + +struct PairingDeleteProtocolMethod: ProtocolMethod { + var method: String = "wc_pairingDelete" + + var requestConfig = RelayConfig(tag: 1000, prompt: false, ttl: 86400) + + var responseConfig = RelayConfig(tag: 1001, prompt: false, ttl: 86400) +} diff --git a/Sources/WalletConnectSign/Types/ProtocolMethods/SessionDeleteProtocolMethod.swift b/Sources/WalletConnectSign/Types/ProtocolMethods/SessionDeleteProtocolMethod.swift new file mode 100644 index 000000000..e1b487aa8 --- /dev/null +++ b/Sources/WalletConnectSign/Types/ProtocolMethods/SessionDeleteProtocolMethod.swift @@ -0,0 +1,11 @@ +import Foundation +import WalletConnectNetworking + +struct SessionDeleteProtocolMethod: ProtocolMethod { + var method: String = "wc_sessionDelete" + + var requestConfig = RelayConfig(tag: 1112, prompt: false, ttl: 86400) + + var responseConfig = RelayConfig(tag: 1113, prompt: false, ttl: 86400) +} + diff --git a/Sources/WalletConnectSign/Types/ProtocolMethods/SessionEventProtocolMethod.swift b/Sources/WalletConnectSign/Types/ProtocolMethods/SessionEventProtocolMethod.swift new file mode 100644 index 000000000..585796e3f --- /dev/null +++ b/Sources/WalletConnectSign/Types/ProtocolMethods/SessionEventProtocolMethod.swift @@ -0,0 +1,10 @@ +import Foundation +import WalletConnectNetworking + +struct SessionEventProtocolMethod: ProtocolMethod { + var method: String = "wc_sessionEvent" + + var requestConfig = RelayConfig(tag: 1110, prompt: true, ttl: 300) + + var responseConfig = RelayConfig(tag: 1111, prompt: false, ttl: 300) +} diff --git a/Sources/WalletConnectSign/Types/ProtocolMethods/SessionExtendProtocolMethod.swift b/Sources/WalletConnectSign/Types/ProtocolMethods/SessionExtendProtocolMethod.swift new file mode 100644 index 000000000..94425014c --- /dev/null +++ b/Sources/WalletConnectSign/Types/ProtocolMethods/SessionExtendProtocolMethod.swift @@ -0,0 +1,10 @@ +import Foundation +import WalletConnectNetworking + +struct SessionExtendProtocolMethod: ProtocolMethod { + var method: String = "wc_sessionExtend" + + var requestConfig = RelayConfig(tag: 1106, prompt: false, ttl: 86400) + + var responseConfig = RelayConfig(tag: 1107, prompt: false, ttl: 86400) +} diff --git a/Sources/WalletConnectSign/Types/ProtocolMethods/SessionPingProtocolMethod.swift b/Sources/WalletConnectSign/Types/ProtocolMethods/SessionPingProtocolMethod.swift new file mode 100644 index 000000000..9be7feea0 --- /dev/null +++ b/Sources/WalletConnectSign/Types/ProtocolMethods/SessionPingProtocolMethod.swift @@ -0,0 +1,11 @@ +import Foundation +import WalletConnectNetworking + +struct SessionPingProtocolMethod: ProtocolMethod { + var method: String = "wc_sessionPing" + + var requestConfig = RelayConfig(tag: 1114, prompt: false, ttl: 30) + + var responseConfig = RelayConfig(tag: 1115, prompt: false, ttl: 30) +} + diff --git a/Sources/WalletConnectSign/Types/ProtocolMethods/SessionProposeProtocolMethod.swift b/Sources/WalletConnectSign/Types/ProtocolMethods/SessionProposeProtocolMethod.swift new file mode 100644 index 000000000..017325d99 --- /dev/null +++ b/Sources/WalletConnectSign/Types/ProtocolMethods/SessionProposeProtocolMethod.swift @@ -0,0 +1,10 @@ +import Foundation +import WalletConnectNetworking + +struct SessionProposeProtocolMethod: ProtocolMethod { + var method: String = "wc_sessionPropose" + + var requestConfig = RelayConfig(tag: 1100, prompt: true, ttl: 300) + + var responseConfig = RelayConfig(tag: 1101, prompt: false, ttl: 300) +} diff --git a/Sources/WalletConnectSign/Types/ProtocolMethods/SessionRequestProtocolMethod.swift b/Sources/WalletConnectSign/Types/ProtocolMethods/SessionRequestProtocolMethod.swift new file mode 100644 index 000000000..90047d521 --- /dev/null +++ b/Sources/WalletConnectSign/Types/ProtocolMethods/SessionRequestProtocolMethod.swift @@ -0,0 +1,10 @@ +import Foundation +import WalletConnectNetworking + +struct SessionRequestProtocolMethod: ProtocolMethod { + var method: String = "wc_sessionRequest" + + var requestConfig = RelayConfig(tag: 1108, prompt: true, ttl: 300) + + var responseConfig = RelayConfig(tag: 1109, prompt: false, ttl: 300) +} diff --git a/Sources/WalletConnectSign/Types/ProtocolMethods/SessionSettleProtocolMethod.swift b/Sources/WalletConnectSign/Types/ProtocolMethods/SessionSettleProtocolMethod.swift new file mode 100644 index 000000000..7a0341c1f --- /dev/null +++ b/Sources/WalletConnectSign/Types/ProtocolMethods/SessionSettleProtocolMethod.swift @@ -0,0 +1,10 @@ +import Foundation +import WalletConnectNetworking + +struct SessionSettleProtocolMethod: ProtocolMethod { + var method: String = "wc_sessionSettle" + + var requestConfig = RelayConfig(tag: 1102, prompt: false, ttl: 300) + + var responseConfig = RelayConfig(tag: 1103, prompt: false, ttl: 300) +} diff --git a/Sources/WalletConnectSign/Types/ProtocolMethods/SessionUpdateProtocolMethod.swift b/Sources/WalletConnectSign/Types/ProtocolMethods/SessionUpdateProtocolMethod.swift new file mode 100644 index 000000000..7ff5eb754 --- /dev/null +++ b/Sources/WalletConnectSign/Types/ProtocolMethods/SessionUpdateProtocolMethod.swift @@ -0,0 +1,11 @@ +import Foundation +import WalletConnectNetworking + +struct SessionUpdateProtocolMethod: ProtocolMethod { + var method: String = "wc_sessionUpdate" + + var requestConfig = RelayConfig(tag: 1104, prompt: false, ttl: 86400) + + var responseConfig = RelayConfig(tag: 1105, prompt: false, ttl: 86400) +} + diff --git a/Sources/WalletConnectSign/Types/ProtocolMethods/SignProtocolMethod.swift b/Sources/WalletConnectSign/Types/ProtocolMethods/SignProtocolMethod.swift deleted file mode 100644 index 2058a0044..000000000 --- a/Sources/WalletConnectSign/Types/ProtocolMethods/SignProtocolMethod.swift +++ /dev/null @@ -1,83 +0,0 @@ -import Foundation -import WalletConnectNetworking - -struct PairingPingProtocolMethod: ProtocolMethod { - var method: String = "wc_pairingPing" - - var requestConfig = RelayConfig(tag: 1002, prompt: false) - - var responseConfig = RelayConfig(tag: 1003, prompt: false) -} - -struct PairingDeleteProtocolMethod: ProtocolMethod { - var method: String = "wc_pairingDelete" - - var requestConfig = RelayConfig(tag: 1000, prompt: false) - - var responseConfig = RelayConfig(tag: 1001, prompt: false) -} - -struct SessionProposeProtocolMethod: ProtocolMethod { - var method: String = "wc_sessionPropose" - - var requestConfig = RelayConfig(tag: 1100, prompt: true) - - var responseConfig = RelayConfig(tag: 1101, prompt: false) -} - -struct SessionSettleProtocolMethod: ProtocolMethod { - var method: String = "wc_sessionSettle" - - var requestConfig = RelayConfig(tag: 1102, prompt: false) - - var responseConfig = RelayConfig(tag: 1103, prompt: false) -} - -struct SessionUpdateProtocolMethod: ProtocolMethod { - var method: String = "wc_sessionUpdate" - - var requestConfig = RelayConfig(tag: 1104, prompt: false) - - var responseConfig = RelayConfig(tag: 1105, prompt: false) -} - -struct SessionExtendProtocolMethod: ProtocolMethod { - var method: String = "wc_sessionExtend" - - var requestConfig = RelayConfig(tag: 1106, prompt: false) - - var responseConfig = RelayConfig(tag: 1107, prompt: false) -} - -struct SessionDeleteProtocolMethod: ProtocolMethod { - var method: String = "wc_sessionDelete" - - var requestConfig = RelayConfig(tag: 1112, prompt: false) - - var responseConfig = RelayConfig(tag: 1113, prompt: false) -} - - -struct SessionRequestProtocolMethod: ProtocolMethod { - var method: String = "wc_sessionRequest" - - var requestConfig = RelayConfig(tag: 1108, prompt: true) - - var responseConfig = RelayConfig(tag: 1109, prompt: false) -} - -struct SessionPingProtocolMethod: ProtocolMethod { - var method: String = "wc_sessionPing" - - var requestConfig = RelayConfig(tag: 1114, prompt: false) - - var responseConfig = RelayConfig(tag: 1115, prompt: false) -} - -struct SessionEventProtocolMethod: ProtocolMethod { - var method: String = "wc_sessionEvent" - - var requestConfig = RelayConfig(tag: 1110, prompt: true) - - var responseConfig = RelayConfig(tag: 1111, prompt: false) -} From 80e050f8ca83328e347c66d3a7bb4aa629047c54 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 26 Sep 2022 12:54:21 +0200 Subject: [PATCH 064/175] fix unit tests --- Tests/AuthTests/WalletRequestSubscriberTests.swift | 2 +- Tests/RelayerTests/RelayClientTests.swift | 4 ++-- Tests/TestingUtils/NetworkingInteractorMock.swift | 6 +++--- Tests/WalletConnectSignTests/ApproveEngineTests.swift | 8 ++++---- Tests/WalletConnectSignTests/Stub/Stubs.swift | 8 ++++---- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Tests/AuthTests/WalletRequestSubscriberTests.swift b/Tests/AuthTests/WalletRequestSubscriberTests.swift index 5f30d166e..6df79685b 100644 --- a/Tests/AuthTests/WalletRequestSubscriberTests.swift +++ b/Tests/AuthTests/WalletRequestSubscriberTests.swift @@ -37,7 +37,7 @@ class WalletRequestSubscriberTests: XCTestCase { messageExpectation.fulfill() } - let request = RPCRequest(method: AuthProtocolMethod.authRequest.method, params: AuthRequestParams.stub(id: expectedRequestId), id: expectedRequestId.right!) + let request = RPCRequest(method: AuthRequestProtocolMethod().method, params: AuthRequestParams.stub(id: expectedRequestId), id: expectedRequestId.right!) networkingInteractor.requestPublisherSubject.send(("123", request)) wait(for: [messageExpectation], timeout: defaultTimeout) diff --git a/Tests/RelayerTests/RelayClientTests.swift b/Tests/RelayerTests/RelayClientTests.swift index f9c9384c2..f9094bf17 100644 --- a/Tests/RelayerTests/RelayClientTests.swift +++ b/Tests/RelayerTests/RelayClientTests.swift @@ -55,7 +55,7 @@ final class RelayClientTests: XCTestCase { func testPublishRequestAcknowledge() { let expectation = expectation(description: "Publish must callback on relay server acknowledgement") - sut.publish(topic: "", payload: "{}", tag: 0) { error in + sut.publish(topic: "", payload: "{}", tag: 0, prompt: false, ttl: 60) { error in XCTAssertNil(error) expectation.fulfill() } @@ -93,7 +93,7 @@ final class RelayClientTests: XCTestCase { } func testSendOnPublish() { - sut.publish(topic: "", payload: "", tag: 0, onNetworkAcknowledge: { _ in}) + sut.publish(topic: "", payload: "", tag: 0, prompt: false, ttl: 60, onNetworkAcknowledge: { _ in}) XCTAssertTrue(dispatcher.sent) } diff --git a/Tests/TestingUtils/NetworkingInteractorMock.swift b/Tests/TestingUtils/NetworkingInteractorMock.swift index b3e6714bb..171602eba 100644 --- a/Tests/TestingUtils/NetworkingInteractorMock.swift +++ b/Tests/TestingUtils/NetworkingInteractorMock.swift @@ -101,15 +101,15 @@ public class NetworkingInteractorMock: NetworkInteracting { requests.append((topic, request)) } - public func respond(topic: String, response: RPCResponse, tag: Int, envelopeType: Envelope.EnvelopeType) async throws { + public func respond(topic: String, response: RPCResponse, protocolMethod: ProtocolMethod, envelopeType: Envelope.EnvelopeType) async throws { didRespondOnTopic = topic } - public func respondSuccess(topic: String, requestId: RPCID, tag: Int, envelopeType: Envelope.EnvelopeType) async throws { + public func respondSuccess(topic: String, requestId: RPCID, protocolMethod: ProtocolMethod, envelopeType: Envelope.EnvelopeType) async throws { didRespondSuccess = true } - public func respondError(topic: String, requestId: RPCID, tag: Int, reason: Reason, envelopeType: Envelope.EnvelopeType) async throws { + public func respondError(topic: String, requestId: RPCID, protocolMethod: ProtocolMethod, reason: Reason, envelopeType: Envelope.EnvelopeType) async throws { lastErrorCode = reason.code didRespondError = true } diff --git a/Tests/WalletConnectSignTests/ApproveEngineTests.swift b/Tests/WalletConnectSignTests/ApproveEngineTests.swift index 3f2c39da8..4d0b75a7b 100644 --- a/Tests/WalletConnectSignTests/ApproveEngineTests.swift +++ b/Tests/WalletConnectSignTests/ApproveEngineTests.swift @@ -54,7 +54,7 @@ final class ApproveEngineTests: XCTestCase { pairingStorageMock.setPairing(pairing) let proposerPubKey = AgreementPrivateKey().publicKey.hexRepresentation let proposal = SessionProposal.stub(proposerPubKey: proposerPubKey) - let request = RPCRequest(method: SignProtocolMethod.sessionPropose.method, params: proposal) + let request = RPCRequest(method: SessionProposeProtocolMethod().method, params: proposal) networkingInteractor.requestPublisherSubject.send((topicA, request)) try await engine.approveProposal(proposerPubKey: proposal.proposer.publicKey, validating: SessionNamespace.stubDictionary()) @@ -75,7 +75,7 @@ final class ApproveEngineTests: XCTestCase { var sessionProposed = false let proposerPubKey = AgreementPrivateKey().publicKey.hexRepresentation let proposal = SessionProposal.stub(proposerPubKey: proposerPubKey) - let request = RPCRequest(method: SignProtocolMethod.sessionPropose.method, params: proposal) + let request = RPCRequest(method: SessionProposeProtocolMethod().method, params: proposal) engine.onSessionProposal = { _ in sessionProposed = true @@ -119,7 +119,7 @@ final class ApproveEngineTests: XCTestCase { let session = WCSession.stub(isSelfController: true, acknowledged: false) sessionStorageMock.setSession(session) - let request = RPCRequest(method: SignProtocolMethod.sessionSettle.method, params: SessionType.SettleParams.stub()) + let request = RPCRequest(method: SessionSettleProtocolMethod().method, params: SessionType.SettleParams.stub()) let response = RPCResponse(matchingRequest: request, result: RPCResult.response(AnyCodable(true))) networkingInteractor.responsePublisherSubject.send((session.topic, request, response)) @@ -134,7 +134,7 @@ final class ApproveEngineTests: XCTestCase { cryptoMock.setAgreementSecret(AgreementKeys.stub(), topic: session.topic) try! cryptoMock.setPrivateKey(privateKey) - let request = RPCRequest(method: SignProtocolMethod.sessionSettle.method, params: SessionType.SettleParams.stub()) + let request = RPCRequest(method: SessionSettleProtocolMethod().method, params: SessionType.SettleParams.stub()) let response = RPCResponse.stubError(forRequest: request) networkingInteractor.responsePublisherSubject.send((session.topic, request, response)) diff --git a/Tests/WalletConnectSignTests/Stub/Stubs.swift b/Tests/WalletConnectSignTests/Stub/Stubs.swift index f0b87a080..0a15c18d5 100644 --- a/Tests/WalletConnectSignTests/Stub/Stubs.swift +++ b/Tests/WalletConnectSignTests/Stub/Stubs.swift @@ -63,22 +63,22 @@ extension AgreementPeer { extension RPCRequest { static func stubUpdateNamespaces(namespaces: [String: SessionNamespace] = SessionNamespace.stubDictionary()) -> RPCRequest { - return RPCRequest(method: SignProtocolMethod.sessionUpdate.method, params: SessionType.UpdateParams(namespaces: namespaces)) + return RPCRequest(method: SessionUpdateProtocolMethod().method, params: SessionType.UpdateParams(namespaces: namespaces)) } static func stubUpdateExpiry(expiry: Int64) -> RPCRequest { - return RPCRequest(method: SignProtocolMethod.sessionExtend.method, params: SessionType.UpdateExpiryParams(expiry: expiry)) + return RPCRequest(method: SessionExtendProtocolMethod().method, params: SessionType.UpdateExpiryParams(expiry: expiry)) } static func stubSettle() -> RPCRequest { - return RPCRequest(method: SignProtocolMethod.sessionSettle.method, params: SessionType.SettleParams.stub()) + return RPCRequest(method: SessionSettleProtocolMethod().method, params: SessionType.SettleParams.stub()) } static func stubRequest(method: String, chainId: Blockchain) -> RPCRequest { let params = SessionType.RequestParams( request: SessionType.RequestParams.Request(method: method, params: AnyCodable(EmptyCodable())), chainId: chainId) - return RPCRequest(method: SignProtocolMethod.sessionRequest.method, params: params) + return RPCRequest(method: SessionRequestProtocolMethod().method, params: params) } } From 34728a239579f12fa1d12016fad91cff834c2928 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Tue, 27 Sep 2022 08:59:06 +0600 Subject: [PATCH 065/175] Cache key updated --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bcbcf09cf..7218c2e65 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,9 +39,9 @@ jobs: path: | .build SourcePackagesCache - key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }} + key: ${{ runner.os }}-spm1-${{ hashFiles('**/Package.resolved') }} restore-keys: | - ${{ runner.os }}-spm- + ${{ runner.os }}-spm1- - uses: ./.github/actions/ci with: @@ -65,9 +65,9 @@ jobs: path: | .build SourcePackagesCache - key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }} + key: ${{ runner.os }}-spm1-${{ hashFiles('**/Package.resolved') }} restore-keys: | - ${{ runner.os }}-spm- + ${{ runner.os }}-spm1- - uses: ./.github/actions/ci with: From cc80680d1d096fbc19adb4dcd7aa48f36d4bb9dd Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Tue, 27 Sep 2022 09:09:44 +0600 Subject: [PATCH 066/175] Resolve Dependencies after cache restore --- .github/workflows/ci.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7218c2e65..fe608f08c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,12 +28,6 @@ jobs: - name: Setup Xcode Version uses: maxim-lobanov/setup-xcode@v1 - - name: Resolve Dependencies - shell: bash - run: " - xcodebuild -resolvePackageDependencies -project Example/ExampleApp.xcodeproj -scheme DApp -clonedSourcePackagesDirPath SourcePackagesCache; \ - xcodebuild -resolvePackageDependencies -project Example/ExampleApp.xcodeproj -scheme WalletConnect -clonedSourcePackagesDirPath SourcePackagesCache" - - uses: actions/cache@v2 with: path: | @@ -43,6 +37,12 @@ jobs: restore-keys: | ${{ runner.os }}-spm1- + - name: Resolve Dependencies + shell: bash + run: " + xcodebuild -resolvePackageDependencies -project Example/ExampleApp.xcodeproj -scheme DApp -clonedSourcePackagesDirPath SourcePackagesCache; \ + xcodebuild -resolvePackageDependencies -project Example/ExampleApp.xcodeproj -scheme WalletConnect -clonedSourcePackagesDirPath SourcePackagesCache" + - uses: ./.github/actions/ci with: type: ${{ matrix.test-type }} From 272e0051b3351bbdb24a42421c9d8e0a16db165c Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Tue, 27 Sep 2022 09:22:44 +0600 Subject: [PATCH 067/175] Update chache id --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fe608f08c..663d41e88 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,9 +33,9 @@ jobs: path: | .build SourcePackagesCache - key: ${{ runner.os }}-spm1-${{ hashFiles('**/Package.resolved') }} + key: ${{ runner.os }}-spm2-${{ hashFiles('**/Package.resolved') }} restore-keys: | - ${{ runner.os }}-spm1- + ${{ runner.os }}-spm2- - name: Resolve Dependencies shell: bash @@ -65,9 +65,9 @@ jobs: path: | .build SourcePackagesCache - key: ${{ runner.os }}-spm1-${{ hashFiles('**/Package.resolved') }} + key: ${{ runner.os }}-spm2-${{ hashFiles('**/Package.resolved') }} restore-keys: | - ${{ runner.os }}-spm1- + ${{ runner.os }}-spm2- - uses: ./.github/actions/ci with: From f58a867a39213cf0ccab22844c5078ad2503a38f Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Tue, 27 Sep 2022 09:43:08 +0600 Subject: [PATCH 068/175] testHandleSessionProposeResponse stability --- Tests/TestingUtils/NetworkingInteractorMock.swift | 3 +++ Tests/WalletConnectSignTests/PairingEngineTests.swift | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/Tests/TestingUtils/NetworkingInteractorMock.swift b/Tests/TestingUtils/NetworkingInteractorMock.swift index 0ba467bab..b37b3d712 100644 --- a/Tests/TestingUtils/NetworkingInteractorMock.swift +++ b/Tests/TestingUtils/NetworkingInteractorMock.swift @@ -22,6 +22,8 @@ public class NetworkingInteractorMock: NetworkInteracting { private(set) var requestCallCount = 0 var didCallRequest: Bool { requestCallCount > 0 } + var onSubscribeCalled: (() -> Void)? + public let socketConnectionStatusPublisherSubject = PassthroughSubject() public var socketConnectionStatusPublisher: AnyPublisher { socketConnectionStatusPublisherSubject.eraseToAnyPublisher() @@ -79,6 +81,7 @@ public class NetworkingInteractorMock: NetworkInteracting { } public func subscribe(topic: String) async throws { + defer { onSubscribeCalled?() } subscriptions.append(topic) didCallSubscribe = true } diff --git a/Tests/WalletConnectSignTests/PairingEngineTests.swift b/Tests/WalletConnectSignTests/PairingEngineTests.swift index 6bd6538cc..0f99d16d0 100644 --- a/Tests/WalletConnectSignTests/PairingEngineTests.swift +++ b/Tests/WalletConnectSignTests/PairingEngineTests.swift @@ -87,6 +87,7 @@ final class PairingEngineTests: XCTestCase { } func testHandleSessionProposeResponse() async { + let exp = expectation(description: "testHandleSessionProposeResponse") let uri = try! await engine.create() let pairing = storageMock.getPairing(forTopic: uri.topic)! let topicA = pairing.topic @@ -107,10 +108,17 @@ final class PairingEngineTests: XCTestCase { let response = RPCResponse(id: request.id!, result: RPCResult.response(AnyCodable(proposalResponse))) + networkingInteractor.onSubscribeCalled = { + exp.fulfill() + } + networkingInteractor.responsePublisherSubject.send((topicA, request, response)) let privateKey = try! cryptoMock.getPrivateKey(for: proposal.proposer.publicKey)! let topicB = deriveTopic(publicKey: responder.publicKey, privateKey: privateKey) let storedPairing = storageMock.getPairing(forTopic: topicA)! + + wait(for: [exp], timeout: 5) + let sessionTopic = networkingInteractor.subscriptions.last! XCTAssertTrue(networkingInteractor.didCallSubscribe) From ba9019369a39da4c17dea224ed0381613d074b67 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Tue, 27 Sep 2022 09:44:03 +0600 Subject: [PATCH 069/175] Update cache id --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 663d41e88..5baea21a0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,9 +33,9 @@ jobs: path: | .build SourcePackagesCache - key: ${{ runner.os }}-spm2-${{ hashFiles('**/Package.resolved') }} + key: ${{ runner.os }}-spm3-${{ hashFiles('**/Package.resolved') }} restore-keys: | - ${{ runner.os }}-spm2- + ${{ runner.os }}-spm3- - name: Resolve Dependencies shell: bash @@ -65,9 +65,9 @@ jobs: path: | .build SourcePackagesCache - key: ${{ runner.os }}-spm2-${{ hashFiles('**/Package.resolved') }} + key: ${{ runner.os }}-spm3-${{ hashFiles('**/Package.resolved') }} restore-keys: | - ${{ runner.os }}-spm2- + ${{ runner.os }}-spm3- - uses: ./.github/actions/ci with: From 79e7adcc1602b02a79635e4dd511f5b10ed25b39 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Tue, 27 Sep 2022 13:50:48 +0600 Subject: [PATCH 070/175] Default cache id --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5baea21a0..a81d10cb4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,9 +33,9 @@ jobs: path: | .build SourcePackagesCache - key: ${{ runner.os }}-spm3-${{ hashFiles('**/Package.resolved') }} + key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }} restore-keys: | - ${{ runner.os }}-spm3- + ${{ runner.os }}-spm- - name: Resolve Dependencies shell: bash @@ -65,9 +65,9 @@ jobs: path: | .build SourcePackagesCache - key: ${{ runner.os }}-spm3-${{ hashFiles('**/Package.resolved') }} + key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }} restore-keys: | - ${{ runner.os }}-spm3- + ${{ runner.os }}-spm- - uses: ./.github/actions/ci with: From 85cb411b3bf5e8017621fae1ea4d4116b2407ab8 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Tue, 27 Sep 2022 14:57:25 +0600 Subject: [PATCH 071/175] Combine timeout for dispatcher logic --- Example/IntegrationTests/Auth/AuthTests.swift | 17 ------------ Example/IntegrationTests/Chat/ChatTests.swift | 21 --------------- .../Sign/SignClientTests.swift | 15 ----------- Sources/WalletConnectRelay/Dispatching.swift | 27 ++++++++++++------- 4 files changed, 17 insertions(+), 63 deletions(-) diff --git a/Example/IntegrationTests/Auth/AuthTests.swift b/Example/IntegrationTests/Auth/AuthTests.swift index 6abe13983..4ee2bf33d 100644 --- a/Example/IntegrationTests/Auth/AuthTests.swift +++ b/Example/IntegrationTests/Auth/AuthTests.swift @@ -16,23 +16,6 @@ final class AuthTests: XCTestCase { app = makeClient(prefix: "👻 App") let walletAccount = Account(chainIdentifier: "eip155:1", address: "0x724d0D2DaD3fbB0C168f947B87Fa5DBe36F1A8bf")! wallet = makeClient(prefix: "🤑 Wallet", account: walletAccount) - - let expectation = expectation(description: "Wait Clients Connected") - expectation.expectedFulfillmentCount = 2 - - app.socketConnectionStatusPublisher.sink { status in - if status == .connected { - expectation.fulfill() - } - }.store(in: &publishers) - - wallet.socketConnectionStatusPublisher.sink { status in - if status == .connected { - expectation.fulfill() - } - }.store(in: &publishers) - - wait(for: [expectation], timeout: 5) } func makeClient(prefix: String, account: Account? = nil) -> AuthClient { diff --git a/Example/IntegrationTests/Chat/ChatTests.swift b/Example/IntegrationTests/Chat/ChatTests.swift index 191ea398f..c4863b8af 100644 --- a/Example/IntegrationTests/Chat/ChatTests.swift +++ b/Example/IntegrationTests/Chat/ChatTests.swift @@ -16,27 +16,6 @@ final class ChatTests: XCTestCase { registry = KeyValueRegistry() invitee = makeClient(prefix: "🦖 Registered") inviter = makeClient(prefix: "🍄 Inviter") - - waitClientsConnected() - } - - private func waitClientsConnected() { - let expectation = expectation(description: "Wait Clients Connected") - expectation.expectedFulfillmentCount = 2 - - invitee.socketConnectionStatusPublisher.sink { status in - if status == .connected { - expectation.fulfill() - } - }.store(in: &publishers) - - inviter.socketConnectionStatusPublisher.sink { status in - if status == .connected { - expectation.fulfill() - } - }.store(in: &publishers) - - wait(for: [expectation], timeout: 5) } func makeClient(prefix: String) -> ChatClient { diff --git a/Example/IntegrationTests/Sign/SignClientTests.swift b/Example/IntegrationTests/Sign/SignClientTests.swift index a62321a05..20bd91504 100644 --- a/Example/IntegrationTests/Sign/SignClientTests.swift +++ b/Example/IntegrationTests/Sign/SignClientTests.swift @@ -37,24 +37,9 @@ final class SignClientTests: XCTestCase { return ClientDelegate(client: client) } - private func listenForConnection() async { - let group = DispatchGroup() - group.enter() - dapp.onConnected = { - group.leave() - } - group.enter() - wallet.onConnected = { - group.leave() - } - group.wait() - return - } - override func setUp() async throws { dapp = Self.makeClientDelegate(name: "🍏P") wallet = Self.makeClientDelegate(name: "🍎R") - await listenForConnection() } override func tearDown() { diff --git a/Sources/WalletConnectRelay/Dispatching.swift b/Sources/WalletConnectRelay/Dispatching.swift index 6df8a56f4..cd08efe12 100644 --- a/Sources/WalletConnectRelay/Dispatching.swift +++ b/Sources/WalletConnectRelay/Dispatching.swift @@ -26,6 +26,8 @@ final class Dispatcher: NSObject, Dispatching { socketConnectionStatusPublisherSubject.eraseToAnyPublisher() } + private let concurrentQueue = DispatchQueue(label: "com.walletconnect.sdk.dispatcher", attributes: .concurrent) + init(socket: WebSocketConnecting, socketConnectionHandler: SocketConnectionHandler, logger: ConsoleLogging) { @@ -53,16 +55,21 @@ final class Dispatcher: NSObject, Dispatching { } var cancellable: AnyCancellable? - cancellable = socketConnectionStatusPublisher.sink { [unowned self] status in - guard status == .connected else { return } - cancellable?.cancel() - send(string, completion: completion) - } - - DispatchQueue.global().asyncAfter(deadline: .now() + .seconds(defaultTimeout)) { - completion(NetworkError.webSocketNotConnected) - cancellable?.cancel() - } + cancellable = socketConnectionStatusPublisher + .filter { $0 == .connected } + .setFailureType(to: NetworkError.self) + .timeout(.seconds(defaultTimeout), scheduler: concurrentQueue, customError: { .webSocketNotConnected }) + .sink(receiveCompletion: { result in + switch result { + case .failure(let error): + cancellable?.cancel() + completion(error) + case .finished: break + } + }, receiveValue: { [unowned self] status in + cancellable?.cancel() + send(string, completion: completion) + }) } func protectedSend(_ string: String) async throws { From ac90e6f97a819f058b88d4e45afcccd39b568124 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 27 Sep 2022 11:03:41 +0200 Subject: [PATCH 072/175] change vars for lets in protocol methods --- Sources/Auth/AuthProtocolMethod.swift | 18 +++++++++--------- Sources/Chat/Types/ChatProtocolMethod.swift | 12 ++++++------ .../PairingProtocolMethod.swift | 6 +++--- .../Engine/Common/SessionEngine.swift | 4 ++-- .../PairingProtocolMethods.swift | 12 ++++++------ .../SessionDeleteProtocolMethod.swift | 6 +++--- .../SessionEventProtocolMethod.swift | 6 +++--- .../SessionExtendProtocolMethod.swift | 6 +++--- .../SessionPingProtocolMethod.swift | 6 +++--- .../SessionProposeProtocolMethod.swift | 6 +++--- .../SessionRequestProtocolMethod.swift | 6 +++--- .../SessionSettleProtocolMethod.swift | 6 +++--- .../SessionUpdateProtocolMethod.swift | 6 +++--- 13 files changed, 50 insertions(+), 50 deletions(-) diff --git a/Sources/Auth/AuthProtocolMethod.swift b/Sources/Auth/AuthProtocolMethod.swift index e54889755..02d53d559 100644 --- a/Sources/Auth/AuthProtocolMethod.swift +++ b/Sources/Auth/AuthProtocolMethod.swift @@ -2,27 +2,27 @@ import Foundation import WalletConnectNetworking struct AuthRequestProtocolMethod: ProtocolMethod { - var method: String = "wc_authRequest" + let method: String = "wc_authRequest" - var requestConfig = RelayConfig(tag: 3000, prompt: true, ttl: 86400) + let requestConfig = RelayConfig(tag: 3000, prompt: true, ttl: 86400) - var responseConfig = RelayConfig(tag: 3001, prompt: false, ttl: 86400) + let responseConfig = RelayConfig(tag: 3001, prompt: false, ttl: 86400) } struct PairingPingProtocolMethod: ProtocolMethod { - var method: String = "wc_pairingPing" + let method: String = "wc_pairingPing" - var requestConfig = RelayConfig(tag: 1002, prompt: false, ttl: 30) + let requestConfig = RelayConfig(tag: 1002, prompt: false, ttl: 30) - var responseConfig = RelayConfig(tag: 1003, prompt: false, ttl: 30) + let responseConfig = RelayConfig(tag: 1003, prompt: false, ttl: 30) } struct PairingDeleteProtocolMethod: ProtocolMethod { - var method: String = "wc_pairingDelete" + let method: String = "wc_pairingDelete" - var requestConfig = RelayConfig(tag: 1000, prompt: false, ttl: 86400) + let requestConfig = RelayConfig(tag: 1000, prompt: false, ttl: 86400) - var responseConfig = RelayConfig(tag: 1001, prompt: false, ttl: 86400) + let responseConfig = RelayConfig(tag: 1001, prompt: false, ttl: 86400) } diff --git a/Sources/Chat/Types/ChatProtocolMethod.swift b/Sources/Chat/Types/ChatProtocolMethod.swift index 82dcd15dc..f24a5f971 100644 --- a/Sources/Chat/Types/ChatProtocolMethod.swift +++ b/Sources/Chat/Types/ChatProtocolMethod.swift @@ -2,19 +2,19 @@ import Foundation import WalletConnectNetworking struct ChatInviteProtocolMethod: ProtocolMethod { - var method: String = "wc_chatInvite" + let method: String = "wc_chatInvite" - var requestConfig = RelayConfig(tag: 2000, prompt: true, ttl: 86400) + let requestConfig = RelayConfig(tag: 2000, prompt: true, ttl: 86400) - var responseConfig = RelayConfig(tag: 2001, prompt: false, ttl: 86400) + let responseConfig = RelayConfig(tag: 2001, prompt: false, ttl: 86400) } struct ChatMessageProtocolMethod: ProtocolMethod { - var method: String = "wc_chatMessage" + let method: String = "wc_chatMessage" - var requestConfig = RelayConfig(tag: 2002, prompt: true, ttl: 86400) + let requestConfig = RelayConfig(tag: 2002, prompt: true, ttl: 86400) - var responseConfig = RelayConfig(tag: 2003, prompt: false, ttl: 86400) + let responseConfig = RelayConfig(tag: 2003, prompt: false, ttl: 86400) } diff --git a/Sources/WalletConnectPairing/PairingProtocolMethod.swift b/Sources/WalletConnectPairing/PairingProtocolMethod.swift index 2cd6ac361..10d39127f 100644 --- a/Sources/WalletConnectPairing/PairingProtocolMethod.swift +++ b/Sources/WalletConnectPairing/PairingProtocolMethod.swift @@ -2,9 +2,9 @@ import Foundation import WalletConnectNetworking struct PairingPingProtocolMethod: ProtocolMethod { - var method: String = "wc_pairingPing" + let method: String = "wc_pairingPing" - var requestConfig = RelayConfig(tag: 1002, prompt: false, ttl: 30) + let requestConfig = RelayConfig(tag: 1002, prompt: false, ttl: 30) - var responseConfig = RelayConfig(tag: 1003, prompt: false, ttl: 30) + let responseConfig = RelayConfig(tag: 1003, prompt: false, ttl: 30) } diff --git a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift index 2ca824ff1..2a2ca7d19 100644 --- a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift @@ -58,8 +58,8 @@ final class SessionEngine { } let chainRequest = SessionType.RequestParams.Request(method: request.method, params: request.params) let sessionRequestParams = SessionType.RequestParams(request: chainRequest, chainId: request.chainId) - - let rpcRequest = RPCRequest(method: SessionRequestProtocolMethod().method, params: sessionRequestParams) + let protocolMethod = SessionRequestProtocolMethod() + let rpcRequest = RPCRequest(method: protocolMethod.method, params: sessionRequestParams) try await networkingInteractor.request(rpcRequest, topic: request.topic, protocolMethod: SessionRequestProtocolMethod()) } diff --git a/Sources/WalletConnectSign/Types/ProtocolMethods/PairingProtocolMethods.swift b/Sources/WalletConnectSign/Types/ProtocolMethods/PairingProtocolMethods.swift index cfd3bd53c..19167c4b5 100644 --- a/Sources/WalletConnectSign/Types/ProtocolMethods/PairingProtocolMethods.swift +++ b/Sources/WalletConnectSign/Types/ProtocolMethods/PairingProtocolMethods.swift @@ -2,17 +2,17 @@ import Foundation import WalletConnectNetworking struct PairingPingProtocolMethod: ProtocolMethod { - var method: String = "wc_pairingPing" + let method: String = "wc_pairingPing" - var requestConfig = RelayConfig(tag: 1002, prompt: false, ttl: 30) + let requestConfig = RelayConfig(tag: 1002, prompt: false, ttl: 30) - var responseConfig = RelayConfig(tag: 1003, prompt: false, ttl: 30) + let responseConfig = RelayConfig(tag: 1003, prompt: false, ttl: 30) } struct PairingDeleteProtocolMethod: ProtocolMethod { - var method: String = "wc_pairingDelete" + let method: String = "wc_pairingDelete" - var requestConfig = RelayConfig(tag: 1000, prompt: false, ttl: 86400) + let requestConfig = RelayConfig(tag: 1000, prompt: false, ttl: 86400) - var responseConfig = RelayConfig(tag: 1001, prompt: false, ttl: 86400) + let responseConfig = RelayConfig(tag: 1001, prompt: false, ttl: 86400) } diff --git a/Sources/WalletConnectSign/Types/ProtocolMethods/SessionDeleteProtocolMethod.swift b/Sources/WalletConnectSign/Types/ProtocolMethods/SessionDeleteProtocolMethod.swift index e1b487aa8..b9d7fc9f3 100644 --- a/Sources/WalletConnectSign/Types/ProtocolMethods/SessionDeleteProtocolMethod.swift +++ b/Sources/WalletConnectSign/Types/ProtocolMethods/SessionDeleteProtocolMethod.swift @@ -2,10 +2,10 @@ import Foundation import WalletConnectNetworking struct SessionDeleteProtocolMethod: ProtocolMethod { - var method: String = "wc_sessionDelete" + let method: String = "wc_sessionDelete" - var requestConfig = RelayConfig(tag: 1112, prompt: false, ttl: 86400) + let requestConfig = RelayConfig(tag: 1112, prompt: false, ttl: 86400) - var responseConfig = RelayConfig(tag: 1113, prompt: false, ttl: 86400) + let responseConfig = RelayConfig(tag: 1113, prompt: false, ttl: 86400) } diff --git a/Sources/WalletConnectSign/Types/ProtocolMethods/SessionEventProtocolMethod.swift b/Sources/WalletConnectSign/Types/ProtocolMethods/SessionEventProtocolMethod.swift index 585796e3f..bee208ed4 100644 --- a/Sources/WalletConnectSign/Types/ProtocolMethods/SessionEventProtocolMethod.swift +++ b/Sources/WalletConnectSign/Types/ProtocolMethods/SessionEventProtocolMethod.swift @@ -2,9 +2,9 @@ import Foundation import WalletConnectNetworking struct SessionEventProtocolMethod: ProtocolMethod { - var method: String = "wc_sessionEvent" + let method: String = "wc_sessionEvent" - var requestConfig = RelayConfig(tag: 1110, prompt: true, ttl: 300) + let requestConfig = RelayConfig(tag: 1110, prompt: true, ttl: 300) - var responseConfig = RelayConfig(tag: 1111, prompt: false, ttl: 300) + let responseConfig = RelayConfig(tag: 1111, prompt: false, ttl: 300) } diff --git a/Sources/WalletConnectSign/Types/ProtocolMethods/SessionExtendProtocolMethod.swift b/Sources/WalletConnectSign/Types/ProtocolMethods/SessionExtendProtocolMethod.swift index 94425014c..b77e5b9ff 100644 --- a/Sources/WalletConnectSign/Types/ProtocolMethods/SessionExtendProtocolMethod.swift +++ b/Sources/WalletConnectSign/Types/ProtocolMethods/SessionExtendProtocolMethod.swift @@ -2,9 +2,9 @@ import Foundation import WalletConnectNetworking struct SessionExtendProtocolMethod: ProtocolMethod { - var method: String = "wc_sessionExtend" + let method: String = "wc_sessionExtend" - var requestConfig = RelayConfig(tag: 1106, prompt: false, ttl: 86400) + let requestConfig = RelayConfig(tag: 1106, prompt: false, ttl: 86400) - var responseConfig = RelayConfig(tag: 1107, prompt: false, ttl: 86400) + let responseConfig = RelayConfig(tag: 1107, prompt: false, ttl: 86400) } diff --git a/Sources/WalletConnectSign/Types/ProtocolMethods/SessionPingProtocolMethod.swift b/Sources/WalletConnectSign/Types/ProtocolMethods/SessionPingProtocolMethod.swift index 9be7feea0..109ff0870 100644 --- a/Sources/WalletConnectSign/Types/ProtocolMethods/SessionPingProtocolMethod.swift +++ b/Sources/WalletConnectSign/Types/ProtocolMethods/SessionPingProtocolMethod.swift @@ -2,10 +2,10 @@ import Foundation import WalletConnectNetworking struct SessionPingProtocolMethod: ProtocolMethod { - var method: String = "wc_sessionPing" + let method: String = "wc_sessionPing" - var requestConfig = RelayConfig(tag: 1114, prompt: false, ttl: 30) + let requestConfig = RelayConfig(tag: 1114, prompt: false, ttl: 30) - var responseConfig = RelayConfig(tag: 1115, prompt: false, ttl: 30) + let responseConfig = RelayConfig(tag: 1115, prompt: false, ttl: 30) } diff --git a/Sources/WalletConnectSign/Types/ProtocolMethods/SessionProposeProtocolMethod.swift b/Sources/WalletConnectSign/Types/ProtocolMethods/SessionProposeProtocolMethod.swift index 017325d99..47191c4a2 100644 --- a/Sources/WalletConnectSign/Types/ProtocolMethods/SessionProposeProtocolMethod.swift +++ b/Sources/WalletConnectSign/Types/ProtocolMethods/SessionProposeProtocolMethod.swift @@ -2,9 +2,9 @@ import Foundation import WalletConnectNetworking struct SessionProposeProtocolMethod: ProtocolMethod { - var method: String = "wc_sessionPropose" + let method: String = "wc_sessionPropose" - var requestConfig = RelayConfig(tag: 1100, prompt: true, ttl: 300) + let requestConfig = RelayConfig(tag: 1100, prompt: true, ttl: 300) - var responseConfig = RelayConfig(tag: 1101, prompt: false, ttl: 300) + let responseConfig = RelayConfig(tag: 1101, prompt: false, ttl: 300) } diff --git a/Sources/WalletConnectSign/Types/ProtocolMethods/SessionRequestProtocolMethod.swift b/Sources/WalletConnectSign/Types/ProtocolMethods/SessionRequestProtocolMethod.swift index 90047d521..e2c0e4234 100644 --- a/Sources/WalletConnectSign/Types/ProtocolMethods/SessionRequestProtocolMethod.swift +++ b/Sources/WalletConnectSign/Types/ProtocolMethods/SessionRequestProtocolMethod.swift @@ -2,9 +2,9 @@ import Foundation import WalletConnectNetworking struct SessionRequestProtocolMethod: ProtocolMethod { - var method: String = "wc_sessionRequest" + let method: String = "wc_sessionRequest" - var requestConfig = RelayConfig(tag: 1108, prompt: true, ttl: 300) + let requestConfig = RelayConfig(tag: 1108, prompt: true, ttl: 300) - var responseConfig = RelayConfig(tag: 1109, prompt: false, ttl: 300) + let responseConfig = RelayConfig(tag: 1109, prompt: false, ttl: 300) } diff --git a/Sources/WalletConnectSign/Types/ProtocolMethods/SessionSettleProtocolMethod.swift b/Sources/WalletConnectSign/Types/ProtocolMethods/SessionSettleProtocolMethod.swift index 7a0341c1f..131560b4c 100644 --- a/Sources/WalletConnectSign/Types/ProtocolMethods/SessionSettleProtocolMethod.swift +++ b/Sources/WalletConnectSign/Types/ProtocolMethods/SessionSettleProtocolMethod.swift @@ -2,9 +2,9 @@ import Foundation import WalletConnectNetworking struct SessionSettleProtocolMethod: ProtocolMethod { - var method: String = "wc_sessionSettle" + let method: String = "wc_sessionSettle" - var requestConfig = RelayConfig(tag: 1102, prompt: false, ttl: 300) + let requestConfig = RelayConfig(tag: 1102, prompt: false, ttl: 300) - var responseConfig = RelayConfig(tag: 1103, prompt: false, ttl: 300) + let responseConfig = RelayConfig(tag: 1103, prompt: false, ttl: 300) } diff --git a/Sources/WalletConnectSign/Types/ProtocolMethods/SessionUpdateProtocolMethod.swift b/Sources/WalletConnectSign/Types/ProtocolMethods/SessionUpdateProtocolMethod.swift index 7ff5eb754..859bddbd8 100644 --- a/Sources/WalletConnectSign/Types/ProtocolMethods/SessionUpdateProtocolMethod.swift +++ b/Sources/WalletConnectSign/Types/ProtocolMethods/SessionUpdateProtocolMethod.swift @@ -2,10 +2,10 @@ import Foundation import WalletConnectNetworking struct SessionUpdateProtocolMethod: ProtocolMethod { - var method: String = "wc_sessionUpdate" + let method: String = "wc_sessionUpdate" - var requestConfig = RelayConfig(tag: 1104, prompt: false, ttl: 86400) + let requestConfig = RelayConfig(tag: 1104, prompt: false, ttl: 86400) - var responseConfig = RelayConfig(tag: 1105, prompt: false, ttl: 86400) + let responseConfig = RelayConfig(tag: 1105, prompt: false, ttl: 86400) } From 81c4fd71e7e4484f8c45f561d6654af15e59e8e8 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 27 Sep 2022 12:48:00 +0200 Subject: [PATCH 073/175] remove wait clients connected, refactor push protocol method --- .../Pairing/PairingTests.swift | 21 ++------------- .../PairingRequester.swift | 5 ++-- .../PairingRequestsSubscriber.swift | 2 +- .../Push/PushClient.swift | 8 +++--- .../Push/PushProposeProtocolMethod.swift | 12 +++++++++ .../Push/PushProtocolMethod.swift | 26 ------------------- 6 files changed, 23 insertions(+), 51 deletions(-) create mode 100644 Sources/WalletConnectPairing/Push/PushProposeProtocolMethod.swift delete mode 100644 Sources/WalletConnectPairing/Push/PushProtocolMethod.swift diff --git a/Example/IntegrationTests/Pairing/PairingTests.swift b/Example/IntegrationTests/Pairing/PairingTests.swift index 5a581a215..01085df49 100644 --- a/Example/IntegrationTests/Pairing/PairingTests.swift +++ b/Example/IntegrationTests/Pairing/PairingTests.swift @@ -20,25 +20,8 @@ final class PairingTests: XCTestCase { private var publishers = [AnyCancellable]() override func setUp() { - (appPairingClient, appPushClient) = makeClients(prefix: "👻 App") - (walletPairingClient, walletPushClient) = makeClients(prefix: "🤑 Wallet") - - let expectation = expectation(description: "Wait Clients Connected") - expectation.expectedFulfillmentCount = 2 - - appPairingClient.socketConnectionStatusPublisher.sink { status in - if status == .connected { - expectation.fulfill() - } - }.store(in: &publishers) - - walletPairingClient.socketConnectionStatusPublisher.sink { status in - if status == .connected { - expectation.fulfill() - } - }.store(in: &publishers) - - wait(for: [expectation], timeout: 5) + (appPairingClient, appPushClient) = makeClients(prefix: "🤖 App") + (walletPairingClient, walletPushClient) = makeClients(prefix: "🐶 Wallet") } func makeClients(prefix: String) -> (PairingClient, PushClient) { diff --git a/Sources/WalletConnectPairing/PairingRequester.swift b/Sources/WalletConnectPairing/PairingRequester.swift index bdc89d2dc..535f9eb34 100644 --- a/Sources/WalletConnectPairing/PairingRequester.swift +++ b/Sources/WalletConnectPairing/PairingRequester.swift @@ -20,7 +20,8 @@ public class PushProposer { } func request(topic: String, params: AnyCodable) async throws { - let request = RPCRequest(method: PushProtocolMethod.propose.method, params: params) - try await networkingInteractor.requestNetworkAck(request, topic: topic, tag: PushProtocolMethod.propose.requestTag) + let protocolMethod = PushProposeProtocolMethod() + let request = RPCRequest(method: protocolMethod.method, params: params) + try await networkingInteractor.requestNetworkAck(request, topic: topic, protocolMethod: protocolMethod) } } diff --git a/Sources/WalletConnectPairing/PairingRequestsSubscriber.swift b/Sources/WalletConnectPairing/PairingRequestsSubscriber.swift index f997b886f..ed3ede277 100644 --- a/Sources/WalletConnectPairing/PairingRequestsSubscriber.swift +++ b/Sources/WalletConnectPairing/PairingRequestsSubscriber.swift @@ -27,7 +27,7 @@ public class PairingRequestsSubscriber { .sink { [unowned self] topic, request in Task(priority: .high) { // TODO - spec tag - try await networkingInteractor.respondError(topic: topic, requestId: request.id!, tag: 123456, reason: PairError.methodUnsupported) + try await networkingInteractor.respondError(topic: topic, requestId: request.id!, protocolMethod: protocolMethod, reason: PairError.methodUnsupported) } }.store(in: &publishers) diff --git a/Sources/WalletConnectPairing/Push/PushClient.swift b/Sources/WalletConnectPairing/Push/PushClient.swift index 4739593e5..5dee1b892 100644 --- a/Sources/WalletConnectPairing/Push/PushClient.swift +++ b/Sources/WalletConnectPairing/Push/PushClient.swift @@ -42,14 +42,16 @@ public class PushClient { private extension PushClient { func setupPairingSubscriptions() { - pairingRegisterer.register(method: PushProtocolMethod.propose) + let protocolMethod = PushProposeProtocolMethod() - networkInteractor.responseErrorSubscription(on: PushProtocolMethod.propose) + pairingRegisterer.register(method: protocolMethod) + + networkInteractor.responseErrorSubscription(on: protocolMethod) .sink { [unowned self] (payload: ResponseSubscriptionErrorPayload) in logger.error(payload.error.localizedDescription) }.store(in: &publishers) - networkInteractor.requestSubscription(on: PushProtocolMethod.propose) + networkInteractor.requestSubscription(on: protocolMethod) .sink { [unowned self] (payload: RequestSubscriptionPayload) in requestPublisherSubject.send((payload.topic, payload.request)) }.store(in: &publishers) diff --git a/Sources/WalletConnectPairing/Push/PushProposeProtocolMethod.swift b/Sources/WalletConnectPairing/Push/PushProposeProtocolMethod.swift new file mode 100644 index 000000000..1c5e62906 --- /dev/null +++ b/Sources/WalletConnectPairing/Push/PushProposeProtocolMethod.swift @@ -0,0 +1,12 @@ +import Foundation +import WalletConnectNetworking + +struct PushProposeProtocolMethod: ProtocolMethod { + let method: String = "wc_pushPropose" + + let requestConfig: RelayConfig = RelayConfig(tag: 111, prompt: true, ttl: 300) + + let responseConfig: RelayConfig = RelayConfig(tag: 112, prompt: true, ttl: 300) +} + +struct PushRequestParams: Codable {} diff --git a/Sources/WalletConnectPairing/Push/PushProtocolMethod.swift b/Sources/WalletConnectPairing/Push/PushProtocolMethod.swift deleted file mode 100644 index 0b6772da0..000000000 --- a/Sources/WalletConnectPairing/Push/PushProtocolMethod.swift +++ /dev/null @@ -1,26 +0,0 @@ -import Foundation -import WalletConnectNetworking - -enum PushProtocolMethod: String, ProtocolMethod { - case propose = "wc_pushPropose" - - var method: String { - return self.rawValue - } - - var requestTag: Int { - switch self { - case .propose: - return 3000 - } - } - - var responseTag: Int { - switch self { - case .propose: - return 3001 - } - } -} - -struct PushRequestParams: Codable {} From 8673ab77a44cf408b5cbe76048b787c1e8ab48de Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 27 Sep 2022 13:34:42 +0200 Subject: [PATCH 074/175] extract push to separate package --- Example/ExampleApp.xcodeproj/project.pbxproj | 7 +++++++ Example/IntegrationTests/Pairing/PairingTests.swift | 1 + Package.swift | 10 ++++++++++ .../Push => WalletConnectPush}/PushClient.swift | 3 ++- .../Push => WalletConnectPush}/PushClientFactory.swift | 3 ++- .../PushProposeProtocolMethod.swift | 2 +- .../PushProposer.swift} | 0 7 files changed, 23 insertions(+), 3 deletions(-) rename Sources/{WalletConnectPairing/Push => WalletConnectPush}/PushClient.swift (93%) rename Sources/{WalletConnectPairing/Push => WalletConnectPush}/PushClientFactory.swift (78%) rename Sources/{WalletConnectPairing/Push => WalletConnectPush}/PushProposeProtocolMethod.swift (87%) rename Sources/{WalletConnectPairing/PairingRequester.swift => WalletConnectPush/PushProposer.swift} (100%) diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index 645d75745..00e4103a9 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -30,6 +30,7 @@ 8448F1D427E4726F0000B866 /* WalletConnect in Frameworks */ = {isa = PBXBuildFile; productRef = 8448F1D327E4726F0000B866 /* WalletConnect */; }; 84494388278D9C1B00CC26BB /* UIAlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84494387278D9C1B00CC26BB /* UIAlertController.swift */; }; 8460DCFC274F98A10081F94C /* RequestViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8460DCFB274F98A10081F94C /* RequestViewController.swift */; }; + 847CF3AF28E3141700F1D760 /* WalletConnectPush in Frameworks */ = {isa = PBXBuildFile; productRef = 847CF3AE28E3141700F1D760 /* WalletConnectPush */; settings = {ATTRIBUTES = (Required, ); }; }; 84AA01DB28CF0CD7005D48D8 /* XCTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84AA01DA28CF0CD7005D48D8 /* XCTest.swift */; }; 84CE641F27981DED00142511 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CE641E27981DED00142511 /* AppDelegate.swift */; }; 84CE642127981DED00142511 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CE642027981DED00142511 /* SceneDelegate.swift */; }; @@ -404,6 +405,7 @@ files = ( A5E03DFF2864662500888481 /* WalletConnect in Frameworks */, A5E03DF52864651200888481 /* Starscream in Frameworks */, + 847CF3AF28E3141700F1D760 /* WalletConnectPush in Frameworks */, 84DDB4ED28ABB663003D66ED /* WalletConnectAuth in Frameworks */, A5E03E01286466EA00888481 /* WalletConnectChat in Frameworks */, ); @@ -1201,6 +1203,7 @@ A5E03DFE2864662500888481 /* WalletConnect */, A5E03E00286466EA00888481 /* WalletConnectChat */, 84DDB4EC28ABB663003D66ED /* WalletConnectAuth */, + 847CF3AE28E3141700F1D760 /* WalletConnectPush */, ); productName = IntegrationTests; productReference = A5E03DED286464DB00888481 /* IntegrationTests.xctest */; @@ -1989,6 +1992,10 @@ isa = XCSwiftPackageProductDependency; productName = WalletConnect; }; + 847CF3AE28E3141700F1D760 /* WalletConnectPush */ = { + isa = XCSwiftPackageProductDependency; + productName = WalletConnectPush; + }; 84DDB4EC28ABB663003D66ED /* WalletConnectAuth */ = { isa = XCSwiftPackageProductDependency; productName = WalletConnectAuth; diff --git a/Example/IntegrationTests/Pairing/PairingTests.swift b/Example/IntegrationTests/Pairing/PairingTests.swift index 01085df49..9ff3214d5 100644 --- a/Example/IntegrationTests/Pairing/PairingTests.swift +++ b/Example/IntegrationTests/Pairing/PairingTests.swift @@ -5,6 +5,7 @@ import WalletConnectUtils import WalletConnectRelay import Combine import WalletConnectNetworking +import WalletConnectPush @testable import WalletConnectPairing diff --git a/Package.swift b/Package.swift index 4c925ebf6..d6c5b1ae7 100644 --- a/Package.swift +++ b/Package.swift @@ -19,6 +19,12 @@ let package = Package( .library( name: "WalletConnectAuth", targets: ["Auth"]), + .library( + name: "WalletConnectPairing", + targets: ["WalletConnectPairing"]), + .library( + name: "WalletConnectPush", + targets: ["WalletConnectPush"]), .library( name: "WalletConnectRouter", targets: ["WalletConnectRouter"]), @@ -42,6 +48,10 @@ let package = Package( name: "Auth", dependencies: ["WalletConnectPairing", "WalletConnectNetworking", .product(name: "Web3", package: "Web3.swift")], path: "Sources/Auth"), + .target( + name: "WalletConnectPush", + dependencies: ["WalletConnectPairing", "WalletConnectNetworking"], + path: "Sources/WalletConnectPush"), .target( name: "WalletConnectRelay", dependencies: ["WalletConnectUtils", "WalletConnectKMS"], diff --git a/Sources/WalletConnectPairing/Push/PushClient.swift b/Sources/WalletConnectPush/PushClient.swift similarity index 93% rename from Sources/WalletConnectPairing/Push/PushClient.swift rename to Sources/WalletConnectPush/PushClient.swift index 5dee1b892..c9eb91fd4 100644 --- a/Sources/WalletConnectPairing/Push/PushClient.swift +++ b/Sources/WalletConnectPush/PushClient.swift @@ -4,6 +4,7 @@ import Combine import WalletConnectKMS import WalletConnectUtils import WalletConnectNetworking +import WalletConnectPairing public class PushClient { @@ -11,7 +12,7 @@ public class PushClient { let requestPublisherSubject = PassthroughSubject<(topic: String, params: PushRequestParams), Never>() - var proposalPublisher: AnyPublisher<(topic: String, params: PushRequestParams), Never> { + public var proposalPublisher: AnyPublisher<(topic: String, params: PushRequestParams), Never> { requestPublisherSubject.eraseToAnyPublisher() } diff --git a/Sources/WalletConnectPairing/Push/PushClientFactory.swift b/Sources/WalletConnectPush/PushClientFactory.swift similarity index 78% rename from Sources/WalletConnectPairing/Push/PushClientFactory.swift rename to Sources/WalletConnectPush/PushClientFactory.swift index 21c4cf74c..5d6ce047c 100644 --- a/Sources/WalletConnectPairing/Push/PushClientFactory.swift +++ b/Sources/WalletConnectPush/PushClientFactory.swift @@ -3,10 +3,11 @@ import WalletConnectRelay import WalletConnectUtils import WalletConnectKMS import WalletConnectNetworking +import WalletConnectPairing public struct PushClientFactory { - static func create(logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, relayClient: RelayClient, pairingClient: PairingClient) -> PushClient { + static public func create(logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, relayClient: RelayClient, pairingClient: PairingClient) -> PushClient { let kms = KeyManagementService(keychain: keychainStorage) let serializer = Serializer(kms: kms) let history = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage) diff --git a/Sources/WalletConnectPairing/Push/PushProposeProtocolMethod.swift b/Sources/WalletConnectPush/PushProposeProtocolMethod.swift similarity index 87% rename from Sources/WalletConnectPairing/Push/PushProposeProtocolMethod.swift rename to Sources/WalletConnectPush/PushProposeProtocolMethod.swift index 1c5e62906..9ee2de7c8 100644 --- a/Sources/WalletConnectPairing/Push/PushProposeProtocolMethod.swift +++ b/Sources/WalletConnectPush/PushProposeProtocolMethod.swift @@ -9,4 +9,4 @@ struct PushProposeProtocolMethod: ProtocolMethod { let responseConfig: RelayConfig = RelayConfig(tag: 112, prompt: true, ttl: 300) } -struct PushRequestParams: Codable {} +public struct PushRequestParams: Codable {} diff --git a/Sources/WalletConnectPairing/PairingRequester.swift b/Sources/WalletConnectPush/PushProposer.swift similarity index 100% rename from Sources/WalletConnectPairing/PairingRequester.swift rename to Sources/WalletConnectPush/PushProposer.swift From 0a6fbbea2df15a9fc6df51e1befd7733a8d7c443 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 27 Sep 2022 15:27:06 +0200 Subject: [PATCH 075/175] Add topics store to networking interactor --- .../NetworkInteractor.swift | 4 ++++ Sources/WalletConnectNetworking/SetStore.swift | 14 ++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 Sources/WalletConnectNetworking/SetStore.swift diff --git a/Sources/WalletConnectNetworking/NetworkInteractor.swift b/Sources/WalletConnectNetworking/NetworkInteractor.swift index 00efdc43b..87cf4ce5b 100644 --- a/Sources/WalletConnectNetworking/NetworkInteractor.swift +++ b/Sources/WalletConnectNetworking/NetworkInteractor.swift @@ -5,12 +5,14 @@ import WalletConnectRelay import WalletConnectUtils import WalletConnectKMS + public class NetworkingInteractor: NetworkInteracting { private var publishers = Set() private let relayClient: RelayClient private let serializer: Serializing private let rpcHistory: RPCHistory private let logger: ConsoleLogging + private let topics = SetStore() private let requestPublisherSubject = PassthroughSubject<(topic: String, request: RPCRequest), Never>() private let responsePublisherSubject = PassthroughSubject<(topic: String, request: RPCRequest, response: RPCResponse), Never>() @@ -43,10 +45,12 @@ public class NetworkingInteractor: NetworkInteracting { } public func subscribe(topic: String) async throws { + await topics.insert(topic) try await relayClient.subscribe(topic: topic) } public func unsubscribe(topic: String) { + Task { await topics.remove(topic) } relayClient.unsubscribe(topic: topic) { [unowned self] error in if let error = error { logger.error(error) diff --git a/Sources/WalletConnectNetworking/SetStore.swift b/Sources/WalletConnectNetworking/SetStore.swift new file mode 100644 index 000000000..5e19e62b7 --- /dev/null +++ b/Sources/WalletConnectNetworking/SetStore.swift @@ -0,0 +1,14 @@ + +import Foundation + +actor SetStore { + private var store: Set = Set() + + func insert(_ element: T) { + store.insert(element) + } + + @discardableResult func remove(_ element: T) -> T? { + store.remove(element) + } +} From 535ed8b68088764366fcb145cfa7220bf97cbe9d Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Tue, 27 Sep 2022 19:49:37 +0600 Subject: [PATCH 076/175] Default timeouts increased --- Example/IntegrationTests/Auth/AuthTests.swift | 12 +++++++----- Example/IntegrationTests/Chat/ChatTests.swift | 8 +++++--- Example/IntegrationTests/Pairing/PairingTests.swift | 5 ++++- .../Relay/RelayClientEndToEndTests.swift | 4 ++-- Example/IntegrationTests/Sign/SignClientTests.swift | 2 +- 5 files changed, 19 insertions(+), 12 deletions(-) diff --git a/Example/IntegrationTests/Auth/AuthTests.swift b/Example/IntegrationTests/Auth/AuthTests.swift index 4ee2bf33d..3f48fd85d 100644 --- a/Example/IntegrationTests/Auth/AuthTests.swift +++ b/Example/IntegrationTests/Auth/AuthTests.swift @@ -12,6 +12,8 @@ final class AuthTests: XCTestCase { let prvKey = Data(hex: "462c1dad6832d7d96ccf87bd6a686a4110e114aaaebd5512e552c0e3a87b480f") private var publishers = [AnyCancellable]() + private let defaultTimeout: TimeInterval = 30 + override func setUp() { app = makeClient(prefix: "👻 App") let walletAccount = Account(chainIdentifier: "eip155:1", address: "0x724d0D2DaD3fbB0C168f947B87Fa5DBe36F1A8bf")! @@ -40,7 +42,7 @@ final class AuthTests: XCTestCase { wallet.authRequestPublisher.sink { _ in requestExpectation.fulfill() }.store(in: &publishers) - wait(for: [requestExpectation], timeout: 2) + wait(for: [requestExpectation], timeout: defaultTimeout) } func testRespondSuccess() async { @@ -59,7 +61,7 @@ final class AuthTests: XCTestCase { responseExpectation.fulfill() } .store(in: &publishers) - wait(for: [responseExpectation], timeout: 5) + wait(for: [responseExpectation], timeout: defaultTimeout) } func testUserRespondError() async { @@ -78,7 +80,7 @@ final class AuthTests: XCTestCase { responseExpectation.fulfill() } .store(in: &publishers) - wait(for: [responseExpectation], timeout: 5) + wait(for: [responseExpectation], timeout: defaultTimeout) } func testRespondSignatureVerificationFailed() async { @@ -99,7 +101,7 @@ final class AuthTests: XCTestCase { responseExpectation.fulfill() } .store(in: &publishers) - wait(for: [responseExpectation], timeout: 2) + wait(for: [responseExpectation], timeout: defaultTimeout) } func testPing() async { @@ -113,6 +115,6 @@ final class AuthTests: XCTestCase { pingExpectation.fulfill() } .store(in: &publishers) - wait(for: [pingExpectation], timeout: 5) + wait(for: [pingExpectation], timeout: defaultTimeout) } } diff --git a/Example/IntegrationTests/Chat/ChatTests.swift b/Example/IntegrationTests/Chat/ChatTests.swift index c4863b8af..f6e4b1912 100644 --- a/Example/IntegrationTests/Chat/ChatTests.swift +++ b/Example/IntegrationTests/Chat/ChatTests.swift @@ -12,6 +12,8 @@ final class ChatTests: XCTestCase { var registry: KeyValueRegistry! private var publishers = [AnyCancellable]() + private let defaultTimeout: TimeInterval = 30 + override func setUp() { registry = KeyValueRegistry() invitee = makeClient(prefix: "🦖 Registered") @@ -35,7 +37,7 @@ final class ChatTests: XCTestCase { invitee.invitePublisher.sink { _ in inviteExpectation.fulfill() }.store(in: &publishers) - wait(for: [inviteExpectation], timeout: 4) + wait(for: [inviteExpectation], timeout: defaultTimeout) } func testAcceptAndCreateNewThread() { @@ -62,7 +64,7 @@ final class ChatTests: XCTestCase { newThreadInviterExpectation.fulfill() }.store(in: &publishers) - wait(for: [newThreadinviteeExpectation, newThreadInviterExpectation], timeout: 10) + wait(for: [newThreadinviteeExpectation, newThreadInviterExpectation], timeout: defaultTimeout) } func testMessage() { @@ -97,6 +99,6 @@ final class ChatTests: XCTestCase { messageExpectation.fulfill() }.store(in: &publishers) - wait(for: [messageExpectation], timeout: 10) + wait(for: [messageExpectation], timeout: defaultTimeout) } } diff --git a/Example/IntegrationTests/Pairing/PairingTests.swift b/Example/IntegrationTests/Pairing/PairingTests.swift index 01085df49..ae9f177a5 100644 --- a/Example/IntegrationTests/Pairing/PairingTests.swift +++ b/Example/IntegrationTests/Pairing/PairingTests.swift @@ -9,6 +9,7 @@ import WalletConnectNetworking final class PairingTests: XCTestCase { + var appPairingClient: PairingClient! var walletPairingClient: PairingClient! @@ -19,6 +20,8 @@ final class PairingTests: XCTestCase { private var publishers = [AnyCancellable]() + private let defaultTimeout: TimeInterval = 30 + override func setUp() { (appPairingClient, appPushClient) = makeClients(prefix: "🤖 App") (walletPairingClient, walletPushClient) = makeClients(prefix: "🐶 Wallet") @@ -50,7 +53,7 @@ final class PairingTests: XCTestCase { try await appPushClient.propose(topic: uri.topic) - wait(for: [exp], timeout: 2) + wait(for: [exp], timeout: defaultTimeout) } } diff --git a/Example/IntegrationTests/Relay/RelayClientEndToEndTests.swift b/Example/IntegrationTests/Relay/RelayClientEndToEndTests.swift index 5c1843eb3..547ae1b36 100644 --- a/Example/IntegrationTests/Relay/RelayClientEndToEndTests.swift +++ b/Example/IntegrationTests/Relay/RelayClientEndToEndTests.swift @@ -7,11 +7,11 @@ import Starscream final class RelayClientEndToEndTests: XCTestCase { - let defaultTimeout: TimeInterval = 10 - let projectId = "3ca2919724fbfa5456a25194e369a8b4" private var publishers = Set() + private let defaultTimeout: TimeInterval = 30 + func makeRelayClient() -> RelayClient { let clientIdStorage = ClientIdStorage(keychain: KeychainStorageMock()) let socketAuthenticator = SocketAuthenticator( diff --git a/Example/IntegrationTests/Sign/SignClientTests.swift b/Example/IntegrationTests/Sign/SignClientTests.swift index 20bd91504..1652a696c 100644 --- a/Example/IntegrationTests/Sign/SignClientTests.swift +++ b/Example/IntegrationTests/Sign/SignClientTests.swift @@ -7,7 +7,7 @@ import JSONRPC final class SignClientTests: XCTestCase { - let defaultTimeout: TimeInterval = 8 + private let defaultTimeout: TimeInterval = 30 var dapp: ClientDelegate! var wallet: ClientDelegate! From c6ea818873b5cf4f95c12eefd2608174200275c0 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Tue, 27 Sep 2022 20:04:51 +0600 Subject: [PATCH 077/175] InputConfig defaultTimeout --- Example/ExampleApp.xcodeproj/project.pbxproj | 8 +++--- Example/IntegrationTests/Auth/AuthTests.swift | 14 +++++----- Example/IntegrationTests/Chat/ChatTests.swift | 10 +++---- .../Pairing/PairingTests.swift | 6 ++--- .../Relay/RelayClientEndToEndTests.swift | 10 +++---- .../Sign/SignClientTests.swift | 27 +++++++++---------- .../{URLConfig.swift => InputConfig.swift} | 6 ++++- 7 files changed, 37 insertions(+), 44 deletions(-) rename Example/IntegrationTests/Stubs/{URLConfig.swift => InputConfig.swift} (58%) diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index 645d75745..58afb012c 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -50,9 +50,9 @@ 84F568C2279582D200D0A289 /* Signer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F568C1279582D200D0A289 /* Signer.swift */; }; 84F568C42795832A00D0A289 /* EthereumTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F568C32795832A00D0A289 /* EthereumTransaction.swift */; }; 84FE684628ACDB4700C893FF /* RequestParams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84FE684528ACDB4700C893FF /* RequestParams.swift */; }; - A501AC2728C8E59800CEAA42 /* URLConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = A501AC2628C8E59800CEAA42 /* URLConfig.swift */; }; A50C036528AAD32200FE72D3 /* ClientDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A50C036428AAD32200FE72D3 /* ClientDelegate.swift */; }; A50F3946288005B200064555 /* Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = A50F3945288005B200064555 /* Types.swift */; }; + A518B31428E33A6500A2CE93 /* InputConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = A518B31328E33A6500A2CE93 /* InputConfig.swift */; }; A55CAAB028B92AFF00844382 /* ScanModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = A55CAAAB28B92AFF00844382 /* ScanModule.swift */; }; A55CAAB128B92AFF00844382 /* ScanPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = A55CAAAC28B92AFF00844382 /* ScanPresenter.swift */; }; A55CAAB228B92AFF00844382 /* ScanRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = A55CAAAD28B92AFF00844382 /* ScanRouter.swift */; }; @@ -241,9 +241,9 @@ 84F568C1279582D200D0A289 /* Signer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Signer.swift; sourceTree = ""; }; 84F568C32795832A00D0A289 /* EthereumTransaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EthereumTransaction.swift; sourceTree = ""; }; 84FE684528ACDB4700C893FF /* RequestParams.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestParams.swift; sourceTree = ""; }; - A501AC2628C8E59800CEAA42 /* URLConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLConfig.swift; sourceTree = ""; }; A50C036428AAD32200FE72D3 /* ClientDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClientDelegate.swift; sourceTree = ""; }; A50F3945288005B200064555 /* Types.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Types.swift; sourceTree = ""; }; + A518B31328E33A6500A2CE93 /* InputConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InputConfig.swift; sourceTree = ""; }; A55CAAAB28B92AFF00844382 /* ScanModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanModule.swift; sourceTree = ""; }; A55CAAAC28B92AFF00844382 /* ScanPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanPresenter.swift; sourceTree = ""; }; A55CAAAD28B92AFF00844382 /* ScanRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanRouter.swift; sourceTree = ""; }; @@ -1060,11 +1060,11 @@ A5E03E0A28646A8A00888481 /* Stubs */ = { isa = PBXGroup; children = ( + A518B31328E33A6500A2CE93 /* InputConfig.swift */, A5E03E1028646F8000888481 /* KeychainStorageMock.swift */, A5E03E0E28646D8A00888481 /* WebSocketFactory.swift */, A5E03DFC286465D100888481 /* Stubs.swift */, 84FE684528ACDB4700C893FF /* RequestParams.swift */, - A501AC2628C8E59800CEAA42 /* URLConfig.swift */, ); path = Stubs; sourceTree = ""; @@ -1468,13 +1468,13 @@ files = ( 84CEC64628D89D6B00D081A8 /* PairingTests.swift in Sources */, 767DC83528997F8E00080FA9 /* EthSendTransaction.swift in Sources */, + A518B31428E33A6500A2CE93 /* InputConfig.swift in Sources */, A5E03E03286466F400888481 /* ChatTests.swift in Sources */, 84D2A66628A4F51E0088AE09 /* AuthTests.swift in Sources */, 84FE684628ACDB4700C893FF /* RequestParams.swift in Sources */, 7694A5262874296A0001257E /* RegistryTests.swift in Sources */, A50C036528AAD32200FE72D3 /* ClientDelegate.swift in Sources */, A5E03E0D28646AD200888481 /* RelayClientEndToEndTests.swift in Sources */, - A501AC2728C8E59800CEAA42 /* URLConfig.swift in Sources */, A5E03DFA286465C700888481 /* SignClientTests.swift in Sources */, A5E03E0F28646D8A00888481 /* WebSocketFactory.swift in Sources */, 84AA01DB28CF0CD7005D48D8 /* XCTest.swift in Sources */, diff --git a/Example/IntegrationTests/Auth/AuthTests.swift b/Example/IntegrationTests/Auth/AuthTests.swift index 3f48fd85d..fda43a39d 100644 --- a/Example/IntegrationTests/Auth/AuthTests.swift +++ b/Example/IntegrationTests/Auth/AuthTests.swift @@ -12,8 +12,6 @@ final class AuthTests: XCTestCase { let prvKey = Data(hex: "462c1dad6832d7d96ccf87bd6a686a4110e114aaaebd5512e552c0e3a87b480f") private var publishers = [AnyCancellable]() - private let defaultTimeout: TimeInterval = 30 - override func setUp() { app = makeClient(prefix: "👻 App") let walletAccount = Account(chainIdentifier: "eip155:1", address: "0x724d0D2DaD3fbB0C168f947B87Fa5DBe36F1A8bf")! @@ -24,7 +22,7 @@ final class AuthTests: XCTestCase { let logger = ConsoleLogger(suffix: prefix, loggingLevel: .debug) let projectId = "3ca2919724fbfa5456a25194e369a8b4" let keychain = KeychainStorageMock() - let relayClient = RelayClient(relayHost: URLConfig.relayHost, projectId: projectId, keychainStorage: keychain, socketFactory: SocketFactory(), logger: logger) + let relayClient = RelayClient(relayHost: InputConfig.relayHost, projectId: projectId, keychainStorage: keychain, socketFactory: SocketFactory(), logger: logger) return AuthClientFactory.create( metadata: AppMetadata(name: name, description: "", url: "", icons: [""]), @@ -42,7 +40,7 @@ final class AuthTests: XCTestCase { wallet.authRequestPublisher.sink { _ in requestExpectation.fulfill() }.store(in: &publishers) - wait(for: [requestExpectation], timeout: defaultTimeout) + wait(for: [requestExpectation], timeout: InputConfig.defaultTimeout) } func testRespondSuccess() async { @@ -61,7 +59,7 @@ final class AuthTests: XCTestCase { responseExpectation.fulfill() } .store(in: &publishers) - wait(for: [responseExpectation], timeout: defaultTimeout) + wait(for: [responseExpectation], timeout: InputConfig.defaultTimeout) } func testUserRespondError() async { @@ -80,7 +78,7 @@ final class AuthTests: XCTestCase { responseExpectation.fulfill() } .store(in: &publishers) - wait(for: [responseExpectation], timeout: defaultTimeout) + wait(for: [responseExpectation], timeout: InputConfig.defaultTimeout) } func testRespondSignatureVerificationFailed() async { @@ -101,7 +99,7 @@ final class AuthTests: XCTestCase { responseExpectation.fulfill() } .store(in: &publishers) - wait(for: [responseExpectation], timeout: defaultTimeout) + wait(for: [responseExpectation], timeout: InputConfig.defaultTimeout) } func testPing() async { @@ -115,6 +113,6 @@ final class AuthTests: XCTestCase { pingExpectation.fulfill() } .store(in: &publishers) - wait(for: [pingExpectation], timeout: defaultTimeout) + wait(for: [pingExpectation], timeout: InputConfig.defaultTimeout) } } diff --git a/Example/IntegrationTests/Chat/ChatTests.swift b/Example/IntegrationTests/Chat/ChatTests.swift index f6e4b1912..a2a9d25b7 100644 --- a/Example/IntegrationTests/Chat/ChatTests.swift +++ b/Example/IntegrationTests/Chat/ChatTests.swift @@ -12,8 +12,6 @@ final class ChatTests: XCTestCase { var registry: KeyValueRegistry! private var publishers = [AnyCancellable]() - private let defaultTimeout: TimeInterval = 30 - override func setUp() { registry = KeyValueRegistry() invitee = makeClient(prefix: "🦖 Registered") @@ -24,7 +22,7 @@ final class ChatTests: XCTestCase { let logger = ConsoleLogger(suffix: prefix, loggingLevel: .debug) let projectId = "3ca2919724fbfa5456a25194e369a8b4" let keychain = KeychainStorageMock() - let relayClient = RelayClient(relayHost: URLConfig.relayHost, projectId: projectId, keychainStorage: keychain, socketFactory: SocketFactory(), logger: logger) + let relayClient = RelayClient(relayHost: InputConfig.relayHost, projectId: projectId, keychainStorage: keychain, socketFactory: SocketFactory(), logger: logger) return ChatClientFactory.create(registry: registry, relayClient: relayClient, kms: KeyManagementService(keychain: keychain), logger: logger, keyValueStorage: RuntimeKeyValueStorage()) } @@ -37,7 +35,7 @@ final class ChatTests: XCTestCase { invitee.invitePublisher.sink { _ in inviteExpectation.fulfill() }.store(in: &publishers) - wait(for: [inviteExpectation], timeout: defaultTimeout) + wait(for: [inviteExpectation], timeout: InputConfig.defaultTimeout) } func testAcceptAndCreateNewThread() { @@ -64,7 +62,7 @@ final class ChatTests: XCTestCase { newThreadInviterExpectation.fulfill() }.store(in: &publishers) - wait(for: [newThreadinviteeExpectation, newThreadInviterExpectation], timeout: defaultTimeout) + wait(for: [newThreadinviteeExpectation, newThreadInviterExpectation], timeout: InputConfig.defaultTimeout) } func testMessage() { @@ -99,6 +97,6 @@ final class ChatTests: XCTestCase { messageExpectation.fulfill() }.store(in: &publishers) - wait(for: [messageExpectation], timeout: defaultTimeout) + wait(for: [messageExpectation], timeout: InputConfig.defaultTimeout) } } diff --git a/Example/IntegrationTests/Pairing/PairingTests.swift b/Example/IntegrationTests/Pairing/PairingTests.swift index ae9f177a5..0eab257ac 100644 --- a/Example/IntegrationTests/Pairing/PairingTests.swift +++ b/Example/IntegrationTests/Pairing/PairingTests.swift @@ -20,8 +20,6 @@ final class PairingTests: XCTestCase { private var publishers = [AnyCancellable]() - private let defaultTimeout: TimeInterval = 30 - override func setUp() { (appPairingClient, appPushClient) = makeClients(prefix: "🤖 App") (walletPairingClient, walletPushClient) = makeClients(prefix: "🐶 Wallet") @@ -31,7 +29,7 @@ final class PairingTests: XCTestCase { let keychain = KeychainStorageMock() let logger = ConsoleLogger(suffix: prefix, loggingLevel: .debug) let projectId = "3ca2919724fbfa5456a25194e369a8b4" - let relayClient = RelayClient(relayHost: URLConfig.relayHost, projectId: projectId, keychainStorage: keychain, socketFactory: SocketFactory(), logger: logger) + let relayClient = RelayClient(relayHost: InputConfig.relayHost, projectId: projectId, keychainStorage: keychain, socketFactory: SocketFactory(), logger: logger) let pairingClient = PairingClientFactory.create(logger: logger, keyValueStorage: RuntimeKeyValueStorage(), keychainStorage: keychain, relayClient: relayClient) @@ -53,7 +51,7 @@ final class PairingTests: XCTestCase { try await appPushClient.propose(topic: uri.topic) - wait(for: [exp], timeout: defaultTimeout) + wait(for: [exp], timeout: InputConfig.defaultTimeout) } } diff --git a/Example/IntegrationTests/Relay/RelayClientEndToEndTests.swift b/Example/IntegrationTests/Relay/RelayClientEndToEndTests.swift index 547ae1b36..171656061 100644 --- a/Example/IntegrationTests/Relay/RelayClientEndToEndTests.swift +++ b/Example/IntegrationTests/Relay/RelayClientEndToEndTests.swift @@ -10,17 +10,15 @@ final class RelayClientEndToEndTests: XCTestCase { let projectId = "3ca2919724fbfa5456a25194e369a8b4" private var publishers = Set() - private let defaultTimeout: TimeInterval = 30 - func makeRelayClient() -> RelayClient { let clientIdStorage = ClientIdStorage(keychain: KeychainStorageMock()) let socketAuthenticator = SocketAuthenticator( clientIdStorage: clientIdStorage, didKeyFactory: ED25519DIDKeyFactory(), - relayHost: URLConfig.relayHost + relayHost: InputConfig.relayHost ) let urlFactory = RelayUrlFactory(socketAuthenticator: socketAuthenticator) - let socket = WebSocket(url: urlFactory.create(host: URLConfig.relayHost, projectId: projectId)) + let socket = WebSocket(url: urlFactory.create(host: InputConfig.relayHost, projectId: projectId)) let logger = ConsoleLogger() let dispatcher = Dispatcher(socket: socket, socketConnectionHandler: ManualSocketConnectionHandler(socket: socket), logger: logger) @@ -42,7 +40,7 @@ final class RelayClientEndToEndTests: XCTestCase { } }.store(in: &publishers) - wait(for: [subscribeExpectation], timeout: defaultTimeout) + wait(for: [subscribeExpectation], timeout: InputConfig.defaultTimeout) } func testEndToEndPayload() { @@ -93,7 +91,7 @@ final class RelayClientEndToEndTests: XCTestCase { } }.store(in: &publishers) - wait(for: [expectationA, expectationB], timeout: defaultTimeout) + wait(for: [expectationA, expectationB], timeout: InputConfig.defaultTimeout) XCTAssertEqual(subscriptionATopic, randomTopic) XCTAssertEqual(subscriptionBTopic, randomTopic) diff --git a/Example/IntegrationTests/Sign/SignClientTests.swift b/Example/IntegrationTests/Sign/SignClientTests.swift index 1652a696c..a0995a778 100644 --- a/Example/IntegrationTests/Sign/SignClientTests.swift +++ b/Example/IntegrationTests/Sign/SignClientTests.swift @@ -6,9 +6,6 @@ import JSONRPC @testable import WalletConnectRelay final class SignClientTests: XCTestCase { - - private let defaultTimeout: TimeInterval = 30 - var dapp: ClientDelegate! var wallet: ClientDelegate! @@ -19,7 +16,7 @@ final class SignClientTests: XCTestCase { let logger = ConsoleLogger(suffix: name, loggingLevel: .debug) let keychain = KeychainStorageMock() let relayClient = RelayClient( - relayHost: URLConfig.relayHost, + relayHost: InputConfig.relayHost, projectId: projectId, keyValueStorage: RuntimeKeyValueStorage(), keychainStorage: keychain, @@ -71,7 +68,7 @@ final class SignClientTests: XCTestCase { let uri = try await dapp.client.connect(requiredNamespaces: requiredNamespaces) try await wallet.client.pair(uri: uri!) - wait(for: [dappSettlementExpectation, walletSettlementExpectation], timeout: defaultTimeout) + wait(for: [dappSettlementExpectation, walletSettlementExpectation], timeout: InputConfig.defaultTimeout) } func testSessionReject() async throws { @@ -95,7 +92,7 @@ final class SignClientTests: XCTestCase { XCTAssertEqual(store.rejectedProposal, proposal) sessionRejectExpectation.fulfill() // TODO: Assert reason code } - wait(for: [sessionRejectExpectation], timeout: defaultTimeout) + wait(for: [sessionRejectExpectation], timeout: InputConfig.defaultTimeout) } func testSessionDelete() async throws { @@ -119,7 +116,7 @@ final class SignClientTests: XCTestCase { let uri = try await dapp.client.connect(requiredNamespaces: requiredNamespaces) try await wallet.client.pair(uri: uri!) - wait(for: [sessionDeleteExpectation], timeout: defaultTimeout) + wait(for: [sessionDeleteExpectation], timeout: InputConfig.defaultTimeout) } func testPairingPing() async throws { @@ -137,7 +134,7 @@ final class SignClientTests: XCTestCase { try await wallet.client.ping(topic: pairing.topic) - wait(for: [pongResponseExpectation], timeout: defaultTimeout) + wait(for: [pongResponseExpectation], timeout: InputConfig.defaultTimeout) } func testSessionPing() async throws { @@ -216,7 +213,7 @@ final class SignClientTests: XCTestCase { let uri = try await dapp.client.connect(requiredNamespaces: requiredNamespaces) try await wallet.client.pair(uri: uri!) - wait(for: [requestExpectation, responseExpectation], timeout: defaultTimeout) + wait(for: [requestExpectation, responseExpectation], timeout: InputConfig.defaultTimeout) } func testSessionRequestFailureResponse() async throws { @@ -258,7 +255,7 @@ final class SignClientTests: XCTestCase { let uri = try await dapp.client.connect(requiredNamespaces: requiredNamespaces) try await wallet.client.pair(uri: uri!) - wait(for: [expectation], timeout: defaultTimeout) + wait(for: [expectation], timeout: InputConfig.defaultTimeout) } func testNewSessionOnExistingPairing() async { @@ -295,7 +292,7 @@ final class SignClientTests: XCTestCase { let uri = try! await dapp.client.connect(requiredNamespaces: requiredNamespaces) try! await wallet.client.pair(uri: uri!) - wait(for: [dappSettlementExpectation, walletSettlementExpectation], timeout: defaultTimeout) + wait(for: [dappSettlementExpectation, walletSettlementExpectation], timeout: InputConfig.defaultTimeout) } @@ -319,7 +316,7 @@ final class SignClientTests: XCTestCase { } let uri = try! await dapp.client.connect(requiredNamespaces: requiredNamespaces) try! await wallet.client.pair(uri: uri!) - wait(for: [expectation], timeout: defaultTimeout) + wait(for: [expectation], timeout: InputConfig.defaultTimeout) } @@ -348,7 +345,7 @@ final class SignClientTests: XCTestCase { let uri = try! await dapp.client.connect(requiredNamespaces: requiredNamespaces) try! await wallet.client.pair(uri: uri!) - wait(for: [expectation], timeout: defaultTimeout) + wait(for: [expectation], timeout: InputConfig.defaultTimeout) } func testSessionEventSucceeds() async { @@ -378,7 +375,7 @@ final class SignClientTests: XCTestCase { let uri = try! await dapp.client.connect(requiredNamespaces: requiredNamespaces) try! await wallet.client.pair(uri: uri!) - wait(for: [expectation], timeout: defaultTimeout) + wait(for: [expectation], timeout: InputConfig.defaultTimeout) } func testSessionEventFails() async { @@ -405,6 +402,6 @@ final class SignClientTests: XCTestCase { let uri = try! await dapp.client.connect(requiredNamespaces: requiredNamespaces) try! await wallet.client.pair(uri: uri!) - wait(for: [expectation], timeout: defaultTimeout) + wait(for: [expectation], timeout: InputConfig.defaultTimeout) } } diff --git a/Example/IntegrationTests/Stubs/URLConfig.swift b/Example/IntegrationTests/Stubs/InputConfig.swift similarity index 58% rename from Example/IntegrationTests/Stubs/URLConfig.swift rename to Example/IntegrationTests/Stubs/InputConfig.swift index 8fed455dc..6219aa36e 100644 --- a/Example/IntegrationTests/Stubs/URLConfig.swift +++ b/Example/IntegrationTests/Stubs/InputConfig.swift @@ -1,8 +1,12 @@ import Foundation -struct URLConfig { +struct InputConfig { static var relayHost: String { return ProcessInfo.processInfo.environment["RELAY_HOST"]! } + + static var defaultTimeout: TimeInterval { + return 30 + } } From 320e986decdb74eb95e746067193e3cb30180aa5 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Wed, 28 Sep 2022 13:57:40 +0600 Subject: [PATCH 078/175] Input project id for integration tests --- .gitignore | 3 +++ Example/ExampleApp.xcodeproj/project.pbxproj | 16 ++++++++++++++-- .../xcschemes/IntegrationTests.xcscheme | 5 +++++ Example/IntegrationTests/Auth/AuthTests.swift | 3 +-- Example/IntegrationTests/Chat/ChatTests.swift | 3 +-- .../IntegrationTests/Pairing/PairingTests.swift | 3 +-- .../Relay/RelayClientEndToEndTests.swift | 3 +-- .../IntegrationTests/Sign/SignClientTests.swift | 7 ++----- Example/IntegrationTests/Stubs/InputConfig.swift | 10 +++++++++- 9 files changed, 37 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index 36f794f62..2261bbf75 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,6 @@ Package.resolved */fastlane/test_output */fastlane/README.md +# Example/IntegrationTests/Secrets +Configuration.xcconfig + diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index 58afb012c..f9a35762b 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -244,6 +244,7 @@ A50C036428AAD32200FE72D3 /* ClientDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClientDelegate.swift; sourceTree = ""; }; A50F3945288005B200064555 /* Types.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Types.swift; sourceTree = ""; }; A518B31328E33A6500A2CE93 /* InputConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InputConfig.swift; sourceTree = ""; }; + A51AC0D528E42DCF001BACF9 /* Configuration.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Configuration.xcconfig; sourceTree = ""; }; A55CAAAB28B92AFF00844382 /* ScanModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanModule.swift; sourceTree = ""; }; A55CAAAC28B92AFF00844382 /* ScanPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanPresenter.swift; sourceTree = ""; }; A55CAAAD28B92AFF00844382 /* ScanRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanRouter.swift; sourceTree = ""; }; @@ -452,6 +453,7 @@ 764E1D3326F8D3FC00A1FB15 = { isa = PBXGroup; children = ( + A51AC0D528E42DCF001BACF9 /* Configuration.xcconfig */, 84CE6453279FFE1100142511 /* Wallet.entitlements */, 764E1D3E26F8D3FC00A1FB15 /* ExampleApp */, 84CE641D27981DED00142511 /* DApp */, @@ -1528,6 +1530,7 @@ /* Begin XCBuildConfiguration section */ 764E1D4E26F8D3FE00A1FB15 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = A51AC0D528E42DCF001BACF9 /* Configuration.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -1589,6 +1592,7 @@ }; 764E1D4F26F8D3FE00A1FB15 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = A51AC0D528E42DCF001BACF9 /* Configuration.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -1645,6 +1649,7 @@ }; 764E1D5126F8D3FE00A1FB15 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = A51AC0D528E42DCF001BACF9 /* Configuration.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; @@ -1670,6 +1675,7 @@ }; 764E1D5226F8D3FE00A1FB15 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = A51AC0D528E42DCF001BACF9 /* Configuration.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; @@ -1695,6 +1701,7 @@ }; 84CE642D27981DF000142511 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = A51AC0D528E42DCF001BACF9 /* Configuration.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; @@ -1729,6 +1736,7 @@ }; 84CE642E27981DF000142511 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = A51AC0D528E42DCF001BACF9 /* Configuration.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; @@ -1763,6 +1771,7 @@ }; A58E7CF928729F550082D443 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = A51AC0D528E42DCF001BACF9 /* Configuration.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; @@ -1793,6 +1802,7 @@ }; A58E7CFA28729F550082D443 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = A51AC0D528E42DCF001BACF9 /* Configuration.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; @@ -1823,6 +1833,7 @@ }; A5A4FC782840C12C00BBEC1E /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = A51AC0D528E42DCF001BACF9 /* Configuration.xcconfig */; buildSettings = { CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Automatic; @@ -1842,6 +1853,7 @@ }; A5A4FC792840C12C00BBEC1E /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = A51AC0D528E42DCF001BACF9 /* Configuration.xcconfig */; buildSettings = { CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Automatic; @@ -1861,6 +1873,7 @@ }; A5E03DF2286464DB00888481 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = A51AC0D528E42DCF001BACF9 /* Configuration.xcconfig */; buildSettings = { CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Automatic; @@ -1876,7 +1889,6 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.IntegrationTests; PRODUCT_NAME = "$(TARGET_NAME)"; - RELAY_HOST = relay.walletconnect.com; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -1885,6 +1897,7 @@ }; A5E03DF3286464DB00888481 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = A51AC0D528E42DCF001BACF9 /* Configuration.xcconfig */; buildSettings = { CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Automatic; @@ -1895,7 +1908,6 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.IntegrationTests; PRODUCT_NAME = "$(TARGET_NAME)"; - RELAY_HOST = relay.walletconnect.com; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/IntegrationTests.xcscheme b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/IntegrationTests.xcscheme index 54745ba8d..6c42dec52 100644 --- a/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/IntegrationTests.xcscheme +++ b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/IntegrationTests.xcscheme @@ -26,6 +26,11 @@ value = "$(RELAY_HOST)" isEnabled = "YES"> + + AuthClient { let logger = ConsoleLogger(suffix: prefix, loggingLevel: .debug) - let projectId = "3ca2919724fbfa5456a25194e369a8b4" let keychain = KeychainStorageMock() - let relayClient = RelayClient(relayHost: InputConfig.relayHost, projectId: projectId, keychainStorage: keychain, socketFactory: SocketFactory(), logger: logger) + let relayClient = RelayClient(relayHost: InputConfig.relayHost, projectId: InputConfig.projectId, keychainStorage: keychain, socketFactory: SocketFactory(), logger: logger) return AuthClientFactory.create( metadata: AppMetadata(name: name, description: "", url: "", icons: [""]), diff --git a/Example/IntegrationTests/Chat/ChatTests.swift b/Example/IntegrationTests/Chat/ChatTests.swift index a2a9d25b7..177bda36f 100644 --- a/Example/IntegrationTests/Chat/ChatTests.swift +++ b/Example/IntegrationTests/Chat/ChatTests.swift @@ -20,9 +20,8 @@ final class ChatTests: XCTestCase { func makeClient(prefix: String) -> ChatClient { let logger = ConsoleLogger(suffix: prefix, loggingLevel: .debug) - let projectId = "3ca2919724fbfa5456a25194e369a8b4" let keychain = KeychainStorageMock() - let relayClient = RelayClient(relayHost: InputConfig.relayHost, projectId: projectId, keychainStorage: keychain, socketFactory: SocketFactory(), logger: logger) + let relayClient = RelayClient(relayHost: InputConfig.relayHost, projectId: InputConfig.projectId, keychainStorage: keychain, socketFactory: SocketFactory(), logger: logger) return ChatClientFactory.create(registry: registry, relayClient: relayClient, kms: KeyManagementService(keychain: keychain), logger: logger, keyValueStorage: RuntimeKeyValueStorage()) } diff --git a/Example/IntegrationTests/Pairing/PairingTests.swift b/Example/IntegrationTests/Pairing/PairingTests.swift index 0eab257ac..3e7caabed 100644 --- a/Example/IntegrationTests/Pairing/PairingTests.swift +++ b/Example/IntegrationTests/Pairing/PairingTests.swift @@ -28,8 +28,7 @@ final class PairingTests: XCTestCase { func makeClients(prefix: String) -> (PairingClient, PushClient) { let keychain = KeychainStorageMock() let logger = ConsoleLogger(suffix: prefix, loggingLevel: .debug) - let projectId = "3ca2919724fbfa5456a25194e369a8b4" - let relayClient = RelayClient(relayHost: InputConfig.relayHost, projectId: projectId, keychainStorage: keychain, socketFactory: SocketFactory(), logger: logger) + let relayClient = RelayClient(relayHost: InputConfig.relayHost, projectId: InputConfig.projectId, keychainStorage: keychain, socketFactory: SocketFactory(), logger: logger) let pairingClient = PairingClientFactory.create(logger: logger, keyValueStorage: RuntimeKeyValueStorage(), keychainStorage: keychain, relayClient: relayClient) diff --git a/Example/IntegrationTests/Relay/RelayClientEndToEndTests.swift b/Example/IntegrationTests/Relay/RelayClientEndToEndTests.swift index 171656061..e14d9481e 100644 --- a/Example/IntegrationTests/Relay/RelayClientEndToEndTests.swift +++ b/Example/IntegrationTests/Relay/RelayClientEndToEndTests.swift @@ -7,7 +7,6 @@ import Starscream final class RelayClientEndToEndTests: XCTestCase { - let projectId = "3ca2919724fbfa5456a25194e369a8b4" private var publishers = Set() func makeRelayClient() -> RelayClient { @@ -18,7 +17,7 @@ final class RelayClientEndToEndTests: XCTestCase { relayHost: InputConfig.relayHost ) let urlFactory = RelayUrlFactory(socketAuthenticator: socketAuthenticator) - let socket = WebSocket(url: urlFactory.create(host: InputConfig.relayHost, projectId: projectId)) + let socket = WebSocket(url: urlFactory.create(host: InputConfig.relayHost, projectId: InputConfig.projectId)) let logger = ConsoleLogger() let dispatcher = Dispatcher(socket: socket, socketConnectionHandler: ManualSocketConnectionHandler(socket: socket), logger: logger) diff --git a/Example/IntegrationTests/Sign/SignClientTests.swift b/Example/IntegrationTests/Sign/SignClientTests.swift index a0995a778..41dd6e31e 100644 --- a/Example/IntegrationTests/Sign/SignClientTests.swift +++ b/Example/IntegrationTests/Sign/SignClientTests.swift @@ -9,15 +9,12 @@ final class SignClientTests: XCTestCase { var dapp: ClientDelegate! var wallet: ClientDelegate! - static private func makeClientDelegate( - name: String, - projectId: String = "3ca2919724fbfa5456a25194e369a8b4" - ) -> ClientDelegate { + static private func makeClientDelegate(name: String) -> ClientDelegate { let logger = ConsoleLogger(suffix: name, loggingLevel: .debug) let keychain = KeychainStorageMock() let relayClient = RelayClient( relayHost: InputConfig.relayHost, - projectId: projectId, + projectId: InputConfig.projectId, keyValueStorage: RuntimeKeyValueStorage(), keychainStorage: keychain, socketFactory: SocketFactory(), diff --git a/Example/IntegrationTests/Stubs/InputConfig.swift b/Example/IntegrationTests/Stubs/InputConfig.swift index 6219aa36e..b0ab8db88 100644 --- a/Example/IntegrationTests/Stubs/InputConfig.swift +++ b/Example/IntegrationTests/Stubs/InputConfig.swift @@ -3,10 +3,18 @@ import Foundation struct InputConfig { static var relayHost: String { - return ProcessInfo.processInfo.environment["RELAY_HOST"]! + return config(for: "RELAY_HOST") ?? "relay.walletconnect.com" + } + + static var projectId: String { + return config(for: "PROJECT_ID") ?? "3ca2919724fbfa5456a25194e369a8b4" } static var defaultTimeout: TimeInterval { return 30 } + + private static func config(for key: String) -> String? { + return ProcessInfo.processInfo.environment[key] + } } From 83517a6632f9acac2da97222fb7a5dd8858838f9 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Wed, 28 Sep 2022 14:13:08 +0600 Subject: [PATCH 079/175] Sample apps Input config --- Example/DApp/Common/InputConfig.swift | 12 +++++++++++ Example/DApp/Info.plist | 2 ++ Example/DApp/SceneDelegate.swift | 2 +- Example/ExampleApp.xcodeproj/project.pbxproj | 20 +++++++++++++++++++ Example/ExampleApp/Common/InputConfig.swift | 12 +++++++++++ Example/ExampleApp/Info.plist | 2 ++ Example/ExampleApp/SceneDelegate.swift | 2 +- .../Configurator/ThirdPartyConfigurator.swift | 2 +- Example/Showcase/Common/InputConfig.swift | 12 +++++++++++ Example/Showcase/Other/Info.plist | 2 ++ 10 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 Example/DApp/Common/InputConfig.swift create mode 100644 Example/ExampleApp/Common/InputConfig.swift create mode 100644 Example/Showcase/Common/InputConfig.swift diff --git a/Example/DApp/Common/InputConfig.swift b/Example/DApp/Common/InputConfig.swift new file mode 100644 index 000000000..3d16a8cf9 --- /dev/null +++ b/Example/DApp/Common/InputConfig.swift @@ -0,0 +1,12 @@ +import Foundation + +struct InputConfig { + + static var projectId: String { + return config(for: "PROJECT_ID") ?? "3ca2919724fbfa5456a25194e369a8b4" + } + + private static func config(for key: String) -> String? { + return Bundle.main.object(forInfoDictionaryKey: key) as? String + } +} diff --git a/Example/DApp/Info.plist b/Example/DApp/Info.plist index 7c5f0be1e..07ba0e705 100644 --- a/Example/DApp/Info.plist +++ b/Example/DApp/Info.plist @@ -2,6 +2,8 @@ + PROJECT_ID + $(PROJECT_ID) ITSAppUsesNonExemptEncryption UIApplicationSceneManifest diff --git a/Example/DApp/SceneDelegate.swift b/Example/DApp/SceneDelegate.swift index d130770b8..25c130617 100644 --- a/Example/DApp/SceneDelegate.swift +++ b/Example/DApp/SceneDelegate.swift @@ -18,7 +18,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { private let authCoordinator = AuthCoordinator() func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { - Relay.configure(projectId: "3ca2919724fbfa5456a25194e369a8b4", socketFactory: SocketFactory()) + Relay.configure(projectId: InputConfig.projectId, socketFactory: SocketFactory()) setupWindow(scene: scene) } diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index f9a35762b..a2de86a98 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -53,6 +53,9 @@ A50C036528AAD32200FE72D3 /* ClientDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A50C036428AAD32200FE72D3 /* ClientDelegate.swift */; }; A50F3946288005B200064555 /* Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = A50F3945288005B200064555 /* Types.swift */; }; A518B31428E33A6500A2CE93 /* InputConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = A518B31328E33A6500A2CE93 /* InputConfig.swift */; }; + A51AC0D928E436A3001BACF9 /* InputConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51AC0D828E436A3001BACF9 /* InputConfig.swift */; }; + A51AC0DD28E43727001BACF9 /* InputConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51AC0DB28E436E6001BACF9 /* InputConfig.swift */; }; + A51AC0DF28E4379F001BACF9 /* InputConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51AC0DE28E4379F001BACF9 /* InputConfig.swift */; }; A55CAAB028B92AFF00844382 /* ScanModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = A55CAAAB28B92AFF00844382 /* ScanModule.swift */; }; A55CAAB128B92AFF00844382 /* ScanPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = A55CAAAC28B92AFF00844382 /* ScanPresenter.swift */; }; A55CAAB228B92AFF00844382 /* ScanRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = A55CAAAD28B92AFF00844382 /* ScanRouter.swift */; }; @@ -245,6 +248,9 @@ A50F3945288005B200064555 /* Types.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Types.swift; sourceTree = ""; }; A518B31328E33A6500A2CE93 /* InputConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InputConfig.swift; sourceTree = ""; }; A51AC0D528E42DCF001BACF9 /* Configuration.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Configuration.xcconfig; sourceTree = ""; }; + A51AC0D828E436A3001BACF9 /* InputConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputConfig.swift; sourceTree = ""; }; + A51AC0DB28E436E6001BACF9 /* InputConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputConfig.swift; sourceTree = ""; }; + A51AC0DE28E4379F001BACF9 /* InputConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputConfig.swift; sourceTree = ""; }; A55CAAAB28B92AFF00844382 /* ScanModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanModule.swift; sourceTree = ""; }; A55CAAAC28B92AFF00844382 /* ScanPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanPresenter.swift; sourceTree = ""; }; A55CAAAD28B92AFF00844382 /* ScanRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanRouter.swift; sourceTree = ""; }; @@ -483,6 +489,7 @@ children = ( 764E1D3F26F8D3FC00A1FB15 /* AppDelegate.swift */, 764E1D4126F8D3FC00A1FB15 /* SceneDelegate.swift */, + A51AC0DA28E436DE001BACF9 /* Common */, 761248152819F9A800CB6D48 /* Wallet */, 761C64A426FCB08B004239D1 /* SessionProposal */, 8460DCFE2750D6DF0081F94C /* SessionDetails */, @@ -609,6 +616,14 @@ path = Types; sourceTree = ""; }; + A51AC0DA28E436DE001BACF9 /* Common */ = { + isa = PBXGroup; + children = ( + A51AC0DB28E436E6001BACF9 /* InputConfig.swift */, + ); + path = Common; + sourceTree = ""; + }; A55CAAAA28B92AF200844382 /* Scan */ = { isa = PBXGroup; children = ( @@ -764,6 +779,7 @@ A58E7CFD2872A0F80082D443 /* Common */ = { isa = PBXGroup; children = ( + A51AC0DE28E4379F001BACF9 /* InputConfig.swift */, A50F3944288005A700064555 /* Types */, A5C2021F287EA5AF007E3188 /* Components */, A578FA332873049400AA7720 /* Style */, @@ -981,6 +997,7 @@ A5BB7FAB28B6AA7100707FC6 /* Common */ = { isa = PBXGroup; children = ( + A51AC0D828E436A3001BACF9 /* InputConfig.swift */, A5BB7FAC28B6AA7D00707FC6 /* QRCodeGenerator.swift */, ); path = Common; @@ -1309,6 +1326,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + A51AC0DD28E43727001BACF9 /* InputConfig.swift in Sources */, 76235E892820198B004ED0AA /* UIKit+Previews.swift in Sources */, 76B149F02821C03B00F05F91 /* Proposal.swift in Sources */, 765056272821989600F9AE79 /* Color+Extension.swift in Sources */, @@ -1346,6 +1364,7 @@ 84CE645527A29D4D00142511 /* ResponseViewController.swift in Sources */, 84CE641F27981DED00142511 /* AppDelegate.swift in Sources */, A5BB7FAD28B6AA7D00707FC6 /* QRCodeGenerator.swift in Sources */, + A51AC0D928E436A3001BACF9 /* InputConfig.swift in Sources */, A5BB7FA928B6A5FD00707FC6 /* AuthViewModel.swift in Sources */, 84CE6452279ED42B00142511 /* ConnectView.swift in Sources */, 84CE6448279AE68600142511 /* AccountRequestViewController.swift in Sources */, @@ -1365,6 +1384,7 @@ files = ( A58E7D3B2872D55F0082D443 /* ChatInteractor.swift in Sources */, A58E7D1F2872A57B0082D443 /* ApplicationConfigurator.swift in Sources */, + A51AC0DF28E4379F001BACF9 /* InputConfig.swift in Sources */, A58E7D452872EE570082D443 /* ContentMessageView.swift in Sources */, A5C20223287EA7E2007E3188 /* BrandButton.swift in Sources */, A5629ADF2876CC6E00094373 /* InviteListPresenter.swift in Sources */, diff --git a/Example/ExampleApp/Common/InputConfig.swift b/Example/ExampleApp/Common/InputConfig.swift new file mode 100644 index 000000000..3d16a8cf9 --- /dev/null +++ b/Example/ExampleApp/Common/InputConfig.swift @@ -0,0 +1,12 @@ +import Foundation + +struct InputConfig { + + static var projectId: String { + return config(for: "PROJECT_ID") ?? "3ca2919724fbfa5456a25194e369a8b4" + } + + private static func config(for key: String) -> String? { + return Bundle.main.object(forInfoDictionaryKey: key) as? String + } +} diff --git a/Example/ExampleApp/Info.plist b/Example/ExampleApp/Info.plist index fd00d23da..4d76b146a 100644 --- a/Example/ExampleApp/Info.plist +++ b/Example/ExampleApp/Info.plist @@ -2,6 +2,8 @@ + PROJECT_ID + $(PROJECT_ID) CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName diff --git a/Example/ExampleApp/SceneDelegate.swift b/Example/ExampleApp/SceneDelegate.swift index f52c84ae6..b32a34de4 100644 --- a/Example/ExampleApp/SceneDelegate.swift +++ b/Example/ExampleApp/SceneDelegate.swift @@ -25,7 +25,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { url: "example.wallet", icons: ["https://avatars.githubusercontent.com/u/37784886"]) - Relay.configure(projectId: "3ca2919724fbfa5456a25194e369a8b4", socketFactory: SocketFactory()) + Relay.configure(projectId: InputConfig.projectId, socketFactory: SocketFactory()) Sign.configure(metadata: metadata) #if DEBUG if CommandLine.arguments.contains("-cleanInstall") { diff --git a/Example/Showcase/Classes/ApplicationLayer/Configurator/ThirdPartyConfigurator.swift b/Example/Showcase/Classes/ApplicationLayer/Configurator/ThirdPartyConfigurator.swift index 8aa2f8457..ce8dbd3b2 100644 --- a/Example/Showcase/Classes/ApplicationLayer/Configurator/ThirdPartyConfigurator.swift +++ b/Example/Showcase/Classes/ApplicationLayer/Configurator/ThirdPartyConfigurator.swift @@ -5,7 +5,7 @@ import Auth struct ThirdPartyConfigurator: Configurator { func configure() { - Relay.configure(projectId: "relay.walletconnect.com", socketFactory: SocketFactory()) + Relay.configure(projectId: InputConfig.projectId, socketFactory: SocketFactory()) Auth.configure( metadata: AppMetadata( diff --git a/Example/Showcase/Common/InputConfig.swift b/Example/Showcase/Common/InputConfig.swift new file mode 100644 index 000000000..3d16a8cf9 --- /dev/null +++ b/Example/Showcase/Common/InputConfig.swift @@ -0,0 +1,12 @@ +import Foundation + +struct InputConfig { + + static var projectId: String { + return config(for: "PROJECT_ID") ?? "3ca2919724fbfa5456a25194e369a8b4" + } + + private static func config(for key: String) -> String? { + return Bundle.main.object(forInfoDictionaryKey: key) as? String + } +} diff --git a/Example/Showcase/Other/Info.plist b/Example/Showcase/Other/Info.plist index 95d2824b0..2b842100f 100644 --- a/Example/Showcase/Other/Info.plist +++ b/Example/Showcase/Other/Info.plist @@ -2,6 +2,8 @@ + PROJECT_ID + $(PROJECT_ID) CFBundleIconName AppIcon CFBundleURLTypes From 936b521eb9f8ee7860123135153f142bf9ceaf9d Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Wed, 28 Sep 2022 14:25:35 +0600 Subject: [PATCH 080/175] Empty config --- Configuration.xcconfig | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 Configuration.xcconfig diff --git a/Configuration.xcconfig b/Configuration.xcconfig new file mode 100644 index 000000000..e69de29bb From 99ca31f688cd4c3e9990fb9be270b018b9e62b4b Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Wed, 28 Sep 2022 14:29:53 +0600 Subject: [PATCH 081/175] Set config for project --- Example/ExampleApp.xcodeproj/project.pbxproj | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index a2de86a98..d662f8937 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -247,7 +247,6 @@ A50C036428AAD32200FE72D3 /* ClientDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClientDelegate.swift; sourceTree = ""; }; A50F3945288005B200064555 /* Types.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Types.swift; sourceTree = ""; }; A518B31328E33A6500A2CE93 /* InputConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InputConfig.swift; sourceTree = ""; }; - A51AC0D528E42DCF001BACF9 /* Configuration.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Configuration.xcconfig; sourceTree = ""; }; A51AC0D828E436A3001BACF9 /* InputConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputConfig.swift; sourceTree = ""; }; A51AC0DB28E436E6001BACF9 /* InputConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputConfig.swift; sourceTree = ""; }; A51AC0DE28E4379F001BACF9 /* InputConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputConfig.swift; sourceTree = ""; }; @@ -363,6 +362,7 @@ A5E22D212840C8D300E36487 /* WalletEngine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WalletEngine.swift; sourceTree = ""; }; A5E22D232840C8DB00E36487 /* SafariEngine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariEngine.swift; sourceTree = ""; }; A5E22D2B2840EAC300E36487 /* XCUIElement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCUIElement.swift; sourceTree = ""; }; + A5F48A0528E43D3F0034CBFB /* Configuration.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Configuration.xcconfig; path = ../Configuration.xcconfig; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -459,7 +459,7 @@ 764E1D3326F8D3FC00A1FB15 = { isa = PBXGroup; children = ( - A51AC0D528E42DCF001BACF9 /* Configuration.xcconfig */, + A5F48A0528E43D3F0034CBFB /* Configuration.xcconfig */, 84CE6453279FFE1100142511 /* Wallet.entitlements */, 764E1D3E26F8D3FC00A1FB15 /* ExampleApp */, 84CE641D27981DED00142511 /* DApp */, @@ -1550,7 +1550,7 @@ /* Begin XCBuildConfiguration section */ 764E1D4E26F8D3FE00A1FB15 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = A51AC0D528E42DCF001BACF9 /* Configuration.xcconfig */; + baseConfigurationReference = A5F48A0528E43D3F0034CBFB /* Configuration.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -1612,7 +1612,7 @@ }; 764E1D4F26F8D3FE00A1FB15 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = A51AC0D528E42DCF001BACF9 /* Configuration.xcconfig */; + baseConfigurationReference = A5F48A0528E43D3F0034CBFB /* Configuration.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -1669,7 +1669,6 @@ }; 764E1D5126F8D3FE00A1FB15 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = A51AC0D528E42DCF001BACF9 /* Configuration.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; @@ -1695,7 +1694,6 @@ }; 764E1D5226F8D3FE00A1FB15 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = A51AC0D528E42DCF001BACF9 /* Configuration.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; @@ -1721,7 +1719,6 @@ }; 84CE642D27981DF000142511 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = A51AC0D528E42DCF001BACF9 /* Configuration.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; @@ -1756,7 +1753,6 @@ }; 84CE642E27981DF000142511 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = A51AC0D528E42DCF001BACF9 /* Configuration.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; @@ -1791,7 +1787,6 @@ }; A58E7CF928729F550082D443 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = A51AC0D528E42DCF001BACF9 /* Configuration.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; @@ -1822,7 +1817,6 @@ }; A58E7CFA28729F550082D443 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = A51AC0D528E42DCF001BACF9 /* Configuration.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; @@ -1853,7 +1847,6 @@ }; A5A4FC782840C12C00BBEC1E /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = A51AC0D528E42DCF001BACF9 /* Configuration.xcconfig */; buildSettings = { CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Automatic; @@ -1873,7 +1866,6 @@ }; A5A4FC792840C12C00BBEC1E /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = A51AC0D528E42DCF001BACF9 /* Configuration.xcconfig */; buildSettings = { CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Automatic; @@ -1893,7 +1885,6 @@ }; A5E03DF2286464DB00888481 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = A51AC0D528E42DCF001BACF9 /* Configuration.xcconfig */; buildSettings = { CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Automatic; @@ -1917,7 +1908,6 @@ }; A5E03DF3286464DB00888481 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = A51AC0D528E42DCF001BACF9 /* Configuration.xcconfig */; buildSettings = { CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Automatic; From 038ae8e46266527b5579df50f6c2ce9d6b767542 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 28 Sep 2022 10:51:54 +0200 Subject: [PATCH 082/175] savepoint --- .../WalletConnectNetworking/NetworkInteractor.swift | 10 ++++++++-- Sources/WalletConnectNetworking/SetStore.swift | 4 ++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/Sources/WalletConnectNetworking/NetworkInteractor.swift b/Sources/WalletConnectNetworking/NetworkInteractor.swift index 87cf4ce5b..f322f4e79 100644 --- a/Sources/WalletConnectNetworking/NetworkInteractor.swift +++ b/Sources/WalletConnectNetworking/NetworkInteractor.swift @@ -5,7 +5,6 @@ import WalletConnectRelay import WalletConnectUtils import WalletConnectKMS - public class NetworkingInteractor: NetworkInteracting { private var publishers = Set() private let relayClient: RelayClient @@ -38,7 +37,14 @@ public class NetworkingInteractor: NetworkInteracting { self.rpcHistory = rpcHistory self.logger = logger self.socketConnectionStatusPublisher = relayClient.socketConnectionStatusPublisher - relayClient.messagePublisher.sink { [unowned self] (topic, message) in + setupRelaySubscribtion() + } + + private func setupRelaySubscribtion() { + relayClient.messagePublisher + //TODO - add async filter + .asyncFilter { [unowned self] in await topics.contains($0.topic)} + .sink { [unowned self] (topic, message) in manageSubscription(topic, message) } .store(in: &publishers) diff --git a/Sources/WalletConnectNetworking/SetStore.swift b/Sources/WalletConnectNetworking/SetStore.swift index 5e19e62b7..a812824d2 100644 --- a/Sources/WalletConnectNetworking/SetStore.swift +++ b/Sources/WalletConnectNetworking/SetStore.swift @@ -11,4 +11,8 @@ actor SetStore { @discardableResult func remove(_ element: T) -> T? { store.remove(element) } + + func contains(_ element: T) -> Bool { + return store.contains(element) + } } From 3abbf992a4f828d90a7d53f92248c38bb11b2e77 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 28 Sep 2022 11:43:30 +0200 Subject: [PATCH 083/175] refactor pairing client to transport requests --- .../NetworkInteractor.swift | 8 +++--- .../WalletConnectNetworking/SetStore.swift | 17 ++++++++++++- .../WalletConnectPairing/PairingClient.swift | 3 ++- .../PairingRegisterer.swift | 4 ++- .../PairingRequestsSubscriber.swift | 25 +++++++++---------- Sources/WalletConnectPush/PushClient.swift | 8 +++--- 6 files changed, 42 insertions(+), 23 deletions(-) diff --git a/Sources/WalletConnectNetworking/NetworkInteractor.swift b/Sources/WalletConnectNetworking/NetworkInteractor.swift index f322f4e79..c288d78ec 100644 --- a/Sources/WalletConnectNetworking/NetworkInteractor.swift +++ b/Sources/WalletConnectNetworking/NetworkInteractor.swift @@ -43,7 +43,7 @@ public class NetworkingInteractor: NetworkInteracting { private func setupRelaySubscribtion() { relayClient.messagePublisher //TODO - add async filter - .asyncFilter { [unowned self] in await topics.contains($0.topic)} + .filter { [unowned self] in topics.contains($0.topic)} .sink { [unowned self] (topic, message) in manageSubscription(topic, message) } @@ -51,12 +51,14 @@ public class NetworkingInteractor: NetworkInteracting { } public func subscribe(topic: String) async throws { - await topics.insert(topic) +// await topics.insert(topic) + topics.insert(topic) try await relayClient.subscribe(topic: topic) } public func unsubscribe(topic: String) { - Task { await topics.remove(topic) } +// Task { await topics.remove(topic) } + topics.remove(topic) relayClient.unsubscribe(topic: topic) { [unowned self] error in if let error = error { logger.error(error) diff --git a/Sources/WalletConnectNetworking/SetStore.swift b/Sources/WalletConnectNetworking/SetStore.swift index a812824d2..cd256df57 100644 --- a/Sources/WalletConnectNetworking/SetStore.swift +++ b/Sources/WalletConnectNetworking/SetStore.swift @@ -1,7 +1,22 @@ import Foundation -actor SetStore { +//actor SetStore { +// private var store: Set = Set() +// +// func insert(_ element: T) { +// store.insert(element) +// } +// +// @discardableResult func remove(_ element: T) -> T? { +// store.remove(element) +// } +// +// func contains(_ element: T) -> Bool { +// return store.contains(element) +// } +//} +class SetStore { private var store: Set = Set() func insert(_ element: T) { diff --git a/Sources/WalletConnectPairing/PairingClient.swift b/Sources/WalletConnectPairing/PairingClient.swift index de16ec03b..031adb384 100644 --- a/Sources/WalletConnectPairing/PairingClient.swift +++ b/Sources/WalletConnectPairing/PairingClient.swift @@ -3,6 +3,7 @@ import WalletConnectUtils import WalletConnectRelay import WalletConnectNetworking import Combine +import JSONRPC public class PairingClient: PairingRegisterer { private let walletPairService: WalletPairService @@ -41,7 +42,7 @@ public class PairingClient: PairingRegisterer { return try await appPairService.create() } - public func register(method: ProtocolMethod) { + public func register(method: ProtocolMethod) -> AnyPublisher<(topic: String, request: RPCRequest), Never> { pairingRequestsSubscriber.subscribeForRequest(method) } } diff --git a/Sources/WalletConnectPairing/PairingRegisterer.swift b/Sources/WalletConnectPairing/PairingRegisterer.swift index 6eafa7b1e..5fd2b5a7a 100644 --- a/Sources/WalletConnectPairing/PairingRegisterer.swift +++ b/Sources/WalletConnectPairing/PairingRegisterer.swift @@ -1,6 +1,8 @@ import Foundation import WalletConnectNetworking +import Combine +import JSONRPC public protocol PairingRegisterer { - func register(method: ProtocolMethod) + func register(method: ProtocolMethod) -> AnyPublisher<(topic: String, request: RPCRequest), Never> } diff --git a/Sources/WalletConnectPairing/PairingRequestsSubscriber.swift b/Sources/WalletConnectPairing/PairingRequestsSubscriber.swift index ed3ede277..d33c1afc5 100644 --- a/Sources/WalletConnectPairing/PairingRequestsSubscriber.swift +++ b/Sources/WalletConnectPairing/PairingRequestsSubscriber.swift @@ -2,6 +2,7 @@ import Foundation import Combine import WalletConnectUtils import WalletConnectNetworking +import JSONRPC public class PairingRequestsSubscriber { private let networkingInteractor: NetworkInteracting @@ -13,24 +14,22 @@ public class PairingRequestsSubscriber { self.pairingStorage = pairingStorage } - func subscribeForRequest(_ protocolMethod: ProtocolMethod) { + func subscribeForRequest(_ protocolMethod: ProtocolMethod) -> AnyPublisher<(topic: String, request: RPCRequest), Never> { + let publisherSubject = PassthroughSubject<(topic: String, request: RPCRequest), Never>() networkingInteractor.requestPublisher - // Pairing requests only - .filter { [unowned self] payload in - return pairingStorage.hasPairing(forTopic: payload.topic) - } - // Wrong method - .filter { payload in - return payload.request.method != protocolMethod.method - } - // Respond error .sink { [unowned self] topic, request in - Task(priority: .high) { - // TODO - spec tag - try await networkingInteractor.respondError(topic: topic, requestId: request.id!, protocolMethod: protocolMethod, reason: PairError.methodUnsupported) + if request.method != protocolMethod.method { + Task(priority: .high) { + // TODO - spec tag + try await networkingInteractor.respondError(topic: topic, requestId: request.id!, protocolMethod: protocolMethod, reason: PairError.methodUnsupported) + } + } else { + publisherSubject.send((topic, request)) } }.store(in: &publishers) + + return publisherSubject.eraseToAnyPublisher() } } diff --git a/Sources/WalletConnectPush/PushClient.swift b/Sources/WalletConnectPush/PushClient.swift index c9eb91fd4..7b3f6650a 100644 --- a/Sources/WalletConnectPush/PushClient.swift +++ b/Sources/WalletConnectPush/PushClient.swift @@ -46,15 +46,15 @@ private extension PushClient { let protocolMethod = PushProposeProtocolMethod() pairingRegisterer.register(method: protocolMethod) + .sink { [unowned self] (topic, request) in + let params = try! request.params!.get(PushRequestParams.self) + requestPublisherSubject.send((topic: topic, params: params)) + }.store(in: &publishers) networkInteractor.responseErrorSubscription(on: protocolMethod) .sink { [unowned self] (payload: ResponseSubscriptionErrorPayload) in logger.error(payload.error.localizedDescription) }.store(in: &publishers) - networkInteractor.requestSubscription(on: protocolMethod) - .sink { [unowned self] (payload: RequestSubscriptionPayload) in - requestPublisherSubject.send((payload.topic, payload.request)) - }.store(in: &publishers) } } From 6b444990f46824d57789688d600681a7a93e8ac7 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 28 Sep 2022 12:14:07 +0200 Subject: [PATCH 084/175] add async filter --- .../Extensions/Publisher.swift | 17 +++++++++++++++++ .../NetworkInteractor.swift | 9 +++------ Sources/WalletConnectNetworking/SetStore.swift | 17 +---------------- 3 files changed, 21 insertions(+), 22 deletions(-) create mode 100644 Sources/WalletConnectNetworking/Extensions/Publisher.swift diff --git a/Sources/WalletConnectNetworking/Extensions/Publisher.swift b/Sources/WalletConnectNetworking/Extensions/Publisher.swift new file mode 100644 index 000000000..f5f5b689b --- /dev/null +++ b/Sources/WalletConnectNetworking/Extensions/Publisher.swift @@ -0,0 +1,17 @@ +import Combine +import Foundation + +extension Publisher { + + func asyncFilter(filter: @escaping (Output) async -> Bool) -> AnyPublisher { + return flatMap { output in + return Future { completion in + Task(priority: .high) { + if await filter(output) { + completion(.success(output)) + } + } + } + }.eraseToAnyPublisher() + } +} diff --git a/Sources/WalletConnectNetworking/NetworkInteractor.swift b/Sources/WalletConnectNetworking/NetworkInteractor.swift index c288d78ec..ca56f38b5 100644 --- a/Sources/WalletConnectNetworking/NetworkInteractor.swift +++ b/Sources/WalletConnectNetworking/NetworkInteractor.swift @@ -42,8 +42,7 @@ public class NetworkingInteractor: NetworkInteracting { private func setupRelaySubscribtion() { relayClient.messagePublisher - //TODO - add async filter - .filter { [unowned self] in topics.contains($0.topic)} + .asyncFilter { [unowned self] in await topics.contains($0.topic)} .sink { [unowned self] (topic, message) in manageSubscription(topic, message) } @@ -51,14 +50,12 @@ public class NetworkingInteractor: NetworkInteracting { } public func subscribe(topic: String) async throws { -// await topics.insert(topic) - topics.insert(topic) + await topics.insert(topic) try await relayClient.subscribe(topic: topic) } public func unsubscribe(topic: String) { -// Task { await topics.remove(topic) } - topics.remove(topic) + Task { await topics.remove(topic) } relayClient.unsubscribe(topic: topic) { [unowned self] error in if let error = error { logger.error(error) diff --git a/Sources/WalletConnectNetworking/SetStore.swift b/Sources/WalletConnectNetworking/SetStore.swift index cd256df57..a812824d2 100644 --- a/Sources/WalletConnectNetworking/SetStore.swift +++ b/Sources/WalletConnectNetworking/SetStore.swift @@ -1,22 +1,7 @@ import Foundation -//actor SetStore { -// private var store: Set = Set() -// -// func insert(_ element: T) { -// store.insert(element) -// } -// -// @discardableResult func remove(_ element: T) -> T? { -// store.remove(element) -// } -// -// func contains(_ element: T) -> Bool { -// return store.contains(element) -// } -//} -class SetStore { +actor SetStore { private var store: Set = Set() func insert(_ element: T) { From dbf0eaa982e9b70772296bbe04c269d95cef1d7c Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 28 Sep 2022 12:23:13 +0200 Subject: [PATCH 085/175] update pairing client public interface --- .../WalletConnectPairing/PairingClient.swift | 24 +++++++++++++++++++ .../Common/DeletePairingService.swift | 0 .../Types/PairingProtocolMethod.swift | 16 +++++++++++++ .../PairingProtocolMethods.swift | 18 -------------- 4 files changed, 40 insertions(+), 18 deletions(-) rename Sources/{WalletConnectSign/Engine => WalletConnectPairing/Services}/Common/DeletePairingService.swift (100%) delete mode 100644 Sources/WalletConnectSign/Types/ProtocolMethods/PairingProtocolMethods.swift diff --git a/Sources/WalletConnectPairing/PairingClient.swift b/Sources/WalletConnectPairing/PairingClient.swift index 031adb384..714e87341 100644 --- a/Sources/WalletConnectPairing/PairingClient.swift +++ b/Sources/WalletConnectPairing/PairingClient.swift @@ -42,6 +42,30 @@ public class PairingClient: PairingRegisterer { return try await appPairService.create() } + public func activate(_ topic: String) { + + } + + public func updateExpiry(_ topic: String) { + + } + + public func updateMetadata(_ topic: String, metadata: AppMetadata) { + + } + + public func getPairings() -> [Pairing] { + + } + + public func ping(_ topic: String) { + + } + + public func disconnect(_ topic: String) { + + } + public func register(method: ProtocolMethod) -> AnyPublisher<(topic: String, request: RPCRequest), Never> { pairingRequestsSubscriber.subscribeForRequest(method) } diff --git a/Sources/WalletConnectSign/Engine/Common/DeletePairingService.swift b/Sources/WalletConnectPairing/Services/Common/DeletePairingService.swift similarity index 100% rename from Sources/WalletConnectSign/Engine/Common/DeletePairingService.swift rename to Sources/WalletConnectPairing/Services/Common/DeletePairingService.swift diff --git a/Sources/WalletConnectPairing/Types/PairingProtocolMethod.swift b/Sources/WalletConnectPairing/Types/PairingProtocolMethod.swift index 10d39127f..02b400ffb 100644 --- a/Sources/WalletConnectPairing/Types/PairingProtocolMethod.swift +++ b/Sources/WalletConnectPairing/Types/PairingProtocolMethod.swift @@ -8,3 +8,19 @@ struct PairingPingProtocolMethod: ProtocolMethod { let responseConfig = RelayConfig(tag: 1003, prompt: false, ttl: 30) } + +struct PairingPingProtocolMethod: ProtocolMethod { + let method: String = "wc_pairingPing" + + let requestConfig = RelayConfig(tag: 1002, prompt: false, ttl: 30) + + let responseConfig = RelayConfig(tag: 1003, prompt: false, ttl: 30) +} + +struct PairingDeleteProtocolMethod: ProtocolMethod { + let method: String = "wc_pairingDelete" + + let requestConfig = RelayConfig(tag: 1000, prompt: false, ttl: 86400) + + let responseConfig = RelayConfig(tag: 1001, prompt: false, ttl: 86400) +} diff --git a/Sources/WalletConnectSign/Types/ProtocolMethods/PairingProtocolMethods.swift b/Sources/WalletConnectSign/Types/ProtocolMethods/PairingProtocolMethods.swift deleted file mode 100644 index 19167c4b5..000000000 --- a/Sources/WalletConnectSign/Types/ProtocolMethods/PairingProtocolMethods.swift +++ /dev/null @@ -1,18 +0,0 @@ -import Foundation -import WalletConnectNetworking - -struct PairingPingProtocolMethod: ProtocolMethod { - let method: String = "wc_pairingPing" - - let requestConfig = RelayConfig(tag: 1002, prompt: false, ttl: 30) - - let responseConfig = RelayConfig(tag: 1003, prompt: false, ttl: 30) -} - -struct PairingDeleteProtocolMethod: ProtocolMethod { - let method: String = "wc_pairingDelete" - - let requestConfig = RelayConfig(tag: 1000, prompt: false, ttl: 86400) - - let responseConfig = RelayConfig(tag: 1001, prompt: false, ttl: 86400) -} From 5ae9fb4d80b590de621564b54788ff3612c65c2a Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Wed, 28 Sep 2022 14:57:08 +0600 Subject: [PATCH 086/175] Project ID secret --- .github/actions/ci/action.yml | 5 +++++ .github/workflows/ci.yml | 1 + 2 files changed, 6 insertions(+) diff --git a/.github/actions/ci/action.yml b/.github/actions/ci/action.yml index 7e1880db3..44f9f0988 100644 --- a/.github/actions/ci/action.yml +++ b/.github/actions/ci/action.yml @@ -8,6 +8,9 @@ inputs: description: 'The endpoint of the relay e.g. relay.walletconnect.com' required: false default: 'relay.walletconnect.com' + project-id: + description: 'WalletConnect project id' + required: true runs: using: "composite" @@ -29,12 +32,14 @@ runs: shell: bash env: RELAY_ENDPOINT: ${{ inputs.relay-endpoint }} + PROJECT_ID: ${{ inputs.project-id }} run: "xcodebuild \ -project Example/ExampleApp.xcodeproj \ -scheme IntegrationTests \ -clonedSourcePackagesDirPath SourcePackagesCache \ -destination 'platform=iOS Simulator,name=iPhone 13' \ RELAY_HOST='$RELAY_ENDPOINT' \ + PROJECT_ID='$PROJECT_ID' \ test" # Wallet build diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a81d10cb4..9b697507a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,6 +46,7 @@ jobs: - uses: ./.github/actions/ci with: type: ${{ matrix.test-type }} + project-id: ${{ secrets.PROJECT_ID }} test-ui: if: github.ref == 'refs/heads/main' From e1cd337b3d6e95674cb5a1d3c16f13e9bc5d34ad Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 28 Sep 2022 12:30:11 +0200 Subject: [PATCH 087/175] replace pairings provider --- Sources/WalletConnectPairing/PairingClient.swift | 7 +++++-- .../Services/Common/DeletePairingService.swift | 5 ++--- .../Services/Common/PairingsProvider.swift | 1 - .../Types/PairingProtocolMethod.swift | 8 -------- .../WalletConnectPairing/Types/ReasonCode.swift | 15 +++++++++++++++ 5 files changed, 22 insertions(+), 14 deletions(-) rename Sources/{Auth => WalletConnectPairing}/Services/Common/PairingsProvider.swift (93%) create mode 100644 Sources/WalletConnectPairing/Types/ReasonCode.swift diff --git a/Sources/WalletConnectPairing/PairingClient.swift b/Sources/WalletConnectPairing/PairingClient.swift index 714e87341..5e5dc30d0 100644 --- a/Sources/WalletConnectPairing/PairingClient.swift +++ b/Sources/WalletConnectPairing/PairingClient.swift @@ -12,13 +12,15 @@ public class PairingClient: PairingRegisterer { private let logger: ConsoleLogging private let networkingInteractor: NetworkInteracting private let pairingRequestsSubscriber: PairingRequestsSubscriber + private let pairingsProvider: PairingsProvider init(appPairService: AppPairService, networkingInteractor: NetworkInteracting, logger: ConsoleLogging, walletPairService: WalletPairService, pairingRequestsSubscriber: PairingRequestsSubscriber, - socketConnectionStatusPublisher: AnyPublisher + socketConnectionStatusPublisher: AnyPublisher, + pairingsProvider: PairingsProvider ) { self.appPairService = appPairService self.walletPairService = walletPairService @@ -26,6 +28,7 @@ public class PairingClient: PairingRegisterer { self.socketConnectionStatusPublisher = socketConnectionStatusPublisher self.logger = logger self.pairingRequestsSubscriber = pairingRequestsSubscriber + self.pairingsProvider = pairingsProvider } /// For wallet to establish a pairing /// Wallet should call this function in order to accept peer's pairing proposal and be able to subscribe for future requests. @@ -55,7 +58,7 @@ public class PairingClient: PairingRegisterer { } public func getPairings() -> [Pairing] { - + pairingsProvider.getPairings() } public func ping(_ topic: String) { diff --git a/Sources/WalletConnectPairing/Services/Common/DeletePairingService.swift b/Sources/WalletConnectPairing/Services/Common/DeletePairingService.swift index 8928013b5..06ae1f8fe 100644 --- a/Sources/WalletConnectPairing/Services/Common/DeletePairingService.swift +++ b/Sources/WalletConnectPairing/Services/Common/DeletePairingService.swift @@ -2,9 +2,9 @@ import Foundation import JSONRPC import WalletConnectKMS import WalletConnectUtils -import WalletConnectPairing import WalletConnectNetworking + class DeletePairingService { private let networkingInteractor: NetworkInteracting private let kms: KeyManagementServiceProtocol @@ -22,9 +22,8 @@ class DeletePairingService { } func delete(topic: String) async throws { - let reasonCode = ReasonCode.userDisconnected + let reason = ReasonCode.userDisconnected let protocolMethod = PairingDeleteProtocolMethod() - let reason = SessionType.Reason(code: reasonCode.code, message: reasonCode.message) logger.debug("Will delete pairing for reason: message: \(reason.message) code: \(reason.code)") let request = RPCRequest(method: protocolMethod.method, params: reason) try await networkingInteractor.request(request, topic: topic, protocolMethod: protocolMethod) diff --git a/Sources/Auth/Services/Common/PairingsProvider.swift b/Sources/WalletConnectPairing/Services/Common/PairingsProvider.swift similarity index 93% rename from Sources/Auth/Services/Common/PairingsProvider.swift rename to Sources/WalletConnectPairing/Services/Common/PairingsProvider.swift index 081f3fa6d..60b854361 100644 --- a/Sources/Auth/Services/Common/PairingsProvider.swift +++ b/Sources/WalletConnectPairing/Services/Common/PairingsProvider.swift @@ -1,5 +1,4 @@ import Foundation -import WalletConnectPairing public class PairingsProvider { private let pairingStorage: WCPairingStorage diff --git a/Sources/WalletConnectPairing/Types/PairingProtocolMethod.swift b/Sources/WalletConnectPairing/Types/PairingProtocolMethod.swift index 02b400ffb..19167c4b5 100644 --- a/Sources/WalletConnectPairing/Types/PairingProtocolMethod.swift +++ b/Sources/WalletConnectPairing/Types/PairingProtocolMethod.swift @@ -9,14 +9,6 @@ struct PairingPingProtocolMethod: ProtocolMethod { let responseConfig = RelayConfig(tag: 1003, prompt: false, ttl: 30) } -struct PairingPingProtocolMethod: ProtocolMethod { - let method: String = "wc_pairingPing" - - let requestConfig = RelayConfig(tag: 1002, prompt: false, ttl: 30) - - let responseConfig = RelayConfig(tag: 1003, prompt: false, ttl: 30) -} - struct PairingDeleteProtocolMethod: ProtocolMethod { let method: String = "wc_pairingDelete" diff --git a/Sources/WalletConnectPairing/Types/ReasonCode.swift b/Sources/WalletConnectPairing/Types/ReasonCode.swift new file mode 100644 index 000000000..5b211a902 --- /dev/null +++ b/Sources/WalletConnectPairing/Types/ReasonCode.swift @@ -0,0 +1,15 @@ + +import Foundation +import WalletConnectNetworking + +enum ReasonCode: Reason, Codable { + case userDisconnected + + var code: Int { + return 6000 + } + + var message: String { + return "User Disconnected" + } +} From 2de9f71b8198258bc4db241b4da9becfd9777a8f Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 28 Sep 2022 12:47:15 +0200 Subject: [PATCH 088/175] savepoint - refactor auth for pairing --- Sources/Auth/AuthClient.swift | 37 +++++------------- Sources/Auth/AuthClientFactory.swift | 18 +++------ .../Common/DeletePairingService.swift | 38 ------------------- .../Services/Wallet/WalletPairService.swift | 38 ------------------- .../WalletConnectPairing/PairingClient.swift | 16 +++++++- .../PairingClientFactory.swift | 8 +++- .../Services/Common/CleanupService.swift | 0 .../StorageDomainIdentifiers.swift | 0 8 files changed, 36 insertions(+), 119 deletions(-) delete mode 100644 Sources/Auth/Services/Common/DeletePairingService.swift delete mode 100644 Sources/Auth/Services/Wallet/WalletPairService.swift rename Sources/{Auth => WalletConnectPairing}/Services/Common/CleanupService.swift (100%) rename Sources/{Auth => WalletConnectPairing}/StorageDomainIdentifiers.swift (100%) diff --git a/Sources/Auth/AuthClient.swift b/Sources/Auth/AuthClient.swift index c75a2895e..64a263c76 100644 --- a/Sources/Auth/AuthClient.swift +++ b/Sources/Auth/AuthClient.swift @@ -46,54 +46,46 @@ public class AuthClient { // MARK: - Private Properties + private let pairingClient: PairingClient + private var authResponsePublisherSubject = PassthroughSubject<(id: RPCID, result: Result), Never>() private var authRequestPublisherSubject = PassthroughSubject() private var pingResponsePublisherSubject = PassthroughSubject() - private let appPairService: AppPairService private let appRequestService: AppRequestService - private let deletePairingService: DeletePairingService private let appRespondSubscriber: AppRespondSubscriber - private let walletPairService: WalletPairService private let walletRequestSubscriber: WalletRequestSubscriber private let walletRespondService: WalletRespondService - private let cleanupService: CleanupService private let pairingStorage: WCPairingStorage private let pendingRequestsProvider: PendingRequestsProvider private let pingService: PairingPingService private let pairingsProvider: PairingsProvider private var account: Account? - init(appPairService: AppPairService, - appRequestService: AppRequestService, + init(appRequestService: AppRequestService, appRespondSubscriber: AppRespondSubscriber, - walletPairService: WalletPairService, walletRequestSubscriber: WalletRequestSubscriber, walletRespondService: WalletRespondService, - deletePairingService: DeletePairingService, account: Account?, pendingRequestsProvider: PendingRequestsProvider, - cleanupService: CleanupService, logger: ConsoleLogging, pairingStorage: WCPairingStorage, socketConnectionStatusPublisher: AnyPublisher, pingService: PairingPingService, - pairingsProvider: PairingsProvider + pairingsProvider: PairingsProvider, + pairingClient: PairingClient ) { - self.appPairService = appPairService self.appRequestService = appRequestService - self.walletPairService = walletPairService self.walletRequestSubscriber = walletRequestSubscriber self.walletRespondService = walletRespondService self.appRespondSubscriber = appRespondSubscriber self.account = account self.pendingRequestsProvider = pendingRequestsProvider - self.cleanupService = cleanupService self.logger = logger self.pairingStorage = pairingStorage self.socketConnectionStatusPublisher = socketConnectionStatusPublisher - self.deletePairingService = deletePairingService self.pingService = pingService self.pairingsProvider = pairingsProvider + self.pairingClient = pairingClient setUpPublishers() } @@ -108,7 +100,7 @@ public class AuthClient { guard uri.api == .auth else { throw Errors.pairingUriWrongApiParam } - try await walletPairService.pair(uri) + try await pairingClient.pair(uri: uri) } /// For a dapp to send an authentication request to a wallet @@ -117,7 +109,7 @@ public class AuthClient { /// - Returns: Pairing URI that should be shared with wallet out of bound. Common way is to present it as a QR code. public func request(_ params: RequestParams) async throws -> WalletConnectURI { logger.debug("Requesting Authentication") - let uri = try await appPairService.create() + let uri = try await pairingClient.create() try await appRequestService.request(params: params, topic: uri.topic) return uri } @@ -149,7 +141,7 @@ public class AuthClient { } public func disconnect(topic: String) async throws { - try await deletePairingService.delete(topic: topic) + try await pairingClient.disconnect(topic: topic) } public func ping(topic: String) async throws { @@ -157,7 +149,7 @@ public class AuthClient { } public func getPairings() -> [Pairing] { - pairingsProvider.getPairings() + pairingClient.getPairings() } /// Query pending authentication requests @@ -167,15 +159,6 @@ public class AuthClient { return try pendingRequestsProvider.getPendingRequests(account: account) } -#if DEBUG - /// Delete all stored data such as: pairings, keys - /// - /// - Note: Doesn't unsubscribe from topics - public func cleanup() throws { - try cleanupService.cleanup() - } -#endif - private func setUpPublishers() { appRespondSubscriber.onResponse = { [unowned self] (id, result) in authResponsePublisherSubject.send((id, result)) diff --git a/Sources/Auth/AuthClientFactory.swift b/Sources/Auth/AuthClientFactory.swift index 6d7c8926a..59449c6fc 100644 --- a/Sources/Auth/AuthClientFactory.swift +++ b/Sources/Auth/AuthClientFactory.swift @@ -15,39 +15,33 @@ public struct AuthClientFactory { } static func create(metadata: AppMetadata, account: Account?, logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, relayClient: RelayClient) -> AuthClient { - let pairingStore = PairingStorage(storage: SequenceStore(store: .init(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.pairings.rawValue))) let kms = KeyManagementService(keychain: keychainStorage) let serializer = Serializer(kms: kms) let history = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage) let networkingInteractor = NetworkingInteractor(relayClient: relayClient, serializer: serializer, logger: logger, rpcHistory: history) let messageFormatter = SIWEMessageFormatter() - let appPairService = AppPairService(networkingInteractor: networkingInteractor, kms: kms, pairingStorage: pairingStore) let appRequestService = AppRequestService(networkingInteractor: networkingInteractor, kms: kms, appMetadata: metadata, logger: logger) let messageSigner = MessageSigner(signer: Signer()) let appRespondSubscriber = AppRespondSubscriber(networkingInteractor: networkingInteractor, logger: logger, rpcHistory: history, signatureVerifier: messageSigner, messageFormatter: messageFormatter, pairingStorage: pairingStore) - let walletPairService = WalletPairService(networkingInteractor: networkingInteractor, kms: kms, pairingStorage: pairingStore) let walletErrorResponder = WalletErrorResponder(networkingInteractor: networkingInteractor, logger: logger, kms: kms, rpcHistory: history) let walletRequestSubscriber = WalletRequestSubscriber(networkingInteractor: networkingInteractor, logger: logger, kms: kms, messageFormatter: messageFormatter, address: account?.address, walletErrorResponder: walletErrorResponder) let walletRespondService = WalletRespondService(networkingInteractor: networkingInteractor, logger: logger, kms: kms, rpcHistory: history, walletErrorResponder: walletErrorResponder) let pendingRequestsProvider = PendingRequestsProvider(rpcHistory: history) - let cleanupService = CleanupService(pairingStore: pairingStore, kms: kms) - let deletePairingService = DeletePairingService(networkingInteractor: networkingInteractor, kms: kms, pairingStorage: pairingStore, logger: logger) let pingService = PairingPingService(pairingStorage: pairingStore, networkingInteractor: networkingInteractor, logger: logger) - let pairingsProvider = PairingsProvider(pairingStorage: pairingStore) - return AuthClient(appPairService: appPairService, - appRequestService: appRequestService, + //TODO - fix this - singleton? + let pairingClient = PairingClientFactory.create(relayClient: relayClient) + + return AuthClient(appRequestService: appRequestService, appRespondSubscriber: appRespondSubscriber, - walletPairService: walletPairService, walletRequestSubscriber: walletRequestSubscriber, - walletRespondService: walletRespondService, deletePairingService: deletePairingService, + walletRespondService: walletRespondService, account: account, pendingRequestsProvider: pendingRequestsProvider, - cleanupService: cleanupService, logger: logger, pairingStorage: pairingStore, socketConnectionStatusPublisher: relayClient.socketConnectionStatusPublisher, pingService: pingService, - pairingsProvider: pairingsProvider) + pairingClient: pairingClient) } } diff --git a/Sources/Auth/Services/Common/DeletePairingService.swift b/Sources/Auth/Services/Common/DeletePairingService.swift deleted file mode 100644 index 414118348..000000000 --- a/Sources/Auth/Services/Common/DeletePairingService.swift +++ /dev/null @@ -1,38 +0,0 @@ -import Foundation -import WalletConnectNetworking -import JSONRPC -import WalletConnectKMS -import WalletConnectUtils -import WalletConnectPairing - -class DeletePairingService { - enum Errors: Error { - case pairingNotFound - } - private let networkingInteractor: NetworkInteracting - private let kms: KeyManagementServiceProtocol - private let pairingStorage: WCPairingStorage - private let logger: ConsoleLogging - - init(networkingInteractor: NetworkInteracting, - kms: KeyManagementServiceProtocol, - pairingStorage: WCPairingStorage, - logger: ConsoleLogging) { - self.networkingInteractor = networkingInteractor - self.kms = kms - self.pairingStorage = pairingStorage - self.logger = logger - } - - func delete(topic: String) async throws { - guard pairingStorage.hasPairing(forTopic: topic) else { throw Errors.pairingNotFound} - let protocolMethod = PairingDeleteProtocolMethod() - let reason = AuthError.userDisconnected - logger.debug("Will delete pairing for reason: message: \(reason.message) code: \(reason.code)") - let request = RPCRequest(method: protocolMethod.method, params: reason) - try await networkingInteractor.request(request, topic: topic, protocolMethod: protocolMethod) - pairingStorage.delete(topic: topic) - kms.deleteSymmetricKey(for: topic) - networkingInteractor.unsubscribe(topic: topic) - } -} diff --git a/Sources/Auth/Services/Wallet/WalletPairService.swift b/Sources/Auth/Services/Wallet/WalletPairService.swift deleted file mode 100644 index 7868c01a4..000000000 --- a/Sources/Auth/Services/Wallet/WalletPairService.swift +++ /dev/null @@ -1,38 +0,0 @@ -import Foundation -import WalletConnectKMS -import WalletConnectPairing -import WalletConnectNetworking - -actor WalletPairService { - enum Errors: Error { - case pairingAlreadyExist - } - - private let networkingInteractor: NetworkInteracting - private let kms: KeyManagementServiceProtocol - private let pairingStorage: WCPairingStorage - - init(networkingInteractor: NetworkInteracting, - kms: KeyManagementServiceProtocol, - pairingStorage: WCPairingStorage) { - self.networkingInteractor = networkingInteractor - self.kms = kms - self.pairingStorage = pairingStorage - } - - func pair(_ uri: WalletConnectURI) async throws { - guard !hasPairing(for: uri.topic) else { - throw Errors.pairingAlreadyExist - } - var pairing = WCPairing(uri: uri) - let symKey = try SymmetricKey(hex: uri.symKey) - try kms.setSymmetricKey(symKey, for: pairing.topic) - pairing.activate() - pairingStorage.setPairing(pairing) - try await networkingInteractor.subscribe(topic: pairing.topic) - } - - func hasPairing(for topic: String) -> Bool { - return pairingStorage.hasPairing(forTopic: topic) - } -} diff --git a/Sources/WalletConnectPairing/PairingClient.swift b/Sources/WalletConnectPairing/PairingClient.swift index 5e5dc30d0..682fc86fe 100644 --- a/Sources/WalletConnectPairing/PairingClient.swift +++ b/Sources/WalletConnectPairing/PairingClient.swift @@ -13,12 +13,14 @@ public class PairingClient: PairingRegisterer { private let networkingInteractor: NetworkInteracting private let pairingRequestsSubscriber: PairingRequestsSubscriber private let pairingsProvider: PairingsProvider + private let cleanupService: CleanupService init(appPairService: AppPairService, networkingInteractor: NetworkInteracting, logger: ConsoleLogging, walletPairService: WalletPairService, pairingRequestsSubscriber: PairingRequestsSubscriber, + cleanupService: CleanupService, socketConnectionStatusPublisher: AnyPublisher, pairingsProvider: PairingsProvider ) { @@ -27,6 +29,7 @@ public class PairingClient: PairingRegisterer { self.networkingInteractor = networkingInteractor self.socketConnectionStatusPublisher = socketConnectionStatusPublisher self.logger = logger + self.cleanupService = cleanupService self.pairingRequestsSubscriber = pairingRequestsSubscriber self.pairingsProvider = pairingsProvider } @@ -65,12 +68,21 @@ public class PairingClient: PairingRegisterer { } - public func disconnect(_ topic: String) { - + public func disconnect(topic: String) async throws { + } public func register(method: ProtocolMethod) -> AnyPublisher<(topic: String, request: RPCRequest), Never> { pairingRequestsSubscriber.subscribeForRequest(method) } + +#if DEBUG + /// Delete all stored data such as: pairings, keys + /// + /// - Note: Doesn't unsubscribe from topics + public func cleanup() throws { + try cleanupService.cleanup() + } +#endif } diff --git a/Sources/WalletConnectPairing/PairingClientFactory.swift b/Sources/WalletConnectPairing/PairingClientFactory.swift index 63a447038..582df039e 100644 --- a/Sources/WalletConnectPairing/PairingClientFactory.swift +++ b/Sources/WalletConnectPairing/PairingClientFactory.swift @@ -14,14 +14,16 @@ public struct PairingClientFactory { } static func create(logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, relayClient: RelayClient) -> PairingClient { + let pairingStore = PairingStorage(storage: SequenceStore(store: .init(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.pairings.rawValue))) let kms = KeyManagementService(keychain: keychainStorage) let serializer = Serializer(kms: kms) let history = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage) let networkingInteractor = NetworkingInteractor(relayClient: relayClient, serializer: serializer, logger: logger, rpcHistory: history) - let pairingStore = PairingStorage(storage: SequenceStore(store: .init(defaults: keyValueStorage, identifier: ""))) let appPairService = AppPairService(networkingInteractor: networkingInteractor, kms: kms, pairingStorage: pairingStore) let walletPairService = WalletPairService(networkingInteractor: networkingInteractor, kms: kms, pairingStorage: pairingStore) let pairingRequestsSubscriber = PairingRequestsSubscriber(networkingInteractor: networkingInteractor, pairingStorage: pairingStore, logger: logger) + let pairingsProvider = PairingsProvider(pairingStorage: pairingStore) + let cleanupService = CleanupService(pairingStore: pairingStore, kms: kms) return PairingClient( appPairService: appPairService, @@ -29,7 +31,9 @@ public struct PairingClientFactory { logger: logger, walletPairService: walletPairService, pairingRequestsSubscriber: pairingRequestsSubscriber, - socketConnectionStatusPublisher: relayClient.socketConnectionStatusPublisher + cleanupService: cleanupService, + socketConnectionStatusPublisher: relayClient.socketConnectionStatusPublisher, + pairingsProvider: pairingsProvider ) } } diff --git a/Sources/Auth/Services/Common/CleanupService.swift b/Sources/WalletConnectPairing/Services/Common/CleanupService.swift similarity index 100% rename from Sources/Auth/Services/Common/CleanupService.swift rename to Sources/WalletConnectPairing/Services/Common/CleanupService.swift diff --git a/Sources/Auth/StorageDomainIdentifiers.swift b/Sources/WalletConnectPairing/StorageDomainIdentifiers.swift similarity index 100% rename from Sources/Auth/StorageDomainIdentifiers.swift rename to Sources/WalletConnectPairing/StorageDomainIdentifiers.swift From 47a0ce7e00e5bb906d8dafff958646bd46981e35 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Wed, 28 Sep 2022 16:47:49 +0600 Subject: [PATCH 089/175] gitignore title --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 2261bbf75..dad29299f 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,6 @@ Package.resolved */fastlane/test_output */fastlane/README.md -# Example/IntegrationTests/Secrets +# Configuration Configuration.xcconfig From fa5b9c5f25b492f1fb1b42aa4438e303d39a3bdb Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 28 Sep 2022 14:32:45 +0200 Subject: [PATCH 090/175] refactor auth client for pairing --- Sources/Auth/AuthClient.swift | 29 ++--------------- Sources/Auth/AuthClientFactory.swift | 5 +-- .../Services/App/AppRespondSubscriber.swift | 19 ++--------- .../WalletConnectPairing/PairingClient.swift | 32 +++++++++++++++++-- .../PairingClientFactory.swift | 5 +++ .../App/PairingActivationService.swift | 15 +++++++++ .../Services/Common/PairingsProvider.swift | 13 +++++++- .../WalletConnectPairing/Types/Pairing.swift | 6 ++++ 8 files changed, 75 insertions(+), 49 deletions(-) create mode 100644 Sources/WalletConnectPairing/Services/App/PairingActivationService.swift diff --git a/Sources/Auth/AuthClient.swift b/Sources/Auth/AuthClient.swift index 64a263c76..af58c65a7 100644 --- a/Sources/Auth/AuthClient.swift +++ b/Sources/Auth/AuthClient.swift @@ -11,9 +11,7 @@ import WalletConnectRelay /// Access via `Auth.instance` public class AuthClient { enum Errors: Error { - case pairingUriWrongApiParam case unknownWalletAddress - case noPairingMatchingTopic } // MARK: - Public Properties @@ -34,9 +32,7 @@ public class AuthClient { authResponsePublisherSubject.eraseToAnyPublisher() } - public var pingResponsePublisher: AnyPublisher<(String), Never> { - pingResponsePublisherSubject.eraseToAnyPublisher() - } + /// Publisher that sends web socket connection status public let socketConnectionStatusPublisher: AnyPublisher @@ -50,15 +46,11 @@ public class AuthClient { private var authResponsePublisherSubject = PassthroughSubject<(id: RPCID, result: Result), Never>() private var authRequestPublisherSubject = PassthroughSubject() - private var pingResponsePublisherSubject = PassthroughSubject() private let appRequestService: AppRequestService private let appRespondSubscriber: AppRespondSubscriber private let walletRequestSubscriber: WalletRequestSubscriber private let walletRespondService: WalletRespondService - private let pairingStorage: WCPairingStorage private let pendingRequestsProvider: PendingRequestsProvider - private let pingService: PairingPingService - private let pairingsProvider: PairingsProvider private var account: Account? init(appRequestService: AppRequestService, @@ -68,10 +60,7 @@ public class AuthClient { account: Account?, pendingRequestsProvider: PendingRequestsProvider, logger: ConsoleLogging, - pairingStorage: WCPairingStorage, socketConnectionStatusPublisher: AnyPublisher, - pingService: PairingPingService, - pairingsProvider: PairingsProvider, pairingClient: PairingClient ) { self.appRequestService = appRequestService @@ -81,10 +70,7 @@ public class AuthClient { self.account = account self.pendingRequestsProvider = pendingRequestsProvider self.logger = logger - self.pairingStorage = pairingStorage self.socketConnectionStatusPublisher = socketConnectionStatusPublisher - self.pingService = pingService - self.pairingsProvider = pairingsProvider self.pairingClient = pairingClient setUpPublishers() } @@ -97,9 +83,6 @@ public class AuthClient { /// - When URI is invalid format or missing params /// - When topic is already in use public func pair(uri: WalletConnectURI) async throws { - guard uri.api == .auth else { - throw Errors.pairingUriWrongApiParam - } try await pairingClient.pair(uri: uri) } @@ -119,9 +102,7 @@ public class AuthClient { /// - Parameter topic: Pairing topic that wallet already subscribes for public func request(_ params: RequestParams, topic: String) async throws { logger.debug("Requesting Authentication on existing pairing") - guard pairingStorage.hasPairing(forTopic: topic) else { - throw Errors.noPairingMatchingTopic - } + try pairingClient.getPairing(for: topic) try await appRequestService.request(params: params, topic: topic) } @@ -145,7 +126,7 @@ public class AuthClient { } public func ping(topic: String) async throws { - try await pingService.ping(topic: topic) + try await pairingClient.ping(topic: topic) } public func getPairings() -> [Pairing] { @@ -167,9 +148,5 @@ public class AuthClient { walletRequestSubscriber.onRequest = { [unowned self] request in authRequestPublisherSubject.send(request) } - - pingService.onResponse = { [unowned self] topic in - pingResponsePublisherSubject.send(topic) - } } } diff --git a/Sources/Auth/AuthClientFactory.swift b/Sources/Auth/AuthClientFactory.swift index 59449c6fc..657db632b 100644 --- a/Sources/Auth/AuthClientFactory.swift +++ b/Sources/Auth/AuthClientFactory.swift @@ -22,12 +22,11 @@ public struct AuthClientFactory { let messageFormatter = SIWEMessageFormatter() let appRequestService = AppRequestService(networkingInteractor: networkingInteractor, kms: kms, appMetadata: metadata, logger: logger) let messageSigner = MessageSigner(signer: Signer()) - let appRespondSubscriber = AppRespondSubscriber(networkingInteractor: networkingInteractor, logger: logger, rpcHistory: history, signatureVerifier: messageSigner, messageFormatter: messageFormatter, pairingStorage: pairingStore) + let appRespondSubscriber = AppRespondSubscriber(networkingInteractor: networkingInteractor, logger: logger, rpcHistory: history, signatureVerifier: messageSigner, messageFormatter: messageFormatter) let walletErrorResponder = WalletErrorResponder(networkingInteractor: networkingInteractor, logger: logger, kms: kms, rpcHistory: history) let walletRequestSubscriber = WalletRequestSubscriber(networkingInteractor: networkingInteractor, logger: logger, kms: kms, messageFormatter: messageFormatter, address: account?.address, walletErrorResponder: walletErrorResponder) let walletRespondService = WalletRespondService(networkingInteractor: networkingInteractor, logger: logger, kms: kms, rpcHistory: history, walletErrorResponder: walletErrorResponder) let pendingRequestsProvider = PendingRequestsProvider(rpcHistory: history) - let pingService = PairingPingService(pairingStorage: pairingStore, networkingInteractor: networkingInteractor, logger: logger) //TODO - fix this - singleton? let pairingClient = PairingClientFactory.create(relayClient: relayClient) @@ -39,9 +38,7 @@ public struct AuthClientFactory { account: account, pendingRequestsProvider: pendingRequestsProvider, logger: logger, - pairingStorage: pairingStore, socketConnectionStatusPublisher: relayClient.socketConnectionStatusPublisher, - pingService: pingService, pairingClient: pairingClient) } } diff --git a/Sources/Auth/Services/App/AppRespondSubscriber.swift b/Sources/Auth/Services/App/AppRespondSubscriber.swift index 542d4e27c..7e47f5cd8 100644 --- a/Sources/Auth/Services/App/AppRespondSubscriber.swift +++ b/Sources/Auth/Services/App/AppRespondSubscriber.swift @@ -7,7 +7,6 @@ import WalletConnectPairing class AppRespondSubscriber { private let networkingInteractor: NetworkInteracting - private let pairingStorage: WCPairingStorage private let logger: ConsoleLogging private let rpcHistory: RPCHistory private let signatureVerifier: MessageSignatureVerifying @@ -20,14 +19,12 @@ class AppRespondSubscriber { logger: ConsoleLogging, rpcHistory: RPCHistory, signatureVerifier: MessageSignatureVerifying, - messageFormatter: SIWEMessageFormatting, - pairingStorage: WCPairingStorage) { + messageFormatter: SIWEMessageFormatting) { self.networkingInteractor = networkingInteractor self.logger = logger self.rpcHistory = rpcHistory self.signatureVerifier = signatureVerifier self.messageFormatter = messageFormatter - self.pairingStorage = pairingStorage subscribeForResponse() } @@ -41,7 +38,8 @@ class AppRespondSubscriber { networkingInteractor.responseSubscription(on: AuthRequestProtocolMethod()) .sink { [unowned self] (payload: ResponseSubscriptionPayload) in - activatePairingIfNeeded(id: payload.id) + //TODO - call pairing client to activate +// activatePairingIfNeeded(id: payload.id) networkingInteractor.unsubscribe(topic: payload.topic) let requestId = payload.id @@ -63,15 +61,4 @@ class AppRespondSubscriber { }.store(in: &publishers) } - - private func activatePairingIfNeeded(id: RPCID) { - guard let record = rpcHistory.get(recordId: id) else { return } - let pairingTopic = record.topic - guard var pairing = pairingStorage.getPairing(forTopic: pairingTopic) else { return } - if !pairing.active { - pairing.activate() - } else { - try? pairing.updateExpiry() - } - } } diff --git a/Sources/WalletConnectPairing/PairingClient.swift b/Sources/WalletConnectPairing/PairingClient.swift index 682fc86fe..af9898384 100644 --- a/Sources/WalletConnectPairing/PairingClient.swift +++ b/Sources/WalletConnectPairing/PairingClient.swift @@ -6,21 +6,34 @@ import Combine import JSONRPC public class PairingClient: PairingRegisterer { + + public var pingResponsePublisher: AnyPublisher<(String), Never> { + pingResponsePublisherSubject.eraseToAnyPublisher() + } + public let socketConnectionStatusPublisher: AnyPublisher + private let walletPairService: WalletPairService private let appPairService: AppPairService - public let socketConnectionStatusPublisher: AnyPublisher + private var pingResponsePublisherSubject = PassthroughSubject() private let logger: ConsoleLogging + private let pingService: PairingPingService private let networkingInteractor: NetworkInteracting private let pairingRequestsSubscriber: PairingRequestsSubscriber private let pairingsProvider: PairingsProvider + private let deletePairingService: DeletePairingService + private let pairingStorage: WCPairingStorage + private let cleanupService: CleanupService init(appPairService: AppPairService, networkingInteractor: NetworkInteracting, logger: ConsoleLogging, walletPairService: WalletPairService, + deletePairingService: DeletePairingService, pairingRequestsSubscriber: PairingRequestsSubscriber, + pairingStorage: WCPairingStorage, cleanupService: CleanupService, + pingService: PairingPingService, socketConnectionStatusPublisher: AnyPublisher, pairingsProvider: PairingsProvider ) { @@ -29,10 +42,20 @@ public class PairingClient: PairingRegisterer { self.networkingInteractor = networkingInteractor self.socketConnectionStatusPublisher = socketConnectionStatusPublisher self.logger = logger + self.pairingStorage = pairingStorage + self.deletePairingService = deletePairingService self.cleanupService = cleanupService + self.pingService = pingService self.pairingRequestsSubscriber = pairingRequestsSubscriber self.pairingsProvider = pairingsProvider } + + private func setUpPublishers() { + pingService.onResponse = { [unowned self] topic in + pingResponsePublisherSubject.send(topic) + } + } + /// For wallet to establish a pairing /// Wallet should call this function in order to accept peer's pairing proposal and be able to subscribe for future requests. /// - Parameter uri: Pairing URI that is commonly presented as a QR code by a dapp or delivered with universal linking. @@ -64,11 +87,16 @@ public class PairingClient: PairingRegisterer { pairingsProvider.getPairings() } - public func ping(_ topic: String) { + public func getPairing(for topic: String) throws -> Pairing { + try pairingsProvider.getPairing(for: topic) + } + public func ping(topic: String) async throws { + try await pingService.ping(topic: topic) } public func disconnect(topic: String) async throws { + try await deletePairingService.delete(topic: topic) } diff --git a/Sources/WalletConnectPairing/PairingClientFactory.swift b/Sources/WalletConnectPairing/PairingClientFactory.swift index 582df039e..44cc9dac0 100644 --- a/Sources/WalletConnectPairing/PairingClientFactory.swift +++ b/Sources/WalletConnectPairing/PairingClientFactory.swift @@ -24,14 +24,19 @@ public struct PairingClientFactory { let pairingRequestsSubscriber = PairingRequestsSubscriber(networkingInteractor: networkingInteractor, pairingStorage: pairingStore, logger: logger) let pairingsProvider = PairingsProvider(pairingStorage: pairingStore) let cleanupService = CleanupService(pairingStore: pairingStore, kms: kms) + let deletePairingService = DeletePairingService(networkingInteractor: networkingInteractor, kms: kms, pairingStorage: pairingStore, logger: logger) + let pingService = PairingPingService(pairingStorage: pairingStore, networkingInteractor: networkingInteractor, logger: logger) return PairingClient( appPairService: appPairService, networkingInteractor: networkingInteractor, logger: logger, walletPairService: walletPairService, + deletePairingService: deletePairingService, pairingRequestsSubscriber: pairingRequestsSubscriber, + pairingStorage: pairingStore, cleanupService: cleanupService, + pingService: pingService, socketConnectionStatusPublisher: relayClient.socketConnectionStatusPublisher, pairingsProvider: pairingsProvider ) diff --git a/Sources/WalletConnectPairing/Services/App/PairingActivationService.swift b/Sources/WalletConnectPairing/Services/App/PairingActivationService.swift new file mode 100644 index 000000000..5062dafc9 --- /dev/null +++ b/Sources/WalletConnectPairing/Services/App/PairingActivationService.swift @@ -0,0 +1,15 @@ +// + +import Foundation + +// TODO +//private func activatePairingIfNeeded(id: RPCID) { +// guard let record = rpcHistory.get(recordId: id) else { return } +// let pairingTopic = record.topic +// guard var pairing = pairingStorage.getPairing(forTopic: pairingTopic) else { return } +// if !pairing.active { +// pairing.activate() +// } else { +// try? pairing.updateExpiry() +// } +//} diff --git a/Sources/WalletConnectPairing/Services/Common/PairingsProvider.swift b/Sources/WalletConnectPairing/Services/Common/PairingsProvider.swift index 60b854361..d7ee75a48 100644 --- a/Sources/WalletConnectPairing/Services/Common/PairingsProvider.swift +++ b/Sources/WalletConnectPairing/Services/Common/PairingsProvider.swift @@ -1,6 +1,9 @@ import Foundation public class PairingsProvider { + enum Errors: Error { + case noPairingMatchingTopic + } private let pairingStorage: WCPairingStorage public init(pairingStorage: WCPairingStorage) { @@ -9,6 +12,14 @@ public class PairingsProvider { func getPairings() -> [Pairing] { pairingStorage.getAll() - .map {Pairing(topic: $0.topic, peer: $0.peerMetadata, expiryDate: $0.expiryDate)} + .map {Pairing($0)} + } + + + public func getPairing(for topic: String) throws -> Pairing { + guard let pairing = pairingStorage.getPairing(forTopic: topic) else { + throw Errors.noPairingMatchingTopic + } + return Pairing(pairing) } } diff --git a/Sources/WalletConnectPairing/Types/Pairing.swift b/Sources/WalletConnectPairing/Types/Pairing.swift index f9886d6a2..03ed01a41 100644 --- a/Sources/WalletConnectPairing/Types/Pairing.swift +++ b/Sources/WalletConnectPairing/Types/Pairing.swift @@ -12,4 +12,10 @@ public struct Pairing { self.peer = peer self.expiryDate = expiryDate } + + init(_ pairing: WCPairing) { + self.topic = pairing.topic + self.peer = pairing.peerMetadata + self.expiryDate = pairing.expiryDate + } } From 8bdef8caa26887e98b93bcc3c5a38f4b5a25e79e Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 29 Sep 2022 08:52:13 +0200 Subject: [PATCH 091/175] pass auth tests --- Example/IntegrationTests/Auth/AuthTests.swift | 41 +++++++++++-------- .../Sign/SignClientTests.swift | 5 ++- Sources/Auth/Auth.swift | 4 +- Sources/Auth/AuthClientFactory.swift | 11 ++--- .../Wallet/WalletRequestSubscriber.swift | 17 +++++--- Sources/WalletConnectPairing/Pair.swift | 31 ++++++++++++++ .../WalletConnectPairing/PairingClient.swift | 3 +- .../PairingClientFactory.swift | 2 +- .../Engine/Common/PairingEngine.swift | 16 ++++---- .../DisconnectService.swift | 17 ++++---- Sources/WalletConnectSign/Sign/Sign.swift | 4 +- .../WalletConnectSign/Sign/SignClient.swift | 6 ++- .../Sign/SignClientFactory.swift | 12 +++--- 13 files changed, 109 insertions(+), 60 deletions(-) create mode 100644 Sources/WalletConnectPairing/Pair.swift rename Sources/WalletConnectSign/{Engine/Common => Services}/DisconnectService.swift (54%) diff --git a/Example/IntegrationTests/Auth/AuthTests.swift b/Example/IntegrationTests/Auth/AuthTests.swift index 4ee2bf33d..7a6ce01b7 100644 --- a/Example/IntegrationTests/Auth/AuthTests.swift +++ b/Example/IntegrationTests/Auth/AuthTests.swift @@ -5,6 +5,7 @@ import WalletConnectUtils import WalletConnectRelay import Combine @testable import Auth +import WalletConnectPairing final class AuthTests: XCTestCase { var app: AuthClient! @@ -23,14 +24,22 @@ final class AuthTests: XCTestCase { let projectId = "3ca2919724fbfa5456a25194e369a8b4" let keychain = KeychainStorageMock() let relayClient = RelayClient(relayHost: URLConfig.relayHost, projectId: projectId, keychainStorage: keychain, socketFactory: SocketFactory(), logger: logger) + let keyValueStorage = RuntimeKeyValueStorage() + + let pairingClient = PairingClientFactory.create( + logger: logger, + keyValueStorage: keyValueStorage, + keychainStorage: keychain, + relayClient: relayClient) return AuthClientFactory.create( metadata: AppMetadata(name: name, description: "", url: "", icons: [""]), account: account, logger: logger, - keyValueStorage: RuntimeKeyValueStorage(), + keyValueStorage: keyValueStorage, keychainStorage: keychain, - relayClient: relayClient) + relayClient: relayClient, + pairingClient: pairingClient) } func testRequest() async { @@ -101,18 +110,18 @@ final class AuthTests: XCTestCase { .store(in: &publishers) wait(for: [responseExpectation], timeout: 2) } - - func testPing() async { - let pingExpectation = expectation(description: "expects ping response") - let uri = try! await app.request(RequestParams.stub()) - try! await wallet.pair(uri: uri) - try! await wallet.ping(topic: uri.topic) - wallet.pingResponsePublisher - .sink { topic in - XCTAssertEqual(topic, uri.topic) - pingExpectation.fulfill() - } - .store(in: &publishers) - wait(for: [pingExpectation], timeout: 5) - } +// TODO - uncomment +// func testPing() async { +// let pingExpectation = expectation(description: "expects ping response") +// let uri = try! await app.request(RequestParams.stub()) +// try! await wallet.pair(uri: uri) +// try! await wallet.ping(topic: uri.topic) +// wallet.pingResponsePublisher +// .sink { topic in +// XCTAssertEqual(topic, uri.topic) +// pingExpectation.fulfill() +// } +// .store(in: &publishers) +// wait(for: [pingExpectation], timeout: 5) +// } } diff --git a/Example/IntegrationTests/Sign/SignClientTests.swift b/Example/IntegrationTests/Sign/SignClientTests.swift index 20bd91504..eb68b232a 100644 --- a/Example/IntegrationTests/Sign/SignClientTests.swift +++ b/Example/IntegrationTests/Sign/SignClientTests.swift @@ -4,6 +4,7 @@ import JSONRPC @testable import WalletConnectKMS @testable import WalletConnectSign @testable import WalletConnectRelay +import WalletConnectPairing final class SignClientTests: XCTestCase { @@ -27,12 +28,14 @@ final class SignClientTests: XCTestCase { socketConnectionType: .automatic, logger: logger ) + let pairingClient = PairingClientFactory.create(relayClient: relayClient) let client = SignClientFactory.create( metadata: AppMetadata(name: name, description: "", url: "", icons: [""]), logger: logger, keyValueStorage: RuntimeKeyValueStorage(), keychainStorage: keychain, - relayClient: relayClient + relayClient: relayClient, + pairingClient: pairingClient ) return ClientDelegate(client: client) } diff --git a/Sources/Auth/Auth.swift b/Sources/Auth/Auth.swift index b209104bd..49d7e888b 100644 --- a/Sources/Auth/Auth.swift +++ b/Sources/Auth/Auth.swift @@ -1,5 +1,6 @@ import Foundation import WalletConnectRelay +import WalletConnectPairing import Combine /// Auth instatnce wrapper @@ -23,7 +24,8 @@ public class Auth { return AuthClientFactory.create( metadata: config.metadata, account: config.account, - relayClient: Relay.instance) + relayClient: Relay.instance, + pairingClient: Pair.instance) }() private static var config: Config? diff --git a/Sources/Auth/AuthClientFactory.swift b/Sources/Auth/AuthClientFactory.swift index 657db632b..4513eb53c 100644 --- a/Sources/Auth/AuthClientFactory.swift +++ b/Sources/Auth/AuthClientFactory.swift @@ -7,14 +7,14 @@ import WalletConnectNetworking public struct AuthClientFactory { - public static func create(metadata: AppMetadata, account: Account?, relayClient: RelayClient) -> AuthClient { + public static func create(metadata: AppMetadata, account: Account?, relayClient: RelayClient, pairingClient: PairingClient) -> AuthClient { let logger = ConsoleLogger(loggingLevel: .off) let keyValueStorage = UserDefaults.standard let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk") - return AuthClientFactory.create(metadata: metadata, account: account, logger: logger, keyValueStorage: keyValueStorage, keychainStorage: keychainStorage, relayClient: relayClient) + return AuthClientFactory.create(metadata: metadata, account: account, logger: logger, keyValueStorage: keyValueStorage, keychainStorage: keychainStorage, relayClient: relayClient, pairingClient: pairingClient) } - static func create(metadata: AppMetadata, account: Account?, logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, relayClient: RelayClient) -> AuthClient { + static func create(metadata: AppMetadata, account: Account?, logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, relayClient: RelayClient, pairingClient: PairingClient) -> AuthClient { let kms = KeyManagementService(keychain: keychainStorage) let serializer = Serializer(kms: kms) let history = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage) @@ -24,13 +24,10 @@ public struct AuthClientFactory { let messageSigner = MessageSigner(signer: Signer()) let appRespondSubscriber = AppRespondSubscriber(networkingInteractor: networkingInteractor, logger: logger, rpcHistory: history, signatureVerifier: messageSigner, messageFormatter: messageFormatter) let walletErrorResponder = WalletErrorResponder(networkingInteractor: networkingInteractor, logger: logger, kms: kms, rpcHistory: history) - let walletRequestSubscriber = WalletRequestSubscriber(networkingInteractor: networkingInteractor, logger: logger, kms: kms, messageFormatter: messageFormatter, address: account?.address, walletErrorResponder: walletErrorResponder) + let walletRequestSubscriber = WalletRequestSubscriber(networkingInteractor: networkingInteractor, logger: logger, kms: kms, messageFormatter: messageFormatter, address: account?.address, walletErrorResponder: walletErrorResponder, pairingRegisterer: pairingClient) let walletRespondService = WalletRespondService(networkingInteractor: networkingInteractor, logger: logger, kms: kms, rpcHistory: history, walletErrorResponder: walletErrorResponder) let pendingRequestsProvider = PendingRequestsProvider(rpcHistory: history) - //TODO - fix this - singleton? - let pairingClient = PairingClientFactory.create(relayClient: relayClient) - return AuthClient(appRequestService: appRequestService, appRespondSubscriber: appRespondSubscriber, walletRequestSubscriber: walletRequestSubscriber, diff --git a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift index 05d028fe1..95067926f 100644 --- a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift +++ b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift @@ -4,6 +4,7 @@ import JSONRPC import WalletConnectNetworking import WalletConnectUtils import WalletConnectKMS +import WalletConnectPairing class WalletRequestSubscriber { private let networkingInteractor: NetworkInteracting @@ -13,6 +14,7 @@ class WalletRequestSubscriber { private var publishers = [AnyCancellable]() private let messageFormatter: SIWEMessageFormatting private let walletErrorResponder: WalletErrorResponder + private let pairingRegisterer: PairingRegisterer var onRequest: ((AuthRequest) -> Void)? init(networkingInteractor: NetworkInteracting, @@ -20,29 +22,32 @@ class WalletRequestSubscriber { kms: KeyManagementServiceProtocol, messageFormatter: SIWEMessageFormatting, address: String?, - walletErrorResponder: WalletErrorResponder) { + walletErrorResponder: WalletErrorResponder, + pairingRegisterer: PairingRegisterer) { self.networkingInteractor = networkingInteractor self.logger = logger self.kms = kms self.address = address self.messageFormatter = messageFormatter self.walletErrorResponder = walletErrorResponder + self.pairingRegisterer = pairingRegisterer subscribeForRequest() } private func subscribeForRequest() { guard let address = address else { return } - networkingInteractor.requestSubscription(on: AuthRequestProtocolMethod()) - .sink { [unowned self] (payload: RequestSubscriptionPayload) in + pairingRegisterer.register(method: AuthRequestProtocolMethod()) + .sink { [unowned self] (topic, request) in + let params = try! request.params!.get(AuthRequestParams.self) logger.debug("WalletRequestSubscriber: Received request") - guard let message = messageFormatter.formatMessage(from: payload.request.payloadParams, address: address) else { + guard let message = messageFormatter.formatMessage(from: params.payloadParams, address: address) else { Task(priority: .high) { - try? await walletErrorResponder.respondError(AuthError.malformedRequestParams, requestId: payload.id) + try? await walletErrorResponder.respondError(AuthError.malformedRequestParams, requestId: request.id!) } return } - onRequest?(.init(id: payload.id, message: message)) + onRequest?(.init(id: request.id!, message: message)) }.store(in: &publishers) } } diff --git a/Sources/WalletConnectPairing/Pair.swift b/Sources/WalletConnectPairing/Pair.swift new file mode 100644 index 000000000..40fdcec3b --- /dev/null +++ b/Sources/WalletConnectPairing/Pair.swift @@ -0,0 +1,31 @@ +import Foundation +import WalletConnectRelay +import Combine + +extension Pair { + struct Config { + let metadata: AppMetadata + } +} + +public class Pair { + + /// Pairing client instance + public static var instance: PairingClient = { + guard let config = Pair.config else { + fatalError("Error - you must call Pair.configure(_:) before accessing the shared instance.") + } + return PairingClientFactory.create(relayClient: Relay.instance) + }() + + private static var config: Config? + + private init() { } + + /// Pairing instance config method + /// - Parameters: + /// - metadata: App metadata + static public func configure(metadata: AppMetadata) { + Pair.config = Pair.Config(metadata: metadata) + } +} diff --git a/Sources/WalletConnectPairing/PairingClient.swift b/Sources/WalletConnectPairing/PairingClient.swift index af9898384..00babd086 100644 --- a/Sources/WalletConnectPairing/PairingClient.swift +++ b/Sources/WalletConnectPairing/PairingClient.swift @@ -101,7 +101,8 @@ public class PairingClient: PairingRegisterer { } public func register(method: ProtocolMethod) -> AnyPublisher<(topic: String, request: RPCRequest), Never> { - pairingRequestsSubscriber.subscribeForRequest(method) + logger.debug("Pairing Client - regitring for \(method.method)") + return pairingRequestsSubscriber.subscribeForRequest(method) } #if DEBUG diff --git a/Sources/WalletConnectPairing/PairingClientFactory.swift b/Sources/WalletConnectPairing/PairingClientFactory.swift index 44cc9dac0..a74bd04f2 100644 --- a/Sources/WalletConnectPairing/PairingClientFactory.swift +++ b/Sources/WalletConnectPairing/PairingClientFactory.swift @@ -13,7 +13,7 @@ public struct PairingClientFactory { return PairingClientFactory.create(logger: logger, keyValueStorage: keyValueStorage, keychainStorage: keychainStorage, relayClient: relayClient) } - static func create(logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, relayClient: RelayClient) -> PairingClient { + public static func create(logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, relayClient: RelayClient) -> PairingClient { let pairingStore = PairingStorage(storage: SequenceStore(store: .init(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.pairings.rawValue))) let kms = KeyManagementService(keychain: keychainStorage) let serializer = Serializer(kms: kms) diff --git a/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift b/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift index deee4888b..3a51e729c 100644 --- a/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift @@ -91,15 +91,15 @@ private extension PairingEngine { } } .store(in: &publishers) - let protocolMethod = PairingPingProtocolMethod() +// let protocolMethod = PairingPingProtocolMethod() - networkingInteractor.requestSubscription(on: protocolMethod) - .sink { [unowned self] (payload: RequestSubscriptionPayload) in - Task(priority: .high) { - try await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, protocolMethod: protocolMethod) - } - } - .store(in: &publishers) +// networkingInteractor.requestSubscription(on: protocolMethod) +// .sink { [unowned self] (payload: RequestSubscriptionPayload) in +// Task(priority: .high) { +// try await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, protocolMethod: protocolMethod) +// } +// } +// .store(in: &publishers) } func setupExpirationHandling() { diff --git a/Sources/WalletConnectSign/Engine/Common/DisconnectService.swift b/Sources/WalletConnectSign/Services/DisconnectService.swift similarity index 54% rename from Sources/WalletConnectSign/Engine/Common/DisconnectService.swift rename to Sources/WalletConnectSign/Services/DisconnectService.swift index d0b417a7f..b4975c749 100644 --- a/Sources/WalletConnectSign/Engine/Common/DisconnectService.swift +++ b/Sources/WalletConnectSign/Services/DisconnectService.swift @@ -6,24 +6,21 @@ class DisconnectService { case objectForTopicNotFound } - private let deletePairingService: DeletePairingService private let deleteSessionService: DeleteSessionService - private let pairingStorage: WCPairingStorage private let sessionStorage: WCSessionStorage + private let pairingClient: PairingClient - init(deletePairingService: DeletePairingService, - deleteSessionService: DeleteSessionService, - pairingStorage: WCPairingStorage, - sessionStorage: WCSessionStorage) { - self.deletePairingService = deletePairingService + init(deleteSessionService: DeleteSessionService, + sessionStorage: WCSessionStorage, + pairingClient: PairingClient) { self.deleteSessionService = deleteSessionService - self.pairingStorage = pairingStorage self.sessionStorage = sessionStorage + self.pairingClient = pairingClient } func disconnect(topic: String) async throws { - if pairingStorage.hasPairing(forTopic: topic) { - try await deletePairingService.delete(topic: topic) + if let _ = try? pairingClient.getPairing(for: topic) { + try await pairingClient.disconnect(topic: topic) } else if sessionStorage.hasSession(forTopic: topic) { try await deleteSessionService.delete(topic: topic) } else { diff --git a/Sources/WalletConnectSign/Sign/Sign.swift b/Sources/WalletConnectSign/Sign/Sign.swift index 1005410cc..5effea2db 100644 --- a/Sources/WalletConnectSign/Sign/Sign.swift +++ b/Sources/WalletConnectSign/Sign/Sign.swift @@ -4,6 +4,7 @@ import JSONRPC import WalletConnectUtils import WalletConnectRelay import WalletConnectNetworking +import WalletConnectPairing public typealias Account = WalletConnectUtils.Account public typealias Blockchain = WalletConnectUtils.Blockchain @@ -30,7 +31,8 @@ public class Sign { } return SignClientFactory.create( metadata: metadata, - relayClient: Relay.instance + relayClient: Relay.instance, + pairingClient: Pair.instance ) }() diff --git a/Sources/WalletConnectSign/Sign/SignClient.swift b/Sources/WalletConnectSign/Sign/SignClient.swift index 83ba5cbb0..d448d2338 100644 --- a/Sources/WalletConnectSign/Sign/SignClient.swift +++ b/Sources/WalletConnectSign/Sign/SignClient.swift @@ -95,7 +95,7 @@ public final class SignClient { public let logger: ConsoleLogging // MARK: - Private properties - + private let pairingClient: PairingClient private let relayClient: RelayClient private let pairingEngine: PairingEngine private let pairEngine: PairEngine @@ -137,7 +137,8 @@ public final class SignClient { controllerSessionStateMachine: ControllerSessionStateMachine, disconnectService: DisconnectService, history: RPCHistory, - cleanupService: CleanupService + cleanupService: CleanupService, + pairingClient: PairingClient ) { self.logger = logger self.relayClient = relayClient @@ -152,6 +153,7 @@ public final class SignClient { self.history = history self.cleanupService = cleanupService self.disconnectService = disconnectService + self.pairingClient = pairingClient setUpConnectionObserving() setUpEnginesCallbacks() } diff --git a/Sources/WalletConnectSign/Sign/SignClientFactory.swift b/Sources/WalletConnectSign/Sign/SignClientFactory.swift index b26989d46..ec376df34 100644 --- a/Sources/WalletConnectSign/Sign/SignClientFactory.swift +++ b/Sources/WalletConnectSign/Sign/SignClientFactory.swift @@ -16,14 +16,14 @@ public struct SignClientFactory { /// - keyValueStorage: by default WalletConnect SDK will store sequences in UserDefaults /// /// WalletConnect Client is not a singleton but once you create an instance, you should not deinitialize it. Usually only one instance of a client is required in the application. - public static func create(metadata: AppMetadata, relayClient: RelayClient) -> SignClient { + public static func create(metadata: AppMetadata, relayClient: RelayClient, pairingClient: PairingClient) -> SignClient { let logger = ConsoleLogger(loggingLevel: .off) let keyValueStorage = UserDefaults.standard let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk") - return SignClientFactory.create(metadata: metadata, logger: logger, keyValueStorage: keyValueStorage, keychainStorage: keychainStorage, relayClient: relayClient) + return SignClientFactory.create(metadata: metadata, logger: logger, keyValueStorage: keyValueStorage, keychainStorage: keychainStorage, relayClient: relayClient, pairingClient: pairingClient) } - static func create(metadata: AppMetadata, logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, relayClient: RelayClient) -> SignClient { + static func create(metadata: AppMetadata, logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, relayClient: RelayClient, pairingClient: PairingClient) -> SignClient { let kms = KeyManagementService(keychain: keychainStorage) let serializer = Serializer(kms: kms) let rpcHistory = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage) @@ -39,9 +39,8 @@ public struct SignClientFactory { let pairEngine = PairEngine(networkingInteractor: networkingInteractor, kms: kms, pairingStore: pairingStore) let approveEngine = ApproveEngine(networkingInteractor: networkingInteractor, proposalPayloadsStore: proposalPayloadsStore, sessionToPairingTopic: sessionToPairingTopic, metadata: metadata, kms: kms, logger: logger, pairingStore: pairingStore, sessionStore: sessionStore) let cleanupService = CleanupService(pairingStore: pairingStore, sessionStore: sessionStore, kms: kms, sessionToPairingTopic: sessionToPairingTopic) - let deletePairingService = DeletePairingService(networkingInteractor: networkingInteractor, kms: kms, pairingStorage: pairingStore, logger: logger) let deleteSessionService = DeleteSessionService(networkingInteractor: networkingInteractor, kms: kms, sessionStore: sessionStore, logger: logger) - let disconnectService = DisconnectService(deletePairingService: deletePairingService, deleteSessionService: deleteSessionService, pairingStorage: pairingStore, sessionStorage: sessionStore) + let disconnectService = DisconnectService(deleteSessionService: deleteSessionService, sessionStorage: sessionStore, pairingClient: pairingClient) let sessionPingService = SessionPingService(sessionStorage: sessionStore, networkingInteractor: networkingInteractor, logger: logger) let pairingPingService = PairingPingService(pairingStorage: pairingStore, networkingInteractor: networkingInteractor, logger: logger) @@ -57,7 +56,8 @@ public struct SignClientFactory { nonControllerSessionStateMachine: nonControllerSessionStateMachine, controllerSessionStateMachine: controllerSessionStateMachine, disconnectService: disconnectService, history: rpcHistory, - cleanupService: cleanupService + cleanupService: cleanupService, + pairingClient: pairingClient ) return client } From d5e514cc98d51f21c523a67c1e6cce364a5ecc20 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 29 Sep 2022 09:39:53 +0200 Subject: [PATCH 092/175] refactor pairing registry --- .../Wallet/WalletRequestSubscriber.swift | 9 +++--- .../WalletConnectNetworking/SetStore.swift | 18 ----------- .../WalletConnectPairing/PairingClient.swift | 2 +- .../PairingRegisterer.swift | 2 +- .../PairingRequestsSubscriber.swift | 32 ++++++++++++------- .../Types/PairingProtocolMethod.swift | 9 ++++++ Sources/WalletConnectPush/PushClient.swift | 5 ++- .../Extensions/Publisher.swift | 2 +- Sources/WalletConnectUtils/SetStore.swift | 21 ++++++++++++ 9 files changed, 59 insertions(+), 41 deletions(-) delete mode 100644 Sources/WalletConnectNetworking/SetStore.swift rename Sources/{WalletConnectNetworking => WalletConnectUtils}/Extensions/Publisher.swift (94%) create mode 100644 Sources/WalletConnectUtils/SetStore.swift diff --git a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift index 95067926f..bd5c5c251 100644 --- a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift +++ b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift @@ -38,16 +38,15 @@ class WalletRequestSubscriber { guard let address = address else { return } pairingRegisterer.register(method: AuthRequestProtocolMethod()) - .sink { [unowned self] (topic, request) in - let params = try! request.params!.get(AuthRequestParams.self) + .sink { [unowned self] (payload: RequestSubscriptionPayload) in logger.debug("WalletRequestSubscriber: Received request") - guard let message = messageFormatter.formatMessage(from: params.payloadParams, address: address) else { + guard let message = messageFormatter.formatMessage(from: payload.request.payloadParams, address: address) else { Task(priority: .high) { - try? await walletErrorResponder.respondError(AuthError.malformedRequestParams, requestId: request.id!) + try? await walletErrorResponder.respondError(AuthError.malformedRequestParams, requestId: payload.id) } return } - onRequest?(.init(id: request.id!, message: message)) + onRequest?(.init(id: payload.id, message: message)) }.store(in: &publishers) } } diff --git a/Sources/WalletConnectNetworking/SetStore.swift b/Sources/WalletConnectNetworking/SetStore.swift deleted file mode 100644 index a812824d2..000000000 --- a/Sources/WalletConnectNetworking/SetStore.swift +++ /dev/null @@ -1,18 +0,0 @@ - -import Foundation - -actor SetStore { - private var store: Set = Set() - - func insert(_ element: T) { - store.insert(element) - } - - @discardableResult func remove(_ element: T) -> T? { - store.remove(element) - } - - func contains(_ element: T) -> Bool { - return store.contains(element) - } -} diff --git a/Sources/WalletConnectPairing/PairingClient.swift b/Sources/WalletConnectPairing/PairingClient.swift index 00babd086..60da21435 100644 --- a/Sources/WalletConnectPairing/PairingClient.swift +++ b/Sources/WalletConnectPairing/PairingClient.swift @@ -100,7 +100,7 @@ public class PairingClient: PairingRegisterer { } - public func register(method: ProtocolMethod) -> AnyPublisher<(topic: String, request: RPCRequest), Never> { + public func register(method: ProtocolMethod) -> AnyPublisher, Never> { logger.debug("Pairing Client - regitring for \(method.method)") return pairingRequestsSubscriber.subscribeForRequest(method) } diff --git a/Sources/WalletConnectPairing/PairingRegisterer.swift b/Sources/WalletConnectPairing/PairingRegisterer.swift index 5fd2b5a7a..dac4d1ddc 100644 --- a/Sources/WalletConnectPairing/PairingRegisterer.swift +++ b/Sources/WalletConnectPairing/PairingRegisterer.swift @@ -4,5 +4,5 @@ import Combine import JSONRPC public protocol PairingRegisterer { - func register(method: ProtocolMethod) -> AnyPublisher<(topic: String, request: RPCRequest), Never> + func register(method: ProtocolMethod) -> AnyPublisher, Never> } diff --git a/Sources/WalletConnectPairing/PairingRequestsSubscriber.swift b/Sources/WalletConnectPairing/PairingRequestsSubscriber.swift index d33c1afc5..8562bf60b 100644 --- a/Sources/WalletConnectPairing/PairingRequestsSubscriber.swift +++ b/Sources/WalletConnectPairing/PairingRequestsSubscriber.swift @@ -8,28 +8,36 @@ public class PairingRequestsSubscriber { private let networkingInteractor: NetworkInteracting private let pairingStorage: PairingStorage private var publishers = Set() + private var protocolMethods = SetStore() init(networkingInteractor: NetworkInteracting, pairingStorage: PairingStorage, logger: ConsoleLogging) { self.networkingInteractor = networkingInteractor self.pairingStorage = pairingStorage + handleUnregisteredRequests() } - func subscribeForRequest(_ protocolMethod: ProtocolMethod) -> AnyPublisher<(topic: String, request: RPCRequest), Never> { - let publisherSubject = PassthroughSubject<(topic: String, request: RPCRequest), Never>() + func subscribeForRequest(_ protocolMethod: ProtocolMethod) -> AnyPublisher, Never> { + + Task(priority: .high) { await protocolMethods.insert(protocolMethod.method) } + + let publisherSubject = PassthroughSubject, Never>() + + networkingInteractor.requestSubscription(on: protocolMethod).sink { (payload: RequestSubscriptionPayload) in + publisherSubject.send(payload) + }.store(in: &publishers) + + return publisherSubject.eraseToAnyPublisher() + } + + func handleUnregisteredRequests() { networkingInteractor.requestPublisher + .asyncFilter { [unowned self] in await !protocolMethods.contains($0.request.method)} .sink { [unowned self] topic, request in - if request.method != protocolMethod.method { - Task(priority: .high) { - // TODO - spec tag - try await networkingInteractor.respondError(topic: topic, requestId: request.id!, protocolMethod: protocolMethod, reason: PairError.methodUnsupported) - } - } else { - publisherSubject.send((topic, request)) + Task(priority: .high) { + let protocolMethod = UnsupportedProtocolMethod(method: request.method) + try await networkingInteractor.respondError(topic: topic, requestId: request.id!, protocolMethod: protocolMethod, reason: PairError.methodUnsupported) } - }.store(in: &publishers) - - return publisherSubject.eraseToAnyPublisher() } } diff --git a/Sources/WalletConnectPairing/Types/PairingProtocolMethod.swift b/Sources/WalletConnectPairing/Types/PairingProtocolMethod.swift index 19167c4b5..1e69303ac 100644 --- a/Sources/WalletConnectPairing/Types/PairingProtocolMethod.swift +++ b/Sources/WalletConnectPairing/Types/PairingProtocolMethod.swift @@ -16,3 +16,12 @@ struct PairingDeleteProtocolMethod: ProtocolMethod { let responseConfig = RelayConfig(tag: 1001, prompt: false, ttl: 86400) } + +struct UnsupportedProtocolMethod: ProtocolMethod { + let method: String + + // TODO - spec tag + let requestConfig = RelayConfig(tag: 0, prompt: false, ttl: 86400) + + let responseConfig = RelayConfig(tag: 0, prompt: false, ttl: 86400) +} diff --git a/Sources/WalletConnectPush/PushClient.swift b/Sources/WalletConnectPush/PushClient.swift index 7b3f6650a..e9ddc5473 100644 --- a/Sources/WalletConnectPush/PushClient.swift +++ b/Sources/WalletConnectPush/PushClient.swift @@ -46,9 +46,8 @@ private extension PushClient { let protocolMethod = PushProposeProtocolMethod() pairingRegisterer.register(method: protocolMethod) - .sink { [unowned self] (topic, request) in - let params = try! request.params!.get(PushRequestParams.self) - requestPublisherSubject.send((topic: topic, params: params)) + .sink { [unowned self] (payload: RequestSubscriptionPayload) in + requestPublisherSubject.send((topic: payload.topic, params: payload.request)) }.store(in: &publishers) networkInteractor.responseErrorSubscription(on: protocolMethod) diff --git a/Sources/WalletConnectNetworking/Extensions/Publisher.swift b/Sources/WalletConnectUtils/Extensions/Publisher.swift similarity index 94% rename from Sources/WalletConnectNetworking/Extensions/Publisher.swift rename to Sources/WalletConnectUtils/Extensions/Publisher.swift index f5f5b689b..5a1b10b3c 100644 --- a/Sources/WalletConnectNetworking/Extensions/Publisher.swift +++ b/Sources/WalletConnectUtils/Extensions/Publisher.swift @@ -1,7 +1,7 @@ import Combine import Foundation -extension Publisher { +public extension Publisher { func asyncFilter(filter: @escaping (Output) async -> Bool) -> AnyPublisher { return flatMap { output in diff --git a/Sources/WalletConnectUtils/SetStore.swift b/Sources/WalletConnectUtils/SetStore.swift new file mode 100644 index 000000000..d7f543434 --- /dev/null +++ b/Sources/WalletConnectUtils/SetStore.swift @@ -0,0 +1,21 @@ + +import Foundation + +public actor SetStore { + private var store: Set = Set() + + public init(){} + + public func insert(_ element: T) { + store.insert(element) + } + + @discardableResult public func remove(_ element: T) -> T? { + store.remove(element) + } + + public func contains(_ element: T) -> Bool { + return store.contains(element) + } + +} From 37569ea7480c78ce2fcb089128f4a22f92fe67f4 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 29 Sep 2022 09:52:33 +0200 Subject: [PATCH 093/175] move ping test to pairing --- Example/IntegrationTests/Auth/AuthTests.swift | 14 -------------- .../IntegrationTests/Pairing/PairingTests.swift | 14 ++++++++++++++ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Example/IntegrationTests/Auth/AuthTests.swift b/Example/IntegrationTests/Auth/AuthTests.swift index 4578c5952..abd7cab3f 100644 --- a/Example/IntegrationTests/Auth/AuthTests.swift +++ b/Example/IntegrationTests/Auth/AuthTests.swift @@ -110,18 +110,4 @@ final class AuthTests: XCTestCase { .store(in: &publishers) wait(for: [responseExpectation], timeout: InputConfig.defaultTimeout) } - - func testPing() async { - let pingExpectation = expectation(description: "expects ping response") - let uri = try! await app.request(RequestParams.stub()) - try! await wallet.pair(uri: uri) - try! await wallet.ping(topic: uri.topic) - wallet.pingResponsePublisher - .sink { topic in - XCTAssertEqual(topic, uri.topic) - pingExpectation.fulfill() - } - .store(in: &publishers) - wait(for: [pingExpectation], timeout: InputConfig.defaultTimeout) - } } diff --git a/Example/IntegrationTests/Pairing/PairingTests.swift b/Example/IntegrationTests/Pairing/PairingTests.swift index df527f8af..a579eb018 100644 --- a/Example/IntegrationTests/Pairing/PairingTests.swift +++ b/Example/IntegrationTests/Pairing/PairingTests.swift @@ -54,5 +54,19 @@ final class PairingTests: XCTestCase { wait(for: [exp], timeout: InputConfig.defaultTimeout) } + + func testPing() async { + let pingExpectation = expectation(description: "expects ping response") + let uri = try! await appPairingClient.create() + try! await walletPairingClient.pair(uri: uri) + try! await walletPairingClient.ping(topic: uri.topic) + walletPairingClient.pingResponsePublisher + .sink { topic in + XCTAssertEqual(topic, uri.topic) + pingExpectation.fulfill() + } + .store(in: &publishers) + wait(for: [pingExpectation], timeout: InputConfig.defaultTimeout) + } } From b37513913f8151a53660b6c6d9c8fb53ad4edd6d Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 29 Sep 2022 13:53:56 +0600 Subject: [PATCH 094/175] Project ID required --- Configuration.xcconfig | 2 ++ Example/DApp/Common/InputConfig.swift | 2 +- Example/ExampleApp/Common/InputConfig.swift | 2 +- Example/IntegrationTests/Stubs/InputConfig.swift | 4 ++-- Example/Showcase/Common/InputConfig.swift | 2 +- 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Configuration.xcconfig b/Configuration.xcconfig index e69de29bb..ef29a642d 100644 --- a/Configuration.xcconfig +++ b/Configuration.xcconfig @@ -0,0 +1,2 @@ +// Uncomment next line and paste your project id. Get this on: https://cloud.walletconnect.com/sign-in +// PROJECT_ID = YOUR_PROJECT_ID diff --git a/Example/DApp/Common/InputConfig.swift b/Example/DApp/Common/InputConfig.swift index 3d16a8cf9..53931721a 100644 --- a/Example/DApp/Common/InputConfig.swift +++ b/Example/DApp/Common/InputConfig.swift @@ -3,7 +3,7 @@ import Foundation struct InputConfig { static var projectId: String { - return config(for: "PROJECT_ID") ?? "3ca2919724fbfa5456a25194e369a8b4" + return config(for: "PROJECT_ID")! } private static func config(for key: String) -> String? { diff --git a/Example/ExampleApp/Common/InputConfig.swift b/Example/ExampleApp/Common/InputConfig.swift index 3d16a8cf9..53931721a 100644 --- a/Example/ExampleApp/Common/InputConfig.swift +++ b/Example/ExampleApp/Common/InputConfig.swift @@ -3,7 +3,7 @@ import Foundation struct InputConfig { static var projectId: String { - return config(for: "PROJECT_ID") ?? "3ca2919724fbfa5456a25194e369a8b4" + return config(for: "PROJECT_ID")! } private static func config(for key: String) -> String? { diff --git a/Example/IntegrationTests/Stubs/InputConfig.swift b/Example/IntegrationTests/Stubs/InputConfig.swift index b0ab8db88..0e0c2efb7 100644 --- a/Example/IntegrationTests/Stubs/InputConfig.swift +++ b/Example/IntegrationTests/Stubs/InputConfig.swift @@ -3,11 +3,11 @@ import Foundation struct InputConfig { static var relayHost: String { - return config(for: "RELAY_HOST") ?? "relay.walletconnect.com" + return config(for: "RELAY_HOST")! } static var projectId: String { - return config(for: "PROJECT_ID") ?? "3ca2919724fbfa5456a25194e369a8b4" + return config(for: "PROJECT_ID")! } static var defaultTimeout: TimeInterval { diff --git a/Example/Showcase/Common/InputConfig.swift b/Example/Showcase/Common/InputConfig.swift index 3d16a8cf9..53931721a 100644 --- a/Example/Showcase/Common/InputConfig.swift +++ b/Example/Showcase/Common/InputConfig.swift @@ -3,7 +3,7 @@ import Foundation struct InputConfig { static var projectId: String { - return config(for: "PROJECT_ID") ?? "3ca2919724fbfa5456a25194e369a8b4" + return config(for: "PROJECT_ID")! } private static func config(for key: String) -> String? { From 524aecfa4fdfa45792758d8d83655bc80a87f9d6 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 29 Sep 2022 13:59:42 +0600 Subject: [PATCH 095/175] Update README.md --- Example/ExampleApp/Common/InputConfig.swift | 2 +- README.md | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Example/ExampleApp/Common/InputConfig.swift b/Example/ExampleApp/Common/InputConfig.swift index 53931721a..b6c381ea1 100644 --- a/Example/ExampleApp/Common/InputConfig.swift +++ b/Example/ExampleApp/Common/InputConfig.swift @@ -1,5 +1,5 @@ import Foundation - + struct InputConfig { static var projectId: String { diff --git a/README.md b/README.md index d723213a6..32b7736dd 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,12 @@ dependencies: [ .package(url: "https://github.com/WalletConnect/WalletConnectSwiftV2", .branch("main")), ], ``` +## Setting Project ID +Follow instructions from *Configuration.xcconfig* and configure PROJECT_ID with your ID from WalletConnect Dashboard +``` +// Uncomment next line and paste your project id. Get this on: https://cloud.walletconnect.com/sign-in +// PROJECT_ID = YOUR_PROJECT_ID +``` ## Example App open `Example/ExampleApp.xcodeproj` From 61db990093012eadd860c068d8d4f2c720ccdcc4 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 29 Sep 2022 10:07:46 +0200 Subject: [PATCH 096/175] fix pairing ping test --- Sources/WalletConnectPairing/PairingClient.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Sources/WalletConnectPairing/PairingClient.swift b/Sources/WalletConnectPairing/PairingClient.swift index 60da21435..31690a75e 100644 --- a/Sources/WalletConnectPairing/PairingClient.swift +++ b/Sources/WalletConnectPairing/PairingClient.swift @@ -48,6 +48,7 @@ public class PairingClient: PairingRegisterer { self.pingService = pingService self.pairingRequestsSubscriber = pairingRequestsSubscriber self.pairingsProvider = pairingsProvider + setUpPublishers() } private func setUpPublishers() { From 535fb6cd993a5823af05c00225471f1d9e567ce7 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 29 Sep 2022 10:21:15 +0200 Subject: [PATCH 097/175] fix tests build --- Tests/AuthTests/AppRespondSubscriberTests.swift | 3 +-- Tests/AuthTests/Mocks/PairingRegistererMock.swift | 10 ++++++++++ Tests/AuthTests/WalletRequestSubscriberTests.swift | 4 +++- 3 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 Tests/AuthTests/Mocks/PairingRegistererMock.swift diff --git a/Tests/AuthTests/AppRespondSubscriberTests.swift b/Tests/AuthTests/AppRespondSubscriberTests.swift index aac4a12ea..3314fbd59 100644 --- a/Tests/AuthTests/AppRespondSubscriberTests.swift +++ b/Tests/AuthTests/AppRespondSubscriberTests.swift @@ -29,8 +29,7 @@ class AppRespondSubscriberTests: XCTestCase { logger: ConsoleLoggerMock(), rpcHistory: rpcHistory, signatureVerifier: messageSigner, - messageFormatter: messageFormatter, - pairingStorage: pairingStorage) + messageFormatter: messageFormatter) } func testMessageCompromisedFailure() { diff --git a/Tests/AuthTests/Mocks/PairingRegistererMock.swift b/Tests/AuthTests/Mocks/PairingRegistererMock.swift new file mode 100644 index 000000000..d5d1a5a4a --- /dev/null +++ b/Tests/AuthTests/Mocks/PairingRegistererMock.swift @@ -0,0 +1,10 @@ +import Foundation +import WalletConnectPairing +import Combine +import WalletConnectNetworking + +struct PairingRegistererMock: PairingRegisterer { + func register(method: ProtocolMethod) -> AnyPublisher, Never> where RequestParams : Decodable, RequestParams : Encodable { + fatalError() + } +} diff --git a/Tests/AuthTests/WalletRequestSubscriberTests.swift b/Tests/AuthTests/WalletRequestSubscriberTests.swift index 6df79685b..3509b3d08 100644 --- a/Tests/AuthTests/WalletRequestSubscriberTests.swift +++ b/Tests/AuthTests/WalletRequestSubscriberTests.swift @@ -21,7 +21,9 @@ class WalletRequestSubscriberTests: XCTestCase { sut = WalletRequestSubscriber(networkingInteractor: networkingInteractor, logger: ConsoleLoggerMock(), kms: KeyManagementServiceMock(), - messageFormatter: messageFormatter, address: "", walletErrorResponder: walletErrorResponder) + messageFormatter: messageFormatter, address: "", + walletErrorResponder: walletErrorResponder, + pairingRegisterer: PairingRegistererMock()) } func testSubscribeRequest() { From f7f3f3516a66b53a180b158f9acf92eb17d09b59 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 29 Sep 2022 14:51:00 +0600 Subject: [PATCH 098/175] Web3 from WalletConnect repo --- Example/ExampleApp.xcodeproj/project.pbxproj | 6 +++--- .../xcshareddata/swiftpm/Package.resolved | 16 ++++++++-------- Package.swift | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index 58afb012c..9b6e3092d 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -1964,10 +1964,10 @@ /* Begin XCRemoteSwiftPackageReference section */ A5AE354528A1A2AC0059AE8A /* XCRemoteSwiftPackageReference "Web3" */ = { isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/flypaper0/Web3.swift"; + repositoryURL = "https://github.com/WalletConnect/Web3.swift"; requirement = { - branch = "feature/eip-155"; - kind = branch; + kind = exactVersion; + version = 1.0.0; }; }; A5D85224286333D500DAF5C3 /* XCRemoteSwiftPackageReference "Starscream" */ = { diff --git a/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 1b5f80e67..01652b58f 100644 --- a/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -15,8 +15,8 @@ "repositoryURL": "https://github.com/krzyzanowskim/CryptoSwift.git", "state": { "branch": null, - "revision": "039f56c5d7960f277087a0be51f5eb04ed0ec073", - "version": "1.5.1" + "revision": "19b3c3ceed117c5cc883517c4e658548315ba70b", + "version": "1.6.0" } }, { @@ -24,8 +24,8 @@ "repositoryURL": "https://github.com/mxcl/PromiseKit.git", "state": { "branch": null, - "revision": "ed3192004c0b00b4b3d7baa9578ee655c66faae3", - "version": "6.18.0" + "revision": "43772616c46a44a9977e41924ae01d0e55f2f9ca", + "version": "6.18.1" } }, { @@ -57,11 +57,11 @@ }, { "package": "Web3", - "repositoryURL": "https://github.com/flypaper0/Web3.swift", + "repositoryURL": "https://github.com/WalletConnect/Web3.swift", "state": { - "branch": "feature/eip-155", - "revision": "92a43a8c279b9df25fe23dd6f8311e6fb0ea06ed", - "version": null + "branch": null, + "revision": "bdaaed96eee3a9bf7f341165f89a94288961d14c", + "version": "1.0.0" } } ] diff --git a/Package.swift b/Package.swift index 4c925ebf6..e35e1f23c 100644 --- a/Package.swift +++ b/Package.swift @@ -27,7 +27,7 @@ let package = Package( targets: ["WalletConnectNetworking"]) ], dependencies: [ - .package(url: "https://github.com/flypaper0/Web3.swift", .branch("feature/eip-155")) + .package(url: "https://github.com/WalletConnect/Web3.swift", .exact("1.0.0")) ], targets: [ .target( From 270f775f46c3c43faf8b4991560e1c663bb1a1db Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 29 Sep 2022 15:20:56 +0600 Subject: [PATCH 099/175] AppMetadata redirect --- .../Types/AppMetadata.swift | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/Sources/WalletConnectPairing/Types/AppMetadata.swift b/Sources/WalletConnectPairing/Types/AppMetadata.swift index 531bd5fe3..5a039740d 100644 --- a/Sources/WalletConnectPairing/Types/AppMetadata.swift +++ b/Sources/WalletConnectPairing/Types/AppMetadata.swift @@ -11,6 +11,26 @@ import Foundation */ public struct AppMetadata: Codable, Equatable { + public struct Redirect: Codable, Equatable { + /// Native deeplink URL string. + public let native: String? + + /// Universal link URL string. + public let universal: String? + + /** + Creates a new Redirect object with the specified information. + + - parameters: + - native: Native deeplink URL string. + - universal: Universal link URL string. + */ + public init(native: String?, universal: String?) { + self.native = native + self.universal = universal + } + } + /// The name of the app. public let name: String @@ -23,6 +43,9 @@ public struct AppMetadata: Codable, Equatable { /// An array of URL strings pointing to the icon assets on the web. public let icons: [String] + /// Redirect links which could be manually used on wallet side + public let redirect: Redirect? + /** Creates a new metadata object with the specified information. @@ -32,10 +55,11 @@ public struct AppMetadata: Codable, Equatable { - url: The URL string that identifies the official domain of the app. - icons: An array of URL strings pointing to the icon assets on the web. */ - public init(name: String, description: String, url: String, icons: [String]) { + public init(name: String, description: String, url: String, icons: [String], redirect: Redirect? = nil) { self.name = name self.description = description self.url = url self.icons = icons + self.redirect = redirect } } From c08a5d40a1194553a4af143ecf8abb32cad5dd49 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 29 Sep 2022 12:20:06 +0200 Subject: [PATCH 100/175] fix tests --- .../WalletConnectPairing/PairingRegisterer.swift | 2 +- Tests/AuthTests/Mocks/PairingRegistererMock.swift | 6 ++++-- .../AuthTests/WalletRequestSubscriberTests.swift | 15 ++++++++++----- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/Sources/WalletConnectPairing/PairingRegisterer.swift b/Sources/WalletConnectPairing/PairingRegisterer.swift index dac4d1ddc..cbf42f72d 100644 --- a/Sources/WalletConnectPairing/PairingRegisterer.swift +++ b/Sources/WalletConnectPairing/PairingRegisterer.swift @@ -4,5 +4,5 @@ import Combine import JSONRPC public protocol PairingRegisterer { - func register(method: ProtocolMethod) -> AnyPublisher, Never> + func register(method: ProtocolMethod) -> AnyPublisher, Never> where RequestParams : Codable } diff --git a/Tests/AuthTests/Mocks/PairingRegistererMock.swift b/Tests/AuthTests/Mocks/PairingRegistererMock.swift index d5d1a5a4a..69c4f34e4 100644 --- a/Tests/AuthTests/Mocks/PairingRegistererMock.swift +++ b/Tests/AuthTests/Mocks/PairingRegistererMock.swift @@ -3,8 +3,10 @@ import WalletConnectPairing import Combine import WalletConnectNetworking -struct PairingRegistererMock: PairingRegisterer { +struct PairingRegistererMock: PairingRegisterer where RequestParams : Codable { + let subject = PassthroughSubject, Never>() + func register(method: ProtocolMethod) -> AnyPublisher, Never> where RequestParams : Decodable, RequestParams : Encodable { - fatalError() + subject.eraseToAnyPublisher() as! AnyPublisher, Never> } } diff --git a/Tests/AuthTests/WalletRequestSubscriberTests.swift b/Tests/AuthTests/WalletRequestSubscriberTests.swift index 3509b3d08..b6049c510 100644 --- a/Tests/AuthTests/WalletRequestSubscriberTests.swift +++ b/Tests/AuthTests/WalletRequestSubscriberTests.swift @@ -8,13 +8,15 @@ import JSONRPC @testable import TestingUtils class WalletRequestSubscriberTests: XCTestCase { - var networkingInteractor: NetworkingInteractorMock! + var pairingRegisterer: PairingRegistererMock! var sut: WalletRequestSubscriber! var messageFormatter: SIWEMessageFormatterMock! + let defaultTimeout: TimeInterval = 0.01 override func setUp() { - networkingInteractor = NetworkingInteractorMock() + let networkingInteractor = NetworkingInteractorMock() + pairingRegisterer = PairingRegistererMock() messageFormatter = SIWEMessageFormatterMock() let walletErrorResponder = WalletErrorResponder(networkingInteractor: networkingInteractor, logger: ConsoleLoggerMock(), kms: KeyManagementServiceMock(), rpcHistory: RPCHistory(keyValueStore: CodableStore(defaults: RuntimeKeyValueStorage(), identifier: ""))) @@ -23,7 +25,7 @@ class WalletRequestSubscriberTests: XCTestCase { kms: KeyManagementServiceMock(), messageFormatter: messageFormatter, address: "", walletErrorResponder: walletErrorResponder, - pairingRegisterer: PairingRegistererMock()) + pairingRegisterer: pairingRegisterer) } func testSubscribeRequest() { @@ -38,9 +40,12 @@ class WalletRequestSubscriberTests: XCTestCase { message = request.message messageExpectation.fulfill() } + + + + let payload = RequestSubscriptionPayload(id: expectedRequestId, topic: "123", request: AuthRequestParams.stub(id: expectedRequestId)) - let request = RPCRequest(method: AuthRequestProtocolMethod().method, params: AuthRequestParams.stub(id: expectedRequestId), id: expectedRequestId.right!) - networkingInteractor.requestPublisherSubject.send(("123", request)) + pairingRegisterer.subject.send(payload) wait(for: [messageExpectation], timeout: defaultTimeout) XCTAssertEqual(message, expectedMessage) From 928d648efc8f5797fd7941bcc7488797150807eb Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 29 Sep 2022 12:32:48 +0200 Subject: [PATCH 101/175] change task priority in unsubscribe, update sign tests setup --- Example/IntegrationTests/Sign/SignClientTests.swift | 11 +++++++++-- .../WalletConnectNetworking/NetworkInteractor.swift | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Example/IntegrationTests/Sign/SignClientTests.swift b/Example/IntegrationTests/Sign/SignClientTests.swift index 37648d275..a74a42cdc 100644 --- a/Example/IntegrationTests/Sign/SignClientTests.swift +++ b/Example/IntegrationTests/Sign/SignClientTests.swift @@ -25,11 +25,18 @@ final class SignClientTests: XCTestCase { socketConnectionType: .automatic, logger: logger ) - let pairingClient = PairingClientFactory.create(relayClient: relayClient) + let keyValueStorage = RuntimeKeyValueStorage() + + let pairingClient = PairingClientFactory.create( + logger: logger, + keyValueStorage: keyValueStorage, + keychainStorage: keychain, + relayClient: relayClient) + let client = SignClientFactory.create( metadata: AppMetadata(name: name, description: "", url: "", icons: [""]), logger: logger, - keyValueStorage: RuntimeKeyValueStorage(), + keyValueStorage: keyValueStorage, keychainStorage: keychain, relayClient: relayClient, pairingClient: pairingClient diff --git a/Sources/WalletConnectNetworking/NetworkInteractor.swift b/Sources/WalletConnectNetworking/NetworkInteractor.swift index ca56f38b5..ccd619921 100644 --- a/Sources/WalletConnectNetworking/NetworkInteractor.swift +++ b/Sources/WalletConnectNetworking/NetworkInteractor.swift @@ -55,7 +55,7 @@ public class NetworkingInteractor: NetworkInteracting { } public func unsubscribe(topic: String) { - Task { await topics.remove(topic) } + Task(priority: .high) { await topics.remove(topic) } relayClient.unsubscribe(topic: topic) { [unowned self] error in if let error = error { logger.error(error) From 7db4cdf46aaacbce490c3ab3e5c59656aad66002 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 29 Sep 2022 12:56:04 +0200 Subject: [PATCH 102/175] Fix bug with responding twice for pairing ping --- .../PairingRequestsSubscriber.swift | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Sources/WalletConnectPairing/PairingRequestsSubscriber.swift b/Sources/WalletConnectPairing/PairingRequestsSubscriber.swift index 8562bf60b..3982588cc 100644 --- a/Sources/WalletConnectPairing/PairingRequestsSubscriber.swift +++ b/Sources/WalletConnectPairing/PairingRequestsSubscriber.swift @@ -9,10 +9,14 @@ public class PairingRequestsSubscriber { private let pairingStorage: PairingStorage private var publishers = Set() private var protocolMethods = SetStore() + private let logger: ConsoleLogging - init(networkingInteractor: NetworkInteracting, pairingStorage: PairingStorage, logger: ConsoleLogging) { + init(networkingInteractor: NetworkInteracting, + pairingStorage: PairingStorage, + logger: ConsoleLogging) { self.networkingInteractor = networkingInteractor self.pairingStorage = pairingStorage + self.logger = logger handleUnregisteredRequests() } @@ -30,11 +34,16 @@ public class PairingRequestsSubscriber { } func handleUnregisteredRequests() { + Task(priority: .high) { [unowned self] in + await protocolMethods.insert(PairingDeleteProtocolMethod().method) + await protocolMethods.insert(PairingPingProtocolMethod().method) + } networkingInteractor.requestPublisher .asyncFilter { [unowned self] in await !protocolMethods.contains($0.request.method)} .sink { [unowned self] topic, request in Task(priority: .high) { let protocolMethod = UnsupportedProtocolMethod(method: request.method) + logger.debug("PairingRequestsSubscriber: responding unregistered request method") try await networkingInteractor.respondError(topic: topic, requestId: request.id!, protocolMethod: protocolMethod, reason: PairError.methodUnsupported) } }.store(in: &publishers) From be26f1aae2ec93447ba26ffb011c3257b6365ce6 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 29 Sep 2022 14:22:36 +0200 Subject: [PATCH 103/175] apply to pr suggestions --- .../NetworkInteractor.swift | 8 ++++---- .../PairingRequestsSubscriber.swift | 12 +++++------- .../Common/DeletePairingService.swift | 1 - .../Types/PairingProtocolMethod.swift | 1 + .../Extensions/Publisher.swift | 17 ----------------- Sources/WalletConnectUtils/SetStore.swift | 19 ++++++++++++++----- 6 files changed, 24 insertions(+), 34 deletions(-) delete mode 100644 Sources/WalletConnectUtils/Extensions/Publisher.swift diff --git a/Sources/WalletConnectNetworking/NetworkInteractor.swift b/Sources/WalletConnectNetworking/NetworkInteractor.swift index ccd619921..24445ba4c 100644 --- a/Sources/WalletConnectNetworking/NetworkInteractor.swift +++ b/Sources/WalletConnectNetworking/NetworkInteractor.swift @@ -11,7 +11,7 @@ public class NetworkingInteractor: NetworkInteracting { private let serializer: Serializing private let rpcHistory: RPCHistory private let logger: ConsoleLogging - private let topics = SetStore() + private let topics = SetStore(label: "com.walletconnect.sdk.networking.topics") private let requestPublisherSubject = PassthroughSubject<(topic: String, request: RPCRequest), Never>() private let responsePublisherSubject = PassthroughSubject<(topic: String, request: RPCRequest, response: RPCResponse), Never>() @@ -42,7 +42,7 @@ public class NetworkingInteractor: NetworkInteracting { private func setupRelaySubscribtion() { relayClient.messagePublisher - .asyncFilter { [unowned self] in await topics.contains($0.topic)} + .filter { [unowned self] in topics.contains($0.topic)} .sink { [unowned self] (topic, message) in manageSubscription(topic, message) } @@ -50,12 +50,12 @@ public class NetworkingInteractor: NetworkInteracting { } public func subscribe(topic: String) async throws { - await topics.insert(topic) + topics.insert(topic) try await relayClient.subscribe(topic: topic) } public func unsubscribe(topic: String) { - Task(priority: .high) { await topics.remove(topic) } + topics.remove(topic) relayClient.unsubscribe(topic: topic) { [unowned self] error in if let error = error { logger.error(error) diff --git a/Sources/WalletConnectPairing/PairingRequestsSubscriber.swift b/Sources/WalletConnectPairing/PairingRequestsSubscriber.swift index 3982588cc..a50047bfa 100644 --- a/Sources/WalletConnectPairing/PairingRequestsSubscriber.swift +++ b/Sources/WalletConnectPairing/PairingRequestsSubscriber.swift @@ -8,7 +8,8 @@ public class PairingRequestsSubscriber { private let networkingInteractor: NetworkInteracting private let pairingStorage: PairingStorage private var publishers = Set() - private var protocolMethods = SetStore() + private var registeredProtocolMethods = SetStore(label: "com.walletconnect.sdk.pairing.registered_protocol_methods") + private let pairingProtocolMethods = ["\(PairingPingProtocolMethod().method)", "\(PairingDeleteProtocolMethod().method)"] private let logger: ConsoleLogging init(networkingInteractor: NetworkInteracting, @@ -22,7 +23,7 @@ public class PairingRequestsSubscriber { func subscribeForRequest(_ protocolMethod: ProtocolMethod) -> AnyPublisher, Never> { - Task(priority: .high) { await protocolMethods.insert(protocolMethod.method) } + registeredProtocolMethods.insert(protocolMethod.method) let publisherSubject = PassthroughSubject, Never>() @@ -34,12 +35,9 @@ public class PairingRequestsSubscriber { } func handleUnregisteredRequests() { - Task(priority: .high) { [unowned self] in - await protocolMethods.insert(PairingDeleteProtocolMethod().method) - await protocolMethods.insert(PairingPingProtocolMethod().method) - } networkingInteractor.requestPublisher - .asyncFilter { [unowned self] in await !protocolMethods.contains($0.request.method)} + .filter { [unowned self] in !pairingProtocolMethods.contains($0.request.method)} + .filter { [unowned self] in !registeredProtocolMethods.contains($0.request.method)} .sink { [unowned self] topic, request in Task(priority: .high) { let protocolMethod = UnsupportedProtocolMethod(method: request.method) diff --git a/Sources/WalletConnectPairing/Services/Common/DeletePairingService.swift b/Sources/WalletConnectPairing/Services/Common/DeletePairingService.swift index 06ae1f8fe..27cd7a7bc 100644 --- a/Sources/WalletConnectPairing/Services/Common/DeletePairingService.swift +++ b/Sources/WalletConnectPairing/Services/Common/DeletePairingService.swift @@ -4,7 +4,6 @@ import WalletConnectKMS import WalletConnectUtils import WalletConnectNetworking - class DeletePairingService { private let networkingInteractor: NetworkInteracting private let kms: KeyManagementServiceProtocol diff --git a/Sources/WalletConnectPairing/Types/PairingProtocolMethod.swift b/Sources/WalletConnectPairing/Types/PairingProtocolMethod.swift index 1e69303ac..96b834651 100644 --- a/Sources/WalletConnectPairing/Types/PairingProtocolMethod.swift +++ b/Sources/WalletConnectPairing/Types/PairingProtocolMethod.swift @@ -25,3 +25,4 @@ struct UnsupportedProtocolMethod: ProtocolMethod { let responseConfig = RelayConfig(tag: 0, prompt: false, ttl: 86400) } + diff --git a/Sources/WalletConnectUtils/Extensions/Publisher.swift b/Sources/WalletConnectUtils/Extensions/Publisher.swift deleted file mode 100644 index 5a1b10b3c..000000000 --- a/Sources/WalletConnectUtils/Extensions/Publisher.swift +++ /dev/null @@ -1,17 +0,0 @@ -import Combine -import Foundation - -public extension Publisher { - - func asyncFilter(filter: @escaping (Output) async -> Bool) -> AnyPublisher { - return flatMap { output in - return Future { completion in - Task(priority: .high) { - if await filter(output) { - completion(.success(output)) - } - } - } - }.eraseToAnyPublisher() - } -} diff --git a/Sources/WalletConnectUtils/SetStore.swift b/Sources/WalletConnectUtils/SetStore.swift index d7f543434..e93a96467 100644 --- a/Sources/WalletConnectUtils/SetStore.swift +++ b/Sources/WalletConnectUtils/SetStore.swift @@ -1,17 +1,26 @@ import Foundation -public actor SetStore { +public class SetStore { + + private let concurrentQueue: DispatchQueue + private var store: Set = Set() - public init(){} + public init(label: String){ + self.concurrentQueue = DispatchQueue(label: label, attributes: .concurrent) + } public func insert(_ element: T) { - store.insert(element) + concurrentQueue.async(flags: .barrier) { [weak self] in + self?.store.insert(element) + } } - @discardableResult public func remove(_ element: T) -> T? { - store.remove(element) + public func remove(_ element: T) { + concurrentQueue.async(flags: .barrier) { [weak self] in + self?.store.remove(element) + } } public func contains(_ element: T) -> Bool { From defa52974355e0088fdccb48a35eaede0df9c090 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 29 Sep 2022 14:56:21 +0200 Subject: [PATCH 104/175] remove pairing ping from sign --- .../WalletConnectSign/Engine/Common/PairingEngine.swift | 9 --------- 1 file changed, 9 deletions(-) diff --git a/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift b/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift index 3a51e729c..9e67e0c3e 100644 --- a/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift @@ -91,15 +91,6 @@ private extension PairingEngine { } } .store(in: &publishers) -// let protocolMethod = PairingPingProtocolMethod() - -// networkingInteractor.requestSubscription(on: protocolMethod) -// .sink { [unowned self] (payload: RequestSubscriptionPayload) in -// Task(priority: .high) { -// try await networkingInteractor.respondSuccess(topic: payload.topic, requestId: payload.id, protocolMethod: protocolMethod) -// } -// } -// .store(in: &publishers) } func setupExpirationHandling() { From 3a5d8288a96e6f55ccbcc44ad21c81a817731c96 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 29 Sep 2022 15:56:51 +0200 Subject: [PATCH 105/175] fix typo --- Sources/WalletConnectPairing/PairingClient.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/WalletConnectPairing/PairingClient.swift b/Sources/WalletConnectPairing/PairingClient.swift index 31690a75e..1c6ff9bd6 100644 --- a/Sources/WalletConnectPairing/PairingClient.swift +++ b/Sources/WalletConnectPairing/PairingClient.swift @@ -102,7 +102,7 @@ public class PairingClient: PairingRegisterer { } public func register(method: ProtocolMethod) -> AnyPublisher, Never> { - logger.debug("Pairing Client - regitring for \(method.method)") + logger.debug("Pairing Client - registering for \(method.method)") return pairingRequestsSubscriber.subscribeForRequest(method) } From 6afd1694a99fb55d734616965a3e2ea13e9c809f Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Fri, 30 Sep 2022 07:57:40 +0600 Subject: [PATCH 106/175] SetStore sync read --- Sources/WalletConnectUtils/SetStore.swift | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Sources/WalletConnectUtils/SetStore.swift b/Sources/WalletConnectUtils/SetStore.swift index e93a96467..feba25809 100644 --- a/Sources/WalletConnectUtils/SetStore.swift +++ b/Sources/WalletConnectUtils/SetStore.swift @@ -24,7 +24,10 @@ public class SetStore { } public func contains(_ element: T) -> Bool { - return store.contains(element) + var contains = false + concurrentQueue.sync { [unowned self] in + contains = store.contains(element) + } + return contains } - } From 30dbb715e3b746522fa7da05b750714b859cbe1e Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Fri, 30 Sep 2022 08:12:58 +0600 Subject: [PATCH 107/175] PairingProtocolMethod enum --- .../PairingRequestsSubscriber.swift | 2 +- .../Common/DeletePairingService.swift | 2 +- .../Common/Ping/PairingPingService.swift | 2 +- .../Types/PairingProtocolMethod.swift | 44 +++++++++++++------ 4 files changed, 33 insertions(+), 17 deletions(-) diff --git a/Sources/WalletConnectPairing/PairingRequestsSubscriber.swift b/Sources/WalletConnectPairing/PairingRequestsSubscriber.swift index a50047bfa..94f87f0c1 100644 --- a/Sources/WalletConnectPairing/PairingRequestsSubscriber.swift +++ b/Sources/WalletConnectPairing/PairingRequestsSubscriber.swift @@ -9,7 +9,7 @@ public class PairingRequestsSubscriber { private let pairingStorage: PairingStorage private var publishers = Set() private var registeredProtocolMethods = SetStore(label: "com.walletconnect.sdk.pairing.registered_protocol_methods") - private let pairingProtocolMethods = ["\(PairingPingProtocolMethod().method)", "\(PairingDeleteProtocolMethod().method)"] + private let pairingProtocolMethods = PairingProtocolMethod.allCases.map { $0.method } private let logger: ConsoleLogging init(networkingInteractor: NetworkInteracting, diff --git a/Sources/WalletConnectPairing/Services/Common/DeletePairingService.swift b/Sources/WalletConnectPairing/Services/Common/DeletePairingService.swift index 27cd7a7bc..05369d56c 100644 --- a/Sources/WalletConnectPairing/Services/Common/DeletePairingService.swift +++ b/Sources/WalletConnectPairing/Services/Common/DeletePairingService.swift @@ -22,7 +22,7 @@ class DeletePairingService { func delete(topic: String) async throws { let reason = ReasonCode.userDisconnected - let protocolMethod = PairingDeleteProtocolMethod() + let protocolMethod = PairingProtocolMethod.delete logger.debug("Will delete pairing for reason: message: \(reason.message) code: \(reason.code)") let request = RPCRequest(method: protocolMethod.method, params: reason) try await networkingInteractor.request(request, topic: topic, protocolMethod: protocolMethod) diff --git a/Sources/WalletConnectPairing/Services/Common/Ping/PairingPingService.swift b/Sources/WalletConnectPairing/Services/Common/Ping/PairingPingService.swift index 053461717..de614cc25 100644 --- a/Sources/WalletConnectPairing/Services/Common/Ping/PairingPingService.swift +++ b/Sources/WalletConnectPairing/Services/Common/Ping/PairingPingService.swift @@ -21,7 +21,7 @@ public class PairingPingService { pairingStorage: WCPairingStorage, networkingInteractor: NetworkInteracting, logger: ConsoleLogging) { - let protocolMethod = PairingPingProtocolMethod() + let protocolMethod = PairingProtocolMethod.ping self.pairingStorage = pairingStorage self.pingRequester = PingRequester(networkingInteractor: networkingInteractor, method: protocolMethod) self.pingResponder = PingResponder(networkingInteractor: networkingInteractor, method: protocolMethod, logger: logger) diff --git a/Sources/WalletConnectPairing/Types/PairingProtocolMethod.swift b/Sources/WalletConnectPairing/Types/PairingProtocolMethod.swift index 96b834651..a2efd3167 100644 --- a/Sources/WalletConnectPairing/Types/PairingProtocolMethod.swift +++ b/Sources/WalletConnectPairing/Types/PairingProtocolMethod.swift @@ -1,20 +1,36 @@ import Foundation import WalletConnectNetworking -struct PairingPingProtocolMethod: ProtocolMethod { - let method: String = "wc_pairingPing" - - let requestConfig = RelayConfig(tag: 1002, prompt: false, ttl: 30) - - let responseConfig = RelayConfig(tag: 1003, prompt: false, ttl: 30) -} - -struct PairingDeleteProtocolMethod: ProtocolMethod { - let method: String = "wc_pairingDelete" - - let requestConfig = RelayConfig(tag: 1000, prompt: false, ttl: 86400) - - let responseConfig = RelayConfig(tag: 1001, prompt: false, ttl: 86400) +enum PairingProtocolMethod: CaseIterable, ProtocolMethod { + case ping + case delete + + var method: String { + switch self { + case .ping: + return "wc_pairingPing" + case .delete: + return "wc_pairingDelete" + } + } + + var requestConfig: RelayConfig { + switch self { + case .ping: + return RelayConfig(tag: 1002, prompt: false, ttl: 30) + case .delete: + return RelayConfig(tag: 1003, prompt: false, ttl: 30) + } + } + + var responseConfig: RelayConfig { + switch self { + case .ping: + return RelayConfig(tag: 1003, prompt: false, ttl: 30) + case .delete: + return RelayConfig(tag: 1001, prompt: false, ttl: 86400) + } + } } struct UnsupportedProtocolMethod: ProtocolMethod { From a175b0c7e21a57eb51dc4d571b8aa3f430f9b3bd Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Fri, 30 Sep 2022 08:33:11 +0600 Subject: [PATCH 108/175] Configure deprecations --- Sources/Auth/Auth.swift | 10 ++++++++++ Sources/WalletConnectPairing/Pair.swift | 13 +++++++------ Sources/WalletConnectPairing/PairConfig.swift | 12 ++++++++++++ Sources/WalletConnectSign/Sign/Sign.swift | 4 +++- 4 files changed, 32 insertions(+), 7 deletions(-) create mode 100644 Sources/WalletConnectPairing/PairConfig.swift diff --git a/Sources/Auth/Auth.swift b/Sources/Auth/Auth.swift index 49d7e888b..62b8675e4 100644 --- a/Sources/Auth/Auth.swift +++ b/Sources/Auth/Auth.swift @@ -36,9 +36,19 @@ public class Auth { /// - Parameters: /// - metadata: App metadata /// - account: account that wallet will be authenticating with. Should be nil for non wallet clients. + @available(*, deprecated, message: "Use Pair.configure(metadata:) with Auth.configure(account:) instead") static public func configure(metadata: AppMetadata, account: Account?) { Auth.config = Auth.Config( metadata: metadata, account: account) } + + /// Auth instance config method + /// - Parameters: + /// - account: account that wallet will be authenticating with. Should be nil for non wallet clients. + static public func configure(account: Account?) { + Auth.config = Auth.Config( + metadata: Pair.metadata, + account: account) + } } diff --git a/Sources/WalletConnectPairing/Pair.swift b/Sources/WalletConnectPairing/Pair.swift index 40fdcec3b..b60f2e99e 100644 --- a/Sources/WalletConnectPairing/Pair.swift +++ b/Sources/WalletConnectPairing/Pair.swift @@ -2,12 +2,6 @@ import Foundation import WalletConnectRelay import Combine -extension Pair { - struct Config { - let metadata: AppMetadata - } -} - public class Pair { /// Pairing client instance @@ -18,6 +12,13 @@ public class Pair { return PairingClientFactory.create(relayClient: Relay.instance) }() + public static var metadata: AppMetadata { + guard let metadata = config?.metadata else { + fatalError("Error - you configure metadata with Pair.configure(metadata:)") + } + return metadata + } + private static var config: Config? private init() { } diff --git a/Sources/WalletConnectPairing/PairConfig.swift b/Sources/WalletConnectPairing/PairConfig.swift new file mode 100644 index 000000000..38f7e35ed --- /dev/null +++ b/Sources/WalletConnectPairing/PairConfig.swift @@ -0,0 +1,12 @@ +import Foundation + +extension Pair { + + public struct Config { + public let metadata: AppMetadata + + public init(metadata: AppMetadata) { + self.metadata = metadata + } + } +} diff --git a/Sources/WalletConnectSign/Sign/Sign.swift b/Sources/WalletConnectSign/Sign/Sign.swift index 5effea2db..e28ed0804 100644 --- a/Sources/WalletConnectSign/Sign/Sign.swift +++ b/Sources/WalletConnectSign/Sign/Sign.swift @@ -26,7 +26,7 @@ public class Sign { /// Sign client instance public static var instance: SignClient = { - guard let metadata = Sign.metadata else { + guard let metadata = Sign.metadata ?? Pair.metadata else { fatalError("Error - you must call Sign.configure(_:) before accessing the shared instance.") } return SignClientFactory.create( @@ -36,6 +36,7 @@ public class Sign { ) }() + @available(*, deprecated, message: "Remove after clients migration") private static var metadata: AppMetadata? private init() { } @@ -43,6 +44,7 @@ public class Sign { /// Sign instance config method /// - Parameters: /// - metadata: App metadata + @available(*, deprecated, message: "Use Pair.configure(metadata:) instead") static public func configure(metadata: AppMetadata) { Sign.metadata = metadata } From c625be74bd05cad5bea098b47b4a74a4dadeb9ba Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Fri, 30 Sep 2022 08:40:07 +0600 Subject: [PATCH 109/175] Auth client deprecations --- Sources/Auth/AuthClient.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Sources/Auth/AuthClient.swift b/Sources/Auth/AuthClient.swift index af58c65a7..04cbcc8c3 100644 --- a/Sources/Auth/AuthClient.swift +++ b/Sources/Auth/AuthClient.swift @@ -82,6 +82,7 @@ public class AuthClient { /// Throws Error: /// - When URI is invalid format or missing params /// - When topic is already in use + @available(*, deprecated, message: "Use Pair.pair(uri:) instead") public func pair(uri: WalletConnectURI) async throws { try await pairingClient.pair(uri: uri) } @@ -121,14 +122,17 @@ public class AuthClient { try await walletRespondService.respondError(requestId: requestId) } + @available(*, deprecated, message: "Use Pair.disconnect(topic:) instead") public func disconnect(topic: String) async throws { try await pairingClient.disconnect(topic: topic) } + @available(*, deprecated, message: "Use Pair.ping(topic:) instead") public func ping(topic: String) async throws { try await pairingClient.ping(topic: topic) } + @available(*, deprecated, message: "Use Pair.getPairings() instead") public func getPairings() -> [Pairing] { pairingClient.getPairings() } From 7bd077ff4e1cf351745d80e7b93b191afdc130d1 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Fri, 30 Sep 2022 08:42:41 +0600 Subject: [PATCH 110/175] Build errors --- Sources/WalletConnectPairing/Pair.swift | 2 +- Sources/WalletConnectSign/Sign/Sign.swift | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/Sources/WalletConnectPairing/Pair.swift b/Sources/WalletConnectPairing/Pair.swift index b60f2e99e..4ead303f0 100644 --- a/Sources/WalletConnectPairing/Pair.swift +++ b/Sources/WalletConnectPairing/Pair.swift @@ -14,7 +14,7 @@ public class Pair { public static var metadata: AppMetadata { guard let metadata = config?.metadata else { - fatalError("Error - you configure metadata with Pair.configure(metadata:)") + fatalError("Error - you must configure metadata with Pair.configure(metadata:)") } return metadata } diff --git a/Sources/WalletConnectSign/Sign/Sign.swift b/Sources/WalletConnectSign/Sign/Sign.swift index e28ed0804..3bea71131 100644 --- a/Sources/WalletConnectSign/Sign/Sign.swift +++ b/Sources/WalletConnectSign/Sign/Sign.swift @@ -26,11 +26,8 @@ public class Sign { /// Sign client instance public static var instance: SignClient = { - guard let metadata = Sign.metadata ?? Pair.metadata else { - fatalError("Error - you must call Sign.configure(_:) before accessing the shared instance.") - } return SignClientFactory.create( - metadata: metadata, + metadata: Sign.metadata ?? Pair.metadata, relayClient: Relay.instance, pairingClient: Pair.instance ) From 05c389789debdf5e29bf7fcacdf1b7ab6cbfd4f6 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Fri, 30 Sep 2022 11:01:15 +0200 Subject: [PATCH 111/175] remove pairing methods from auth --- Sources/Auth/AuthClient.swift | 46 ++----------------- .../PairingRegisterer.swift | 1 + 2 files changed, 4 insertions(+), 43 deletions(-) diff --git a/Sources/Auth/AuthClient.swift b/Sources/Auth/AuthClient.swift index 04cbcc8c3..ebf07ae7a 100644 --- a/Sources/Auth/AuthClient.swift +++ b/Sources/Auth/AuthClient.swift @@ -32,8 +32,6 @@ public class AuthClient { authResponsePublisherSubject.eraseToAnyPublisher() } - - /// Publisher that sends web socket connection status public let socketConnectionStatusPublisher: AnyPublisher @@ -42,7 +40,7 @@ public class AuthClient { // MARK: - Private Properties - private let pairingClient: PairingClient + private let pairingRegisterer: PairingRegisterer private var authResponsePublisherSubject = PassthroughSubject<(id: RPCID, result: Result), Never>() private var authRequestPublisherSubject = PassthroughSubject() @@ -71,39 +69,16 @@ public class AuthClient { self.pendingRequestsProvider = pendingRequestsProvider self.logger = logger self.socketConnectionStatusPublisher = socketConnectionStatusPublisher - self.pairingClient = pairingClient + self.pairingRegisterer = pairingClient setUpPublishers() } - /// For wallet to establish a pairing and receive an authentication request - /// Wallet should call this function in order to accept peer's pairing proposal and be able to subscribe for future authentication request. - /// - Parameter uri: Pairing URI that is commonly presented as a QR code by a dapp or delivered with universal linking. - /// - /// Throws Error: - /// - When URI is invalid format or missing params - /// - When topic is already in use - @available(*, deprecated, message: "Use Pair.pair(uri:) instead") - public func pair(uri: WalletConnectURI) async throws { - try await pairingClient.pair(uri: uri) - } - - /// For a dapp to send an authentication request to a wallet - /// - Parameter params: Set of parameters required to request authentication - /// - /// - Returns: Pairing URI that should be shared with wallet out of bound. Common way is to present it as a QR code. - public func request(_ params: RequestParams) async throws -> WalletConnectURI { - logger.debug("Requesting Authentication") - let uri = try await pairingClient.create() - try await appRequestService.request(params: params, topic: uri.topic) - return uri - } - /// For a dapp to send an authentication request to a wallet /// - Parameter params: Set of parameters required to request authentication /// - Parameter topic: Pairing topic that wallet already subscribes for public func request(_ params: RequestParams, topic: String) async throws { logger.debug("Requesting Authentication on existing pairing") - try pairingClient.getPairing(for: topic) + try pairingRegisterer.validatePairingExistance(topic) try await appRequestService.request(params: params, topic: topic) } @@ -122,21 +97,6 @@ public class AuthClient { try await walletRespondService.respondError(requestId: requestId) } - @available(*, deprecated, message: "Use Pair.disconnect(topic:) instead") - public func disconnect(topic: String) async throws { - try await pairingClient.disconnect(topic: topic) - } - - @available(*, deprecated, message: "Use Pair.ping(topic:) instead") - public func ping(topic: String) async throws { - try await pairingClient.ping(topic: topic) - } - - @available(*, deprecated, message: "Use Pair.getPairings() instead") - public func getPairings() -> [Pairing] { - pairingClient.getPairings() - } - /// Query pending authentication requests /// - Returns: Pending authentication requests public func getPendingRequests() throws -> [AuthRequest] { diff --git a/Sources/WalletConnectPairing/PairingRegisterer.swift b/Sources/WalletConnectPairing/PairingRegisterer.swift index cbf42f72d..67c1eb45e 100644 --- a/Sources/WalletConnectPairing/PairingRegisterer.swift +++ b/Sources/WalletConnectPairing/PairingRegisterer.swift @@ -5,4 +5,5 @@ import JSONRPC public protocol PairingRegisterer { func register(method: ProtocolMethod) -> AnyPublisher, Never> where RequestParams : Codable + func validatePairingExistance(_ topic: String) throws } From 10c13c52796c086ebbd2b3ab6d6b14a7b1094408 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Fri, 30 Sep 2022 11:58:17 +0200 Subject: [PATCH 112/175] pass test request on new pairing interface --- Example/IntegrationTests/Auth/AuthTests.swift | 144 +++++++++--------- Sources/Auth/Auth.swift | 2 +- Sources/Auth/AuthClient.swift | 4 +- Sources/Auth/AuthClientFactory.swift | 10 +- .../WalletConnectPairing/PairingClient.swift | 5 +- .../Services/Common/PairingsProvider.swift | 5 +- 6 files changed, 90 insertions(+), 80 deletions(-) diff --git a/Example/IntegrationTests/Auth/AuthTests.swift b/Example/IntegrationTests/Auth/AuthTests.swift index 805102067..88fa0c066 100644 --- a/Example/IntegrationTests/Auth/AuthTests.swift +++ b/Example/IntegrationTests/Auth/AuthTests.swift @@ -8,18 +8,22 @@ import Combine import WalletConnectPairing final class AuthTests: XCTestCase { - var app: AuthClient! - var wallet: AuthClient! + var appPairingClient: PairingClient! + var walletPairingClient: PairingClient! + + var appAuthClient: AuthClient! + var walletAuthClient: AuthClient! let prvKey = Data(hex: "462c1dad6832d7d96ccf87bd6a686a4110e114aaaebd5512e552c0e3a87b480f") private var publishers = [AnyCancellable]() override func setUp() { - app = makeClient(prefix: "👻 App") let walletAccount = Account(chainIdentifier: "eip155:1", address: "0x724d0D2DaD3fbB0C168f947B87Fa5DBe36F1A8bf")! - wallet = makeClient(prefix: "🤑 Wallet", account: walletAccount) + + (appPairingClient, appAuthClient) = makeClients(prefix: "🤖 App") + (walletPairingClient, walletAuthClient) = makeClients(prefix: "🐶 Wallet", account: walletAccount) } - func makeClient(prefix: String, account: Account? = nil) -> AuthClient { + func makeClients(prefix: String, account: Account? = nil) -> (PairingClient, AuthClient) { let logger = ConsoleLogger(suffix: prefix, loggingLevel: .debug) let keychain = KeychainStorageMock() let relayClient = RelayClient(relayHost: InputConfig.relayHost, projectId: InputConfig.projectId, keychainStorage: keychain, socketFactory: SocketFactory(), logger: logger) @@ -31,82 +35,86 @@ final class AuthTests: XCTestCase { keychainStorage: keychain, relayClient: relayClient) - return AuthClientFactory.create( + let authClient = AuthClientFactory.create( metadata: AppMetadata(name: name, description: "", url: "", icons: [""]), account: account, logger: logger, keyValueStorage: keyValueStorage, keychainStorage: keychain, relayClient: relayClient, - pairingClient: pairingClient) + pairingRegisterer: pairingClient) + + return (pairingClient, authClient) } func testRequest() async { let requestExpectation = expectation(description: "request delivered to wallet") - let uri = try! await app.request(RequestParams.stub()) - try! await wallet.pair(uri: uri) - wallet.authRequestPublisher.sink { _ in + let uri = try! await appPairingClient.create() + try! await appAuthClient.request(RequestParams.stub(), topic: uri.topic) + + try! await walletPairingClient.pair(uri: uri) + walletAuthClient.authRequestPublisher.sink { _ in requestExpectation.fulfill() }.store(in: &publishers) wait(for: [requestExpectation], timeout: InputConfig.defaultTimeout) } - func testRespondSuccess() async { - let responseExpectation = expectation(description: "successful response delivered") - let uri = try! await app.request(RequestParams.stub()) - try! await wallet.pair(uri: uri) - wallet.authRequestPublisher.sink { [unowned self] request in - Task(priority: .high) { - let signature = try! MessageSigner().sign(message: request.message, privateKey: prvKey) - try! await wallet.respond(requestId: request.id, signature: signature) - } - } - .store(in: &publishers) - app.authResponsePublisher.sink { (_, result) in - guard case .success = result else { XCTFail(); return } - responseExpectation.fulfill() - } - .store(in: &publishers) - wait(for: [responseExpectation], timeout: InputConfig.defaultTimeout) - } - - func testUserRespondError() async { - let responseExpectation = expectation(description: "error response delivered") - let uri = try! await app.request(RequestParams.stub()) - try! await wallet.pair(uri: uri) - wallet.authRequestPublisher.sink { [unowned self] request in - Task(priority: .high) { - try! await wallet.reject(requestId: request.id) - } - } - .store(in: &publishers) - app.authResponsePublisher.sink { (_, result) in - guard case .failure(let error) = result else { XCTFail(); return } - XCTAssertEqual(error, .userRejeted) - responseExpectation.fulfill() - } - .store(in: &publishers) - wait(for: [responseExpectation], timeout: InputConfig.defaultTimeout) - } - - func testRespondSignatureVerificationFailed() async { - let responseExpectation = expectation(description: "invalid signature response delivered") - let uri = try! await app.request(RequestParams.stub()) - try! await wallet.pair(uri: uri) - wallet.authRequestPublisher.sink { [unowned self] request in - Task(priority: .high) { - let invalidSignature = "438effc459956b57fcd9f3dac6c675f9cee88abf21acab7305e8e32aa0303a883b06dcbd956279a7a2ca21ffa882ff55cc22e8ab8ec0f3fe90ab45f306938cfa1b" - let cacaoSignature = CacaoSignature(t: "eip191", s: invalidSignature) - try! await wallet.respond(requestId: request.id, signature: cacaoSignature) - } - } - .store(in: &publishers) - app.authResponsePublisher.sink { (_, result) in - guard case .failure(let error) = result else { XCTFail(); return } - XCTAssertEqual(error, .signatureVerificationFailed) - responseExpectation.fulfill() - } - .store(in: &publishers) - wait(for: [responseExpectation], timeout: InputConfig.defaultTimeout) - } +// func testRespondSuccess() async { +// let responseExpectation = expectation(description: "successful response delivered") +// let uri = try! await app.request(RequestParams.stub()) +// try! await wallet.pair(uri: uri) +// walletAuthClient.authRequestPublisher.sink { [unowned self] request in +// Task(priority: .high) { +// let signature = try! MessageSigner().sign(message: request.message, privateKey: prvKey) +// try! await walletAuthClient.respond(requestId: request.id, signature: signature) +// } +// } +// .store(in: &publishers) +// appAuthClient.authResponsePublisher.sink { (_, result) in +// guard case .success = result else { XCTFail(); return } +// responseExpectation.fulfill() +// } +// .store(in: &publishers) +// wait(for: [responseExpectation], timeout: InputConfig.defaultTimeout) +// } +// +// func testUserRespondError() async { +// let responseExpectation = expectation(description: "error response delivered") +// let uri = try! await app.request(RequestParams.stub()) +// try! await wallet.pair(uri: uri) +// walletAuthClient.authRequestPublisher.sink { [unowned self] request in +// Task(priority: .high) { +// try! await walletAuthClient.reject(requestId: request.id) +// } +// } +// .store(in: &publishers) +// appAuthClient.authResponsePublisher.sink { (_, result) in +// guard case .failure(let error) = result else { XCTFail(); return } +// XCTAssertEqual(error, .userRejeted) +// responseExpectation.fulfill() +// } +// .store(in: &publishers) +// wait(for: [responseExpectation], timeout: InputConfig.defaultTimeout) +// } +// +// func testRespondSignatureVerificationFailed() async { +// let responseExpectation = expectation(description: "invalid signature response delivered") +// let uri = try! await app.request(RequestParams.stub()) +// try! await wallet.pair(uri: uri) +// walletAuthClient.authRequestPublisher.sink { [unowned self] request in +// Task(priority: .high) { +// let invalidSignature = "438effc459956b57fcd9f3dac6c675f9cee88abf21acab7305e8e32aa0303a883b06dcbd956279a7a2ca21ffa882ff55cc22e8ab8ec0f3fe90ab45f306938cfa1b" +// let cacaoSignature = CacaoSignature(t: "eip191", s: invalidSignature) +// try! await walletAuthClient.respond(requestId: request.id, signature: cacaoSignature) +// } +// } +// .store(in: &publishers) +// appAuthClient.authResponsePublisher.sink { (_, result) in +// guard case .failure(let error) = result else { XCTFail(); return } +// XCTAssertEqual(error, .signatureVerificationFailed) +// responseExpectation.fulfill() +// } +// .store(in: &publishers) +// wait(for: [responseExpectation], timeout: InputConfig.defaultTimeout) +// } } diff --git a/Sources/Auth/Auth.swift b/Sources/Auth/Auth.swift index 62b8675e4..b85b20847 100644 --- a/Sources/Auth/Auth.swift +++ b/Sources/Auth/Auth.swift @@ -25,7 +25,7 @@ public class Auth { metadata: config.metadata, account: config.account, relayClient: Relay.instance, - pairingClient: Pair.instance) + pairingRegisterer: Pair.instance) }() private static var config: Config? diff --git a/Sources/Auth/AuthClient.swift b/Sources/Auth/AuthClient.swift index ebf07ae7a..f4080be27 100644 --- a/Sources/Auth/AuthClient.swift +++ b/Sources/Auth/AuthClient.swift @@ -59,7 +59,7 @@ public class AuthClient { pendingRequestsProvider: PendingRequestsProvider, logger: ConsoleLogging, socketConnectionStatusPublisher: AnyPublisher, - pairingClient: PairingClient + pairingRegisterer: PairingRegisterer ) { self.appRequestService = appRequestService self.walletRequestSubscriber = walletRequestSubscriber @@ -69,7 +69,7 @@ public class AuthClient { self.pendingRequestsProvider = pendingRequestsProvider self.logger = logger self.socketConnectionStatusPublisher = socketConnectionStatusPublisher - self.pairingRegisterer = pairingClient + self.pairingRegisterer = pairingRegisterer setUpPublishers() } diff --git a/Sources/Auth/AuthClientFactory.swift b/Sources/Auth/AuthClientFactory.swift index 4513eb53c..34c27661e 100644 --- a/Sources/Auth/AuthClientFactory.swift +++ b/Sources/Auth/AuthClientFactory.swift @@ -7,14 +7,14 @@ import WalletConnectNetworking public struct AuthClientFactory { - public static func create(metadata: AppMetadata, account: Account?, relayClient: RelayClient, pairingClient: PairingClient) -> AuthClient { + public static func create(metadata: AppMetadata, account: Account?, relayClient: RelayClient, pairingRegisterer: PairingRegisterer) -> AuthClient { let logger = ConsoleLogger(loggingLevel: .off) let keyValueStorage = UserDefaults.standard let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk") - return AuthClientFactory.create(metadata: metadata, account: account, logger: logger, keyValueStorage: keyValueStorage, keychainStorage: keychainStorage, relayClient: relayClient, pairingClient: pairingClient) + return AuthClientFactory.create(metadata: metadata, account: account, logger: logger, keyValueStorage: keyValueStorage, keychainStorage: keychainStorage, relayClient: relayClient, pairingRegisterer: pairingRegisterer) } - static func create(metadata: AppMetadata, account: Account?, logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, relayClient: RelayClient, pairingClient: PairingClient) -> AuthClient { + static func create(metadata: AppMetadata, account: Account?, logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, relayClient: RelayClient, pairingRegisterer: PairingRegisterer) -> AuthClient { let kms = KeyManagementService(keychain: keychainStorage) let serializer = Serializer(kms: kms) let history = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage) @@ -24,7 +24,7 @@ public struct AuthClientFactory { let messageSigner = MessageSigner(signer: Signer()) let appRespondSubscriber = AppRespondSubscriber(networkingInteractor: networkingInteractor, logger: logger, rpcHistory: history, signatureVerifier: messageSigner, messageFormatter: messageFormatter) let walletErrorResponder = WalletErrorResponder(networkingInteractor: networkingInteractor, logger: logger, kms: kms, rpcHistory: history) - let walletRequestSubscriber = WalletRequestSubscriber(networkingInteractor: networkingInteractor, logger: logger, kms: kms, messageFormatter: messageFormatter, address: account?.address, walletErrorResponder: walletErrorResponder, pairingRegisterer: pairingClient) + let walletRequestSubscriber = WalletRequestSubscriber(networkingInteractor: networkingInteractor, logger: logger, kms: kms, messageFormatter: messageFormatter, address: account?.address, walletErrorResponder: walletErrorResponder, pairingRegisterer: pairingRegisterer) let walletRespondService = WalletRespondService(networkingInteractor: networkingInteractor, logger: logger, kms: kms, rpcHistory: history, walletErrorResponder: walletErrorResponder) let pendingRequestsProvider = PendingRequestsProvider(rpcHistory: history) @@ -36,6 +36,6 @@ public struct AuthClientFactory { pendingRequestsProvider: pendingRequestsProvider, logger: logger, socketConnectionStatusPublisher: relayClient.socketConnectionStatusPublisher, - pairingClient: pairingClient) + pairingRegisterer: pairingRegisterer) } } diff --git a/Sources/WalletConnectPairing/PairingClient.swift b/Sources/WalletConnectPairing/PairingClient.swift index 1c6ff9bd6..a7c9b7a82 100644 --- a/Sources/WalletConnectPairing/PairingClient.swift +++ b/Sources/WalletConnectPairing/PairingClient.swift @@ -6,7 +6,6 @@ import Combine import JSONRPC public class PairingClient: PairingRegisterer { - public var pingResponsePublisher: AnyPublisher<(String), Never> { pingResponsePublisherSubject.eraseToAnyPublisher() } @@ -101,6 +100,10 @@ public class PairingClient: PairingRegisterer { } + public func validatePairingExistance(_ topic: String) throws { + _ = try pairingsProvider.getPairing(for: topic) + } + public func register(method: ProtocolMethod) -> AnyPublisher, Never> { logger.debug("Pairing Client - registering for \(method.method)") return pairingRequestsSubscriber.subscribeForRequest(method) diff --git a/Sources/WalletConnectPairing/Services/Common/PairingsProvider.swift b/Sources/WalletConnectPairing/Services/Common/PairingsProvider.swift index d7ee75a48..aa087a3c0 100644 --- a/Sources/WalletConnectPairing/Services/Common/PairingsProvider.swift +++ b/Sources/WalletConnectPairing/Services/Common/PairingsProvider.swift @@ -1,6 +1,6 @@ import Foundation -public class PairingsProvider { +class PairingsProvider { enum Errors: Error { case noPairingMatchingTopic } @@ -15,8 +15,7 @@ public class PairingsProvider { .map {Pairing($0)} } - - public func getPairing(for topic: String) throws -> Pairing { + func getPairing(for topic: String) throws -> Pairing { guard let pairing = pairingStorage.getPairing(forTopic: topic) else { throw Errors.noPairingMatchingTopic } From 6d8f836f2c2cd2adad53ee700a0993aaf67a3deb Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Fri, 30 Sep 2022 13:51:00 +0200 Subject: [PATCH 113/175] add pair to sample apps and fix integration tests --- Example/DApp/Auth/AuthViewModel.swift | 4 +- Example/IntegrationTests/Auth/AuthTests.swift | 122 +++++++++--------- .../ApplicationLayer/SceneDelegate.swift | 3 +- .../Wallet/Wallet/WalletInteractor.swift | 3 +- 4 files changed, 71 insertions(+), 61 deletions(-) diff --git a/Example/DApp/Auth/AuthViewModel.swift b/Example/DApp/Auth/AuthViewModel.swift index 5554fc8b6..bb9035de5 100644 --- a/Example/DApp/Auth/AuthViewModel.swift +++ b/Example/DApp/Auth/AuthViewModel.swift @@ -1,6 +1,7 @@ import UIKit import Combine import Auth +import WalletConnectPairing final class AuthViewModel: ObservableObject { @@ -27,7 +28,8 @@ final class AuthViewModel: ObservableObject { func setupInitialState() async throws { state = .none uri = nil - uri = try await Auth.instance.request(.stub()).absoluteString + let uri = try! await Pair.instance.create() + try await Auth.instance.request(.stub(), topic: uri.topic) } func copyDidPressed() { diff --git a/Example/IntegrationTests/Auth/AuthTests.swift b/Example/IntegrationTests/Auth/AuthTests.swift index 88fa0c066..1b1521fb6 100644 --- a/Example/IntegrationTests/Auth/AuthTests.swift +++ b/Example/IntegrationTests/Auth/AuthTests.swift @@ -59,62 +59,68 @@ final class AuthTests: XCTestCase { wait(for: [requestExpectation], timeout: InputConfig.defaultTimeout) } -// func testRespondSuccess() async { -// let responseExpectation = expectation(description: "successful response delivered") -// let uri = try! await app.request(RequestParams.stub()) -// try! await wallet.pair(uri: uri) -// walletAuthClient.authRequestPublisher.sink { [unowned self] request in -// Task(priority: .high) { -// let signature = try! MessageSigner().sign(message: request.message, privateKey: prvKey) -// try! await walletAuthClient.respond(requestId: request.id, signature: signature) -// } -// } -// .store(in: &publishers) -// appAuthClient.authResponsePublisher.sink { (_, result) in -// guard case .success = result else { XCTFail(); return } -// responseExpectation.fulfill() -// } -// .store(in: &publishers) -// wait(for: [responseExpectation], timeout: InputConfig.defaultTimeout) -// } -// -// func testUserRespondError() async { -// let responseExpectation = expectation(description: "error response delivered") -// let uri = try! await app.request(RequestParams.stub()) -// try! await wallet.pair(uri: uri) -// walletAuthClient.authRequestPublisher.sink { [unowned self] request in -// Task(priority: .high) { -// try! await walletAuthClient.reject(requestId: request.id) -// } -// } -// .store(in: &publishers) -// appAuthClient.authResponsePublisher.sink { (_, result) in -// guard case .failure(let error) = result else { XCTFail(); return } -// XCTAssertEqual(error, .userRejeted) -// responseExpectation.fulfill() -// } -// .store(in: &publishers) -// wait(for: [responseExpectation], timeout: InputConfig.defaultTimeout) -// } -// -// func testRespondSignatureVerificationFailed() async { -// let responseExpectation = expectation(description: "invalid signature response delivered") -// let uri = try! await app.request(RequestParams.stub()) -// try! await wallet.pair(uri: uri) -// walletAuthClient.authRequestPublisher.sink { [unowned self] request in -// Task(priority: .high) { -// let invalidSignature = "438effc459956b57fcd9f3dac6c675f9cee88abf21acab7305e8e32aa0303a883b06dcbd956279a7a2ca21ffa882ff55cc22e8ab8ec0f3fe90ab45f306938cfa1b" -// let cacaoSignature = CacaoSignature(t: "eip191", s: invalidSignature) -// try! await walletAuthClient.respond(requestId: request.id, signature: cacaoSignature) -// } -// } -// .store(in: &publishers) -// appAuthClient.authResponsePublisher.sink { (_, result) in -// guard case .failure(let error) = result else { XCTFail(); return } -// XCTAssertEqual(error, .signatureVerificationFailed) -// responseExpectation.fulfill() -// } -// .store(in: &publishers) -// wait(for: [responseExpectation], timeout: InputConfig.defaultTimeout) -// } + func testRespondSuccess() async { + let responseExpectation = expectation(description: "successful response delivered") + let uri = try! await appPairingClient.create() + try! await appAuthClient.request(RequestParams.stub(), topic: uri.topic) + + try! await walletPairingClient.pair(uri: uri) + walletAuthClient.authRequestPublisher.sink { [unowned self] request in + Task(priority: .high) { + let signature = try! MessageSigner().sign(message: request.message, privateKey: prvKey) + try! await walletAuthClient.respond(requestId: request.id, signature: signature) + } + } + .store(in: &publishers) + appAuthClient.authResponsePublisher.sink { (_, result) in + guard case .success = result else { XCTFail(); return } + responseExpectation.fulfill() + } + .store(in: &publishers) + wait(for: [responseExpectation], timeout: InputConfig.defaultTimeout) + } + + func testUserRespondError() async { + let responseExpectation = expectation(description: "error response delivered") + let uri = try! await appPairingClient.create() + try! await appAuthClient.request(RequestParams.stub(), topic: uri.topic) + + try! await walletPairingClient.pair(uri: uri) + walletAuthClient.authRequestPublisher.sink { [unowned self] request in + Task(priority: .high) { + try! await walletAuthClient.reject(requestId: request.id) + } + } + .store(in: &publishers) + appAuthClient.authResponsePublisher.sink { (_, result) in + guard case .failure(let error) = result else { XCTFail(); return } + XCTAssertEqual(error, .userRejeted) + responseExpectation.fulfill() + } + .store(in: &publishers) + wait(for: [responseExpectation], timeout: InputConfig.defaultTimeout) + } + + func testRespondSignatureVerificationFailed() async { + let responseExpectation = expectation(description: "invalid signature response delivered") + let uri = try! await appPairingClient.create() + try! await appAuthClient.request(RequestParams.stub(), topic: uri.topic) + + try! await walletPairingClient.pair(uri: uri) + walletAuthClient.authRequestPublisher.sink { [unowned self] request in + Task(priority: .high) { + let invalidSignature = "438effc459956b57fcd9f3dac6c675f9cee88abf21acab7305e8e32aa0303a883b06dcbd956279a7a2ca21ffa882ff55cc22e8ab8ec0f3fe90ab45f306938cfa1b" + let cacaoSignature = CacaoSignature(t: "eip191", s: invalidSignature) + try! await walletAuthClient.respond(requestId: request.id, signature: cacaoSignature) + } + } + .store(in: &publishers) + appAuthClient.authResponsePublisher.sink { (_, result) in + guard case .failure(let error) = result else { XCTFail(); return } + XCTAssertEqual(error, .signatureVerificationFailed) + responseExpectation.fulfill() + } + .store(in: &publishers) + wait(for: [responseExpectation], timeout: InputConfig.defaultTimeout) + } } diff --git a/Example/Showcase/Classes/ApplicationLayer/SceneDelegate.swift b/Example/Showcase/Classes/ApplicationLayer/SceneDelegate.swift index 5956c494e..7681f528a 100644 --- a/Example/Showcase/Classes/ApplicationLayer/SceneDelegate.swift +++ b/Example/Showcase/Classes/ApplicationLayer/SceneDelegate.swift @@ -1,5 +1,6 @@ import UIKit import Auth +import WalletConnectPairing class SceneDelegate: UIResponder, UIWindowSceneDelegate { @@ -30,7 +31,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { let uri = context.url.absoluteString.replacingOccurrences(of: "showcase://wc?uri=", with: "") Task { - try await Auth.instance.pair(uri: WalletConnectURI(string: uri)!) + try await Pair.instance.pair(uri: WalletConnectURI(string: uri)!) } } } diff --git a/Example/Showcase/Classes/PresentationLayer/Wallet/Wallet/WalletInteractor.swift b/Example/Showcase/Classes/PresentationLayer/Wallet/Wallet/WalletInteractor.swift index 051ba266e..7379f18c5 100644 --- a/Example/Showcase/Classes/PresentationLayer/Wallet/Wallet/WalletInteractor.swift +++ b/Example/Showcase/Classes/PresentationLayer/Wallet/Wallet/WalletInteractor.swift @@ -1,10 +1,11 @@ import Combine import Auth +import WalletConnectPairing final class WalletInteractor { func pair(uri: WalletConnectURI) async throws { - try await Auth.instance.pair(uri: uri) + try await Pair.instance.pair(uri: uri) } var requestPublisher: AnyPublisher { From 7534a4b98fa0b7197b7af226f6d05bf1d7cb43df Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Fri, 30 Sep 2022 14:00:09 +0200 Subject: [PATCH 114/175] fix sample apps --- Example/DApp/Sign/SignCoordinator.swift | 4 +++- .../Configurator/ThirdPartyConfigurator.swift | 7 ++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Example/DApp/Sign/SignCoordinator.swift b/Example/DApp/Sign/SignCoordinator.swift index 8470ad200..b25db1760 100644 --- a/Example/DApp/Sign/SignCoordinator.swift +++ b/Example/DApp/Sign/SignCoordinator.swift @@ -2,6 +2,7 @@ import UIKit import Combine import WalletConnectSign import WalletConnectRelay +import WalletConnectPairing final class SignCoordinator { @@ -25,7 +26,8 @@ final class SignCoordinator { url: "wallet.connect", icons: ["https://avatars.githubusercontent.com/u/37784886"]) - Sign.configure(metadata: metadata) + Pair.configure(metadata: metadata) + Sign.configure() #if DEBUG if CommandLine.arguments.contains("-cleanInstall") { try? Sign.instance.cleanup() diff --git a/Example/Showcase/Classes/ApplicationLayer/Configurator/ThirdPartyConfigurator.swift b/Example/Showcase/Classes/ApplicationLayer/Configurator/ThirdPartyConfigurator.swift index ce8dbd3b2..231571f61 100644 --- a/Example/Showcase/Classes/ApplicationLayer/Configurator/ThirdPartyConfigurator.swift +++ b/Example/Showcase/Classes/ApplicationLayer/Configurator/ThirdPartyConfigurator.swift @@ -6,14 +6,15 @@ struct ThirdPartyConfigurator: Configurator { func configure() { Relay.configure(projectId: InputConfig.projectId, socketFactory: SocketFactory()) - - Auth.configure( + Pair.configure( metadata: AppMetadata( name: "Showcase App", description: "Showcase description", url: "example.wallet", icons: ["https://avatars.githubusercontent.com/u/37784886"] - ), + )) + + Auth.configure( account: Account("eip155:1:0xe5EeF1368781911d265fDB6946613dA61915a501")! ) } From df93845b9d3e822a2daff98e4b87bb257e3fd23b Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Fri, 30 Sep 2022 15:02:28 +0200 Subject: [PATCH 115/175] fix unit tests --- Tests/AuthTests/Mocks/PairingRegistererMock.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Tests/AuthTests/Mocks/PairingRegistererMock.swift b/Tests/AuthTests/Mocks/PairingRegistererMock.swift index 69c4f34e4..ceb7807c8 100644 --- a/Tests/AuthTests/Mocks/PairingRegistererMock.swift +++ b/Tests/AuthTests/Mocks/PairingRegistererMock.swift @@ -4,9 +4,13 @@ import Combine import WalletConnectNetworking struct PairingRegistererMock: PairingRegisterer where RequestParams : Codable { + let subject = PassthroughSubject, Never>() func register(method: ProtocolMethod) -> AnyPublisher, Never> where RequestParams : Decodable, RequestParams : Encodable { subject.eraseToAnyPublisher() as! AnyPublisher, Never> } + + func validatePairingExistance(_ topic: String) throws { + } } From ccfc3631afb02bdc0e9cc073074fe7d56d2939d7 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Fri, 30 Sep 2022 15:07:39 +0200 Subject: [PATCH 116/175] fix wallet --- Sources/WalletConnectSign/Sign/Sign.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Sources/WalletConnectSign/Sign/Sign.swift b/Sources/WalletConnectSign/Sign/Sign.swift index 3bea71131..09b02702e 100644 --- a/Sources/WalletConnectSign/Sign/Sign.swift +++ b/Sources/WalletConnectSign/Sign/Sign.swift @@ -43,6 +43,7 @@ public class Sign { /// - metadata: App metadata @available(*, deprecated, message: "Use Pair.configure(metadata:) instead") static public func configure(metadata: AppMetadata) { + Pair.configure(metadata: metadata) Sign.metadata = metadata } } From 8bd029d5c263e73c0bf2aa183cf62ddda945a444 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Fri, 30 Sep 2022 15:08:50 +0200 Subject: [PATCH 117/175] fix dapp --- Example/DApp/Sign/SignCoordinator.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Example/DApp/Sign/SignCoordinator.swift b/Example/DApp/Sign/SignCoordinator.swift index b25db1760..51e70078e 100644 --- a/Example/DApp/Sign/SignCoordinator.swift +++ b/Example/DApp/Sign/SignCoordinator.swift @@ -27,7 +27,6 @@ final class SignCoordinator { icons: ["https://avatars.githubusercontent.com/u/37784886"]) Pair.configure(metadata: metadata) - Sign.configure() #if DEBUG if CommandLine.arguments.contains("-cleanInstall") { try? Sign.instance.cleanup() From 4fe4efca232de79aec9c3e4853687b360b8f27c9 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Fri, 30 Sep 2022 20:29:57 +0600 Subject: [PATCH 118/175] Pair Activate Service --- .../Services/App/AppRespondSubscriber.swift | 4 +- .../WalletConnectPairing/PairingClient.swift | 5 ++- .../PairingClientFactory.swift | 2 + .../App/AppPairActivationService.swift | 39 +++++++++++++++++++ .../App/PairingActivationService.swift | 15 ------- 5 files changed, 46 insertions(+), 19 deletions(-) create mode 100644 Sources/WalletConnectPairing/Services/App/AppPairActivationService.swift delete mode 100644 Sources/WalletConnectPairing/Services/App/PairingActivationService.swift diff --git a/Sources/Auth/Services/App/AppRespondSubscriber.swift b/Sources/Auth/Services/App/AppRespondSubscriber.swift index 7e47f5cd8..822786a8d 100644 --- a/Sources/Auth/Services/App/AppRespondSubscriber.swift +++ b/Sources/Auth/Services/App/AppRespondSubscriber.swift @@ -37,9 +37,7 @@ class AppRespondSubscriber { networkingInteractor.responseSubscription(on: AuthRequestProtocolMethod()) .sink { [unowned self] (payload: ResponseSubscriptionPayload) in - - //TODO - call pairing client to activate -// activatePairingIfNeeded(id: payload.id) + networkingInteractor.unsubscribe(topic: payload.topic) let requestId = payload.id diff --git a/Sources/WalletConnectPairing/PairingClient.swift b/Sources/WalletConnectPairing/PairingClient.swift index a7c9b7a82..3d1efdec3 100644 --- a/Sources/WalletConnectPairing/PairingClient.swift +++ b/Sources/WalletConnectPairing/PairingClient.swift @@ -13,6 +13,7 @@ public class PairingClient: PairingRegisterer { private let walletPairService: WalletPairService private let appPairService: AppPairService + private let appPairActivateService: AppPairActivationService private var pingResponsePublisherSubject = PassthroughSubject() private let logger: ConsoleLogging private let pingService: PairingPingService @@ -30,6 +31,7 @@ public class PairingClient: PairingRegisterer { walletPairService: WalletPairService, deletePairingService: DeletePairingService, pairingRequestsSubscriber: PairingRequestsSubscriber, + appPairActivateService: AppPairActivationService, pairingStorage: WCPairingStorage, cleanupService: CleanupService, pingService: PairingPingService, @@ -43,6 +45,7 @@ public class PairingClient: PairingRegisterer { self.logger = logger self.pairingStorage = pairingStorage self.deletePairingService = deletePairingService + self.appPairActivateService = appPairActivateService self.cleanupService = cleanupService self.pingService = pingService self.pairingRequestsSubscriber = pairingRequestsSubscriber @@ -97,7 +100,6 @@ public class PairingClient: PairingRegisterer { public func disconnect(topic: String) async throws { try await deletePairingService.delete(topic: topic) - } public func validatePairingExistance(_ topic: String) throws { @@ -106,6 +108,7 @@ public class PairingClient: PairingRegisterer { public func register(method: ProtocolMethod) -> AnyPublisher, Never> { logger.debug("Pairing Client - registering for \(method.method)") + appPairActivateService.activate(on: method) return pairingRequestsSubscriber.subscribeForRequest(method) } diff --git a/Sources/WalletConnectPairing/PairingClientFactory.swift b/Sources/WalletConnectPairing/PairingClientFactory.swift index a74bd04f2..894c789b8 100644 --- a/Sources/WalletConnectPairing/PairingClientFactory.swift +++ b/Sources/WalletConnectPairing/PairingClientFactory.swift @@ -26,6 +26,7 @@ public struct PairingClientFactory { let cleanupService = CleanupService(pairingStore: pairingStore, kms: kms) let deletePairingService = DeletePairingService(networkingInteractor: networkingInteractor, kms: kms, pairingStorage: pairingStore, logger: logger) let pingService = PairingPingService(pairingStorage: pairingStore, networkingInteractor: networkingInteractor, logger: logger) + let appPairActivateService = AppPairActivationService(networkInteractor: networkingInteractor, pairingStorage: pairingStore, history: history, logger: logger) return PairingClient( appPairService: appPairService, @@ -34,6 +35,7 @@ public struct PairingClientFactory { walletPairService: walletPairService, deletePairingService: deletePairingService, pairingRequestsSubscriber: pairingRequestsSubscriber, + appPairActivateService: appPairActivateService, pairingStorage: pairingStore, cleanupService: cleanupService, pingService: pingService, diff --git a/Sources/WalletConnectPairing/Services/App/AppPairActivationService.swift b/Sources/WalletConnectPairing/Services/App/AppPairActivationService.swift new file mode 100644 index 000000000..941ddca95 --- /dev/null +++ b/Sources/WalletConnectPairing/Services/App/AppPairActivationService.swift @@ -0,0 +1,39 @@ +import Foundation +import Combine +import WalletConnectNetworking +import WalletConnectUtils + +final class AppPairActivationService { + private let networkInteractor: NetworkInteracting + private let pairingStorage: PairingStorage + private let history: RPCHistory + private let logger: ConsoleLogging + + private var publishers = Set() + + init( + networkInteractor: NetworkInteracting, + pairingStorage: PairingStorage, + history: RPCHistory, + logger: ConsoleLogging + ) { + self.networkInteractor = networkInteractor + self.pairingStorage = pairingStorage + self.history = history + self.logger = logger + } + + func activate(on method: ProtocolMethod) { + networkInteractor.responseSubscription(on: method) + .sink { [unowned self] (payload: ResponseSubscriptionPayload) in + guard var pairing = pairingStorage.getPairing(forTopic: payload.topic) else { + return logger.error("Pairing not found for topic: \(payload.topic)") + } + if !pairing.active { + pairing.activate() + } else { + try? pairing.updateExpiry() + } + }.store(in: &publishers) + } +} diff --git a/Sources/WalletConnectPairing/Services/App/PairingActivationService.swift b/Sources/WalletConnectPairing/Services/App/PairingActivationService.swift deleted file mode 100644 index 5062dafc9..000000000 --- a/Sources/WalletConnectPairing/Services/App/PairingActivationService.swift +++ /dev/null @@ -1,15 +0,0 @@ -// - -import Foundation - -// TODO -//private func activatePairingIfNeeded(id: RPCID) { -// guard let record = rpcHistory.get(recordId: id) else { return } -// let pairingTopic = record.topic -// guard var pairing = pairingStorage.getPairing(forTopic: pairingTopic) else { return } -// if !pairing.active { -// pairing.activate() -// } else { -// try? pairing.updateExpiry() -// } -//} From 1c3094a92bf03f94fa20ff0ae960bf9a6a26a4f8 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Fri, 30 Sep 2022 23:08:00 +0600 Subject: [PATCH 119/175] Pair activate from public interface --- Sources/Auth/AuthClientFactory.swift | 2 +- .../Services/App/AppRespondSubscriber.swift | 6 +++- .../WalletConnectPairing/PairingClient.swift | 5 ++- .../PairingClientFactory.swift | 2 +- .../PairingRegisterer.swift | 6 +++- .../App/AppPairActivationService.swift | 35 ++++++------------- 6 files changed, 27 insertions(+), 29 deletions(-) diff --git a/Sources/Auth/AuthClientFactory.swift b/Sources/Auth/AuthClientFactory.swift index 34c27661e..8e8fbed58 100644 --- a/Sources/Auth/AuthClientFactory.swift +++ b/Sources/Auth/AuthClientFactory.swift @@ -22,7 +22,7 @@ public struct AuthClientFactory { let messageFormatter = SIWEMessageFormatter() let appRequestService = AppRequestService(networkingInteractor: networkingInteractor, kms: kms, appMetadata: metadata, logger: logger) let messageSigner = MessageSigner(signer: Signer()) - let appRespondSubscriber = AppRespondSubscriber(networkingInteractor: networkingInteractor, logger: logger, rpcHistory: history, signatureVerifier: messageSigner, messageFormatter: messageFormatter) + let appRespondSubscriber = AppRespondSubscriber(networkingInteractor: networkingInteractor, logger: logger, rpcHistory: history, signatureVerifier: messageSigner, pairingRegisterer: pairingRegisterer, messageFormatter: messageFormatter) let walletErrorResponder = WalletErrorResponder(networkingInteractor: networkingInteractor, logger: logger, kms: kms, rpcHistory: history) let walletRequestSubscriber = WalletRequestSubscriber(networkingInteractor: networkingInteractor, logger: logger, kms: kms, messageFormatter: messageFormatter, address: account?.address, walletErrorResponder: walletErrorResponder, pairingRegisterer: pairingRegisterer) let walletRespondService = WalletRespondService(networkingInteractor: networkingInteractor, logger: logger, kms: kms, rpcHistory: history, walletErrorResponder: walletErrorResponder) diff --git a/Sources/Auth/Services/App/AppRespondSubscriber.swift b/Sources/Auth/Services/App/AppRespondSubscriber.swift index 822786a8d..56679aad3 100644 --- a/Sources/Auth/Services/App/AppRespondSubscriber.swift +++ b/Sources/Auth/Services/App/AppRespondSubscriber.swift @@ -11,6 +11,7 @@ class AppRespondSubscriber { private let rpcHistory: RPCHistory private let signatureVerifier: MessageSignatureVerifying private let messageFormatter: SIWEMessageFormatting + private let pairingRegisterer: PairingRegisterer private var publishers = [AnyCancellable]() var onResponse: ((_ id: RPCID, _ result: Result) -> Void)? @@ -19,12 +20,14 @@ class AppRespondSubscriber { logger: ConsoleLogging, rpcHistory: RPCHistory, signatureVerifier: MessageSignatureVerifying, + pairingRegisterer: PairingRegisterer, messageFormatter: SIWEMessageFormatting) { self.networkingInteractor = networkingInteractor self.logger = logger self.rpcHistory = rpcHistory self.signatureVerifier = signatureVerifier self.messageFormatter = messageFormatter + self.pairingRegisterer = pairingRegisterer subscribeForResponse() } @@ -37,7 +40,8 @@ class AppRespondSubscriber { networkingInteractor.responseSubscription(on: AuthRequestProtocolMethod()) .sink { [unowned self] (payload: ResponseSubscriptionPayload) in - + + pairingRegisterer.activate(pairingTopic: payload.topic) networkingInteractor.unsubscribe(topic: payload.topic) let requestId = payload.id diff --git a/Sources/WalletConnectPairing/PairingClient.swift b/Sources/WalletConnectPairing/PairingClient.swift index 3d1efdec3..20f035b84 100644 --- a/Sources/WalletConnectPairing/PairingClient.swift +++ b/Sources/WalletConnectPairing/PairingClient.swift @@ -108,10 +108,13 @@ public class PairingClient: PairingRegisterer { public func register(method: ProtocolMethod) -> AnyPublisher, Never> { logger.debug("Pairing Client - registering for \(method.method)") - appPairActivateService.activate(on: method) return pairingRequestsSubscriber.subscribeForRequest(method) } + public func activate(pairingTopic: String) { + appPairActivateService.activate(for: pairingTopic) + } + #if DEBUG /// Delete all stored data such as: pairings, keys /// diff --git a/Sources/WalletConnectPairing/PairingClientFactory.swift b/Sources/WalletConnectPairing/PairingClientFactory.swift index 894c789b8..cfe1d242a 100644 --- a/Sources/WalletConnectPairing/PairingClientFactory.swift +++ b/Sources/WalletConnectPairing/PairingClientFactory.swift @@ -26,7 +26,7 @@ public struct PairingClientFactory { let cleanupService = CleanupService(pairingStore: pairingStore, kms: kms) let deletePairingService = DeletePairingService(networkingInteractor: networkingInteractor, kms: kms, pairingStorage: pairingStore, logger: logger) let pingService = PairingPingService(pairingStorage: pairingStore, networkingInteractor: networkingInteractor, logger: logger) - let appPairActivateService = AppPairActivationService(networkInteractor: networkingInteractor, pairingStorage: pairingStore, history: history, logger: logger) + let appPairActivateService = AppPairActivationService(pairingStorage: pairingStore, logger: logger) return PairingClient( appPairService: appPairService, diff --git a/Sources/WalletConnectPairing/PairingRegisterer.swift b/Sources/WalletConnectPairing/PairingRegisterer.swift index 67c1eb45e..b5d6dc36a 100644 --- a/Sources/WalletConnectPairing/PairingRegisterer.swift +++ b/Sources/WalletConnectPairing/PairingRegisterer.swift @@ -4,6 +4,10 @@ import Combine import JSONRPC public protocol PairingRegisterer { - func register(method: ProtocolMethod) -> AnyPublisher, Never> where RequestParams : Codable + func register( + method: ProtocolMethod + ) -> AnyPublisher, Never> + + func activate(pairingTopic: String) func validatePairingExistance(_ topic: String) throws } diff --git a/Sources/WalletConnectPairing/Services/App/AppPairActivationService.swift b/Sources/WalletConnectPairing/Services/App/AppPairActivationService.swift index 941ddca95..5b96e3908 100644 --- a/Sources/WalletConnectPairing/Services/App/AppPairActivationService.swift +++ b/Sources/WalletConnectPairing/Services/App/AppPairActivationService.swift @@ -4,36 +4,23 @@ import WalletConnectNetworking import WalletConnectUtils final class AppPairActivationService { - private let networkInteractor: NetworkInteracting private let pairingStorage: PairingStorage - private let history: RPCHistory private let logger: ConsoleLogging - private var publishers = Set() - - init( - networkInteractor: NetworkInteracting, - pairingStorage: PairingStorage, - history: RPCHistory, - logger: ConsoleLogging - ) { - self.networkInteractor = networkInteractor + init(pairingStorage: PairingStorage, logger: ConsoleLogging) { self.pairingStorage = pairingStorage - self.history = history self.logger = logger } - func activate(on method: ProtocolMethod) { - networkInteractor.responseSubscription(on: method) - .sink { [unowned self] (payload: ResponseSubscriptionPayload) in - guard var pairing = pairingStorage.getPairing(forTopic: payload.topic) else { - return logger.error("Pairing not found for topic: \(payload.topic)") - } - if !pairing.active { - pairing.activate() - } else { - try? pairing.updateExpiry() - } - }.store(in: &publishers) + func activate(for topic: String) { + guard var pairing = pairingStorage.getPairing(forTopic: topic) else { + return logger.error("Pairing not found for topic: \(topic)") + } + if !pairing.active { + pairing.activate() + } else { + try? pairing.updateExpiry() + } + pairingStorage.setPairing(pairing) } } From 5a55d2e54035f82a54eae3c9cd08d48cbb4ed001 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Fri, 30 Sep 2022 23:14:56 +0600 Subject: [PATCH 120/175] Build tests error --- Tests/AuthTests/AppRespondSubscriberTests.swift | 1 + Tests/AuthTests/Mocks/PairingRegistererMock.swift | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/Tests/AuthTests/AppRespondSubscriberTests.swift b/Tests/AuthTests/AppRespondSubscriberTests.swift index 3314fbd59..fa982be31 100644 --- a/Tests/AuthTests/AppRespondSubscriberTests.swift +++ b/Tests/AuthTests/AppRespondSubscriberTests.swift @@ -29,6 +29,7 @@ class AppRespondSubscriberTests: XCTestCase { logger: ConsoleLoggerMock(), rpcHistory: rpcHistory, signatureVerifier: messageSigner, + pairingRegisterer: PairingRegistererMock(), messageFormatter: messageFormatter) } diff --git a/Tests/AuthTests/Mocks/PairingRegistererMock.swift b/Tests/AuthTests/Mocks/PairingRegistererMock.swift index ceb7807c8..be29b3921 100644 --- a/Tests/AuthTests/Mocks/PairingRegistererMock.swift +++ b/Tests/AuthTests/Mocks/PairingRegistererMock.swift @@ -10,6 +10,10 @@ struct PairingRegistererMock: PairingRegisterer where RequestPara func register(method: ProtocolMethod) -> AnyPublisher, Never> where RequestParams : Decodable, RequestParams : Encodable { subject.eraseToAnyPublisher() as! AnyPublisher, Never> } + + func activate(pairingTopic: String) { + + } func validatePairingExistance(_ topic: String) throws { } From ea3a0856708ceb1d48bb0ad5f8494b876ecf1eaf Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Mon, 3 Oct 2022 11:51:58 +0600 Subject: [PATCH 121/175] Updated config --- .gitignore | 2 +- Configuration.xcconfig | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index dad29299f..67c5412b0 100644 --- a/.gitignore +++ b/.gitignore @@ -21,5 +21,5 @@ Package.resolved */fastlane/README.md # Configuration -Configuration.xcconfig +# Configuration.xcconfig diff --git a/Configuration.xcconfig b/Configuration.xcconfig index ef29a642d..15db0fdbc 100644 --- a/Configuration.xcconfig +++ b/Configuration.xcconfig @@ -1,2 +1,4 @@ +RELAY_HOST = relay.walletconnect.com + // Uncomment next line and paste your project id. Get this on: https://cloud.walletconnect.com/sign-in // PROJECT_ID = YOUR_PROJECT_ID From 8e486ebcb12a13ee7a80e0437f4077764fce0ac1 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Mon, 3 Oct 2022 11:52:09 +0600 Subject: [PATCH 122/175] Uncomment gitignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 67c5412b0..dad29299f 100644 --- a/.gitignore +++ b/.gitignore @@ -21,5 +21,5 @@ Package.resolved */fastlane/README.md # Configuration -# Configuration.xcconfig +Configuration.xcconfig From c248530208e5f9fde000b6ec9e889d2c26152e49 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 3 Oct 2022 12:24:45 +0200 Subject: [PATCH 123/175] savepoint --- .../Pairing/PairingTests.swift | 75 +++++++++++++++---- .../PairingRequestsSubscriber.swift | 1 + .../Types/PairError.swift | 9 +++ .../ProposalResponseSubscriber.swift | 33 ++++++++ Sources/WalletConnectPush/PushClient.swift | 24 +++--- .../WalletConnectPush/PushClientFactory.swift | 5 +- .../PushProposeProtocolMethod.swift | 2 + Sources/WalletConnectPush/PushProposer.swift | 5 +- Sources/WalletConnectUtils/SetStore.swift | 6 +- 9 files changed, 130 insertions(+), 30 deletions(-) create mode 100644 Sources/WalletConnectPush/ProposalResponseSubscriber.swift diff --git a/Example/IntegrationTests/Pairing/PairingTests.swift b/Example/IntegrationTests/Pairing/PairingTests.swift index d1e63482c..ddfcdc5be 100644 --- a/Example/IntegrationTests/Pairing/PairingTests.swift +++ b/Example/IntegrationTests/Pairing/PairingTests.swift @@ -7,6 +7,7 @@ import Combine import WalletConnectNetworking import WalletConnectPush @testable import WalletConnectPairing +import Auth final class PairingTests: XCTestCase { @@ -21,41 +22,62 @@ final class PairingTests: XCTestCase { private var publishers = [AnyCancellable]() - override func setUp() { - (appPairingClient, appPushClient) = makeClients(prefix: "🤖 App") - (walletPairingClient, walletPushClient) = makeClients(prefix: "🐶 Wallet") + func makeClients(prefix: String) -> (PairingClient, PushClient) { + let keychain = KeychainStorageMock() + let keyValueStorage = RuntimeKeyValueStorage(prefix: prefix) + + let relayLogger = ConsoleLogger(suffix: prefix + " [Relay]", loggingLevel: .debug) + let pairingLogger = ConsoleLogger(suffix: prefix + " [Pairing]", loggingLevel: .debug) + let pushLogger = ConsoleLogger(suffix: prefix + " [Push]", loggingLevel: .debug) + + let relayClient = RelayClient( + relayHost: InputConfig.relayHost, + projectId: InputConfig.projectId, + keyValueStorage: RuntimeKeyValueStorage(), + keychainStorage: keychain, + socketFactory: SocketFactory(), + logger: relayLogger) + + let pairingClient = PairingClientFactory.create(logger: pairingLogger, keyValueStorage: keyValueStorage, keychainStorage: keychain, relayClient: relayClient) + + let pushClient = PushClientFactory.create(logger: pushLogger, keyValueStorage: keyValueStorage, keychainStorage: keychain, relayClient: relayClient, pairingClient: pairingClient) + return (pairingClient, pushClient) } - func makeClients(prefix: String) -> (PairingClient, PushClient) { + func makePairingClient(prefix: String) -> PairingClient { let keychain = KeychainStorageMock() let logger = ConsoleLogger(suffix: prefix, loggingLevel: .debug) let relayClient = RelayClient(relayHost: InputConfig.relayHost, projectId: InputConfig.projectId, keychainStorage: keychain, socketFactory: SocketFactory(), logger: logger) let pairingClient = PairingClientFactory.create(logger: logger, keyValueStorage: RuntimeKeyValueStorage(), keychainStorage: keychain, relayClient: relayClient) - - let pushClient = PushClientFactory.create(logger: logger, keyValueStorage: RuntimeKeyValueStorage(), keychainStorage: keychain, relayClient: relayClient, pairingClient: pairingClient) - return (pairingClient, pushClient) - + return pairingClient } - func testProposePushOnPairing() async throws { - let exp = expectation(description: "testProposePushOnPairing") + func testProposePushOnPairing() async { + let exp = expectation(description: "testProposePushOnPairing") + + (appPairingClient, appPushClient) = makeClients(prefix: "🤖 App") + (walletPairingClient, walletPushClient) = makeClients(prefix: "🐶 Wallet") walletPushClient.proposalPublisher.sink { _ in exp.fulfill() }.store(in: &publishers) - let uri = try await appPairingClient.create() + let uri = try! await appPairingClient.create() - try await walletPairingClient.pair(uri: uri) + try! await walletPairingClient.pair(uri: uri) - try await appPushClient.propose(topic: uri.topic) + try! await appPushClient.propose(topic: uri.topic) wait(for: [exp], timeout: InputConfig.defaultTimeout) } func testPing() async { let pingExpectation = expectation(description: "expects ping response") + + (appPairingClient, appPushClient) = makeClients(prefix: "🤖 App") + (walletPairingClient, walletPushClient) = makeClients(prefix: "🐶 Wallet") + let uri = try! await appPairingClient.create() try! await walletPairingClient.pair(uri: uri) try! await walletPairingClient.ping(topic: uri.topic) @@ -63,9 +85,32 @@ final class PairingTests: XCTestCase { .sink { topic in XCTAssertEqual(topic, uri.topic) pingExpectation.fulfill() - } - .store(in: &publishers) + }.store(in: &publishers) wait(for: [pingExpectation], timeout: InputConfig.defaultTimeout) } + + func testResponseErrorForMethodUnregistered() async { + (appPairingClient, appPushClient) = makeClients(prefix: "🤖 App") + walletPairingClient = makePairingClient(prefix: "🐶 Wallet") + + let exp = expectation(description: "testProposePushOnPairing") + + appPushClient.responsePublisher.sink { (id, response) in + XCTAssertEqual(response, .failure(PairError(code: 4444)!)) + }.store(in: &publishers) + + let uri = try! await appPairingClient.create() + + try! await walletPairingClient.pair(uri: uri) + + try! await appPushClient.propose(topic: uri.topic) + + wait(for: [exp], timeout: InputConfig.defaultTimeout) + + } + + func testDisconnect() { + + } } diff --git a/Sources/WalletConnectPairing/PairingRequestsSubscriber.swift b/Sources/WalletConnectPairing/PairingRequestsSubscriber.swift index 94f87f0c1..b390002af 100644 --- a/Sources/WalletConnectPairing/PairingRequestsSubscriber.swift +++ b/Sources/WalletConnectPairing/PairingRequestsSubscriber.swift @@ -31,6 +31,7 @@ public class PairingRequestsSubscriber { publisherSubject.send(payload) }.store(in: &publishers) + return publisherSubject.eraseToAnyPublisher() } diff --git a/Sources/WalletConnectPairing/Types/PairError.swift b/Sources/WalletConnectPairing/Types/PairError.swift index 99e93f336..3ed4296d6 100644 --- a/Sources/WalletConnectPairing/Types/PairError.swift +++ b/Sources/WalletConnectPairing/Types/PairError.swift @@ -3,6 +3,15 @@ import WalletConnectNetworking public enum PairError: Codable, Equatable, Error, Reason { case methodUnsupported + public init?(code: Int) { + switch code { + case Self.methodUnsupported.code: + self = .methodUnsupported + default: + return nil + } + } + public var code: Int { //TODO - spec code return 44444 diff --git a/Sources/WalletConnectPush/ProposalResponseSubscriber.swift b/Sources/WalletConnectPush/ProposalResponseSubscriber.swift new file mode 100644 index 000000000..8a2e0b750 --- /dev/null +++ b/Sources/WalletConnectPush/ProposalResponseSubscriber.swift @@ -0,0 +1,33 @@ +import Foundation +import Combine +import JSONRPC +import WalletConnectUtils +import WalletConnectKMS +import WalletConnectNetworking +import WalletConnectPairing + +class ProposalResponseSubscriber { + private let networkingInteractor: NetworkInteracting + private let kms: KeyManagementServiceProtocol + private let logger: ConsoleLogging + private var publishers = [AnyCancellable]() + var onResponse: ((_ id: RPCID, _ result: Result) -> Void)? + + init(networkingInteractor: NetworkInteracting, + kms: KeyManagementServiceProtocol, + logger: ConsoleLogging) { + self.networkingInteractor = networkingInteractor + self.kms = kms + self.logger = logger + subscribeForProposalErrors() + } + + private func subscribeForProposalErrors() { + let protocolMethod = PushProposeProtocolMethod() + networkingInteractor.responseErrorSubscription(on: protocolMethod) + .sink { [unowned self] (payload: ResponseSubscriptionErrorPayload) in + guard let error = PairError(code: payload.error.code) else { return } + onResponse?(payload.id, .failure(error)) + }.store(in: &publishers) + } +} diff --git a/Sources/WalletConnectPush/PushClient.swift b/Sources/WalletConnectPush/PushClient.swift index e9ddc5473..4e4d170bd 100644 --- a/Sources/WalletConnectPush/PushClient.swift +++ b/Sources/WalletConnectPush/PushClient.swift @@ -10,39 +10,45 @@ public class PushClient { private var publishers = Set() - let requestPublisherSubject = PassthroughSubject<(topic: String, params: PushRequestParams), Never>() + private let requestPublisherSubject = PassthroughSubject<(topic: String, params: PushRequestParams), Never>() + private let responsePublisherSubject = PassthroughSubject<(id: RPCID, result: Result), Never>() public var proposalPublisher: AnyPublisher<(topic: String, params: PushRequestParams), Never> { requestPublisherSubject.eraseToAnyPublisher() } + public var responsePublisher: AnyPublisher<(id: RPCID, result: Result), Never> { + responsePublisherSubject.eraseToAnyPublisher() + } public let logger: ConsoleLogging private let pushProposer: PushProposer private let networkInteractor: NetworkInteracting private let pairingRegisterer: PairingRegisterer + private let proposalResponseSubscriber: ProposalResponseSubscriber init(networkInteractor: NetworkInteracting, logger: ConsoleLogging, kms: KeyManagementServiceProtocol, pushProposer: PushProposer, + proposalResponseSubscriber: ProposalResponseSubscriber, pairingRegisterer: PairingRegisterer) { self.networkInteractor = networkInteractor self.logger = logger self.pushProposer = pushProposer self.pairingRegisterer = pairingRegisterer - - setupPairingSubscriptions() + self.proposalResponseSubscriber = proposalResponseSubscriber + setupSubscriptions() } public func propose(topic: String) async throws { - try await pushProposer.request(topic: topic, params: AnyCodable(PushRequestParams())) + try await pushProposer.request(topic: topic, params: PushRequestParams()) } } private extension PushClient { - func setupPairingSubscriptions() { + func setupSubscriptions() { let protocolMethod = PushProposeProtocolMethod() pairingRegisterer.register(method: protocolMethod) @@ -50,10 +56,8 @@ private extension PushClient { requestPublisherSubject.send((topic: payload.topic, params: payload.request)) }.store(in: &publishers) - networkInteractor.responseErrorSubscription(on: protocolMethod) - .sink { [unowned self] (payload: ResponseSubscriptionErrorPayload) in - logger.error(payload.error.localizedDescription) - }.store(in: &publishers) - + proposalResponseSubscriber.onResponse = {[unowned self] (id, result) in + responsePublisherSubject.send((id, result)) + } } } diff --git a/Sources/WalletConnectPush/PushClientFactory.swift b/Sources/WalletConnectPush/PushClientFactory.swift index 5d6ce047c..de21a7561 100644 --- a/Sources/WalletConnectPush/PushClientFactory.swift +++ b/Sources/WalletConnectPush/PushClientFactory.swift @@ -13,12 +13,13 @@ public struct PushClientFactory { let history = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage) let networkingInteractor = NetworkingInteractor(relayClient: relayClient, serializer: serializer, logger: logger, rpcHistory: history) let pushProposer = PushProposer(networkingInteractor: networkingInteractor, kms: kms, logger: logger) - + let proposalResponseSubscriber = ProposalResponseSubscriber(networkingInteractor: networkingInteractor, kms: kms, logger: logger) + return PushClient( networkInteractor: networkingInteractor, logger: logger, kms: kms, - pushProposer: pushProposer, + pushProposer: pushProposer, proposalResponseSubscriber: proposalResponseSubscriber, pairingRegisterer: pairingClient ) } diff --git a/Sources/WalletConnectPush/PushProposeProtocolMethod.swift b/Sources/WalletConnectPush/PushProposeProtocolMethod.swift index 9ee2de7c8..60d5ee6bf 100644 --- a/Sources/WalletConnectPush/PushProposeProtocolMethod.swift +++ b/Sources/WalletConnectPush/PushProposeProtocolMethod.swift @@ -10,3 +10,5 @@ struct PushProposeProtocolMethod: ProtocolMethod { } public struct PushRequestParams: Codable {} + +public struct PushResponseParams: Codable, Equatable {} diff --git a/Sources/WalletConnectPush/PushProposer.swift b/Sources/WalletConnectPush/PushProposer.swift index 535f9eb34..fc34e54d0 100644 --- a/Sources/WalletConnectPush/PushProposer.swift +++ b/Sources/WalletConnectPush/PushProposer.swift @@ -6,7 +6,7 @@ import WalletConnectKMS import WalletConnectNetworking -public class PushProposer { +class PushProposer { private let networkingInteractor: NetworkInteracting private let kms: KeyManagementServiceProtocol private let logger: ConsoleLogging @@ -19,7 +19,8 @@ public class PushProposer { self.logger = logger } - func request(topic: String, params: AnyCodable) async throws { + func request(topic: String, params: PushRequestParams) async throws { + logger.debug("Sending Push Proposal") let protocolMethod = PushProposeProtocolMethod() let request = RPCRequest(method: protocolMethod.method, params: params) try await networkingInteractor.requestNetworkAck(request, topic: topic, protocolMethod: protocolMethod) diff --git a/Sources/WalletConnectUtils/SetStore.swift b/Sources/WalletConnectUtils/SetStore.swift index feba25809..c0cdb0fff 100644 --- a/Sources/WalletConnectUtils/SetStore.swift +++ b/Sources/WalletConnectUtils/SetStore.swift @@ -1,7 +1,7 @@ import Foundation -public class SetStore { +public class SetStore: CustomStringConvertible { private let concurrentQueue: DispatchQueue @@ -30,4 +30,8 @@ public class SetStore { } return contains } + + public var description: String { + return store.description + } } From 614d57a0f3ed6114babc56201b65b70f60536bd6 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 3 Oct 2022 14:53:05 +0200 Subject: [PATCH 124/175] add networking instance --- Example/IntegrationTests/Auth/AuthTests.swift | 9 ++- .../Pairing/PairingTests.swift | 23 +++++- .../Sign/SignClientTests.swift | 9 ++- .../WalletConnectNetworking/Networking.swift | 78 +++++++++++++++++++ Sources/WalletConnectPairing/Pair.swift | 4 +- .../PairingClientFactory.swift | 23 +++--- .../PairingRequestsSubscriber.swift | 1 + .../WalletConnectPush/PushClientFactory.swift | 11 +-- 8 files changed, 130 insertions(+), 28 deletions(-) create mode 100644 Sources/WalletConnectNetworking/Networking.swift diff --git a/Example/IntegrationTests/Auth/AuthTests.swift b/Example/IntegrationTests/Auth/AuthTests.swift index 1b1521fb6..1cb449b04 100644 --- a/Example/IntegrationTests/Auth/AuthTests.swift +++ b/Example/IntegrationTests/Auth/AuthTests.swift @@ -6,6 +6,7 @@ import WalletConnectRelay import Combine @testable import Auth import WalletConnectPairing +import WalletConnectNetworking final class AuthTests: XCTestCase { var appPairingClient: PairingClient! @@ -29,11 +30,17 @@ final class AuthTests: XCTestCase { let relayClient = RelayClient(relayHost: InputConfig.relayHost, projectId: InputConfig.projectId, keychainStorage: keychain, socketFactory: SocketFactory(), logger: logger) let keyValueStorage = RuntimeKeyValueStorage() + let networkingClient = NetworkingClientFactory.create( + relayClient: relayClient, + logger: logger, + keychainStorage: keychain, + keyValueStorage: keyValueStorage) + let pairingClient = PairingClientFactory.create( logger: logger, keyValueStorage: keyValueStorage, keychainStorage: keychain, - relayClient: relayClient) + networkingClient: networkingClient) let authClient = AuthClientFactory.create( metadata: AppMetadata(name: name, description: "", url: "", icons: [""]), diff --git a/Example/IntegrationTests/Pairing/PairingTests.swift b/Example/IntegrationTests/Pairing/PairingTests.swift index ddfcdc5be..d38256479 100644 --- a/Example/IntegrationTests/Pairing/PairingTests.swift +++ b/Example/IntegrationTests/Pairing/PairingTests.swift @@ -24,11 +24,13 @@ final class PairingTests: XCTestCase { func makeClients(prefix: String) -> (PairingClient, PushClient) { let keychain = KeychainStorageMock() - let keyValueStorage = RuntimeKeyValueStorage(prefix: prefix) + let keyValueStorage = RuntimeKeyValueStorage() let relayLogger = ConsoleLogger(suffix: prefix + " [Relay]", loggingLevel: .debug) let pairingLogger = ConsoleLogger(suffix: prefix + " [Pairing]", loggingLevel: .debug) let pushLogger = ConsoleLogger(suffix: prefix + " [Push]", loggingLevel: .debug) + let networkingLogger = ConsoleLogger(suffix: prefix + " [Networking]", loggingLevel: .debug) + let relayClient = RelayClient( relayHost: InputConfig.relayHost, @@ -38,9 +40,15 @@ final class PairingTests: XCTestCase { socketFactory: SocketFactory(), logger: relayLogger) - let pairingClient = PairingClientFactory.create(logger: pairingLogger, keyValueStorage: keyValueStorage, keychainStorage: keychain, relayClient: relayClient) + let networkingClient = NetworkingClientFactory.create( + relayClient: relayClient, + logger: networkingLogger, + keychainStorage: keychain, + keyValueStorage: keyValueStorage) + + let pairingClient = PairingClientFactory.create(logger: pairingLogger, keyValueStorage: keyValueStorage, keychainStorage: keychain, networkingClient: networkingClient) - let pushClient = PushClientFactory.create(logger: pushLogger, keyValueStorage: keyValueStorage, keychainStorage: keychain, relayClient: relayClient, pairingClient: pairingClient) + let pushClient = PushClientFactory.create(logger: pushLogger, keyValueStorage: keyValueStorage, keychainStorage: keychain, networkingClient: networkingClient, pairingClient: pairingClient) return (pairingClient, pushClient) } @@ -48,8 +56,15 @@ final class PairingTests: XCTestCase { let keychain = KeychainStorageMock() let logger = ConsoleLogger(suffix: prefix, loggingLevel: .debug) let relayClient = RelayClient(relayHost: InputConfig.relayHost, projectId: InputConfig.projectId, keychainStorage: keychain, socketFactory: SocketFactory(), logger: logger) + let keyValueStorage = RuntimeKeyValueStorage() + + let networkingClient = NetworkingClientFactory.create( + relayClient: relayClient, + logger: logger, + keychainStorage: keychain, + keyValueStorage: keyValueStorage) - let pairingClient = PairingClientFactory.create(logger: logger, keyValueStorage: RuntimeKeyValueStorage(), keychainStorage: keychain, relayClient: relayClient) + let pairingClient = PairingClientFactory.create(logger: logger, keyValueStorage: RuntimeKeyValueStorage(), keychainStorage: keychain, networkingClient: networkingClient) return pairingClient } diff --git a/Example/IntegrationTests/Sign/SignClientTests.swift b/Example/IntegrationTests/Sign/SignClientTests.swift index 14abbfe74..8d4f0abab 100644 --- a/Example/IntegrationTests/Sign/SignClientTests.swift +++ b/Example/IntegrationTests/Sign/SignClientTests.swift @@ -5,6 +5,7 @@ import JSONRPC @testable import WalletConnectSign @testable import WalletConnectRelay import WalletConnectPairing +import WalletConnectNetworking final class SignClientTests: XCTestCase { var dapp: ClientDelegate! @@ -24,11 +25,17 @@ final class SignClientTests: XCTestCase { ) let keyValueStorage = RuntimeKeyValueStorage() + let networkingClient = NetworkingClientFactory.create( + relayClient: relayClient, + logger: logger, + keychainStorage: keychain, + keyValueStorage: keyValueStorage) + let pairingClient = PairingClientFactory.create( logger: logger, keyValueStorage: keyValueStorage, keychainStorage: keychain, - relayClient: relayClient) + networkingClient: networkingClient) let client = SignClientFactory.create( metadata: AppMetadata(name: name, description: "", url: "", icons: [""]), diff --git a/Sources/WalletConnectNetworking/Networking.swift b/Sources/WalletConnectNetworking/Networking.swift new file mode 100644 index 000000000..07a336d67 --- /dev/null +++ b/Sources/WalletConnectNetworking/Networking.swift @@ -0,0 +1,78 @@ +import WalletConnectRelay +import WalletConnectUtils +import WalletConnectKMS +import Foundation + +public class Networking { + + /// Relay client instance + public static var instance: NetworkingInteractor = { + guard let config = Networking.config else { + fatalError("Error - you must call Networking.configure(_:) before accessing the shared instance.") + } + + return NetworkingClientFactory.create(relayClient: Relay.instance) + }() + + private static var config: Config? + + private init() { } + + /// Relay instance config method + /// - Parameters: + /// - relayHost: relay host + /// - projectId: project id + /// - socketFactory: web socket factory + /// - socketConnectionType: socket connection type + static public func configure( + relayHost: String = "relay.walletconnect.com", + projectId: String, + socketFactory: WebSocketFactory, + socketConnectionType: SocketConnectionType = .automatic + ) { + Networking.config = Networking.Config( + relayHost: relayHost, + projectId: projectId, + socketFactory: socketFactory, + socketConnectionType: socketConnectionType + ) + Networking.configure( + relayHost: relayHost, + projectId: projectId, + socketFactory: socketFactory, + socketConnectionType: socketConnectionType) + } +} + +extension Networking { + struct Config { + let relayHost: String + let projectId: String + let socketFactory: WebSocketFactory + let socketConnectionType: SocketConnectionType + } +} + +public struct NetworkingClientFactory { + + public static func create(relayClient: RelayClient) -> NetworkingInteractor { + let logger = ConsoleLogger(loggingLevel: .off) + let keyValueStorage = UserDefaults.standard + let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk") + return NetworkingClientFactory.create(relayClient: relayClient, logger: logger, keychainStorage: keychainStorage, keyValueStorage: keyValueStorage) + } + + public static func create(relayClient: RelayClient, logger: ConsoleLogging, keychainStorage: KeychainStorageProtocol, keyValueStorage: KeyValueStorage) -> NetworkingInteractor{ + let kms = KeyManagementService(keychain: keychainStorage) + + let serializer = Serializer(kms: kms) + + let rpcHistory = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage) + + return NetworkingInteractor( + relayClient: relayClient, + serializer: serializer, + logger: logger, + rpcHistory: rpcHistory) + } +} diff --git a/Sources/WalletConnectPairing/Pair.swift b/Sources/WalletConnectPairing/Pair.swift index 4ead303f0..ae1f223ad 100644 --- a/Sources/WalletConnectPairing/Pair.swift +++ b/Sources/WalletConnectPairing/Pair.swift @@ -1,5 +1,5 @@ import Foundation -import WalletConnectRelay +import WalletConnectNetworking import Combine public class Pair { @@ -9,7 +9,7 @@ public class Pair { guard let config = Pair.config else { fatalError("Error - you must call Pair.configure(_:) before accessing the shared instance.") } - return PairingClientFactory.create(relayClient: Relay.instance) + return PairingClientFactory.create(networkingClient: Networking.instance) }() public static var metadata: AppMetadata { diff --git a/Sources/WalletConnectPairing/PairingClientFactory.swift b/Sources/WalletConnectPairing/PairingClientFactory.swift index a74bd04f2..6f7c80dae 100644 --- a/Sources/WalletConnectPairing/PairingClientFactory.swift +++ b/Sources/WalletConnectPairing/PairingClientFactory.swift @@ -6,30 +6,27 @@ import WalletConnectNetworking public struct PairingClientFactory { - public static func create(relayClient: RelayClient) -> PairingClient { + public static func create(networkingClient: NetworkingInteractor) -> PairingClient { let logger = ConsoleLogger(loggingLevel: .off) let keyValueStorage = UserDefaults.standard let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk") - return PairingClientFactory.create(logger: logger, keyValueStorage: keyValueStorage, keychainStorage: keychainStorage, relayClient: relayClient) + return PairingClientFactory.create(logger: logger, keyValueStorage: keyValueStorage, keychainStorage: keychainStorage, networkingClient: networkingClient) } - public static func create(logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, relayClient: RelayClient) -> PairingClient { + public static func create(logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, networkingClient: NetworkingInteractor) -> PairingClient { let pairingStore = PairingStorage(storage: SequenceStore(store: .init(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.pairings.rawValue))) let kms = KeyManagementService(keychain: keychainStorage) - let serializer = Serializer(kms: kms) - let history = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage) - let networkingInteractor = NetworkingInteractor(relayClient: relayClient, serializer: serializer, logger: logger, rpcHistory: history) - let appPairService = AppPairService(networkingInteractor: networkingInteractor, kms: kms, pairingStorage: pairingStore) - let walletPairService = WalletPairService(networkingInteractor: networkingInteractor, kms: kms, pairingStorage: pairingStore) - let pairingRequestsSubscriber = PairingRequestsSubscriber(networkingInteractor: networkingInteractor, pairingStorage: pairingStore, logger: logger) + let appPairService = AppPairService(networkingInteractor: networkingClient, kms: kms, pairingStorage: pairingStore) + let walletPairService = WalletPairService(networkingInteractor: networkingClient, kms: kms, pairingStorage: pairingStore) + let pairingRequestsSubscriber = PairingRequestsSubscriber(networkingInteractor: networkingClient, pairingStorage: pairingStore, logger: logger) let pairingsProvider = PairingsProvider(pairingStorage: pairingStore) let cleanupService = CleanupService(pairingStore: pairingStore, kms: kms) - let deletePairingService = DeletePairingService(networkingInteractor: networkingInteractor, kms: kms, pairingStorage: pairingStore, logger: logger) - let pingService = PairingPingService(pairingStorage: pairingStore, networkingInteractor: networkingInteractor, logger: logger) + let deletePairingService = DeletePairingService(networkingInteractor: networkingClient, kms: kms, pairingStorage: pairingStore, logger: logger) + let pingService = PairingPingService(pairingStorage: pairingStore, networkingInteractor: networkingClient, logger: logger) return PairingClient( appPairService: appPairService, - networkingInteractor: networkingInteractor, + networkingInteractor: networkingClient, logger: logger, walletPairService: walletPairService, deletePairingService: deletePairingService, @@ -37,7 +34,7 @@ public struct PairingClientFactory { pairingStorage: pairingStore, cleanupService: cleanupService, pingService: pingService, - socketConnectionStatusPublisher: relayClient.socketConnectionStatusPublisher, + socketConnectionStatusPublisher: networkingClient.socketConnectionStatusPublisher, pairingsProvider: pairingsProvider ) } diff --git a/Sources/WalletConnectPairing/PairingRequestsSubscriber.swift b/Sources/WalletConnectPairing/PairingRequestsSubscriber.swift index b390002af..e9139f30a 100644 --- a/Sources/WalletConnectPairing/PairingRequestsSubscriber.swift +++ b/Sources/WalletConnectPairing/PairingRequestsSubscriber.swift @@ -38,6 +38,7 @@ public class PairingRequestsSubscriber { func handleUnregisteredRequests() { networkingInteractor.requestPublisher .filter { [unowned self] in !pairingProtocolMethods.contains($0.request.method)} + .filter { [unowned self] in pairingStorage.hasPairing(forTopic: $0.topic)} .filter { [unowned self] in !registeredProtocolMethods.contains($0.request.method)} .sink { [unowned self] topic, request in Task(priority: .high) { diff --git a/Sources/WalletConnectPush/PushClientFactory.swift b/Sources/WalletConnectPush/PushClientFactory.swift index de21a7561..6a1b79429 100644 --- a/Sources/WalletConnectPush/PushClientFactory.swift +++ b/Sources/WalletConnectPush/PushClientFactory.swift @@ -7,16 +7,13 @@ import WalletConnectPairing public struct PushClientFactory { - static public func create(logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, relayClient: RelayClient, pairingClient: PairingClient) -> PushClient { + static public func create(logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, networkingClient: NetworkingInteractor, pairingClient: PairingClient) -> PushClient { let kms = KeyManagementService(keychain: keychainStorage) - let serializer = Serializer(kms: kms) - let history = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage) - let networkingInteractor = NetworkingInteractor(relayClient: relayClient, serializer: serializer, logger: logger, rpcHistory: history) - let pushProposer = PushProposer(networkingInteractor: networkingInteractor, kms: kms, logger: logger) - let proposalResponseSubscriber = ProposalResponseSubscriber(networkingInteractor: networkingInteractor, kms: kms, logger: logger) + let pushProposer = PushProposer(networkingInteractor: networkingClient, kms: kms, logger: logger) + let proposalResponseSubscriber = ProposalResponseSubscriber(networkingInteractor: networkingClient, kms: kms, logger: logger) return PushClient( - networkInteractor: networkingInteractor, + networkInteractor: networkingClient, logger: logger, kms: kms, pushProposer: pushProposer, proposalResponseSubscriber: proposalResponseSubscriber, From 0e7542bcb4a71bb7c3103b83c30ebdb4f73d331e Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Mon, 3 Oct 2022 20:22:28 +0600 Subject: [PATCH 125/175] Activate or extend in WalletSubscriber --- Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift | 1 + Tests/AuthTests/AppRespondSubscriberTests.swift | 5 ++++- Tests/AuthTests/Mocks/PairingRegistererMock.swift | 7 +++++-- Tests/AuthTests/WalletRequestSubscriberTests.swift | 3 +-- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift index bd5c5c251..507479e3b 100644 --- a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift +++ b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift @@ -46,6 +46,7 @@ class WalletRequestSubscriber { } return } + pairingRegisterer.activate(pairingTopic: payload.topic) onRequest?(.init(id: payload.id, message: message)) }.store(in: &publishers) } diff --git a/Tests/AuthTests/AppRespondSubscriberTests.swift b/Tests/AuthTests/AppRespondSubscriberTests.swift index fa982be31..023ad627b 100644 --- a/Tests/AuthTests/AppRespondSubscriberTests.swift +++ b/Tests/AuthTests/AppRespondSubscriberTests.swift @@ -17,6 +17,7 @@ class AppRespondSubscriberTests: XCTestCase { let prvKey = Data(hex: "462c1dad6832d7d96ccf87bd6a686a4110e114aaaebd5512e552c0e3a87b480f") var messageSigner: MessageSigner! var pairingStorage: WCPairingStorageMock! + var pairingRegisterer: PairingRegistererMock! override func setUp() { networkingInteractor = NetworkingInteractorMock() @@ -24,12 +25,13 @@ class AppRespondSubscriberTests: XCTestCase { messageSigner = MessageSigner() rpcHistory = RPCHistoryFactory.createForNetwork(keyValueStorage: RuntimeKeyValueStorage()) pairingStorage = WCPairingStorageMock() + pairingRegisterer = PairingRegistererMock() sut = AppRespondSubscriber( networkingInteractor: networkingInteractor, logger: ConsoleLoggerMock(), rpcHistory: rpcHistory, signatureVerifier: messageSigner, - pairingRegisterer: PairingRegistererMock(), + pairingRegisterer: pairingRegisterer, messageFormatter: messageFormatter) } @@ -63,6 +65,7 @@ class AppRespondSubscriberTests: XCTestCase { networkingInteractor.responsePublisherSubject.send((topic, request, response)) wait(for: [messageExpectation], timeout: defaultTimeout) + XCTAssertTrue(pairingRegisterer.isActivateCalled) XCTAssertEqual(result, .failure(AuthError.messageCompromised)) XCTAssertEqual(messageId, requestId) } diff --git a/Tests/AuthTests/Mocks/PairingRegistererMock.swift b/Tests/AuthTests/Mocks/PairingRegistererMock.swift index be29b3921..94f4c817b 100644 --- a/Tests/AuthTests/Mocks/PairingRegistererMock.swift +++ b/Tests/AuthTests/Mocks/PairingRegistererMock.swift @@ -3,18 +3,21 @@ import WalletConnectPairing import Combine import WalletConnectNetworking -struct PairingRegistererMock: PairingRegisterer where RequestParams : Codable { +class PairingRegistererMock: PairingRegisterer where RequestParams : Codable { let subject = PassthroughSubject, Never>() + var isActivateCalled: Bool = false + func register(method: ProtocolMethod) -> AnyPublisher, Never> where RequestParams : Decodable, RequestParams : Encodable { subject.eraseToAnyPublisher() as! AnyPublisher, Never> } func activate(pairingTopic: String) { - + isActivateCalled = true } func validatePairingExistance(_ topic: String) throws { + } } diff --git a/Tests/AuthTests/WalletRequestSubscriberTests.swift b/Tests/AuthTests/WalletRequestSubscriberTests.swift index b6049c510..f7e953fd6 100644 --- a/Tests/AuthTests/WalletRequestSubscriberTests.swift +++ b/Tests/AuthTests/WalletRequestSubscriberTests.swift @@ -41,13 +41,12 @@ class WalletRequestSubscriberTests: XCTestCase { messageExpectation.fulfill() } - - let payload = RequestSubscriptionPayload(id: expectedRequestId, topic: "123", request: AuthRequestParams.stub(id: expectedRequestId)) pairingRegisterer.subject.send(payload) wait(for: [messageExpectation], timeout: defaultTimeout) + XCTAssertTrue(pairingRegisterer.isActivateCalled) XCTAssertEqual(message, expectedMessage) XCTAssertEqual(messageId, expectedRequestId) } From f0df8a8b471fcf598bc73315c14e500b3ccd261d Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 4 Oct 2022 08:56:22 +0200 Subject: [PATCH 126/175] pass unsupported method test --- Example/IntegrationTests/Pairing/PairingTests.swift | 3 ++- Sources/WalletConnectPairing/Types/PairError.swift | 7 ++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Example/IntegrationTests/Pairing/PairingTests.swift b/Example/IntegrationTests/Pairing/PairingTests.swift index d38256479..e0e87e10e 100644 --- a/Example/IntegrationTests/Pairing/PairingTests.swift +++ b/Example/IntegrationTests/Pairing/PairingTests.swift @@ -111,7 +111,8 @@ final class PairingTests: XCTestCase { let exp = expectation(description: "testProposePushOnPairing") appPushClient.responsePublisher.sink { (id, response) in - XCTAssertEqual(response, .failure(PairError(code: 4444)!)) + XCTAssertEqual(response, .failure(WalletConnectPairing.PairError(code: 0)!)) + exp.fulfill() }.store(in: &publishers) let uri = try! await appPairingClient.create() diff --git a/Sources/WalletConnectPairing/Types/PairError.swift b/Sources/WalletConnectPairing/Types/PairError.swift index 3ed4296d6..4452bc907 100644 --- a/Sources/WalletConnectPairing/Types/PairError.swift +++ b/Sources/WalletConnectPairing/Types/PairError.swift @@ -13,11 +13,12 @@ public enum PairError: Codable, Equatable, Error, Reason { } public var code: Int { - //TODO - spec code - return 44444 + switch self { + case .methodUnsupported: + return 0 + } } - //TODO - spec message public var message: String { return "Method Unsupported" } From 996989f280f162b5c3230a19905369deb4519bb0 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 4 Oct 2022 09:02:46 +0200 Subject: [PATCH 127/175] refactor networking client --- .../WalletConnectNetworking/Networking.swift | 38 ++----------------- .../NetworkingClientFactory.swift | 28 ++++++++++++++ .../NetworkingConfig.swift | 11 ++++++ 3 files changed, 42 insertions(+), 35 deletions(-) create mode 100644 Sources/WalletConnectNetworking/NetworkingClientFactory.swift create mode 100644 Sources/WalletConnectNetworking/NetworkingConfig.swift diff --git a/Sources/WalletConnectNetworking/Networking.swift b/Sources/WalletConnectNetworking/Networking.swift index 07a336d67..95ea937a2 100644 --- a/Sources/WalletConnectNetworking/Networking.swift +++ b/Sources/WalletConnectNetworking/Networking.swift @@ -5,7 +5,7 @@ import Foundation public class Networking { - /// Relay client instance + /// Networking client instance public static var instance: NetworkingInteractor = { guard let config = Networking.config else { fatalError("Error - you must call Networking.configure(_:) before accessing the shared instance.") @@ -18,7 +18,7 @@ public class Networking { private init() { } - /// Relay instance config method + /// Networking instance config method /// - Parameters: /// - relayHost: relay host /// - projectId: project id @@ -36,7 +36,7 @@ public class Networking { socketFactory: socketFactory, socketConnectionType: socketConnectionType ) - Networking.configure( + Relay.configure( relayHost: relayHost, projectId: projectId, socketFactory: socketFactory, @@ -44,35 +44,3 @@ public class Networking { } } -extension Networking { - struct Config { - let relayHost: String - let projectId: String - let socketFactory: WebSocketFactory - let socketConnectionType: SocketConnectionType - } -} - -public struct NetworkingClientFactory { - - public static func create(relayClient: RelayClient) -> NetworkingInteractor { - let logger = ConsoleLogger(loggingLevel: .off) - let keyValueStorage = UserDefaults.standard - let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk") - return NetworkingClientFactory.create(relayClient: relayClient, logger: logger, keychainStorage: keychainStorage, keyValueStorage: keyValueStorage) - } - - public static func create(relayClient: RelayClient, logger: ConsoleLogging, keychainStorage: KeychainStorageProtocol, keyValueStorage: KeyValueStorage) -> NetworkingInteractor{ - let kms = KeyManagementService(keychain: keychainStorage) - - let serializer = Serializer(kms: kms) - - let rpcHistory = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage) - - return NetworkingInteractor( - relayClient: relayClient, - serializer: serializer, - logger: logger, - rpcHistory: rpcHistory) - } -} diff --git a/Sources/WalletConnectNetworking/NetworkingClientFactory.swift b/Sources/WalletConnectNetworking/NetworkingClientFactory.swift new file mode 100644 index 000000000..36df0f6fc --- /dev/null +++ b/Sources/WalletConnectNetworking/NetworkingClientFactory.swift @@ -0,0 +1,28 @@ +import Foundation +import WalletConnectKMS +import WalletConnectRelay +import WalletConnectUtils + +public struct NetworkingClientFactory { + + public static func create(relayClient: RelayClient) -> NetworkingInteractor { + let logger = ConsoleLogger(loggingLevel: .off) + let keyValueStorage = UserDefaults.standard + let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk") + return NetworkingClientFactory.create(relayClient: relayClient, logger: logger, keychainStorage: keychainStorage, keyValueStorage: keyValueStorage) + } + + public static func create(relayClient: RelayClient, logger: ConsoleLogging, keychainStorage: KeychainStorageProtocol, keyValueStorage: KeyValueStorage) -> NetworkingInteractor{ + let kms = KeyManagementService(keychain: keychainStorage) + + let serializer = Serializer(kms: kms) + + let rpcHistory = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage) + + return NetworkingInteractor( + relayClient: relayClient, + serializer: serializer, + logger: logger, + rpcHistory: rpcHistory) + } +} diff --git a/Sources/WalletConnectNetworking/NetworkingConfig.swift b/Sources/WalletConnectNetworking/NetworkingConfig.swift new file mode 100644 index 000000000..c569d813b --- /dev/null +++ b/Sources/WalletConnectNetworking/NetworkingConfig.swift @@ -0,0 +1,11 @@ +import Foundation +import WalletConnectRelay + +extension Networking { + struct Config { + let relayHost: String + let projectId: String + let socketFactory: WebSocketFactory + let socketConnectionType: SocketConnectionType + } +} From 6460163eec5b2301dc61a17346e7e0b05b4e9556 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 4 Oct 2022 09:08:23 +0200 Subject: [PATCH 128/175] update networking configuration in sample apps --- Example/DApp/SceneDelegate.swift | 3 ++- Example/ExampleApp/SceneDelegate.swift | 3 +-- .../Configurator/ThirdPartyConfigurator.swift | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Example/DApp/SceneDelegate.swift b/Example/DApp/SceneDelegate.swift index 25c130617..afeca0dce 100644 --- a/Example/DApp/SceneDelegate.swift +++ b/Example/DApp/SceneDelegate.swift @@ -1,6 +1,7 @@ import UIKit import Starscream import WalletConnectRelay +import WalletConnectNetworking extension WebSocket: WebSocketConnecting { } @@ -18,7 +19,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { private let authCoordinator = AuthCoordinator() func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { - Relay.configure(projectId: InputConfig.projectId, socketFactory: SocketFactory()) + Networking.configure(projectId: InputConfig.projectId, socketFactory: SocketFactory()) setupWindow(scene: scene) } diff --git a/Example/ExampleApp/SceneDelegate.swift b/Example/ExampleApp/SceneDelegate.swift index b32a34de4..d993b8311 100644 --- a/Example/ExampleApp/SceneDelegate.swift +++ b/Example/ExampleApp/SceneDelegate.swift @@ -2,7 +2,6 @@ import UIKit import Foundation import Combine import WalletConnectSign -import WalletConnectRelay import Starscream extension WebSocket: WebSocketConnecting { } @@ -25,7 +24,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { url: "example.wallet", icons: ["https://avatars.githubusercontent.com/u/37784886"]) - Relay.configure(projectId: InputConfig.projectId, socketFactory: SocketFactory()) + Networking.configure(projectId: InputConfig.projectId, socketFactory: SocketFactory()) Sign.configure(metadata: metadata) #if DEBUG if CommandLine.arguments.contains("-cleanInstall") { diff --git a/Example/Showcase/Classes/ApplicationLayer/Configurator/ThirdPartyConfigurator.swift b/Example/Showcase/Classes/ApplicationLayer/Configurator/ThirdPartyConfigurator.swift index 231571f61..a8936517a 100644 --- a/Example/Showcase/Classes/ApplicationLayer/Configurator/ThirdPartyConfigurator.swift +++ b/Example/Showcase/Classes/ApplicationLayer/Configurator/ThirdPartyConfigurator.swift @@ -1,11 +1,11 @@ -import WalletConnectRelay +import WalletConnectNetworking import WalletConnectPairing import Auth struct ThirdPartyConfigurator: Configurator { func configure() { - Relay.configure(projectId: InputConfig.projectId, socketFactory: SocketFactory()) + Networking.configure(projectId: InputConfig.projectId, socketFactory: SocketFactory()) Pair.configure( metadata: AppMetadata( name: "Showcase App", From 9781c87a1c82c6bff2dbcabcfcef53c3e0766bfc Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 4 Oct 2022 09:14:55 +0200 Subject: [PATCH 129/175] integrate networking into auth --- Example/IntegrationTests/Auth/AuthTests.swift | 2 +- Sources/Auth/Auth.swift | 4 ++-- Sources/Auth/AuthClientFactory.swift | 20 +++++++++---------- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/Example/IntegrationTests/Auth/AuthTests.swift b/Example/IntegrationTests/Auth/AuthTests.swift index 1cb449b04..627ea19fa 100644 --- a/Example/IntegrationTests/Auth/AuthTests.swift +++ b/Example/IntegrationTests/Auth/AuthTests.swift @@ -48,7 +48,7 @@ final class AuthTests: XCTestCase { logger: logger, keyValueStorage: keyValueStorage, keychainStorage: keychain, - relayClient: relayClient, + networkingClient: networkingClient, pairingRegisterer: pairingClient) return (pairingClient, authClient) diff --git a/Sources/Auth/Auth.swift b/Sources/Auth/Auth.swift index b85b20847..8a94d74b1 100644 --- a/Sources/Auth/Auth.swift +++ b/Sources/Auth/Auth.swift @@ -1,5 +1,5 @@ import Foundation -import WalletConnectRelay +import WalletConnectNetworking import WalletConnectPairing import Combine @@ -24,7 +24,7 @@ public class Auth { return AuthClientFactory.create( metadata: config.metadata, account: config.account, - relayClient: Relay.instance, + networkingClient: Networking.instance, pairingRegisterer: Pair.instance) }() diff --git a/Sources/Auth/AuthClientFactory.swift b/Sources/Auth/AuthClientFactory.swift index 34c27661e..378420ed2 100644 --- a/Sources/Auth/AuthClientFactory.swift +++ b/Sources/Auth/AuthClientFactory.swift @@ -7,25 +7,23 @@ import WalletConnectNetworking public struct AuthClientFactory { - public static func create(metadata: AppMetadata, account: Account?, relayClient: RelayClient, pairingRegisterer: PairingRegisterer) -> AuthClient { + public static func create(metadata: AppMetadata, account: Account?, networkingClient: NetworkingInteractor, pairingRegisterer: PairingRegisterer) -> AuthClient { let logger = ConsoleLogger(loggingLevel: .off) let keyValueStorage = UserDefaults.standard let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk") - return AuthClientFactory.create(metadata: metadata, account: account, logger: logger, keyValueStorage: keyValueStorage, keychainStorage: keychainStorage, relayClient: relayClient, pairingRegisterer: pairingRegisterer) + return AuthClientFactory.create(metadata: metadata, account: account, logger: logger, keyValueStorage: keyValueStorage, keychainStorage: keychainStorage, networkingClient: networkingClient, pairingRegisterer: pairingRegisterer) } - static func create(metadata: AppMetadata, account: Account?, logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, relayClient: RelayClient, pairingRegisterer: PairingRegisterer) -> AuthClient { + static func create(metadata: AppMetadata, account: Account?, logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, networkingClient: NetworkingInteractor, pairingRegisterer: PairingRegisterer) -> AuthClient { let kms = KeyManagementService(keychain: keychainStorage) - let serializer = Serializer(kms: kms) let history = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage) - let networkingInteractor = NetworkingInteractor(relayClient: relayClient, serializer: serializer, logger: logger, rpcHistory: history) let messageFormatter = SIWEMessageFormatter() - let appRequestService = AppRequestService(networkingInteractor: networkingInteractor, kms: kms, appMetadata: metadata, logger: logger) + let appRequestService = AppRequestService(networkingInteractor: networkingClient, kms: kms, appMetadata: metadata, logger: logger) let messageSigner = MessageSigner(signer: Signer()) - let appRespondSubscriber = AppRespondSubscriber(networkingInteractor: networkingInteractor, logger: logger, rpcHistory: history, signatureVerifier: messageSigner, messageFormatter: messageFormatter) - let walletErrorResponder = WalletErrorResponder(networkingInteractor: networkingInteractor, logger: logger, kms: kms, rpcHistory: history) - let walletRequestSubscriber = WalletRequestSubscriber(networkingInteractor: networkingInteractor, logger: logger, kms: kms, messageFormatter: messageFormatter, address: account?.address, walletErrorResponder: walletErrorResponder, pairingRegisterer: pairingRegisterer) - let walletRespondService = WalletRespondService(networkingInteractor: networkingInteractor, logger: logger, kms: kms, rpcHistory: history, walletErrorResponder: walletErrorResponder) + let appRespondSubscriber = AppRespondSubscriber(networkingInteractor: networkingClient, logger: logger, rpcHistory: history, signatureVerifier: messageSigner, messageFormatter: messageFormatter) + let walletErrorResponder = WalletErrorResponder(networkingInteractor: networkingClient, logger: logger, kms: kms, rpcHistory: history) + let walletRequestSubscriber = WalletRequestSubscriber(networkingInteractor: networkingClient, logger: logger, kms: kms, messageFormatter: messageFormatter, address: account?.address, walletErrorResponder: walletErrorResponder, pairingRegisterer: pairingRegisterer) + let walletRespondService = WalletRespondService(networkingInteractor: networkingClient, logger: logger, kms: kms, rpcHistory: history, walletErrorResponder: walletErrorResponder) let pendingRequestsProvider = PendingRequestsProvider(rpcHistory: history) return AuthClient(appRequestService: appRequestService, @@ -35,7 +33,7 @@ public struct AuthClientFactory { account: account, pendingRequestsProvider: pendingRequestsProvider, logger: logger, - socketConnectionStatusPublisher: relayClient.socketConnectionStatusPublisher, + socketConnectionStatusPublisher: networkingClient.socketConnectionStatusPublisher, pairingRegisterer: pairingRegisterer) } } From c7397d10fd08c46c504dc6e46d449b7e6004aa11 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 4 Oct 2022 09:22:24 +0200 Subject: [PATCH 130/175] update pairing tests --- .../Pairing/PairingTests.swift | 52 +++++++++++++------ 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/Example/IntegrationTests/Pairing/PairingTests.swift b/Example/IntegrationTests/Pairing/PairingTests.swift index e0e87e10e..472d0fb04 100644 --- a/Example/IntegrationTests/Pairing/PairingTests.swift +++ b/Example/IntegrationTests/Pairing/PairingTests.swift @@ -7,8 +7,6 @@ import Combine import WalletConnectNetworking import WalletConnectPush @testable import WalletConnectPairing -import Auth - final class PairingTests: XCTestCase { @@ -31,7 +29,6 @@ final class PairingTests: XCTestCase { let pushLogger = ConsoleLogger(suffix: prefix + " [Push]", loggingLevel: .debug) let networkingLogger = ConsoleLogger(suffix: prefix + " [Networking]", loggingLevel: .debug) - let relayClient = RelayClient( relayHost: InputConfig.relayHost, projectId: InputConfig.projectId, @@ -46,36 +43,57 @@ final class PairingTests: XCTestCase { keychainStorage: keychain, keyValueStorage: keyValueStorage) - let pairingClient = PairingClientFactory.create(logger: pairingLogger, keyValueStorage: keyValueStorage, keychainStorage: keychain, networkingClient: networkingClient) + let pairingClient = PairingClientFactory.create( + logger: pairingLogger, + keyValueStorage: keyValueStorage, + keychainStorage: keychain, + networkingClient: networkingClient) + + let pushClient = PushClientFactory.create( + logger: pushLogger, + keyValueStorage: keyValueStorage, + keychainStorage: keychain, + networkingClient: networkingClient, + pairingClient: pairingClient) - let pushClient = PushClientFactory.create(logger: pushLogger, keyValueStorage: keyValueStorage, keychainStorage: keychain, networkingClient: networkingClient, pairingClient: pairingClient) return (pairingClient, pushClient) } func makePairingClient(prefix: String) -> PairingClient { let keychain = KeychainStorageMock() let logger = ConsoleLogger(suffix: prefix, loggingLevel: .debug) - let relayClient = RelayClient(relayHost: InputConfig.relayHost, projectId: InputConfig.projectId, keychainStorage: keychain, socketFactory: SocketFactory(), logger: logger) let keyValueStorage = RuntimeKeyValueStorage() + let relayClient = RelayClient( + relayHost: InputConfig.relayHost, + projectId: InputConfig.projectId, + keychainStorage: keychain, + socketFactory: SocketFactory(), + logger: logger) + let networkingClient = NetworkingClientFactory.create( relayClient: relayClient, logger: logger, keychainStorage: keychain, keyValueStorage: keyValueStorage) - let pairingClient = PairingClientFactory.create(logger: logger, keyValueStorage: RuntimeKeyValueStorage(), keychainStorage: keychain, networkingClient: networkingClient) + let pairingClient = PairingClientFactory.create( + logger: logger, + keyValueStorage: keyValueStorage, + keychainStorage: keychain, + networkingClient: networkingClient) + return pairingClient } func testProposePushOnPairing() async { - let exp = expectation(description: "testProposePushOnPairing") + let expectation = expectation(description: "propose push on pairing") (appPairingClient, appPushClient) = makeClients(prefix: "🤖 App") (walletPairingClient, walletPushClient) = makeClients(prefix: "🐶 Wallet") walletPushClient.proposalPublisher.sink { _ in - exp.fulfill() + expectation.fulfill() }.store(in: &publishers) let uri = try! await appPairingClient.create() @@ -84,11 +102,11 @@ final class PairingTests: XCTestCase { try! await appPushClient.propose(topic: uri.topic) - wait(for: [exp], timeout: InputConfig.defaultTimeout) + wait(for: [expectation], timeout: InputConfig.defaultTimeout) } func testPing() async { - let pingExpectation = expectation(description: "expects ping response") + let expectation = expectation(description: "expects ping response") (appPairingClient, appPushClient) = makeClients(prefix: "🤖 App") (walletPairingClient, walletPushClient) = makeClients(prefix: "🐶 Wallet") @@ -99,20 +117,20 @@ final class PairingTests: XCTestCase { walletPairingClient.pingResponsePublisher .sink { topic in XCTAssertEqual(topic, uri.topic) - pingExpectation.fulfill() + expectation.fulfill() }.store(in: &publishers) - wait(for: [pingExpectation], timeout: InputConfig.defaultTimeout) + wait(for: [expectation], timeout: InputConfig.defaultTimeout) } func testResponseErrorForMethodUnregistered() async { (appPairingClient, appPushClient) = makeClients(prefix: "🤖 App") walletPairingClient = makePairingClient(prefix: "🐶 Wallet") - let exp = expectation(description: "testProposePushOnPairing") + let expectation = expectation(description: "wallet responds unsupported method for unregistered method") appPushClient.responsePublisher.sink { (id, response) in XCTAssertEqual(response, .failure(WalletConnectPairing.PairError(code: 0)!)) - exp.fulfill() + expectation.fulfill() }.store(in: &publishers) let uri = try! await appPairingClient.create() @@ -121,12 +139,12 @@ final class PairingTests: XCTestCase { try! await appPushClient.propose(topic: uri.topic) - wait(for: [exp], timeout: InputConfig.defaultTimeout) + wait(for: [expectation], timeout: InputConfig.defaultTimeout) } func testDisconnect() { - + //TODO } } From 715269249da5d3a0d3f5878d6f9bdd62ab7ee4d9 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 4 Oct 2022 09:49:21 +0200 Subject: [PATCH 131/175] rename networking interactor --- Sources/Auth/AuthClientFactory.swift | 4 ++-- Sources/Chat/ChatClientFactory.swift | 2 +- Sources/WalletConnectNetworking/NetworkInteractor.swift | 2 +- Sources/WalletConnectNetworking/Networking.swift | 2 +- .../WalletConnectNetworking/NetworkingClientFactory.swift | 6 +++--- Sources/WalletConnectPairing/PairingClientFactory.swift | 4 ++-- Sources/WalletConnectPush/PushClientFactory.swift | 2 +- Sources/WalletConnectSign/Sign/SignClientFactory.swift | 2 +- 8 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Sources/Auth/AuthClientFactory.swift b/Sources/Auth/AuthClientFactory.swift index 378420ed2..afad7adcf 100644 --- a/Sources/Auth/AuthClientFactory.swift +++ b/Sources/Auth/AuthClientFactory.swift @@ -7,14 +7,14 @@ import WalletConnectNetworking public struct AuthClientFactory { - public static func create(metadata: AppMetadata, account: Account?, networkingClient: NetworkingInteractor, pairingRegisterer: PairingRegisterer) -> AuthClient { + public static func create(metadata: AppMetadata, account: Account?, networkingClient: NetworkingClient, pairingRegisterer: PairingRegisterer) -> AuthClient { let logger = ConsoleLogger(loggingLevel: .off) let keyValueStorage = UserDefaults.standard let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk") return AuthClientFactory.create(metadata: metadata, account: account, logger: logger, keyValueStorage: keyValueStorage, keychainStorage: keychainStorage, networkingClient: networkingClient, pairingRegisterer: pairingRegisterer) } - static func create(metadata: AppMetadata, account: Account?, logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, networkingClient: NetworkingInteractor, pairingRegisterer: PairingRegisterer) -> AuthClient { + static func create(metadata: AppMetadata, account: Account?, logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, networkingClient: NetworkingClient, pairingRegisterer: PairingRegisterer) -> AuthClient { let kms = KeyManagementService(keychain: keychainStorage) let history = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage) let messageFormatter = SIWEMessageFormatter() diff --git a/Sources/Chat/ChatClientFactory.swift b/Sources/Chat/ChatClientFactory.swift index b5af0d7b2..10848a6d7 100644 --- a/Sources/Chat/ChatClientFactory.swift +++ b/Sources/Chat/ChatClientFactory.swift @@ -16,7 +16,7 @@ public struct ChatClientFactory { let topicToRegistryRecordStore = CodableStore(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.topicToInvitationPubKey.rawValue) let serialiser = Serializer(kms: kms) let rpcHistory = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage) - let networkingInteractor = NetworkingInteractor(relayClient: relayClient, serializer: serialiser, logger: logger, rpcHistory: rpcHistory) + let networkingInteractor = NetworkingClient(relayClient: relayClient, serializer: serialiser, logger: logger, rpcHistory: rpcHistory) let invitePayloadStore = CodableStore>(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.invite.rawValue) let registryService = RegistryService(registry: registry, networkingInteractor: networkingInteractor, kms: kms, logger: logger, topicToRegistryRecordStore: topicToRegistryRecordStore) let threadStore = Database(keyValueStorage: keyValueStorage, identifier: StorageDomainIdentifiers.threads.rawValue) diff --git a/Sources/WalletConnectNetworking/NetworkInteractor.swift b/Sources/WalletConnectNetworking/NetworkInteractor.swift index 24445ba4c..b51b6aaed 100644 --- a/Sources/WalletConnectNetworking/NetworkInteractor.swift +++ b/Sources/WalletConnectNetworking/NetworkInteractor.swift @@ -5,7 +5,7 @@ import WalletConnectRelay import WalletConnectUtils import WalletConnectKMS -public class NetworkingInteractor: NetworkInteracting { +public class NetworkingClient: NetworkInteracting { private var publishers = Set() private let relayClient: RelayClient private let serializer: Serializing diff --git a/Sources/WalletConnectNetworking/Networking.swift b/Sources/WalletConnectNetworking/Networking.swift index 95ea937a2..cc8c5f063 100644 --- a/Sources/WalletConnectNetworking/Networking.swift +++ b/Sources/WalletConnectNetworking/Networking.swift @@ -6,7 +6,7 @@ import Foundation public class Networking { /// Networking client instance - public static var instance: NetworkingInteractor = { + public static var instance: NetworkingClient = { guard let config = Networking.config else { fatalError("Error - you must call Networking.configure(_:) before accessing the shared instance.") } diff --git a/Sources/WalletConnectNetworking/NetworkingClientFactory.swift b/Sources/WalletConnectNetworking/NetworkingClientFactory.swift index 36df0f6fc..4466b2849 100644 --- a/Sources/WalletConnectNetworking/NetworkingClientFactory.swift +++ b/Sources/WalletConnectNetworking/NetworkingClientFactory.swift @@ -5,21 +5,21 @@ import WalletConnectUtils public struct NetworkingClientFactory { - public static func create(relayClient: RelayClient) -> NetworkingInteractor { + public static func create(relayClient: RelayClient) -> NetworkingClient { let logger = ConsoleLogger(loggingLevel: .off) let keyValueStorage = UserDefaults.standard let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk") return NetworkingClientFactory.create(relayClient: relayClient, logger: logger, keychainStorage: keychainStorage, keyValueStorage: keyValueStorage) } - public static func create(relayClient: RelayClient, logger: ConsoleLogging, keychainStorage: KeychainStorageProtocol, keyValueStorage: KeyValueStorage) -> NetworkingInteractor{ + public static func create(relayClient: RelayClient, logger: ConsoleLogging, keychainStorage: KeychainStorageProtocol, keyValueStorage: KeyValueStorage) -> NetworkingClient{ let kms = KeyManagementService(keychain: keychainStorage) let serializer = Serializer(kms: kms) let rpcHistory = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage) - return NetworkingInteractor( + return NetworkingClient( relayClient: relayClient, serializer: serializer, logger: logger, diff --git a/Sources/WalletConnectPairing/PairingClientFactory.swift b/Sources/WalletConnectPairing/PairingClientFactory.swift index 6f7c80dae..1c7ab9986 100644 --- a/Sources/WalletConnectPairing/PairingClientFactory.swift +++ b/Sources/WalletConnectPairing/PairingClientFactory.swift @@ -6,14 +6,14 @@ import WalletConnectNetworking public struct PairingClientFactory { - public static func create(networkingClient: NetworkingInteractor) -> PairingClient { + public static func create(networkingClient: NetworkingClient) -> PairingClient { let logger = ConsoleLogger(loggingLevel: .off) let keyValueStorage = UserDefaults.standard let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk") return PairingClientFactory.create(logger: logger, keyValueStorage: keyValueStorage, keychainStorage: keychainStorage, networkingClient: networkingClient) } - public static func create(logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, networkingClient: NetworkingInteractor) -> PairingClient { + public static func create(logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, networkingClient: NetworkingClient) -> PairingClient { let pairingStore = PairingStorage(storage: SequenceStore(store: .init(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.pairings.rawValue))) let kms = KeyManagementService(keychain: keychainStorage) let appPairService = AppPairService(networkingInteractor: networkingClient, kms: kms, pairingStorage: pairingStore) diff --git a/Sources/WalletConnectPush/PushClientFactory.swift b/Sources/WalletConnectPush/PushClientFactory.swift index 6a1b79429..e4b3dde6a 100644 --- a/Sources/WalletConnectPush/PushClientFactory.swift +++ b/Sources/WalletConnectPush/PushClientFactory.swift @@ -7,7 +7,7 @@ import WalletConnectPairing public struct PushClientFactory { - static public func create(logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, networkingClient: NetworkingInteractor, pairingClient: PairingClient) -> PushClient { + static public func create(logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, networkingClient: NetworkingClient, pairingClient: PairingClient) -> PushClient { let kms = KeyManagementService(keychain: keychainStorage) let pushProposer = PushProposer(networkingInteractor: networkingClient, kms: kms, logger: logger) let proposalResponseSubscriber = ProposalResponseSubscriber(networkingInteractor: networkingClient, kms: kms, logger: logger) diff --git a/Sources/WalletConnectSign/Sign/SignClientFactory.swift b/Sources/WalletConnectSign/Sign/SignClientFactory.swift index ec376df34..8fc6e151a 100644 --- a/Sources/WalletConnectSign/Sign/SignClientFactory.swift +++ b/Sources/WalletConnectSign/Sign/SignClientFactory.swift @@ -27,7 +27,7 @@ public struct SignClientFactory { let kms = KeyManagementService(keychain: keychainStorage) let serializer = Serializer(kms: kms) let rpcHistory = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage) - let networkingInteractor = NetworkingInteractor(relayClient: relayClient, serializer: serializer, logger: logger, rpcHistory: rpcHistory) + let networkingInteractor = NetworkingClient(relayClient: relayClient, serializer: serializer, logger: logger, rpcHistory: rpcHistory) let pairingStore = PairingStorage(storage: SequenceStore(store: .init(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.pairings.rawValue))) let sessionStore = SessionStorage(storage: SequenceStore(store: .init(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.sessions.rawValue))) let sessionToPairingTopic = CodableStore(defaults: RuntimeKeyValueStorage(), identifier: StorageDomainIdentifiers.sessionToPairingTopic.rawValue) From 60b6b3dd33c80e86e9d73e827c2b8c47e4e74bcf Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 4 Oct 2022 10:04:26 +0200 Subject: [PATCH 132/175] fix build --- Example/ExampleApp/SceneDelegate.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Example/ExampleApp/SceneDelegate.swift b/Example/ExampleApp/SceneDelegate.swift index d993b8311..03a7c8580 100644 --- a/Example/ExampleApp/SceneDelegate.swift +++ b/Example/ExampleApp/SceneDelegate.swift @@ -2,6 +2,8 @@ import UIKit import Foundation import Combine import WalletConnectSign +import WalletConnectNetworking +import WalletConnectRelay import Starscream extension WebSocket: WebSocketConnecting { } From 30e6e898c0d30137dfc9da487321921d059372bc Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Wed, 5 Oct 2022 00:51:41 +0600 Subject: [PATCH 133/175] tvOS operatingSystem --- Sources/WalletConnectRelay/EnvironmentInfo.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Sources/WalletConnectRelay/EnvironmentInfo.swift b/Sources/WalletConnectRelay/EnvironmentInfo.swift index e00872260..7dab63f3b 100644 --- a/Sources/WalletConnectRelay/EnvironmentInfo.swift +++ b/Sources/WalletConnectRelay/EnvironmentInfo.swift @@ -25,8 +25,9 @@ enum EnvironmentInfo { #if os(iOS) return "\(UIDevice.current.systemName)-\(UIDevice.current.systemVersion)" #elseif os(macOS) - let systemVersion = ProcessInfo.processInfo.operatingSystemVersion - return "macOS-\(systemVersion)" + return "macOS-\(ProcessInfo.processInfo.operatingSystemVersion)" +#elseif os(tvOS) + return "tvOS-\(ProcessInfo.processInfo.operatingSystemVersion)" #endif } } From 68162303c2534b6a1b4279c2046453af526bf85f Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Tue, 4 Oct 2022 14:44:03 +0600 Subject: [PATCH 134/175] [Sign] Pair integration step 1 --- .../Auth/Services/App/AppPairService.swift | 2 +- .../WalletConnectPairing/PairingClient.swift | 18 +++- .../PairingClientFactory.swift | 4 + .../Services/App/AppPairService.swift | 2 +- .../Services/Common/ExpirationService.swift | 22 ++++ .../Services/Common/ResubscribeService.swift | 27 +++++ .../Engine/Common/ApproveEngine.swift | 5 +- .../Engine/Common/PairingEngine.swift | 102 ------------------ .../Services/App/AppProposeService.swift | 41 +++++++ .../WalletConnectSign/Sign/SignClient.swift | 39 ++++--- .../Sign/SignClientFactory.swift | 9 +- .../WalletConnectError.swift | 3 - .../WalletConnectUtils/WalletConnectURI.swift | 33 +----- 13 files changed, 147 insertions(+), 160 deletions(-) create mode 100644 Sources/WalletConnectPairing/Services/Common/ExpirationService.swift create mode 100644 Sources/WalletConnectPairing/Services/Common/ResubscribeService.swift delete mode 100644 Sources/WalletConnectSign/Engine/Common/PairingEngine.swift create mode 100644 Sources/WalletConnectSign/Services/App/AppProposeService.swift diff --git a/Sources/Auth/Services/App/AppPairService.swift b/Sources/Auth/Services/App/AppPairService.swift index dc05600b0..f790e06a6 100644 --- a/Sources/Auth/Services/App/AppPairService.swift +++ b/Sources/Auth/Services/App/AppPairService.swift @@ -19,7 +19,7 @@ actor AppPairService { try await networkingInteractor.subscribe(topic: topic) let symKey = try! kms.createSymmetricKey(topic) let pairing = WCPairing(topic: topic) - let uri = WalletConnectURI(topic: topic, symKey: symKey.hexRepresentation, relay: pairing.relay, api: .auth) + let uri = WalletConnectURI(topic: topic, symKey: symKey.hexRepresentation, relay: pairing.relay) pairingStorage.setPairing(pairing) return uri } diff --git a/Sources/WalletConnectPairing/PairingClient.swift b/Sources/WalletConnectPairing/PairingClient.swift index 20f035b84..fa74df88f 100644 --- a/Sources/WalletConnectPairing/PairingClient.swift +++ b/Sources/WalletConnectPairing/PairingClient.swift @@ -21,7 +21,8 @@ public class PairingClient: PairingRegisterer { private let pairingRequestsSubscriber: PairingRequestsSubscriber private let pairingsProvider: PairingsProvider private let deletePairingService: DeletePairingService - private let pairingStorage: WCPairingStorage + private let resubscribeService: ResubscribeService + private let expirationService: ExpirationService private let cleanupService: CleanupService @@ -30,9 +31,10 @@ public class PairingClient: PairingRegisterer { logger: ConsoleLogging, walletPairService: WalletPairService, deletePairingService: DeletePairingService, + resubscribeService: ResubscribeService, + expirationService: ExpirationService, pairingRequestsSubscriber: PairingRequestsSubscriber, appPairActivateService: AppPairActivationService, - pairingStorage: WCPairingStorage, cleanupService: CleanupService, pingService: PairingPingService, socketConnectionStatusPublisher: AnyPublisher, @@ -43,14 +45,16 @@ public class PairingClient: PairingRegisterer { self.networkingInteractor = networkingInteractor self.socketConnectionStatusPublisher = socketConnectionStatusPublisher self.logger = logger - self.pairingStorage = pairingStorage self.deletePairingService = deletePairingService self.appPairActivateService = appPairActivateService + self.resubscribeService = resubscribeService + self.expirationService = expirationService self.cleanupService = cleanupService self.pingService = pingService self.pairingRequestsSubscriber = pairingRequestsSubscriber self.pairingsProvider = pairingsProvider setUpPublishers() + setUpExpiration() } private func setUpPublishers() { @@ -59,6 +63,10 @@ public class PairingClient: PairingRegisterer { } } + private func setUpExpiration() { + expirationService.setupExpirationHandling() + } + /// For wallet to establish a pairing /// Wallet should call this function in order to accept peer's pairing proposal and be able to subscribe for future requests. /// - Parameter uri: Pairing URI that is commonly presented as a QR code by a dapp or delivered with universal linking. @@ -106,6 +114,10 @@ public class PairingClient: PairingRegisterer { _ = try pairingsProvider.getPairing(for: topic) } + public func resubscribe() { + resubscribeService.resubscribe() + } + public func register(method: ProtocolMethod) -> AnyPublisher, Never> { logger.debug("Pairing Client - registering for \(method.method)") return pairingRequestsSubscriber.subscribeForRequest(method) diff --git a/Sources/WalletConnectPairing/PairingClientFactory.swift b/Sources/WalletConnectPairing/PairingClientFactory.swift index a3e5af784..616717184 100644 --- a/Sources/WalletConnectPairing/PairingClientFactory.swift +++ b/Sources/WalletConnectPairing/PairingClientFactory.swift @@ -24,6 +24,8 @@ public struct PairingClientFactory { let deletePairingService = DeletePairingService(networkingInteractor: networkingClient, kms: kms, pairingStorage: pairingStore, logger: logger) let pingService = PairingPingService(pairingStorage: pairingStore, networkingInteractor: networkingClient, logger: logger) let appPairActivateService = AppPairActivationService(pairingStorage: pairingStore, logger: logger) + let expirationService = ExpirationService(pairingStorage: pairingStore, networkInteractor: networkingInteractor, kms: kms) + let resubscribeService = ResubscribeService(networkInteractor: networkingInteractor, pairingStorage: pairingStore) return PairingClient( appPairService: appPairService, @@ -31,6 +33,8 @@ public struct PairingClientFactory { logger: logger, walletPairService: walletPairService, deletePairingService: deletePairingService, + resubscribeService: resubscribeService, + expirationService: expirationService, pairingRequestsSubscriber: pairingRequestsSubscriber, appPairActivateService: appPairActivateService, pairingStorage: pairingStore, diff --git a/Sources/WalletConnectPairing/Services/App/AppPairService.swift b/Sources/WalletConnectPairing/Services/App/AppPairService.swift index 9d223d06a..fb51d8e9b 100644 --- a/Sources/WalletConnectPairing/Services/App/AppPairService.swift +++ b/Sources/WalletConnectPairing/Services/App/AppPairService.swift @@ -19,7 +19,7 @@ actor AppPairService { try await networkingInteractor.subscribe(topic: topic) let symKey = try! kms.createSymmetricKey(topic) let pairing = WCPairing(topic: topic) - let uri = WalletConnectURI(topic: topic, symKey: symKey.hexRepresentation, relay: pairing.relay, api: .auth) + let uri = WalletConnectURI(topic: topic, symKey: symKey.hexRepresentation, relay: pairing.relay) pairingStorage.setPairing(pairing) return uri } diff --git a/Sources/WalletConnectPairing/Services/Common/ExpirationService.swift b/Sources/WalletConnectPairing/Services/Common/ExpirationService.swift new file mode 100644 index 000000000..22e3e7166 --- /dev/null +++ b/Sources/WalletConnectPairing/Services/Common/ExpirationService.swift @@ -0,0 +1,22 @@ +import Foundation +import WalletConnectNetworking +import WalletConnectKMS + +final class ExpirationService { + private let pairingStorage: PairingStorage + private let networkInteractor: NetworkInteracting + private let kms: KeyManagementServiceProtocol + + init(pairingStorage: PairingStorage, networkInteractor: NetworkInteracting, kms: KeyManagementServiceProtocol) { + self.pairingStorage = pairingStorage + self.networkInteractor = networkInteractor + self.kms = kms + } + + func setupExpirationHandling() { + pairingStorage.onPairingExpiration = { [weak self] pairing in + self?.kms.deleteSymmetricKey(for: pairing.topic) + self?.networkInteractor.unsubscribe(topic: pairing.topic) + } + } +} diff --git a/Sources/WalletConnectPairing/Services/Common/ResubscribeService.swift b/Sources/WalletConnectPairing/Services/Common/ResubscribeService.swift new file mode 100644 index 000000000..a26c12283 --- /dev/null +++ b/Sources/WalletConnectPairing/Services/Common/ResubscribeService.swift @@ -0,0 +1,27 @@ +import Foundation +import Combine +import WalletConnectNetworking + +final class ResubscribeService { + + private var publishers = Set() + + private let networkInteractor: NetworkInteracting + private let pairingStorage: PairingStorage + + init(networkInteractor: NetworkInteracting, pairingStorage: PairingStorage) { + self.networkInteractor = networkInteractor + self.pairingStorage = pairingStorage + } + + func resubscribe() { + networkInteractor.socketConnectionStatusPublisher + .sink { [unowned self] status in + pairingStorage.getAll() + .forEach { pairing in + Task(priority: .high) { try await networkInteractor.subscribe(topic: pairing.topic) } + } + } + .store(in: &publishers) + } +} diff --git a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift index 7dbc9f476..00f2f1fac 100644 --- a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift @@ -27,6 +27,7 @@ final class ApproveEngine { private let sessionStore: WCSessionStorage private let proposalPayloadsStore: CodableStore> private let sessionToPairingTopic: CodableStore + private let pairingRegisterer: PairingRegisterer private let metadata: AppMetadata private let kms: KeyManagementServiceProtocol private let logger: ConsoleLogging @@ -37,6 +38,7 @@ final class ApproveEngine { networkingInteractor: NetworkInteracting, proposalPayloadsStore: CodableStore>, sessionToPairingTopic: CodableStore, + pairingRegisterer: PairingRegisterer, metadata: AppMetadata, kms: KeyManagementServiceProtocol, logger: ConsoleLogging, @@ -46,6 +48,7 @@ final class ApproveEngine { self.networkingInteractor = networkingInteractor self.proposalPayloadsStore = proposalPayloadsStore self.sessionToPairingTopic = sessionToPairingTopic + self.pairingRegisterer = pairingRegisterer self.metadata = metadata self.kms = kms self.logger = logger @@ -155,7 +158,7 @@ final class ApproveEngine { private extension ApproveEngine { func setupRequestSubscriptions() { - networkingInteractor.requestSubscription(on: SessionProposeProtocolMethod()) + pairingRegisterer.register(method: SessionProposeProtocolMethod()) .sink { [unowned self] (payload: RequestSubscriptionPayload) in handleSessionProposeRequest(payload: payload) }.store(in: &publishers) diff --git a/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift b/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift deleted file mode 100644 index 9e67e0c3e..000000000 --- a/Sources/WalletConnectSign/Engine/Common/PairingEngine.swift +++ /dev/null @@ -1,102 +0,0 @@ -import Foundation -import Combine -import JSONRPC -import WalletConnectPairing -import WalletConnectUtils -import WalletConnectKMS -import WalletConnectNetworking - -final class PairingEngine { - - private let networkingInteractor: NetworkInteracting - private let kms: KeyManagementServiceProtocol - private let pairingStore: WCPairingStorage - private var metadata: AppMetadata - private var publishers = [AnyCancellable]() - private let logger: ConsoleLogging - private let topicInitializer: () -> String - - init( - networkingInteractor: NetworkInteracting, - kms: KeyManagementServiceProtocol, - pairingStore: WCPairingStorage, - metadata: AppMetadata, - logger: ConsoleLogging, - topicGenerator: @escaping () -> String = String.generateTopic - ) { - self.networkingInteractor = networkingInteractor - self.kms = kms - self.metadata = metadata - self.pairingStore = pairingStore - self.logger = logger - self.topicInitializer = topicGenerator - setupNetworkingSubscriptions() - setupExpirationHandling() - } - - func hasPairing(for topic: String) -> Bool { - return pairingStore.hasPairing(forTopic: topic) - } - - func getSettledPairing(for topic: String) -> WCPairing? { - guard let pairing = pairingStore.getPairing(forTopic: topic) else { - return nil - } - return pairing - } - - func getPairings() -> [Pairing] { - pairingStore.getAll() - .map {Pairing(topic: $0.topic, peer: $0.peerMetadata, expiryDate: $0.expiryDate)} - } - - func create() async throws -> WalletConnectURI { - let topic = topicInitializer() - try await networkingInteractor.subscribe(topic: topic) - let symKey = try! kms.createSymmetricKey(topic) - let pairing = WCPairing(topic: topic) - let uri = WalletConnectURI(topic: topic, symKey: symKey.hexRepresentation, relay: pairing.relay) - pairingStore.setPairing(pairing) - return uri - } - - func propose(pairingTopic: String, namespaces: [String: ProposalNamespace], relay: RelayProtocolOptions) async throws { - logger.debug("Propose Session on topic: \(pairingTopic)") - try Namespace.validate(namespaces) - let protocolMethod = SessionProposeProtocolMethod() - let publicKey = try! kms.createX25519KeyPair() - let proposer = Participant( - publicKey: publicKey.hexRepresentation, - metadata: metadata) - let proposal = SessionProposal( - relays: [relay], - proposer: proposer, - requiredNamespaces: namespaces) - - let request = RPCRequest(method: protocolMethod.method, params: proposal) - try await networkingInteractor.requestNetworkAck(request, topic: pairingTopic, protocolMethod: protocolMethod) - } -} - -// MARK: Private - -private extension PairingEngine { - - func setupNetworkingSubscriptions() { - networkingInteractor.socketConnectionStatusPublisher - .sink { [unowned self] status in - pairingStore.getAll() - .forEach { pairing in - Task(priority: .high) { try await networkingInteractor.subscribe(topic: pairing.topic) } - } - } - .store(in: &publishers) - } - - func setupExpirationHandling() { - pairingStore.onPairingExpiration = { [weak self] pairing in - self?.kms.deleteSymmetricKey(for: pairing.topic) - self?.networkingInteractor.unsubscribe(topic: pairing.topic) - } - } -} diff --git a/Sources/WalletConnectSign/Services/App/AppProposeService.swift b/Sources/WalletConnectSign/Services/App/AppProposeService.swift new file mode 100644 index 000000000..f24db9b39 --- /dev/null +++ b/Sources/WalletConnectSign/Services/App/AppProposeService.swift @@ -0,0 +1,41 @@ +import Foundation +import JSONRPC +import WalletConnectNetworking +import WalletConnectKMS +import WalletConnectUtils + +final class AppProposeService { + private let metadata: AppMetadata + private let networkingInteractor: NetworkInteracting + private let kms: KeyManagementServiceProtocol + private let logger: ConsoleLogging + + init( + metadata: AppMetadata, + networkingInteractor: NetworkInteracting, + kms: KeyManagementServiceProtocol, + logger: ConsoleLogging + ) { + self.metadata = metadata + self.networkingInteractor = networkingInteractor + self.kms = kms + self.logger = logger + } + + func propose(pairingTopic: String, namespaces: [String: ProposalNamespace], relay: RelayProtocolOptions) async throws { + logger.debug("Propose Session on topic: \(pairingTopic)") + try Namespace.validate(namespaces) + let protocolMethod = SessionProposeProtocolMethod() + let publicKey = try! kms.createX25519KeyPair() + let proposer = Participant( + publicKey: publicKey.hexRepresentation, + metadata: metadata) + let proposal = SessionProposal( + relays: [relay], + proposer: proposer, + requiredNamespaces: namespaces) + + let request = RPCRequest(method: protocolMethod.method, params: proposal) + try await networkingInteractor.requestNetworkAck(request, topic: pairingTopic, protocolMethod: protocolMethod) + } +} diff --git a/Sources/WalletConnectSign/Sign/SignClient.swift b/Sources/WalletConnectSign/Sign/SignClient.swift index d448d2338..97215fcee 100644 --- a/Sources/WalletConnectSign/Sign/SignClient.swift +++ b/Sources/WalletConnectSign/Sign/SignClient.swift @@ -95,9 +95,9 @@ public final class SignClient { public let logger: ConsoleLogging // MARK: - Private properties + private let pairingClient: PairingClient private let relayClient: RelayClient - private let pairingEngine: PairingEngine private let pairEngine: PairEngine private let sessionEngine: SessionEngine private let approveEngine: ApproveEngine @@ -106,6 +106,7 @@ public final class SignClient { private let sessionPingService: SessionPingService private let nonControllerSessionStateMachine: NonControllerSessionStateMachine private let controllerSessionStateMachine: ControllerSessionStateMachine + private let appProposeService: AppProposeService private let history: RPCHistory private let cleanupService: CleanupService @@ -127,7 +128,6 @@ public final class SignClient { init(logger: ConsoleLogging, relayClient: RelayClient, - pairingEngine: PairingEngine, pairEngine: PairEngine, sessionEngine: SessionEngine, approveEngine: ApproveEngine, @@ -135,6 +135,7 @@ public final class SignClient { sessionPingService: SessionPingService, nonControllerSessionStateMachine: NonControllerSessionStateMachine, controllerSessionStateMachine: ControllerSessionStateMachine, + appProposeService: AppProposeService, disconnectService: DisconnectService, history: RPCHistory, cleanupService: CleanupService, @@ -142,7 +143,6 @@ public final class SignClient { ) { self.logger = logger self.relayClient = relayClient - self.pairingEngine = pairingEngine self.pairEngine = pairEngine self.sessionEngine = sessionEngine self.approveEngine = approveEngine @@ -150,12 +150,15 @@ public final class SignClient { self.sessionPingService = sessionPingService self.nonControllerSessionStateMachine = nonControllerSessionStateMachine self.controllerSessionStateMachine = controllerSessionStateMachine + self.appProposeService = appProposeService self.history = history self.cleanupService = cleanupService self.disconnectService = disconnectService self.pairingClient = pairingClient + setUpConnectionObserving() setUpEnginesCallbacks() + setUpPairingResubscription() } // MARK: - Public interface @@ -169,15 +172,20 @@ public final class SignClient { public func connect(requiredNamespaces: [String: ProposalNamespace], topic: String? = nil) async throws -> WalletConnectURI? { logger.debug("Connecting Application") if let topic = topic { - guard let pairing = pairingEngine.getSettledPairing(for: topic) else { - throw WalletConnectError.noPairingMatchingTopic(topic) - } - logger.debug("Proposing session on existing pairing") - try await pairingEngine.propose(pairingTopic: topic, namespaces: requiredNamespaces, relay: pairing.relay) + try pairingClient.validatePairingExistance(topic) + try await appProposeService.propose( + pairingTopic: topic, + namespaces: requiredNamespaces, + relay: RelayProtocolOptions(protocol: "irn", data: nil) + ) return nil } else { - let pairingURI = try await pairingEngine.create() - try await pairingEngine.propose(pairingTopic: pairingURI.topic, namespaces: requiredNamespaces, relay: pairingURI.relay) + let pairingURI = try await pairingClient.create() + try await appProposeService.propose( + pairingTopic: pairingURI.topic, + namespaces: requiredNamespaces, + relay: RelayProtocolOptions(protocol: "irn", data: nil) + ) return pairingURI } } @@ -190,9 +198,6 @@ public final class SignClient { /// - When URI has invalid format or missing params /// - When topic is already in use public func pair(uri: WalletConnectURI) async throws { - guard uri.api == .sign else { - throw WalletConnectError.pairingUriWrongApiParam - } try await pairEngine.pair(uri) } @@ -256,7 +261,7 @@ public final class SignClient { /// - topic: Topic of a session or a pairing /// - completion: Result will be success on response or an error public func ping(topic: String) async throws { - if pairingEngine.hasPairing(for: topic) { + if let _ = try? pairingClient.validatePairingExistance(topic) { try await pairingPingService.ping(topic: topic) } else if sessionEngine.hasSession(for: topic) { try await sessionPingService.ping(topic: topic) @@ -297,7 +302,7 @@ public final class SignClient { /// Query pairings /// - Returns: All pairings public func getPairings() -> [Pairing] { - pairingEngine.getPairings() + pairingClient.getPairings() } /// Query pending requests @@ -385,4 +390,8 @@ public final class SignClient { self?.socketConnectionStatusPublisherSubject.send(status) }.store(in: &publishers) } + + private func setUpPairingResubscription() { + pairingClient.resubscribe() + } } diff --git a/Sources/WalletConnectSign/Sign/SignClientFactory.swift b/Sources/WalletConnectSign/Sign/SignClientFactory.swift index 8fc6e151a..b04c651fe 100644 --- a/Sources/WalletConnectSign/Sign/SignClientFactory.swift +++ b/Sources/WalletConnectSign/Sign/SignClientFactory.swift @@ -32,29 +32,30 @@ public struct SignClientFactory { let sessionStore = SessionStorage(storage: SequenceStore(store: .init(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.sessions.rawValue))) let sessionToPairingTopic = CodableStore(defaults: RuntimeKeyValueStorage(), identifier: StorageDomainIdentifiers.sessionToPairingTopic.rawValue) let proposalPayloadsStore = CodableStore>(defaults: RuntimeKeyValueStorage(), identifier: StorageDomainIdentifiers.proposals.rawValue) - let pairingEngine = PairingEngine(networkingInteractor: networkingInteractor, kms: kms, pairingStore: pairingStore, metadata: metadata, logger: logger) let sessionEngine = SessionEngine(networkingInteractor: networkingInteractor, kms: kms, sessionStore: sessionStore, logger: logger) let nonControllerSessionStateMachine = NonControllerSessionStateMachine(networkingInteractor: networkingInteractor, kms: kms, sessionStore: sessionStore, logger: logger) let controllerSessionStateMachine = ControllerSessionStateMachine(networkingInteractor: networkingInteractor, kms: kms, sessionStore: sessionStore, logger: logger) let pairEngine = PairEngine(networkingInteractor: networkingInteractor, kms: kms, pairingStore: pairingStore) - let approveEngine = ApproveEngine(networkingInteractor: networkingInteractor, proposalPayloadsStore: proposalPayloadsStore, sessionToPairingTopic: sessionToPairingTopic, metadata: metadata, kms: kms, logger: logger, pairingStore: pairingStore, sessionStore: sessionStore) + let approveEngine = ApproveEngine(networkingInteractor: networkingInteractor, proposalPayloadsStore: proposalPayloadsStore, sessionToPairingTopic: sessionToPairingTopic, pairingRegisterer: pairingClient, metadata: metadata, kms: kms, logger: logger, pairingStore: pairingStore, sessionStore: sessionStore) let cleanupService = CleanupService(pairingStore: pairingStore, sessionStore: sessionStore, kms: kms, sessionToPairingTopic: sessionToPairingTopic) let deleteSessionService = DeleteSessionService(networkingInteractor: networkingInteractor, kms: kms, sessionStore: sessionStore, logger: logger) let disconnectService = DisconnectService(deleteSessionService: deleteSessionService, sessionStorage: sessionStore, pairingClient: pairingClient) let sessionPingService = SessionPingService(sessionStorage: sessionStore, networkingInteractor: networkingInteractor, logger: logger) let pairingPingService = PairingPingService(pairingStorage: pairingStore, networkingInteractor: networkingInteractor, logger: logger) + let appProposerService = AppProposeService(metadata: metadata, networkingInteractor: networkingInteractor, kms: kms, logger: logger) let client = SignClient( logger: logger, relayClient: relayClient, - pairingEngine: pairingEngine, pairEngine: pairEngine, sessionEngine: sessionEngine, approveEngine: approveEngine, pairingPingService: pairingPingService, sessionPingService: sessionPingService, nonControllerSessionStateMachine: nonControllerSessionStateMachine, - controllerSessionStateMachine: controllerSessionStateMachine, disconnectService: disconnectService, + controllerSessionStateMachine: controllerSessionStateMachine, + appProposeService: appProposerService, + disconnectService: disconnectService, history: rpcHistory, cleanupService: cleanupService, pairingClient: pairingClient diff --git a/Sources/WalletConnectSign/WalletConnectError.swift b/Sources/WalletConnectSign/WalletConnectError.swift index afd7f73b8..5579b1d6f 100644 --- a/Sources/WalletConnectSign/WalletConnectError.swift +++ b/Sources/WalletConnectSign/WalletConnectError.swift @@ -1,7 +1,6 @@ enum WalletConnectError: Error { case pairingProposalFailed - case pairingUriWrongApiParam case noPairingMatchingTopic(String) case noSessionMatchingTopic(String) case sessionNotAcknowledged(String) @@ -29,8 +28,6 @@ extension WalletConnectError { switch self { case .pairingProposalFailed: return "Pairing proposal failed." - case .pairingUriWrongApiParam: - return "Pairing URI containt wrong API param" case .noPairingMatchingTopic(let topic): return "There is no existing pairing matching the topic: \(topic)." case .noSessionMatchingTopic(let topic): diff --git a/Sources/WalletConnectUtils/WalletConnectURI.swift b/Sources/WalletConnectUtils/WalletConnectURI.swift index cd4fa03f7..7db772582 100644 --- a/Sources/WalletConnectUtils/WalletConnectURI.swift +++ b/Sources/WalletConnectUtils/WalletConnectURI.swift @@ -2,36 +2,20 @@ import Foundation public struct WalletConnectURI: Equatable { - public enum TargetAPI: String, CaseIterable { - case sign - case chat - case auth - } - public let topic: String public let version: String public let symKey: String public let relay: RelayProtocolOptions - public var api: TargetAPI { - return apiType ?? .sign - } - public var absoluteString: String { - if let api = apiType { - return "wc:\(api.rawValue)-\(topic)@\(version)?symKey=\(symKey)&\(relayQuery)" - } return "wc:\(topic)@\(version)?symKey=\(symKey)&\(relayQuery)" } - private let apiType: TargetAPI? - - public init(topic: String, symKey: String, relay: RelayProtocolOptions, api: TargetAPI? = nil) { + public init(topic: String, symKey: String, relay: RelayProtocolOptions) { self.version = "2" self.topic = topic self.symKey = symKey self.relay = relay - self.apiType = api } public init?(string: String) { @@ -41,21 +25,19 @@ public struct WalletConnectURI: Equatable { let query: [String: String]? = components.queryItems?.reduce(into: [:]) { $0[$1.name] = $1.value } guard - let userString = components.user, + let topic = components.user, let version = components.host, let symKey = query?["symKey"], let relayProtocol = query?["relay-protocol"] else { return nil } - let uriUser = Self.parseURIUser(from: userString) let relayData = query?["relay-data"] self.version = version - self.topic = uriUser.topic + self.topic = topic self.symKey = symKey self.relay = RelayProtocolOptions(protocol: relayProtocol, data: relayData) - self.apiType = uriUser.api } private var relayQuery: String { @@ -73,13 +55,4 @@ public struct WalletConnectURI: Equatable { let urlString = !string.hasPrefix("wc://") ? string.replacingOccurrences(of: "wc:", with: "wc://") : string return URLComponents(string: urlString) } - - private static func parseURIUser(from string: String) -> (topic: String, api: TargetAPI?) { - let splits = string.split(separator: "-") - if splits.count == 2, let apiFromPrefix = TargetAPI(rawValue: String(splits[0])) { - return (String(splits[1]), apiFromPrefix) - } else { - return (string, nil) - } - } } From 711711f4ca6f65f7db85ae587642bc28259fc5a0 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Tue, 4 Oct 2022 14:46:46 +0600 Subject: [PATCH 135/175] Build error --- Sources/WalletConnectPairing/PairingClientFactory.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Sources/WalletConnectPairing/PairingClientFactory.swift b/Sources/WalletConnectPairing/PairingClientFactory.swift index 616717184..9b186a49c 100644 --- a/Sources/WalletConnectPairing/PairingClientFactory.swift +++ b/Sources/WalletConnectPairing/PairingClientFactory.swift @@ -37,7 +37,6 @@ public struct PairingClientFactory { expirationService: expirationService, pairingRequestsSubscriber: pairingRequestsSubscriber, appPairActivateService: appPairActivateService, - pairingStorage: pairingStore, cleanupService: cleanupService, pingService: pingService, socketConnectionStatusPublisher: networkingClient.socketConnectionStatusPublisher, From 36de1cd049098176d6f4342f701ea8c69f39a69c Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Tue, 4 Oct 2022 15:00:38 +0600 Subject: [PATCH 136/175] PairEngineRemoved --- .../Sign/SignClientTests.swift | 1 - .../IntegrationTests/Stubs/InputConfig.swift | 2 +- .../Engine/Controller/PairEngine.swift | 34 ------------------- .../WalletConnectSign/Sign/SignClient.swift | 5 +-- .../Sign/SignClientFactory.swift | 2 -- 5 files changed, 2 insertions(+), 42 deletions(-) delete mode 100644 Sources/WalletConnectSign/Engine/Controller/PairEngine.swift diff --git a/Example/IntegrationTests/Sign/SignClientTests.swift b/Example/IntegrationTests/Sign/SignClientTests.swift index 8d4f0abab..3be1b8eda 100644 --- a/Example/IntegrationTests/Sign/SignClientTests.swift +++ b/Example/IntegrationTests/Sign/SignClientTests.swift @@ -307,7 +307,6 @@ final class SignClientTests: XCTestCase { let uri = try! await dapp.client.connect(requiredNamespaces: requiredNamespaces) try! await wallet.client.pair(uri: uri!) wait(for: [dappSettlementExpectation, walletSettlementExpectation], timeout: InputConfig.defaultTimeout) - } func testSuccessfulSessionUpdateNamespaces() async { diff --git a/Example/IntegrationTests/Stubs/InputConfig.swift b/Example/IntegrationTests/Stubs/InputConfig.swift index 0e0c2efb7..df25b0fa7 100644 --- a/Example/IntegrationTests/Stubs/InputConfig.swift +++ b/Example/IntegrationTests/Stubs/InputConfig.swift @@ -11,7 +11,7 @@ struct InputConfig { } static var defaultTimeout: TimeInterval { - return 30 + return 5 } private static func config(for key: String) -> String? { diff --git a/Sources/WalletConnectSign/Engine/Controller/PairEngine.swift b/Sources/WalletConnectSign/Engine/Controller/PairEngine.swift deleted file mode 100644 index 81b4465d1..000000000 --- a/Sources/WalletConnectSign/Engine/Controller/PairEngine.swift +++ /dev/null @@ -1,34 +0,0 @@ -import Foundation -import WalletConnectKMS -import WalletConnectPairing -import WalletConnectNetworking - -actor PairEngine { - private let networkingInteractor: NetworkInteracting - private let kms: KeyManagementServiceProtocol - private let pairingStore: WCPairingStorage - - init(networkingInteractor: NetworkInteracting, - kms: KeyManagementServiceProtocol, - pairingStore: WCPairingStorage) { - self.networkingInteractor = networkingInteractor - self.kms = kms - self.pairingStore = pairingStore - } - - func pair(_ uri: WalletConnectURI) async throws { - guard !hasPairing(for: uri.topic) else { - throw WalletConnectError.pairingAlreadyExist - } - var pairing = WCPairing(uri: uri) - let symKey = try SymmetricKey(hex: uri.symKey) - try kms.setSymmetricKey(symKey, for: pairing.topic) - pairing.activate() - pairingStore.setPairing(pairing) - try await networkingInteractor.subscribe(topic: pairing.topic) - } - - func hasPairing(for topic: String) -> Bool { - return pairingStore.hasPairing(forTopic: topic) - } -} diff --git a/Sources/WalletConnectSign/Sign/SignClient.swift b/Sources/WalletConnectSign/Sign/SignClient.swift index 97215fcee..469bb3676 100644 --- a/Sources/WalletConnectSign/Sign/SignClient.swift +++ b/Sources/WalletConnectSign/Sign/SignClient.swift @@ -98,7 +98,6 @@ public final class SignClient { private let pairingClient: PairingClient private let relayClient: RelayClient - private let pairEngine: PairEngine private let sessionEngine: SessionEngine private let approveEngine: ApproveEngine private let disconnectService: DisconnectService @@ -128,7 +127,6 @@ public final class SignClient { init(logger: ConsoleLogging, relayClient: RelayClient, - pairEngine: PairEngine, sessionEngine: SessionEngine, approveEngine: ApproveEngine, pairingPingService: PairingPingService, @@ -143,7 +141,6 @@ public final class SignClient { ) { self.logger = logger self.relayClient = relayClient - self.pairEngine = pairEngine self.sessionEngine = sessionEngine self.approveEngine = approveEngine self.pairingPingService = pairingPingService @@ -198,7 +195,7 @@ public final class SignClient { /// - When URI has invalid format or missing params /// - When topic is already in use public func pair(uri: WalletConnectURI) async throws { - try await pairEngine.pair(uri) + try await pairingClient.pair(uri: uri) } /// For a wallet to approve a session proposal. diff --git a/Sources/WalletConnectSign/Sign/SignClientFactory.swift b/Sources/WalletConnectSign/Sign/SignClientFactory.swift index b04c651fe..482fd8bd1 100644 --- a/Sources/WalletConnectSign/Sign/SignClientFactory.swift +++ b/Sources/WalletConnectSign/Sign/SignClientFactory.swift @@ -35,7 +35,6 @@ public struct SignClientFactory { let sessionEngine = SessionEngine(networkingInteractor: networkingInteractor, kms: kms, sessionStore: sessionStore, logger: logger) let nonControllerSessionStateMachine = NonControllerSessionStateMachine(networkingInteractor: networkingInteractor, kms: kms, sessionStore: sessionStore, logger: logger) let controllerSessionStateMachine = ControllerSessionStateMachine(networkingInteractor: networkingInteractor, kms: kms, sessionStore: sessionStore, logger: logger) - let pairEngine = PairEngine(networkingInteractor: networkingInteractor, kms: kms, pairingStore: pairingStore) let approveEngine = ApproveEngine(networkingInteractor: networkingInteractor, proposalPayloadsStore: proposalPayloadsStore, sessionToPairingTopic: sessionToPairingTopic, pairingRegisterer: pairingClient, metadata: metadata, kms: kms, logger: logger, pairingStore: pairingStore, sessionStore: sessionStore) let cleanupService = CleanupService(pairingStore: pairingStore, sessionStore: sessionStore, kms: kms, sessionToPairingTopic: sessionToPairingTopic) let deleteSessionService = DeleteSessionService(networkingInteractor: networkingInteractor, kms: kms, sessionStore: sessionStore, logger: logger) @@ -47,7 +46,6 @@ public struct SignClientFactory { let client = SignClient( logger: logger, relayClient: relayClient, - pairEngine: pairEngine, sessionEngine: sessionEngine, approveEngine: approveEngine, pairingPingService: pairingPingService, From a7f2197eda264684ed519162efcbdb575b5e546e Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Tue, 4 Oct 2022 16:16:48 +0600 Subject: [PATCH 137/175] Sign Networking instance + topic filter removed --- .../Sign/SignClientTests.swift | 11 +++++---- .../IntegrationTests/Stubs/InputConfig.swift | 2 +- Sources/Auth/Auth.swift | 3 ++- ...nteractor.swift => NetworkingClient.swift} | 9 ++----- .../PairingClientFactory.swift | 4 ++-- .../PairingRequestsSubscriber.swift | 11 +-------- Sources/WalletConnectSign/Sign/Sign.swift | 3 ++- .../Sign/SignClientFactory.swift | 24 +++++++++---------- 8 files changed, 27 insertions(+), 40 deletions(-) rename Sources/WalletConnectNetworking/{NetworkInteractor.swift => NetworkingClient.swift} (96%) diff --git a/Example/IntegrationTests/Sign/SignClientTests.swift b/Example/IntegrationTests/Sign/SignClientTests.swift index 3be1b8eda..e0b463489 100644 --- a/Example/IntegrationTests/Sign/SignClientTests.swift +++ b/Example/IntegrationTests/Sign/SignClientTests.swift @@ -29,21 +29,22 @@ final class SignClientTests: XCTestCase { relayClient: relayClient, logger: logger, keychainStorage: keychain, - keyValueStorage: keyValueStorage) - + keyValueStorage: keyValueStorage + ) let pairingClient = PairingClientFactory.create( logger: logger, keyValueStorage: keyValueStorage, keychainStorage: keychain, - networkingClient: networkingClient) - + networkingClient: networkingClient + ) let client = SignClientFactory.create( metadata: AppMetadata(name: name, description: "", url: "", icons: [""]), logger: logger, keyValueStorage: keyValueStorage, keychainStorage: keychain, relayClient: relayClient, - pairingClient: pairingClient + pairingClient: pairingClient, + networkingClient: networkingClient ) return ClientDelegate(client: client) } diff --git a/Example/IntegrationTests/Stubs/InputConfig.swift b/Example/IntegrationTests/Stubs/InputConfig.swift index df25b0fa7..0e0c2efb7 100644 --- a/Example/IntegrationTests/Stubs/InputConfig.swift +++ b/Example/IntegrationTests/Stubs/InputConfig.swift @@ -11,7 +11,7 @@ struct InputConfig { } static var defaultTimeout: TimeInterval { - return 5 + return 30 } private static func config(for key: String) -> String? { diff --git a/Sources/Auth/Auth.swift b/Sources/Auth/Auth.swift index 8a94d74b1..022b40744 100644 --- a/Sources/Auth/Auth.swift +++ b/Sources/Auth/Auth.swift @@ -25,7 +25,8 @@ public class Auth { metadata: config.metadata, account: config.account, networkingClient: Networking.instance, - pairingRegisterer: Pair.instance) + pairingRegisterer: Pair.instance + ) }() private static var config: Config? diff --git a/Sources/WalletConnectNetworking/NetworkInteractor.swift b/Sources/WalletConnectNetworking/NetworkingClient.swift similarity index 96% rename from Sources/WalletConnectNetworking/NetworkInteractor.swift rename to Sources/WalletConnectNetworking/NetworkingClient.swift index b51b6aaed..76e859428 100644 --- a/Sources/WalletConnectNetworking/NetworkInteractor.swift +++ b/Sources/WalletConnectNetworking/NetworkingClient.swift @@ -11,7 +11,6 @@ public class NetworkingClient: NetworkInteracting { private let serializer: Serializing private let rpcHistory: RPCHistory private let logger: ConsoleLogging - private let topics = SetStore(label: "com.walletconnect.sdk.networking.topics") private let requestPublisherSubject = PassthroughSubject<(topic: String, request: RPCRequest), Never>() private let responsePublisherSubject = PassthroughSubject<(topic: String, request: RPCRequest, response: RPCResponse), Never>() @@ -42,20 +41,16 @@ public class NetworkingClient: NetworkInteracting { private func setupRelaySubscribtion() { relayClient.messagePublisher - .filter { [unowned self] in topics.contains($0.topic)} .sink { [unowned self] (topic, message) in - manageSubscription(topic, message) - } - .store(in: &publishers) + manageSubscription(topic, message) + }.store(in: &publishers) } public func subscribe(topic: String) async throws { - topics.insert(topic) try await relayClient.subscribe(topic: topic) } public func unsubscribe(topic: String) { - topics.remove(topic) relayClient.unsubscribe(topic: topic) { [unowned self] error in if let error = error { logger.error(error) diff --git a/Sources/WalletConnectPairing/PairingClientFactory.swift b/Sources/WalletConnectPairing/PairingClientFactory.swift index 9b186a49c..b9acaa95f 100644 --- a/Sources/WalletConnectPairing/PairingClientFactory.swift +++ b/Sources/WalletConnectPairing/PairingClientFactory.swift @@ -24,8 +24,8 @@ public struct PairingClientFactory { let deletePairingService = DeletePairingService(networkingInteractor: networkingClient, kms: kms, pairingStorage: pairingStore, logger: logger) let pingService = PairingPingService(pairingStorage: pairingStore, networkingInteractor: networkingClient, logger: logger) let appPairActivateService = AppPairActivationService(pairingStorage: pairingStore, logger: logger) - let expirationService = ExpirationService(pairingStorage: pairingStore, networkInteractor: networkingInteractor, kms: kms) - let resubscribeService = ResubscribeService(networkInteractor: networkingInteractor, pairingStorage: pairingStore) + let expirationService = ExpirationService(pairingStorage: pairingStore, networkInteractor: networkingClient, kms: kms) + let resubscribeService = ResubscribeService(networkInteractor: networkingClient, pairingStorage: pairingStore) return PairingClient( appPairService: appPairService, diff --git a/Sources/WalletConnectPairing/PairingRequestsSubscriber.swift b/Sources/WalletConnectPairing/PairingRequestsSubscriber.swift index e9139f30a..f10808792 100644 --- a/Sources/WalletConnectPairing/PairingRequestsSubscriber.swift +++ b/Sources/WalletConnectPairing/PairingRequestsSubscriber.swift @@ -22,17 +22,8 @@ public class PairingRequestsSubscriber { } func subscribeForRequest(_ protocolMethod: ProtocolMethod) -> AnyPublisher, Never> { - registeredProtocolMethods.insert(protocolMethod.method) - - let publisherSubject = PassthroughSubject, Never>() - - networkingInteractor.requestSubscription(on: protocolMethod).sink { (payload: RequestSubscriptionPayload) in - publisherSubject.send(payload) - }.store(in: &publishers) - - - return publisherSubject.eraseToAnyPublisher() + return networkingInteractor.requestSubscription(on: protocolMethod).eraseToAnyPublisher() } func handleUnregisteredRequests() { diff --git a/Sources/WalletConnectSign/Sign/Sign.swift b/Sources/WalletConnectSign/Sign/Sign.swift index 09b02702e..5b0b024e5 100644 --- a/Sources/WalletConnectSign/Sign/Sign.swift +++ b/Sources/WalletConnectSign/Sign/Sign.swift @@ -29,7 +29,8 @@ public class Sign { return SignClientFactory.create( metadata: Sign.metadata ?? Pair.metadata, relayClient: Relay.instance, - pairingClient: Pair.instance + pairingClient: Pair.instance, + networkingClient: Networking.instance ) }() diff --git a/Sources/WalletConnectSign/Sign/SignClientFactory.swift b/Sources/WalletConnectSign/Sign/SignClientFactory.swift index 482fd8bd1..ba59acee8 100644 --- a/Sources/WalletConnectSign/Sign/SignClientFactory.swift +++ b/Sources/WalletConnectSign/Sign/SignClientFactory.swift @@ -16,32 +16,30 @@ public struct SignClientFactory { /// - keyValueStorage: by default WalletConnect SDK will store sequences in UserDefaults /// /// WalletConnect Client is not a singleton but once you create an instance, you should not deinitialize it. Usually only one instance of a client is required in the application. - public static func create(metadata: AppMetadata, relayClient: RelayClient, pairingClient: PairingClient) -> SignClient { + public static func create(metadata: AppMetadata, relayClient: RelayClient, pairingClient: PairingClient, networkingClient: NetworkingClient) -> SignClient { let logger = ConsoleLogger(loggingLevel: .off) let keyValueStorage = UserDefaults.standard let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk") - return SignClientFactory.create(metadata: metadata, logger: logger, keyValueStorage: keyValueStorage, keychainStorage: keychainStorage, relayClient: relayClient, pairingClient: pairingClient) + return SignClientFactory.create(metadata: metadata, logger: logger, keyValueStorage: keyValueStorage, keychainStorage: keychainStorage, relayClient: relayClient, pairingClient: pairingClient, networkingClient: networkingClient) } - static func create(metadata: AppMetadata, logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, relayClient: RelayClient, pairingClient: PairingClient) -> SignClient { + static func create(metadata: AppMetadata, logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, relayClient: RelayClient, pairingClient: PairingClient, networkingClient: NetworkingClient) -> SignClient { let kms = KeyManagementService(keychain: keychainStorage) - let serializer = Serializer(kms: kms) let rpcHistory = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage) - let networkingInteractor = NetworkingClient(relayClient: relayClient, serializer: serializer, logger: logger, rpcHistory: rpcHistory) let pairingStore = PairingStorage(storage: SequenceStore(store: .init(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.pairings.rawValue))) let sessionStore = SessionStorage(storage: SequenceStore(store: .init(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.sessions.rawValue))) let sessionToPairingTopic = CodableStore(defaults: RuntimeKeyValueStorage(), identifier: StorageDomainIdentifiers.sessionToPairingTopic.rawValue) let proposalPayloadsStore = CodableStore>(defaults: RuntimeKeyValueStorage(), identifier: StorageDomainIdentifiers.proposals.rawValue) - let sessionEngine = SessionEngine(networkingInteractor: networkingInteractor, kms: kms, sessionStore: sessionStore, logger: logger) - let nonControllerSessionStateMachine = NonControllerSessionStateMachine(networkingInteractor: networkingInteractor, kms: kms, sessionStore: sessionStore, logger: logger) - let controllerSessionStateMachine = ControllerSessionStateMachine(networkingInteractor: networkingInteractor, kms: kms, sessionStore: sessionStore, logger: logger) - let approveEngine = ApproveEngine(networkingInteractor: networkingInteractor, proposalPayloadsStore: proposalPayloadsStore, sessionToPairingTopic: sessionToPairingTopic, pairingRegisterer: pairingClient, metadata: metadata, kms: kms, logger: logger, pairingStore: pairingStore, sessionStore: sessionStore) + let sessionEngine = SessionEngine(networkingInteractor: networkingClient, kms: kms, sessionStore: sessionStore, logger: logger) + let nonControllerSessionStateMachine = NonControllerSessionStateMachine(networkingInteractor: networkingClient, kms: kms, sessionStore: sessionStore, logger: logger) + let controllerSessionStateMachine = ControllerSessionStateMachine(networkingInteractor: networkingClient, kms: kms, sessionStore: sessionStore, logger: logger) + let approveEngine = ApproveEngine(networkingInteractor: networkingClient, proposalPayloadsStore: proposalPayloadsStore, sessionToPairingTopic: sessionToPairingTopic, pairingRegisterer: pairingClient, metadata: metadata, kms: kms, logger: logger, pairingStore: pairingStore, sessionStore: sessionStore) let cleanupService = CleanupService(pairingStore: pairingStore, sessionStore: sessionStore, kms: kms, sessionToPairingTopic: sessionToPairingTopic) - let deleteSessionService = DeleteSessionService(networkingInteractor: networkingInteractor, kms: kms, sessionStore: sessionStore, logger: logger) + let deleteSessionService = DeleteSessionService(networkingInteractor: networkingClient, kms: kms, sessionStore: sessionStore, logger: logger) let disconnectService = DisconnectService(deleteSessionService: deleteSessionService, sessionStorage: sessionStore, pairingClient: pairingClient) - let sessionPingService = SessionPingService(sessionStorage: sessionStore, networkingInteractor: networkingInteractor, logger: logger) - let pairingPingService = PairingPingService(pairingStorage: pairingStore, networkingInteractor: networkingInteractor, logger: logger) - let appProposerService = AppProposeService(metadata: metadata, networkingInteractor: networkingInteractor, kms: kms, logger: logger) + let sessionPingService = SessionPingService(sessionStorage: sessionStore, networkingInteractor: networkingClient, logger: logger) + let pairingPingService = PairingPingService(pairingStorage: pairingStore, networkingInteractor: networkingClient, logger: logger) + let appProposerService = AppProposeService(metadata: metadata, networkingInteractor: networkingClient, kms: kms, logger: logger) let client = SignClient( logger: logger, From 49684d064e0fdbceb4d9607a1c35f397080e1ce3 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 6 Oct 2022 19:00:54 +0600 Subject: [PATCH 138/175] Stability fixes --- Example/ExampleApp/SceneDelegate.swift | 19 ++++++++++++++----- ...File.swift => ResubscriptionService.swift} | 9 ++++----- .../WalletConnectPairing/PairingClient.swift | 4 ---- .../Services/Common/ResubscribeService.swift | 4 +++- .../Engine/Common/SessionEngine.swift | 1 + .../WalletConnectSign/Sign/SignClient.swift | 5 ----- 6 files changed, 22 insertions(+), 20 deletions(-) rename Sources/Chat/ProtocolServices/Common/{File.swift => ResubscriptionService.swift} (71%) diff --git a/Example/ExampleApp/SceneDelegate.swift b/Example/ExampleApp/SceneDelegate.swift index 03a7c8580..1537a309b 100644 --- a/Example/ExampleApp/SceneDelegate.swift +++ b/Example/ExampleApp/SceneDelegate.swift @@ -38,14 +38,23 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { window = UIWindow(windowScene: windowScene) window?.rootViewController = UITabBarController.createExampleApp() window?.makeKeyAndVisible() + + if let userActivity = connectionOptions.userActivities.first { + handle(userActivity: userActivity) + } } func scene(_ scene: UIScene, continue userActivity: NSUserActivity) { - guard userActivity.activityType == NSUserActivityTypeBrowsingWeb, - let incomingURL = userActivity.webpageURL else { - return - } - let wcUri = incomingURL.absoluteString.deletingPrefix("https://walletconnect.com/wc?uri=") + handle(userActivity: userActivity) + } + + private func handle(userActivity: NSUserActivity) { + guard + let url = userActivity.webpageURL, + userActivity.activityType == NSUserActivityTypeBrowsingWeb + else { return } + + let wcUri = url.absoluteString.deletingPrefix("https://walletconnect.com/wc?uri=") Task(priority: .high) { try! await Sign.instance.pair(uri: WalletConnectURI(string: wcUri)!) } diff --git a/Sources/Chat/ProtocolServices/Common/File.swift b/Sources/Chat/ProtocolServices/Common/ResubscriptionService.swift similarity index 71% rename from Sources/Chat/ProtocolServices/Common/File.swift rename to Sources/Chat/ProtocolServices/Common/ResubscriptionService.swift index e821f03d7..c08738891 100644 --- a/Sources/Chat/ProtocolServices/Common/File.swift +++ b/Sources/Chat/ProtocolServices/Common/ResubscriptionService.swift @@ -22,11 +22,10 @@ class ResubscriptionService { func setUpResubscription() { networkingInteractor.socketConnectionStatusPublisher .sink { [unowned self] status in - if status == .connected { - Task(priority: .background) { - let topics = await threadStore.getAll().map {$0.topic} - topics.forEach { topic in Task(priority: .background) { try? await networkingInteractor.subscribe(topic: topic) } } - } + guard status == .connected else { return } + Task(priority: .background) { + let topics = await threadStore.getAll().map {$0.topic} + topics.forEach { topic in Task(priority: .background) { try? await networkingInteractor.subscribe(topic: topic) } } } }.store(in: &publishers) } diff --git a/Sources/WalletConnectPairing/PairingClient.swift b/Sources/WalletConnectPairing/PairingClient.swift index fa74df88f..39d2d1be1 100644 --- a/Sources/WalletConnectPairing/PairingClient.swift +++ b/Sources/WalletConnectPairing/PairingClient.swift @@ -114,10 +114,6 @@ public class PairingClient: PairingRegisterer { _ = try pairingsProvider.getPairing(for: topic) } - public func resubscribe() { - resubscribeService.resubscribe() - } - public func register(method: ProtocolMethod) -> AnyPublisher, Never> { logger.debug("Pairing Client - registering for \(method.method)") return pairingRequestsSubscriber.subscribeForRequest(method) diff --git a/Sources/WalletConnectPairing/Services/Common/ResubscribeService.swift b/Sources/WalletConnectPairing/Services/Common/ResubscribeService.swift index a26c12283..0c5c091fd 100644 --- a/Sources/WalletConnectPairing/Services/Common/ResubscribeService.swift +++ b/Sources/WalletConnectPairing/Services/Common/ResubscribeService.swift @@ -12,11 +12,13 @@ final class ResubscribeService { init(networkInteractor: NetworkInteracting, pairingStorage: PairingStorage) { self.networkInteractor = networkInteractor self.pairingStorage = pairingStorage + setUpResubscription() } - func resubscribe() { + func setUpResubscription() { networkInteractor.socketConnectionStatusPublisher .sink { [unowned self] status in + guard status == .connected else { return } pairingStorage.getAll() .forEach { pairing in Task(priority: .high) { try await networkInteractor.subscribe(topic: pairing.topic) } diff --git a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift index 2a2ca7d19..028b4016a 100644 --- a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift @@ -92,6 +92,7 @@ private extension SessionEngine { func setupConnectionSubscriptions() { networkingInteractor.socketConnectionStatusPublisher .sink { [unowned self] status in + guard status == .connected else { return } sessionStore.getAll() .forEach { session in Task(priority: .high) { try await networkingInteractor.subscribe(topic: session.topic) } diff --git a/Sources/WalletConnectSign/Sign/SignClient.swift b/Sources/WalletConnectSign/Sign/SignClient.swift index 469bb3676..6ef0b254c 100644 --- a/Sources/WalletConnectSign/Sign/SignClient.swift +++ b/Sources/WalletConnectSign/Sign/SignClient.swift @@ -155,7 +155,6 @@ public final class SignClient { setUpConnectionObserving() setUpEnginesCallbacks() - setUpPairingResubscription() } // MARK: - Public interface @@ -387,8 +386,4 @@ public final class SignClient { self?.socketConnectionStatusPublisherSubject.send(status) }.store(in: &publishers) } - - private func setUpPairingResubscription() { - pairingClient.resubscribe() - } } From 6e9014e0e4a492067f3e45261db40ef6562d078c Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 6 Oct 2022 20:38:46 +0600 Subject: [PATCH 139/175] Message decoding race condition fixed --- .../Services/Wallet/WalletPairService.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/WalletConnectPairing/Services/Wallet/WalletPairService.swift b/Sources/WalletConnectPairing/Services/Wallet/WalletPairService.swift index 199b5a9a9..97b168564 100644 --- a/Sources/WalletConnectPairing/Services/Wallet/WalletPairService.swift +++ b/Sources/WalletConnectPairing/Services/Wallet/WalletPairService.swift @@ -25,11 +25,11 @@ actor WalletPairService { throw Errors.pairingAlreadyExist } var pairing = WCPairing(uri: uri) - try await networkingInteractor.subscribe(topic: pairing.topic) let symKey = try SymmetricKey(hex: uri.symKey) try kms.setSymmetricKey(symKey, for: pairing.topic) pairing.activate() pairingStorage.setPairing(pairing) + try await networkingInteractor.subscribe(topic: pairing.topic) } func hasPairing(for topic: String) -> Bool { From 77d7300f52a8a456e3984b52f605803a77156072 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Fri, 7 Oct 2022 17:44:35 +0600 Subject: [PATCH 140/175] PairingTests target --- .../xcschemes/WalletConnect.xcscheme | 10 + Package.swift | 3 + .../Mocks/PairingRegistererMock.swift | 23 --- .../Mocks/PairingRegistererMock.swift | 23 +++ .../Stubs/AppMetadata+Stub.swift} | 0 .../Stubs/RelayProtocolOptions+Stub.swift | 8 + .../Stubs}/WalletConnectURI+Stub.swift | 7 +- .../AppPairServiceTests.swift | 35 ++++ .../WCPairingTests.swift | 3 +- .../WalletPairServiceTests.swift} | 29 +-- .../remove_this.swift | 156 +++++++++++++++ .../ApproveEngineTests.swift | 1 + .../PairingEngineTests.swift | 188 ------------------ Tests/WalletConnectSignTests/Stub/Stubs.swift | 6 - .../WalletConnectURITests.swift | 27 +-- 15 files changed, 251 insertions(+), 268 deletions(-) delete mode 100644 Tests/AuthTests/Mocks/PairingRegistererMock.swift create mode 100644 Tests/TestingUtils/Mocks/PairingRegistererMock.swift rename Tests/{WalletConnectSignTests/Mocks/AppMetadata.swift => TestingUtils/Stubs/AppMetadata+Stub.swift} (100%) create mode 100644 Tests/TestingUtils/Stubs/RelayProtocolOptions+Stub.swift rename Tests/{WalletConnectSignTests/Stub => TestingUtils/Stubs}/WalletConnectURI+Stub.swift (60%) create mode 100644 Tests/WalletConnectPairingTests/AppPairServiceTests.swift rename Tests/{WalletConnectSignTests => WalletConnectPairingTests}/WCPairingTests.swift (98%) rename Tests/{WalletConnectSignTests/PairEngineTests.swift => WalletConnectPairingTests/WalletPairServiceTests.swift} (63%) create mode 100644 Tests/WalletConnectPairingTests/remove_this.swift delete mode 100644 Tests/WalletConnectSignTests/PairingEngineTests.swift diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/WalletConnect.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/WalletConnect.xcscheme index 75284ea37..ff9119893 100644 --- a/.swiftpm/xcode/xcshareddata/xcschemes/WalletConnect.xcscheme +++ b/.swiftpm/xcode/xcshareddata/xcschemes/WalletConnect.xcscheme @@ -272,6 +272,16 @@ ReferencedContainer = "container:"> + + + + : PairingRegisterer where RequestParams : Codable { - - let subject = PassthroughSubject, Never>() - - var isActivateCalled: Bool = false - - func register(method: ProtocolMethod) -> AnyPublisher, Never> where RequestParams : Decodable, RequestParams : Encodable { - subject.eraseToAnyPublisher() as! AnyPublisher, Never> - } - - func activate(pairingTopic: String) { - isActivateCalled = true - } - - func validatePairingExistance(_ topic: String) throws { - - } -} diff --git a/Tests/TestingUtils/Mocks/PairingRegistererMock.swift b/Tests/TestingUtils/Mocks/PairingRegistererMock.swift new file mode 100644 index 000000000..7d515cbb9 --- /dev/null +++ b/Tests/TestingUtils/Mocks/PairingRegistererMock.swift @@ -0,0 +1,23 @@ +import Foundation +import WalletConnectPairing +import Combine +import WalletConnectNetworking + +public class PairingRegistererMock: PairingRegisterer where RequestParams : Codable { + + public let subject = PassthroughSubject, Never>() + + public var isActivateCalled: Bool = false + + public func register(method: ProtocolMethod) -> AnyPublisher, Never> where RequestParams : Decodable, RequestParams : Encodable { + subject.eraseToAnyPublisher() as! AnyPublisher, Never> + } + + public func activate(pairingTopic: String) { + isActivateCalled = true + } + + public func validatePairingExistance(_ topic: String) throws { + + } +} diff --git a/Tests/WalletConnectSignTests/Mocks/AppMetadata.swift b/Tests/TestingUtils/Stubs/AppMetadata+Stub.swift similarity index 100% rename from Tests/WalletConnectSignTests/Mocks/AppMetadata.swift rename to Tests/TestingUtils/Stubs/AppMetadata+Stub.swift diff --git a/Tests/TestingUtils/Stubs/RelayProtocolOptions+Stub.swift b/Tests/TestingUtils/Stubs/RelayProtocolOptions+Stub.swift new file mode 100644 index 000000000..bd3db0839 --- /dev/null +++ b/Tests/TestingUtils/Stubs/RelayProtocolOptions+Stub.swift @@ -0,0 +1,8 @@ +import WalletConnectUtils + +extension RelayProtocolOptions { + + public static func stub() -> RelayProtocolOptions { + RelayProtocolOptions(protocol: "", data: nil) + } +} diff --git a/Tests/WalletConnectSignTests/Stub/WalletConnectURI+Stub.swift b/Tests/TestingUtils/Stubs/WalletConnectURI+Stub.swift similarity index 60% rename from Tests/WalletConnectSignTests/Stub/WalletConnectURI+Stub.swift rename to Tests/TestingUtils/Stubs/WalletConnectURI+Stub.swift index 5d5129228..67ee5dfe9 100644 --- a/Tests/WalletConnectSignTests/Stub/WalletConnectURI+Stub.swift +++ b/Tests/TestingUtils/Stubs/WalletConnectURI+Stub.swift @@ -1,10 +1,9 @@ -@testable import WalletConnectSign -@testable import WalletConnectKMS -import CryptoKit +import WalletConnectKMS +import WalletConnectUtils extension WalletConnectURI { - static func stub(isController: Bool = false) -> WalletConnectURI { + public static func stub(isController: Bool = false) -> WalletConnectURI { WalletConnectURI( topic: String.generateTopic(), symKey: SymmetricKey().hexRepresentation, diff --git a/Tests/WalletConnectPairingTests/AppPairServiceTests.swift b/Tests/WalletConnectPairingTests/AppPairServiceTests.swift new file mode 100644 index 000000000..d0cb8f9b9 --- /dev/null +++ b/Tests/WalletConnectPairingTests/AppPairServiceTests.swift @@ -0,0 +1,35 @@ +import XCTest +@testable import WalletConnectPairing +@testable import TestingUtils +@testable import WalletConnectKMS +import WalletConnectUtils + +final class AppPairServiceTests: XCTestCase { + + var service: AppPairService! + var networkingInteractor: NetworkingInteractorMock! + var storageMock: WCPairingStorageMock! + var cryptoMock: KeyManagementServiceMock! + + override func setUp() { + networkingInteractor = NetworkingInteractorMock() + storageMock = WCPairingStorageMock() + cryptoMock = KeyManagementServiceMock() + service = AppPairService(networkingInteractor: networkingInteractor, kms: cryptoMock, pairingStorage: storageMock) + } + + override func tearDown() { + networkingInteractor = nil + storageMock = nil + cryptoMock = nil + service = nil + } + + func testCreate() async { + let uri = try! await service.create() + XCTAssert(cryptoMock.hasSymmetricKey(for: uri.topic), "Proposer must store the symmetric key matching the URI.") + XCTAssert(storageMock.hasPairing(forTopic: uri.topic), "The engine must store a pairing after creating one") + XCTAssert(networkingInteractor.didSubscribe(to: uri.topic), "Proposer must subscribe to pairing topic.") + XCTAssert(storageMock.getPairing(forTopic: uri.topic)?.active == false, "Recently created pairing must be inactive.") + } +} diff --git a/Tests/WalletConnectSignTests/WCPairingTests.swift b/Tests/WalletConnectPairingTests/WCPairingTests.swift similarity index 98% rename from Tests/WalletConnectSignTests/WCPairingTests.swift rename to Tests/WalletConnectPairingTests/WCPairingTests.swift index 6dcad44a0..f180efee2 100644 --- a/Tests/WalletConnectSignTests/WCPairingTests.swift +++ b/Tests/WalletConnectPairingTests/WCPairingTests.swift @@ -1,6 +1,7 @@ import XCTest @testable import WalletConnectPairing -@testable import WalletConnectSign +@testable import WalletConnectUtils +@testable import WalletConnectUtils final class WCPairingTests: XCTestCase { diff --git a/Tests/WalletConnectSignTests/PairEngineTests.swift b/Tests/WalletConnectPairingTests/WalletPairServiceTests.swift similarity index 63% rename from Tests/WalletConnectSignTests/PairEngineTests.swift rename to Tests/WalletConnectPairingTests/WalletPairServiceTests.swift index 66018ff7b..7617aee57 100644 --- a/Tests/WalletConnectSignTests/PairEngineTests.swift +++ b/Tests/WalletConnectPairingTests/WalletPairServiceTests.swift @@ -1,14 +1,13 @@ import XCTest -@testable import WalletConnectSign +@testable import WalletConnectPairing @testable import TestingUtils @testable import WalletConnectKMS import WalletConnectUtils import WalletConnectNetworking -final class PairEngineTests: XCTestCase { - - var engine: PairEngine! +final class WalletPairServiceTestsTests: XCTestCase { + var service: WalletPairService! var networkingInteractor: NetworkingInteractorMock! var storageMock: WCPairingStorageMock! var cryptoMock: KeyManagementServiceMock! @@ -17,30 +16,16 @@ final class PairEngineTests: XCTestCase { networkingInteractor = NetworkingInteractorMock() storageMock = WCPairingStorageMock() cryptoMock = KeyManagementServiceMock() - setupEngine() - } - - override func tearDown() { - networkingInteractor = nil - storageMock = nil - cryptoMock = nil - engine = nil - } - - func setupEngine() { - engine = PairEngine( - networkingInteractor: networkingInteractor, - kms: cryptoMock, - pairingStore: storageMock) + service = WalletPairService(networkingInteractor: networkingInteractor, kms: cryptoMock, pairingStorage: storageMock) } func testPairMultipleTimesOnSameURIThrows() async { let uri = WalletConnectURI.stub() for i in 1...10 { if i == 1 { - await XCTAssertNoThrowAsync(try await engine.pair(uri)) + await XCTAssertNoThrowAsync(try await service.pair(uri)) } else { - await XCTAssertThrowsErrorAsync(try await engine.pair(uri)) + await XCTAssertThrowsErrorAsync(try await service.pair(uri)) } } } @@ -48,7 +33,7 @@ final class PairEngineTests: XCTestCase { func testPair() async { let uri = WalletConnectURI.stub() let topic = uri.topic - try! await engine.pair(uri) + try! await service.pair(uri) XCTAssert(networkingInteractor.didSubscribe(to: topic), "Responder must subscribe to pairing topic.") XCTAssert(cryptoMock.hasSymmetricKey(for: topic), "Responder must store the symmetric key matching the pairing topic") XCTAssert(storageMock.hasPairing(forTopic: topic), "The engine must store a pairing") diff --git a/Tests/WalletConnectPairingTests/remove_this.swift b/Tests/WalletConnectPairingTests/remove_this.swift new file mode 100644 index 000000000..7978af71a --- /dev/null +++ b/Tests/WalletConnectPairingTests/remove_this.swift @@ -0,0 +1,156 @@ +//import XCTest +//import Combine +//import JSONRPC +//@testable import WalletConnectPairing +//@testable import TestingUtils +//@testable import WalletConnectKMS +//import WalletConnectUtils +// +//func deriveTopic(publicKey: String, privateKey: AgreementPrivateKey) -> String { +// try! KeyManagementService.generateAgreementKey(from: privateKey, peerPublicKey: publicKey).derivedTopic() +//} +// +//final class WalletPairServiceTests: XCTestCase { +// +// var service: WalletPairService! +// +// var networkingInteractor: NetworkingInteractorMock! +// var storageMock: WCPairingStorageMock! +// var cryptoMock: KeyManagementServiceMock! +// +// var publishers = Set() +// +// override func setUp() { +// networkingInteractor = NetworkingInteractorMock() +// storageMock = WCPairingStorageMock() +// cryptoMock = KeyManagementServiceMock() +// setupEngines() +// } +// +// override func tearDown() { +// networkingInteractor = nil +// storageMock = nil +// cryptoMock = nil +// service = nil +// } +// +// func setupEngines() { +// service = WalletPairService(networkingInteractor: networkingInteractor, kms: cryptoMock, pairingStorage: storageMock) +// } +// +// func testPropose() async { +// let pairing = Pairing.stub() +// let topicA = pairing.topic +// let relayOptions = RelayProtocolOptions(protocol: "", data: nil) +// +// // FIXME: namespace stub +// try! await engine.propose(pairingTopic: pairing.topic, namespaces: ProposalNamespace.stubDictionary(), relay: relayOptions) +// +// guard let publishTopic = networkingInteractor.requests.first?.topic, +// let proposal = try? networkingInteractor.requests.first?.request.params?.get(SessionType.ProposeParams.self) else { +// XCTFail("Proposer must publish a proposal request."); return +// } +// XCTAssert(cryptoMock.hasPrivateKey(for: proposal.proposer.publicKey), "Proposer must store the private key matching the public key sent through the proposal.") +// XCTAssertEqual(publishTopic, topicA) +// } +// +// func testHandleSessionProposeResponse() async { +// let exp = expectation(description: "testHandleSessionProposeResponse") +// let uri = try! await engine.create() +// let pairing = storageMock.getPairing(forTopic: uri.topic)! +// let topicA = pairing.topic +// let relayOptions = RelayProtocolOptions(protocol: "", data: nil) +// +// // Client proposes session +// // FIXME: namespace stub +// try! await engine.propose(pairingTopic: pairing.topic, namespaces: ProposalNamespace.stubDictionary(), relay: relayOptions) +// +// guard let request = networkingInteractor.requests.first?.request, +// let proposal = try? networkingInteractor.requests.first?.request.params?.get(SessionType.ProposeParams.self) else { +// XCTFail("Proposer must publish session proposal request"); return +// } +// +// // Client receives proposal response response +// let responder = Participant.stub() +// let proposalResponse = SessionType.ProposeResponse(relay: relayOptions, responderPublicKey: responder.publicKey) +// +// let response = RPCResponse(id: request.id!, result: RPCResult.response(AnyCodable(proposalResponse))) +// +// networkingInteractor.onSubscribeCalled = { +// exp.fulfill() +// } +// +// networkingInteractor.responsePublisherSubject.send((topicA, request, response)) +// let privateKey = try! cryptoMock.getPrivateKey(for: proposal.proposer.publicKey)! +// let topicB = deriveTopic(publicKey: responder.publicKey, privateKey: privateKey) +// let storedPairing = storageMock.getPairing(forTopic: topicA)! +// +// wait(for: [exp], timeout: 5) +// +// let sessionTopic = networkingInteractor.subscriptions.last! +// +// XCTAssertTrue(networkingInteractor.didCallSubscribe) +// XCTAssert(storedPairing.active) +// XCTAssertEqual(topicB, sessionTopic, "Responder engine calls back with session topic") +// } +// +// func testSessionProposeError() async { +// let uri = try! await engine.create() +// let pairing = storageMock.getPairing(forTopic: uri.topic)! +// let topicA = pairing.topic +// let relayOptions = RelayProtocolOptions(protocol: "", data: nil) +// +// // Client propose session +// // FIXME: namespace stub +// try! await engine.propose(pairingTopic: pairing.topic, namespaces: ProposalNamespace.stubDictionary(), relay: relayOptions) +// +// guard let request = networkingInteractor.requests.first?.request, +// let proposal = try? networkingInteractor.requests.first?.request.params?.get(SessionType.ProposeParams.self) else { +// XCTFail("Proposer must publish session proposal request"); return +// } +// +// let response = RPCResponse.stubError(forRequest: request) +// networkingInteractor.responsePublisherSubject.send((topicA, request, response)) +// +// XCTAssert(networkingInteractor.didUnsubscribe(to: pairing.topic), "Proposer must unsubscribe if pairing is inactive.") +// XCTAssertFalse(storageMock.hasPairing(forTopic: pairing.topic), "Proposer must delete an inactive pairing.") +// XCTAssertFalse(cryptoMock.hasSymmetricKey(for: pairing.topic), "Proposer must delete symmetric key if pairing is inactive.") +// XCTAssertFalse(cryptoMock.hasPrivateKey(for: proposal.proposer.publicKey), "Proposer must remove private key for rejected session") +// } +// +// func testSessionProposeErrorOnActivePairing() async { +// let uri = try! await engine.create() +// let pairing = storageMock.getPairing(forTopic: uri.topic)! +// let topicA = pairing.topic +// let relayOptions = RelayProtocolOptions(protocol: "", data: nil) +// +// // Client propose session +// // FIXME: namespace stub +// try? await engine.propose(pairingTopic: pairing.topic, namespaces: ProposalNamespace.stubDictionary(), relay: relayOptions) +// +// guard let request = networkingInteractor.requests.first?.request, +// let proposal = try? networkingInteractor.requests.first?.request.params?.get(SessionType.ProposeParams.self) else { +// XCTFail("Proposer must publish session proposal request"); return +// } +// +// var storedPairing = storageMock.getPairing(forTopic: topicA)! +// storedPairing.activate() +// storageMock.setPairing(storedPairing) +// +// let response = RPCResponse.stubError(forRequest: request) +// networkingInteractor.responsePublisherSubject.send((topicA, request, response)) +// +// XCTAssertFalse(networkingInteractor.didUnsubscribe(to: pairing.topic), "Proposer must not unsubscribe if pairing is active.") +// XCTAssert(storageMock.hasPairing(forTopic: pairing.topic), "Proposer must not delete an active pairing.") +// XCTAssert(cryptoMock.hasSymmetricKey(for: pairing.topic), "Proposer must not delete symmetric key if pairing is active.") +// XCTAssertFalse(cryptoMock.hasPrivateKey(for: proposal.proposer.publicKey), "Proposer must remove private key for rejected session") +// } +// +// func testPairingExpiration() async { +// let uri = try! await engine.create() +// let pairing = storageMock.getPairing(forTopic: uri.topic)! +// storageMock.onPairingExpiration?(pairing) +// XCTAssertFalse(cryptoMock.hasSymmetricKey(for: uri.topic)) +// XCTAssert(networkingInteractor.didUnsubscribe(to: uri.topic)) +// } +//} diff --git a/Tests/WalletConnectSignTests/ApproveEngineTests.swift b/Tests/WalletConnectSignTests/ApproveEngineTests.swift index 4d0b75a7b..577b28c00 100644 --- a/Tests/WalletConnectSignTests/ApproveEngineTests.swift +++ b/Tests/WalletConnectSignTests/ApproveEngineTests.swift @@ -31,6 +31,7 @@ final class ApproveEngineTests: XCTestCase { networkingInteractor: networkingInteractor, proposalPayloadsStore: proposalPayloadsStore, sessionToPairingTopic: CodableStore(defaults: RuntimeKeyValueStorage(), identifier: ""), + pairingRegisterer: PairingRegistererMock(), metadata: metadata, kms: cryptoMock, logger: ConsoleLoggerMock(), diff --git a/Tests/WalletConnectSignTests/PairingEngineTests.swift b/Tests/WalletConnectSignTests/PairingEngineTests.swift deleted file mode 100644 index 0f99d16d0..000000000 --- a/Tests/WalletConnectSignTests/PairingEngineTests.swift +++ /dev/null @@ -1,188 +0,0 @@ -import XCTest -import Combine -import JSONRPC -@testable import WalletConnectSign -@testable import TestingUtils -@testable import WalletConnectKMS -import WalletConnectUtils - -func deriveTopic(publicKey: String, privateKey: AgreementPrivateKey) -> String { - try! KeyManagementService.generateAgreementKey(from: privateKey, peerPublicKey: publicKey).derivedTopic() -} - -final class PairingEngineTests: XCTestCase { - - var engine: PairingEngine! - var approveEngine: ApproveEngine! - - var networkingInteractor: NetworkingInteractorMock! - var storageMock: WCPairingStorageMock! - var cryptoMock: KeyManagementServiceMock! - - var topicGenerator: TopicGenerator! - var publishers = Set() - - override func setUp() { - networkingInteractor = NetworkingInteractorMock() - storageMock = WCPairingStorageMock() - cryptoMock = KeyManagementServiceMock() - topicGenerator = TopicGenerator() - setupEngines() - } - - override func tearDown() { - networkingInteractor = nil - storageMock = nil - cryptoMock = nil - topicGenerator = nil - engine = nil - approveEngine = nil - } - - func setupEngines() { - let meta = AppMetadata.stub() - let logger = ConsoleLoggerMock() - engine = PairingEngine( - networkingInteractor: networkingInteractor, - kms: cryptoMock, - pairingStore: storageMock, - metadata: meta, - logger: logger, - topicGenerator: topicGenerator.getTopic - ) - approveEngine = ApproveEngine( - networkingInteractor: networkingInteractor, - proposalPayloadsStore: .init(defaults: RuntimeKeyValueStorage(), identifier: ""), - sessionToPairingTopic: CodableStore(defaults: RuntimeKeyValueStorage(), identifier: ""), - metadata: meta, - kms: cryptoMock, - logger: logger, - pairingStore: storageMock, - sessionStore: WCSessionStorageMock() - ) - } - - func testCreate() async { - let uri = try! await engine.create() - XCTAssert(cryptoMock.hasSymmetricKey(for: uri.topic), "Proposer must store the symmetric key matching the URI.") - XCTAssert(storageMock.hasPairing(forTopic: uri.topic), "The engine must store a pairing after creating one") - XCTAssert(networkingInteractor.didSubscribe(to: uri.topic), "Proposer must subscribe to pairing topic.") - XCTAssert(storageMock.getPairing(forTopic: uri.topic)?.active == false, "Recently created pairing must be inactive.") - } - - func testPropose() async { - let pairing = Pairing.stub() - let topicA = pairing.topic - let relayOptions = RelayProtocolOptions(protocol: "", data: nil) - - // FIXME: namespace stub - try! await engine.propose(pairingTopic: pairing.topic, namespaces: ProposalNamespace.stubDictionary(), relay: relayOptions) - - guard let publishTopic = networkingInteractor.requests.first?.topic, - let proposal = try? networkingInteractor.requests.first?.request.params?.get(SessionType.ProposeParams.self) else { - XCTFail("Proposer must publish a proposal request."); return - } - XCTAssert(cryptoMock.hasPrivateKey(for: proposal.proposer.publicKey), "Proposer must store the private key matching the public key sent through the proposal.") - XCTAssertEqual(publishTopic, topicA) - } - - func testHandleSessionProposeResponse() async { - let exp = expectation(description: "testHandleSessionProposeResponse") - let uri = try! await engine.create() - let pairing = storageMock.getPairing(forTopic: uri.topic)! - let topicA = pairing.topic - let relayOptions = RelayProtocolOptions(protocol: "", data: nil) - - // Client proposes session - // FIXME: namespace stub - try! await engine.propose(pairingTopic: pairing.topic, namespaces: ProposalNamespace.stubDictionary(), relay: relayOptions) - - guard let request = networkingInteractor.requests.first?.request, - let proposal = try? networkingInteractor.requests.first?.request.params?.get(SessionType.ProposeParams.self) else { - XCTFail("Proposer must publish session proposal request"); return - } - - // Client receives proposal response response - let responder = Participant.stub() - let proposalResponse = SessionType.ProposeResponse(relay: relayOptions, responderPublicKey: responder.publicKey) - - let response = RPCResponse(id: request.id!, result: RPCResult.response(AnyCodable(proposalResponse))) - - networkingInteractor.onSubscribeCalled = { - exp.fulfill() - } - - networkingInteractor.responsePublisherSubject.send((topicA, request, response)) - let privateKey = try! cryptoMock.getPrivateKey(for: proposal.proposer.publicKey)! - let topicB = deriveTopic(publicKey: responder.publicKey, privateKey: privateKey) - let storedPairing = storageMock.getPairing(forTopic: topicA)! - - wait(for: [exp], timeout: 5) - - let sessionTopic = networkingInteractor.subscriptions.last! - - XCTAssertTrue(networkingInteractor.didCallSubscribe) - XCTAssert(storedPairing.active) - XCTAssertEqual(topicB, sessionTopic, "Responder engine calls back with session topic") - } - - func testSessionProposeError() async { - let uri = try! await engine.create() - let pairing = storageMock.getPairing(forTopic: uri.topic)! - let topicA = pairing.topic - let relayOptions = RelayProtocolOptions(protocol: "", data: nil) - - // Client propose session - // FIXME: namespace stub - try! await engine.propose(pairingTopic: pairing.topic, namespaces: ProposalNamespace.stubDictionary(), relay: relayOptions) - - guard let request = networkingInteractor.requests.first?.request, - let proposal = try? networkingInteractor.requests.first?.request.params?.get(SessionType.ProposeParams.self) else { - XCTFail("Proposer must publish session proposal request"); return - } - - let response = RPCResponse.stubError(forRequest: request) - networkingInteractor.responsePublisherSubject.send((topicA, request, response)) - - XCTAssert(networkingInteractor.didUnsubscribe(to: pairing.topic), "Proposer must unsubscribe if pairing is inactive.") - XCTAssertFalse(storageMock.hasPairing(forTopic: pairing.topic), "Proposer must delete an inactive pairing.") - XCTAssertFalse(cryptoMock.hasSymmetricKey(for: pairing.topic), "Proposer must delete symmetric key if pairing is inactive.") - XCTAssertFalse(cryptoMock.hasPrivateKey(for: proposal.proposer.publicKey), "Proposer must remove private key for rejected session") - } - - func testSessionProposeErrorOnActivePairing() async { - let uri = try! await engine.create() - let pairing = storageMock.getPairing(forTopic: uri.topic)! - let topicA = pairing.topic - let relayOptions = RelayProtocolOptions(protocol: "", data: nil) - - // Client propose session - // FIXME: namespace stub - try? await engine.propose(pairingTopic: pairing.topic, namespaces: ProposalNamespace.stubDictionary(), relay: relayOptions) - - guard let request = networkingInteractor.requests.first?.request, - let proposal = try? networkingInteractor.requests.first?.request.params?.get(SessionType.ProposeParams.self) else { - XCTFail("Proposer must publish session proposal request"); return - } - - var storedPairing = storageMock.getPairing(forTopic: topicA)! - storedPairing.activate() - storageMock.setPairing(storedPairing) - - let response = RPCResponse.stubError(forRequest: request) - networkingInteractor.responsePublisherSubject.send((topicA, request, response)) - - XCTAssertFalse(networkingInteractor.didUnsubscribe(to: pairing.topic), "Proposer must not unsubscribe if pairing is active.") - XCTAssert(storageMock.hasPairing(forTopic: pairing.topic), "Proposer must not delete an active pairing.") - XCTAssert(cryptoMock.hasSymmetricKey(for: pairing.topic), "Proposer must not delete symmetric key if pairing is active.") - XCTAssertFalse(cryptoMock.hasPrivateKey(for: proposal.proposer.publicKey), "Proposer must remove private key for rejected session") - } - - func testPairingExpiration() async { - let uri = try! await engine.create() - let pairing = storageMock.getPairing(forTopic: uri.topic)! - storageMock.onPairingExpiration?(pairing) - XCTAssertFalse(cryptoMock.hasSymmetricKey(for: uri.topic)) - XCTAssert(networkingInteractor.didUnsubscribe(to: uri.topic)) - } -} diff --git a/Tests/WalletConnectSignTests/Stub/Stubs.swift b/Tests/WalletConnectSignTests/Stub/Stubs.swift index 0a15c18d5..3789cdb3e 100644 --- a/Tests/WalletConnectSignTests/Stub/Stubs.swift +++ b/Tests/WalletConnectSignTests/Stub/Stubs.swift @@ -42,12 +42,6 @@ extension SessionNamespace { } } -extension RelayProtocolOptions { - static func stub() -> RelayProtocolOptions { - RelayProtocolOptions(protocol: "", data: nil) - } -} - extension Participant { static func stub(publicKey: String = AgreementPrivateKey().publicKey.hexRepresentation) -> Participant { Participant(publicKey: publicKey, metadata: AppMetadata.stub()) diff --git a/Tests/WalletConnectUtilsTests/WalletConnectURITests.swift b/Tests/WalletConnectUtilsTests/WalletConnectURITests.swift index fc2f8c170..5f65b5c28 100644 --- a/Tests/WalletConnectUtilsTests/WalletConnectURITests.swift +++ b/Tests/WalletConnectUtilsTests/WalletConnectURITests.swift @@ -1,17 +1,15 @@ import XCTest @testable import WalletConnectUtils -private func stubURI(api: WalletConnectURI.TargetAPI? = nil) -> (uri: WalletConnectURI, string: String) { +private func stubURI() -> (uri: WalletConnectURI, string: String) { let topic = Data.randomBytes(count: 32).toHexString() let symKey = Data.randomBytes(count: 32).toHexString() let protocolName = "irn" - let uriBase = api == nil ? "wc:" : "wc:\(api!.rawValue)-" - let uriString = "\(uriBase)\(topic)@2?symKey=\(symKey)&relay-protocol=\(protocolName)" + let uriString = "wc:\(topic)@2?symKey=\(symKey)&relay-protocol=\(protocolName)" let uri = WalletConnectURI( topic: topic, symKey: symKey, - relay: RelayProtocolOptions(protocol: protocolName, data: nil), - api: api) + relay: RelayProtocolOptions(protocol: protocolName, data: nil)) return (uri, uriString) } @@ -42,25 +40,6 @@ final class WalletConnectURITests: XCTestCase { XCTAssertEqual(expectedString, outputURIString) } - // MARK: - Init URI with prefix API identifier - - func testInitFromPrefixedURIString() { - WalletConnectURI.TargetAPI.allCases.forEach { api in - let uriString = stubURI(api: api).string - let uri = WalletConnectURI(string: uriString) - XCTAssertEqual(uri?.api, api) - XCTAssertEqual(uri?.absoluteString, uriString) - } - } - - func testAbsentPrefixFallbackToSign() { - let input = stubURI() - let uriFromParams = input.uri - let uriFromString = WalletConnectURI(string: input.string) - XCTAssertEqual(uriFromParams.api, .sign) - XCTAssertEqual(uriFromString?.api, .sign) - } - // MARK: - Init URI failure cases func testInitFailsBadScheme() { From ab3d84659be42160cf7f817b0119957241c35ec4 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Fri, 7 Oct 2022 18:07:55 +0600 Subject: [PATCH 141/175] Old ApproveEngineTests repaired --- Tests/WalletConnectSignTests/ApproveEngineTests.swift | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Tests/WalletConnectSignTests/ApproveEngineTests.swift b/Tests/WalletConnectSignTests/ApproveEngineTests.swift index 577b28c00..9730deefe 100644 --- a/Tests/WalletConnectSignTests/ApproveEngineTests.swift +++ b/Tests/WalletConnectSignTests/ApproveEngineTests.swift @@ -16,6 +16,7 @@ final class ApproveEngineTests: XCTestCase { var cryptoMock: KeyManagementServiceMock! var pairingStorageMock: WCPairingStorageMock! var sessionStorageMock: WCSessionStorageMock! + var pairingRegisterer: PairingRegistererMock! var proposalPayloadsStore: CodableStore>! var publishers = Set() @@ -26,12 +27,13 @@ final class ApproveEngineTests: XCTestCase { cryptoMock = KeyManagementServiceMock() pairingStorageMock = WCPairingStorageMock() sessionStorageMock = WCSessionStorageMock() + pairingRegisterer = PairingRegistererMock() proposalPayloadsStore = CodableStore>(defaults: RuntimeKeyValueStorage(), identifier: "") engine = ApproveEngine( networkingInteractor: networkingInteractor, proposalPayloadsStore: proposalPayloadsStore, sessionToPairingTopic: CodableStore(defaults: RuntimeKeyValueStorage(), identifier: ""), - pairingRegisterer: PairingRegistererMock(), + pairingRegisterer: pairingRegisterer, metadata: metadata, kms: cryptoMock, logger: ConsoleLoggerMock(), @@ -44,6 +46,7 @@ final class ApproveEngineTests: XCTestCase { networkingInteractor = nil metadata = nil cryptoMock = nil + pairingRegisterer = nil pairingStorageMock = nil engine = nil } @@ -55,8 +58,7 @@ final class ApproveEngineTests: XCTestCase { pairingStorageMock.setPairing(pairing) let proposerPubKey = AgreementPrivateKey().publicKey.hexRepresentation let proposal = SessionProposal.stub(proposerPubKey: proposerPubKey) - let request = RPCRequest(method: SessionProposeProtocolMethod().method, params: proposal) - networkingInteractor.requestPublisherSubject.send((topicA, request)) + pairingRegisterer.subject.send(RequestSubscriptionPayload(id: RPCID("id"), topic: topicA, request: proposal)) try await engine.approveProposal(proposerPubKey: proposal.proposer.publicKey, validating: SessionNamespace.stubDictionary()) @@ -76,13 +78,12 @@ final class ApproveEngineTests: XCTestCase { var sessionProposed = false let proposerPubKey = AgreementPrivateKey().publicKey.hexRepresentation let proposal = SessionProposal.stub(proposerPubKey: proposerPubKey) - let request = RPCRequest(method: SessionProposeProtocolMethod().method, params: proposal) engine.onSessionProposal = { _ in sessionProposed = true } - networkingInteractor.requestPublisherSubject.send((topicA, request)) + pairingRegisterer.subject.send(RequestSubscriptionPayload(id: RPCID("id"), topic: topicA, request: proposal)) XCTAssertNotNil(try! proposalPayloadsStore.get(key: proposal.proposer.publicKey), "Proposer must store proposal payload") XCTAssertTrue(sessionProposed) } From 83c58b3d9ba5c5ce3eb1d5e13ab0099dd92ee31f Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Fri, 7 Oct 2022 18:25:35 +0600 Subject: [PATCH 142/175] Unit tests repaired and restructured --- .../Services/Common/ExpirationService.swift | 4 +- .../ExpirationServiceTests.swift | 40 ++++ .../remove_this.swift | 156 --------------- .../AppProposalServiceTests.swift | 182 ++++++++++++++++++ 4 files changed, 224 insertions(+), 158 deletions(-) create mode 100644 Tests/WalletConnectPairingTests/ExpirationServiceTests.swift delete mode 100644 Tests/WalletConnectPairingTests/remove_this.swift create mode 100644 Tests/WalletConnectSignTests/AppProposalServiceTests.swift diff --git a/Sources/WalletConnectPairing/Services/Common/ExpirationService.swift b/Sources/WalletConnectPairing/Services/Common/ExpirationService.swift index 22e3e7166..d973e093a 100644 --- a/Sources/WalletConnectPairing/Services/Common/ExpirationService.swift +++ b/Sources/WalletConnectPairing/Services/Common/ExpirationService.swift @@ -3,11 +3,11 @@ import WalletConnectNetworking import WalletConnectKMS final class ExpirationService { - private let pairingStorage: PairingStorage + private let pairingStorage: WCPairingStorage private let networkInteractor: NetworkInteracting private let kms: KeyManagementServiceProtocol - init(pairingStorage: PairingStorage, networkInteractor: NetworkInteracting, kms: KeyManagementServiceProtocol) { + init(pairingStorage: WCPairingStorage, networkInteractor: NetworkInteracting, kms: KeyManagementServiceProtocol) { self.pairingStorage = pairingStorage self.networkInteractor = networkInteractor self.kms = kms diff --git a/Tests/WalletConnectPairingTests/ExpirationServiceTests.swift b/Tests/WalletConnectPairingTests/ExpirationServiceTests.swift new file mode 100644 index 000000000..79b8b20d6 --- /dev/null +++ b/Tests/WalletConnectPairingTests/ExpirationServiceTests.swift @@ -0,0 +1,40 @@ +import XCTest +@testable import WalletConnectPairing +@testable import TestingUtils +@testable import WalletConnectKMS +import WalletConnectUtils +import WalletConnectNetworking + +final class ExpirationServiceTestsTests: XCTestCase { + + var service: ExpirationService! + var appPairService: AppPairService! + var networkingInteractor: NetworkingInteractorMock! + var storageMock: WCPairingStorageMock! + var cryptoMock: KeyManagementServiceMock! + + override func setUp() { + networkingInteractor = NetworkingInteractorMock() + storageMock = WCPairingStorageMock() + cryptoMock = KeyManagementServiceMock() + service = ExpirationService( + pairingStorage: storageMock, + networkInteractor: networkingInteractor, + kms: cryptoMock + ) + appPairService = AppPairService( + networkingInteractor: networkingInteractor, + kms: cryptoMock, + pairingStorage: storageMock + ) + } + + func testPairingExpiration() async { + let uri = try! await appPairService.create() + let pairing = storageMock.getPairing(forTopic: uri.topic)! + service.setupExpirationHandling() + storageMock.onPairingExpiration?(pairing) + XCTAssertFalse(cryptoMock.hasSymmetricKey(for: uri.topic)) + XCTAssert(networkingInteractor.didUnsubscribe(to: uri.topic)) + } +} diff --git a/Tests/WalletConnectPairingTests/remove_this.swift b/Tests/WalletConnectPairingTests/remove_this.swift deleted file mode 100644 index 7978af71a..000000000 --- a/Tests/WalletConnectPairingTests/remove_this.swift +++ /dev/null @@ -1,156 +0,0 @@ -//import XCTest -//import Combine -//import JSONRPC -//@testable import WalletConnectPairing -//@testable import TestingUtils -//@testable import WalletConnectKMS -//import WalletConnectUtils -// -//func deriveTopic(publicKey: String, privateKey: AgreementPrivateKey) -> String { -// try! KeyManagementService.generateAgreementKey(from: privateKey, peerPublicKey: publicKey).derivedTopic() -//} -// -//final class WalletPairServiceTests: XCTestCase { -// -// var service: WalletPairService! -// -// var networkingInteractor: NetworkingInteractorMock! -// var storageMock: WCPairingStorageMock! -// var cryptoMock: KeyManagementServiceMock! -// -// var publishers = Set() -// -// override func setUp() { -// networkingInteractor = NetworkingInteractorMock() -// storageMock = WCPairingStorageMock() -// cryptoMock = KeyManagementServiceMock() -// setupEngines() -// } -// -// override func tearDown() { -// networkingInteractor = nil -// storageMock = nil -// cryptoMock = nil -// service = nil -// } -// -// func setupEngines() { -// service = WalletPairService(networkingInteractor: networkingInteractor, kms: cryptoMock, pairingStorage: storageMock) -// } -// -// func testPropose() async { -// let pairing = Pairing.stub() -// let topicA = pairing.topic -// let relayOptions = RelayProtocolOptions(protocol: "", data: nil) -// -// // FIXME: namespace stub -// try! await engine.propose(pairingTopic: pairing.topic, namespaces: ProposalNamespace.stubDictionary(), relay: relayOptions) -// -// guard let publishTopic = networkingInteractor.requests.first?.topic, -// let proposal = try? networkingInteractor.requests.first?.request.params?.get(SessionType.ProposeParams.self) else { -// XCTFail("Proposer must publish a proposal request."); return -// } -// XCTAssert(cryptoMock.hasPrivateKey(for: proposal.proposer.publicKey), "Proposer must store the private key matching the public key sent through the proposal.") -// XCTAssertEqual(publishTopic, topicA) -// } -// -// func testHandleSessionProposeResponse() async { -// let exp = expectation(description: "testHandleSessionProposeResponse") -// let uri = try! await engine.create() -// let pairing = storageMock.getPairing(forTopic: uri.topic)! -// let topicA = pairing.topic -// let relayOptions = RelayProtocolOptions(protocol: "", data: nil) -// -// // Client proposes session -// // FIXME: namespace stub -// try! await engine.propose(pairingTopic: pairing.topic, namespaces: ProposalNamespace.stubDictionary(), relay: relayOptions) -// -// guard let request = networkingInteractor.requests.first?.request, -// let proposal = try? networkingInteractor.requests.first?.request.params?.get(SessionType.ProposeParams.self) else { -// XCTFail("Proposer must publish session proposal request"); return -// } -// -// // Client receives proposal response response -// let responder = Participant.stub() -// let proposalResponse = SessionType.ProposeResponse(relay: relayOptions, responderPublicKey: responder.publicKey) -// -// let response = RPCResponse(id: request.id!, result: RPCResult.response(AnyCodable(proposalResponse))) -// -// networkingInteractor.onSubscribeCalled = { -// exp.fulfill() -// } -// -// networkingInteractor.responsePublisherSubject.send((topicA, request, response)) -// let privateKey = try! cryptoMock.getPrivateKey(for: proposal.proposer.publicKey)! -// let topicB = deriveTopic(publicKey: responder.publicKey, privateKey: privateKey) -// let storedPairing = storageMock.getPairing(forTopic: topicA)! -// -// wait(for: [exp], timeout: 5) -// -// let sessionTopic = networkingInteractor.subscriptions.last! -// -// XCTAssertTrue(networkingInteractor.didCallSubscribe) -// XCTAssert(storedPairing.active) -// XCTAssertEqual(topicB, sessionTopic, "Responder engine calls back with session topic") -// } -// -// func testSessionProposeError() async { -// let uri = try! await engine.create() -// let pairing = storageMock.getPairing(forTopic: uri.topic)! -// let topicA = pairing.topic -// let relayOptions = RelayProtocolOptions(protocol: "", data: nil) -// -// // Client propose session -// // FIXME: namespace stub -// try! await engine.propose(pairingTopic: pairing.topic, namespaces: ProposalNamespace.stubDictionary(), relay: relayOptions) -// -// guard let request = networkingInteractor.requests.first?.request, -// let proposal = try? networkingInteractor.requests.first?.request.params?.get(SessionType.ProposeParams.self) else { -// XCTFail("Proposer must publish session proposal request"); return -// } -// -// let response = RPCResponse.stubError(forRequest: request) -// networkingInteractor.responsePublisherSubject.send((topicA, request, response)) -// -// XCTAssert(networkingInteractor.didUnsubscribe(to: pairing.topic), "Proposer must unsubscribe if pairing is inactive.") -// XCTAssertFalse(storageMock.hasPairing(forTopic: pairing.topic), "Proposer must delete an inactive pairing.") -// XCTAssertFalse(cryptoMock.hasSymmetricKey(for: pairing.topic), "Proposer must delete symmetric key if pairing is inactive.") -// XCTAssertFalse(cryptoMock.hasPrivateKey(for: proposal.proposer.publicKey), "Proposer must remove private key for rejected session") -// } -// -// func testSessionProposeErrorOnActivePairing() async { -// let uri = try! await engine.create() -// let pairing = storageMock.getPairing(forTopic: uri.topic)! -// let topicA = pairing.topic -// let relayOptions = RelayProtocolOptions(protocol: "", data: nil) -// -// // Client propose session -// // FIXME: namespace stub -// try? await engine.propose(pairingTopic: pairing.topic, namespaces: ProposalNamespace.stubDictionary(), relay: relayOptions) -// -// guard let request = networkingInteractor.requests.first?.request, -// let proposal = try? networkingInteractor.requests.first?.request.params?.get(SessionType.ProposeParams.self) else { -// XCTFail("Proposer must publish session proposal request"); return -// } -// -// var storedPairing = storageMock.getPairing(forTopic: topicA)! -// storedPairing.activate() -// storageMock.setPairing(storedPairing) -// -// let response = RPCResponse.stubError(forRequest: request) -// networkingInteractor.responsePublisherSubject.send((topicA, request, response)) -// -// XCTAssertFalse(networkingInteractor.didUnsubscribe(to: pairing.topic), "Proposer must not unsubscribe if pairing is active.") -// XCTAssert(storageMock.hasPairing(forTopic: pairing.topic), "Proposer must not delete an active pairing.") -// XCTAssert(cryptoMock.hasSymmetricKey(for: pairing.topic), "Proposer must not delete symmetric key if pairing is active.") -// XCTAssertFalse(cryptoMock.hasPrivateKey(for: proposal.proposer.publicKey), "Proposer must remove private key for rejected session") -// } -// -// func testPairingExpiration() async { -// let uri = try! await engine.create() -// let pairing = storageMock.getPairing(forTopic: uri.topic)! -// storageMock.onPairingExpiration?(pairing) -// XCTAssertFalse(cryptoMock.hasSymmetricKey(for: uri.topic)) -// XCTAssert(networkingInteractor.didUnsubscribe(to: uri.topic)) -// } -//} diff --git a/Tests/WalletConnectSignTests/AppProposalServiceTests.swift b/Tests/WalletConnectSignTests/AppProposalServiceTests.swift new file mode 100644 index 000000000..de37d08cf --- /dev/null +++ b/Tests/WalletConnectSignTests/AppProposalServiceTests.swift @@ -0,0 +1,182 @@ +import XCTest +import Combine +import JSONRPC +@testable import WalletConnectSign +@testable import TestingUtils +@testable import WalletConnectKMS +@testable import WalletConnectPairing +import WalletConnectUtils + +func deriveTopic(publicKey: String, privateKey: AgreementPrivateKey) -> String { + try! KeyManagementService.generateAgreementKey(from: privateKey, peerPublicKey: publicKey).derivedTopic() +} + +final class AppProposalServiceTests: XCTestCase { + + var service: AppProposeService! + + var appPairService: AppPairService! + var pairingRegisterer: PairingRegistererMock! + var approveEngine: ApproveEngine! + + var networkingInteractor: NetworkingInteractorMock! + var storageMock: WCPairingStorageMock! + var cryptoMock: KeyManagementServiceMock! + + var topicGenerator: TopicGenerator! + var publishers = Set() + + override func setUp() { + networkingInteractor = NetworkingInteractorMock() + storageMock = WCPairingStorageMock() + cryptoMock = KeyManagementServiceMock() + topicGenerator = TopicGenerator() + pairingRegisterer = PairingRegistererMock() + setupServices() + } + + override func tearDown() { + networkingInteractor = nil + storageMock = nil + cryptoMock = nil + topicGenerator = nil + pairingRegisterer = nil + approveEngine = nil + } + + func setupServices() { + let meta = AppMetadata.stub() + let logger = ConsoleLoggerMock() + + appPairService = AppPairService( + networkingInteractor: networkingInteractor, + kms: cryptoMock, + pairingStorage: storageMock + ) + service = AppProposeService( + metadata: .stub(), + networkingInteractor: networkingInteractor, + kms: cryptoMock, + logger: logger + ) + approveEngine = ApproveEngine( + networkingInteractor: networkingInteractor, + proposalPayloadsStore: .init(defaults: RuntimeKeyValueStorage(), identifier: ""), + sessionToPairingTopic: CodableStore(defaults: RuntimeKeyValueStorage(), identifier: ""), + pairingRegisterer: pairingRegisterer, + metadata: meta, + kms: cryptoMock, + logger: logger, + pairingStore: storageMock, + sessionStore: WCSessionStorageMock() + ) + } + + func testPropose() async { + let pairing = Pairing.stub() + let topicA = pairing.topic + let relayOptions = RelayProtocolOptions(protocol: "", data: nil) + + // FIXME: namespace stub + try! await service.propose(pairingTopic: pairing.topic, namespaces: ProposalNamespace.stubDictionary(), relay: relayOptions) + + guard let publishTopic = networkingInteractor.requests.first?.topic, + let proposal = try? networkingInteractor.requests.first?.request.params?.get(SessionType.ProposeParams.self) else { + XCTFail("Proposer must publish a proposal request."); return + } + XCTAssert(cryptoMock.hasPrivateKey(for: proposal.proposer.publicKey), "Proposer must store the private key matching the public key sent through the proposal.") + XCTAssertEqual(publishTopic, topicA) + } + + func testHandleSessionProposeResponse() async { + let exp = expectation(description: "testHandleSessionProposeResponse") + let uri = try! await appPairService.create() + let pairing = storageMock.getPairing(forTopic: uri.topic)! + let topicA = pairing.topic + let relayOptions = RelayProtocolOptions(protocol: "", data: nil) + + // Client proposes session + // FIXME: namespace stub + try! await service.propose(pairingTopic: pairing.topic, namespaces: ProposalNamespace.stubDictionary(), relay: relayOptions) + + guard let request = networkingInteractor.requests.first?.request, + let proposal = try? networkingInteractor.requests.first?.request.params?.get(SessionType.ProposeParams.self) else { + XCTFail("Proposer must publish session proposal request"); return + } + + // Client receives proposal response response + let responder = Participant.stub() + let proposalResponse = SessionType.ProposeResponse(relay: relayOptions, responderPublicKey: responder.publicKey) + + let response = RPCResponse(id: request.id!, result: RPCResult.response(AnyCodable(proposalResponse))) + + networkingInteractor.onSubscribeCalled = { + exp.fulfill() + } + + networkingInteractor.responsePublisherSubject.send((topicA, request, response)) + let privateKey = try! cryptoMock.getPrivateKey(for: proposal.proposer.publicKey)! + let topicB = deriveTopic(publicKey: responder.publicKey, privateKey: privateKey) + let storedPairing = storageMock.getPairing(forTopic: topicA)! + + wait(for: [exp], timeout: 5) + + let sessionTopic = networkingInteractor.subscriptions.last! + + XCTAssertTrue(networkingInteractor.didCallSubscribe) + XCTAssert(storedPairing.active) + XCTAssertEqual(topicB, sessionTopic, "Responder engine calls back with session topic") + } + + func testSessionProposeError() async { + let uri = try! await appPairService.create() + let pairing = storageMock.getPairing(forTopic: uri.topic)! + let topicA = pairing.topic + let relayOptions = RelayProtocolOptions(protocol: "", data: nil) + + // Client propose session + // FIXME: namespace stub + try! await service.propose(pairingTopic: pairing.topic, namespaces: ProposalNamespace.stubDictionary(), relay: relayOptions) + + guard let request = networkingInteractor.requests.first?.request, + let proposal = try? networkingInteractor.requests.first?.request.params?.get(SessionType.ProposeParams.self) else { + XCTFail("Proposer must publish session proposal request"); return + } + + let response = RPCResponse.stubError(forRequest: request) + networkingInteractor.responsePublisherSubject.send((topicA, request, response)) + + XCTAssert(networkingInteractor.didUnsubscribe(to: pairing.topic), "Proposer must unsubscribe if pairing is inactive.") + XCTAssertFalse(storageMock.hasPairing(forTopic: pairing.topic), "Proposer must delete an inactive pairing.") + XCTAssertFalse(cryptoMock.hasSymmetricKey(for: pairing.topic), "Proposer must delete symmetric key if pairing is inactive.") + XCTAssertFalse(cryptoMock.hasPrivateKey(for: proposal.proposer.publicKey), "Proposer must remove private key for rejected session") + } + + func testSessionProposeErrorOnActivePairing() async { + let uri = try! await appPairService.create() + let pairing = storageMock.getPairing(forTopic: uri.topic)! + let topicA = pairing.topic + let relayOptions = RelayProtocolOptions(protocol: "", data: nil) + + // Client propose session + // FIXME: namespace stub + try? await service.propose(pairingTopic: pairing.topic, namespaces: ProposalNamespace.stubDictionary(), relay: relayOptions) + + guard let request = networkingInteractor.requests.first?.request, + let proposal = try? networkingInteractor.requests.first?.request.params?.get(SessionType.ProposeParams.self) else { + XCTFail("Proposer must publish session proposal request"); return + } + + var storedPairing = storageMock.getPairing(forTopic: topicA)! + storedPairing.activate() + storageMock.setPairing(storedPairing) + + let response = RPCResponse.stubError(forRequest: request) + networkingInteractor.responsePublisherSubject.send((topicA, request, response)) + + XCTAssertFalse(networkingInteractor.didUnsubscribe(to: pairing.topic), "Proposer must not unsubscribe if pairing is active.") + XCTAssert(storageMock.hasPairing(forTopic: pairing.topic), "Proposer must not delete an active pairing.") + XCTAssert(cryptoMock.hasSymmetricKey(for: pairing.topic), "Proposer must not delete symmetric key if pairing is active.") + XCTAssertFalse(cryptoMock.hasPrivateKey(for: proposal.proposer.publicKey), "Proposer must remove private key for rejected session") + } +} From 199b6d1c0c13791df435f0b3c6956f317420b904 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Fri, 7 Oct 2022 18:36:07 +0600 Subject: [PATCH 143/175] Update metadata service --- .../WalletConnectPairing/PairingClient.swift | 17 ++++++----------- .../PairingClientFactory.swift | 2 ++ .../PairingRegisterer.swift | 1 + .../Services/App/AppUpdateMetadataService.swift | 16 ++++++++++++++++ .../Engine/Common/ApproveEngine.swift | 8 +------- .../Mocks/PairingRegistererMock.swift | 4 ++++ 6 files changed, 30 insertions(+), 18 deletions(-) create mode 100644 Sources/WalletConnectPairing/Services/App/AppUpdateMetadataService.swift diff --git a/Sources/WalletConnectPairing/PairingClient.swift b/Sources/WalletConnectPairing/PairingClient.swift index 39d2d1be1..a73bdb010 100644 --- a/Sources/WalletConnectPairing/PairingClient.swift +++ b/Sources/WalletConnectPairing/PairingClient.swift @@ -14,6 +14,7 @@ public class PairingClient: PairingRegisterer { private let walletPairService: WalletPairService private let appPairService: AppPairService private let appPairActivateService: AppPairActivationService + private let appUpdateMetadataService: AppUpdateMetadataService private var pingResponsePublisherSubject = PassthroughSubject() private let logger: ConsoleLogging private let pingService: PairingPingService @@ -35,6 +36,7 @@ public class PairingClient: PairingRegisterer { expirationService: ExpirationService, pairingRequestsSubscriber: PairingRequestsSubscriber, appPairActivateService: AppPairActivationService, + appUpdateMetadataService: AppUpdateMetadataService, cleanupService: CleanupService, pingService: PairingPingService, socketConnectionStatusPublisher: AnyPublisher, @@ -47,6 +49,7 @@ public class PairingClient: PairingRegisterer { self.logger = logger self.deletePairingService = deletePairingService self.appPairActivateService = appPairActivateService + self.appUpdateMetadataService = appUpdateMetadataService self.resubscribeService = resubscribeService self.expirationService = expirationService self.cleanupService = cleanupService @@ -82,16 +85,12 @@ public class PairingClient: PairingRegisterer { return try await appPairService.create() } - public func activate(_ topic: String) { - - } - - public func updateExpiry(_ topic: String) { - + public func activate(pairingTopic: String) { + appPairActivateService.activate(for: pairingTopic) } public func updateMetadata(_ topic: String, metadata: AppMetadata) { - + appUpdateMetadataService.updatePairingMetadata(topic: topic, metadata: metadata) } public func getPairings() -> [Pairing] { @@ -119,10 +118,6 @@ public class PairingClient: PairingRegisterer { return pairingRequestsSubscriber.subscribeForRequest(method) } - public func activate(pairingTopic: String) { - appPairActivateService.activate(for: pairingTopic) - } - #if DEBUG /// Delete all stored data such as: pairings, keys /// diff --git a/Sources/WalletConnectPairing/PairingClientFactory.swift b/Sources/WalletConnectPairing/PairingClientFactory.swift index b9acaa95f..b95a435ea 100644 --- a/Sources/WalletConnectPairing/PairingClientFactory.swift +++ b/Sources/WalletConnectPairing/PairingClientFactory.swift @@ -24,6 +24,7 @@ public struct PairingClientFactory { let deletePairingService = DeletePairingService(networkingInteractor: networkingClient, kms: kms, pairingStorage: pairingStore, logger: logger) let pingService = PairingPingService(pairingStorage: pairingStore, networkingInteractor: networkingClient, logger: logger) let appPairActivateService = AppPairActivationService(pairingStorage: pairingStore, logger: logger) + let appUpdateMetadataService = AppUpdateMetadataService(pairingStore: pairingStore) let expirationService = ExpirationService(pairingStorage: pairingStore, networkInteractor: networkingClient, kms: kms) let resubscribeService = ResubscribeService(networkInteractor: networkingClient, pairingStorage: pairingStore) @@ -37,6 +38,7 @@ public struct PairingClientFactory { expirationService: expirationService, pairingRequestsSubscriber: pairingRequestsSubscriber, appPairActivateService: appPairActivateService, + appUpdateMetadataService: appUpdateMetadataService, cleanupService: cleanupService, pingService: pingService, socketConnectionStatusPublisher: networkingClient.socketConnectionStatusPublisher, diff --git a/Sources/WalletConnectPairing/PairingRegisterer.swift b/Sources/WalletConnectPairing/PairingRegisterer.swift index b5d6dc36a..e6acb31e6 100644 --- a/Sources/WalletConnectPairing/PairingRegisterer.swift +++ b/Sources/WalletConnectPairing/PairingRegisterer.swift @@ -10,4 +10,5 @@ public protocol PairingRegisterer { func activate(pairingTopic: String) func validatePairingExistance(_ topic: String) throws + func updateMetadata(_ topic: String, metadata: AppMetadata) } diff --git a/Sources/WalletConnectPairing/Services/App/AppUpdateMetadataService.swift b/Sources/WalletConnectPairing/Services/App/AppUpdateMetadataService.swift new file mode 100644 index 000000000..4901e567a --- /dev/null +++ b/Sources/WalletConnectPairing/Services/App/AppUpdateMetadataService.swift @@ -0,0 +1,16 @@ +import Foundation + +final class AppUpdateMetadataService { + + private let pairingStore: WCPairingStorage + + init(pairingStore: WCPairingStorage) { + self.pairingStore = pairingStore + } + + func updatePairingMetadata(topic: String, metadata: AppMetadata) { + guard var pairing = pairingStore.getPairing(forTopic: topic) else { return } + pairing.peerMetadata = metadata + pairingStore.setPairing(pairing) + } +} diff --git a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift index 00f2f1fac..9f8fcbcf1 100644 --- a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift @@ -203,12 +203,6 @@ private extension ApproveEngine { } } - func updatePairingMetadata(topic: String, metadata: AppMetadata) { - guard var pairing = pairingStore.getPairing(forTopic: topic) else { return } - pairing.peerMetadata = metadata - pairingStore.setPairing(pairing) - } - // MARK: SessionProposeResponse // TODO: Move to Non-Controller SettleEngine func handleSessionProposeResponse(payload: ResponseSubscriptionPayload) { @@ -334,7 +328,7 @@ private extension ApproveEngine { metadata: metadata ) if let pairingTopic = try? sessionToPairingTopic.get(key: topic) { - updatePairingMetadata(topic: pairingTopic, metadata: params.controller.metadata) + pairingRegisterer.updateMetadata(pairingTopic, metadata: params.controller.metadata) } let session = WCSession( diff --git a/Tests/TestingUtils/Mocks/PairingRegistererMock.swift b/Tests/TestingUtils/Mocks/PairingRegistererMock.swift index 7d515cbb9..1bea7c188 100644 --- a/Tests/TestingUtils/Mocks/PairingRegistererMock.swift +++ b/Tests/TestingUtils/Mocks/PairingRegistererMock.swift @@ -20,4 +20,8 @@ public class PairingRegistererMock: PairingRegisterer where Reque public func validatePairingExistance(_ topic: String) throws { } + + public func updateMetadata(_ topic: String, metadata: AppMetadata) { + + } } From 4c06a6ea3d0289f841e8fcaaaed3886a85e563ac Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 10 Oct 2022 11:12:47 -0500 Subject: [PATCH 144/175] update wallet to use pair --- Example/ExampleApp/SceneDelegate.swift | 3 ++- Example/ExampleApp/Wallet/WalletViewController.swift | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Example/ExampleApp/SceneDelegate.swift b/Example/ExampleApp/SceneDelegate.swift index 1537a309b..a6a854e87 100644 --- a/Example/ExampleApp/SceneDelegate.swift +++ b/Example/ExampleApp/SceneDelegate.swift @@ -4,6 +4,7 @@ import Combine import WalletConnectSign import WalletConnectNetworking import WalletConnectRelay +import WalletConnectPairing import Starscream extension WebSocket: WebSocketConnecting { } @@ -27,7 +28,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { icons: ["https://avatars.githubusercontent.com/u/37784886"]) Networking.configure(projectId: InputConfig.projectId, socketFactory: SocketFactory()) - Sign.configure(metadata: metadata) + Pair.configure(metadata: metadata) #if DEBUG if CommandLine.arguments.contains("-cleanInstall") { try? Sign.instance.cleanup() diff --git a/Example/ExampleApp/Wallet/WalletViewController.swift b/Example/ExampleApp/Wallet/WalletViewController.swift index d53343480..a888df7bd 100644 --- a/Example/ExampleApp/Wallet/WalletViewController.swift +++ b/Example/ExampleApp/Wallet/WalletViewController.swift @@ -1,6 +1,7 @@ import UIKit import WalletConnectSign import WalletConnectUtils +import WalletConnectPairing import WalletConnectRouter import Web3 import CryptoSwift @@ -122,7 +123,7 @@ final class WalletViewController: UIViewController { print("[WALLET] Pairing to: \(uri)") Task { do { - try await Sign.instance.pair(uri: uri) + try await Pair.instance.pair(uri: uri) } catch { print("[DAPP] Pairing connect error: \(error)") } From b6f87b404addc1435cbb30a80ae025a8fe160d97 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 10 Oct 2022 11:36:55 -0500 Subject: [PATCH 145/175] add new connect method to sign client --- .../WalletConnectSign/Sign/SignClient.swift | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Sources/WalletConnectSign/Sign/SignClient.swift b/Sources/WalletConnectSign/Sign/SignClient.swift index 6ef0b254c..2229fb1f8 100644 --- a/Sources/WalletConnectSign/Sign/SignClient.swift +++ b/Sources/WalletConnectSign/Sign/SignClient.swift @@ -165,6 +165,7 @@ public final class SignClient { /// - requiredNamespaces: required namespaces for a session /// - topic: Optional parameter - use it if you already have an established pairing with peer client. /// - Returns: Pairing URI that should be shared with responder out of bound. Common way is to present it as a QR code. Pairing URI will be nil if you are going to establish a session on existing Pairing and `topic` function parameter was provided. + @available(*, deprecated, message: "use Pair.instance.create() and connect(requiredNamespaces: [String: ProposalNamespace]): instead") public func connect(requiredNamespaces: [String: ProposalNamespace], topic: String? = nil) async throws -> WalletConnectURI? { logger.debug("Connecting Application") if let topic = topic { @@ -186,6 +187,21 @@ public final class SignClient { } } + /// For a dApp to propose a session to a wallet. + /// Function will propose a session on existing pairing. + /// - Parameters: + /// - requiredNamespaces: required namespaces for a session + /// - topic: pairing topic + public func connect(requiredNamespaces: [String: ProposalNamespace], topic: String) async throws { + logger.debug("Connecting Application") + try pairingClient.validatePairingExistance(topic) + try await appProposeService.propose( + pairingTopic: topic, + namespaces: requiredNamespaces, + relay: RelayProtocolOptions(protocol: "irn", data: nil) + ) + } + /// For wallet to receive a session proposal from a dApp /// Responder should call this function in order to accept peer's pairing and be able to subscribe for future session proposals. /// - Parameter uri: Pairing URI that is commonly presented as a QR code by a dapp. @@ -193,6 +209,7 @@ public final class SignClient { /// Should Error: /// - When URI has invalid format or missing params /// - When topic is already in use + @available(*, deprecated, message: "use Pair.instance.pair(uri: WalletConnectURI): instead") public func pair(uri: WalletConnectURI) async throws { try await pairingClient.pair(uri: uri) } @@ -297,6 +314,7 @@ public final class SignClient { /// Query pairings /// - Returns: All pairings + @available(*, deprecated, message: "use Pair.instance.getPairings(uri: WalletConnectURI): instead") public func getPairings() -> [Pairing] { pairingClient.getPairings() } From 1100a267e4249bbe1bc66f2a17a9b044a5ee42f9 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 10 Oct 2022 11:47:19 -0500 Subject: [PATCH 146/175] update ping method --- Sources/WalletConnectSign/Sign/SignClient.swift | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/Sources/WalletConnectSign/Sign/SignClient.swift b/Sources/WalletConnectSign/Sign/SignClient.swift index 2229fb1f8..85880c096 100644 --- a/Sources/WalletConnectSign/Sign/SignClient.swift +++ b/Sources/WalletConnectSign/Sign/SignClient.swift @@ -13,6 +13,9 @@ import WalletConnectPairing /// /// Access via `Sign.instance` public final class SignClient { + enum Errors: Error { + case sessionForTopicNotFound + } // MARK: - Public Properties @@ -268,17 +271,12 @@ public final class SignClient { /// /// Should Error: /// - When the session topic is not found - /// - When the response is neither result or error /// /// - Parameters: - /// - topic: Topic of a session or a pairing - /// - completion: Result will be success on response or an error + /// - topic: Topic of a session public func ping(topic: String) async throws { - if let _ = try? pairingClient.validatePairingExistance(topic) { - try await pairingPingService.ping(topic: topic) - } else if sessionEngine.hasSession(for: topic) { - try await sessionPingService.ping(topic: topic) - } + guard sessionEngine.hasSession(for: topic) else { Errors.sessionForTopicNotFound } + try await sessionPingService.ping(topic: topic) } /// For the wallet to emit an event to a dApp From c39144d180b5755fabfe82c2eab83eb6fd1628b9 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 10 Oct 2022 11:55:01 -0500 Subject: [PATCH 147/175] update sign disconnect method --- .../Services/DisconnectService.swift | 14 ++++---------- Sources/WalletConnectSign/Sign/SignClient.swift | 2 +- .../WalletConnectSign/Sign/SignClientFactory.swift | 2 +- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/Sources/WalletConnectSign/Services/DisconnectService.swift b/Sources/WalletConnectSign/Services/DisconnectService.swift index b4975c749..a0fab00e8 100644 --- a/Sources/WalletConnectSign/Services/DisconnectService.swift +++ b/Sources/WalletConnectSign/Services/DisconnectService.swift @@ -1,30 +1,24 @@ import Foundation -import WalletConnectPairing class DisconnectService { enum Errors: Error { - case objectForTopicNotFound + case sessionForTopicNotFound } private let deleteSessionService: DeleteSessionService private let sessionStorage: WCSessionStorage - private let pairingClient: PairingClient init(deleteSessionService: DeleteSessionService, - sessionStorage: WCSessionStorage, - pairingClient: PairingClient) { + sessionStorage: WCSessionStorage) { self.deleteSessionService = deleteSessionService self.sessionStorage = sessionStorage - self.pairingClient = pairingClient } func disconnect(topic: String) async throws { - if let _ = try? pairingClient.getPairing(for: topic) { - try await pairingClient.disconnect(topic: topic) - } else if sessionStorage.hasSession(forTopic: topic) { + if sessionStorage.hasSession(forTopic: topic) { try await deleteSessionService.delete(topic: topic) } else { - throw Errors.objectForTopicNotFound + throw Errors.sessionForTopicNotFound } } } diff --git a/Sources/WalletConnectSign/Sign/SignClient.swift b/Sources/WalletConnectSign/Sign/SignClient.swift index 85880c096..9febde303 100644 --- a/Sources/WalletConnectSign/Sign/SignClient.swift +++ b/Sources/WalletConnectSign/Sign/SignClient.swift @@ -275,7 +275,7 @@ public final class SignClient { /// - Parameters: /// - topic: Topic of a session public func ping(topic: String) async throws { - guard sessionEngine.hasSession(for: topic) else { Errors.sessionForTopicNotFound } + guard sessionEngine.hasSession(for: topic) else { throw Errors.sessionForTopicNotFound } try await sessionPingService.ping(topic: topic) } diff --git a/Sources/WalletConnectSign/Sign/SignClientFactory.swift b/Sources/WalletConnectSign/Sign/SignClientFactory.swift index ba59acee8..4d10f2733 100644 --- a/Sources/WalletConnectSign/Sign/SignClientFactory.swift +++ b/Sources/WalletConnectSign/Sign/SignClientFactory.swift @@ -36,7 +36,7 @@ public struct SignClientFactory { let approveEngine = ApproveEngine(networkingInteractor: networkingClient, proposalPayloadsStore: proposalPayloadsStore, sessionToPairingTopic: sessionToPairingTopic, pairingRegisterer: pairingClient, metadata: metadata, kms: kms, logger: logger, pairingStore: pairingStore, sessionStore: sessionStore) let cleanupService = CleanupService(pairingStore: pairingStore, sessionStore: sessionStore, kms: kms, sessionToPairingTopic: sessionToPairingTopic) let deleteSessionService = DeleteSessionService(networkingInteractor: networkingClient, kms: kms, sessionStore: sessionStore, logger: logger) - let disconnectService = DisconnectService(deleteSessionService: deleteSessionService, sessionStorage: sessionStore, pairingClient: pairingClient) + let disconnectService = DisconnectService(deleteSessionService: deleteSessionService, sessionStorage: sessionStore) let sessionPingService = SessionPingService(sessionStorage: sessionStore, networkingInteractor: networkingClient, logger: logger) let pairingPingService = PairingPingService(pairingStorage: pairingStore, networkingInteractor: networkingClient, logger: logger) let appProposerService = AppProposeService(metadata: metadata, networkingInteractor: networkingClient, kms: kms, logger: logger) From 11d0107192e1cb92ce7d4b714fcfd1bceecd3d32 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 10 Oct 2022 12:13:12 -0500 Subject: [PATCH 148/175] migrate dapp to use Pair --- Example/DApp/Auth/AuthCoordinator.swift | 4 +++- Example/DApp/Sign/Connect/ConnectViewController.swift | 3 ++- .../DApp/Sign/SelectChain/SelectChainViewController.swift | 6 ++++-- Example/ExampleApp/SceneDelegate.swift | 2 +- .../Services/Common/CleanupService.swift | 1 - 5 files changed, 10 insertions(+), 6 deletions(-) diff --git a/Example/DApp/Auth/AuthCoordinator.swift b/Example/DApp/Auth/AuthCoordinator.swift index 231f0be4b..e34c79368 100644 --- a/Example/DApp/Auth/AuthCoordinator.swift +++ b/Example/DApp/Auth/AuthCoordinator.swift @@ -1,5 +1,6 @@ import SwiftUI import Auth +import WalletConnectPairing final class AuthCoordinator { @@ -30,7 +31,8 @@ final class AuthCoordinator { url: "wallet.connect", icons: ["https://avatars.githubusercontent.com/u/37784886"]) - Auth.configure(metadata: metadata, account: nil) + Pair.configure(metadata: metadata) + Auth.configure(account: nil) navigationController.viewControllers = [authViewController] } diff --git a/Example/DApp/Sign/Connect/ConnectViewController.swift b/Example/DApp/Sign/Connect/ConnectViewController.swift index adebac5c3..15e50a9fd 100644 --- a/Example/DApp/Sign/Connect/ConnectViewController.swift +++ b/Example/DApp/Sign/Connect/ConnectViewController.swift @@ -1,10 +1,11 @@ import Foundation import UIKit import WalletConnectSign +import WalletConnectPairing class ConnectViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { let uri: WalletConnectURI - let activePairings: [Pairing] = Sign.instance.getPairings() + let activePairings: [Pairing] = Pair.instance.getPairings() let segmentedControl = UISegmentedControl(items: ["Pairings", "New Pairing"]) init(uri: WalletConnectURI) { diff --git a/Example/DApp/Sign/SelectChain/SelectChainViewController.swift b/Example/DApp/Sign/SelectChain/SelectChainViewController.swift index 3a78d7efc..7abd2b7d4 100644 --- a/Example/DApp/Sign/SelectChain/SelectChainViewController.swift +++ b/Example/DApp/Sign/SelectChain/SelectChainViewController.swift @@ -1,5 +1,6 @@ import Foundation import WalletConnectSign +import WalletConnectPairing import UIKit import Combine @@ -34,8 +35,9 @@ class SelectChainViewController: UIViewController, UITableViewDataSource { let blockchains: Set = [Blockchain("eip155:1")!, Blockchain("eip155:137")!] let namespaces: [String: ProposalNamespace] = ["eip155": ProposalNamespace(chains: blockchains, methods: methods, events: [], extensions: nil)] Task { - let uri = try await Sign.instance.connect(requiredNamespaces: namespaces) - showConnectScreen(uri: uri!) + let uri = try await Pair.instance.create() + try await Sign.instance.connect(requiredNamespaces: namespaces, topic: uri.topic) + showConnectScreen(uri: uri) } } diff --git a/Example/ExampleApp/SceneDelegate.swift b/Example/ExampleApp/SceneDelegate.swift index a6a854e87..0037fb05b 100644 --- a/Example/ExampleApp/SceneDelegate.swift +++ b/Example/ExampleApp/SceneDelegate.swift @@ -57,7 +57,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { let wcUri = url.absoluteString.deletingPrefix("https://walletconnect.com/wc?uri=") Task(priority: .high) { - try! await Sign.instance.pair(uri: WalletConnectURI(string: wcUri)!) + try! await Pair.instance.pair(uri: WalletConnectURI(string: wcUri)!) } } } diff --git a/Sources/WalletConnectPairing/Services/Common/CleanupService.swift b/Sources/WalletConnectPairing/Services/Common/CleanupService.swift index 6a5a01334..2ee49d54d 100644 --- a/Sources/WalletConnectPairing/Services/Common/CleanupService.swift +++ b/Sources/WalletConnectPairing/Services/Common/CleanupService.swift @@ -1,7 +1,6 @@ import Foundation import WalletConnectKMS import WalletConnectUtils -import WalletConnectPairing final class CleanupService { From 276e2bf1664e9bdcc4e21738a67b6ddfff6e5adf Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 10 Oct 2022 16:19:56 -0500 Subject: [PATCH 149/175] add pairingInteracting protocol remove irrelevant test from sign --- .../Sign/SignClientTests.swift | 18 ------------------ Sources/Auth/Auth.swift | 2 +- Sources/WalletConnectPairing/Pair.swift | 2 +- .../WalletConnectPairing/PairingClient.swift | 2 +- .../PairingInteracting.swift | 18 ++++++++++++++++++ Sources/WalletConnectSign/Sign/Sign.swift | 2 +- 6 files changed, 22 insertions(+), 22 deletions(-) create mode 100644 Sources/WalletConnectPairing/PairingInteracting.swift diff --git a/Example/IntegrationTests/Sign/SignClientTests.swift b/Example/IntegrationTests/Sign/SignClientTests.swift index e0b463489..cb23354cb 100644 --- a/Example/IntegrationTests/Sign/SignClientTests.swift +++ b/Example/IntegrationTests/Sign/SignClientTests.swift @@ -134,24 +134,6 @@ final class SignClientTests: XCTestCase { wait(for: [sessionDeleteExpectation], timeout: InputConfig.defaultTimeout) } - func testPairingPing() async throws { - let pongResponseExpectation = expectation(description: "Ping sender receives a pong response") - - let uri = try await dapp.client.connect(requiredNamespaces: ProposalNamespace.stubRequired())! - try await wallet.client.pair(uri: uri) - - let pairing = wallet.client.getPairings().first! - - wallet.onPing = { topic in - XCTAssertEqual(topic, pairing.topic) - pongResponseExpectation.fulfill() - } - - try await wallet.client.ping(topic: pairing.topic) - - wait(for: [pongResponseExpectation], timeout: InputConfig.defaultTimeout) - } - func testSessionPing() async throws { let expectation = expectation(description: "Proposer receives ping response") diff --git a/Sources/Auth/Auth.swift b/Sources/Auth/Auth.swift index 022b40744..14d1e9cec 100644 --- a/Sources/Auth/Auth.swift +++ b/Sources/Auth/Auth.swift @@ -25,7 +25,7 @@ public class Auth { metadata: config.metadata, account: config.account, networkingClient: Networking.instance, - pairingRegisterer: Pair.instance + pairingRegisterer: Pair.instance as! PairingRegisterer ) }() diff --git a/Sources/WalletConnectPairing/Pair.swift b/Sources/WalletConnectPairing/Pair.swift index ae1f223ad..d156e51a0 100644 --- a/Sources/WalletConnectPairing/Pair.swift +++ b/Sources/WalletConnectPairing/Pair.swift @@ -5,7 +5,7 @@ import Combine public class Pair { /// Pairing client instance - public static var instance: PairingClient = { + public static var instance: PairingInteracting = { guard let config = Pair.config else { fatalError("Error - you must call Pair.configure(_:) before accessing the shared instance.") } diff --git a/Sources/WalletConnectPairing/PairingClient.swift b/Sources/WalletConnectPairing/PairingClient.swift index a73bdb010..784d55fba 100644 --- a/Sources/WalletConnectPairing/PairingClient.swift +++ b/Sources/WalletConnectPairing/PairingClient.swift @@ -5,7 +5,7 @@ import WalletConnectNetworking import Combine import JSONRPC -public class PairingClient: PairingRegisterer { +public class PairingClient: PairingRegisterer, PairingInteracting { public var pingResponsePublisher: AnyPublisher<(String), Never> { pingResponsePublisherSubject.eraseToAnyPublisher() } diff --git a/Sources/WalletConnectPairing/PairingInteracting.swift b/Sources/WalletConnectPairing/PairingInteracting.swift new file mode 100644 index 000000000..90b85bd51 --- /dev/null +++ b/Sources/WalletConnectPairing/PairingInteracting.swift @@ -0,0 +1,18 @@ +import Foundation +import WalletConnectUtils + +public protocol PairingInteracting { + func pair(uri: WalletConnectURI) async throws + + func create() async throws -> WalletConnectURI + + func getPairings() -> [Pairing] + + func getPairing(for topic: String) throws -> Pairing + + func ping(topic: String) async throws + + func disconnect(topic: String) async throws + + func cleanup() throws +} diff --git a/Sources/WalletConnectSign/Sign/Sign.swift b/Sources/WalletConnectSign/Sign/Sign.swift index 5b0b024e5..ee4c9f192 100644 --- a/Sources/WalletConnectSign/Sign/Sign.swift +++ b/Sources/WalletConnectSign/Sign/Sign.swift @@ -29,7 +29,7 @@ public class Sign { return SignClientFactory.create( metadata: Sign.metadata ?? Pair.metadata, relayClient: Relay.instance, - pairingClient: Pair.instance, + pairingClient: Pair.instance as! PairingClient, networkingClient: Networking.instance ) }() From 1e1bd3db0ff3b4bb8f1a1576bb83e637e3f91c5d Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 11 Oct 2022 11:58:15 -0500 Subject: [PATCH 150/175] add networking client interface --- Sources/Auth/AuthClientFactory.swift | 4 +- Sources/Chat/ChatClientFactory.swift | 2 +- .../NetworkingClientFactory.swift | 6 +- .../NetworkingInteractor.swift | 188 ++++++++++++++++++ Sources/WalletConnectPairing/Pair.swift | 2 +- .../PairingClientFactory.swift | 4 +- .../WalletConnectPush/PushClientFactory.swift | 2 +- Sources/WalletConnectSign/Sign/Sign.swift | 3 +- .../WalletConnectSign/Sign/SignClient.swift | 9 +- .../Sign/SignClientFactory.swift | 8 +- 10 files changed, 207 insertions(+), 21 deletions(-) create mode 100644 Sources/WalletConnectNetworking/NetworkingInteractor.swift diff --git a/Sources/Auth/AuthClientFactory.swift b/Sources/Auth/AuthClientFactory.swift index 6b8e8224a..c4e3f7e9f 100644 --- a/Sources/Auth/AuthClientFactory.swift +++ b/Sources/Auth/AuthClientFactory.swift @@ -7,14 +7,14 @@ import WalletConnectNetworking public struct AuthClientFactory { - public static func create(metadata: AppMetadata, account: Account?, networkingClient: NetworkingClient, pairingRegisterer: PairingRegisterer) -> AuthClient { + public static func create(metadata: AppMetadata, account: Account?, networkingClient: NetworkingInteractor, pairingRegisterer: PairingRegisterer) -> AuthClient { let logger = ConsoleLogger(loggingLevel: .off) let keyValueStorage = UserDefaults.standard let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk") return AuthClientFactory.create(metadata: metadata, account: account, logger: logger, keyValueStorage: keyValueStorage, keychainStorage: keychainStorage, networkingClient: networkingClient, pairingRegisterer: pairingRegisterer) } - static func create(metadata: AppMetadata, account: Account?, logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, networkingClient: NetworkingClient, pairingRegisterer: PairingRegisterer) -> AuthClient { + static func create(metadata: AppMetadata, account: Account?, logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, networkingClient: NetworkingInteractor, pairingRegisterer: PairingRegisterer) -> AuthClient { let kms = KeyManagementService(keychain: keychainStorage) let history = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage) let messageFormatter = SIWEMessageFormatter() diff --git a/Sources/Chat/ChatClientFactory.swift b/Sources/Chat/ChatClientFactory.swift index 10848a6d7..b5af0d7b2 100644 --- a/Sources/Chat/ChatClientFactory.swift +++ b/Sources/Chat/ChatClientFactory.swift @@ -16,7 +16,7 @@ public struct ChatClientFactory { let topicToRegistryRecordStore = CodableStore(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.topicToInvitationPubKey.rawValue) let serialiser = Serializer(kms: kms) let rpcHistory = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage) - let networkingInteractor = NetworkingClient(relayClient: relayClient, serializer: serialiser, logger: logger, rpcHistory: rpcHistory) + let networkingInteractor = NetworkingInteractor(relayClient: relayClient, serializer: serialiser, logger: logger, rpcHistory: rpcHistory) let invitePayloadStore = CodableStore>(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.invite.rawValue) let registryService = RegistryService(registry: registry, networkingInteractor: networkingInteractor, kms: kms, logger: logger, topicToRegistryRecordStore: topicToRegistryRecordStore) let threadStore = Database(keyValueStorage: keyValueStorage, identifier: StorageDomainIdentifiers.threads.rawValue) diff --git a/Sources/WalletConnectNetworking/NetworkingClientFactory.swift b/Sources/WalletConnectNetworking/NetworkingClientFactory.swift index 4466b2849..36df0f6fc 100644 --- a/Sources/WalletConnectNetworking/NetworkingClientFactory.swift +++ b/Sources/WalletConnectNetworking/NetworkingClientFactory.swift @@ -5,21 +5,21 @@ import WalletConnectUtils public struct NetworkingClientFactory { - public static func create(relayClient: RelayClient) -> NetworkingClient { + public static func create(relayClient: RelayClient) -> NetworkingInteractor { let logger = ConsoleLogger(loggingLevel: .off) let keyValueStorage = UserDefaults.standard let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk") return NetworkingClientFactory.create(relayClient: relayClient, logger: logger, keychainStorage: keychainStorage, keyValueStorage: keyValueStorage) } - public static func create(relayClient: RelayClient, logger: ConsoleLogging, keychainStorage: KeychainStorageProtocol, keyValueStorage: KeyValueStorage) -> NetworkingClient{ + public static func create(relayClient: RelayClient, logger: ConsoleLogging, keychainStorage: KeychainStorageProtocol, keyValueStorage: KeyValueStorage) -> NetworkingInteractor{ let kms = KeyManagementService(keychain: keychainStorage) let serializer = Serializer(kms: kms) let rpcHistory = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage) - return NetworkingClient( + return NetworkingInteractor( relayClient: relayClient, serializer: serializer, logger: logger, diff --git a/Sources/WalletConnectNetworking/NetworkingInteractor.swift b/Sources/WalletConnectNetworking/NetworkingInteractor.swift new file mode 100644 index 000000000..3437c8829 --- /dev/null +++ b/Sources/WalletConnectNetworking/NetworkingInteractor.swift @@ -0,0 +1,188 @@ +import Foundation +import Combine +import JSONRPC +import WalletConnectRelay +import WalletConnectUtils +import WalletConnectKMS + +public protocol NetworkingClient { + var socketConnectionStatusPublisher: AnyPublisher { get } + func connect() throws + func disconnect(closeCode: URLSessionWebSocketTask.CloseCode) throws +} + +extension NetworkingInteractor: NetworkingClient { + public func connect() throws { + try relayClient.connect() + } + + public func disconnect(closeCode: URLSessionWebSocketTask.CloseCode) throws { + try relayClient.disconnect(closeCode: closeCode) + } +} + +public class NetworkingInteractor: NetworkInteracting { + private var publishers = Set() + private let relayClient: RelayClient + private let serializer: Serializing + private let rpcHistory: RPCHistory + private let logger: ConsoleLogging + + private let requestPublisherSubject = PassthroughSubject<(topic: String, request: RPCRequest), Never>() + private let responsePublisherSubject = PassthroughSubject<(topic: String, request: RPCRequest, response: RPCResponse), Never>() + + public var requestPublisher: AnyPublisher<(topic: String, request: RPCRequest), Never> { + requestPublisherSubject.eraseToAnyPublisher() + } + + private var responsePublisher: AnyPublisher<(topic: String, request: RPCRequest, response: RPCResponse), Never> { + responsePublisherSubject.eraseToAnyPublisher() + } + + public var socketConnectionStatusPublisher: AnyPublisher + + public init( + relayClient: RelayClient, + serializer: Serializing, + logger: ConsoleLogging, + rpcHistory: RPCHistory + ) { + self.relayClient = relayClient + self.serializer = serializer + self.rpcHistory = rpcHistory + self.logger = logger + self.socketConnectionStatusPublisher = relayClient.socketConnectionStatusPublisher + setupRelaySubscribtion() + } + + private func setupRelaySubscribtion() { + relayClient.messagePublisher + .sink { [unowned self] (topic, message) in + manageSubscription(topic, message) + }.store(in: &publishers) + } + + public func subscribe(topic: String) async throws { + try await relayClient.subscribe(topic: topic) + } + + public func unsubscribe(topic: String) { + relayClient.unsubscribe(topic: topic) { [unowned self] error in + if let error = error { + logger.error(error) + } else { + rpcHistory.deleteAll(forTopic: topic) + } + } + } + + public func requestSubscription(on request: ProtocolMethod) -> AnyPublisher, Never> { + return requestPublisher + .filter { rpcRequest in + return rpcRequest.request.method == request.method + } + .compactMap { topic, rpcRequest in + guard let id = rpcRequest.id, let request = try? rpcRequest.params?.get(RequestParams.self) else { return nil } + return RequestSubscriptionPayload(id: id, topic: topic, request: request) + } + .eraseToAnyPublisher() + } + + public func responseSubscription(on request: ProtocolMethod) -> AnyPublisher, Never> { + return responsePublisher + .filter { rpcRequest in + return rpcRequest.request.method == request.method + } + .compactMap { topic, rpcRequest, rpcResponse in + guard + let id = rpcRequest.id, + let request = try? rpcRequest.params?.get(Request.self), + let response = try? rpcResponse.result?.get(Response.self) else { return nil } + return ResponseSubscriptionPayload(id: id, topic: topic, request: request, response: response) + } + .eraseToAnyPublisher() + } + + public func responseErrorSubscription(on request: ProtocolMethod) -> AnyPublisher, Never> { + return responsePublisher + .filter { $0.request.method == request.method } + .compactMap { (topic, rpcRequest, rpcResponse) in + guard let id = rpcResponse.id, let request = try? rpcRequest.params?.get(Request.self), let error = rpcResponse.error else { return nil } + return ResponseSubscriptionErrorPayload(id: id, topic: topic, request: request, error: error) + } + .eraseToAnyPublisher() + } + + public func request(_ request: RPCRequest, topic: String, protocolMethod: ProtocolMethod, envelopeType: Envelope.EnvelopeType) async throws { + try rpcHistory.set(request, forTopic: topic, emmitedBy: .local) + let message = try! serializer.serialize(topic: topic, encodable: request, envelopeType: envelopeType) + try await relayClient.publish(topic: topic, payload: message, tag: protocolMethod.requestConfig.tag, prompt: protocolMethod.requestConfig.prompt, ttl: protocolMethod.requestConfig.ttl) + } + + /// Completes with an acknowledgement from the relay network. + /// completes with error if networking client was not able to send a message + /// TODO - relay client should provide async function - continualion should be removed from here + public func requestNetworkAck(_ request: RPCRequest, topic: String, protocolMethod: ProtocolMethod) async throws { + do { + try rpcHistory.set(request, forTopic: topic, emmitedBy: .local) + let message = try serializer.serialize(topic: topic, encodable: request) + return try await withCheckedThrowingContinuation { continuation in + relayClient.publish(topic: topic, payload: message, tag: protocolMethod.requestConfig.tag, prompt: protocolMethod.requestConfig.prompt, ttl: protocolMethod.requestConfig.ttl) { error in + if let error = error { + continuation.resume(throwing: error) + } else { + continuation.resume() + } + } + } + } catch { + logger.error(error) + } + } + + public func respond(topic: String, response: RPCResponse, protocolMethod: ProtocolMethod, envelopeType: Envelope.EnvelopeType) async throws { + try rpcHistory.resolve(response) + let message = try! serializer.serialize(topic: topic, encodable: response, envelopeType: envelopeType) + try await relayClient.publish(topic: topic, payload: message, tag: protocolMethod.responseConfig.tag, prompt: protocolMethod.responseConfig.prompt, ttl: protocolMethod.responseConfig.ttl) + } + + public func respondSuccess(topic: String, requestId: RPCID, protocolMethod: ProtocolMethod, envelopeType: Envelope.EnvelopeType) async throws { + let response = RPCResponse(id: requestId, result: true) + try await respond(topic: topic, response: response, protocolMethod: protocolMethod, envelopeType: envelopeType) + } + + public func respondError(topic: String, requestId: RPCID, protocolMethod: ProtocolMethod, reason: Reason, envelopeType: Envelope.EnvelopeType) async throws { + let error = JSONRPCError(code: reason.code, message: reason.message) + let response = RPCResponse(id: requestId, error: error) + try await respond(topic: topic, response: response, protocolMethod: protocolMethod, envelopeType: envelopeType) + } + + private func manageSubscription(_ topic: String, _ encodedEnvelope: String) { + if let deserializedJsonRpcRequest: RPCRequest = serializer.tryDeserialize(topic: topic, encodedEnvelope: encodedEnvelope) { + handleRequest(topic: topic, request: deserializedJsonRpcRequest) + } else if let response: RPCResponse = serializer.tryDeserialize(topic: topic, encodedEnvelope: encodedEnvelope) { + handleResponse(response: response) + } else { + logger.debug("Networking Interactor - Received unknown object type from networking relay") + } + } + + private func handleRequest(topic: String, request: RPCRequest) { + do { + try rpcHistory.set(request, forTopic: topic, emmitedBy: .remote) + requestPublisherSubject.send((topic, request)) + } catch { + logger.debug(error) + } + } + + private func handleResponse(response: RPCResponse) { + do { + try rpcHistory.resolve(response) + let record = rpcHistory.get(recordId: response.id!)! + responsePublisherSubject.send((record.topic, record.request, response)) + } catch { + logger.debug("Handle json rpc response error: \(error)") + } + } +} diff --git a/Sources/WalletConnectPairing/Pair.swift b/Sources/WalletConnectPairing/Pair.swift index d156e51a0..6772f41b6 100644 --- a/Sources/WalletConnectPairing/Pair.swift +++ b/Sources/WalletConnectPairing/Pair.swift @@ -9,7 +9,7 @@ public class Pair { guard let config = Pair.config else { fatalError("Error - you must call Pair.configure(_:) before accessing the shared instance.") } - return PairingClientFactory.create(networkingClient: Networking.instance) + return PairingClientFactory.create(networkingClient: Networking.instance as! NetworkingInteractor) }() public static var metadata: AppMetadata { diff --git a/Sources/WalletConnectPairing/PairingClientFactory.swift b/Sources/WalletConnectPairing/PairingClientFactory.swift index b95a435ea..eb019917a 100644 --- a/Sources/WalletConnectPairing/PairingClientFactory.swift +++ b/Sources/WalletConnectPairing/PairingClientFactory.swift @@ -6,14 +6,14 @@ import WalletConnectNetworking public struct PairingClientFactory { - public static func create(networkingClient: NetworkingClient) -> PairingClient { + public static func create(networkingClient: NetworkingInteractor) -> PairingClient { let logger = ConsoleLogger(loggingLevel: .off) let keyValueStorage = UserDefaults.standard let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk") return PairingClientFactory.create(logger: logger, keyValueStorage: keyValueStorage, keychainStorage: keychainStorage, networkingClient: networkingClient) } - public static func create(logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, networkingClient: NetworkingClient) -> PairingClient { + public static func create(logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, networkingClient: NetworkingInteractor) -> PairingClient { let pairingStore = PairingStorage(storage: SequenceStore(store: .init(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.pairings.rawValue))) let kms = KeyManagementService(keychain: keychainStorage) let appPairService = AppPairService(networkingInteractor: networkingClient, kms: kms, pairingStorage: pairingStore) diff --git a/Sources/WalletConnectPush/PushClientFactory.swift b/Sources/WalletConnectPush/PushClientFactory.swift index e4b3dde6a..6a1b79429 100644 --- a/Sources/WalletConnectPush/PushClientFactory.swift +++ b/Sources/WalletConnectPush/PushClientFactory.swift @@ -7,7 +7,7 @@ import WalletConnectPairing public struct PushClientFactory { - static public func create(logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, networkingClient: NetworkingClient, pairingClient: PairingClient) -> PushClient { + static public func create(logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, networkingClient: NetworkingInteractor, pairingClient: PairingClient) -> PushClient { let kms = KeyManagementService(keychain: keychainStorage) let pushProposer = PushProposer(networkingInteractor: networkingClient, kms: kms, logger: logger) let proposalResponseSubscriber = ProposalResponseSubscriber(networkingInteractor: networkingClient, kms: kms, logger: logger) diff --git a/Sources/WalletConnectSign/Sign/Sign.swift b/Sources/WalletConnectSign/Sign/Sign.swift index ee4c9f192..d61e7b0ff 100644 --- a/Sources/WalletConnectSign/Sign/Sign.swift +++ b/Sources/WalletConnectSign/Sign/Sign.swift @@ -28,9 +28,8 @@ public class Sign { public static var instance: SignClient = { return SignClientFactory.create( metadata: Sign.metadata ?? Pair.metadata, - relayClient: Relay.instance, pairingClient: Pair.instance as! PairingClient, - networkingClient: Networking.instance + networkingClient: Networking.instance as! NetworkingInteractor ) }() diff --git a/Sources/WalletConnectSign/Sign/SignClient.swift b/Sources/WalletConnectSign/Sign/SignClient.swift index 9febde303..388f33692 100644 --- a/Sources/WalletConnectSign/Sign/SignClient.swift +++ b/Sources/WalletConnectSign/Sign/SignClient.swift @@ -1,7 +1,6 @@ import Foundation import Combine import JSONRPC -import WalletConnectRelay import WalletConnectUtils import WalletConnectKMS import WalletConnectNetworking @@ -100,7 +99,7 @@ public final class SignClient { // MARK: - Private properties private let pairingClient: PairingClient - private let relayClient: RelayClient + private let networkingClient: NetworkingInteractor private let sessionEngine: SessionEngine private let approveEngine: ApproveEngine private let disconnectService: DisconnectService @@ -129,7 +128,7 @@ public final class SignClient { // MARK: - Initialization init(logger: ConsoleLogging, - relayClient: RelayClient, + networkingClient: NetworkingInteractor, sessionEngine: SessionEngine, approveEngine: ApproveEngine, pairingPingService: PairingPingService, @@ -143,7 +142,7 @@ public final class SignClient { pairingClient: PairingClient ) { self.logger = logger - self.relayClient = relayClient + self.networkingClient = networkingClient self.sessionEngine = sessionEngine self.approveEngine = approveEngine self.pairingPingService = pairingPingService @@ -398,7 +397,7 @@ public final class SignClient { } private func setUpConnectionObserving() { - relayClient.socketConnectionStatusPublisher.sink { [weak self] status in + networkingClient.socketConnectionStatusPublisher.sink { [weak self] status in self?.socketConnectionStatusPublisherSubject.send(status) }.store(in: &publishers) } diff --git a/Sources/WalletConnectSign/Sign/SignClientFactory.swift b/Sources/WalletConnectSign/Sign/SignClientFactory.swift index 4d10f2733..a1c086783 100644 --- a/Sources/WalletConnectSign/Sign/SignClientFactory.swift +++ b/Sources/WalletConnectSign/Sign/SignClientFactory.swift @@ -16,14 +16,14 @@ public struct SignClientFactory { /// - keyValueStorage: by default WalletConnect SDK will store sequences in UserDefaults /// /// WalletConnect Client is not a singleton but once you create an instance, you should not deinitialize it. Usually only one instance of a client is required in the application. - public static func create(metadata: AppMetadata, relayClient: RelayClient, pairingClient: PairingClient, networkingClient: NetworkingClient) -> SignClient { + public static func create(metadata: AppMetadata, pairingClient: PairingClient, networkingClient: NetworkingInteractor) -> SignClient { let logger = ConsoleLogger(loggingLevel: .off) let keyValueStorage = UserDefaults.standard let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk") - return SignClientFactory.create(metadata: metadata, logger: logger, keyValueStorage: keyValueStorage, keychainStorage: keychainStorage, relayClient: relayClient, pairingClient: pairingClient, networkingClient: networkingClient) + return SignClientFactory.create(metadata: metadata, logger: logger, keyValueStorage: keyValueStorage, keychainStorage: keychainStorage, pairingClient: pairingClient, networkingClient: networkingClient) } - static func create(metadata: AppMetadata, logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, relayClient: RelayClient, pairingClient: PairingClient, networkingClient: NetworkingClient) -> SignClient { + static func create(metadata: AppMetadata, logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, pairingClient: PairingClient, networkingClient: NetworkingInteractor) -> SignClient { let kms = KeyManagementService(keychain: keychainStorage) let rpcHistory = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage) let pairingStore = PairingStorage(storage: SequenceStore(store: .init(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.pairings.rawValue))) @@ -43,7 +43,7 @@ public struct SignClientFactory { let client = SignClient( logger: logger, - relayClient: relayClient, + networkingClient: networkingClient, sessionEngine: sessionEngine, approveEngine: approveEngine, pairingPingService: pairingPingService, From a4c0a1c6258038c9ce043b4a7f3a0dc7a3ed280b Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 11 Oct 2022 12:03:18 -0500 Subject: [PATCH 151/175] restructure file --- .../NetworkingClient.swift | 171 +----------------- .../NetworkingInteractor.swift | 27 ++- 2 files changed, 15 insertions(+), 183 deletions(-) diff --git a/Sources/WalletConnectNetworking/NetworkingClient.swift b/Sources/WalletConnectNetworking/NetworkingClient.swift index 76e859428..db04cd32e 100644 --- a/Sources/WalletConnectNetworking/NetworkingClient.swift +++ b/Sources/WalletConnectNetworking/NetworkingClient.swift @@ -1,172 +1,9 @@ import Foundation import Combine -import JSONRPC import WalletConnectRelay -import WalletConnectUtils -import WalletConnectKMS -public class NetworkingClient: NetworkInteracting { - private var publishers = Set() - private let relayClient: RelayClient - private let serializer: Serializing - private let rpcHistory: RPCHistory - private let logger: ConsoleLogging - - private let requestPublisherSubject = PassthroughSubject<(topic: String, request: RPCRequest), Never>() - private let responsePublisherSubject = PassthroughSubject<(topic: String, request: RPCRequest, response: RPCResponse), Never>() - - public var requestPublisher: AnyPublisher<(topic: String, request: RPCRequest), Never> { - requestPublisherSubject.eraseToAnyPublisher() - } - - private var responsePublisher: AnyPublisher<(topic: String, request: RPCRequest, response: RPCResponse), Never> { - responsePublisherSubject.eraseToAnyPublisher() - } - - public var socketConnectionStatusPublisher: AnyPublisher - - public init( - relayClient: RelayClient, - serializer: Serializing, - logger: ConsoleLogging, - rpcHistory: RPCHistory - ) { - self.relayClient = relayClient - self.serializer = serializer - self.rpcHistory = rpcHistory - self.logger = logger - self.socketConnectionStatusPublisher = relayClient.socketConnectionStatusPublisher - setupRelaySubscribtion() - } - - private func setupRelaySubscribtion() { - relayClient.messagePublisher - .sink { [unowned self] (topic, message) in - manageSubscription(topic, message) - }.store(in: &publishers) - } - - public func subscribe(topic: String) async throws { - try await relayClient.subscribe(topic: topic) - } - - public func unsubscribe(topic: String) { - relayClient.unsubscribe(topic: topic) { [unowned self] error in - if let error = error { - logger.error(error) - } else { - rpcHistory.deleteAll(forTopic: topic) - } - } - } - - public func requestSubscription(on request: ProtocolMethod) -> AnyPublisher, Never> { - return requestPublisher - .filter { rpcRequest in - return rpcRequest.request.method == request.method - } - .compactMap { topic, rpcRequest in - guard let id = rpcRequest.id, let request = try? rpcRequest.params?.get(RequestParams.self) else { return nil } - return RequestSubscriptionPayload(id: id, topic: topic, request: request) - } - .eraseToAnyPublisher() - } - - public func responseSubscription(on request: ProtocolMethod) -> AnyPublisher, Never> { - return responsePublisher - .filter { rpcRequest in - return rpcRequest.request.method == request.method - } - .compactMap { topic, rpcRequest, rpcResponse in - guard - let id = rpcRequest.id, - let request = try? rpcRequest.params?.get(Request.self), - let response = try? rpcResponse.result?.get(Response.self) else { return nil } - return ResponseSubscriptionPayload(id: id, topic: topic, request: request, response: response) - } - .eraseToAnyPublisher() - } - - public func responseErrorSubscription(on request: ProtocolMethod) -> AnyPublisher, Never> { - return responsePublisher - .filter { $0.request.method == request.method } - .compactMap { (topic, rpcRequest, rpcResponse) in - guard let id = rpcResponse.id, let request = try? rpcRequest.params?.get(Request.self), let error = rpcResponse.error else { return nil } - return ResponseSubscriptionErrorPayload(id: id, topic: topic, request: request, error: error) - } - .eraseToAnyPublisher() - } - - public func request(_ request: RPCRequest, topic: String, protocolMethod: ProtocolMethod, envelopeType: Envelope.EnvelopeType) async throws { - try rpcHistory.set(request, forTopic: topic, emmitedBy: .local) - let message = try! serializer.serialize(topic: topic, encodable: request, envelopeType: envelopeType) - try await relayClient.publish(topic: topic, payload: message, tag: protocolMethod.requestConfig.tag, prompt: protocolMethod.requestConfig.prompt, ttl: protocolMethod.requestConfig.ttl) - } - - /// Completes with an acknowledgement from the relay network. - /// completes with error if networking client was not able to send a message - /// TODO - relay client should provide async function - continualion should be removed from here - public func requestNetworkAck(_ request: RPCRequest, topic: String, protocolMethod: ProtocolMethod) async throws { - do { - try rpcHistory.set(request, forTopic: topic, emmitedBy: .local) - let message = try serializer.serialize(topic: topic, encodable: request) - return try await withCheckedThrowingContinuation { continuation in - relayClient.publish(topic: topic, payload: message, tag: protocolMethod.requestConfig.tag, prompt: protocolMethod.requestConfig.prompt, ttl: protocolMethod.requestConfig.ttl) { error in - if let error = error { - continuation.resume(throwing: error) - } else { - continuation.resume() - } - } - } - } catch { - logger.error(error) - } - } - - public func respond(topic: String, response: RPCResponse, protocolMethod: ProtocolMethod, envelopeType: Envelope.EnvelopeType) async throws { - try rpcHistory.resolve(response) - let message = try! serializer.serialize(topic: topic, encodable: response, envelopeType: envelopeType) - try await relayClient.publish(topic: topic, payload: message, tag: protocolMethod.responseConfig.tag, prompt: protocolMethod.responseConfig.prompt, ttl: protocolMethod.responseConfig.ttl) - } - - public func respondSuccess(topic: String, requestId: RPCID, protocolMethod: ProtocolMethod, envelopeType: Envelope.EnvelopeType) async throws { - let response = RPCResponse(id: requestId, result: true) - try await respond(topic: topic, response: response, protocolMethod: protocolMethod, envelopeType: envelopeType) - } - - public func respondError(topic: String, requestId: RPCID, protocolMethod: ProtocolMethod, reason: Reason, envelopeType: Envelope.EnvelopeType) async throws { - let error = JSONRPCError(code: reason.code, message: reason.message) - let response = RPCResponse(id: requestId, error: error) - try await respond(topic: topic, response: response, protocolMethod: protocolMethod, envelopeType: envelopeType) - } - - private func manageSubscription(_ topic: String, _ encodedEnvelope: String) { - if let deserializedJsonRpcRequest: RPCRequest = serializer.tryDeserialize(topic: topic, encodedEnvelope: encodedEnvelope) { - handleRequest(topic: topic, request: deserializedJsonRpcRequest) - } else if let response: RPCResponse = serializer.tryDeserialize(topic: topic, encodedEnvelope: encodedEnvelope) { - handleResponse(response: response) - } else { - logger.debug("Networking Interactor - Received unknown object type from networking relay") - } - } - - private func handleRequest(topic: String, request: RPCRequest) { - do { - try rpcHistory.set(request, forTopic: topic, emmitedBy: .remote) - requestPublisherSubject.send((topic, request)) - } catch { - logger.debug(error) - } - } - - private func handleResponse(response: RPCResponse) { - do { - try rpcHistory.resolve(response) - let record = rpcHistory.get(recordId: response.id!)! - responsePublisherSubject.send((record.topic, record.request, response)) - } catch { - logger.debug("Handle json rpc response error: \(error)") - } - } +public protocol NetworkingClient { + var socketConnectionStatusPublisher: AnyPublisher { get } + func connect() throws + func disconnect(closeCode: URLSessionWebSocketTask.CloseCode) throws } diff --git a/Sources/WalletConnectNetworking/NetworkingInteractor.swift b/Sources/WalletConnectNetworking/NetworkingInteractor.swift index 3437c8829..4e025d8de 100644 --- a/Sources/WalletConnectNetworking/NetworkingInteractor.swift +++ b/Sources/WalletConnectNetworking/NetworkingInteractor.swift @@ -5,22 +5,6 @@ import WalletConnectRelay import WalletConnectUtils import WalletConnectKMS -public protocol NetworkingClient { - var socketConnectionStatusPublisher: AnyPublisher { get } - func connect() throws - func disconnect(closeCode: URLSessionWebSocketTask.CloseCode) throws -} - -extension NetworkingInteractor: NetworkingClient { - public func connect() throws { - try relayClient.connect() - } - - public func disconnect(closeCode: URLSessionWebSocketTask.CloseCode) throws { - try relayClient.disconnect(closeCode: closeCode) - } -} - public class NetworkingInteractor: NetworkInteracting { private var publishers = Set() private let relayClient: RelayClient @@ -186,3 +170,14 @@ public class NetworkingInteractor: NetworkInteracting { } } } + + +extension NetworkingInteractor: NetworkingClient { + public func connect() throws { + try relayClient.connect() + } + + public func disconnect(closeCode: URLSessionWebSocketTask.CloseCode) throws { + try relayClient.disconnect(closeCode: closeCode) + } +} From 4e96fe6a93c17523b7e9d060107c64649ff5e8f4 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 11 Oct 2022 12:04:55 -0500 Subject: [PATCH 152/175] fix buid --- Sources/Auth/Auth.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Auth/Auth.swift b/Sources/Auth/Auth.swift index 14d1e9cec..c1bd461ff 100644 --- a/Sources/Auth/Auth.swift +++ b/Sources/Auth/Auth.swift @@ -24,7 +24,7 @@ public class Auth { return AuthClientFactory.create( metadata: config.metadata, account: config.account, - networkingClient: Networking.instance, + networkingClient: Networking.instance as! NetworkingInteractor, pairingRegisterer: Pair.instance as! PairingRegisterer ) }() From abfd5fa53004c19b4bc9549612b536c10b1ed705 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 11 Oct 2022 12:05:22 -0500 Subject: [PATCH 153/175] fix integration tests build --- Example/IntegrationTests/Sign/SignClientTests.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Example/IntegrationTests/Sign/SignClientTests.swift b/Example/IntegrationTests/Sign/SignClientTests.swift index cb23354cb..a38e2e84e 100644 --- a/Example/IntegrationTests/Sign/SignClientTests.swift +++ b/Example/IntegrationTests/Sign/SignClientTests.swift @@ -42,7 +42,6 @@ final class SignClientTests: XCTestCase { logger: logger, keyValueStorage: keyValueStorage, keychainStorage: keychain, - relayClient: relayClient, pairingClient: pairingClient, networkingClient: networkingClient ) From c7295e20e2c728bfc989dc9834ea632526b22708 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 11 Oct 2022 16:45:23 -0500 Subject: [PATCH 154/175] update auth and pair interfaces --- Sources/Auth/Auth.swift | 32 ++++++------------- Sources/Auth/AuthConfig.swift | 1 - .../WalletConnectNetworking/Networking.swift | 11 ++++--- Sources/WalletConnectPairing/Pair.swift | 24 ++++++++++---- 4 files changed, 34 insertions(+), 34 deletions(-) diff --git a/Sources/Auth/Auth.swift b/Sources/Auth/Auth.swift index c1bd461ff..f42fe07ce 100644 --- a/Sources/Auth/Auth.swift +++ b/Sources/Auth/Auth.swift @@ -18,38 +18,24 @@ public class Auth { /// Auth client instance public static var instance: AuthClient = { - guard let config = Auth.config else { - fatalError("Error - you must call Auth.configure(_:) before accessing the shared instance.") - } + return AuthClientFactory.create( - metadata: config.metadata, - account: config.account, + metadata: Pair.metadata, + account: config?.account, networkingClient: Networking.instance as! NetworkingInteractor, - pairingRegisterer: Pair.instance as! PairingRegisterer + pairingRegisterer: Pair.registerer ) }() + private static var config: Config? private init() { } - /// Auth instance config method + /// Auth instance wallet config method /// - Parameters: - /// - metadata: App metadata - /// - account: account that wallet will be authenticating with. Should be nil for non wallet clients. - @available(*, deprecated, message: "Use Pair.configure(metadata:) with Auth.configure(account:) instead") - static public func configure(metadata: AppMetadata, account: Account?) { - Auth.config = Auth.Config( - metadata: metadata, - account: account) - } - - /// Auth instance config method - /// - Parameters: - /// - account: account that wallet will be authenticating with. Should be nil for non wallet clients. - static public func configure(account: Account?) { - Auth.config = Auth.Config( - metadata: Pair.metadata, - account: account) + /// - account: account that wallet will be authenticating with. + static public func configure(account: Account) { + Auth.config = Auth.Config(account: account) } } diff --git a/Sources/Auth/AuthConfig.swift b/Sources/Auth/AuthConfig.swift index b364ec507..00a98faed 100644 --- a/Sources/Auth/AuthConfig.swift +++ b/Sources/Auth/AuthConfig.swift @@ -2,7 +2,6 @@ import Foundation extension Auth { struct Config { - let metadata: AppMetadata let account: Account? } } diff --git a/Sources/WalletConnectNetworking/Networking.swift b/Sources/WalletConnectNetworking/Networking.swift index cc8c5f063..41def110b 100644 --- a/Sources/WalletConnectNetworking/Networking.swift +++ b/Sources/WalletConnectNetworking/Networking.swift @@ -6,13 +6,17 @@ import Foundation public class Networking { /// Networking client instance - public static var instance: NetworkingClient = { - guard let config = Networking.config else { + public static var instance: NetworkingClient { + return Networking.interactor + } + + public static var interactor: NetworkingInteractor { + guard let _ = Networking.config else { fatalError("Error - you must call Networking.configure(_:) before accessing the shared instance.") } return NetworkingClientFactory.create(relayClient: Relay.instance) - }() + } private static var config: Config? @@ -43,4 +47,3 @@ public class Networking { socketConnectionType: socketConnectionType) } } - diff --git a/Sources/WalletConnectPairing/Pair.swift b/Sources/WalletConnectPairing/Pair.swift index 6772f41b6..86680b758 100644 --- a/Sources/WalletConnectPairing/Pair.swift +++ b/Sources/WalletConnectPairing/Pair.swift @@ -5,12 +5,14 @@ import Combine public class Pair { /// Pairing client instance - public static var instance: PairingInteracting = { - guard let config = Pair.config else { - fatalError("Error - you must call Pair.configure(_:) before accessing the shared instance.") - } - return PairingClientFactory.create(networkingClient: Networking.instance as! NetworkingInteractor) - }() + public static var instance: PairingInteracting { + return Pair.client + } + + public static var registerer: PairingRegisterer { + return Pair.client + } + public static var metadata: AppMetadata { guard let metadata = config?.metadata else { @@ -30,3 +32,13 @@ public class Pair { Pair.config = Pair.Config(metadata: metadata) } } + +private extension Pair { + + static var client: PairingClient = { + guard let config = Pair.config else { + fatalError("Error - you must call Pair.configure(_:) before accessing the shared instance.") + } + return PairingClientFactory.create(networkingClient: Networking.interactor) + }() +} From 28109d722d05fa5633bae2eed666ae7919f46ab3 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 11 Oct 2022 17:25:12 -0500 Subject: [PATCH 155/175] auth fix --- Sources/Auth/Auth.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Auth/Auth.swift b/Sources/Auth/Auth.swift index f42fe07ce..f6cf8da49 100644 --- a/Sources/Auth/Auth.swift +++ b/Sources/Auth/Auth.swift @@ -22,7 +22,7 @@ public class Auth { return AuthClientFactory.create( metadata: Pair.metadata, account: config?.account, - networkingClient: Networking.instance as! NetworkingInteractor, + networkingClient: Networking.interactor, pairingRegisterer: Pair.registerer ) }() From a3d6c50aec1bc43c7116467b535affe03b4602d1 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 13 Oct 2022 09:45:27 -0500 Subject: [PATCH 156/175] fix dapp build --- Example/DApp/Auth/AuthCoordinator.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/Example/DApp/Auth/AuthCoordinator.swift b/Example/DApp/Auth/AuthCoordinator.swift index e34c79368..f5594f8ab 100644 --- a/Example/DApp/Auth/AuthCoordinator.swift +++ b/Example/DApp/Auth/AuthCoordinator.swift @@ -32,8 +32,6 @@ final class AuthCoordinator { icons: ["https://avatars.githubusercontent.com/u/37784886"]) Pair.configure(metadata: metadata) - Auth.configure(account: nil) - navigationController.viewControllers = [authViewController] } } From cacea637f9739cfbb4dea8d444a52ae931e0d1ab Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 13 Oct 2022 09:43:31 -0500 Subject: [PATCH 157/175] expose client id --- .../Relay/RelayClientEndToEndTests.swift | 7 ++++--- .../NetworkingClient.swift | 1 + .../NetworkingInteractor.swift | 4 ++++ .../ClientAuth/ClientIdStorage.swift | 12 +++++++++++- Sources/WalletConnectRelay/RelayClient.swift | 18 ++++++++++++++---- 5 files changed, 34 insertions(+), 8 deletions(-) diff --git a/Example/IntegrationTests/Relay/RelayClientEndToEndTests.swift b/Example/IntegrationTests/Relay/RelayClientEndToEndTests.swift index e14d9481e..60ab35252 100644 --- a/Example/IntegrationTests/Relay/RelayClientEndToEndTests.swift +++ b/Example/IntegrationTests/Relay/RelayClientEndToEndTests.swift @@ -10,10 +10,11 @@ final class RelayClientEndToEndTests: XCTestCase { private var publishers = Set() func makeRelayClient() -> RelayClient { - let clientIdStorage = ClientIdStorage(keychain: KeychainStorageMock()) + let didKeyFactory = ED25519DIDKeyFactory() + let clientIdStorage = ClientIdStorage(keychain: KeychainStorageMock(), didKeyFactory: didKeyFactory) let socketAuthenticator = SocketAuthenticator( clientIdStorage: clientIdStorage, - didKeyFactory: ED25519DIDKeyFactory(), + didKeyFactory: didKeyFactory, relayHost: InputConfig.relayHost ) let urlFactory = RelayUrlFactory(socketAuthenticator: socketAuthenticator) @@ -21,7 +22,7 @@ final class RelayClientEndToEndTests: XCTestCase { let logger = ConsoleLogger() let dispatcher = Dispatcher(socket: socket, socketConnectionHandler: ManualSocketConnectionHandler(socket: socket), logger: logger) - return RelayClient(dispatcher: dispatcher, logger: logger, keyValueStorage: RuntimeKeyValueStorage()) + return RelayClient(dispatcher: dispatcher, logger: logger, keyValueStorage: RuntimeKeyValueStorage(), clientIdStorage: clientIdStorage) } func testSubscribe() { diff --git a/Sources/WalletConnectNetworking/NetworkingClient.swift b/Sources/WalletConnectNetworking/NetworkingClient.swift index db04cd32e..f39e2790d 100644 --- a/Sources/WalletConnectNetworking/NetworkingClient.swift +++ b/Sources/WalletConnectNetworking/NetworkingClient.swift @@ -6,4 +6,5 @@ public protocol NetworkingClient { var socketConnectionStatusPublisher: AnyPublisher { get } func connect() throws func disconnect(closeCode: URLSessionWebSocketTask.CloseCode) throws + func getClientId() throws -> String } diff --git a/Sources/WalletConnectNetworking/NetworkingInteractor.swift b/Sources/WalletConnectNetworking/NetworkingInteractor.swift index 4e025d8de..39faee02a 100644 --- a/Sources/WalletConnectNetworking/NetworkingInteractor.swift +++ b/Sources/WalletConnectNetworking/NetworkingInteractor.swift @@ -180,4 +180,8 @@ extension NetworkingInteractor: NetworkingClient { public func disconnect(closeCode: URLSessionWebSocketTask.CloseCode) throws { try relayClient.disconnect(closeCode: closeCode) } + + public func getClientId() throws -> String { + try relayClient.getClientId() + } } diff --git a/Sources/WalletConnectRelay/ClientAuth/ClientIdStorage.swift b/Sources/WalletConnectRelay/ClientAuth/ClientIdStorage.swift index 826ae67e6..b22cfbbfb 100644 --- a/Sources/WalletConnectRelay/ClientAuth/ClientIdStorage.swift +++ b/Sources/WalletConnectRelay/ClientAuth/ClientIdStorage.swift @@ -3,14 +3,18 @@ import WalletConnectKMS protocol ClientIdStoring { func getOrCreateKeyPair() throws -> SigningPrivateKey + func getClientId() throws -> String } struct ClientIdStorage: ClientIdStoring { private let key = "com.walletconnect.iridium.client_id" private let keychain: KeychainStorageProtocol + private let didKeyFactory: ED25519DIDKeyFactory - init(keychain: KeychainStorageProtocol) { + init(keychain: KeychainStorageProtocol, + didKeyFactory: ED25519DIDKeyFactory) { self.keychain = keychain + self.didKeyFactory = didKeyFactory } func getOrCreateKeyPair() throws -> SigningPrivateKey { @@ -22,4 +26,10 @@ struct ClientIdStorage: ClientIdStoring { return privateKey } } + + func getClientId() throws -> String { + let privateKey: SigningPrivateKey = try keychain.read(key: key) + let pubKey = privateKey.publicKey.rawRepresentation + return didKeyFactory.make(pubKey: pubKey, prefix: true) + } } diff --git a/Sources/WalletConnectRelay/RelayClient.swift b/Sources/WalletConnectRelay/RelayClient.swift index c038cb0dd..6cd97b19e 100644 --- a/Sources/WalletConnectRelay/RelayClient.swift +++ b/Sources/WalletConnectRelay/RelayClient.swift @@ -41,6 +41,8 @@ public final class RelayClient { requestAcknowledgePublisherSubject.eraseToAnyPublisher() } + private let clientIdStorage: ClientIdStorage + private var dispatcher: Dispatching private let rpcHistory: RPCHistory private let logger: ConsoleLogging @@ -52,11 +54,13 @@ public final class RelayClient { init( dispatcher: Dispatching, logger: ConsoleLogging, - keyValueStorage: KeyValueStorage + keyValueStorage: KeyValueStorage, + clientIdStorage: ClientIdStorage ) { self.logger = logger self.dispatcher = dispatcher self.rpcHistory = RPCHistoryFactory.createForRelay(keyValueStorage: keyValueStorage) + self.clientIdStorage = clientIdStorage setUpBindings() } @@ -82,9 +86,11 @@ public final class RelayClient { socketConnectionType: SocketConnectionType = .automatic, logger: ConsoleLogging = ConsoleLogger(loggingLevel: .off) ) { + let didKeyFactory = ED25519DIDKeyFactory() + let clientIdStorage = ClientIdStorage(keychain: keychainStorage, didKeyFactory: didKeyFactory) let socketAuthenticator = SocketAuthenticator( - clientIdStorage: ClientIdStorage(keychain: keychainStorage), - didKeyFactory: ED25519DIDKeyFactory(), + clientIdStorage: clientIdStorage, + didKeyFactory: didKeyFactory, relayHost: relayHost ) let relayUrlFactory = RelayUrlFactory(socketAuthenticator: socketAuthenticator) @@ -101,7 +107,7 @@ public final class RelayClient { socketConnectionHandler = ManualSocketConnectionHandler(socket: socket) } let dispatcher = Dispatcher(socket: socket, socketConnectionHandler: socketConnectionHandler, logger: logger) - self.init(dispatcher: dispatcher, logger: logger, keyValueStorage: keyValueStorage) + self.init(dispatcher: dispatcher, logger: logger, keyValueStorage: keyValueStorage, clientIdStorage: clientIdStorage) } /// Connects web socket @@ -231,6 +237,10 @@ public final class RelayClient { } } + public func getClientId() throws -> String { + try clientIdStorage.getClientId() + } + // FIXME: Parse data to string once before trying to decode -> respond error on fail private func handlePayloadMessage(_ payload: String) { if let request = tryDecode(RPCRequest.self, from: payload) { From 71409b6e6dc7e82b9eca5e2c40ccb741c578f35c Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 13 Oct 2022 11:46:57 -0500 Subject: [PATCH 158/175] fix build --- .../ClientAuth/ClientIdStorage.swift | 4 ++-- Sources/WalletConnectRelay/RelayClient.swift | 4 ++-- .../AuthTests/ClientIdStorageTests.swift | 17 ++++++++++++----- .../Mocks/ClientIdStorageMock.swift | 4 ++++ Tests/RelayerTests/RelayClientTests.swift | 4 ++-- 5 files changed, 22 insertions(+), 11 deletions(-) diff --git a/Sources/WalletConnectRelay/ClientAuth/ClientIdStorage.swift b/Sources/WalletConnectRelay/ClientAuth/ClientIdStorage.swift index b22cfbbfb..7d945bcda 100644 --- a/Sources/WalletConnectRelay/ClientAuth/ClientIdStorage.swift +++ b/Sources/WalletConnectRelay/ClientAuth/ClientIdStorage.swift @@ -9,10 +9,10 @@ protocol ClientIdStoring { struct ClientIdStorage: ClientIdStoring { private let key = "com.walletconnect.iridium.client_id" private let keychain: KeychainStorageProtocol - private let didKeyFactory: ED25519DIDKeyFactory + private let didKeyFactory: DIDKeyFactory init(keychain: KeychainStorageProtocol, - didKeyFactory: ED25519DIDKeyFactory) { + didKeyFactory: DIDKeyFactory) { self.keychain = keychain self.didKeyFactory = didKeyFactory } diff --git a/Sources/WalletConnectRelay/RelayClient.swift b/Sources/WalletConnectRelay/RelayClient.swift index 6cd97b19e..0a156c7f5 100644 --- a/Sources/WalletConnectRelay/RelayClient.swift +++ b/Sources/WalletConnectRelay/RelayClient.swift @@ -41,7 +41,7 @@ public final class RelayClient { requestAcknowledgePublisherSubject.eraseToAnyPublisher() } - private let clientIdStorage: ClientIdStorage + private let clientIdStorage: ClientIdStoring private var dispatcher: Dispatching private let rpcHistory: RPCHistory @@ -55,7 +55,7 @@ public final class RelayClient { dispatcher: Dispatching, logger: ConsoleLogging, keyValueStorage: KeyValueStorage, - clientIdStorage: ClientIdStorage + clientIdStorage: ClientIdStoring ) { self.logger = logger self.dispatcher = dispatcher diff --git a/Tests/RelayerTests/AuthTests/ClientIdStorageTests.swift b/Tests/RelayerTests/AuthTests/ClientIdStorageTests.swift index 38e27a073..dfd0d77f4 100644 --- a/Tests/RelayerTests/AuthTests/ClientIdStorageTests.swift +++ b/Tests/RelayerTests/AuthTests/ClientIdStorageTests.swift @@ -6,16 +6,23 @@ import WalletConnectKMS final class ClientIdStorageTests: XCTestCase { - func testGetOrCreate() throws { - let keychain = KeychainStorageMock() - let storage = ClientIdStorage(keychain: keychain) + var sut: ClientIdStorage! + var keychain: KeychainStorageMock! + var didKeyFactory: ED25519DIDKeyFactoryMock! + + override func setUp() { + keychain = KeychainStorageMock() + didKeyFactory = ED25519DIDKeyFactoryMock() + sut = ClientIdStorage(keychain: keychain, didKeyFactory: didKeyFactory) + } + func testGetOrCreate() throws { XCTAssertThrowsError(try keychain.read(key: "com.walletconnect.iridium.client_id") as SigningPrivateKey) - let saved = try storage.getOrCreateKeyPair() + let saved = try sut.getOrCreateKeyPair() XCTAssertEqual(saved, try keychain.read(key: "com.walletconnect.iridium.client_id")) - let restored = try storage.getOrCreateKeyPair() + let restored = try sut.getOrCreateKeyPair() XCTAssertEqual(saved, restored) } } diff --git a/Tests/RelayerTests/Mocks/ClientIdStorageMock.swift b/Tests/RelayerTests/Mocks/ClientIdStorageMock.swift index 2453c6174..1adac1a0a 100644 --- a/Tests/RelayerTests/Mocks/ClientIdStorageMock.swift +++ b/Tests/RelayerTests/Mocks/ClientIdStorageMock.swift @@ -3,6 +3,10 @@ import WalletConnectKMS import Foundation class ClientIdStorageMock: ClientIdStoring { + func getClientId() throws -> String { + fatalError() + } + var keyPair: SigningPrivateKey! func getOrCreateKeyPair() throws -> SigningPrivateKey { diff --git a/Tests/RelayerTests/RelayClientTests.swift b/Tests/RelayerTests/RelayClientTests.swift index f9094bf17..f37148fca 100644 --- a/Tests/RelayerTests/RelayClientTests.swift +++ b/Tests/RelayerTests/RelayClientTests.swift @@ -9,13 +9,13 @@ final class RelayClientTests: XCTestCase { var sut: RelayClient! var dispatcher: DispatcherMock! - var publishers = Set() override func setUp() { dispatcher = DispatcherMock() let logger = ConsoleLogger() - sut = RelayClient(dispatcher: dispatcher, logger: logger, keyValueStorage: RuntimeKeyValueStorage()) + let clientIdStorage = ClientIdStorageMock() + sut = RelayClient(dispatcher: dispatcher, logger: logger, keyValueStorage: RuntimeKeyValueStorage(), clientIdStorage: clientIdStorage) } override func tearDown() { From 433460f61a0098d0be9baa421fe8b326aecab959 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 13 Oct 2022 12:04:31 -0500 Subject: [PATCH 159/175] update tests --- .../RelayerTests/AuthTests/ClientIdStorageTests.swift | 10 ++++++++++ Tests/RelayerTests/Mocks/ClientIdStorageMock.swift | 7 ++++--- .../RelayerTests/Mocks/ED25519DIDKeyFactoryMock.swift | 2 +- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/Tests/RelayerTests/AuthTests/ClientIdStorageTests.swift b/Tests/RelayerTests/AuthTests/ClientIdStorageTests.swift index dfd0d77f4..a9e3e2770 100644 --- a/Tests/RelayerTests/AuthTests/ClientIdStorageTests.swift +++ b/Tests/RelayerTests/AuthTests/ClientIdStorageTests.swift @@ -25,4 +25,14 @@ final class ClientIdStorageTests: XCTestCase { let restored = try sut.getOrCreateKeyPair() XCTAssertEqual(saved, restored) } + + func testGetClientId() { + let did = "did:key:z6MkodHZwneVRShtaLf8JKYkxpDGp1vGZnpGmdBpX8M2exxH" + didKeyFactory.did = did + _ = try! sut.getOrCreateKeyPair() + + let clientId = try! sut.getClientId() + XCTAssertEqual(did, clientId) + + } } diff --git a/Tests/RelayerTests/Mocks/ClientIdStorageMock.swift b/Tests/RelayerTests/Mocks/ClientIdStorageMock.swift index 1adac1a0a..5d26c6d34 100644 --- a/Tests/RelayerTests/Mocks/ClientIdStorageMock.swift +++ b/Tests/RelayerTests/Mocks/ClientIdStorageMock.swift @@ -3,13 +3,14 @@ import WalletConnectKMS import Foundation class ClientIdStorageMock: ClientIdStoring { - func getClientId() throws -> String { - fatalError() - } var keyPair: SigningPrivateKey! func getOrCreateKeyPair() throws -> SigningPrivateKey { return keyPair } + + func getClientId() throws -> String { + fatalError() + } } diff --git a/Tests/RelayerTests/Mocks/ED25519DIDKeyFactoryMock.swift b/Tests/RelayerTests/Mocks/ED25519DIDKeyFactoryMock.swift index 23afd5c3c..11ef10631 100644 --- a/Tests/RelayerTests/Mocks/ED25519DIDKeyFactoryMock.swift +++ b/Tests/RelayerTests/Mocks/ED25519DIDKeyFactoryMock.swift @@ -2,7 +2,7 @@ import WalletConnectKMS @testable import WalletConnectRelay import Foundation -struct ED25519DIDKeyFactoryMock: DIDKeyFactory { +class ED25519DIDKeyFactoryMock: DIDKeyFactory { var did: String! func make(pubKey: Data, prefix: Bool) -> String { return did From 9e952c8dc09e07abbb38dcf40aad3ac1eeab1a64 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 13 Oct 2022 15:38:30 -0500 Subject: [PATCH 160/175] update method unsupported --- Sources/WalletConnectPairing/Types/PairError.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/WalletConnectPairing/Types/PairError.swift b/Sources/WalletConnectPairing/Types/PairError.swift index 4452bc907..419edd9b3 100644 --- a/Sources/WalletConnectPairing/Types/PairError.swift +++ b/Sources/WalletConnectPairing/Types/PairError.swift @@ -15,7 +15,7 @@ public enum PairError: Codable, Equatable, Error, Reason { public var code: Int { switch self { case .methodUnsupported: - return 0 + return 10001 } } From 5cf07358579c3fec4604bd5c6679cb5803a12de2 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Fri, 14 Oct 2022 14:26:52 -0500 Subject: [PATCH 161/175] Test case repaired --- Example/IntegrationTests/Pairing/PairingTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Example/IntegrationTests/Pairing/PairingTests.swift b/Example/IntegrationTests/Pairing/PairingTests.swift index 472d0fb04..93b2fe456 100644 --- a/Example/IntegrationTests/Pairing/PairingTests.swift +++ b/Example/IntegrationTests/Pairing/PairingTests.swift @@ -129,7 +129,7 @@ final class PairingTests: XCTestCase { let expectation = expectation(description: "wallet responds unsupported method for unregistered method") appPushClient.responsePublisher.sink { (id, response) in - XCTAssertEqual(response, .failure(WalletConnectPairing.PairError(code: 0)!)) + XCTAssertEqual(response, .failure(WalletConnectPairing.PairError(code: 10001)!)) expectation.fulfill() }.store(in: &publishers) From a3232a6c8b085049a81fc3aaf3560c2a7bc76440 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Tue, 11 Oct 2022 16:01:32 -0500 Subject: [PATCH 162/175] EIP1271 verification --- Sources/Auth/Auth.swift | 9 +-- Sources/Auth/AuthClientFactory.swift | 8 +-- Sources/Auth/AuthConfig.swift | 1 + .../Services/App/AppRespondSubscriber.swift | 18 +++-- .../Signer/EIP1271/EIP1271Verifier.swift | 44 ++++++++++++ .../Services/Signer/EIP1271/RPCService.swift | 30 ++++++++ .../Signer/EIP1271/ValidSignatureMethod.swift | 29 ++++++++ .../Signer/EIP191/EIP191Verifier.swift | 37 ++++++++++ .../Auth/Services/Signer/MessageSigner.swift | 45 +++++++++--- .../Signer/MessageSignerFactory.swift | 16 +++++ Sources/Auth/Services/Signer/Signer.swift | 27 +------ Sources/Auth/Types/Cacao/CacaoSignature.swift | 9 ++- .../AuthTests/AppRespondSubscriberTests.swift | 2 +- Tests/AuthTests/CacaoSignerTests.swift | 12 ++-- Tests/AuthTests/EIP1271VerifierTests.swift | 8 +++ Tests/AuthTests/SignerTests.swift | 72 +++++++++++-------- 16 files changed, 280 insertions(+), 87 deletions(-) create mode 100644 Sources/Auth/Services/Signer/EIP1271/EIP1271Verifier.swift create mode 100644 Sources/Auth/Services/Signer/EIP1271/RPCService.swift create mode 100644 Sources/Auth/Services/Signer/EIP1271/ValidSignatureMethod.swift create mode 100644 Sources/Auth/Services/Signer/EIP191/EIP191Verifier.swift create mode 100644 Sources/Auth/Services/Signer/MessageSignerFactory.swift create mode 100644 Tests/AuthTests/EIP1271VerifierTests.swift diff --git a/Sources/Auth/Auth.swift b/Sources/Auth/Auth.swift index f6cf8da49..297986b7c 100644 --- a/Sources/Auth/Auth.swift +++ b/Sources/Auth/Auth.swift @@ -20,10 +20,11 @@ public class Auth { public static var instance: AuthClient = { return AuthClientFactory.create( - metadata: Pair.metadata, - account: config?.account, - networkingClient: Networking.interactor, - pairingRegisterer: Pair.registerer + metadata: config.metadata, + account: config.account, + projectId: Networking.projectId, + networkingClient: Networking.instance, + pairingRegisterer: Pair.instance ) }() diff --git a/Sources/Auth/AuthClientFactory.swift b/Sources/Auth/AuthClientFactory.swift index c4e3f7e9f..999b28977 100644 --- a/Sources/Auth/AuthClientFactory.swift +++ b/Sources/Auth/AuthClientFactory.swift @@ -7,19 +7,19 @@ import WalletConnectNetworking public struct AuthClientFactory { - public static func create(metadata: AppMetadata, account: Account?, networkingClient: NetworkingInteractor, pairingRegisterer: PairingRegisterer) -> AuthClient { + public static func create(metadata: AppMetadata, account: Account?, projectId: String, networkingClient: NetworkingInteractor, pairingRegisterer: PairingRegisterer) -> AuthClient { let logger = ConsoleLogger(loggingLevel: .off) let keyValueStorage = UserDefaults.standard let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk") - return AuthClientFactory.create(metadata: metadata, account: account, logger: logger, keyValueStorage: keyValueStorage, keychainStorage: keychainStorage, networkingClient: networkingClient, pairingRegisterer: pairingRegisterer) + return AuthClientFactory.create(metadata: metadata, account: account, projectId: projectId, logger: logger, keyValueStorage: keyValueStorage, keychainStorage: keychainStorage, networkingClient: networkingClient, pairingRegisterer: pairingRegisterer) } - static func create(metadata: AppMetadata, account: Account?, logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, networkingClient: NetworkingInteractor, pairingRegisterer: PairingRegisterer) -> AuthClient { + static func create(metadata: AppMetadata, account: Account?, projectId: String, logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, networkingClient: NetworkingInteractor, pairingRegisterer: PairingRegisterer) -> AuthClient { let kms = KeyManagementService(keychain: keychainStorage) let history = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage) let messageFormatter = SIWEMessageFormatter() let appRequestService = AppRequestService(networkingInteractor: networkingClient, kms: kms, appMetadata: metadata, logger: logger) - let messageSigner = MessageSigner(signer: Signer()) + let messageSigner = MessageSignerFactory.create(projectId: projectId) let appRespondSubscriber = AppRespondSubscriber(networkingInteractor: networkingClient, logger: logger, rpcHistory: history, signatureVerifier: messageSigner, pairingRegisterer: pairingRegisterer, messageFormatter: messageFormatter) let walletErrorResponder = WalletErrorResponder(networkingInteractor: networkingClient, logger: logger, kms: kms, rpcHistory: history) let walletRequestSubscriber = WalletRequestSubscriber(networkingInteractor: networkingClient, logger: logger, kms: kms, messageFormatter: messageFormatter, address: account?.address, walletErrorResponder: walletErrorResponder, pairingRegisterer: pairingRegisterer) diff --git a/Sources/Auth/AuthConfig.swift b/Sources/Auth/AuthConfig.swift index 00a98faed..965d3aa2c 100644 --- a/Sources/Auth/AuthConfig.swift +++ b/Sources/Auth/AuthConfig.swift @@ -3,5 +3,6 @@ import Foundation extension Auth { struct Config { let account: Account? + let projectId: String } } diff --git a/Sources/Auth/Services/App/AppRespondSubscriber.swift b/Sources/Auth/Services/App/AppRespondSubscriber.swift index 56679aad3..db811c60d 100644 --- a/Sources/Auth/Services/App/AppRespondSubscriber.swift +++ b/Sources/Auth/Services/App/AppRespondSubscriber.swift @@ -56,11 +56,19 @@ class AppRespondSubscriber { guard messageFormatter.formatMessage(from: requestPayload.payloadParams, address: address) == message else { self.onResponse?(requestId, .failure(.messageCompromised)); return } - guard let _ = try? signatureVerifier.verify(signature: cacao.s, message: message, address: address) - else { self.onResponse?(requestId, .failure(.signatureVerificationFailed)); return } - - onResponse?(requestId, .success(cacao)) - + Task(priority: .high) { + do { + try await signatureVerifier.verify( + signature: cacao.s, + message: message, + address: address + ) + onResponse?(requestId, .success(cacao)) + } catch { + logger.error("Signature verification failed with: \(error.localizedDescription)") + onResponse?(requestId, .failure(.signatureVerificationFailed)) + } + } }.store(in: &publishers) } } diff --git a/Sources/Auth/Services/Signer/EIP1271/EIP1271Verifier.swift b/Sources/Auth/Services/Signer/EIP1271/EIP1271Verifier.swift new file mode 100644 index 000000000..a6bac678f --- /dev/null +++ b/Sources/Auth/Services/Signer/EIP1271/EIP1271Verifier.swift @@ -0,0 +1,44 @@ +import Foundation +import JSONRPC +import WalletConnectUtils +import WalletConnectRelay + +actor EIP1271Verifier { + private let projectId: String + private let httpClient: HTTPClient // TODO: Move in Networking package? + + init(projectId: String, httpClient: HTTPClient) { + self.projectId = projectId + self.httpClient = httpClient + } + + func verify(signature: Data, message: Data, address: String) async throws { + let encoder = ValidSignatureMethod(signature: signature, messageHash: message.keccak256) + let call = EthCall(to: address, data: encoder.encode()) + let params = AnyCodable([AnyCodable(call), AnyCodable("latest")]) + let request = RPCRequest(method: "eth_call", params: params) + let data = try JSONEncoder().encode(request) + let httpService = RPCService(data: data, projectId: projectId) + let response = try await httpClient.request(RPCResponse.self, at: httpService) + try validateResponse(response) + } + + private func validateResponse(_ response: RPCResponse) throws { + guard + let result = try response.result?.get(String.self), + result.starts(with: ValidSignatureMethod.methodHash) + else { throw Errors.invalidSignature } + } +} + +extension EIP1271Verifier { + + enum Errors: Error { + case invalidSignature + } + + struct EthCall: Codable { + let to: String + let data: String + } +} diff --git a/Sources/Auth/Services/Signer/EIP1271/RPCService.swift b/Sources/Auth/Services/Signer/EIP1271/RPCService.swift new file mode 100644 index 000000000..310f891ed --- /dev/null +++ b/Sources/Auth/Services/Signer/EIP1271/RPCService.swift @@ -0,0 +1,30 @@ +import Foundation +import WalletConnectRelay + +struct RPCService: HTTPService { + let data: Data + let projectId: String + + var path: String { + return "/v1" + } + + var method: HTTPMethod { + return .post + } + + var scheme: String { + return "https" + } + + var body: Data? { + return data + } + + var queryParameters: [String : String]? { + return [ + "chainId": "eip155:1", + "projectId": projectId + ] + } +} diff --git a/Sources/Auth/Services/Signer/EIP1271/ValidSignatureMethod.swift b/Sources/Auth/Services/Signer/EIP1271/ValidSignatureMethod.swift new file mode 100644 index 000000000..950602a63 --- /dev/null +++ b/Sources/Auth/Services/Signer/EIP1271/ValidSignatureMethod.swift @@ -0,0 +1,29 @@ +import Foundation + +struct ValidSignatureMethod { + static let methodHash = "0x1626ba7e" + static let paddingIndex = "0000000000000000000000000000000000000000000000000000000000000040" + static let signatureLength = "0000000000000000000000000000000000000000000000000000000000000041" + static let signaturePadding = "00000000000000000000000000000000000000000000000000000000000000" + + + let signature: Data + let messageHash: Data + + func encode() -> String { + return [ + ValidSignatureMethod.methodHash, + leadingZeros(for: messageHash.toHexString(), end: false), + ValidSignatureMethod.paddingIndex, + ValidSignatureMethod.signatureLength, + leadingZeros(for: signature.toHexString(), end: true), + ValidSignatureMethod.signaturePadding + ].joined() + } + + private func leadingZeros(for value: String, end: Bool) -> String { + let count = max(0, value.count % 32 - 2) + let padding = String(repeating: "0", count: count) + return end ? padding + value : value + padding + } +} diff --git a/Sources/Auth/Services/Signer/EIP191/EIP191Verifier.swift b/Sources/Auth/Services/Signer/EIP191/EIP191Verifier.swift new file mode 100644 index 000000000..be9d7a362 --- /dev/null +++ b/Sources/Auth/Services/Signer/EIP191/EIP191Verifier.swift @@ -0,0 +1,37 @@ +import Foundation +import Web3 + +actor EIP191Verifier { + + func verify(signature: Data, message: Data, address: String) async throws { + let sig = decompose(signature: signature) + let publicKey = try EthereumPublicKey.init( + message: message.bytes, + v: EthereumQuantity(quantity: BigUInt(sig.v)), + r: EthereumQuantity(sig.r), + s: EthereumQuantity(sig.s) + ) + try verifyPublicKey(publicKey, address: address) + } + + // TODO: typalias file ??? + private func decompose(signature: Data) -> Signer.Signature { + let v = signature.bytes[signature.count-1] + let r = signature.bytes[0..<32] + let s = signature.bytes[32..<64] + return (UInt(v), [UInt8](r), [UInt8](s)) + } + + private func verifyPublicKey(_ publicKey: EthereumPublicKey, address: String) throws { + guard publicKey.address.hex(eip55: false) == address.lowercased() else { + throw Errors.invalidSignature + } + } +} + +extension EIP191Verifier { + + enum Errors: Error { + case invalidSignature + } +} diff --git a/Sources/Auth/Services/Signer/MessageSigner.swift b/Sources/Auth/Services/Signer/MessageSigner.swift index 3f6252efd..25f70338f 100644 --- a/Sources/Auth/Services/Signer/MessageSigner.swift +++ b/Sources/Auth/Services/Signer/MessageSigner.swift @@ -1,7 +1,7 @@ import Foundation protocol MessageSignatureVerifying { - func verify(signature: CacaoSignature, message: String, address: String) throws + func verify(signature: CacaoSignature, message: String, address: String) async throws } protocol MessageSigning { @@ -11,27 +11,54 @@ protocol MessageSigning { public struct MessageSigner: MessageSignatureVerifying, MessageSigning { enum Errors: Error { - case signatureValidationFailed case utf8EncodingFailed } private let signer: Signer + private let eip191Verifier: EIP191Verifier + private let eip1271Verifier: EIP1271Verifier - public init(signer: Signer = Signer()) { + init(signer: Signer, eip191Verifier: EIP191Verifier, eip1271Verifier: EIP1271Verifier) { self.signer = signer + self.eip191Verifier = eip191Verifier + self.eip1271Verifier = eip1271Verifier } public func sign(message: String, privateKey: Data) throws -> CacaoSignature { guard let messageData = message.data(using: .utf8) else { throw Errors.utf8EncodingFailed } - let signature = try signer.sign(message: messageData, with: privateKey) + let signature = try signer.sign(message: prefixed(messageData), with: privateKey) let prefixedHexSignature = "0x" + signature.toHexString() - return CacaoSignature(t: "eip191", s: prefixedHexSignature) + return CacaoSignature(t: .eip191, s: prefixedHexSignature) } - public func verify(signature: CacaoSignature, message: String, address: String) throws { - guard let messageData = message.data(using: .utf8) else { throw Errors.utf8EncodingFailed } + public func verify(signature: CacaoSignature, message: String, address: String) async throws { + guard let messageData = message.data(using: .utf8) else { + throw Errors.utf8EncodingFailed + } + let signatureData = Data(hex: signature.s) - guard try signer.isValid(signature: signatureData, message: messageData, address: address) - else { throw Errors.signatureValidationFailed } + + switch signature.t { + case .eip191: + return try await eip191Verifier.verify( + signature: signatureData, + message: prefixed(messageData), + address: address + ) + case .eip1271: + return try await eip191Verifier.verify( + signature: signatureData, + message: prefixed(messageData), + address: address + ) + } + } +} + +private extension MessageSigner { + + private func prefixed(_ message: Data) -> Data { + return "\u{19}Ethereum Signed Message:\n\(message.count)" + .data(using: .utf8)! + message } } diff --git a/Sources/Auth/Services/Signer/MessageSignerFactory.swift b/Sources/Auth/Services/Signer/MessageSignerFactory.swift new file mode 100644 index 000000000..96fa2487e --- /dev/null +++ b/Sources/Auth/Services/Signer/MessageSignerFactory.swift @@ -0,0 +1,16 @@ +import Foundation +import WalletConnectRelay + +public struct MessageSignerFactory { + + public static func create(projectId: String) -> MessageSigner { + return MessageSigner( + signer: Signer(), + eip191Verifier: EIP191Verifier(), + eip1271Verifier: EIP1271Verifier( + projectId: projectId, + httpClient: HTTPClient(host: "rpc.walletconnect.com") + ) + ) + } +} diff --git a/Sources/Auth/Services/Signer/Signer.swift b/Sources/Auth/Services/Signer/Signer.swift index 3d5903296..3721db118 100644 --- a/Sources/Auth/Services/Signer/Signer.swift +++ b/Sources/Auth/Services/Signer/Signer.swift @@ -8,37 +8,12 @@ public struct Signer { public init() {} func sign(message: Data, with key: Data) throws -> Data { - let prefixed = prefixed(message: message) let privateKey = try EthereumPrivateKey(privateKey: key.bytes) - let signature = try privateKey.sign(message: prefixed.bytes) + let signature = try privateKey.sign(message: message.bytes) return serialized(signature: signature) } - func isValid(signature: Data, message: Data, address: String) throws -> Bool { - let sig = decompose(signature: signature) - let prefixed = prefixed(message: message) - let publicKey = try EthereumPublicKey( - message: prefixed.bytes, - v: EthereumQuantity(quantity: BigUInt(sig.v)), - r: EthereumQuantity(sig.r), - s: EthereumQuantity(sig.s) - ) - return publicKey.address.hex(eip55: false) == address.lowercased() - } - - private func decompose(signature: Data) -> Signature { - let v = signature.bytes[signature.count-1] - let r = signature.bytes[0..<32] - let s = signature.bytes[32..<64] - return (UInt(v), [UInt8](r), [UInt8](s)) - } - private func serialized(signature: Signature) -> Data { return Data(signature.r + signature.s + [UInt8(signature.v)]) } - - private func prefixed(message: Data) -> Data { - return "\u{19}Ethereum Signed Message:\n\(message.count)" - .data(using: .utf8)! + message - } } diff --git a/Sources/Auth/Types/Cacao/CacaoSignature.swift b/Sources/Auth/Types/Cacao/CacaoSignature.swift index b34ee1a40..7137f9dea 100644 --- a/Sources/Auth/Types/Cacao/CacaoSignature.swift +++ b/Sources/Auth/Types/Cacao/CacaoSignature.swift @@ -1,11 +1,16 @@ import Foundation +public enum CacaoSignatureType: String, Codable { + case eip191 + case eip1271 +} + public struct CacaoSignature: Codable, Equatable { - let t: String + let t: CacaoSignatureType let s: String let m: String? - public init(t: String, s: String, m: String? = nil) { + public init(t: CacaoSignatureType, s: String, m: String? = nil) { self.t = t self.s = s self.m = m diff --git a/Tests/AuthTests/AppRespondSubscriberTests.swift b/Tests/AuthTests/AppRespondSubscriberTests.swift index 023ad627b..512144aa3 100644 --- a/Tests/AuthTests/AppRespondSubscriberTests.swift +++ b/Tests/AuthTests/AppRespondSubscriberTests.swift @@ -22,7 +22,7 @@ class AppRespondSubscriberTests: XCTestCase { override func setUp() { networkingInteractor = NetworkingInteractorMock() messageFormatter = SIWEMessageFormatter() - messageSigner = MessageSigner() + messageSigner = MessageSignerFactory.create(projectId: "project-id") rpcHistory = RPCHistoryFactory.createForNetwork(keyValueStorage: RuntimeKeyValueStorage()) pairingStorage = WCPairingStorageMock() pairingRegisterer = PairingRegistererMock() diff --git a/Tests/AuthTests/CacaoSignerTests.swift b/Tests/AuthTests/CacaoSignerTests.swift index 159eebc41..258bde093 100644 --- a/Tests/AuthTests/CacaoSignerTests.swift +++ b/Tests/AuthTests/CacaoSignerTests.swift @@ -5,6 +5,8 @@ import TestingUtils class CacaoSignerTest: XCTestCase { + let signer = MessageSignerFactory.create(projectId: "project-id") + let privateKey = Data(hex: "305c6cde3846927892cd32762f6120539f3ec74c9e3a16b9b798b1e85351ae2a") let message: String = @@ -24,17 +26,13 @@ class CacaoSignerTest: XCTestCase { - https://example.com/my-web2-claim.json """ - let signature = CacaoSignature(t: "eip191", s: "0x438effc459956b57fcd9f3dac6c675f9cee88abf21acab7305e8e32aa0303a883b06dcbd956279a7a2ca21ffa882ff55cc22e8ab8ec0f3fe90ab45f306938cfa1b") + let signature = CacaoSignature(t: .eip191, s: "0x438effc459956b57fcd9f3dac6c675f9cee88abf21acab7305e8e32aa0303a883b06dcbd956279a7a2ca21ffa882ff55cc22e8ab8ec0f3fe90ab45f306938cfa1b") func testCacaoSign() throws { - let signer = MessageSigner(signer: Signer()) - XCTAssertEqual(try signer.sign(message: message, privateKey: privateKey), signature) } - func testCacaoVerify() throws { - let signer = MessageSigner(signer: Signer()) - - try signer.verify(signature: signature, message: message, address: "0x15bca56b6e2728aec2532df9d436bd1600e86688") + func testCacaoVerify() async throws { + try await signer.verify(signature: signature, message: message, address: "0x15bca56b6e2728aec2532df9d436bd1600e86688") } } diff --git a/Tests/AuthTests/EIP1271VerifierTests.swift b/Tests/AuthTests/EIP1271VerifierTests.swift new file mode 100644 index 000000000..1f2d39c11 --- /dev/null +++ b/Tests/AuthTests/EIP1271VerifierTests.swift @@ -0,0 +1,8 @@ +// +// File.swift +// +// +// Created by WalletConnect on 10.10.2022. +// + +import Foundation diff --git a/Tests/AuthTests/SignerTests.swift b/Tests/AuthTests/SignerTests.swift index 5f546bd08..7dc4d2dca 100644 --- a/Tests/AuthTests/SignerTests.swift +++ b/Tests/AuthTests/SignerTests.swift @@ -4,12 +4,13 @@ import XCTest import secp256k1 import Web3 import WalletConnectUtils +import WalletConnectRelay class SignerTest: XCTestCase { private let signer = Signer() - private let message = "Message".data(using: .utf8)! + private let message = "\u{19}Ethereum Signed Message:\n7Message".data(using: .utf8)! private let privateKey = Data(hex: "305c6cde3846927892cd32762f6120539f3ec74c9e3a16b9b798b1e85351ae2a") private let signature = Data(hex: "66121e60cccc01fbf7fcba694a1e08ac5db35fb4ec6c045bedba7860445b95c021cad2c595f0bf68ff896964c7c02bb2f3a3e9540e8e4595c98b737ce264cc541b") private var address = "0x15bca56b6e2728aec2532df9d436bd1600e86688" @@ -18,38 +19,51 @@ class SignerTest: XCTestCase { let result = try signer.sign(message: message, with: privateKey) XCTAssertEqual(signature.toHexString(), result.toHexString()) - XCTAssertTrue(try signer.isValid(signature: result, message: message, address: address)) +// XCTAssertTrue(try signer.isValid(signature: result, message: message, address: address)) } - func testEtherscanSignature() throws { - let addressFromEtherscan = "0x6721591d424c18b7173d55895efa1839aa57d9c2" - let message = "[Etherscan.io 12/08/2022 09:26:23] I, hereby verify that I am the owner/creator of the address [0x7e77dcb127f99ece88230a64db8d595f31f1b068]" - let signedMessageFromEtherscan = message.data(using: .utf8)! - let signatureHashFromEtherscan = Data(hex: "60eb9cfe362210f1b4855f4865eafc378bd442c406de22354cc9f643fb84cb265b7f6d9d10b13199e450558c328814a9038884d9993d9feb79b727366736853d1b") - XCTAssertTrue(try signer.isValid( - signature: signatureHashFromEtherscan, - message: signedMessageFromEtherscan, - address: addressFromEtherscan - )) + private func prefixed(_ message: Data) -> Data { + return "\u{19}Ethereum Signed Message:\n\(message.count)" + .data(using: .utf8)! + message } - func testInvalidMessage() throws { - let message = "Message One".data(using: .utf8)! - - XCTAssertFalse(try signer.isValid(signature: signature, message: message, address: address)) - } - - func testInvalidPubKey() throws { - let address = "0xBAc675C310721717Cd4A37F6cbeA1F081b1C2a07" - - XCTAssertFalse(try signer.isValid(signature: signature, message: message, address: address)) - } - - func testInvalidSignature() throws { - let signature = Data(hex: "86deb09d045608f2753ef12f46e8da5fc2559e3a9162e580df3e62c875df7c3f64433462a59bc4ff38ce52412bff10527f4b99cc078f63ef2bb4a6f7427080aa01") - - XCTAssertFalse(try signer.isValid(signature: signature, message: message, address: address)) - } +// func testEtherscanSignature() async throws { +// let addressFromEtherscan = "0x6721591d424c18b7173d55895efa1839aa57d9c2" +// let message = "[Etherscan.io 12/08/2022 09:26:23] I, hereby verify that I am the owner/creator of the address [0x7e77dcb127f99ece88230a64db8d595f31f1b068]" +// let signedMessageFromEtherscan = message.data(using: .utf8)! +// let signatureHashFromEtherscan = Data(hex: "60eb9cfe362210f1b4855f4865eafc378bd442c406de22354cc9f643fb84cb265b7f6d9d10b13199e450558c328814a9038884d9993d9feb79b727366736853d1b") +// XCTAssertTrue(try signer.isValid( +// signature: signatureHashFromEtherscan, +// message: signedMessageFromEtherscan, +// address: addressFromEtherscan +// )) +// +// let client = HTTPClient(host: "rpc.walletconnect.com") +// let service = EIP1271Verifier(projectId: "28fc11c6a12a4184bc8e9c371edff2bc", httpClient: client) +// try await service.verify( +// signature: Data(hex: "c1505719b2504095116db01baaf276361efd3a73c28cf8cc28dabefa945b8d536011289ac0a3b048600c1e692ff173ca944246cf7ceb319ac2262d27b395c82b1c"), +// messageHash: Data(hex: "3aaa8393796c7388e4e062861d8238503de7584c977676fe9d8d551c30e11f84"), +// address: "0x2faf83c542b68f1b4cdc0e770e8cb9f567b08f71" +// ) +// } +// +// func testInvalidMessage() throws { +// let message = "Message One".data(using: .utf8)! +// +// XCTAssertFalse(try signer.isValid(signature: signature, message: message, address: address)) +// } +// +// func testInvalidPubKey() throws { +// let address = "0xBAc675C310721717Cd4A37F6cbeA1F081b1C2a07" +// +// XCTAssertFalse(try signer.isValid(signature: signature, message: message, address: address)) +// } +// +// func testInvalidSignature() throws { +// let signature = Data(hex: "86deb09d045608f2753ef12f46e8da5fc2559e3a9162e580df3e62c875df7c3f64433462a59bc4ff38ce52412bff10527f4b99cc078f63ef2bb4a6f7427080aa01") +// +// XCTAssertFalse(try signer.isValid(signature: signature, message: message, address: address)) +// } func testSignerAddressFromIss() throws { let iss = "did:pkh:eip155:1:0xBAc675C310721717Cd4A37F6cbeA1F081b1C2a07" From cde65284bd4dd5170dcb9f6019cdeff303ff08ff Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Tue, 11 Oct 2022 16:52:19 -0500 Subject: [PATCH 163/175] EIP191VerifierTests --- Tests/AuthTests/EIP1271VerifierTests.swift | 13 ++++--- Tests/AuthTests/EIP191VerifierTests.swift | 42 ++++++++++++++++++++++ Tests/AuthTests/SignerTests.swift | 1 - 3 files changed, 48 insertions(+), 8 deletions(-) create mode 100644 Tests/AuthTests/EIP191VerifierTests.swift diff --git a/Tests/AuthTests/EIP1271VerifierTests.swift b/Tests/AuthTests/EIP1271VerifierTests.swift index 1f2d39c11..bacd00e60 100644 --- a/Tests/AuthTests/EIP1271VerifierTests.swift +++ b/Tests/AuthTests/EIP1271VerifierTests.swift @@ -1,8 +1,7 @@ -// -// File.swift -// -// -// Created by WalletConnect on 10.10.2022. -// - import Foundation +import XCTest +@testable import Auth + +class EIP1271VerifierTests: XCTestCase { + +} diff --git a/Tests/AuthTests/EIP191VerifierTests.swift b/Tests/AuthTests/EIP191VerifierTests.swift new file mode 100644 index 000000000..3571c03fd --- /dev/null +++ b/Tests/AuthTests/EIP191VerifierTests.swift @@ -0,0 +1,42 @@ +import Foundation +import XCTest +import TestingUtils +@testable import Auth + +class EIP191VerifierTests: XCTestCase { + + private let verifier = EIP191Verifier() + + private let address = "0x15bca56b6e2728aec2532df9d436bd1600e86688" + private let message = "\u{19}Ethereum Signed Message:\n7Message".data(using: .utf8)! + private let signature = Data(hex: "66121e60cccc01fbf7fcba694a1e08ac5db35fb4ec6c045bedba7860445b95c021cad2c595f0bf68ff896964c7c02bb2f3a3e9540e8e4595c98b737ce264cc541b") + + + func testVerify() async throws { + try await verifier.verify(signature: signature, message: message, address: address) + } + + func testEtherscanSignature() async throws { + let address = "0x6721591d424c18b7173d55895efa1839aa57d9c2" + let message = "\u{19}Ethereum Signed Message:\n139[Etherscan.io 12/08/2022 09:26:23] I, hereby verify that I am the owner/creator of the address [0x7e77dcb127f99ece88230a64db8d595f31f1b068]".data(using: .utf8)! + let signature = Data(hex: "60eb9cfe362210f1b4855f4865eafc378bd442c406de22354cc9f643fb84cb265b7f6d9d10b13199e450558c328814a9038884d9993d9feb79b727366736853d1b") + + try await verifier.verify(signature: signature, message: message, address: address) + } + + func testInvalidMessage() async throws { + let message = Data(hex: "0xdeadbeaf") + await XCTAssertThrowsErrorAsync(try await verifier.verify(signature: signature, message: message, address: address)) + } + + func testInvalidPubKey() async throws { + let address = "0xBAc675C310721717Cd4A37F6cbeA1F081b1C2a07" + await XCTAssertThrowsErrorAsync(try await verifier.verify(signature: signature, message: message, address: address)) + } + + func testInvalidSignature() async throws { + let signature = Data(hex: "86deb09d045608f2753ef12f46e8da5fc2559e3a9162e580df3e62c875df7c3f64433462a59bc4ff38ce52412bff10527f4b99cc078f63ef2bb4a6f7427080aa01") + + await XCTAssertThrowsErrorAsync(try await verifier.verify(signature: signature, message: message, address: address)) + } +} diff --git a/Tests/AuthTests/SignerTests.swift b/Tests/AuthTests/SignerTests.swift index 7dc4d2dca..0ee11254f 100644 --- a/Tests/AuthTests/SignerTests.swift +++ b/Tests/AuthTests/SignerTests.swift @@ -19,7 +19,6 @@ class SignerTest: XCTestCase { let result = try signer.sign(message: message, with: privateKey) XCTAssertEqual(signature.toHexString(), result.toHexString()) -// XCTAssertTrue(try signer.isValid(signature: result, message: message, address: address)) } private func prefixed(_ message: Data) -> Data { From af92e582498d007dcc02df7741abcaf003077be4 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Tue, 11 Oct 2022 17:12:48 -0500 Subject: [PATCH 164/175] Cleanup --- Tests/AuthTests/EIP1271VerifierTests.swift | 11 +++++++ Tests/AuthTests/SignerTests.swift | 38 ---------------------- 2 files changed, 11 insertions(+), 38 deletions(-) diff --git a/Tests/AuthTests/EIP1271VerifierTests.swift b/Tests/AuthTests/EIP1271VerifierTests.swift index bacd00e60..74cc3d4d8 100644 --- a/Tests/AuthTests/EIP1271VerifierTests.swift +++ b/Tests/AuthTests/EIP1271VerifierTests.swift @@ -4,4 +4,15 @@ import XCTest class EIP1271VerifierTests: XCTestCase { +// private let verifier = EIP1271Verifier(projectId: "project-id", httpClient: HTTPClient) + + func testVerify() async throws { + // let client = HTTPClient(host: "rpc.walletconnect.com") + // let service = EIP1271Verifier(projectId: "", httpClient: client) + // try await service.verify( + // signature: Data(hex: "c1505719b2504095116db01baaf276361efd3a73c28cf8cc28dabefa945b8d536011289ac0a3b048600c1e692ff173ca944246cf7ceb319ac2262d27b395c82b1c"), + // messageHash: Data(hex: "3aaa8393796c7388e4e062861d8238503de7584c977676fe9d8d551c30e11f84"), + // address: "0x2faf83c542b68f1b4cdc0e770e8cb9f567b08f71" + // ) + } } diff --git a/Tests/AuthTests/SignerTests.swift b/Tests/AuthTests/SignerTests.swift index 0ee11254f..80b19ecfb 100644 --- a/Tests/AuthTests/SignerTests.swift +++ b/Tests/AuthTests/SignerTests.swift @@ -26,44 +26,6 @@ class SignerTest: XCTestCase { .data(using: .utf8)! + message } -// func testEtherscanSignature() async throws { -// let addressFromEtherscan = "0x6721591d424c18b7173d55895efa1839aa57d9c2" -// let message = "[Etherscan.io 12/08/2022 09:26:23] I, hereby verify that I am the owner/creator of the address [0x7e77dcb127f99ece88230a64db8d595f31f1b068]" -// let signedMessageFromEtherscan = message.data(using: .utf8)! -// let signatureHashFromEtherscan = Data(hex: "60eb9cfe362210f1b4855f4865eafc378bd442c406de22354cc9f643fb84cb265b7f6d9d10b13199e450558c328814a9038884d9993d9feb79b727366736853d1b") -// XCTAssertTrue(try signer.isValid( -// signature: signatureHashFromEtherscan, -// message: signedMessageFromEtherscan, -// address: addressFromEtherscan -// )) -// -// let client = HTTPClient(host: "rpc.walletconnect.com") -// let service = EIP1271Verifier(projectId: "28fc11c6a12a4184bc8e9c371edff2bc", httpClient: client) -// try await service.verify( -// signature: Data(hex: "c1505719b2504095116db01baaf276361efd3a73c28cf8cc28dabefa945b8d536011289ac0a3b048600c1e692ff173ca944246cf7ceb319ac2262d27b395c82b1c"), -// messageHash: Data(hex: "3aaa8393796c7388e4e062861d8238503de7584c977676fe9d8d551c30e11f84"), -// address: "0x2faf83c542b68f1b4cdc0e770e8cb9f567b08f71" -// ) -// } -// -// func testInvalidMessage() throws { -// let message = "Message One".data(using: .utf8)! -// -// XCTAssertFalse(try signer.isValid(signature: signature, message: message, address: address)) -// } -// -// func testInvalidPubKey() throws { -// let address = "0xBAc675C310721717Cd4A37F6cbeA1F081b1C2a07" -// -// XCTAssertFalse(try signer.isValid(signature: signature, message: message, address: address)) -// } -// -// func testInvalidSignature() throws { -// let signature = Data(hex: "86deb09d045608f2753ef12f46e8da5fc2559e3a9162e580df3e62c875df7c3f64433462a59bc4ff38ce52412bff10527f4b99cc078f63ef2bb4a6f7427080aa01") -// -// XCTAssertFalse(try signer.isValid(signature: signature, message: message, address: address)) -// } - func testSignerAddressFromIss() throws { let iss = "did:pkh:eip155:1:0xBAc675C310721717Cd4A37F6cbeA1F081b1C2a07" From 50a4a7d1a99f15fcfc75ad6c5f05aa8da4de6d85 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 13 Oct 2022 11:57:39 -0500 Subject: [PATCH 165/175] ProjectID from Networking package --- Example/IntegrationTests/Auth/AuthTests.swift | 6 ++++-- Example/IntegrationTests/Chat/RegistryTests.swift | 2 +- .../Classes/DomainLayer/Chat/ChatFactory.swift | 3 ++- .../Wallet/AuthRequest/AuthRequestInteractor.swift | 2 +- .../Auth/Services/Signer/EIP1271/EIP1271Verifier.swift | 2 +- Sources/Auth/Services/Signer/EIP1271/RPCService.swift | 2 +- Sources/Auth/Services/Signer/MessageSigner.swift | 10 +++++----- .../Auth/Services/Signer/MessageSignerFactory.swift | 8 ++++++-- Sources/Auth/Services/Signer/Signer.swift | 4 ++-- Sources/Chat/HTTPServices/RegisterService.swift | 2 +- Sources/Chat/HTTPServices/ResolveService.swift | 2 +- Sources/Chat/Registry.swift | 2 +- .../HTTPClient}/HTTPClient.swift | 0 .../HTTPClient}/HTTPError.swift | 0 .../HTTPClient}/HTTPService.swift | 0 Sources/WalletConnectNetworking/Networking.swift | 7 +++++++ Tests/AuthTests/AppRespondSubscriberTests.swift | 2 +- Tests/RelayerTests/Helpers/Error+Extension.swift | 1 + 18 files changed, 35 insertions(+), 20 deletions(-) rename Sources/{WalletConnectRelay/HTTP => WalletConnectNetworking/HTTPClient}/HTTPClient.swift (100%) rename Sources/{WalletConnectRelay/HTTP => WalletConnectNetworking/HTTPClient}/HTTPError.swift (100%) rename Sources/{WalletConnectRelay/HTTP => WalletConnectNetworking/HTTPClient}/HTTPService.swift (100%) diff --git a/Example/IntegrationTests/Auth/AuthTests.swift b/Example/IntegrationTests/Auth/AuthTests.swift index 627ea19fa..af2fe58b0 100644 --- a/Example/IntegrationTests/Auth/AuthTests.swift +++ b/Example/IntegrationTests/Auth/AuthTests.swift @@ -45,6 +45,7 @@ final class AuthTests: XCTestCase { let authClient = AuthClientFactory.create( metadata: AppMetadata(name: name, description: "", url: "", icons: [""]), account: account, + projectId: InputConfig.projectId, logger: logger, keyValueStorage: keyValueStorage, keychainStorage: keychain, @@ -74,7 +75,8 @@ final class AuthTests: XCTestCase { try! await walletPairingClient.pair(uri: uri) walletAuthClient.authRequestPublisher.sink { [unowned self] request in Task(priority: .high) { - let signature = try! MessageSigner().sign(message: request.message, privateKey: prvKey) + let signer = MessageSignerFactory.create(projectId: InputConfig.projectId) + let signature = try! signer.sign(message: request.message, privateKey: prvKey) try! await walletAuthClient.respond(requestId: request.id, signature: signature) } } @@ -117,7 +119,7 @@ final class AuthTests: XCTestCase { walletAuthClient.authRequestPublisher.sink { [unowned self] request in Task(priority: .high) { let invalidSignature = "438effc459956b57fcd9f3dac6c675f9cee88abf21acab7305e8e32aa0303a883b06dcbd956279a7a2ca21ffa882ff55cc22e8ab8ec0f3fe90ab45f306938cfa1b" - let cacaoSignature = CacaoSignature(t: "eip191", s: invalidSignature) + let cacaoSignature = CacaoSignature(t: .eip191, s: invalidSignature) try! await walletAuthClient.respond(requestId: request.id, signature: cacaoSignature) } } diff --git a/Example/IntegrationTests/Chat/RegistryTests.swift b/Example/IntegrationTests/Chat/RegistryTests.swift index 7a6fb6a1c..291cea4ea 100644 --- a/Example/IntegrationTests/Chat/RegistryTests.swift +++ b/Example/IntegrationTests/Chat/RegistryTests.swift @@ -1,5 +1,5 @@ import XCTest -import WalletConnectRelay +import WalletConnectNetworking import WalletConnectKMS import WalletConnectUtils @testable import Chat diff --git a/Example/Showcase/Classes/DomainLayer/Chat/ChatFactory.swift b/Example/Showcase/Classes/DomainLayer/Chat/ChatFactory.swift index b47b23b17..bffbf7332 100644 --- a/Example/Showcase/Classes/DomainLayer/Chat/ChatFactory.swift +++ b/Example/Showcase/Classes/DomainLayer/Chat/ChatFactory.swift @@ -1,7 +1,8 @@ import Foundation import Chat -import WalletConnectKMS +import WalletConnectNetworking import WalletConnectRelay +import WalletConnectKMS import WalletConnectUtils class ChatFactory { diff --git a/Example/Showcase/Classes/PresentationLayer/Wallet/AuthRequest/AuthRequestInteractor.swift b/Example/Showcase/Classes/PresentationLayer/Wallet/AuthRequest/AuthRequestInteractor.swift index ed3702e8f..8e7e4a2d5 100644 --- a/Example/Showcase/Classes/PresentationLayer/Wallet/AuthRequest/AuthRequestInteractor.swift +++ b/Example/Showcase/Classes/PresentationLayer/Wallet/AuthRequest/AuthRequestInteractor.swift @@ -6,7 +6,7 @@ final class AuthRequestInteractor { func approve(request: AuthRequest) async throws { let privateKey = Data(hex: "e56da0e170b5e09a8bb8f1b693392c7d56c3739a9c75740fbc558a2877868540") - let signer = MessageSigner() + let signer = MessageSignerFactory.create() let signature = try signer.sign(message: request.message, privateKey: privateKey) try await Auth.instance.respond(requestId: request.id, signature: signature) } diff --git a/Sources/Auth/Services/Signer/EIP1271/EIP1271Verifier.swift b/Sources/Auth/Services/Signer/EIP1271/EIP1271Verifier.swift index a6bac678f..4e4b1ba84 100644 --- a/Sources/Auth/Services/Signer/EIP1271/EIP1271Verifier.swift +++ b/Sources/Auth/Services/Signer/EIP1271/EIP1271Verifier.swift @@ -1,7 +1,7 @@ import Foundation import JSONRPC +import WalletConnectNetworking import WalletConnectUtils -import WalletConnectRelay actor EIP1271Verifier { private let projectId: String diff --git a/Sources/Auth/Services/Signer/EIP1271/RPCService.swift b/Sources/Auth/Services/Signer/EIP1271/RPCService.swift index 310f891ed..f6b0ba6cf 100644 --- a/Sources/Auth/Services/Signer/EIP1271/RPCService.swift +++ b/Sources/Auth/Services/Signer/EIP1271/RPCService.swift @@ -1,5 +1,5 @@ import Foundation -import WalletConnectRelay +import WalletConnectNetworking struct RPCService: HTTPService { let data: Data diff --git a/Sources/Auth/Services/Signer/MessageSigner.swift b/Sources/Auth/Services/Signer/MessageSigner.swift index 25f70338f..8ef6e51d1 100644 --- a/Sources/Auth/Services/Signer/MessageSigner.swift +++ b/Sources/Auth/Services/Signer/MessageSigner.swift @@ -1,14 +1,14 @@ import Foundation -protocol MessageSignatureVerifying { +public protocol MessageSignatureVerifying { func verify(signature: CacaoSignature, message: String, address: String) async throws } -protocol MessageSigning { +public protocol MessageSigning { func sign(message: String, privateKey: Data) throws -> CacaoSignature } -public struct MessageSigner: MessageSignatureVerifying, MessageSigning { +struct MessageSigner: MessageSignatureVerifying, MessageSigning { enum Errors: Error { case utf8EncodingFailed @@ -24,14 +24,14 @@ public struct MessageSigner: MessageSignatureVerifying, MessageSigning { self.eip1271Verifier = eip1271Verifier } - public func sign(message: String, privateKey: Data) throws -> CacaoSignature { + func sign(message: String, privateKey: Data) throws -> CacaoSignature { guard let messageData = message.data(using: .utf8) else { throw Errors.utf8EncodingFailed } let signature = try signer.sign(message: prefixed(messageData), with: privateKey) let prefixedHexSignature = "0x" + signature.toHexString() return CacaoSignature(t: .eip191, s: prefixedHexSignature) } - public func verify(signature: CacaoSignature, message: String, address: String) async throws { + func verify(signature: CacaoSignature, message: String, address: String) async throws { guard let messageData = message.data(using: .utf8) else { throw Errors.utf8EncodingFailed } diff --git a/Sources/Auth/Services/Signer/MessageSignerFactory.swift b/Sources/Auth/Services/Signer/MessageSignerFactory.swift index 96fa2487e..6a4a51971 100644 --- a/Sources/Auth/Services/Signer/MessageSignerFactory.swift +++ b/Sources/Auth/Services/Signer/MessageSignerFactory.swift @@ -1,9 +1,13 @@ import Foundation -import WalletConnectRelay +import WalletConnectNetworking public struct MessageSignerFactory { - public static func create(projectId: String) -> MessageSigner { + public static func create() -> MessageSigning & MessageSignatureVerifying { + return create(projectId: Networking.projectId) + } + + static func create(projectId: String) -> MessageSigning & MessageSignatureVerifying { return MessageSigner( signer: Signer(), eip191Verifier: EIP191Verifier(), diff --git a/Sources/Auth/Services/Signer/Signer.swift b/Sources/Auth/Services/Signer/Signer.swift index 3721db118..cbd56ca89 100644 --- a/Sources/Auth/Services/Signer/Signer.swift +++ b/Sources/Auth/Services/Signer/Signer.swift @@ -1,11 +1,11 @@ import Foundation import Web3 -public struct Signer { +struct Signer { typealias Signature = (v: UInt, r: [UInt8], s: [UInt8]) - public init() {} + init() {} func sign(message: Data, with key: Data) throws -> Data { let privateKey = try EthereumPrivateKey(privateKey: key.bytes) diff --git a/Sources/Chat/HTTPServices/RegisterService.swift b/Sources/Chat/HTTPServices/RegisterService.swift index 0734be114..618c64035 100644 --- a/Sources/Chat/HTTPServices/RegisterService.swift +++ b/Sources/Chat/HTTPServices/RegisterService.swift @@ -1,5 +1,5 @@ import Foundation -import WalletConnectRelay +import WalletConnectNetworking struct RegisterService: HTTPService { diff --git a/Sources/Chat/HTTPServices/ResolveService.swift b/Sources/Chat/HTTPServices/ResolveService.swift index 993833c64..795584000 100644 --- a/Sources/Chat/HTTPServices/ResolveService.swift +++ b/Sources/Chat/HTTPServices/ResolveService.swift @@ -1,5 +1,5 @@ import Foundation -import WalletConnectRelay +import WalletConnectNetworking import WalletConnectUtils struct ResolveService: HTTPService { diff --git a/Sources/Chat/Registry.swift b/Sources/Chat/Registry.swift index e7f31d357..bd8b82a55 100644 --- a/Sources/Chat/Registry.swift +++ b/Sources/Chat/Registry.swift @@ -1,5 +1,5 @@ import Foundation -import WalletConnectRelay +import WalletConnectNetworking import WalletConnectUtils public protocol Registry { diff --git a/Sources/WalletConnectRelay/HTTP/HTTPClient.swift b/Sources/WalletConnectNetworking/HTTPClient/HTTPClient.swift similarity index 100% rename from Sources/WalletConnectRelay/HTTP/HTTPClient.swift rename to Sources/WalletConnectNetworking/HTTPClient/HTTPClient.swift diff --git a/Sources/WalletConnectRelay/HTTP/HTTPError.swift b/Sources/WalletConnectNetworking/HTTPClient/HTTPError.swift similarity index 100% rename from Sources/WalletConnectRelay/HTTP/HTTPError.swift rename to Sources/WalletConnectNetworking/HTTPClient/HTTPError.swift diff --git a/Sources/WalletConnectRelay/HTTP/HTTPService.swift b/Sources/WalletConnectNetworking/HTTPClient/HTTPService.swift similarity index 100% rename from Sources/WalletConnectRelay/HTTP/HTTPService.swift rename to Sources/WalletConnectNetworking/HTTPClient/HTTPService.swift diff --git a/Sources/WalletConnectNetworking/Networking.swift b/Sources/WalletConnectNetworking/Networking.swift index 41def110b..37cf4a6c6 100644 --- a/Sources/WalletConnectNetworking/Networking.swift +++ b/Sources/WalletConnectNetworking/Networking.swift @@ -18,6 +18,13 @@ public class Networking { return NetworkingClientFactory.create(relayClient: Relay.instance) } + public static var projectId: String { + guard let projectId = config?.projectId else { + fatalError("Error - you must configure projectId with Networking.configure(_:)") + } + return projectId + } + private static var config: Config? private init() { } diff --git a/Tests/AuthTests/AppRespondSubscriberTests.swift b/Tests/AuthTests/AppRespondSubscriberTests.swift index 512144aa3..e00a455a3 100644 --- a/Tests/AuthTests/AppRespondSubscriberTests.swift +++ b/Tests/AuthTests/AppRespondSubscriberTests.swift @@ -15,7 +15,7 @@ class AppRespondSubscriberTests: XCTestCase { let defaultTimeout: TimeInterval = 0.01 let walletAccount = Account(chainIdentifier: "eip155:1", address: "0x724d0D2DaD3fbB0C168f947B87Fa5DBe36F1A8bf")! let prvKey = Data(hex: "462c1dad6832d7d96ccf87bd6a686a4110e114aaaebd5512e552c0e3a87b480f") - var messageSigner: MessageSigner! + var messageSigner: (MessageSigning & MessageSignatureVerifying)! var pairingStorage: WCPairingStorageMock! var pairingRegisterer: PairingRegistererMock! diff --git a/Tests/RelayerTests/Helpers/Error+Extension.swift b/Tests/RelayerTests/Helpers/Error+Extension.swift index bee887b0d..cf5525e24 100644 --- a/Tests/RelayerTests/Helpers/Error+Extension.swift +++ b/Tests/RelayerTests/Helpers/Error+Extension.swift @@ -1,5 +1,6 @@ import Foundation @testable import WalletConnectRelay +@testable import WalletConnectNetworking extension NSError { From 37b9568edd3fb921bbaef5bb42ed4302e2ab564c Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 13 Oct 2022 12:17:34 -0500 Subject: [PATCH 166/175] After merge fixes --- Example/DApp/Auth/AuthViewModel.swift | 11 ++++++----- Sources/Auth/Auth.swift | 9 ++++----- Sources/Auth/AuthConfig.swift | 1 - 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/Example/DApp/Auth/AuthViewModel.swift b/Example/DApp/Auth/AuthViewModel.swift index bb9035de5..69b0fb46e 100644 --- a/Example/DApp/Auth/AuthViewModel.swift +++ b/Example/DApp/Auth/AuthViewModel.swift @@ -14,10 +14,10 @@ final class AuthViewModel: ObservableObject { private var disposeBag = Set() @Published var state: SigningState = .none - @Published var uri: String? + @Published var uriString: String? var qrImage: UIImage? { - return uri.map { QRCodeGenerator.generateQRCode(from: $0) } + return uriString.map { QRCodeGenerator.generateQRCode(from: $0) } } init() { @@ -27,13 +27,14 @@ final class AuthViewModel: ObservableObject { @MainActor func setupInitialState() async throws { state = .none - uri = nil + uriString = nil let uri = try! await Pair.instance.create() + uriString = uri.absoluteString try await Auth.instance.request(.stub(), topic: uri.topic) } func copyDidPressed() { - UIPasteboard.general.string = uri + UIPasteboard.general.string = uriString } func walletDidPressed() { @@ -41,7 +42,7 @@ final class AuthViewModel: ObservableObject { } func deeplinkPressed() { - guard let uri = uri else { return } + guard let uri = uriString else { return } UIApplication.shared.open(URL(string: "showcase://wc?uri=\(uri)")!) } } diff --git a/Sources/Auth/Auth.swift b/Sources/Auth/Auth.swift index 297986b7c..274ad962e 100644 --- a/Sources/Auth/Auth.swift +++ b/Sources/Auth/Auth.swift @@ -18,13 +18,12 @@ public class Auth { /// Auth client instance public static var instance: AuthClient = { - return AuthClientFactory.create( - metadata: config.metadata, - account: config.account, + metadata: Pair.metadata, + account: config?.account, projectId: Networking.projectId, - networkingClient: Networking.instance, - pairingRegisterer: Pair.instance + networkingClient: Networking.interactor, + pairingRegisterer: Pair.registerer ) }() diff --git a/Sources/Auth/AuthConfig.swift b/Sources/Auth/AuthConfig.swift index 965d3aa2c..00a98faed 100644 --- a/Sources/Auth/AuthConfig.swift +++ b/Sources/Auth/AuthConfig.swift @@ -3,6 +3,5 @@ import Foundation extension Auth { struct Config { let account: Account? - let projectId: String } } From 554351a961bebf0406c3cf4c33e55c84baf1637b Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 13 Oct 2022 12:39:14 -0500 Subject: [PATCH 167/175] EIP1271VerifierTests unit tests --- .../IntegrationTests/Chat/RegistryTests.swift | 2 +- .../DomainLayer/Chat/ChatFactory.swift | 2 +- .../Signer/EIP1271/EIP1271Verifier.swift | 2 +- .../Signer/MessageSignerFactory.swift | 2 +- .../HTTPClient/HTTPClient.swift | 80 +------------------ .../HTTPClient/HTTPNetworkClient.swift | 80 +++++++++++++++++++ Tests/AuthTests/EIP1271VerifierTests.swift | 35 +++++--- Tests/TestingUtils/Mocks/HTTPClientMock.swift | 19 +++++ 8 files changed, 132 insertions(+), 90 deletions(-) create mode 100644 Sources/WalletConnectNetworking/HTTPClient/HTTPNetworkClient.swift create mode 100644 Tests/TestingUtils/Mocks/HTTPClientMock.swift diff --git a/Example/IntegrationTests/Chat/RegistryTests.swift b/Example/IntegrationTests/Chat/RegistryTests.swift index 291cea4ea..eb98d22f2 100644 --- a/Example/IntegrationTests/Chat/RegistryTests.swift +++ b/Example/IntegrationTests/Chat/RegistryTests.swift @@ -7,7 +7,7 @@ import WalletConnectUtils final class RegistryTests: XCTestCase { func testRegistry() async throws { - let client = HTTPClient(host: "keys.walletconnect.com") + let client = HTTPNetworkClient(host: "keys.walletconnect.com") let registry = KeyserverRegistryProvider(client: client) let account = Account("eip155:1:" + Data.randomBytes(count: 16).toHexString())! let pubKey = SigningPrivateKey().publicKey.hexRepresentation diff --git a/Example/Showcase/Classes/DomainLayer/Chat/ChatFactory.swift b/Example/Showcase/Classes/DomainLayer/Chat/ChatFactory.swift index bffbf7332..8b4e0c597 100644 --- a/Example/Showcase/Classes/DomainLayer/Chat/ChatFactory.swift +++ b/Example/Showcase/Classes/DomainLayer/Chat/ChatFactory.swift @@ -9,7 +9,7 @@ class ChatFactory { static func create() -> ChatClient { let keychain = KeychainStorage(serviceIdentifier: "com.walletconnect.showcase") - let client = HTTPClient(host: "keys.walletconnect.com") + let client = HTTPNetworkClient(host: "keys.walletconnect.com") let registry = KeyserverRegistryProvider(client: client) return ChatClientFactory.create( registry: registry, diff --git a/Sources/Auth/Services/Signer/EIP1271/EIP1271Verifier.swift b/Sources/Auth/Services/Signer/EIP1271/EIP1271Verifier.swift index 4e4b1ba84..b6266173d 100644 --- a/Sources/Auth/Services/Signer/EIP1271/EIP1271Verifier.swift +++ b/Sources/Auth/Services/Signer/EIP1271/EIP1271Verifier.swift @@ -5,7 +5,7 @@ import WalletConnectUtils actor EIP1271Verifier { private let projectId: String - private let httpClient: HTTPClient // TODO: Move in Networking package? + private let httpClient: HTTPClient init(projectId: String, httpClient: HTTPClient) { self.projectId = projectId diff --git a/Sources/Auth/Services/Signer/MessageSignerFactory.swift b/Sources/Auth/Services/Signer/MessageSignerFactory.swift index 6a4a51971..81ba54de5 100644 --- a/Sources/Auth/Services/Signer/MessageSignerFactory.swift +++ b/Sources/Auth/Services/Signer/MessageSignerFactory.swift @@ -13,7 +13,7 @@ public struct MessageSignerFactory { eip191Verifier: EIP191Verifier(), eip1271Verifier: EIP1271Verifier( projectId: projectId, - httpClient: HTTPClient(host: "rpc.walletconnect.com") + httpClient: HTTPNetworkClient(host: "rpc.walletconnect.com") ) ) } diff --git a/Sources/WalletConnectNetworking/HTTPClient/HTTPClient.swift b/Sources/WalletConnectNetworking/HTTPClient/HTTPClient.swift index 03ec8626d..0fb4c8a68 100644 --- a/Sources/WalletConnectNetworking/HTTPClient/HTTPClient.swift +++ b/Sources/WalletConnectNetworking/HTTPClient/HTTPClient.swift @@ -1,80 +1,6 @@ import Foundation -public actor HTTPClient { - - let host: String - - private let session: URLSession - - public init(host: String, session: URLSession = .shared) { - self.host = host - self.session = session - } - - public func request(_ type: T.Type, at service: HTTPService) async throws -> T { - return try await withCheckedThrowingContinuation { continuation in - request(T.self, at: service) { result in - do { - let value = try result.get() - continuation.resume(returning: value) - } catch { - continuation.resume(throwing: error) - } - } - } - } - - public func request(service: HTTPService) async throws { - return try await withCheckedThrowingContinuation { continuation in - request(service: service) { result in - continuation.resume(with: result) - } - } - } - - func request(_ type: T.Type, at service: HTTPService, completion: @escaping (Result) -> Void) { - guard let request = service.resolve(for: host) else { - completion(.failure(HTTPError.malformedURL(service))) - return - } - session.dataTask(with: request) { data, response, error in - do { - try HTTPClient.validate(response, error) - guard let validData = data else { - throw HTTPError.responseDataNil - } - let decoded = try JSONDecoder().decode(T.self, from: validData) - completion(.success(decoded)) - } catch { - completion(.failure(error)) - } - }.resume() - } - - func request(service: HTTPService, completion: @escaping (Result) -> Void) { - guard let request = service.resolve(for: host) else { - completion(.failure(HTTPError.malformedURL(service))) - return - } - session.dataTask(with: request) { _, response, error in - do { - try HTTPClient.validate(response, error) - completion(.success(())) - } catch { - completion(.failure(error)) - } - }.resume() - } - - private static func validate(_ urlResponse: URLResponse?, _ error: Error?) throws { - if let error = error { - throw HTTPError.dataTaskError(error) - } - guard let httpResponse = urlResponse as? HTTPURLResponse else { - throw HTTPError.noResponse - } - guard (200..<300) ~= httpResponse.statusCode else { - throw HTTPError.badStatusCode(httpResponse.statusCode) - } - } +public protocol HTTPClient { + func request(_ type: T.Type, at service: HTTPService) async throws -> T + func request(service: HTTPService) async throws } diff --git a/Sources/WalletConnectNetworking/HTTPClient/HTTPNetworkClient.swift b/Sources/WalletConnectNetworking/HTTPClient/HTTPNetworkClient.swift new file mode 100644 index 000000000..b87948bd6 --- /dev/null +++ b/Sources/WalletConnectNetworking/HTTPClient/HTTPNetworkClient.swift @@ -0,0 +1,80 @@ +import Foundation + +public actor HTTPNetworkClient: HTTPClient { + + let host: String + + private let session: URLSession + + public init(host: String, session: URLSession = .shared) { + self.host = host + self.session = session + } + + public func request(_ type: T.Type, at service: HTTPService) async throws -> T { + return try await withCheckedThrowingContinuation { continuation in + request(T.self, at: service) { result in + do { + let value = try result.get() + continuation.resume(returning: value) + } catch { + continuation.resume(throwing: error) + } + } + } + } + + public func request(service: HTTPService) async throws { + return try await withCheckedThrowingContinuation { continuation in + request(service: service) { result in + continuation.resume(with: result) + } + } + } + + private func request(_ type: T.Type, at service: HTTPService, completion: @escaping (Result) -> Void) { + guard let request = service.resolve(for: host) else { + completion(.failure(HTTPError.malformedURL(service))) + return + } + session.dataTask(with: request) { data, response, error in + do { + try HTTPNetworkClient.validate(response, error) + guard let validData = data else { + throw HTTPError.responseDataNil + } + let decoded = try JSONDecoder().decode(T.self, from: validData) + completion(.success(decoded)) + } catch { + completion(.failure(error)) + } + }.resume() + } + + private func request(service: HTTPService, completion: @escaping (Result) -> Void) { + guard let request = service.resolve(for: host) else { + completion(.failure(HTTPError.malformedURL(service))) + return + } + session.dataTask(with: request) { _, response, error in + do { + try HTTPNetworkClient.validate(response, error) + completion(.success(())) + } catch { + completion(.failure(error)) + } + }.resume() + } + + private static func validate(_ urlResponse: URLResponse?, _ error: Error?) throws { + if let error = error { + throw HTTPError.dataTaskError(error) + } + guard let httpResponse = urlResponse as? HTTPURLResponse else { + throw HTTPError.noResponse + } + guard (200..<300) ~= httpResponse.statusCode else { + throw HTTPError.badStatusCode(httpResponse.statusCode) + } + } +} diff --git a/Tests/AuthTests/EIP1271VerifierTests.swift b/Tests/AuthTests/EIP1271VerifierTests.swift index 74cc3d4d8..5c2632728 100644 --- a/Tests/AuthTests/EIP1271VerifierTests.swift +++ b/Tests/AuthTests/EIP1271VerifierTests.swift @@ -1,18 +1,35 @@ import Foundation import XCTest @testable import Auth +import JSONRPC +import TestingUtils class EIP1271VerifierTests: XCTestCase { -// private let verifier = EIP1271Verifier(projectId: "project-id", httpClient: HTTPClient) + let signature = Data(hex: "c1505719b2504095116db01baaf276361efd3a73c28cf8cc28dabefa945b8d536011289ac0a3b048600c1e692ff173ca944246cf7ceb319ac2262d27b395c82b1c") + let message = Data(hex: "3aaa8393796c7388e4e062861d8238503de7584c977676fe9d8d551c30e11f84") + let address = "0x2faf83c542b68f1b4cdc0e770e8cb9f567b08f71" - func testVerify() async throws { - // let client = HTTPClient(host: "rpc.walletconnect.com") - // let service = EIP1271Verifier(projectId: "", httpClient: client) - // try await service.verify( - // signature: Data(hex: "c1505719b2504095116db01baaf276361efd3a73c28cf8cc28dabefa945b8d536011289ac0a3b048600c1e692ff173ca944246cf7ceb319ac2262d27b395c82b1c"), - // messageHash: Data(hex: "3aaa8393796c7388e4e062861d8238503de7584c977676fe9d8d551c30e11f84"), - // address: "0x2faf83c542b68f1b4cdc0e770e8cb9f567b08f71" - // ) + func testSuccessVerify() async throws { + let response = RPCResponse(id: "1", result: "0x1626ba7e00000000000000000000000000000000000000000000000000000000") + let httpClient = HTTPClientMock(object: response) + let verifier = EIP1271Verifier(projectId: "project-id", httpClient: httpClient) + try await verifier.verify( + signature: signature, + message: message, + address: address + ) + } + + func testFailureVerify() async throws { + let response = RPCResponse(id: "1", error: .internalError) + let httpClient = HTTPClientMock(object: response) + let verifier = EIP1271Verifier(projectId: "project-id", httpClient: httpClient) + + await XCTAssertThrowsErrorAsync(try await verifier.verify( + signature: signature, + message: message, + address: address + )) } } diff --git a/Tests/TestingUtils/Mocks/HTTPClientMock.swift b/Tests/TestingUtils/Mocks/HTTPClientMock.swift new file mode 100644 index 000000000..bca495c63 --- /dev/null +++ b/Tests/TestingUtils/Mocks/HTTPClientMock.swift @@ -0,0 +1,19 @@ +import Foundation +import WalletConnectNetworking + +public final class HTTPClientMock: HTTPClient { + + private let object: T + + public init(object: T) { + self.object = object + } + + public func request(_ type: T.Type, at service: HTTPService) async throws -> T where T : Decodable { + return object as! T + } + + public func request(service: HTTPService) async throws { + + } +} From 2a16c8d92ef3a3a29060a6c023b6d00d36a80698 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 13 Oct 2022 13:20:07 -0500 Subject: [PATCH 168/175] SigType in public interface --- Example/IntegrationTests/Auth/AuthTests.swift | 4 ++-- .../Wallet/AuthRequest/AuthRequestInteractor.swift | 2 +- Sources/Auth/Services/Signer/MessageSigner.swift | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Example/IntegrationTests/Auth/AuthTests.swift b/Example/IntegrationTests/Auth/AuthTests.swift index af2fe58b0..776f51b65 100644 --- a/Example/IntegrationTests/Auth/AuthTests.swift +++ b/Example/IntegrationTests/Auth/AuthTests.swift @@ -67,7 +67,7 @@ final class AuthTests: XCTestCase { wait(for: [requestExpectation], timeout: InputConfig.defaultTimeout) } - func testRespondSuccess() async { + func testEIP191RespondSuccess() async { let responseExpectation = expectation(description: "successful response delivered") let uri = try! await appPairingClient.create() try! await appAuthClient.request(RequestParams.stub(), topic: uri.topic) @@ -76,7 +76,7 @@ final class AuthTests: XCTestCase { walletAuthClient.authRequestPublisher.sink { [unowned self] request in Task(priority: .high) { let signer = MessageSignerFactory.create(projectId: InputConfig.projectId) - let signature = try! signer.sign(message: request.message, privateKey: prvKey) + let signature = try! signer.sign(message: request.message, privateKey: prvKey, type: .eip191) try! await walletAuthClient.respond(requestId: request.id, signature: signature) } } diff --git a/Example/Showcase/Classes/PresentationLayer/Wallet/AuthRequest/AuthRequestInteractor.swift b/Example/Showcase/Classes/PresentationLayer/Wallet/AuthRequest/AuthRequestInteractor.swift index 8e7e4a2d5..c0c3a8bd3 100644 --- a/Example/Showcase/Classes/PresentationLayer/Wallet/AuthRequest/AuthRequestInteractor.swift +++ b/Example/Showcase/Classes/PresentationLayer/Wallet/AuthRequest/AuthRequestInteractor.swift @@ -7,7 +7,7 @@ final class AuthRequestInteractor { func approve(request: AuthRequest) async throws { let privateKey = Data(hex: "e56da0e170b5e09a8bb8f1b693392c7d56c3739a9c75740fbc558a2877868540") let signer = MessageSignerFactory.create() - let signature = try signer.sign(message: request.message, privateKey: privateKey) + let signature = try signer.sign(message: request.message, privateKey: privateKey, type: .eip191) try await Auth.instance.respond(requestId: request.id, signature: signature) } diff --git a/Sources/Auth/Services/Signer/MessageSigner.swift b/Sources/Auth/Services/Signer/MessageSigner.swift index 8ef6e51d1..224de89c1 100644 --- a/Sources/Auth/Services/Signer/MessageSigner.swift +++ b/Sources/Auth/Services/Signer/MessageSigner.swift @@ -5,7 +5,7 @@ public protocol MessageSignatureVerifying { } public protocol MessageSigning { - func sign(message: String, privateKey: Data) throws -> CacaoSignature + func sign(message: String, privateKey: Data, type: CacaoSignatureType) throws -> CacaoSignature } struct MessageSigner: MessageSignatureVerifying, MessageSigning { @@ -24,11 +24,11 @@ struct MessageSigner: MessageSignatureVerifying, MessageSigning { self.eip1271Verifier = eip1271Verifier } - func sign(message: String, privateKey: Data) throws -> CacaoSignature { + func sign(message: String, privateKey: Data, type: CacaoSignatureType) throws -> CacaoSignature { guard let messageData = message.data(using: .utf8) else { throw Errors.utf8EncodingFailed } let signature = try signer.sign(message: prefixed(messageData), with: privateKey) let prefixedHexSignature = "0x" + signature.toHexString() - return CacaoSignature(t: .eip191, s: prefixedHexSignature) + return CacaoSignature(t: type, s: prefixedHexSignature) } func verify(signature: CacaoSignature, message: String, address: String) async throws { From 15415cc0847b370a22ec412a661a111235722fce Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 13 Oct 2022 14:43:57 -0500 Subject: [PATCH 169/175] EIP1271 integration tests --- Example/IntegrationTests/Auth/AuthTests.swift | 75 +++++++++++++++++-- Sources/Auth/AuthClientFactory.swift | 6 +- .../Auth/Services/App/AppRequestService.swift | 8 +- .../Auth/Services/Common/IATProvider.swift | 11 +++ .../Auth/Services/Signer/MessageSigner.swift | 2 +- 5 files changed, 90 insertions(+), 12 deletions(-) create mode 100644 Sources/Auth/Services/Common/IATProvider.swift diff --git a/Example/IntegrationTests/Auth/AuthTests.swift b/Example/IntegrationTests/Auth/AuthTests.swift index 776f51b65..0c83ef21b 100644 --- a/Example/IntegrationTests/Auth/AuthTests.swift +++ b/Example/IntegrationTests/Auth/AuthTests.swift @@ -15,16 +15,20 @@ final class AuthTests: XCTestCase { var appAuthClient: AuthClient! var walletAuthClient: AuthClient! let prvKey = Data(hex: "462c1dad6832d7d96ccf87bd6a686a4110e114aaaebd5512e552c0e3a87b480f") + let eip1271Signature = "0xc1505719b2504095116db01baaf276361efd3a73c28cf8cc28dabefa945b8d536011289ac0a3b048600c1e692ff173ca944246cf7ceb319ac2262d27b395c82b1c" private var publishers = [AnyCancellable]() override func setUp() { - let walletAccount = Account(chainIdentifier: "eip155:1", address: "0x724d0D2DaD3fbB0C168f947B87Fa5DBe36F1A8bf")! + setupClients() + } - (appPairingClient, appAuthClient) = makeClients(prefix: "🤖 App") - (walletPairingClient, walletAuthClient) = makeClients(prefix: "🐶 Wallet", account: walletAccount) + private func setupClients(address: String = "0x724d0D2DaD3fbB0C168f947B87Fa5DBe36F1A8bf", iatProvider: IATProvider = DefaultIATProvider()) { + let walletAccount = Account(chainIdentifier: "eip155:1", address: address)! + (appPairingClient, appAuthClient) = makeClients(prefix: "🤖 App", iatProvider: iatProvider) + (walletPairingClient, walletAuthClient) = makeClients(prefix: "🐶 Wallet", account: walletAccount, iatProvider: iatProvider) } - func makeClients(prefix: String, account: Account? = nil) -> (PairingClient, AuthClient) { + func makeClients(prefix: String, account: Account? = nil, iatProvider: IATProvider) -> (PairingClient, AuthClient) { let logger = ConsoleLogger(suffix: prefix, loggingLevel: .debug) let keychain = KeychainStorageMock() let relayClient = RelayClient(relayHost: InputConfig.relayHost, projectId: InputConfig.projectId, keychainStorage: keychain, socketFactory: SocketFactory(), logger: logger) @@ -50,7 +54,8 @@ final class AuthTests: XCTestCase { keyValueStorage: keyValueStorage, keychainStorage: keychain, networkingClient: networkingClient, - pairingRegisterer: pairingClient) + pairingRegisterer: pairingClient, + iatProvider: iatProvider) return (pairingClient, authClient) } @@ -89,6 +94,60 @@ final class AuthTests: XCTestCase { wait(for: [responseExpectation], timeout: InputConfig.defaultTimeout) } + func testEIP1271RespondSuccess() async { + setupClients(address: "0x2faf83c542b68f1b4cdc0e770e8cb9f567b08f71", iatProvider: IATProviderMock()) + + let responseExpectation = expectation(description: "successful response delivered") + let uri = try! await appPairingClient.create() + try! await appAuthClient.request(RequestParams( + domain: "localhost", + chainId: "eip155:1", + nonce: "1665443015700", + aud: "http://localhost:3000/", + nbf: nil, + exp: "2022-10-11T23:03:35.700Z", + statement: nil, + requestId: nil, + resources: nil + ), topic: uri.topic) + + try! await walletPairingClient.pair(uri: uri) + walletAuthClient.authRequestPublisher.sink { [unowned self] request in + Task(priority: .high) { + let signature = CacaoSignature(t: .eip1271, s: eip1271Signature) + try! await walletAuthClient.respond(requestId: request.id, signature: signature) + } + } + .store(in: &publishers) + appAuthClient.authResponsePublisher.sink { (_, result) in + guard case .success = result else { XCTFail(); return } + responseExpectation.fulfill() + } + .store(in: &publishers) + wait(for: [responseExpectation], timeout: InputConfig.defaultTimeout) + } + + func testEIP191RespondError() async { + let responseExpectation = expectation(description: "successful response delivered") + let uri = try! await appPairingClient.create() + try! await appAuthClient.request(RequestParams.stub(), topic: uri.topic) + + try! await walletPairingClient.pair(uri: uri) + walletAuthClient.authRequestPublisher.sink { [unowned self] request in + Task(priority: .high) { + let signature = CacaoSignature(t: .eip1271, s: eip1271Signature) + try! await walletAuthClient.respond(requestId: request.id, signature: signature) + } + } + .store(in: &publishers) + appAuthClient.authResponsePublisher.sink { (_, result) in + guard case let .failure(error) = result, error == .signatureVerificationFailed else { XCTFail(); return } + responseExpectation.fulfill() + } + .store(in: &publishers) + wait(for: [responseExpectation], timeout: InputConfig.defaultTimeout) + } + func testUserRespondError() async { let responseExpectation = expectation(description: "error response delivered") let uri = try! await appPairingClient.create() @@ -133,3 +192,9 @@ final class AuthTests: XCTestCase { wait(for: [responseExpectation], timeout: InputConfig.defaultTimeout) } } + +private struct IATProviderMock: IATProvider { + var iat: String { + return "2022-10-10T23:03:35.700Z" + } +} diff --git a/Sources/Auth/AuthClientFactory.swift b/Sources/Auth/AuthClientFactory.swift index 999b28977..d2cb35859 100644 --- a/Sources/Auth/AuthClientFactory.swift +++ b/Sources/Auth/AuthClientFactory.swift @@ -11,14 +11,14 @@ public struct AuthClientFactory { let logger = ConsoleLogger(loggingLevel: .off) let keyValueStorage = UserDefaults.standard let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk") - return AuthClientFactory.create(metadata: metadata, account: account, projectId: projectId, logger: logger, keyValueStorage: keyValueStorage, keychainStorage: keychainStorage, networkingClient: networkingClient, pairingRegisterer: pairingRegisterer) + return AuthClientFactory.create(metadata: metadata, account: account, projectId: projectId, logger: logger, keyValueStorage: keyValueStorage, keychainStorage: keychainStorage, networkingClient: networkingClient, pairingRegisterer: pairingRegisterer, iatProvider: DefaultIATProvider()) } - static func create(metadata: AppMetadata, account: Account?, projectId: String, logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, networkingClient: NetworkingInteractor, pairingRegisterer: PairingRegisterer) -> AuthClient { + static func create(metadata: AppMetadata, account: Account?, projectId: String, logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, networkingClient: NetworkingInteractor, pairingRegisterer: PairingRegisterer, iatProvider: IATProvider) -> AuthClient { let kms = KeyManagementService(keychain: keychainStorage) let history = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage) let messageFormatter = SIWEMessageFormatter() - let appRequestService = AppRequestService(networkingInteractor: networkingClient, kms: kms, appMetadata: metadata, logger: logger) + let appRequestService = AppRequestService(networkingInteractor: networkingClient, kms: kms, appMetadata: metadata, logger: logger, iatProvader: iatProvider) let messageSigner = MessageSignerFactory.create(projectId: projectId) let appRespondSubscriber = AppRespondSubscriber(networkingInteractor: networkingClient, logger: logger, rpcHistory: history, signatureVerifier: messageSigner, pairingRegisterer: pairingRegisterer, messageFormatter: messageFormatter) let walletErrorResponder = WalletErrorResponder(networkingInteractor: networkingClient, logger: logger, kms: kms, rpcHistory: history) diff --git a/Sources/Auth/Services/App/AppRequestService.swift b/Sources/Auth/Services/App/AppRequestService.swift index 54c492e8a..6ea6ab339 100644 --- a/Sources/Auth/Services/App/AppRequestService.swift +++ b/Sources/Auth/Services/App/AppRequestService.swift @@ -9,23 +9,25 @@ actor AppRequestService { private let appMetadata: AppMetadata private let kms: KeyManagementService private let logger: ConsoleLogging + private let iatProvader: IATProvider init(networkingInteractor: NetworkInteracting, kms: KeyManagementService, appMetadata: AppMetadata, - logger: ConsoleLogging) { + logger: ConsoleLogging, + iatProvader: IATProvider) { self.networkingInteractor = networkingInteractor self.kms = kms self.appMetadata = appMetadata self.logger = logger + self.iatProvader = iatProvader } func request(params: RequestParams, topic: String) async throws { let pubKey = try kms.createX25519KeyPair() let responseTopic = pubKey.rawRepresentation.sha256().toHexString() let requester = AuthRequestParams.Requester(publicKey: pubKey.hexRepresentation, metadata: appMetadata) - let issueAt = ISO8601DateFormatter().string(from: Date()) - let payload = AuthPayload(requestParams: params, iat: issueAt) + let payload = AuthPayload(requestParams: params, iat: iatProvader.iat) let params = AuthRequestParams(requester: requester, payloadParams: payload) let request = RPCRequest(method: "wc_authRequest", params: params) try kms.setPublicKey(publicKey: pubKey, for: responseTopic) diff --git a/Sources/Auth/Services/Common/IATProvider.swift b/Sources/Auth/Services/Common/IATProvider.swift new file mode 100644 index 000000000..1d386c11e --- /dev/null +++ b/Sources/Auth/Services/Common/IATProvider.swift @@ -0,0 +1,11 @@ +import Foundation + +protocol IATProvider { + var iat: String { get } +} + +struct DefaultIATProvider: IATProvider { + var iat: String { + return ISO8601DateFormatter().string(from: Date()) + } +} diff --git a/Sources/Auth/Services/Signer/MessageSigner.swift b/Sources/Auth/Services/Signer/MessageSigner.swift index 224de89c1..c2f16442e 100644 --- a/Sources/Auth/Services/Signer/MessageSigner.swift +++ b/Sources/Auth/Services/Signer/MessageSigner.swift @@ -46,7 +46,7 @@ struct MessageSigner: MessageSignatureVerifying, MessageSigning { address: address ) case .eip1271: - return try await eip191Verifier.verify( + return try await eip1271Verifier.verify( signature: signatureData, message: prefixed(messageData), address: address From 0174c01230fc134337e0d00aadc8f2ef457cd162 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 13 Oct 2022 15:00:44 -0500 Subject: [PATCH 170/175] Unit test build fixed --- Sources/Auth/Services/Signer/EIP1271/ValidSignatureMethod.swift | 1 - Sources/Auth/Services/Signer/EIP191/EIP191Verifier.swift | 1 - Tests/AuthTests/AppRespondSubscriberTests.swift | 2 +- Tests/AuthTests/CacaoSignerTests.swift | 2 +- 4 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Sources/Auth/Services/Signer/EIP1271/ValidSignatureMethod.swift b/Sources/Auth/Services/Signer/EIP1271/ValidSignatureMethod.swift index 950602a63..134d62427 100644 --- a/Sources/Auth/Services/Signer/EIP1271/ValidSignatureMethod.swift +++ b/Sources/Auth/Services/Signer/EIP1271/ValidSignatureMethod.swift @@ -6,7 +6,6 @@ struct ValidSignatureMethod { static let signatureLength = "0000000000000000000000000000000000000000000000000000000000000041" static let signaturePadding = "00000000000000000000000000000000000000000000000000000000000000" - let signature: Data let messageHash: Data diff --git a/Sources/Auth/Services/Signer/EIP191/EIP191Verifier.swift b/Sources/Auth/Services/Signer/EIP191/EIP191Verifier.swift index be9d7a362..7622cae14 100644 --- a/Sources/Auth/Services/Signer/EIP191/EIP191Verifier.swift +++ b/Sources/Auth/Services/Signer/EIP191/EIP191Verifier.swift @@ -14,7 +14,6 @@ actor EIP191Verifier { try verifyPublicKey(publicKey, address: address) } - // TODO: typalias file ??? private func decompose(signature: Data) -> Signer.Signature { let v = signature.bytes[signature.count-1] let r = signature.bytes[0..<32] diff --git a/Tests/AuthTests/AppRespondSubscriberTests.swift b/Tests/AuthTests/AppRespondSubscriberTests.swift index e00a455a3..07492323e 100644 --- a/Tests/AuthTests/AppRespondSubscriberTests.swift +++ b/Tests/AuthTests/AppRespondSubscriberTests.swift @@ -57,7 +57,7 @@ class AppRespondSubscriberTests: XCTestCase { let payload = CacaoPayload(params: AuthPayload.stub(nonce: "compromised nonce"), didpkh: DIDPKH(account: walletAccount)) let message = try! messageFormatter.formatMessage(from: payload) - let cacaoSignature = try! messageSigner.sign(message: message, privateKey: prvKey) + let cacaoSignature = try! messageSigner.sign(message: message, privateKey: prvKey, type: .eip191) let cacao = Cacao(h: header, p: payload, s: cacaoSignature) diff --git a/Tests/AuthTests/CacaoSignerTests.swift b/Tests/AuthTests/CacaoSignerTests.swift index 258bde093..98715eb36 100644 --- a/Tests/AuthTests/CacaoSignerTests.swift +++ b/Tests/AuthTests/CacaoSignerTests.swift @@ -29,7 +29,7 @@ class CacaoSignerTest: XCTestCase { let signature = CacaoSignature(t: .eip191, s: "0x438effc459956b57fcd9f3dac6c675f9cee88abf21acab7305e8e32aa0303a883b06dcbd956279a7a2ca21ffa882ff55cc22e8ab8ec0f3fe90ab45f306938cfa1b") func testCacaoSign() throws { - XCTAssertEqual(try signer.sign(message: message, privateKey: privateKey), signature) + XCTAssertEqual(try signer.sign(message: message, privateKey: privateKey, type: .eip191), signature) } func testCacaoVerify() async throws { From 87831d2f13684f710faf6823e3a5bff7585bb1d3 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Wed, 19 Oct 2022 02:18:25 +0600 Subject: [PATCH 171/175] RPCService: chainID from AuthRequestParams --- Example/IntegrationTests/Auth/AuthTests.swift | 2 +- Sources/Auth/Services/App/AppRespondSubscriber.swift | 3 ++- Sources/Auth/Services/Signer/EIP1271/EIP1271Verifier.swift | 4 ++-- Sources/Auth/Services/Signer/EIP1271/RPCService.swift | 3 ++- Sources/Auth/Services/Signer/MessageSigner.swift | 7 ++++--- 5 files changed, 11 insertions(+), 8 deletions(-) diff --git a/Example/IntegrationTests/Auth/AuthTests.swift b/Example/IntegrationTests/Auth/AuthTests.swift index 0c83ef21b..ea165a98f 100644 --- a/Example/IntegrationTests/Auth/AuthTests.swift +++ b/Example/IntegrationTests/Auth/AuthTests.swift @@ -128,7 +128,7 @@ final class AuthTests: XCTestCase { } func testEIP191RespondError() async { - let responseExpectation = expectation(description: "successful response delivered") + let responseExpectation = expectation(description: "error response delivered") let uri = try! await appPairingClient.create() try! await appAuthClient.request(RequestParams.stub(), topic: uri.topic) diff --git a/Sources/Auth/Services/App/AppRespondSubscriber.swift b/Sources/Auth/Services/App/AppRespondSubscriber.swift index db811c60d..4b5628221 100644 --- a/Sources/Auth/Services/App/AppRespondSubscriber.swift +++ b/Sources/Auth/Services/App/AppRespondSubscriber.swift @@ -61,7 +61,8 @@ class AppRespondSubscriber { try await signatureVerifier.verify( signature: cacao.s, message: message, - address: address + address: address, + chainId: requestPayload.payloadParams.chainId ) onResponse?(requestId, .success(cacao)) } catch { diff --git a/Sources/Auth/Services/Signer/EIP1271/EIP1271Verifier.swift b/Sources/Auth/Services/Signer/EIP1271/EIP1271Verifier.swift index b6266173d..a68dd7742 100644 --- a/Sources/Auth/Services/Signer/EIP1271/EIP1271Verifier.swift +++ b/Sources/Auth/Services/Signer/EIP1271/EIP1271Verifier.swift @@ -12,13 +12,13 @@ actor EIP1271Verifier { self.httpClient = httpClient } - func verify(signature: Data, message: Data, address: String) async throws { + func verify(signature: Data, message: Data, address: String, chainId: String) async throws { let encoder = ValidSignatureMethod(signature: signature, messageHash: message.keccak256) let call = EthCall(to: address, data: encoder.encode()) let params = AnyCodable([AnyCodable(call), AnyCodable("latest")]) let request = RPCRequest(method: "eth_call", params: params) let data = try JSONEncoder().encode(request) - let httpService = RPCService(data: data, projectId: projectId) + let httpService = RPCService(data: data, projectId: projectId, chainId: chainId) let response = try await httpClient.request(RPCResponse.self, at: httpService) try validateResponse(response) } diff --git a/Sources/Auth/Services/Signer/EIP1271/RPCService.swift b/Sources/Auth/Services/Signer/EIP1271/RPCService.swift index f6b0ba6cf..095b53011 100644 --- a/Sources/Auth/Services/Signer/EIP1271/RPCService.swift +++ b/Sources/Auth/Services/Signer/EIP1271/RPCService.swift @@ -4,6 +4,7 @@ import WalletConnectNetworking struct RPCService: HTTPService { let data: Data let projectId: String + let chainId: String var path: String { return "/v1" @@ -23,7 +24,7 @@ struct RPCService: HTTPService { var queryParameters: [String : String]? { return [ - "chainId": "eip155:1", + "chainId": chainId, "projectId": projectId ] } diff --git a/Sources/Auth/Services/Signer/MessageSigner.swift b/Sources/Auth/Services/Signer/MessageSigner.swift index c2f16442e..fd4ffffbb 100644 --- a/Sources/Auth/Services/Signer/MessageSigner.swift +++ b/Sources/Auth/Services/Signer/MessageSigner.swift @@ -1,7 +1,7 @@ import Foundation public protocol MessageSignatureVerifying { - func verify(signature: CacaoSignature, message: String, address: String) async throws + func verify(signature: CacaoSignature, message: String, address: String, chainId: String) async throws } public protocol MessageSigning { @@ -31,7 +31,7 @@ struct MessageSigner: MessageSignatureVerifying, MessageSigning { return CacaoSignature(t: type, s: prefixedHexSignature) } - func verify(signature: CacaoSignature, message: String, address: String) async throws { + func verify(signature: CacaoSignature, message: String, address: String, chainId: String) async throws { guard let messageData = message.data(using: .utf8) else { throw Errors.utf8EncodingFailed } @@ -49,7 +49,8 @@ struct MessageSigner: MessageSignatureVerifying, MessageSigning { return try await eip1271Verifier.verify( signature: signatureData, message: prefixed(messageData), - address: address + address: address, + chainId: chainId ) } } From 52edd5ba73b926d804025e62d2d07a4a9dc882bb Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Wed, 19 Oct 2022 02:20:33 +0600 Subject: [PATCH 172/175] Unit tests: chainId param --- Tests/AuthTests/CacaoSignerTests.swift | 2 +- Tests/AuthTests/EIP1271VerifierTests.swift | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Tests/AuthTests/CacaoSignerTests.swift b/Tests/AuthTests/CacaoSignerTests.swift index 98715eb36..95a01a538 100644 --- a/Tests/AuthTests/CacaoSignerTests.swift +++ b/Tests/AuthTests/CacaoSignerTests.swift @@ -33,6 +33,6 @@ class CacaoSignerTest: XCTestCase { } func testCacaoVerify() async throws { - try await signer.verify(signature: signature, message: message, address: "0x15bca56b6e2728aec2532df9d436bd1600e86688") + try await signer.verify(signature: signature, message: message, address: "0x15bca56b6e2728aec2532df9d436bd1600e86688", chainId: "eip155:1") } } diff --git a/Tests/AuthTests/EIP1271VerifierTests.swift b/Tests/AuthTests/EIP1271VerifierTests.swift index 5c2632728..796c146ff 100644 --- a/Tests/AuthTests/EIP1271VerifierTests.swift +++ b/Tests/AuthTests/EIP1271VerifierTests.swift @@ -9,6 +9,7 @@ class EIP1271VerifierTests: XCTestCase { let signature = Data(hex: "c1505719b2504095116db01baaf276361efd3a73c28cf8cc28dabefa945b8d536011289ac0a3b048600c1e692ff173ca944246cf7ceb319ac2262d27b395c82b1c") let message = Data(hex: "3aaa8393796c7388e4e062861d8238503de7584c977676fe9d8d551c30e11f84") let address = "0x2faf83c542b68f1b4cdc0e770e8cb9f567b08f71" + let chainId = "eip155:1" func testSuccessVerify() async throws { let response = RPCResponse(id: "1", result: "0x1626ba7e00000000000000000000000000000000000000000000000000000000") @@ -17,7 +18,8 @@ class EIP1271VerifierTests: XCTestCase { try await verifier.verify( signature: signature, message: message, - address: address + address: address, + chainId: chainId ) } @@ -29,7 +31,8 @@ class EIP1271VerifierTests: XCTestCase { await XCTAssertThrowsErrorAsync(try await verifier.verify( signature: signature, message: message, - address: address + address: address, + chainId: chainId )) } } From b3014f6fbe5d505f602002508c4aafeccc0c2099 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Wed, 19 Oct 2022 02:20:50 +0600 Subject: [PATCH 173/175] lint --- Example/ExampleApp/Common/InputConfig.swift | 2 +- Example/IntegrationTests/Pairing/PairingTests.swift | 7 +++---- Example/IntegrationTests/Sign/SignClientTests.swift | 3 +-- Example/UITests/Regression/RegressionTests.swift | 1 - Sources/Auth/Auth.swift | 1 - Sources/Auth/AuthProtocolMethod.swift | 2 -- Sources/Auth/Services/Signer/EIP1271/RPCService.swift | 2 +- Sources/Auth/Services/Wallet/WalletErrorResponder.swift | 3 +-- Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift | 1 - .../Chat/ProtocolServices/Common/MessagingService.swift | 2 +- .../WalletConnectNetworking/NetworkingClientFactory.swift | 2 +- Sources/WalletConnectNetworking/NetworkingInteractor.swift | 1 - Sources/WalletConnectNetworking/ProtocolMethod.swift | 2 +- Sources/WalletConnectPairing/Pair.swift | 1 - Sources/WalletConnectPairing/PairingClient.swift | 1 - Sources/WalletConnectPairing/PairingClientFactory.swift | 1 - Sources/WalletConnectPairing/PairingRegisterer.swift | 2 +- .../Services/Common/Ping/PairingPingService.swift | 2 +- .../Services/Common/Ping/PingResponseSubscriber.swift | 2 +- .../WalletConnectPairing/Types/PairingProtocolMethod.swift | 1 - Sources/WalletConnectPairing/Types/ReasonCode.swift | 1 - Sources/WalletConnectPush/PushClientFactory.swift | 2 +- Sources/WalletConnectPush/PushProposer.swift | 1 - Sources/WalletConnectRelay/Dispatching.swift | 2 +- .../WalletConnectSign/Services/SessionPingService.swift | 2 +- Sources/WalletConnectSign/Sign/SignClient.swift | 2 +- .../ProtocolMethods/SessionDeleteProtocolMethod.swift | 1 - .../Types/ProtocolMethods/SessionPingProtocolMethod.swift | 1 - .../ProtocolMethods/SessionUpdateProtocolMethod.swift | 1 - Sources/WalletConnectUtils/SetStore.swift | 3 +-- Tests/AuthTests/EIP191VerifierTests.swift | 1 - Tests/AuthTests/WalletRequestSubscriberTests.swift | 2 +- Tests/RelayerTests/AuthTests/ClientIdStorageTests.swift | 2 +- Tests/TestingUtils/Mocks/HTTPClientMock.swift | 2 +- Tests/TestingUtils/Mocks/PairingRegistererMock.swift | 6 +++--- Tests/WalletConnectSignTests/Mocks/MockedRelayClient.swift | 4 ++-- 36 files changed, 26 insertions(+), 46 deletions(-) diff --git a/Example/ExampleApp/Common/InputConfig.swift b/Example/ExampleApp/Common/InputConfig.swift index b6c381ea1..53931721a 100644 --- a/Example/ExampleApp/Common/InputConfig.swift +++ b/Example/ExampleApp/Common/InputConfig.swift @@ -1,5 +1,5 @@ import Foundation - + struct InputConfig { static var projectId: String { diff --git a/Example/IntegrationTests/Pairing/PairingTests.swift b/Example/IntegrationTests/Pairing/PairingTests.swift index 93b2fe456..dce08c134 100644 --- a/Example/IntegrationTests/Pairing/PairingTests.swift +++ b/Example/IntegrationTests/Pairing/PairingTests.swift @@ -121,14 +121,14 @@ final class PairingTests: XCTestCase { }.store(in: &publishers) wait(for: [expectation], timeout: InputConfig.defaultTimeout) } - + func testResponseErrorForMethodUnregistered() async { (appPairingClient, appPushClient) = makeClients(prefix: "🤖 App") walletPairingClient = makePairingClient(prefix: "🐶 Wallet") let expectation = expectation(description: "wallet responds unsupported method for unregistered method") - appPushClient.responsePublisher.sink { (id, response) in + appPushClient.responsePublisher.sink { (_, response) in XCTAssertEqual(response, .failure(WalletConnectPairing.PairError(code: 10001)!)) expectation.fulfill() }.store(in: &publishers) @@ -144,7 +144,6 @@ final class PairingTests: XCTestCase { } func testDisconnect() { - //TODO + // TODO } } - diff --git a/Example/IntegrationTests/Sign/SignClientTests.swift b/Example/IntegrationTests/Sign/SignClientTests.swift index a38e2e84e..acbfbb728 100644 --- a/Example/IntegrationTests/Sign/SignClientTests.swift +++ b/Example/IntegrationTests/Sign/SignClientTests.swift @@ -277,7 +277,7 @@ final class SignClientTests: XCTestCase { let pairingTopic = dapp.client.getPairings().first!.topic if !initiatedSecondSession { Task(priority: .high) { - let _ = try! await dapp.client.connect(requiredNamespaces: requiredNamespaces, topic: pairingTopic) + _ = try! await dapp.client.connect(requiredNamespaces: requiredNamespaces, topic: pairingTopic) } initiatedSecondSession = true } @@ -314,7 +314,6 @@ final class SignClientTests: XCTestCase { wait(for: [expectation], timeout: InputConfig.defaultTimeout) } - func testSuccessfulSessionExtend() async { let expectation = expectation(description: "Dapp extends session") diff --git a/Example/UITests/Regression/RegressionTests.swift b/Example/UITests/Regression/RegressionTests.swift index 4d3016b60..6b5047e9c 100644 --- a/Example/UITests/Regression/RegressionTests.swift +++ b/Example/UITests/Regression/RegressionTests.swift @@ -10,7 +10,6 @@ class PairingTests: XCTestCase { engine.routing.launch(app: .wallet, clean: true) } - /// Check pairing proposal approval via QR code or uri /// - TU001 func test01PairingCreation() { diff --git a/Sources/Auth/Auth.swift b/Sources/Auth/Auth.swift index 274ad962e..8687e05d9 100644 --- a/Sources/Auth/Auth.swift +++ b/Sources/Auth/Auth.swift @@ -27,7 +27,6 @@ public class Auth { ) }() - private static var config: Config? private init() { } diff --git a/Sources/Auth/AuthProtocolMethod.swift b/Sources/Auth/AuthProtocolMethod.swift index 02d53d559..85bab9cff 100644 --- a/Sources/Auth/AuthProtocolMethod.swift +++ b/Sources/Auth/AuthProtocolMethod.swift @@ -9,7 +9,6 @@ struct AuthRequestProtocolMethod: ProtocolMethod { let responseConfig = RelayConfig(tag: 3001, prompt: false, ttl: 86400) } - struct PairingPingProtocolMethod: ProtocolMethod { let method: String = "wc_pairingPing" @@ -18,7 +17,6 @@ struct PairingPingProtocolMethod: ProtocolMethod { let responseConfig = RelayConfig(tag: 1003, prompt: false, ttl: 30) } - struct PairingDeleteProtocolMethod: ProtocolMethod { let method: String = "wc_pairingDelete" diff --git a/Sources/Auth/Services/Signer/EIP1271/RPCService.swift b/Sources/Auth/Services/Signer/EIP1271/RPCService.swift index 095b53011..c7c81c995 100644 --- a/Sources/Auth/Services/Signer/EIP1271/RPCService.swift +++ b/Sources/Auth/Services/Signer/EIP1271/RPCService.swift @@ -22,7 +22,7 @@ struct RPCService: HTTPService { return data } - var queryParameters: [String : String]? { + var queryParameters: [String: String]? { return [ "chainId": chainId, "projectId": projectId diff --git a/Sources/Auth/Services/Wallet/WalletErrorResponder.swift b/Sources/Auth/Services/Wallet/WalletErrorResponder.swift index 491cf73ad..3234b86fd 100644 --- a/Sources/Auth/Services/Wallet/WalletErrorResponder.swift +++ b/Sources/Auth/Services/Wallet/WalletErrorResponder.swift @@ -25,7 +25,6 @@ actor WalletErrorResponder { self.rpcHistory = rpcHistory } - func respondError(_ error: AuthError, requestId: RPCID) async throws { let authRequestParams = try getAuthRequestParams(requestId: requestId) let (topic, keys) = try generateAgreementKeys(requestParams: authRequestParams) @@ -51,7 +50,7 @@ actor WalletErrorResponder { let topic = peerPubKey.rawRepresentation.sha256().toHexString() let selfPubKey = try kms.createX25519KeyPair() let keys = try kms.performKeyAgreement(selfPublicKey: selfPubKey, peerPublicKey: peerPubKey.hexRepresentation) - //TODO - remove keys + // TODO - remove keys return (topic, keys) } } diff --git a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift index 507479e3b..fde464a43 100644 --- a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift +++ b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift @@ -51,4 +51,3 @@ class WalletRequestSubscriber { }.store(in: &publishers) } } - diff --git a/Sources/Chat/ProtocolServices/Common/MessagingService.swift b/Sources/Chat/ProtocolServices/Common/MessagingService.swift index ea13faca4..e4c746cf3 100644 --- a/Sources/Chat/ProtocolServices/Common/MessagingService.swift +++ b/Sources/Chat/ProtocolServices/Common/MessagingService.swift @@ -44,7 +44,7 @@ class MessagingService { private func setUpResponseHandling() { networkingInteractor.responseSubscription(on: ChatMessageProtocolMethod()) - .sink { [unowned self] (payload: ResponseSubscriptionPayload) in + .sink { [unowned self] (_: ResponseSubscriptionPayload) in logger.debug("Received Message response") }.store(in: &publishers) } diff --git a/Sources/WalletConnectNetworking/NetworkingClientFactory.swift b/Sources/WalletConnectNetworking/NetworkingClientFactory.swift index 36df0f6fc..91a1cdacf 100644 --- a/Sources/WalletConnectNetworking/NetworkingClientFactory.swift +++ b/Sources/WalletConnectNetworking/NetworkingClientFactory.swift @@ -12,7 +12,7 @@ public struct NetworkingClientFactory { return NetworkingClientFactory.create(relayClient: relayClient, logger: logger, keychainStorage: keychainStorage, keyValueStorage: keyValueStorage) } - public static func create(relayClient: RelayClient, logger: ConsoleLogging, keychainStorage: KeychainStorageProtocol, keyValueStorage: KeyValueStorage) -> NetworkingInteractor{ + public static func create(relayClient: RelayClient, logger: ConsoleLogging, keychainStorage: KeychainStorageProtocol, keyValueStorage: KeyValueStorage) -> NetworkingInteractor { let kms = KeyManagementService(keychain: keychainStorage) let serializer = Serializer(kms: kms) diff --git a/Sources/WalletConnectNetworking/NetworkingInteractor.swift b/Sources/WalletConnectNetworking/NetworkingInteractor.swift index 39faee02a..66647efd2 100644 --- a/Sources/WalletConnectNetworking/NetworkingInteractor.swift +++ b/Sources/WalletConnectNetworking/NetworkingInteractor.swift @@ -171,7 +171,6 @@ public class NetworkingInteractor: NetworkInteracting { } } - extension NetworkingInteractor: NetworkingClient { public func connect() throws { try relayClient.connect() diff --git a/Sources/WalletConnectNetworking/ProtocolMethod.swift b/Sources/WalletConnectNetworking/ProtocolMethod.swift index fa061623f..624d74aa2 100644 --- a/Sources/WalletConnectNetworking/ProtocolMethod.swift +++ b/Sources/WalletConnectNetworking/ProtocolMethod.swift @@ -10,7 +10,7 @@ public struct RelayConfig { let tag: Int let prompt: Bool let ttl: Int - + public init(tag: Int, prompt: Bool, ttl: Int) { self.tag = tag self.prompt = prompt diff --git a/Sources/WalletConnectPairing/Pair.swift b/Sources/WalletConnectPairing/Pair.swift index 86680b758..b016ed6e6 100644 --- a/Sources/WalletConnectPairing/Pair.swift +++ b/Sources/WalletConnectPairing/Pair.swift @@ -13,7 +13,6 @@ public class Pair { return Pair.client } - public static var metadata: AppMetadata { guard let metadata = config?.metadata else { fatalError("Error - you must configure metadata with Pair.configure(metadata:)") diff --git a/Sources/WalletConnectPairing/PairingClient.swift b/Sources/WalletConnectPairing/PairingClient.swift index 784d55fba..2b115c4e5 100644 --- a/Sources/WalletConnectPairing/PairingClient.swift +++ b/Sources/WalletConnectPairing/PairingClient.swift @@ -127,4 +127,3 @@ public class PairingClient: PairingRegisterer, PairingInteracting { } #endif } - diff --git a/Sources/WalletConnectPairing/PairingClientFactory.swift b/Sources/WalletConnectPairing/PairingClientFactory.swift index eb019917a..9926292e0 100644 --- a/Sources/WalletConnectPairing/PairingClientFactory.swift +++ b/Sources/WalletConnectPairing/PairingClientFactory.swift @@ -46,4 +46,3 @@ public struct PairingClientFactory { ) } } - diff --git a/Sources/WalletConnectPairing/PairingRegisterer.swift b/Sources/WalletConnectPairing/PairingRegisterer.swift index e6acb31e6..f6b136d74 100644 --- a/Sources/WalletConnectPairing/PairingRegisterer.swift +++ b/Sources/WalletConnectPairing/PairingRegisterer.swift @@ -10,5 +10,5 @@ public protocol PairingRegisterer { func activate(pairingTopic: String) func validatePairingExistance(_ topic: String) throws - func updateMetadata(_ topic: String, metadata: AppMetadata) + func updateMetadata(_ topic: String, metadata: AppMetadata) } diff --git a/Sources/WalletConnectPairing/Services/Common/Ping/PairingPingService.swift b/Sources/WalletConnectPairing/Services/Common/Ping/PairingPingService.swift index de614cc25..8cd852eb9 100644 --- a/Sources/WalletConnectPairing/Services/Common/Ping/PairingPingService.swift +++ b/Sources/WalletConnectPairing/Services/Common/Ping/PairingPingService.swift @@ -8,7 +8,7 @@ public class PairingPingService { private let pingResponder: PingResponder private let pingResponseSubscriber: PingResponseSubscriber - public var onResponse: ((String)->())? { + public var onResponse: ((String)->Void)? { get { return pingResponseSubscriber.onResponse } diff --git a/Sources/WalletConnectPairing/Services/Common/Ping/PingResponseSubscriber.swift b/Sources/WalletConnectPairing/Services/Common/Ping/PingResponseSubscriber.swift index e473bd3e4..f28b7ca9e 100644 --- a/Sources/WalletConnectPairing/Services/Common/Ping/PingResponseSubscriber.swift +++ b/Sources/WalletConnectPairing/Services/Common/Ping/PingResponseSubscriber.swift @@ -8,7 +8,7 @@ public class PingResponseSubscriber { private let logger: ConsoleLogging private var publishers = [AnyCancellable]() - public var onResponse: ((String)->())? + public var onResponse: ((String)->Void)? public init(networkingInteractor: NetworkInteracting, method: ProtocolMethod, diff --git a/Sources/WalletConnectPairing/Types/PairingProtocolMethod.swift b/Sources/WalletConnectPairing/Types/PairingProtocolMethod.swift index a2efd3167..f121a62a9 100644 --- a/Sources/WalletConnectPairing/Types/PairingProtocolMethod.swift +++ b/Sources/WalletConnectPairing/Types/PairingProtocolMethod.swift @@ -41,4 +41,3 @@ struct UnsupportedProtocolMethod: ProtocolMethod { let responseConfig = RelayConfig(tag: 0, prompt: false, ttl: 86400) } - diff --git a/Sources/WalletConnectPairing/Types/ReasonCode.swift b/Sources/WalletConnectPairing/Types/ReasonCode.swift index 5b211a902..e5f633835 100644 --- a/Sources/WalletConnectPairing/Types/ReasonCode.swift +++ b/Sources/WalletConnectPairing/Types/ReasonCode.swift @@ -1,4 +1,3 @@ - import Foundation import WalletConnectNetworking diff --git a/Sources/WalletConnectPush/PushClientFactory.swift b/Sources/WalletConnectPush/PushClientFactory.swift index 6a1b79429..f9715f86d 100644 --- a/Sources/WalletConnectPush/PushClientFactory.swift +++ b/Sources/WalletConnectPush/PushClientFactory.swift @@ -11,7 +11,7 @@ public struct PushClientFactory { let kms = KeyManagementService(keychain: keychainStorage) let pushProposer = PushProposer(networkingInteractor: networkingClient, kms: kms, logger: logger) let proposalResponseSubscriber = ProposalResponseSubscriber(networkingInteractor: networkingClient, kms: kms, logger: logger) - + return PushClient( networkInteractor: networkingClient, logger: logger, diff --git a/Sources/WalletConnectPush/PushProposer.swift b/Sources/WalletConnectPush/PushProposer.swift index fc34e54d0..6a08305fe 100644 --- a/Sources/WalletConnectPush/PushProposer.swift +++ b/Sources/WalletConnectPush/PushProposer.swift @@ -5,7 +5,6 @@ import WalletConnectUtils import WalletConnectKMS import WalletConnectNetworking - class PushProposer { private let networkingInteractor: NetworkInteracting private let kms: KeyManagementServiceProtocol diff --git a/Sources/WalletConnectRelay/Dispatching.swift b/Sources/WalletConnectRelay/Dispatching.swift index cd08efe12..d08c7e0bf 100644 --- a/Sources/WalletConnectRelay/Dispatching.swift +++ b/Sources/WalletConnectRelay/Dispatching.swift @@ -66,7 +66,7 @@ final class Dispatcher: NSObject, Dispatching { completion(error) case .finished: break } - }, receiveValue: { [unowned self] status in + }, receiveValue: { [unowned self] _ in cancellable?.cancel() send(string, completion: completion) }) diff --git a/Sources/WalletConnectSign/Services/SessionPingService.swift b/Sources/WalletConnectSign/Services/SessionPingService.swift index 1d10e2105..8bc03dbad 100644 --- a/Sources/WalletConnectSign/Services/SessionPingService.swift +++ b/Sources/WalletConnectSign/Services/SessionPingService.swift @@ -9,7 +9,7 @@ class SessionPingService { private let pingResponder: PingResponder private let pingResponseSubscriber: PingResponseSubscriber - var onResponse: ((String)->())? { + var onResponse: ((String)->Void)? { get { return pingResponseSubscriber.onResponse } diff --git a/Sources/WalletConnectSign/Sign/SignClient.swift b/Sources/WalletConnectSign/Sign/SignClient.swift index 388f33692..27a156a63 100644 --- a/Sources/WalletConnectSign/Sign/SignClient.swift +++ b/Sources/WalletConnectSign/Sign/SignClient.swift @@ -194,7 +194,7 @@ public final class SignClient { /// - Parameters: /// - requiredNamespaces: required namespaces for a session /// - topic: pairing topic - public func connect(requiredNamespaces: [String: ProposalNamespace], topic: String) async throws { + public func connect(requiredNamespaces: [String: ProposalNamespace], topic: String) async throws { logger.debug("Connecting Application") try pairingClient.validatePairingExistance(topic) try await appProposeService.propose( diff --git a/Sources/WalletConnectSign/Types/ProtocolMethods/SessionDeleteProtocolMethod.swift b/Sources/WalletConnectSign/Types/ProtocolMethods/SessionDeleteProtocolMethod.swift index b9d7fc9f3..927f100af 100644 --- a/Sources/WalletConnectSign/Types/ProtocolMethods/SessionDeleteProtocolMethod.swift +++ b/Sources/WalletConnectSign/Types/ProtocolMethods/SessionDeleteProtocolMethod.swift @@ -8,4 +8,3 @@ struct SessionDeleteProtocolMethod: ProtocolMethod { let responseConfig = RelayConfig(tag: 1113, prompt: false, ttl: 86400) } - diff --git a/Sources/WalletConnectSign/Types/ProtocolMethods/SessionPingProtocolMethod.swift b/Sources/WalletConnectSign/Types/ProtocolMethods/SessionPingProtocolMethod.swift index 109ff0870..91ff13035 100644 --- a/Sources/WalletConnectSign/Types/ProtocolMethods/SessionPingProtocolMethod.swift +++ b/Sources/WalletConnectSign/Types/ProtocolMethods/SessionPingProtocolMethod.swift @@ -8,4 +8,3 @@ struct SessionPingProtocolMethod: ProtocolMethod { let responseConfig = RelayConfig(tag: 1115, prompt: false, ttl: 30) } - diff --git a/Sources/WalletConnectSign/Types/ProtocolMethods/SessionUpdateProtocolMethod.swift b/Sources/WalletConnectSign/Types/ProtocolMethods/SessionUpdateProtocolMethod.swift index 859bddbd8..eb7936b4b 100644 --- a/Sources/WalletConnectSign/Types/ProtocolMethods/SessionUpdateProtocolMethod.swift +++ b/Sources/WalletConnectSign/Types/ProtocolMethods/SessionUpdateProtocolMethod.swift @@ -8,4 +8,3 @@ struct SessionUpdateProtocolMethod: ProtocolMethod { let responseConfig = RelayConfig(tag: 1105, prompt: false, ttl: 86400) } - diff --git a/Sources/WalletConnectUtils/SetStore.swift b/Sources/WalletConnectUtils/SetStore.swift index c0cdb0fff..a39508150 100644 --- a/Sources/WalletConnectUtils/SetStore.swift +++ b/Sources/WalletConnectUtils/SetStore.swift @@ -1,4 +1,3 @@ - import Foundation public class SetStore: CustomStringConvertible { @@ -7,7 +6,7 @@ public class SetStore: CustomStringConvertible { private var store: Set = Set() - public init(label: String){ + public init(label: String) { self.concurrentQueue = DispatchQueue(label: label, attributes: .concurrent) } diff --git a/Tests/AuthTests/EIP191VerifierTests.swift b/Tests/AuthTests/EIP191VerifierTests.swift index 3571c03fd..7b683ab14 100644 --- a/Tests/AuthTests/EIP191VerifierTests.swift +++ b/Tests/AuthTests/EIP191VerifierTests.swift @@ -11,7 +11,6 @@ class EIP191VerifierTests: XCTestCase { private let message = "\u{19}Ethereum Signed Message:\n7Message".data(using: .utf8)! private let signature = Data(hex: "66121e60cccc01fbf7fcba694a1e08ac5db35fb4ec6c045bedba7860445b95c021cad2c595f0bf68ff896964c7c02bb2f3a3e9540e8e4595c98b737ce264cc541b") - func testVerify() async throws { try await verifier.verify(signature: signature, message: message, address: address) } diff --git a/Tests/AuthTests/WalletRequestSubscriberTests.swift b/Tests/AuthTests/WalletRequestSubscriberTests.swift index f7e953fd6..de7dbe28d 100644 --- a/Tests/AuthTests/WalletRequestSubscriberTests.swift +++ b/Tests/AuthTests/WalletRequestSubscriberTests.swift @@ -40,7 +40,7 @@ class WalletRequestSubscriberTests: XCTestCase { message = request.message messageExpectation.fulfill() } - + let payload = RequestSubscriptionPayload(id: expectedRequestId, topic: "123", request: AuthRequestParams.stub(id: expectedRequestId)) pairingRegisterer.subject.send(payload) diff --git a/Tests/RelayerTests/AuthTests/ClientIdStorageTests.swift b/Tests/RelayerTests/AuthTests/ClientIdStorageTests.swift index a9e3e2770..aa58c9761 100644 --- a/Tests/RelayerTests/AuthTests/ClientIdStorageTests.swift +++ b/Tests/RelayerTests/AuthTests/ClientIdStorageTests.swift @@ -30,7 +30,7 @@ final class ClientIdStorageTests: XCTestCase { let did = "did:key:z6MkodHZwneVRShtaLf8JKYkxpDGp1vGZnpGmdBpX8M2exxH" didKeyFactory.did = did _ = try! sut.getOrCreateKeyPair() - + let clientId = try! sut.getClientId() XCTAssertEqual(did, clientId) diff --git a/Tests/TestingUtils/Mocks/HTTPClientMock.swift b/Tests/TestingUtils/Mocks/HTTPClientMock.swift index bca495c63..c769c2b68 100644 --- a/Tests/TestingUtils/Mocks/HTTPClientMock.swift +++ b/Tests/TestingUtils/Mocks/HTTPClientMock.swift @@ -9,7 +9,7 @@ public final class HTTPClientMock: HTTPClient { self.object = object } - public func request(_ type: T.Type, at service: HTTPService) async throws -> T where T : Decodable { + public func request(_ type: T.Type, at service: HTTPService) async throws -> T where T: Decodable { return object as! T } diff --git a/Tests/TestingUtils/Mocks/PairingRegistererMock.swift b/Tests/TestingUtils/Mocks/PairingRegistererMock.swift index 1bea7c188..9e48d13da 100644 --- a/Tests/TestingUtils/Mocks/PairingRegistererMock.swift +++ b/Tests/TestingUtils/Mocks/PairingRegistererMock.swift @@ -3,20 +3,20 @@ import WalletConnectPairing import Combine import WalletConnectNetworking -public class PairingRegistererMock: PairingRegisterer where RequestParams : Codable { +public class PairingRegistererMock: PairingRegisterer where RequestParams: Codable { public let subject = PassthroughSubject, Never>() public var isActivateCalled: Bool = false - public func register(method: ProtocolMethod) -> AnyPublisher, Never> where RequestParams : Decodable, RequestParams : Encodable { + public func register(method: ProtocolMethod) -> AnyPublisher, Never> where RequestParams: Decodable, RequestParams: Encodable { subject.eraseToAnyPublisher() as! AnyPublisher, Never> } public func activate(pairingTopic: String) { isActivateCalled = true } - + public func validatePairingExistance(_ topic: String) throws { } diff --git a/Tests/WalletConnectSignTests/Mocks/MockedRelayClient.swift b/Tests/WalletConnectSignTests/Mocks/MockedRelayClient.swift index a371a1e79..4044c4b2e 100644 --- a/Tests/WalletConnectSignTests/Mocks/MockedRelayClient.swift +++ b/Tests/WalletConnectSignTests/Mocks/MockedRelayClient.swift @@ -3,7 +3,7 @@ import Foundation @testable import WalletConnectRelay @testable import WalletConnectSign -//class MockedRelayClient: NetworkRelaying { +// class MockedRelayClient: NetworkRelaying { // // var messagePublisherSubject = PassthroughSubject<(topic: String, message: String), Never>() // var messagePublisher: AnyPublisher<(topic: String, message: String), Never> { @@ -41,4 +41,4 @@ import Foundation // func disconnect(closeCode: URLSessionWebSocketTask.CloseCode) { // } // -//} +// } From 82d5177b195bba2d96ef9495ebca52915acccd8c Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Wed, 19 Oct 2022 19:32:16 +0600 Subject: [PATCH 174/175] Stored Networking interactor --- Sources/WalletConnectNetworking/Networking.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/WalletConnectNetworking/Networking.swift b/Sources/WalletConnectNetworking/Networking.swift index 37cf4a6c6..fdad4f621 100644 --- a/Sources/WalletConnectNetworking/Networking.swift +++ b/Sources/WalletConnectNetworking/Networking.swift @@ -10,13 +10,13 @@ public class Networking { return Networking.interactor } - public static var interactor: NetworkingInteractor { + public static var interactor: NetworkingInteractor = { guard let _ = Networking.config else { fatalError("Error - you must call Networking.configure(_:) before accessing the shared instance.") } return NetworkingClientFactory.create(relayClient: Relay.instance) - } + }() public static var projectId: String { guard let projectId = config?.projectId else { From 2070b1c1abb455805ca4ee6082f8a022d7edd678 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Wed, 19 Oct 2022 19:48:36 +0600 Subject: [PATCH 175/175] Ping success response --- .../Services/Common/Ping/PingResponseSubscriber.swift | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Sources/WalletConnectPairing/Services/Common/Ping/PingResponseSubscriber.swift b/Sources/WalletConnectPairing/Services/Common/Ping/PingResponseSubscriber.swift index f28b7ca9e..7da5c8a56 100644 --- a/Sources/WalletConnectPairing/Services/Common/Ping/PingResponseSubscriber.swift +++ b/Sources/WalletConnectPairing/Services/Common/Ping/PingResponseSubscriber.swift @@ -23,6 +23,14 @@ public class PingResponseSubscriber { networkingInteractor.responseSubscription(on: method) .sink { [unowned self] (payload: ResponseSubscriptionPayload) in onResponse?(payload.topic) + + Task(priority: .high) { + try await networkingInteractor.respondSuccess( + topic: payload.topic, + requestId: payload.id, + protocolMethod: method + ) + } } .store(in: &publishers) }