From f2f2137decc787382dba3995838e0fe25e7cff3b Mon Sep 17 00:00:00 2001 From: Derek Date: Mon, 1 Aug 2022 12:16:54 +0200 Subject: [PATCH 01/14] feat: moves issues to the Swift board --- .github/workflows/intake.yml | 43 ++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 .github/workflows/intake.yml diff --git a/.github/workflows/intake.yml b/.github/workflows/intake.yml new file mode 100644 index 000000000..4b79bc326 --- /dev/null +++ b/.github/workflows/intake.yml @@ -0,0 +1,43 @@ +# This workflow moves issues to the Swift board +# when they receive the "accepted" label +# When WalletConnect Org members create issues they +# are automatically "accepted". +# Else they need to manually receive that label during intake. +name: intake + +on: + issues: + types: [opened, labeled] + pull_request: + types: [opened, labeled] + +jobs: + add-to-project: + name: Add issue to board + if: github.event.action == 'labeled' && github.event.label.name == 'accepted' + runs-on: ubuntu-latest + steps: + - uses: actions/add-to-project@v0.1.0 + with: + project-url: https://github.com/orgs/WalletConnect/projects/5 + github-token: ${{ secrets.ASSIGN_TO_PROJECT_GITHUB_TOKEN }} + labeled: accepted + label-operator: OR + auto-promote: + name: auto-promote + if: github.event.action == 'opened' + runs-on: ubuntu-latest + steps: + - name: Check if organization member + id: is_organization_member + if: github.event.action == 'opened' + uses: JamesSingleton/is-organization-member@1.0.0 + with: + organization: WalletConnect + username: ${{ github.event_name != 'pull_request' && github.event.issue.user.login || github.event.sender.login }} + token: ${{ secrets.ASSIGN_TO_PROJECT_GITHUB_TOKEN }} + - name: Label issues + uses: andymckay/labeler@e6c4322d0397f3240f0e7e30a33b5c5df2d39e90 + with: + add-labels: "accepted" + repo-token: ${{ secrets.ASSIGN_TO_PROJECT_GITHUB_TOKEN }} From 043c1ce51012bcb5de0daf1dfff5920495399683 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Vants?= Date: Thu, 28 Jul 2022 12:17:21 -0300 Subject: [PATCH 02/14] Successful integration on publish test --- Package.swift | 2 +- Sources/JSONRPC/RPCResponse.swift | 6 +- Sources/WalletConnectRelay/RelayClient.swift | 55 +++++++++++++------ Sources/WalletConnectRelay/RelayMethods.swift | 50 +++++++++++++++++ Sources/WalletConnectRelay/RelayRPC.swift | 54 ++++++++++++++++++ .../NetworkInteractor/NetworkRelaying.swift | 2 +- Sources/WalletConnectUtils/Encodable.swift | 10 ++++ Tests/RelayerTests/Mocks/DispatcherMock.swift | 19 ++++++- ...elayTests.swift => RelayClientTests.swift} | 45 +++++++++------ .../Mocks/MockedRelayClient.swift | 4 +- 10 files changed, 206 insertions(+), 41 deletions(-) create mode 100644 Sources/WalletConnectRelay/RelayMethods.swift create mode 100644 Sources/WalletConnectRelay/RelayRPC.swift rename Tests/RelayerTests/{IridiumRelayTests.swift => RelayClientTests.swift} (69%) diff --git a/Package.swift b/Package.swift index f58a2bc4f..1d1cdbf9f 100644 --- a/Package.swift +++ b/Package.swift @@ -36,7 +36,7 @@ let package = Package( path: "Sources/Auth"), .target( name: "WalletConnectRelay", - dependencies: ["WalletConnectUtils", "WalletConnectKMS"], + dependencies: ["WalletConnectUtils", "WalletConnectKMS", "JSONRPC"], path: "Sources/WalletConnectRelay"), .target( name: "WalletConnectKMS", diff --git a/Sources/JSONRPC/RPCResponse.swift b/Sources/JSONRPC/RPCResponse.swift index 0934adb7e..fec762286 100644 --- a/Sources/JSONRPC/RPCResponse.swift +++ b/Sources/JSONRPC/RPCResponse.swift @@ -19,7 +19,7 @@ public struct RPCResponse: Equatable { return nil } - private let outcome: Result + public let outcome: Result internal init(id: RPCID?, outcome: Result) { self.jsonrpc = "2.0" @@ -27,6 +27,10 @@ public struct RPCResponse: Equatable { self.outcome = outcome } + public init(matchingRequest: RPCRequest, result: C) where C: Codable { + self.init(id: matchingRequest.id, outcome: .success(AnyCodable(result))) + } + public init(id: Int, result: C) where C: Codable { self.init(id: RPCID(id), outcome: .success(AnyCodable(result))) } diff --git a/Sources/WalletConnectRelay/RelayClient.swift b/Sources/WalletConnectRelay/RelayClient.swift index 47dc706b8..7871404d8 100644 --- a/Sources/WalletConnectRelay/RelayClient.swift +++ b/Sources/WalletConnectRelay/RelayClient.swift @@ -2,6 +2,7 @@ import Foundation import Combine import WalletConnectUtils import WalletConnectKMS +import JSONRPC public enum SocketConnectionStatus { case connected @@ -31,10 +32,11 @@ public final class RelayClient { subscriptionResponsePublisherSubject.eraseToAnyPublisher() } private let subscriptionResponsePublisherSubject = PassthroughSubject, Never>() - private var requestAcknowledgePublisher: AnyPublisher, Never> { + + private let requestAcknowledgePublisherSubject = PassthroughSubject() + private var requestAcknowledgePublisher: AnyPublisher { requestAcknowledgePublisherSubject.eraseToAnyPublisher() } - private let requestAcknowledgePublisherSubject = PassthroughSubject, Never>() let logger: ConsoleLogging static let historyIdentifier = "com.walletconnect.sdk.relayer_client.subscription_json_rpc_record" @@ -111,13 +113,20 @@ public final class RelayClient { payload: String, tag: Int, prompt: Bool = false, - onNetworkAcknowledge: @escaping ((Error?) -> Void)) -> Int64 { - let params = RelayJSONRPC.PublishParams(topic: topic, message: payload, ttl: defaultTtl, prompt: prompt, tag: tag) - let request = JSONRPCRequest(method: RelayJSONRPC.Method.publish.method, params: params) - let requestJson = try! request.json() + onNetworkAcknowledge: @escaping ((Error?) -> Void) + ) { //-> Int64 { +// let params = RelayJSONRPC.PublishParams(topic: topic, message: payload, ttl: defaultTtl, prompt: prompt, tag: tag) +// let request = JSONRPCRequest(method: RelayJSONRPC.Method.publish.method, params: params) +// let message = try! request.json() + + let rpc = Publish(params: .init(topic: topic, message: payload, ttl: defaultTtl, prompt: prompt, tag: tag)) + let request = rpc + .wrapToIridium() + .asRPCRequest() + let message = try! request.asJSONEncodedString() logger.debug("iridium: Publishing Payload on Topic: \(topic)") var cancellable: AnyCancellable? - dispatcher.send(requestJson) { [weak self] error in + dispatcher.send(message) { [weak self] error in if let error = error { self?.logger.debug("Failed to Publish Payload, error: \(error)") cancellable?.cancel() @@ -125,12 +134,12 @@ public final class RelayClient { } } cancellable = requestAcknowledgePublisher - .filter {$0.id == request.id} + .filter { $0 == request.id } .sink { (_) in cancellable?.cancel() onNetworkAcknowledge(nil) } - return request.id +// return Int64(request.id!.right!) } @available(*, renamed: "subscribe(topic:)") @@ -194,12 +203,12 @@ public final class RelayClient { completion(nil) } } - cancellable = requestAcknowledgePublisher - .filter {$0.id == request.id} - .sink { (_) in - cancellable?.cancel() - completion(nil) - } +// cancellable = requestAcknowledgePublisher +// .filter { $0 == request.id } +// .sink { (_) in +// cancellable?.cancel() +// completion(nil) +// } return request.id } @@ -221,8 +230,20 @@ public final class RelayClient { } catch { logger.info("Relay Client Info: Json Rpc Duplicate Detected") } - } else if let response = tryDecode(RequestAcknowledgement.self, from: payload) { - requestAcknowledgePublisherSubject.send(response) + } else if let response = tryDecode(RPCResponse.self, from: payload) { + // use this + switch response.outcome { + case .success(let anyCodable): + if let acknowledgement = try? anyCodable.get(Bool.self) { + requestAcknowledgePublisherSubject.send(response.id) + } else if let subscription = try? anyCodable.get(String.self) { + + } + case .failure(let rpcError): + break + } +// } else if let response = tryDecode(RequestAcknowledgement.self, from: payload) { +// requestAcknowledgePublisherSubject.send(response) } else if let response = tryDecode(SubscriptionResponse.self, from: payload) { subscriptionResponsePublisherSubject.send(response) } else if let response = tryDecode(JSONRPCErrorResponse.self, from: payload) { diff --git a/Sources/WalletConnectRelay/RelayMethods.swift b/Sources/WalletConnectRelay/RelayMethods.swift new file mode 100644 index 000000000..dd9d43fb2 --- /dev/null +++ b/Sources/WalletConnectRelay/RelayMethods.swift @@ -0,0 +1,50 @@ +struct Subscribe: RelayRPC { + + struct Params: Codable { + let topic: String + } + + let params: Params + + var method: String { + "subscribe" + } +} + +struct Unsubscribe: RelayRPC { + + struct Params: Codable { + let id: String + let topic: String + } + + let params: Params + + var method: String { + "unsubscribe" + } +} + +struct Publish: RelayRPC { + + struct Params: Codable { + let topic: String + let message: String + let ttl: Int + let prompt: Bool? + let tag: Int? + } + + let params: Params + + var method: String { + "publish" + } +} + +struct Subscription { + + var method: String { + "subscription" + } +} diff --git a/Sources/WalletConnectRelay/RelayRPC.swift b/Sources/WalletConnectRelay/RelayRPC.swift new file mode 100644 index 000000000..45bbe6227 --- /dev/null +++ b/Sources/WalletConnectRelay/RelayRPC.swift @@ -0,0 +1,54 @@ +import JSONRPC + +protocol RPCMethod { + associatedtype Parameters + var method: String { get } + var params: Parameters { get } +} + +protocol RelayRPC: RPCMethod {} + +extension RelayRPC where Parameters: Codable { + + var idGenerator: IdentifierGenerator { + return WalletConnectRPCID() + } + + func wrapToIridium() -> PrefixDecorator { + return PrefixDecorator(rpcMethod: self, prefix: "iridium") +// return PrefixDecorator.iridium(rpcMethod: self) + } + + func asRPCRequest() -> RPCRequest { + RPCRequest(method: self.method, params: self.params, idGenerator: self.idGenerator) + } +} + +struct PrefixDecorator: RelayRPC where T: RelayRPC { + + typealias Parameters = T.Parameters + + let rpcMethod: T + let prefix: String + + var method: String { + "\(prefix)_\(rpcMethod.method)" + } + + var params: Parameters { + rpcMethod.params + } +} + + +// TODO: Move +import Foundation + +struct WalletConnectRPCID: IdentifierGenerator { + + func next() -> RPCID { + let timestamp = Int64(Date().timeIntervalSince1970 * 1000) * 1000 + let random = Int64.random(in: 0..<1000) + return .right(Int(timestamp + random)) + } +} diff --git a/Sources/WalletConnectSign/NetworkInteractor/NetworkRelaying.swift b/Sources/WalletConnectSign/NetworkInteractor/NetworkRelaying.swift index 8892ff6a7..244a1dcba 100644 --- a/Sources/WalletConnectSign/NetworkInteractor/NetworkRelaying.swift +++ b/Sources/WalletConnectSign/NetworkInteractor/NetworkRelaying.swift @@ -11,7 +11,7 @@ protocol NetworkRelaying { func disconnect(closeCode: URLSessionWebSocketTask.CloseCode) throws func publish(topic: String, payload: String, tag: Int, prompt: Bool) async throws /// - returns: request id - @discardableResult func publish(topic: String, payload: String, tag: Int, prompt: Bool, onNetworkAcknowledge: @escaping ((Error?) -> Void)) -> Int64 + @discardableResult func publish(topic: String, payload: String, tag: Int, prompt: Bool, onNetworkAcknowledge: @escaping ((Error?) -> Void)) //-> Int64 func subscribe(topic: String, completion: @escaping (Error?) -> Void) func subscribe(topic: String) async throws /// - returns: request id diff --git a/Sources/WalletConnectUtils/Encodable.swift b/Sources/WalletConnectUtils/Encodable.swift index 74d14671c..4d18c65b9 100644 --- a/Sources/WalletConnectUtils/Encodable.swift +++ b/Sources/WalletConnectUtils/Encodable.swift @@ -8,6 +8,8 @@ public enum DataConversionError: Error { } public extension Encodable { + + // TODO: Migrate func json() throws -> String { let data = try JSONEncoder().encode(self) guard let string = String(data: data, encoding: .utf8) else { @@ -15,4 +17,12 @@ public extension Encodable { } return string } + + func asJSONEncodedString() throws -> String { + let data = try JSONEncoder().encode(self) + guard let string = String(data: data, encoding: .utf8) else { + throw DataConversionError.dataToStringFailed + } + return string + } } diff --git a/Tests/RelayerTests/Mocks/DispatcherMock.swift b/Tests/RelayerTests/Mocks/DispatcherMock.swift index 4efc9c222..97ddac5dc 100644 --- a/Tests/RelayerTests/Mocks/DispatcherMock.swift +++ b/Tests/RelayerTests/Mocks/DispatcherMock.swift @@ -1,17 +1,32 @@ import Foundation +import JSONRPC @testable import WalletConnectRelay class DispatcherMock: Dispatching { + var onConnect: (() -> Void)? var onDisconnect: (() -> Void)? var onMessage: ((String) -> Void)? + + func connect() {} + func disconnect(closeCode: URLSessionWebSocketTask.CloseCode) {} + var sent = false + var lastMessage: String = "" + func send(_ string: String, completion: @escaping (Error?) -> Void) { sent = true + lastMessage = string } func send(_ string: String) async throws { send(string, completion: { _ in }) } - func connect() {} - func disconnect(closeCode: URLSessionWebSocketTask.CloseCode) {} +} + +extension DispatcherMock { + + func getLastRequestSent() -> RPCRequest { + let data = lastMessage.data(using: .utf8)! + return try! JSONDecoder().decode(RPCRequest.self, from: data) + } } diff --git a/Tests/RelayerTests/IridiumRelayTests.swift b/Tests/RelayerTests/RelayClientTests.swift similarity index 69% rename from Tests/RelayerTests/IridiumRelayTests.swift rename to Tests/RelayerTests/RelayClientTests.swift index 4ce71e96f..f436fca26 100644 --- a/Tests/RelayerTests/IridiumRelayTests.swift +++ b/Tests/RelayerTests/RelayClientTests.swift @@ -1,21 +1,23 @@ import WalletConnectUtils import Foundation import Combine +import JSONRPC import XCTest @testable import WalletConnectRelay -class IridiumRelayTests: XCTestCase { - var iridiumRelay: RelayClient! +final class RelayClientTests: XCTestCase { + + var sut: RelayClient! var dispatcher: DispatcherMock! override func setUp() { dispatcher = DispatcherMock() let logger = ConsoleLogger() - iridiumRelay = RelayClient(dispatcher: dispatcher, logger: logger, keyValueStorage: RuntimeKeyValueStorage()) + sut = RelayClient(dispatcher: dispatcher, logger: logger, keyValueStorage: RuntimeKeyValueStorage()) } override func tearDown() { - iridiumRelay = nil + sut = nil dispatcher = nil } @@ -26,7 +28,7 @@ class IridiumRelayTests: XCTestCase { let subscriptionId = "sub-id" let subscriptionParams = RelayJSONRPC.SubscriptionParams(id: subscriptionId, data: RelayJSONRPC.SubscriptionData(topic: topic, message: message)) let subscriptionRequest = JSONRPCRequest(id: 12345, method: RelayJSONRPC.Method.subscription.method, params: subscriptionParams) - iridiumRelay.onMessage = { subscriptionTopic, subscriptionMessage in + sut.onMessage = { subscriptionTopic, subscriptionMessage in XCTAssertEqual(subscriptionMessage, message) XCTAssertEqual(subscriptionTopic, topic) subscriptionExpectation.fulfill() @@ -37,20 +39,29 @@ class IridiumRelayTests: XCTestCase { func testPublishRequestAcknowledge() { let acknowledgeExpectation = expectation(description: "completion with no error on iridium request acknowledge after publish") - let requestId = iridiumRelay.publish(topic: "", payload: "{}", tag: 0, onNetworkAcknowledge: { error in - acknowledgeExpectation.fulfill() + sut.publish(topic: "", payload: "{}", tag: 0) { error in XCTAssertNil(error) - }) - let response = try! JSONRPCResponse(id: requestId, result: true).json() - dispatcher.onMessage?(response) + acknowledgeExpectation.fulfill() + } + let request = dispatcher.getLastRequestSent() + let response = RPCResponse(matchingRequest: request, result: true) + dispatcher.onMessage?(try! response.asJSONEncodedString()) waitForExpectations(timeout: 0.001, handler: nil) + +// let requestId = sut.publish(topic: "", payload: "{}", tag: 0, onNetworkAcknowledge: { error in +// acknowledgeExpectation.fulfill() +// XCTAssertNil(error) +// }) +// let response = try! JSONRPCResponse(id: requestId, result: true).json() +// dispatcher.onMessage?(response) +// waitForExpectations(timeout: 0.001, handler: nil) } func testUnsubscribeRequestAcknowledge() { let acknowledgeExpectation = expectation(description: "completion with no error on iridium request acknowledge after unsubscribe") let topic = "1234" - iridiumRelay.subscriptions[topic] = "" - let requestId = iridiumRelay.unsubscribe(topic: topic) { error in + sut.subscriptions[topic] = "" + let requestId = sut.unsubscribe(topic: topic) { error in XCTAssertNil(error) acknowledgeExpectation.fulfill() } @@ -63,7 +74,7 @@ class IridiumRelayTests: XCTestCase { let expectation = expectation(description: "Request duplicate not delivered") let subscriptionParams = RelayJSONRPC.SubscriptionParams(id: "sub_id", data: RelayJSONRPC.SubscriptionData(topic: "topic", message: "message")) let subscriptionRequest = JSONRPCRequest(id: 12345, method: RelayJSONRPC.Method.subscription.method, params: subscriptionParams) - iridiumRelay.onMessage = { _, _ in + sut.onMessage = { _, _ in expectation.fulfill() } dispatcher.onMessage?(try! subscriptionRequest.json()) @@ -72,19 +83,19 @@ class IridiumRelayTests: XCTestCase { } func testSendOnPublish() { - iridiumRelay.publish(topic: "", payload: "", tag: 0, onNetworkAcknowledge: { _ in}) + sut.publish(topic: "", payload: "", tag: 0, onNetworkAcknowledge: { _ in}) XCTAssertTrue(dispatcher.sent) } func testSendOnSubscribe() { - iridiumRelay.subscribe(topic: "") {_ in } + sut.subscribe(topic: "") {_ in } XCTAssertTrue(dispatcher.sent) } func testSendOnUnsubscribe() { let topic = "123" - iridiumRelay.subscriptions[topic] = "" - iridiumRelay.unsubscribe(topic: topic) {_ in } + sut.subscriptions[topic] = "" + sut.unsubscribe(topic: topic) {_ in } XCTAssertTrue(dispatcher.sent) } } diff --git a/Tests/WalletConnectSignTests/Mocks/MockedRelayClient.swift b/Tests/WalletConnectSignTests/Mocks/MockedRelayClient.swift index b8de74a1a..6ba828abb 100644 --- a/Tests/WalletConnectSignTests/Mocks/MockedRelayClient.swift +++ b/Tests/WalletConnectSignTests/Mocks/MockedRelayClient.swift @@ -18,10 +18,10 @@ class MockedRelayClient: NetworkRelaying { var onMessage: ((String, String) -> Void)? var error: Error? var prompt = false - func publish(topic: String, payload: String, tag: Int, prompt: Bool, onNetworkAcknowledge: @escaping ((Error?) -> Void)) -> Int64 { + func publish(topic: String, payload: String, tag: Int, prompt: Bool, onNetworkAcknowledge: @escaping ((Error?) -> Void)) {//}-> Int64 { self.prompt = prompt onNetworkAcknowledge(error) - return 0 +// return 0 } func subscribe(topic: String, completion: @escaping (Error?) -> Void) { From caf380996f79800b97b4385e800159029df36387 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Vants?= Date: Thu, 28 Jul 2022 12:32:44 -0300 Subject: [PATCH 03/14] Replaced unsubscribe call and fixed test --- Sources/WalletConnectRelay/RelayClient.swift | 34 ++++++++----------- .../NetworkInteractor/NetworkRelaying.swift | 4 +-- Tests/RelayerTests/RelayClientTests.swift | 21 ++++-------- .../Mocks/MockedRelayClient.swift | 7 ++-- 4 files changed, 27 insertions(+), 39 deletions(-) diff --git a/Sources/WalletConnectRelay/RelayClient.swift b/Sources/WalletConnectRelay/RelayClient.swift index 7871404d8..544078323 100644 --- a/Sources/WalletConnectRelay/RelayClient.swift +++ b/Sources/WalletConnectRelay/RelayClient.swift @@ -114,11 +114,7 @@ public final class RelayClient { tag: Int, prompt: Bool = false, onNetworkAcknowledge: @escaping ((Error?) -> Void) - ) { //-> Int64 { -// let params = RelayJSONRPC.PublishParams(topic: topic, message: payload, ttl: defaultTtl, prompt: prompt, tag: tag) -// let request = JSONRPCRequest(method: RelayJSONRPC.Method.publish.method, params: params) -// let message = try! request.json() - + ) { let rpc = Publish(params: .init(topic: topic, message: payload, ttl: defaultTtl, prompt: prompt, tag: tag)) let request = rpc .wrapToIridium() @@ -139,7 +135,6 @@ public final class RelayClient { cancellable?.cancel() onNetworkAcknowledge(nil) } -// return Int64(request.id!.right!) } @available(*, renamed: "subscribe(topic:)") @@ -180,18 +175,20 @@ public final class RelayClient { } } - @discardableResult public func unsubscribe(topic: String, completion: @escaping ((Error?) -> Void)) -> Int64? { + @discardableResult public func unsubscribe(topic: String, completion: @escaping ((Error?) -> Void)) { guard let subscriptionId = subscriptions[topic] else { completion(RelyerError.subscriptionIdNotFound) - return nil + return } logger.debug("iridium: Unsubscribing on Topic: \(topic)") - let params = RelayJSONRPC.UnsubscribeParams(id: subscriptionId, topic: topic) - let request = JSONRPCRequest(method: RelayJSONRPC.Method.unsubscribe.method, params: params) - let requestJson = try! request.json() + let rpc = Unsubscribe(params: .init(id: subscriptionId, topic: topic)) + let request = rpc + .wrapToIridium() + .asRPCRequest() + let message = try! request.asJSONEncodedString() var cancellable: AnyCancellable? jsonRpcSubscriptionsHistory.delete(topic: topic) - dispatcher.send(requestJson) { [weak self] error in + dispatcher.send(message) { [weak self] error in if let error = error { self?.logger.debug("Failed to Unsubscribe on Topic") cancellable?.cancel() @@ -203,13 +200,12 @@ public final class RelayClient { completion(nil) } } -// cancellable = requestAcknowledgePublisher -// .filter { $0 == request.id } -// .sink { (_) in -// cancellable?.cancel() -// completion(nil) -// } - return request.id + cancellable = requestAcknowledgePublisher + .filter { $0 == request.id } + .sink { (_) in + cancellable?.cancel() + completion(nil) + } } private func setUpBindings() { diff --git a/Sources/WalletConnectSign/NetworkInteractor/NetworkRelaying.swift b/Sources/WalletConnectSign/NetworkInteractor/NetworkRelaying.swift index 244a1dcba..2ea62420d 100644 --- a/Sources/WalletConnectSign/NetworkInteractor/NetworkRelaying.swift +++ b/Sources/WalletConnectSign/NetworkInteractor/NetworkRelaying.swift @@ -11,9 +11,9 @@ protocol NetworkRelaying { func disconnect(closeCode: URLSessionWebSocketTask.CloseCode) throws func publish(topic: String, payload: String, tag: Int, prompt: Bool) async throws /// - returns: request id - @discardableResult func publish(topic: String, payload: String, tag: Int, prompt: Bool, onNetworkAcknowledge: @escaping ((Error?) -> Void)) //-> Int64 + 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 - @discardableResult func unsubscribe(topic: String, completion: @escaping ((Error?) -> Void)) -> Int64? + func unsubscribe(topic: String, completion: @escaping ((Error?) -> Void)) } diff --git a/Tests/RelayerTests/RelayClientTests.swift b/Tests/RelayerTests/RelayClientTests.swift index f436fca26..7fffdb654 100644 --- a/Tests/RelayerTests/RelayClientTests.swift +++ b/Tests/RelayerTests/RelayClientTests.swift @@ -46,28 +46,21 @@ final class RelayClientTests: XCTestCase { let request = dispatcher.getLastRequestSent() let response = RPCResponse(matchingRequest: request, result: true) dispatcher.onMessage?(try! response.asJSONEncodedString()) - waitForExpectations(timeout: 0.001, handler: nil) - -// let requestId = sut.publish(topic: "", payload: "{}", tag: 0, onNetworkAcknowledge: { error in -// acknowledgeExpectation.fulfill() -// XCTAssertNil(error) -// }) -// let response = try! JSONRPCResponse(id: requestId, result: true).json() -// dispatcher.onMessage?(response) -// waitForExpectations(timeout: 0.001, handler: nil) + waitForExpectations(timeout: 0.1, handler: nil) } func testUnsubscribeRequestAcknowledge() { let acknowledgeExpectation = expectation(description: "completion with no error on iridium request acknowledge after unsubscribe") - let topic = "1234" + let topic = String.randomTopic() sut.subscriptions[topic] = "" - let requestId = sut.unsubscribe(topic: topic) { error in + sut.unsubscribe(topic: topic) { error in XCTAssertNil(error) acknowledgeExpectation.fulfill() } - let response = try! JSONRPCResponse(id: requestId!, result: true).json() - dispatcher.onMessage?(response) - waitForExpectations(timeout: 0.001, handler: nil) + let request = dispatcher.getLastRequestSent() + let response = RPCResponse(matchingRequest: request, result: true) + dispatcher.onMessage?(try! response.asJSONEncodedString()) + waitForExpectations(timeout: 0.1, handler: nil) } func testSubscriptionRequestDeliveredOnce() { diff --git a/Tests/WalletConnectSignTests/Mocks/MockedRelayClient.swift b/Tests/WalletConnectSignTests/Mocks/MockedRelayClient.swift index 6ba828abb..98f57f0ef 100644 --- a/Tests/WalletConnectSignTests/Mocks/MockedRelayClient.swift +++ b/Tests/WalletConnectSignTests/Mocks/MockedRelayClient.swift @@ -18,18 +18,17 @@ class MockedRelayClient: NetworkRelaying { var onMessage: ((String, String) -> Void)? var error: Error? var prompt = false - func publish(topic: String, payload: String, tag: Int, prompt: Bool, onNetworkAcknowledge: @escaping ((Error?) -> Void)) {//}-> Int64 { + func publish(topic: String, payload: String, tag: Int, prompt: Bool, onNetworkAcknowledge: @escaping ((Error?) -> Void)) { self.prompt = prompt onNetworkAcknowledge(error) -// return 0 } func subscribe(topic: String, completion: @escaping (Error?) -> Void) { } - func unsubscribe(topic: String, completion: @escaping ((Error?) -> Void)) -> Int64? { - return 0 + func unsubscribe(topic: String, completion: @escaping ((Error?) -> Void)) { } + func connect() { } From 9e86741249ef43fdb8e4748d24825c74bb4dd2a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Vants?= Date: Thu, 28 Jul 2022 12:46:59 -0300 Subject: [PATCH 04/14] Replaced subscribe call and added test --- Sources/WalletConnectRelay/RelayClient.swift | 76 ++++++++++---------- Tests/RelayerTests/RelayClientTests.swift | 12 ++++ 2 files changed, 51 insertions(+), 37 deletions(-) diff --git a/Sources/WalletConnectRelay/RelayClient.swift b/Sources/WalletConnectRelay/RelayClient.swift index 544078323..29d97655d 100644 --- a/Sources/WalletConnectRelay/RelayClient.swift +++ b/Sources/WalletConnectRelay/RelayClient.swift @@ -28,10 +28,11 @@ public final class RelayClient { } private let socketConnectionStatusPublisherSubject = PassthroughSubject() - private var subscriptionResponsePublisher: AnyPublisher, Never> { + + private let subscriptionResponsePublisherSubject = PassthroughSubject<(RPCID?, String), Never>() + private var subscriptionResponsePublisher: AnyPublisher<(RPCID?, String), Never> { subscriptionResponsePublisherSubject.eraseToAnyPublisher() } - private let subscriptionResponsePublisherSubject = PassthroughSubject, Never>() private let requestAcknowledgePublisherSubject = PassthroughSubject() private var requestAcknowledgePublisher: AnyPublisher { @@ -108,7 +109,7 @@ public final class RelayClient { } /// Completes with an acknowledgement from the relay network. - @discardableResult public func publish( + public func publish( topic: String, payload: String, tag: Int, @@ -122,6 +123,12 @@ public final class RelayClient { let message = try! request.asJSONEncodedString() logger.debug("iridium: Publishing Payload on Topic: \(topic)") var cancellable: AnyCancellable? + cancellable = requestAcknowledgePublisher + .filter { $0 == request.id } + .sink { (_) in + cancellable?.cancel() + onNetworkAcknowledge(nil) + } dispatcher.send(message) { [weak self] error in if let error = error { self?.logger.debug("Failed to Publish Payload, error: \(error)") @@ -129,38 +136,33 @@ public final class RelayClient { onNetworkAcknowledge(error) } } - cancellable = requestAcknowledgePublisher - .filter { $0 == request.id } - .sink { (_) in - cancellable?.cancel() - onNetworkAcknowledge(nil) - } } @available(*, renamed: "subscribe(topic:)") public func subscribe(topic: String, completion: @escaping (Error?) -> Void) { logger.debug("iridium: Subscribing on Topic: \(topic)") - let params = RelayJSONRPC.SubscribeParams(topic: topic) - let request = JSONRPCRequest(method: RelayJSONRPC.Method.subscribe.method, params: params) - let requestJson = try! request.json() + let rpc = Subscribe(params: .init(topic: topic)) + let request = rpc + .wrapToIridium() + .asRPCRequest() + let message = try! request.asJSONEncodedString() var cancellable: AnyCancellable? - dispatcher.send(requestJson) { [weak self] error in + cancellable = subscriptionResponsePublisher + .filter { $0.0 == request.id } + .sink { [weak self] subscriptionInfo in + cancellable?.cancel() + self?.concurrentQueue.async(flags: .barrier) { + self?.subscriptions[topic] = subscriptionInfo.1 + } + completion(nil) + } + dispatcher.send(message) { [weak self] error in if let error = error { self?.logger.debug("Failed to Subscribe on Topic \(error)") cancellable?.cancel() completion(error) - } else { - completion(nil) } } - cancellable = subscriptionResponsePublisher - .filter {$0.id == request.id} - .sink { [weak self] (subscriptionResponse) in - cancellable?.cancel() - self?.concurrentQueue.async(flags: .barrier) { - self?.subscriptions[topic] = subscriptionResponse.result - } - } } public func subscribe(topic: String) async throws { @@ -175,7 +177,7 @@ public final class RelayClient { } } - @discardableResult public func unsubscribe(topic: String, completion: @escaping ((Error?) -> Void)) { + public func unsubscribe(topic: String, completion: @escaping ((Error?) -> Void)) { guard let subscriptionId = subscriptions[topic] else { completion(RelyerError.subscriptionIdNotFound) return @@ -186,8 +188,14 @@ public final class RelayClient { .wrapToIridium() .asRPCRequest() let message = try! request.asJSONEncodedString() - var cancellable: AnyCancellable? jsonRpcSubscriptionsHistory.delete(topic: topic) + var cancellable: AnyCancellable? + cancellable = requestAcknowledgePublisher + .filter { $0 == request.id } + .sink { (_) in + cancellable?.cancel() + completion(nil) + } dispatcher.send(message) { [weak self] error in if let error = error { self?.logger.debug("Failed to Unsubscribe on Topic") @@ -200,12 +208,6 @@ public final class RelayClient { completion(nil) } } - cancellable = requestAcknowledgePublisher - .filter { $0 == request.id } - .sink { (_) in - cancellable?.cancel() - completion(nil) - } } private func setUpBindings() { @@ -230,18 +232,18 @@ public final class RelayClient { // use this switch response.outcome { case .success(let anyCodable): - if let acknowledgement = try? anyCodable.get(Bool.self) { + if let _ = try? anyCodable.get(Bool.self) { // TODO: Handle success vs. error requestAcknowledgePublisherSubject.send(response.id) - } else if let subscription = try? anyCodable.get(String.self) { - + } else if let subscriptionId = try? anyCodable.get(String.self) { + subscriptionResponsePublisherSubject.send((response.id, subscriptionId)) } case .failure(let rpcError): - break + logger.error("Received error message from iridium network, code: \(rpcError.code), message: \(rpcError.message)") } // } else if let response = tryDecode(RequestAcknowledgement.self, from: payload) { // requestAcknowledgePublisherSubject.send(response) - } else if let response = tryDecode(SubscriptionResponse.self, from: payload) { - subscriptionResponsePublisherSubject.send(response) +// } else if let response = tryDecode(SubscriptionResponse.self, from: payload) { +// subscriptionResponsePublisherSubject.send(response) } else if let response = tryDecode(JSONRPCErrorResponse.self, from: payload) { logger.error("Received error message from iridium network, code: \(response.error.code), message: \(response.error.message)") } else { diff --git a/Tests/RelayerTests/RelayClientTests.swift b/Tests/RelayerTests/RelayClientTests.swift index 7fffdb654..874f8e85d 100644 --- a/Tests/RelayerTests/RelayClientTests.swift +++ b/Tests/RelayerTests/RelayClientTests.swift @@ -37,6 +37,18 @@ final class RelayClientTests: XCTestCase { waitForExpectations(timeout: 0.001, handler: nil) } + func testSubscribeRequestAcknowledge() { + let acknowledgeExpectation = expectation(description: "") + sut.subscribe(topic: "") { error in + XCTAssertNil(error) + acknowledgeExpectation.fulfill() + } + let request = dispatcher.getLastRequestSent() + let response = RPCResponse(matchingRequest: request, result: "id") + dispatcher.onMessage?(try! response.asJSONEncodedString()) + waitForExpectations(timeout: 0.1, handler: nil) + } + func testPublishRequestAcknowledge() { let acknowledgeExpectation = expectation(description: "completion with no error on iridium request acknowledge after publish") sut.publish(topic: "", payload: "{}", tag: 0) { error in From d8d8564eb8877adec9eeba4cae3f97de987557d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Vants?= Date: Thu, 28 Jul 2022 13:09:13 -0300 Subject: [PATCH 05/14] Add subscription parsing --- Sources/WalletConnectRelay/RelayClient.swift | 33 ++++++++++--------- Sources/WalletConnectRelay/RelayMethods.swift | 13 +++++++- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/Sources/WalletConnectRelay/RelayClient.swift b/Sources/WalletConnectRelay/RelayClient.swift index 29d97655d..dcb40d499 100644 --- a/Sources/WalletConnectRelay/RelayClient.swift +++ b/Sources/WalletConnectRelay/RelayClient.swift @@ -13,8 +13,7 @@ public final class RelayClient { case subscriptionIdNotFound } private typealias SubscriptionRequest = JSONRPCRequest - private typealias SubscriptionResponse = JSONRPCResponse - private typealias RequestAcknowledgement = JSONRPCResponse + private let concurrentQueue = DispatchQueue(label: "com.walletconnect.sdk.relay_client", attributes: .concurrent) let jsonRpcSubscriptionsHistory: JsonRpcHistory @@ -220,16 +219,24 @@ public final class RelayClient { } private func handlePayloadMessage(_ payload: String) { - if let request = tryDecode(SubscriptionRequest.self, from: payload), validate(request: request, method: .subscription) { - do { - try jsonRpcSubscriptionsHistory.set(topic: request.params.data.topic, request: request) - onMessage?(request.params.data.topic, request.params.data.message) - acknowledgeSubscription(requestId: request.id) - } catch { - logger.info("Relay Client Info: Json Rpc Duplicate Detected") +// if let request = tryDecode(SubscriptionRequest.self, from: payload), validate(request: request, method: .subscription) { +// do { +// try jsonRpcSubscriptionsHistory.set(topic: request.params.data.topic, request: request) +// onMessage?(request.params.data.topic, request.params.data.message) +// acknowledgeSubscription(requestId: request.id) +// } catch { +// logger.info("Relay Client Info: Json Rpc Duplicate Detected") +// } +// } + if let request = tryDecode(RPCRequest.self, from: payload) { + if let params = try? request.params?.get(Subscription.Params.self) { + // set history + onMessage?(params.data.topic, params.data.message) + acknowledgeSubscription(requestId: Int64(request.id!.right!)) + } else { + // not subscription } } else if let response = tryDecode(RPCResponse.self, from: payload) { - // use this switch response.outcome { case .success(let anyCodable): if let _ = try? anyCodable.get(Bool.self) { // TODO: Handle success vs. error @@ -240,12 +247,6 @@ public final class RelayClient { case .failure(let rpcError): logger.error("Received error message from iridium network, code: \(rpcError.code), message: \(rpcError.message)") } -// } else if let response = tryDecode(RequestAcknowledgement.self, from: payload) { -// requestAcknowledgePublisherSubject.send(response) -// } else if let response = tryDecode(SubscriptionResponse.self, from: payload) { -// subscriptionResponsePublisherSubject.send(response) - } else if let response = tryDecode(JSONRPCErrorResponse.self, from: payload) { - logger.error("Received error message from iridium network, code: \(response.error.code), message: \(response.error.message)") } else { logger.error("Unexpected response from network") } diff --git a/Sources/WalletConnectRelay/RelayMethods.swift b/Sources/WalletConnectRelay/RelayMethods.swift index dd9d43fb2..66adbade6 100644 --- a/Sources/WalletConnectRelay/RelayMethods.swift +++ b/Sources/WalletConnectRelay/RelayMethods.swift @@ -42,7 +42,18 @@ struct Publish: RelayRPC { } } -struct Subscription { +struct Subscription: RelayRPC { + + struct Params: Codable { + struct Contents: Codable { + let topic: String + let message: String + } + let id: String + let data: Contents + } + + let params: Params var method: String { "subscription" From 84fb09db98b2a8afba174dd910dd23d43a33ab8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Vants?= Date: Thu, 28 Jul 2022 19:33:46 -0300 Subject: [PATCH 06/14] Introduces new and improved RPC history --- Package.swift | 2 +- Sources/WalletConnectRelay/RelayClient.swift | 3 +- Sources/WalletConnectUtils/RPCHistory.swift | 67 ++++++++++++++++++++ 3 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 Sources/WalletConnectUtils/RPCHistory.swift diff --git a/Package.swift b/Package.swift index 1d1cdbf9f..4741a09f3 100644 --- a/Package.swift +++ b/Package.swift @@ -47,7 +47,7 @@ let package = Package( dependencies: ["WalletConnectUtils"]), .target( name: "WalletConnectUtils", - dependencies: ["Commons"]), + dependencies: ["Commons", "JSONRPC"]), .target( name: "JSONRPC", dependencies: ["Commons"]), diff --git a/Sources/WalletConnectRelay/RelayClient.swift b/Sources/WalletConnectRelay/RelayClient.swift index dcb40d499..206d2c68c 100644 --- a/Sources/WalletConnectRelay/RelayClient.swift +++ b/Sources/WalletConnectRelay/RelayClient.swift @@ -13,7 +13,7 @@ public final class RelayClient { case subscriptionIdNotFound } private typealias SubscriptionRequest = JSONRPCRequest - + private let concurrentQueue = DispatchQueue(label: "com.walletconnect.sdk.relay_client", attributes: .concurrent) let jsonRpcSubscriptionsHistory: JsonRpcHistory @@ -218,6 +218,7 @@ public final class RelayClient { } } + // FIXME: Parse data to string once before trying to decode -> respond error on fail private func handlePayloadMessage(_ payload: String) { // if let request = tryDecode(SubscriptionRequest.self, from: payload), validate(request: request, method: .subscription) { // do { diff --git a/Sources/WalletConnectUtils/RPCHistory.swift b/Sources/WalletConnectUtils/RPCHistory.swift new file mode 100644 index 000000000..50ad886f5 --- /dev/null +++ b/Sources/WalletConnectUtils/RPCHistory.swift @@ -0,0 +1,67 @@ +import JSONRPC + +public final class RPCHistory { + + public struct Record: Codable { + public enum Origin: String, Codable { + case local + case remote + } + let id: RPCID + let topic: String + let origin: Origin + let request: RPCRequest + var response: RPCResponse? = nil + } + + enum HistoryError: Error { + case unidentifiedRequest + case unidentifiedResponse + case requestDuplicateNotAllowed + case responseDuplicateNotAllowed + case requestMatchingResponseNotFound + } + + private let storage: CodableStore + + public init(keyValueStore: CodableStore) { + self.storage = keyValueStore + } + + public func get(recordId: RPCID) -> Record? { + try? storage.get(key: "\(recordId)") + } + + public func set(_ request: RPCRequest, for topic: String, emmitedBy origin: Record.Origin) throws { + guard let id = request.id else { + throw HistoryError.unidentifiedRequest + } + guard get(recordId: id) == nil else { + throw HistoryError.requestDuplicateNotAllowed + } + let record = Record(id: id, topic: topic, origin: origin, request: request) + storage.set(record, forKey: "\(record.id)") + } + + public func resolve(_ response: RPCResponse) throws { + guard let id = response.id else { + throw HistoryError.unidentifiedResponse + } + guard var record = get(recordId: id) else { + throw HistoryError.requestMatchingResponseNotFound + } + guard record.response == nil else { + throw HistoryError.responseDuplicateNotAllowed + } + record.response = response + storage.set(record, forKey: "\(record.id)") + } + + public func delete(topic: String) { + storage.getAll().forEach { record in + if record.topic == topic { + storage.delete(forKey: "\(record.id)") + } + } + } +} From ccacb2ffe65ba29ba3e5e41684e9920d0b6033f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Vants?= Date: Thu, 28 Jul 2022 19:53:24 -0300 Subject: [PATCH 07/14] Replace rpc history used in relay client --- Sources/WalletConnectRelay/RelayClient.swift | 96 +++++++++----------- 1 file changed, 44 insertions(+), 52 deletions(-) diff --git a/Sources/WalletConnectRelay/RelayClient.swift b/Sources/WalletConnectRelay/RelayClient.swift index 206d2c68c..c94f63da8 100644 --- a/Sources/WalletConnectRelay/RelayClient.swift +++ b/Sources/WalletConnectRelay/RelayClient.swift @@ -8,37 +8,39 @@ public enum SocketConnectionStatus { case connected case disconnected } + public final class RelayClient { - enum RelyerError: Error { + + enum RelayerError: Error { case subscriptionIdNotFound } - private typealias SubscriptionRequest = JSONRPCRequest - private let concurrentQueue = DispatchQueue(label: "com.walletconnect.sdk.relay_client", - attributes: .concurrent) - let jsonRpcSubscriptionsHistory: JsonRpcHistory + static let historyIdentifier = "com.walletconnect.sdk.relayer_client.subscription_json_rpc_record" + public var onMessage: ((String, String) -> Void)? - private var dispatcher: Dispatching - var subscriptions: [String: String] = [:] + let defaultTtl = 6*Time.hour + var subscriptions: [String: String] = [:] public var socketConnectionStatusPublisher: AnyPublisher { socketConnectionStatusPublisherSubject.eraseToAnyPublisher() } private let socketConnectionStatusPublisherSubject = PassthroughSubject() - private let subscriptionResponsePublisherSubject = PassthroughSubject<(RPCID?, String), Never>() private var subscriptionResponsePublisher: AnyPublisher<(RPCID?, String), Never> { subscriptionResponsePublisherSubject.eraseToAnyPublisher() } - private let requestAcknowledgePublisherSubject = PassthroughSubject() private var requestAcknowledgePublisher: AnyPublisher { requestAcknowledgePublisherSubject.eraseToAnyPublisher() } - let logger: ConsoleLogging - static let historyIdentifier = "com.walletconnect.sdk.relayer_client.subscription_json_rpc_record" + + private var dispatcher: Dispatching + private let rpcHistory: RPCHistory + private let logger: ConsoleLogging + + private let concurrentQueue = DispatchQueue(label: "com.walletconnect.sdk.relay_client", attributes: .concurrent) init( dispatcher: Dispatching, @@ -47,11 +49,19 @@ public final class RelayClient { ) { self.logger = logger self.dispatcher = dispatcher - - self.jsonRpcSubscriptionsHistory = JsonRpcHistory(logger: logger, keyValueStore: CodableStore(defaults: keyValueStorage, identifier: Self.historyIdentifier)) + self.rpcHistory = RPCHistory(keyValueStore: CodableStore(defaults: keyValueStorage, identifier: Self.historyIdentifier)) setUpBindings() } + private func setUpBindings() { + dispatcher.onMessage = { [weak self] payload in + self?.handlePayloadMessage(payload) + } + dispatcher.onConnect = { [unowned self] in + self.socketConnectionStatusPublisherSubject.send(.connected) + } + } + /// Instantiates Relay Client /// - Parameters: /// - relayHost: proxy server host that your application will use to connect to Iridium Network. If you register your project at `www.walletconnect.com` you can use `relay.walletconnect.com` @@ -139,7 +149,7 @@ public final class RelayClient { @available(*, renamed: "subscribe(topic:)") public func subscribe(topic: String, completion: @escaping (Error?) -> Void) { - logger.debug("iridium: Subscribing on Topic: \(topic)") + logger.debug("Relay: Subscribing to topic: \(topic)") let rpc = Subscribe(params: .init(topic: topic)) let request = rpc .wrapToIridium() @@ -157,7 +167,7 @@ public final class RelayClient { } dispatcher.send(message) { [weak self] error in if let error = error { - self?.logger.debug("Failed to Subscribe on Topic \(error)") + self?.logger.debug("Failed to subscribe to topic \(error)") cancellable?.cancel() completion(error) } @@ -178,16 +188,16 @@ public final class RelayClient { public func unsubscribe(topic: String, completion: @escaping ((Error?) -> Void)) { guard let subscriptionId = subscriptions[topic] else { - completion(RelyerError.subscriptionIdNotFound) + completion(RelayerError.subscriptionIdNotFound) return } - logger.debug("iridium: Unsubscribing on Topic: \(topic)") + logger.debug("Relay: Unsubscribing from topic: \(topic)") let rpc = Unsubscribe(params: .init(id: subscriptionId, topic: topic)) let request = rpc .wrapToIridium() .asRPCRequest() let message = try! request.asJSONEncodedString() - jsonRpcSubscriptionsHistory.delete(topic: topic) + rpcHistory.delete(topic: topic) var cancellable: AnyCancellable? cancellable = requestAcknowledgePublisher .filter { $0 == request.id } @@ -197,7 +207,7 @@ public final class RelayClient { } dispatcher.send(message) { [weak self] error in if let error = error { - self?.logger.debug("Failed to Unsubscribe on Topic") + self?.logger.debug("Failed to unsubscribe from topic") cancellable?.cancel() completion(error) } else { @@ -209,33 +219,19 @@ public final class RelayClient { } } - private func setUpBindings() { - dispatcher.onMessage = { [weak self] payload in - self?.handlePayloadMessage(payload) - } - dispatcher.onConnect = { [unowned self] in - self.socketConnectionStatusPublisherSubject.send(.connected) - } - } - // FIXME: Parse data to string once before trying to decode -> respond error on fail private func handlePayloadMessage(_ payload: String) { -// if let request = tryDecode(SubscriptionRequest.self, from: payload), validate(request: request, method: .subscription) { -// do { -// try jsonRpcSubscriptionsHistory.set(topic: request.params.data.topic, request: request) -// onMessage?(request.params.data.topic, request.params.data.message) -// acknowledgeSubscription(requestId: request.id) -// } catch { -// logger.info("Relay Client Info: Json Rpc Duplicate Detected") -// } -// } if let request = tryDecode(RPCRequest.self, from: payload) { if let params = try? request.params?.get(Subscription.Params.self) { - // set history - onMessage?(params.data.topic, params.data.message) - acknowledgeSubscription(requestId: Int64(request.id!.right!)) + do { + try rpcHistory.set(request, for: params.data.topic, emmitedBy: .remote) + try acknowledgeRequest(request) + onMessage?(params.data.topic, params.data.message) + } catch { + logger.error("[RelayClient] RPC History 'set()' error: \(error)") + } } else { - // not subscription + logger.error("Unexpected request from network") } } else if let response = tryDecode(RPCResponse.self, from: payload) { switch response.outcome { @@ -253,10 +249,6 @@ public final class RelayClient { } } - private func validate(request: JSONRPCRequest, method: RelayJSONRPC.Method) -> Bool { - return request.method.contains(method.name) - } - private func tryDecode(_ type: T.Type, from payload: String) -> T? { if let data = payload.data(using: .utf8), let response = try? JSONDecoder().decode(T.self, from: data) { @@ -266,13 +258,13 @@ public final class RelayClient { } } - private func acknowledgeSubscription(requestId: Int64) { - let response = JSONRPCResponse(id: requestId, result: AnyCodable(true)) - let responseJson = try! response.json() - _ = try? jsonRpcSubscriptionsHistory.resolve(response: JsonRpcResult.response(response)) - dispatcher.send(responseJson) { [weak self] error in - if let error = error { - self?.logger.debug("Failed to Respond for request id: \(requestId), error: \(error)") + private func acknowledgeRequest(_ request: RPCRequest) throws { + let response = RPCResponse(matchingRequest: request, result: true) + try rpcHistory.resolve(response) + let message = try response.asJSONEncodedString() + dispatcher.send(message) { [weak self] in + if let error = $0 { + self?.logger.debug("Failed to dispatch response: \(response), error: \(error)") } } } From 527fb669b61c89559a5df2c1a1aca6fddc806750 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Vants?= Date: Thu, 28 Jul 2022 20:05:41 -0300 Subject: [PATCH 08/14] Removed old relay method types --- Sources/WalletConnectRelay/RelayClient.swift | 11 ++-- Sources/WalletConnectRelay/RelayJSONRPC.swift | 63 ------------------- Sources/WalletConnectRelay/RelayMethods.swift | 4 ++ Tests/RelayerTests/RelayClientTests.swift | 16 ++--- 4 files changed, 18 insertions(+), 76 deletions(-) delete mode 100644 Sources/WalletConnectRelay/RelayJSONRPC.swift diff --git a/Sources/WalletConnectRelay/RelayClient.swift b/Sources/WalletConnectRelay/RelayClient.swift index c94f63da8..b5236567d 100644 --- a/Sources/WalletConnectRelay/RelayClient.swift +++ b/Sources/WalletConnectRelay/RelayClient.swift @@ -110,11 +110,12 @@ 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 params = RelayJSONRPC.PublishParams(topic: topic, message: payload, ttl: defaultTtl, prompt: prompt, tag: tag) - let request = JSONRPCRequest(method: RelayJSONRPC.Method.publish.method, params: params) - logger.debug("Publishing Payload on Topic: \(topic)") - let requestJson = try request.json() - try await dispatcher.send(requestJson) + let request = Publish(params: .init(topic: topic, message: payload, ttl: defaultTtl, prompt: prompt, tag: tag)) + .wrapToIridium() + .asRPCRequest() + let message = try request.asJSONEncodedString() + logger.debug("Publishing payload on topic: \(topic)") + try await dispatcher.send(message) } /// Completes with an acknowledgement from the relay network. diff --git a/Sources/WalletConnectRelay/RelayJSONRPC.swift b/Sources/WalletConnectRelay/RelayJSONRPC.swift deleted file mode 100644 index e83de4427..000000000 --- a/Sources/WalletConnectRelay/RelayJSONRPC.swift +++ /dev/null @@ -1,63 +0,0 @@ -// - -import Foundation - -enum RelayJSONRPC { - enum Method { - case subscribe - case publish - case subscription - case unsubscribe - } - - struct PublishParams: Codable, Equatable { - let topic: String - let message: String - let ttl: Int - let prompt: Bool? - let tag: Int? - } - - struct SubscribeParams: Codable, Equatable { - let topic: String - } - - struct SubscriptionData: Codable, Equatable { - let topic: String - let message: String - } - - struct SubscriptionParams: Codable, Equatable { - let id: String - let data: SubscriptionData - } - - struct UnsubscribeParams: Codable, Equatable { - let id: String - let topic: String - } -} - -extension RelayJSONRPC.Method { - - var prefix: String { - return "iridium" - } - - var name: String { - switch self { - case .subscribe: - return "subscribe" - case .publish: - return "publish" - case .subscription: - return "subscription" - case .unsubscribe: - return "unsubscribe" - } - } - - var method: String { - return "\(prefix)_\(name)" - } -} diff --git a/Sources/WalletConnectRelay/RelayMethods.swift b/Sources/WalletConnectRelay/RelayMethods.swift index 66adbade6..be94ac20e 100644 --- a/Sources/WalletConnectRelay/RelayMethods.swift +++ b/Sources/WalletConnectRelay/RelayMethods.swift @@ -58,4 +58,8 @@ struct Subscription: RelayRPC { var method: String { "subscription" } + + init(id: String, topic: String, message: String) { + self.params = Params(id: id, data: Params.Contents(topic: topic, message: message)) + } } diff --git a/Tests/RelayerTests/RelayClientTests.swift b/Tests/RelayerTests/RelayClientTests.swift index 874f8e85d..5958d3194 100644 --- a/Tests/RelayerTests/RelayClientTests.swift +++ b/Tests/RelayerTests/RelayClientTests.swift @@ -26,14 +26,15 @@ final class RelayClientTests: XCTestCase { let topic = "0987" let message = "qwerty" let subscriptionId = "sub-id" - let subscriptionParams = RelayJSONRPC.SubscriptionParams(id: subscriptionId, data: RelayJSONRPC.SubscriptionData(topic: topic, message: message)) - let subscriptionRequest = JSONRPCRequest(id: 12345, method: RelayJSONRPC.Method.subscription.method, params: subscriptionParams) + let subscription = Subscription(id: subscriptionId, topic: topic, message: message) + let request = subscription.asRPCRequest() + sut.onMessage = { subscriptionTopic, subscriptionMessage in XCTAssertEqual(subscriptionMessage, message) XCTAssertEqual(subscriptionTopic, topic) subscriptionExpectation.fulfill() } - dispatcher.onMessage?(try! subscriptionRequest.json()) + dispatcher.onMessage?(try! request.asJSONEncodedString()) waitForExpectations(timeout: 0.001, handler: nil) } @@ -77,14 +78,13 @@ final class RelayClientTests: XCTestCase { func testSubscriptionRequestDeliveredOnce() { let expectation = expectation(description: "Request duplicate not delivered") - let subscriptionParams = RelayJSONRPC.SubscriptionParams(id: "sub_id", data: RelayJSONRPC.SubscriptionData(topic: "topic", message: "message")) - let subscriptionRequest = JSONRPCRequest(id: 12345, method: RelayJSONRPC.Method.subscription.method, params: subscriptionParams) + let request = Subscription.init(id: "sub_id", topic: "topic", message: "message").asRPCRequest() sut.onMessage = { _, _ in expectation.fulfill() } - dispatcher.onMessage?(try! subscriptionRequest.json()) - dispatcher.onMessage?(try! subscriptionRequest.json()) - waitForExpectations(timeout: 0.001, handler: nil) + dispatcher.onMessage?(try! request.asJSONEncodedString()) + dispatcher.onMessage?(try! request.asJSONEncodedString()) + waitForExpectations(timeout: 0.1, handler: nil) } func testSendOnPublish() { From 4c6a55374c301f11f5060b9ab1cb192593f2a328 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Vants?= Date: Thu, 28 Jul 2022 20:12:03 -0300 Subject: [PATCH 09/14] Organize files and folders --- .../{RelayMethods.swift => RPC/Methods.swift} | 0 .../WalletConnectRelay/RPC/RPCMethod.swift | 5 ++++ .../{ => RPC}/RelayRPC.swift | 24 ++++--------------- .../RPC/WalletConnectRPCID.swift | 11 +++++++++ 4 files changed, 20 insertions(+), 20 deletions(-) rename Sources/WalletConnectRelay/{RelayMethods.swift => RPC/Methods.swift} (100%) create mode 100644 Sources/WalletConnectRelay/RPC/RPCMethod.swift rename Sources/WalletConnectRelay/{ => RPC}/RelayRPC.swift (61%) create mode 100644 Sources/WalletConnectRelay/RPC/WalletConnectRPCID.swift diff --git a/Sources/WalletConnectRelay/RelayMethods.swift b/Sources/WalletConnectRelay/RPC/Methods.swift similarity index 100% rename from Sources/WalletConnectRelay/RelayMethods.swift rename to Sources/WalletConnectRelay/RPC/Methods.swift diff --git a/Sources/WalletConnectRelay/RPC/RPCMethod.swift b/Sources/WalletConnectRelay/RPC/RPCMethod.swift new file mode 100644 index 000000000..1f104f7c7 --- /dev/null +++ b/Sources/WalletConnectRelay/RPC/RPCMethod.swift @@ -0,0 +1,5 @@ +protocol RPCMethod { + associatedtype Parameters + var method: String { get } + var params: Parameters { get } +} diff --git a/Sources/WalletConnectRelay/RelayRPC.swift b/Sources/WalletConnectRelay/RPC/RelayRPC.swift similarity index 61% rename from Sources/WalletConnectRelay/RelayRPC.swift rename to Sources/WalletConnectRelay/RPC/RelayRPC.swift index 45bbe6227..a8f3230a7 100644 --- a/Sources/WalletConnectRelay/RelayRPC.swift +++ b/Sources/WalletConnectRelay/RPC/RelayRPC.swift @@ -1,11 +1,5 @@ import JSONRPC -protocol RPCMethod { - associatedtype Parameters - var method: String { get } - var params: Parameters { get } -} - protocol RelayRPC: RPCMethod {} extension RelayRPC where Parameters: Codable { @@ -16,7 +10,10 @@ extension RelayRPC where Parameters: Codable { func wrapToIridium() -> PrefixDecorator { return PrefixDecorator(rpcMethod: self, prefix: "iridium") -// return PrefixDecorator.iridium(rpcMethod: self) + } + + func wrapToIRN() -> PrefixDecorator { + return PrefixDecorator(rpcMethod: self, prefix: "irn") } func asRPCRequest() -> RPCRequest { @@ -39,16 +36,3 @@ struct PrefixDecorator: RelayRPC where T: RelayRPC { rpcMethod.params } } - - -// TODO: Move -import Foundation - -struct WalletConnectRPCID: IdentifierGenerator { - - func next() -> RPCID { - let timestamp = Int64(Date().timeIntervalSince1970 * 1000) * 1000 - let random = Int64.random(in: 0..<1000) - return .right(Int(timestamp + random)) - } -} diff --git a/Sources/WalletConnectRelay/RPC/WalletConnectRPCID.swift b/Sources/WalletConnectRelay/RPC/WalletConnectRPCID.swift new file mode 100644 index 000000000..956b28868 --- /dev/null +++ b/Sources/WalletConnectRelay/RPC/WalletConnectRPCID.swift @@ -0,0 +1,11 @@ +import Foundation +import JSONRPC + +struct WalletConnectRPCID: IdentifierGenerator { + + func next() -> RPCID { + let timestamp = Int64(Date().timeIntervalSince1970 * 1000) * 1000 + let random = Int64.random(in: 0..<1000) + return .right(Int(timestamp + random)) + } +} From 156e32b16b4e021da2335107c076b4a09196bf23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Vants?= Date: Fri, 29 Jul 2022 14:46:35 -0300 Subject: [PATCH 10/14] Add unit tests for RPCHistory --- Package.swift | 4 +- Sources/Commons/Either.swift | 12 ++ Sources/JSONRPC/RPCRequest.swift | 4 +- Sources/JSONRPC/RPCResponse.swift | 4 + Sources/WalletConnectRelay/RelayClient.swift | 4 +- Sources/WalletConnectUtils/RPCHistory.swift | 4 +- Tests/TestingUtils/Mocks/RPC.swift | 12 ++ .../RPCHistoryTests.swift | 110 ++++++++++++++++++ 8 files changed, 146 insertions(+), 8 deletions(-) create mode 100644 Tests/TestingUtils/Mocks/RPC.swift create mode 100644 Tests/WalletConnectUtilsTests/RPCHistoryTests.swift diff --git a/Package.swift b/Package.swift index 4741a09f3..a6bd606d6 100644 --- a/Package.swift +++ b/Package.swift @@ -71,11 +71,11 @@ let package = Package( dependencies: ["WalletConnectKMS", "WalletConnectUtils", "TestingUtils"]), .target( name: "TestingUtils", - dependencies: ["WalletConnectUtils", "WalletConnectKMS"], + dependencies: ["WalletConnectUtils", "WalletConnectKMS", "JSONRPC"], path: "Tests/TestingUtils"), .testTarget( name: "WalletConnectUtilsTests", - dependencies: ["WalletConnectUtils"]), + dependencies: ["WalletConnectUtils", "JSONRPC", "TestingUtils"]), .testTarget( name: "JSONRPCTests", dependencies: ["JSONRPC", "TestingUtils"]), diff --git a/Sources/Commons/Either.swift b/Sources/Commons/Either.swift index 71e7b8dde..3c5f1fbdf 100644 --- a/Sources/Commons/Either.swift +++ b/Sources/Commons/Either.swift @@ -63,3 +63,15 @@ extension Either: Codable where L: Codable, R: Codable { } } } + +extension Either: CustomStringConvertible { + + public var description: String { + switch self { + case let .left(left): + return "\(left)" + case let .right(right): + return "\(right)" + } + } +} diff --git a/Sources/JSONRPC/RPCRequest.swift b/Sources/JSONRPC/RPCRequest.swift index 473550899..ad0bfed2b 100644 --- a/Sources/JSONRPC/RPCRequest.swift +++ b/Sources/JSONRPC/RPCRequest.swift @@ -72,11 +72,11 @@ public struct RPCRequest: Equatable { extension RPCRequest { - static func notification(method: String, params: C) -> RPCRequest where C: Codable { + public static func notification(method: String, params: C) -> RPCRequest where C: Codable { return RPCRequest(method: method, params: AnyCodable(params), id: nil) } - static func notification(method: String) -> RPCRequest { + public static func notification(method: String) -> RPCRequest { return RPCRequest(method: method, params: nil, id: nil) } diff --git a/Sources/JSONRPC/RPCResponse.swift b/Sources/JSONRPC/RPCResponse.swift index fec762286..d09055542 100644 --- a/Sources/JSONRPC/RPCResponse.swift +++ b/Sources/JSONRPC/RPCResponse.swift @@ -31,6 +31,10 @@ public struct RPCResponse: Equatable { self.init(id: matchingRequest.id, outcome: .success(AnyCodable(result))) } + public init(matchingRequest: RPCRequest, error: JSONRPCError) { + self.init(id: matchingRequest.id, outcome: .failure(error)) + } + public init(id: Int, result: C) where C: Codable { self.init(id: RPCID(id), outcome: .success(AnyCodable(result))) } diff --git a/Sources/WalletConnectRelay/RelayClient.swift b/Sources/WalletConnectRelay/RelayClient.swift index b5236567d..edc105070 100644 --- a/Sources/WalletConnectRelay/RelayClient.swift +++ b/Sources/WalletConnectRelay/RelayClient.swift @@ -198,7 +198,7 @@ public final class RelayClient { .wrapToIridium() .asRPCRequest() let message = try! request.asJSONEncodedString() - rpcHistory.delete(topic: topic) + rpcHistory.deleteAll(forTopic: topic) var cancellable: AnyCancellable? cancellable = requestAcknowledgePublisher .filter { $0 == request.id } @@ -225,7 +225,7 @@ public final class RelayClient { if let request = tryDecode(RPCRequest.self, from: payload) { if let params = try? request.params?.get(Subscription.Params.self) { do { - try rpcHistory.set(request, for: params.data.topic, emmitedBy: .remote) + try rpcHistory.set(request, forTopic: params.data.topic, emmitedBy: .remote) try acknowledgeRequest(request) onMessage?(params.data.topic, params.data.message) } catch { diff --git a/Sources/WalletConnectUtils/RPCHistory.swift b/Sources/WalletConnectUtils/RPCHistory.swift index 50ad886f5..86c5b8d05 100644 --- a/Sources/WalletConnectUtils/RPCHistory.swift +++ b/Sources/WalletConnectUtils/RPCHistory.swift @@ -32,7 +32,7 @@ public final class RPCHistory { try? storage.get(key: "\(recordId)") } - public func set(_ request: RPCRequest, for topic: String, emmitedBy origin: Record.Origin) throws { + public func set(_ request: RPCRequest, forTopic topic: String, emmitedBy origin: Record.Origin) throws { guard let id = request.id else { throw HistoryError.unidentifiedRequest } @@ -57,7 +57,7 @@ public final class RPCHistory { storage.set(record, forKey: "\(record.id)") } - public func delete(topic: String) { + public func deleteAll(forTopic topic: String) { storage.getAll().forEach { record in if record.topic == topic { storage.delete(forKey: "\(record.id)") diff --git a/Tests/TestingUtils/Mocks/RPC.swift b/Tests/TestingUtils/Mocks/RPC.swift new file mode 100644 index 000000000..964c472b1 --- /dev/null +++ b/Tests/TestingUtils/Mocks/RPC.swift @@ -0,0 +1,12 @@ +import JSONRPC + +public extension RPCRequest { + + static func stub() -> RPCRequest { + RPCRequest(method: "method", params: EmptyCodable()) + } + + static func stub(method: String, id: Int) -> RPCRequest { + RPCRequest(method: method, params: EmptyCodable(), id: id) + } +} diff --git a/Tests/WalletConnectUtilsTests/RPCHistoryTests.swift b/Tests/WalletConnectUtilsTests/RPCHistoryTests.swift new file mode 100644 index 000000000..10bfbc049 --- /dev/null +++ b/Tests/WalletConnectUtilsTests/RPCHistoryTests.swift @@ -0,0 +1,110 @@ +import XCTest +import JSONRPC +import TestingUtils +@testable import WalletConnectUtils + +final class RPCHistoryTests: XCTestCase { + + var sut: RPCHistory! + + override func setUp() { + let storage = CodableStore(defaults: RuntimeKeyValueStorage(), identifier: "") + sut = RPCHistory(keyValueStore: storage) + } + + override func tearDown() { + sut = nil + } + + // MARK: History Storage Tests + + func testRoundTrip() throws { + let request = RPCRequest.stub() + try sut.set(request, forTopic: String.randomTopic(), emmitedBy: .local) + let record = sut.get(recordId: request.id!) + XCTAssertNil(record?.response) + XCTAssertEqual(record?.request, request) + } + + func testResolveSuccessAndError() throws { + let requestA = RPCRequest.stub() + let requestB = RPCRequest.stub() + let responseA = RPCResponse(matchingRequest: requestA, result: true) + let responseB = RPCResponse(matchingRequest: requestB, error: .internalError) + try sut.set(requestA, forTopic: String.randomTopic(), emmitedBy: .remote) + try sut.set(requestB, forTopic: String.randomTopic(), emmitedBy: .local) + try sut.resolve(responseA) + try sut.resolve(responseB) + let recordA = sut.get(recordId: requestA.id!) + let recordB = sut.get(recordId: requestB.id!) + XCTAssertEqual(recordA?.response, responseA) + XCTAssertEqual(recordB?.response, responseB) + } + + func testDelete() throws { + let requests = (1...5).map { _ in RPCRequest.stub() } + let topic = String.randomTopic() + try requests.forEach { try sut.set($0, forTopic: topic, emmitedBy: .local) } + sut.deleteAll(forTopic: topic) + requests.forEach { + XCTAssertNil(sut.get(recordId: $0.id!)) + } + } + + // MARK: Error Cases Tests + + func testSetUnidentifiedRequest() { + let expectedError = RPCHistory.HistoryError.unidentifiedRequest + + let request = RPCRequest.notification(method: "notify") + XCTAssertThrowsError(try sut.set(request, forTopic: String.randomTopic(), emmitedBy: .local)) { error in + XCTAssertEqual(expectedError, error as? RPCHistory.HistoryError) + } + } + + func testSetDuplicateRequest() throws { + let expectedError = RPCHistory.HistoryError.requestDuplicateNotAllowed + + let id = Int.random() + let requestA = RPCRequest.stub(method: "method-1", id: id) + let requestB = RPCRequest.stub(method: "method-2", id: id) + let topic = String.randomTopic() + + try sut.set(requestA, forTopic: topic, emmitedBy: .local) + XCTAssertThrowsError(try sut.set(requestB, forTopic: topic, emmitedBy: .local)) { error in + XCTAssertEqual(expectedError, error as? RPCHistory.HistoryError) + } + } + + func testResolveResponseWithoutRequest() throws { + let expectedError = RPCHistory.HistoryError.requestMatchingResponseNotFound + + let response = RPCResponse(id: 0, result: true) + XCTAssertThrowsError(try sut.resolve(response)) { error in + XCTAssertEqual(expectedError, error as? RPCHistory.HistoryError) + } + } + + func testResolveUnidentifiedResponse() throws { + let expectedError = RPCHistory.HistoryError.unidentifiedResponse + + let response = RPCResponse(errorWithoutID: JSONRPCError.internalError) + XCTAssertThrowsError(try sut.resolve(response)) { error in + XCTAssertEqual(expectedError, error as? RPCHistory.HistoryError) + } + } + + func testResolveDuplicateResponse() throws { + let expectedError = RPCHistory.HistoryError.responseDuplicateNotAllowed + + let request = RPCRequest.stub() + let responseA = RPCResponse(matchingRequest: request, result: true) + let responseB = RPCResponse(matchingRequest: request, result: false) + + try sut.set(request, forTopic: String.randomTopic(), emmitedBy: .local) + try sut.resolve(responseA) + XCTAssertThrowsError(try sut.resolve(responseB)) { error in + XCTAssertEqual(expectedError, error as? RPCHistory.HistoryError) + } + } +} From ad826a66e3413f42065cf124641c2020f119d6db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Vants?= Date: Mon, 1 Aug 2022 10:50:13 -0300 Subject: [PATCH 11/14] Removed some references of iridium naming in relay --- Sources/WalletConnectRelay/RelayClient.swift | 6 +++--- Tests/RelayerTests/RelayClientTests.swift | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Sources/WalletConnectRelay/RelayClient.swift b/Sources/WalletConnectRelay/RelayClient.swift index edc105070..b97340adc 100644 --- a/Sources/WalletConnectRelay/RelayClient.swift +++ b/Sources/WalletConnectRelay/RelayClient.swift @@ -64,7 +64,7 @@ public final class RelayClient { /// Instantiates Relay Client /// - Parameters: - /// - relayHost: proxy server host that your application will use to connect to Iridium Network. If you register your project at `www.walletconnect.com` you can use `relay.walletconnect.com` + /// - relayHost: proxy server host that your application will use to connect to Relay Network. If you register your project at `www.walletconnect.com` you can use `relay.walletconnect.com` /// - projectId: an optional parameter used to access the public WalletConnect infrastructure. Go to `www.walletconnect.com` for info. /// - keyValueStorage: by default WalletConnect SDK will store sequences in UserDefaults /// - socketConnectionType: socket connection type @@ -131,7 +131,7 @@ public final class RelayClient { .wrapToIridium() .asRPCRequest() let message = try! request.asJSONEncodedString() - logger.debug("iridium: Publishing Payload on Topic: \(topic)") + logger.debug("Publishing Payload on Topic: \(topic)") var cancellable: AnyCancellable? cancellable = requestAcknowledgePublisher .filter { $0 == request.id } @@ -243,7 +243,7 @@ public final class RelayClient { subscriptionResponsePublisherSubject.send((response.id, subscriptionId)) } case .failure(let rpcError): - logger.error("Received error message from iridium network, code: \(rpcError.code), message: \(rpcError.message)") + logger.error("Received RPC error from relay network: \(rpcError)") } } else { logger.error("Unexpected response from network") diff --git a/Tests/RelayerTests/RelayClientTests.swift b/Tests/RelayerTests/RelayClientTests.swift index 5958d3194..d45bb61fe 100644 --- a/Tests/RelayerTests/RelayClientTests.swift +++ b/Tests/RelayerTests/RelayClientTests.swift @@ -22,7 +22,7 @@ final class RelayClientTests: XCTestCase { } func testNotifyOnSubscriptionRequest() { - let subscriptionExpectation = expectation(description: "notifies with encoded message on a iridium subscription event") + let expectation = expectation(description: "Relay must notify listener on a Subscription request") let topic = "0987" let message = "qwerty" let subscriptionId = "sub-id" @@ -32,7 +32,7 @@ final class RelayClientTests: XCTestCase { sut.onMessage = { subscriptionTopic, subscriptionMessage in XCTAssertEqual(subscriptionMessage, message) XCTAssertEqual(subscriptionTopic, topic) - subscriptionExpectation.fulfill() + expectation.fulfill() } dispatcher.onMessage?(try! request.asJSONEncodedString()) waitForExpectations(timeout: 0.001, handler: nil) @@ -51,10 +51,10 @@ final class RelayClientTests: XCTestCase { } func testPublishRequestAcknowledge() { - let acknowledgeExpectation = expectation(description: "completion with no error on iridium request acknowledge after publish") + let expectation = expectation(description: "Publish must callback on relay server acknowledgement") sut.publish(topic: "", payload: "{}", tag: 0) { error in XCTAssertNil(error) - acknowledgeExpectation.fulfill() + expectation.fulfill() } let request = dispatcher.getLastRequestSent() let response = RPCResponse(matchingRequest: request, result: true) @@ -63,12 +63,12 @@ final class RelayClientTests: XCTestCase { } func testUnsubscribeRequestAcknowledge() { - let acknowledgeExpectation = expectation(description: "completion with no error on iridium request acknowledge after unsubscribe") + let expectation = expectation(description: "Unsubscribe must callback on relay server acknowledgement") let topic = String.randomTopic() sut.subscriptions[topic] = "" sut.unsubscribe(topic: topic) { error in XCTAssertNil(error) - acknowledgeExpectation.fulfill() + expectation.fulfill() } let request = dispatcher.getLastRequestSent() let response = RPCResponse(matchingRequest: request, result: true) @@ -77,7 +77,7 @@ final class RelayClientTests: XCTestCase { } func testSubscriptionRequestDeliveredOnce() { - let expectation = expectation(description: "Request duplicate not delivered") + let expectation = expectation(description: "Duplicate Subscription requests must notify only the first time") let request = Subscription.init(id: "sub_id", topic: "topic", message: "message").asRPCRequest() sut.onMessage = { _, _ in expectation.fulfill() From a9fc882cc4f5c83afd89b8ada485a12f1b775b15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Vants?= Date: Mon, 1 Aug 2022 10:52:25 -0300 Subject: [PATCH 12/14] Renamed relayer errors --- Sources/WalletConnectRelay/RelayClient.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/WalletConnectRelay/RelayClient.swift b/Sources/WalletConnectRelay/RelayClient.swift index b97340adc..94815d407 100644 --- a/Sources/WalletConnectRelay/RelayClient.swift +++ b/Sources/WalletConnectRelay/RelayClient.swift @@ -11,7 +11,7 @@ public enum SocketConnectionStatus { public final class RelayClient { - enum RelayerError: Error { + enum Errors: Error { case subscriptionIdNotFound } @@ -189,7 +189,7 @@ public final class RelayClient { public func unsubscribe(topic: String, completion: @escaping ((Error?) -> Void)) { guard let subscriptionId = subscriptions[topic] else { - completion(RelayerError.subscriptionIdNotFound) + completion(Errors.subscriptionIdNotFound) return } logger.debug("Relay: Unsubscribing from topic: \(topic)") From ccd153f2a5d7ac4ba69d99890945921086cc695f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Vants?= Date: Mon, 1 Aug 2022 11:30:12 -0300 Subject: [PATCH 13/14] Removed unnecessary dependencies --- Package.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Package.swift b/Package.swift index a6bd606d6..14aa4884f 100644 --- a/Package.swift +++ b/Package.swift @@ -36,7 +36,7 @@ let package = Package( path: "Sources/Auth"), .target( name: "WalletConnectRelay", - dependencies: ["WalletConnectUtils", "WalletConnectKMS", "JSONRPC"], + dependencies: ["WalletConnectUtils", "WalletConnectKMS"], path: "Sources/WalletConnectRelay"), .target( name: "WalletConnectKMS", @@ -75,7 +75,7 @@ let package = Package( path: "Tests/TestingUtils"), .testTarget( name: "WalletConnectUtilsTests", - dependencies: ["WalletConnectUtils", "JSONRPC", "TestingUtils"]), + dependencies: ["WalletConnectUtils", "TestingUtils"]), .testTarget( name: "JSONRPCTests", dependencies: ["JSONRPC", "TestingUtils"]), From 9b7f82f7338bd25e32682edba27eca8df8dfb8ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Vants?= Date: Mon, 1 Aug 2022 11:58:24 -0300 Subject: [PATCH 14/14] Fixed broken keychain test --- .../WalletConnectKMSTests/KeychainStorageTests.swift | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Tests/WalletConnectKMSTests/KeychainStorageTests.swift b/Tests/WalletConnectKMSTests/KeychainStorageTests.swift index 19c57bdaf..422f23bcf 100644 --- a/Tests/WalletConnectKMSTests/KeychainStorageTests.swift +++ b/Tests/WalletConnectKMSTests/KeychainStorageTests.swift @@ -32,13 +32,12 @@ final class KeychainStorageTests: XCTestCase { XCTAssertNoThrow(try sut.add(privateKey, forKey: "id-2")) } - func testAddDuplicateItemError() { + func testAddDuplicateItem() throws { let privateKey = Curve25519.KeyAgreement.PrivateKey() - try? sut.add(privateKey, forKey: defaultIdentifier) - XCTAssertThrowsError(try sut.add(privateKey, forKey: defaultIdentifier)) { error in - guard let error = error as? KeychainError else { XCTFail(); return } - XCTAssertEqual(error.status, errSecDuplicateItem) - } + try sut.add(privateKey, forKey: defaultIdentifier) + let newPrivateKey = Curve25519.KeyAgreement.PrivateKey() + XCTAssertNoThrow(try sut.add(newPrivateKey, forKey: defaultIdentifier)) + XCTAssertEqual(try sut.read(key: defaultIdentifier), newPrivateKey) } func testAddUnknownFailure() {