From c24e74d900743340587f05ff504efed5c29f1a74 Mon Sep 17 00:00:00 2001 From: Maisa Date: Sat, 13 Aug 2022 23:35:25 -0300 Subject: [PATCH 01/10] Improve tests for WCPairing activate and update expiry functions --- .../WCPairingTests.swift | 49 +++++++++++++++++-- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/Tests/WalletConnectSignTests/WCPairingTests.swift b/Tests/WalletConnectSignTests/WCPairingTests.swift index 6826add6d..96d2aaf77 100644 --- a/Tests/WalletConnectSignTests/WCPairingTests.swift +++ b/Tests/WalletConnectSignTests/WCPairingTests.swift @@ -1,5 +1,5 @@ import XCTest -import WalletConnectPairing +@testable import WalletConnectPairing @testable import WalletConnectSign final class WCPairingTests: XCTestCase { @@ -35,18 +35,61 @@ final class WCPairingTests: XCTestCase { XCTAssertEqual(pairing.expiryDate, inactiveExpiry) } - func testUpdateExpiry() { + func testUpdateExpiryForTopic() { var pairing = WCPairing(topic: "") let activeExpiry = referenceDate.advanced(by: WCPairing.timeToLiveActive) try? pairing.updateExpiry() XCTAssertEqual(pairing.expiryDate, activeExpiry) } + + func testUpdateExpiryForUri() { + var pairing = WCPairing(uri: WalletConnectURI.stub()) + let activeExpiry = referenceDate.advanced(by: WCPairing.timeToLiveActive) + try? pairing.updateExpiry() + XCTAssertEqual(pairing.expiryDate, activeExpiry) + } - func testActivate() { + func testActivateTopic() { var pairing = WCPairing(topic: "") let activeExpiry = referenceDate.advanced(by: WCPairing.timeToLiveActive) + XCTAssertFalse(pairing.active) + pairing.activate() + XCTAssertTrue(pairing.active) + XCTAssertEqual(pairing.expiryDate, activeExpiry) + } + + func testActivateURI() { + var pairing = WCPairing(uri: WalletConnectURI.stub()) + let activeExpiry = referenceDate.advanced(by: WCPairing.timeToLiveActive) + XCTAssertFalse(pairing.active) pairing.activate() XCTAssertTrue(pairing.active) XCTAssertEqual(pairing.expiryDate, activeExpiry) } + + func testUpdateExpiry_WhenValueIsGreaterThanMax_ShouldThrowInvalidUpdateExpiryValue() { + var pairing = WCPairing(topic: "", relay: .stub(), peerMetadata: .stub(), expiryDate: referenceDate) + XCTAssertThrowsError(try pairing.updateExpiry(40 * .day)) { error in + XCTAssertEqual(error as! WCPairing.Errors, WCPairing.Errors.invalidUpdateExpiryValue) + } + } + + func testUpdateExpiry_WhenNewExpiryDateIsLessThanExpiryDate_ShouldThrowInvalidUpdateExpiryValue() { + let expiryDate = referenceDate.advanced(by: 10 * .minute) + var pairing = WCPairing(topic: "", relay: .stub(), peerMetadata: .stub(), expiryDate: expiryDate) + XCTAssertThrowsError(try pairing.updateExpiry(40 * .day)) { error in + XCTAssertEqual(error as! WCPairing.Errors, WCPairing.Errors.invalidUpdateExpiryValue) + } + } + + func testActivate_WhenUpdateExpiryFails_ShouldActivateAndThrowError() { + let expiryDate = referenceDate.advanced(by: 10 * .minute) + var pairing = WCPairing(topic: "", relay: .stub(), peerMetadata: .stub(), expiryDate: expiryDate) + XCTAssertFalse(pairing.active) + pairing.activate() + XCTAssertThrowsError(try pairing.updateExpiry(10 * .minute)) { error in + XCTAssertEqual(error as! WCPairing.Errors, WCPairing.Errors.invalidUpdateExpiryValue) + } + XCTAssertTrue(pairing.active) + } } From 356c62fdce4639e4815ee14417d045318b43a2a4 Mon Sep 17 00:00:00 2001 From: Maisa Date: Mon, 15 Aug 2022 22:28:34 -0300 Subject: [PATCH 02/10] Update with RC comments --- .../WCPairingTests.swift | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/Tests/WalletConnectSignTests/WCPairingTests.swift b/Tests/WalletConnectSignTests/WCPairingTests.swift index 96d2aaf77..39854b5ba 100644 --- a/Tests/WalletConnectSignTests/WCPairingTests.swift +++ b/Tests/WalletConnectSignTests/WCPairingTests.swift @@ -75,21 +75,27 @@ final class WCPairingTests: XCTestCase { } func testUpdateExpiry_WhenNewExpiryDateIsLessThanExpiryDate_ShouldThrowInvalidUpdateExpiryValue() { - let expiryDate = referenceDate.advanced(by: 10 * .minute) + let expiryDate = referenceDate.advanced(by: 40 * .day) var pairing = WCPairing(topic: "", relay: .stub(), peerMetadata: .stub(), expiryDate: expiryDate) - XCTAssertThrowsError(try pairing.updateExpiry(40 * .day)) { error in + XCTAssertThrowsError(try pairing.updateExpiry(10 * .minute)) { error in XCTAssertEqual(error as! WCPairing.Errors, WCPairing.Errors.invalidUpdateExpiryValue) } } - func testActivate_WhenUpdateExpiryFails_ShouldActivateAndThrowError() { - let expiryDate = referenceDate.advanced(by: 10 * .minute) + func testActivate_WhenCanUpdateExpiry_ShouldActivateAndUpdateExpiryIn30Days() { + var pairing = WCPairing(topic: "", relay: .stub(), peerMetadata: .stub(), expiryDate: referenceDate) + XCTAssertFalse(pairing.active) + pairing.activate() + XCTAssertTrue(pairing.active) + XCTAssertEqual(referenceDate.advanced(by: 30 * .day), pairing.expiryDate) + } + + func testActivate_WhenUpdateExpiryIsInvalid_ShouldActivateAndNotUpdateExpiry() { + let expiryDate = referenceDate.advanced(by: 40 * .day) var pairing = WCPairing(topic: "", relay: .stub(), peerMetadata: .stub(), expiryDate: expiryDate) XCTAssertFalse(pairing.active) pairing.activate() - XCTAssertThrowsError(try pairing.updateExpiry(10 * .minute)) { error in - XCTAssertEqual(error as! WCPairing.Errors, WCPairing.Errors.invalidUpdateExpiryValue) - } XCTAssertTrue(pairing.active) + XCTAssertEqual(expiryDate, pairing.expiryDate) } } From f36d57165679e3cac5bdfa966fccc21363de871a Mon Sep 17 00:00:00 2001 From: Maisa Date: Thu, 18 Aug 2022 01:06:53 -0300 Subject: [PATCH 03/10] Update tests naming --- Tests/WalletConnectSignTests/WCPairingTests.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Tests/WalletConnectSignTests/WCPairingTests.swift b/Tests/WalletConnectSignTests/WCPairingTests.swift index 39854b5ba..ca6872e24 100644 --- a/Tests/WalletConnectSignTests/WCPairingTests.swift +++ b/Tests/WalletConnectSignTests/WCPairingTests.swift @@ -67,14 +67,14 @@ final class WCPairingTests: XCTestCase { XCTAssertEqual(pairing.expiryDate, activeExpiry) } - func testUpdateExpiry_WhenValueIsGreaterThanMax_ShouldThrowInvalidUpdateExpiryValue() { + func testUpdateExpiryWhenValueIsGreaterThanMax() { var pairing = WCPairing(topic: "", relay: .stub(), peerMetadata: .stub(), expiryDate: referenceDate) XCTAssertThrowsError(try pairing.updateExpiry(40 * .day)) { error in XCTAssertEqual(error as! WCPairing.Errors, WCPairing.Errors.invalidUpdateExpiryValue) } } - func testUpdateExpiry_WhenNewExpiryDateIsLessThanExpiryDate_ShouldThrowInvalidUpdateExpiryValue() { + func testUpdateExpiryWhenNewExpiryDateIsLessThanExpiryDate() { let expiryDate = referenceDate.advanced(by: 40 * .day) var pairing = WCPairing(topic: "", relay: .stub(), peerMetadata: .stub(), expiryDate: expiryDate) XCTAssertThrowsError(try pairing.updateExpiry(10 * .minute)) { error in @@ -82,7 +82,7 @@ final class WCPairingTests: XCTestCase { } } - func testActivate_WhenCanUpdateExpiry_ShouldActivateAndUpdateExpiryIn30Days() { + func testActivateWhenCanUpdateExpiry() { var pairing = WCPairing(topic: "", relay: .stub(), peerMetadata: .stub(), expiryDate: referenceDate) XCTAssertFalse(pairing.active) pairing.activate() @@ -90,7 +90,7 @@ final class WCPairingTests: XCTestCase { XCTAssertEqual(referenceDate.advanced(by: 30 * .day), pairing.expiryDate) } - func testActivate_WhenUpdateExpiryIsInvalid_ShouldActivateAndNotUpdateExpiry() { + func testActivateWhenUpdateExpiryIsInvalid() { let expiryDate = referenceDate.advanced(by: 40 * .day) var pairing = WCPairing(topic: "", relay: .stub(), peerMetadata: .stub(), expiryDate: expiryDate) XCTAssertFalse(pairing.active) From dab13243a07d9817cbf31cd868c07c97439459b8 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 18 Aug 2022 02:28:47 +0300 Subject: [PATCH 04/10] Subscription errors --- Sources/Auth/AuthClient.swift | 4 +- .../Services/App/AppRespondSubscriber.swift | 37 ++++++++----------- .../Wallet/WalletRequestSubscriber.swift | 30 ++++++--------- Sources/Auth/Types/ErrorCode.swift | 8 ++-- .../AuthTests/AuthRequstSubscriberTests.swift | 3 +- 5 files changed, 36 insertions(+), 46 deletions(-) diff --git a/Sources/Auth/AuthClient.swift b/Sources/Auth/AuthClient.swift index 27ff070d6..4a6acd91d 100644 --- a/Sources/Auth/AuthClient.swift +++ b/Sources/Auth/AuthClient.swift @@ -10,8 +10,8 @@ public class AuthClient { case unknownWalletAddress case noPairingMatchingTopic } - private var authRequestPublisherSubject = PassthroughSubject<(id: RPCID, message: String), Never>() - public var authRequestPublisher: AnyPublisher<(id: RPCID, message: String), Never> { + private var authRequestPublisherSubject = PassthroughSubject<(id: RPCID, result: Result), Never>() + public var authRequestPublisher: AnyPublisher<(id: RPCID, result: Result), Never> { authRequestPublisherSubject.eraseToAnyPublisher() } diff --git a/Sources/Auth/Services/App/AppRespondSubscriber.swift b/Sources/Auth/Services/App/AppRespondSubscriber.swift index cba492bf3..ab5f38f2f 100644 --- a/Sources/Auth/Services/App/AppRespondSubscriber.swift +++ b/Sources/Auth/Services/App/AppRespondSubscriber.swift @@ -10,6 +10,7 @@ class AppRespondSubscriber { private let signatureVerifier: MessageSignatureVerifying private let messageFormatter: SIWEMessageFormatting private var publishers = [AnyCancellable]() + var onResponse: ((_ id: RPCID, _ result: Result) -> Void)? init(networkingInteractor: NetworkInteracting, @@ -26,7 +27,6 @@ class AppRespondSubscriber { } private func subscribeForResponse() { - // TODO - handle error response networkingInteractor.responsePublisher.sink { [unowned self] subscriptionPayload in guard let requestId = subscriptionPayload.response.id, @@ -36,30 +36,23 @@ class AppRespondSubscriber { networkingInteractor.unsubscribe(topic: subscriptionPayload.topic) - do { - guard let cacao = try subscriptionPayload.response.result?.get(Cacao.self) else { - return logger.debug("Malformed auth response params") - } + guard + let cacao = try? subscriptionPayload.response.result?.get(Cacao.self), + let address = try? DIDPKH(iss: cacao.payload.iss).account.address, + let message = try? messageFormatter.formatMessage(from: cacao.payload) + else { self.onResponse?(requestId, .failure(.malformedResponseParams)); return } + + guard let requestPayload = try? requestParams.get(AuthRequestParams.self) + else { self.onResponse?(requestId, .failure(.malformedRequestParams)); return } + + guard messageFormatter.formatMessage(from: requestPayload.payloadParams, address: address) == message + else { self.onResponse?(requestId, .failure(.messageCompromised)); return } - let requestPayload = try requestParams.get(AuthRequestParams.self) - let address = try DIDPKH(iss: cacao.payload.iss).account.address - let message = try messageFormatter.formatMessage(from: cacao.payload) - let originalMessage = messageFormatter.formatMessage(from: requestPayload.payloadParams, address: address) + guard let _ = try? signatureVerifier.verify(signature: cacao.signature.s, message: message, address: address) + else { self.onResponse?(requestId, .failure(.messageVerificationFailed)); return } - guard originalMessage == message else { - return logger.debug("Original message compromised") - } + onResponse?(requestId, .success(cacao)) - try signatureVerifier.verify( - signature: cacao.signature.s, - message: message, - address: address - ) - logger.debug("Received response with valid signature") - onResponse?(requestId, .success(cacao)) - } catch { - logger.debug("Received response with invalid signature") - } }.store(in: &publishers) } } diff --git a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift index aa5cdde47..5eaf3c262 100644 --- a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift +++ b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift @@ -9,7 +9,7 @@ class WalletRequestSubscriber { private let address: String? private var publishers = [AnyCancellable]() private let messageFormatter: SIWEMessageFormatting - var onRequest: ((_ id: RPCID, _ message: String) -> Void)? + var onRequest: ((_ id: RPCID, _ result: Result) -> Void)? init(networkingInteractor: NetworkInteracting, logger: ConsoleLogging, @@ -25,24 +25,18 @@ class WalletRequestSubscriber { private func subscribeForRequest() { guard let address = address else {return} networkingInteractor.requestPublisher.sink { [unowned self] subscriptionPayload in + logger.debug("WalletRequestSubscriber: Received request") - guard - let requestId = subscriptionPayload.request.id, - subscriptionPayload.request.method == "wc_authRequest" else { return } - - do { - guard let authRequestParams = try subscriptionPayload.request.params?.get(AuthRequestParams.self) else { return logger.debug("Malformed auth request params") - } - - let message = messageFormatter.formatMessage( - from: authRequestParams.payloadParams, - address: address - ) - - onRequest?(requestId, message) - } catch { - logger.debug(error) - } + + guard let requestId = subscriptionPayload.request.id, subscriptionPayload.request.method == "wc_authRequest" + else { return } + + guard let authRequestParams = try? subscriptionPayload.request.params?.get(AuthRequestParams.self) + else { self.onRequest?(requestId, .failure(.malformedRequestParams)); return } + + let message = messageFormatter.formatMessage(from: authRequestParams.payloadParams, address: address) + + onRequest?(requestId, .success(message)) }.store(in: &publishers) } diff --git a/Sources/Auth/Types/ErrorCode.swift b/Sources/Auth/Types/ErrorCode.swift index e56498149..f457b494f 100644 --- a/Sources/Auth/Types/ErrorCode.swift +++ b/Sources/Auth/Types/ErrorCode.swift @@ -1,7 +1,9 @@ import Foundation -public struct ErrorCode: Codable, Equatable, Error { - let code: Int - let message: String +public enum ErrorCode: Codable, Equatable, Error { + case malformedResponseParams + case malformedRequestParams + case messageCompromised + case messageVerificationFailed } diff --git a/Tests/AuthTests/AuthRequstSubscriberTests.swift b/Tests/AuthTests/AuthRequstSubscriberTests.swift index dbf7bba20..1ecb0bc2b 100644 --- a/Tests/AuthTests/AuthRequstSubscriberTests.swift +++ b/Tests/AuthTests/AuthRequstSubscriberTests.swift @@ -27,7 +27,8 @@ class AuthRequstSubscriberTests: XCTestCase { messageFormatter.formattedMessage = expectedMessage var messageId: RPCID! var message: String! - sut.onRequest = { id, formattedMessage in + sut.onRequest = { id, result in + guard case .success(let formattedMessage) = result else { return XCTFail() } messageId = id message = formattedMessage messageExpectation.fulfill() From 914442227a9d64f30aea8c2740a1e9e0b88a58eb Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 18 Aug 2022 21:01:47 +0300 Subject: [PATCH 05/10] Message codes + respondError --- Sources/Auth/AuthClient.swift | 4 +-- .../Common/NetworkingInteractor.swift | 19 +++++++--- .../Wallet/WalletRespondService.swift | 22 +++++------- Sources/Auth/Types/ErrorCode.swift | 35 ++++++++++++++++++- Sources/JSONRPC/RPCResponse.swift | 8 ++--- .../Mocks/NetworkingInteractorMock.swift | 4 +++ 6 files changed, 65 insertions(+), 27 deletions(-) diff --git a/Sources/Auth/AuthClient.swift b/Sources/Auth/AuthClient.swift index 4a6acd91d..d903fee98 100644 --- a/Sources/Auth/AuthClient.swift +++ b/Sources/Auth/AuthClient.swift @@ -87,9 +87,9 @@ public class AuthClient { try await appRequestService.request(params: params, topic: topic) } - public func respond(_ result: Result) async throws { + public func respond(_ params: RespondParams) async throws { guard let account = account else { throw Errors.unknownWalletAddress } - try await walletRespondService.respond(result: result, account: account) + try await walletRespondService.respond(params: params, account: account) } public func getPendingRequests() throws -> [AuthRequest] { diff --git a/Sources/Auth/Services/Common/NetworkingInteractor.swift b/Sources/Auth/Services/Common/NetworkingInteractor.swift index 62d152ccb..24419b295 100644 --- a/Sources/Auth/Services/Common/NetworkingInteractor.swift +++ b/Sources/Auth/Services/Common/NetworkingInteractor.swift @@ -13,6 +13,7 @@ protocol NetworkInteracting { 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 respondError(topic: String, requestId: RPCID, tag: Int, reason: Reason, envelopeType: Envelope.EnvelopeType) async throws } extension NetworkInteracting { @@ -27,14 +28,17 @@ class NetworkingInteractor: NetworkInteracting { private let serializer: Serializing private let rpcHistory: RPCHistory private let logger: ConsoleLogging + + private let requestPublisherSubject = PassthroughSubject() var requestPublisher: AnyPublisher { requestPublisherSubject.eraseToAnyPublisher() } - private let requestPublisherSubject = PassthroughSubject() + + private let responsePublisherSubject = PassthroughSubject() var responsePublisher: AnyPublisher { responsePublisherSubject.eraseToAnyPublisher() } - private let responsePublisherSubject = PassthroughSubject() + var socketConnectionStatusPublisher: AnyPublisher init(relayClient: RelayClient, @@ -99,11 +103,18 @@ class NetworkingInteractor: NetworkInteracting { try await relayClient.publish(topic: topic, payload: message, tag: tag) } + func respondError(topic: String, requestId: RPCID, tag: Int, reason: Reason, envelopeType: Envelope.EnvelopeType) async throws { + let error = JSONRPCError(code: reason.code, message: reason.message) + let response = RPCResponse(id: requestId, error: error) + let message = try! serializer.serialize(topic: topic, encodable: response, envelopeType: envelopeType) + try await relayClient.publish(topic: topic, payload: message, tag: tag) + } + 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 deserializedJsonRpcResponse: RPCResponse = serializer.tryDeserialize(topic: topic, encodedEnvelope: encodedEnvelope) { - handleResponse(response: deserializedJsonRpcResponse) + } 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") } diff --git a/Sources/Auth/Services/Wallet/WalletRespondService.swift b/Sources/Auth/Services/Wallet/WalletRespondService.swift index 6b5030df9..c752a5278 100644 --- a/Sources/Auth/Services/Wallet/WalletRespondService.swift +++ b/Sources/Auth/Services/Wallet/WalletRespondService.swift @@ -22,19 +22,13 @@ actor WalletRespondService { self.kms = kms self.rpcHistory = rpcHistory } - - func respond(result: Result, account: Account) async throws { - switch result { - case .success(let params): - try await respond(respondParams: params, account: account) - case .failure(let error): - fatalError("TODO respond with error") - } - } - private func respond(respondParams: RespondParams, account: Account) async throws { - guard let request = rpcHistory.get(recordId: respondParams.id)?.request else { throw Errors.recordForIdNotFound } - guard let authRequestParams = try? request.params?.get(AuthRequestParams.self) else { throw Errors.malformedAuthRequestParams } + func respond(params: RespondParams, account: Account) async throws { + guard let request = rpcHistory.get(recordId: RPCID(params.id))?.request + else { throw ErrorCode.malformedRequestParams } + + guard let authRequestParams = try request.params?.get(AuthRequestParams.self) + else { throw ErrorCode.malformedRequestParams } let peerPubKey = try AgreementPublicKey(hex: authRequestParams.requester.publicKey) let responseTopic = peerPubKey.rawRepresentation.sha256().toHexString() @@ -43,9 +37,9 @@ actor WalletRespondService { try kms.setAgreementSecret(agreementKeys, topic: responseTopic) let didpkh = DIDPKH(account: account) - let cacao = CacaoFormatter().format(authRequestParams, respondParams.signature, didpkh) + let cacao = CacaoFormatter().format(authRequestParams, params.signature, didpkh) let response = RPCResponse(id: request.id!, result: cacao) - try await networkingInteractor.respond(topic: responseTopic, response: response, tag: AuthResponseParams.tag, envelopeType: .type1(pubKey: selfPubKey.rawRepresentation)) + try await networkingInteractor.respond(topic: params.topic, response: response, tag: AuthResponseParams.tag, envelopeType: .type1(pubKey: selfPubKey.rawRepresentation)) } } diff --git a/Sources/Auth/Types/ErrorCode.swift b/Sources/Auth/Types/ErrorCode.swift index f457b494f..052f2719d 100644 --- a/Sources/Auth/Types/ErrorCode.swift +++ b/Sources/Auth/Types/ErrorCode.swift @@ -1,9 +1,42 @@ - import Foundation +public protocol Reason { + var code: Int { get } + var message: String { get } +} + public enum ErrorCode: Codable, Equatable, Error { case malformedResponseParams case malformedRequestParams case messageCompromised case messageVerificationFailed } + +extension ErrorCode: Reason { + + public var code: Int { + switch self { + case .malformedResponseParams: + return 1001 + case .malformedRequestParams: + return 1002 + case .messageCompromised: + return 1003 + case .messageVerificationFailed: + return 1004 + } + } + + public var message: String { + switch self { + case .malformedResponseParams: + return "Response params malformed" + case .malformedRequestParams: + return "Request params malformed" + case .messageCompromised: + return "Original message compromised" + case .messageVerificationFailed: + return "Message verification failed" + } + } +} diff --git a/Sources/JSONRPC/RPCResponse.swift b/Sources/JSONRPC/RPCResponse.swift index e0e60f06f..fb9c819db 100644 --- a/Sources/JSONRPC/RPCResponse.swift +++ b/Sources/JSONRPC/RPCResponse.swift @@ -47,12 +47,8 @@ public struct RPCResponse: Equatable { self.init(id: id, outcome: .success(AnyCodable(result))) } - public init(id: Int64, error: JSONRPCError) { - self.init(id: RPCID(id), outcome: .failure(error)) - } - - public init(id: String, error: JSONRPCError) { - self.init(id: RPCID(id), outcome: .failure(error)) + public init(id: RPCID?, error: JSONRPCError) { + self.init(id: id, outcome: .failure(error)) } public init(id: Int64, errorCode: Int, message: String, associatedData: AnyCodable? = nil) { diff --git a/Tests/AuthTests/Mocks/NetworkingInteractorMock.swift b/Tests/AuthTests/Mocks/NetworkingInteractorMock.swift index 838b47ea7..a499ea4af 100644 --- a/Tests/AuthTests/Mocks/NetworkingInteractorMock.swift +++ b/Tests/AuthTests/Mocks/NetworkingInteractorMock.swift @@ -32,6 +32,10 @@ struct NetworkingInteractorMock: NetworkInteracting { } + func respondError(topic: String, requestId: RPCID, tag: Int, reason: Reason, envelopeType: Envelope.EnvelopeType) async throws { + + } + func requestNetworkAck(_ request: RPCRequest, topic: String, tag: Int) async throws { } From 4429f9337bc68746d13bcad24e698dd30e85c0ac Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Fri, 19 Aug 2022 13:35:05 +0300 Subject: [PATCH 06/10] External error handling --- Sources/Auth/AuthClient.swift | 12 ++--- Sources/Auth/AuthClientFactory.swift | 2 +- .../Services/App/AppRespondSubscriber.swift | 2 +- .../Wallet/WalletRequestSubscriber.swift | 33 +++++++++--- .../Wallet/WalletRespondService.swift | 54 ++++++++++++++----- Sources/Auth/Types/Error/ExternalError.swift | 22 ++++++++ .../InternalError.swift} | 9 +--- Sources/Auth/Types/Error/Reason.swift | 7 +++ 8 files changed, 107 insertions(+), 34 deletions(-) create mode 100644 Sources/Auth/Types/Error/ExternalError.swift rename Sources/Auth/Types/{ErrorCode.swift => Error/InternalError.swift} (84%) create mode 100644 Sources/Auth/Types/Error/Reason.swift diff --git a/Sources/Auth/AuthClient.swift b/Sources/Auth/AuthClient.swift index d903fee98..74cd7e65e 100644 --- a/Sources/Auth/AuthClient.swift +++ b/Sources/Auth/AuthClient.swift @@ -10,13 +10,13 @@ public class AuthClient { case unknownWalletAddress case noPairingMatchingTopic } - private var authRequestPublisherSubject = PassthroughSubject<(id: RPCID, result: Result), Never>() - public var authRequestPublisher: AnyPublisher<(id: RPCID, result: Result), Never> { + private var authRequestPublisherSubject = PassthroughSubject<(id: RPCID, message: String), Never>() + public var authRequestPublisher: AnyPublisher<(id: RPCID, message: String), Never> { authRequestPublisherSubject.eraseToAnyPublisher() } - private var authResponsePublisherSubject = PassthroughSubject<(id: RPCID, result: Result), Never>() - public var authResponsePublisher: AnyPublisher<(id: RPCID, result: Result), Never> { + private var authResponsePublisherSubject = PassthroughSubject<(id: RPCID, result: Result), Never>() + public var authResponsePublisher: AnyPublisher<(id: RPCID, result: Result), Never> { authResponsePublisherSubject.eraseToAnyPublisher() } @@ -87,9 +87,9 @@ public class AuthClient { try await appRequestService.request(params: params, topic: topic) } - public func respond(_ params: RespondParams) async throws { + public func respond(requestId: RPCID, result: Result) async throws { guard let account = account else { throw Errors.unknownWalletAddress } - try await walletRespondService.respond(params: params, account: account) + try await walletRespondService.respond(requestId: requestId, result: result, account: account) } public func getPendingRequests() throws -> [AuthRequest] { diff --git a/Sources/Auth/AuthClientFactory.swift b/Sources/Auth/AuthClientFactory.swift index cf75c3ce1..6cc3f3a35 100644 --- a/Sources/Auth/AuthClientFactory.swift +++ b/Sources/Auth/AuthClientFactory.swift @@ -26,7 +26,7 @@ public struct AuthClientFactory { let messageSigner = MessageSigner(signer: Signer()) let appRespondSubscriber = AppRespondSubscriber(networkingInteractor: networkingInteractor, logger: logger, rpcHistory: history, signatureVerifier: messageSigner, messageFormatter: messageFormatter) let walletPairService = WalletPairService(networkingInteractor: networkingInteractor, kms: kms, pairingStorage: pairingStore) - let walletRequestSubscriber = WalletRequestSubscriber(networkingInteractor: networkingInteractor, logger: logger, messageFormatter: messageFormatter, address: account?.address) + let walletRequestSubscriber = WalletRequestSubscriber(networkingInteractor: networkingInteractor, logger: logger, rpcHistory: history, kms: kms, messageFormatter: messageFormatter, address: account?.address) let walletRespondService = WalletRespondService(networkingInteractor: networkingInteractor, logger: logger, kms: kms, rpcHistory: history) let pendingRequestsProvider = PendingRequestsProvider(rpcHistory: history) let cleanupService = CleanupService(pairingStore: pairingStore, kms: kms) diff --git a/Sources/Auth/Services/App/AppRespondSubscriber.swift b/Sources/Auth/Services/App/AppRespondSubscriber.swift index ab5f38f2f..809962cfc 100644 --- a/Sources/Auth/Services/App/AppRespondSubscriber.swift +++ b/Sources/Auth/Services/App/AppRespondSubscriber.swift @@ -11,7 +11,7 @@ class AppRespondSubscriber { private let messageFormatter: SIWEMessageFormatting private var publishers = [AnyCancellable]() - var onResponse: ((_ id: RPCID, _ result: Result) -> Void)? + var onResponse: ((_ id: RPCID, _ result: Result) -> Void)? init(networkingInteractor: NetworkInteracting, logger: ConsoleLogging, diff --git a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift index 5eaf3c262..4d872742a 100644 --- a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift +++ b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift @@ -2,42 +2,61 @@ import Combine import Foundation import WalletConnectUtils import JSONRPC +import WalletConnectKMS class WalletRequestSubscriber { private let networkingInteractor: NetworkInteracting private let logger: ConsoleLogging + private let rpcHistory: RPCHistory + private let kms: KeyManagementService private let address: String? private var publishers = [AnyCancellable]() private let messageFormatter: SIWEMessageFormatting - var onRequest: ((_ id: RPCID, _ result: Result) -> Void)? + var onRequest: ((_ id: RPCID, _ message: String) -> Void)? init(networkingInteractor: NetworkInteracting, logger: ConsoleLogging, + rpcHistory: RPCHistory, + kms: KeyManagementService, messageFormatter: SIWEMessageFormatting, address: String?) { self.networkingInteractor = networkingInteractor self.logger = logger + self.rpcHistory = rpcHistory + self.kms = kms self.address = address self.messageFormatter = messageFormatter subscribeForRequest() } private func subscribeForRequest() { - guard let address = address else {return} - networkingInteractor.requestPublisher.sink { [unowned self] subscriptionPayload in + networkingInteractor.requestPublisher.sink { [unowned self] payload in logger.debug("WalletRequestSubscriber: Received request") - guard let requestId = subscriptionPayload.request.id, subscriptionPayload.request.method == "wc_authRequest" + guard let address = address else { return } + + guard let requestId = payload.request.id, payload.request.method == "wc_authRequest" else { return } - guard let authRequestParams = try? subscriptionPayload.request.params?.get(AuthRequestParams.self) - else { self.onRequest?(requestId, .failure(.malformedRequestParams)); return } + guard let authRequestParams = try? payload.request.params?.get(AuthRequestParams.self) + else { return respondError(.malformedRequestParams, topic: payload.topic, requestId: requestId) } let message = messageFormatter.formatMessage(from: authRequestParams.payloadParams, address: address) - onRequest?(requestId, .success(message)) + onRequest?(requestId, message) }.store(in: &publishers) } + private func respondError(_ error: InternalError, topic: String, requestId: RPCID) { + guard let pubKey = kms.getAgreementSecret(for: topic)?.publicKey + else { return logger.error("Agreement key for topic \(topic) not found") } + + let tag = AuthResponseParams.tag + let envelopeType = Envelope.EnvelopeType.type1(pubKey: pubKey.rawRepresentation) + + Task(priority: .high) { + try await networkingInteractor.respondError(topic: topic, requestId: requestId, tag: tag, reason: error, envelopeType: envelopeType) + } + } } diff --git a/Sources/Auth/Services/Wallet/WalletRespondService.swift b/Sources/Auth/Services/Wallet/WalletRespondService.swift index c752a5278..93f64318b 100644 --- a/Sources/Auth/Services/Wallet/WalletRespondService.swift +++ b/Sources/Auth/Services/Wallet/WalletRespondService.swift @@ -23,23 +23,53 @@ actor WalletRespondService { self.rpcHistory = rpcHistory } - func respond(params: RespondParams, account: Account) async throws { - guard let request = rpcHistory.get(recordId: RPCID(params.id))?.request - else { throw ErrorCode.malformedRequestParams } + func respond(requestId: RPCID, result: Result, account: Account) async throws { + switch result { + case .success(let params): + try await respond(requestId: requestId, params: params, account: account) + case .failure(let error): + try await respond(error: error, requestId: requestId) + } + } - guard let authRequestParams = try request.params?.get(AuthRequestParams.self) - else { throw ErrorCode.malformedRequestParams } + private func respond(requestId: RPCID, params: RespondParams, account: Account) async throws { + let authRequestParams = try getAuthRequestParams(requestId: requestId) + let (topic, keys) = try generateAgreementKeys(requestParams: authRequestParams) - let peerPubKey = try AgreementPublicKey(hex: authRequestParams.requester.publicKey) - let responseTopic = peerPubKey.rawRepresentation.sha256().toHexString() - let selfPubKey = try kms.createX25519KeyPair() - let agreementKeys = try kms.performKeyAgreement(selfPublicKey: selfPubKey, peerPublicKey: peerPubKey.hexRepresentation) - try kms.setAgreementSecret(agreementKeys, topic: responseTopic) + try kms.setAgreementSecret(keys, topic: topic) let didpkh = DIDPKH(account: account) let cacao = CacaoFormatter().format(authRequestParams, params.signature, didpkh) - let response = RPCResponse(id: request.id!, result: cacao) + let response = RPCResponse(id: requestId, result: cacao) + try await networkingInteractor.respond(topic: params.topic, response: response, tag: AuthResponseParams.tag, envelopeType: .type1(pubKey: keys.publicKey.rawRepresentation)) + } + + private func respond(error: ExternalError, requestId: RPCID) async throws { + let authRequestParams = try getAuthRequestParams(requestId: requestId) + let (topic, keys) = try generateAgreementKeys(requestParams: authRequestParams) + + try kms.setAgreementSecret(keys, topic: topic) + + let tag = AuthResponseParams.tag + let envelopeType = Envelope.EnvelopeType.type1(pubKey: keys.publicKey.rawRepresentation) + try await networkingInteractor.respondError(topic: topic, requestId: requestId, tag: tag, reason: error, envelopeType: envelopeType) + } + + private func getAuthRequestParams(requestId: RPCID) throws -> AuthRequestParams { + guard let request = rpcHistory.get(recordId: requestId)?.request + else { throw Errors.recordForIdNotFound } + + guard let authRequestParams = try request.params?.get(AuthRequestParams.self) + else { throw Errors.malformedAuthRequestParams } - try await networkingInteractor.respond(topic: params.topic, response: response, tag: AuthResponseParams.tag, envelopeType: .type1(pubKey: selfPubKey.rawRepresentation)) + return authRequestParams + } + + private func generateAgreementKeys(requestParams: AuthRequestParams) throws -> (topic: String, keys: AgreementKeys) { + let peerPubKey = requestParams.requester.publicKey + let topic = peerPubKey.rawRepresentation.sha256().toHexString() + let selfPubKey = try kms.createX25519KeyPair() + let keys = try kms.performKeyAgreement(selfPublicKey: selfPubKey, peerPublicKey: peerPubKey) + return (topic, keys) } } diff --git a/Sources/Auth/Types/Error/ExternalError.swift b/Sources/Auth/Types/Error/ExternalError.swift new file mode 100644 index 000000000..596369b0f --- /dev/null +++ b/Sources/Auth/Types/Error/ExternalError.swift @@ -0,0 +1,22 @@ +import Foundation + +public enum ExternalError: Codable, Equatable, Error { + case userRejeted +} + +extension ExternalError: Reason { + + public var code: Int { + switch self { + case .userRejeted: + return 2001 + } + } + + public var message: String { + switch self { + case .userRejeted: + return "Auth request rejected by user" + } + } +} diff --git a/Sources/Auth/Types/ErrorCode.swift b/Sources/Auth/Types/Error/InternalError.swift similarity index 84% rename from Sources/Auth/Types/ErrorCode.swift rename to Sources/Auth/Types/Error/InternalError.swift index 052f2719d..2a62472fa 100644 --- a/Sources/Auth/Types/ErrorCode.swift +++ b/Sources/Auth/Types/Error/InternalError.swift @@ -1,18 +1,13 @@ import Foundation -public protocol Reason { - var code: Int { get } - var message: String { get } -} - -public enum ErrorCode: Codable, Equatable, Error { +public enum InternalError: Codable, Equatable, Error { case malformedResponseParams case malformedRequestParams case messageCompromised case messageVerificationFailed } -extension ErrorCode: Reason { +extension InternalError: Reason { public var code: Int { switch self { diff --git a/Sources/Auth/Types/Error/Reason.swift b/Sources/Auth/Types/Error/Reason.swift new file mode 100644 index 000000000..bbf491fdd --- /dev/null +++ b/Sources/Auth/Types/Error/Reason.swift @@ -0,0 +1,7 @@ +import Foundation + +protocol Reason { + var code: Int { get } + var message: String { get } +} + From a556e59614681f5d44a09f83937d61d55a1cacc9 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Fri, 19 Aug 2022 14:11:31 +0300 Subject: [PATCH 07/10] Build errors fix --- Sources/Auth/AuthClient.swift | 2 +- Sources/Auth/AuthClientFactory.swift | 2 +- .../Services/Wallet/WalletRequestSubscriber.swift | 3 --- .../Auth/Services/Wallet/WalletRespondService.swift | 12 ++++++------ Sources/Auth/Types/Cacao/CacaoSignature.swift | 8 ++++---- Tests/AuthTests/AuthRequstSubscriberTests.swift | 9 ++++----- 6 files changed, 16 insertions(+), 20 deletions(-) diff --git a/Sources/Auth/AuthClient.swift b/Sources/Auth/AuthClient.swift index 74cd7e65e..83d8febab 100644 --- a/Sources/Auth/AuthClient.swift +++ b/Sources/Auth/AuthClient.swift @@ -87,7 +87,7 @@ public class AuthClient { try await appRequestService.request(params: params, topic: topic) } - public func respond(requestId: RPCID, result: Result) async throws { + public func respond(requestId: RPCID, result: Result) async throws { guard let account = account else { throw Errors.unknownWalletAddress } try await walletRespondService.respond(requestId: requestId, result: result, account: account) } diff --git a/Sources/Auth/AuthClientFactory.swift b/Sources/Auth/AuthClientFactory.swift index 6cc3f3a35..6be615528 100644 --- a/Sources/Auth/AuthClientFactory.swift +++ b/Sources/Auth/AuthClientFactory.swift @@ -26,7 +26,7 @@ public struct AuthClientFactory { let messageSigner = MessageSigner(signer: Signer()) let appRespondSubscriber = AppRespondSubscriber(networkingInteractor: networkingInteractor, logger: logger, rpcHistory: history, signatureVerifier: messageSigner, messageFormatter: messageFormatter) let walletPairService = WalletPairService(networkingInteractor: networkingInteractor, kms: kms, pairingStorage: pairingStore) - let walletRequestSubscriber = WalletRequestSubscriber(networkingInteractor: networkingInteractor, logger: logger, rpcHistory: history, kms: kms, messageFormatter: messageFormatter, address: account?.address) + let walletRequestSubscriber = WalletRequestSubscriber(networkingInteractor: networkingInteractor, logger: logger, kms: kms, messageFormatter: messageFormatter, address: account?.address) let walletRespondService = WalletRespondService(networkingInteractor: networkingInteractor, logger: logger, kms: kms, rpcHistory: history) let pendingRequestsProvider = PendingRequestsProvider(rpcHistory: history) let cleanupService = CleanupService(pairingStore: pairingStore, kms: kms) diff --git a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift index 4d872742a..f6511a400 100644 --- a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift +++ b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift @@ -7,7 +7,6 @@ import WalletConnectKMS class WalletRequestSubscriber { private let networkingInteractor: NetworkInteracting private let logger: ConsoleLogging - private let rpcHistory: RPCHistory private let kms: KeyManagementService private let address: String? private var publishers = [AnyCancellable]() @@ -16,13 +15,11 @@ class WalletRequestSubscriber { init(networkingInteractor: NetworkInteracting, logger: ConsoleLogging, - rpcHistory: RPCHistory, kms: KeyManagementService, messageFormatter: SIWEMessageFormatting, address: String?) { self.networkingInteractor = networkingInteractor self.logger = logger - self.rpcHistory = rpcHistory self.kms = kms self.address = address self.messageFormatter = messageFormatter diff --git a/Sources/Auth/Services/Wallet/WalletRespondService.swift b/Sources/Auth/Services/Wallet/WalletRespondService.swift index 93f64318b..079fd82f8 100644 --- a/Sources/Auth/Services/Wallet/WalletRespondService.swift +++ b/Sources/Auth/Services/Wallet/WalletRespondService.swift @@ -23,25 +23,25 @@ actor WalletRespondService { self.rpcHistory = rpcHistory } - func respond(requestId: RPCID, result: Result, account: Account) async throws { + func respond(requestId: RPCID, result: Result, account: Account) async throws { switch result { - case .success(let params): - try await respond(requestId: requestId, params: params, account: account) + case .success(let signature): + try await respond(requestId: requestId, signature: signature, account: account) case .failure(let error): try await respond(error: error, requestId: requestId) } } - private func respond(requestId: RPCID, params: RespondParams, account: Account) async throws { + private func respond(requestId: RPCID, signature: CacaoSignature, account: Account) async throws { let authRequestParams = try getAuthRequestParams(requestId: requestId) let (topic, keys) = try generateAgreementKeys(requestParams: authRequestParams) try kms.setAgreementSecret(keys, topic: topic) let didpkh = DIDPKH(account: account) - let cacao = CacaoFormatter().format(authRequestParams, params.signature, didpkh) + let cacao = CacaoFormatter().format(authRequestParams, signature, didpkh) let response = RPCResponse(id: requestId, result: cacao) - try await networkingInteractor.respond(topic: params.topic, response: response, tag: AuthResponseParams.tag, envelopeType: .type1(pubKey: keys.publicKey.rawRepresentation)) + try await networkingInteractor.respond(topic: topic, response: response, tag: AuthResponseParams.tag, envelopeType: .type1(pubKey: keys.publicKey.rawRepresentation)) } private func respond(error: ExternalError, requestId: RPCID) async throws { diff --git a/Sources/Auth/Types/Cacao/CacaoSignature.swift b/Sources/Auth/Types/Cacao/CacaoSignature.swift index 2c6c4c49e..69784b55e 100644 --- a/Sources/Auth/Types/Cacao/CacaoSignature.swift +++ b/Sources/Auth/Types/Cacao/CacaoSignature.swift @@ -1,7 +1,7 @@ import Foundation -struct CacaoSignature: Codable, Equatable { - let t: String - let s: String - let m: String? = nil +public struct CacaoSignature: Codable, Equatable { + public let t: String + public let s: String + public let m: String? = nil } diff --git a/Tests/AuthTests/AuthRequstSubscriberTests.swift b/Tests/AuthTests/AuthRequstSubscriberTests.swift index 1ecb0bc2b..14a73bef5 100644 --- a/Tests/AuthTests/AuthRequstSubscriberTests.swift +++ b/Tests/AuthTests/AuthRequstSubscriberTests.swift @@ -15,9 +15,9 @@ class AuthRequstSubscriberTests: XCTestCase { override func setUp() { networkingInteractor = NetworkingInteractorMock() messageFormatter = SIWEMessageFormatterMock() - sut = WalletRequestSubscriber(networkingInteractor: networkingInteractor, - logger: ConsoleLoggerMock(), - messageFormatter: messageFormatter, address: "") +// sut = WalletRequestSubscriber(networkingInteractor: networkingInteractor, +// logger: ConsoleLoggerMock(), +// messageFormatter: messageFormatter, address: "") } func testSubscribeRequest() { @@ -27,8 +27,7 @@ class AuthRequstSubscriberTests: XCTestCase { messageFormatter.formattedMessage = expectedMessage var messageId: RPCID! var message: String! - sut.onRequest = { id, result in - guard case .success(let formattedMessage) = result else { return XCTFail() } + sut.onRequest = { id, formattedMessage in messageId = id message = formattedMessage messageExpectation.fulfill() From 0396e442f3e5cf74e0618bb913221477e1e68f6a Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Fri, 19 Aug 2022 14:34:44 +0300 Subject: [PATCH 08/10] Rebase with develop --- Example/IntegrationTests/Auth/AuthTests.swift | 4 ++-- .../Auth/Services/Wallet/WalletRequestSubscriber.swift | 8 ++++---- Sources/Auth/Services/Wallet/WalletRespondService.swift | 4 ++-- Tests/AuthTests/AuthRequstSubscriberTests.swift | 7 ++++--- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/Example/IntegrationTests/Auth/AuthTests.swift b/Example/IntegrationTests/Auth/AuthTests.swift index 05cb74088..1cab04dc3 100644 --- a/Example/IntegrationTests/Auth/AuthTests.swift +++ b/Example/IntegrationTests/Auth/AuthTests.swift @@ -70,7 +70,7 @@ final class AuthTests: XCTestCase { Task(priority: .high) { let signature = try! MessageSigner(signer: Signer()).sign(message: message, privateKey: prvKey) let cacaoSignature = CacaoSignature(t: "eip191", s: signature) - try! await wallet.respond(.success(RespondParams(id: id, signature: cacaoSignature))) + try! await wallet.respond(requestId: id, result: .success(cacaoSignature)) } } .store(in: &publishers) @@ -79,6 +79,6 @@ final class AuthTests: XCTestCase { responseExpectation.fulfill() } .store(in: &publishers) - wait(for: [responseExpectation], timeout: 2) + wait(for: [responseExpectation], timeout: .infinity) } } diff --git a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift index f6511a400..c9a402273 100644 --- a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift +++ b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift @@ -7,7 +7,7 @@ import WalletConnectKMS class WalletRequestSubscriber { private let networkingInteractor: NetworkInteracting private let logger: ConsoleLogging - private let kms: KeyManagementService + private let kms: KeyManagementServiceProtocol private let address: String? private var publishers = [AnyCancellable]() private let messageFormatter: SIWEMessageFormatting @@ -15,7 +15,7 @@ class WalletRequestSubscriber { init(networkingInteractor: NetworkInteracting, logger: ConsoleLogging, - kms: KeyManagementService, + kms: KeyManagementServiceProtocol, messageFormatter: SIWEMessageFormatting, address: String?) { self.networkingInteractor = networkingInteractor @@ -27,12 +27,12 @@ class WalletRequestSubscriber { } private func subscribeForRequest() { + guard let address = address else { return } + networkingInteractor.requestPublisher.sink { [unowned self] payload in logger.debug("WalletRequestSubscriber: Received request") - guard let address = address else { return } - guard let requestId = payload.request.id, payload.request.method == "wc_authRequest" else { return } diff --git a/Sources/Auth/Services/Wallet/WalletRespondService.swift b/Sources/Auth/Services/Wallet/WalletRespondService.swift index 079fd82f8..c5aec4e7a 100644 --- a/Sources/Auth/Services/Wallet/WalletRespondService.swift +++ b/Sources/Auth/Services/Wallet/WalletRespondService.swift @@ -66,10 +66,10 @@ actor WalletRespondService { } private func generateAgreementKeys(requestParams: AuthRequestParams) throws -> (topic: String, keys: AgreementKeys) { - let peerPubKey = requestParams.requester.publicKey + let peerPubKey = try AgreementPublicKey(hex: requestParams.requester.publicKey) let topic = peerPubKey.rawRepresentation.sha256().toHexString() let selfPubKey = try kms.createX25519KeyPair() - let keys = try kms.performKeyAgreement(selfPublicKey: selfPubKey, peerPublicKey: peerPubKey) + let keys = try kms.performKeyAgreement(selfPublicKey: selfPubKey, peerPublicKey: peerPubKey.hexRepresentation) return (topic, keys) } } diff --git a/Tests/AuthTests/AuthRequstSubscriberTests.swift b/Tests/AuthTests/AuthRequstSubscriberTests.swift index 14a73bef5..fc7c6ebbb 100644 --- a/Tests/AuthTests/AuthRequstSubscriberTests.swift +++ b/Tests/AuthTests/AuthRequstSubscriberTests.swift @@ -15,9 +15,10 @@ class AuthRequstSubscriberTests: XCTestCase { override func setUp() { networkingInteractor = NetworkingInteractorMock() messageFormatter = SIWEMessageFormatterMock() -// sut = WalletRequestSubscriber(networkingInteractor: networkingInteractor, -// logger: ConsoleLoggerMock(), -// messageFormatter: messageFormatter, address: "") + sut = WalletRequestSubscriber(networkingInteractor: networkingInteractor, + logger: ConsoleLoggerMock(), + kms: KeyManagementServiceMock(), + messageFormatter: messageFormatter, address: "") } func testSubscribeRequest() { From 6733bf5c304511ad9e52829dffda39ce62da5658 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Fri, 19 Aug 2022 15:04:00 +0300 Subject: [PATCH 09/10] RPCResponse additional --- Sources/JSONRPC/RPCResponse.swift | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Sources/JSONRPC/RPCResponse.swift b/Sources/JSONRPC/RPCResponse.swift index fb9c819db..ad6ba9ca6 100644 --- a/Sources/JSONRPC/RPCResponse.swift +++ b/Sources/JSONRPC/RPCResponse.swift @@ -51,6 +51,14 @@ public struct RPCResponse: Equatable { self.init(id: id, outcome: .failure(error)) } + public init(id: Int64, error: JSONRPCError) { + self.init(id: RPCID(id), outcome: .failure(error)) + } + + public init(id: String, error: JSONRPCError) { + self.init(id: RPCID(id), outcome: .failure(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))) } From 44519cb0dd18c190821c7778ba59f74f78484122 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Mon, 22 Aug 2022 17:40:29 +0300 Subject: [PATCH 10/10] AuthError --- Example/IntegrationTests/Auth/AuthTests.swift | 3 +-- Sources/Auth/AuthClient.swift | 6 ++--- .../Services/App/AppRespondSubscriber.swift | 2 +- .../Wallet/WalletRequestSubscriber.swift | 2 +- .../Wallet/WalletRespondService.swift | 9 ++++---- Sources/Auth/Types/Error/ExternalError.swift | 22 ------------------- .../AuthError.swift} | 17 +++++++++----- .../Auth/Types/{Error => Errors}/Reason.swift | 0 8 files changed, 22 insertions(+), 39 deletions(-) delete mode 100644 Sources/Auth/Types/Error/ExternalError.swift rename Sources/Auth/Types/{Error/InternalError.swift => Errors/AuthError.swift} (70%) rename Sources/Auth/Types/{Error => Errors}/Reason.swift (100%) diff --git a/Example/IntegrationTests/Auth/AuthTests.swift b/Example/IntegrationTests/Auth/AuthTests.swift index 1cab04dc3..20337b451 100644 --- a/Example/IntegrationTests/Auth/AuthTests.swift +++ b/Example/IntegrationTests/Auth/AuthTests.swift @@ -35,7 +35,6 @@ final class AuthTests: XCTestCase { wait(for: [expectation], timeout: 5) } - func makeClient(prefix: String, account: Account? = nil) -> AuthClient { let logger = ConsoleLogger(suffix: prefix, loggingLevel: .debug) let relayHost = "relay.walletconnect.com" @@ -79,6 +78,6 @@ final class AuthTests: XCTestCase { responseExpectation.fulfill() } .store(in: &publishers) - wait(for: [responseExpectation], timeout: .infinity) + wait(for: [responseExpectation], timeout: 5) } } diff --git a/Sources/Auth/AuthClient.swift b/Sources/Auth/AuthClient.swift index 83d8febab..ad4192861 100644 --- a/Sources/Auth/AuthClient.swift +++ b/Sources/Auth/AuthClient.swift @@ -15,8 +15,8 @@ public class AuthClient { authRequestPublisherSubject.eraseToAnyPublisher() } - private var authResponsePublisherSubject = PassthroughSubject<(id: RPCID, result: Result), Never>() - public var authResponsePublisher: AnyPublisher<(id: RPCID, result: Result), Never> { + private var authResponsePublisherSubject = PassthroughSubject<(id: RPCID, result: Result), Never>() + public var authResponsePublisher: AnyPublisher<(id: RPCID, result: Result), Never> { authResponsePublisherSubject.eraseToAnyPublisher() } @@ -87,7 +87,7 @@ public class AuthClient { try await appRequestService.request(params: params, topic: topic) } - public func respond(requestId: RPCID, result: Result) async throws { + public func respond(requestId: RPCID, result: Result) async throws { guard let account = account else { throw Errors.unknownWalletAddress } try await walletRespondService.respond(requestId: requestId, result: result, account: account) } diff --git a/Sources/Auth/Services/App/AppRespondSubscriber.swift b/Sources/Auth/Services/App/AppRespondSubscriber.swift index 809962cfc..d013d8fb4 100644 --- a/Sources/Auth/Services/App/AppRespondSubscriber.swift +++ b/Sources/Auth/Services/App/AppRespondSubscriber.swift @@ -11,7 +11,7 @@ class AppRespondSubscriber { private let messageFormatter: SIWEMessageFormatting private var publishers = [AnyCancellable]() - var onResponse: ((_ id: RPCID, _ result: Result) -> Void)? + var onResponse: ((_ id: RPCID, _ result: Result) -> Void)? init(networkingInteractor: NetworkInteracting, logger: ConsoleLogging, diff --git a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift index c9a402273..6d04629ca 100644 --- a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift +++ b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift @@ -45,7 +45,7 @@ class WalletRequestSubscriber { }.store(in: &publishers) } - private func respondError(_ error: InternalError, topic: String, requestId: RPCID) { + private func respondError(_ error: AuthError, topic: String, requestId: RPCID) { guard let pubKey = kms.getAgreementSecret(for: topic)?.publicKey else { return logger.error("Agreement key for topic \(topic) not found") } diff --git a/Sources/Auth/Services/Wallet/WalletRespondService.swift b/Sources/Auth/Services/Wallet/WalletRespondService.swift index c5aec4e7a..0aba57f14 100644 --- a/Sources/Auth/Services/Wallet/WalletRespondService.swift +++ b/Sources/Auth/Services/Wallet/WalletRespondService.swift @@ -23,12 +23,12 @@ actor WalletRespondService { self.rpcHistory = rpcHistory } - func respond(requestId: RPCID, result: Result, account: Account) async throws { + func respond(requestId: RPCID, result: Result, account: Account) async throws { switch result { case .success(let signature): try await respond(requestId: requestId, signature: signature, account: account) - case .failure(let error): - try await respond(error: error, requestId: requestId) + case .failure: + try await respondError(requestId: requestId) } } @@ -44,13 +44,14 @@ actor WalletRespondService { try await networkingInteractor.respond(topic: topic, response: response, tag: AuthResponseParams.tag, envelopeType: .type1(pubKey: keys.publicKey.rawRepresentation)) } - private func respond(error: ExternalError, requestId: RPCID) async throws { + private func respondError(requestId: RPCID) async throws { let authRequestParams = try getAuthRequestParams(requestId: requestId) let (topic, keys) = try generateAgreementKeys(requestParams: authRequestParams) try kms.setAgreementSecret(keys, topic: topic) let tag = AuthResponseParams.tag + let error = AuthError.userRejeted let envelopeType = Envelope.EnvelopeType.type1(pubKey: keys.publicKey.rawRepresentation) try await networkingInteractor.respondError(topic: topic, requestId: requestId, tag: tag, reason: error, envelopeType: envelopeType) } diff --git a/Sources/Auth/Types/Error/ExternalError.swift b/Sources/Auth/Types/Error/ExternalError.swift deleted file mode 100644 index 596369b0f..000000000 --- a/Sources/Auth/Types/Error/ExternalError.swift +++ /dev/null @@ -1,22 +0,0 @@ -import Foundation - -public enum ExternalError: Codable, Equatable, Error { - case userRejeted -} - -extension ExternalError: Reason { - - public var code: Int { - switch self { - case .userRejeted: - return 2001 - } - } - - public var message: String { - switch self { - case .userRejeted: - return "Auth request rejected by user" - } - } -} diff --git a/Sources/Auth/Types/Error/InternalError.swift b/Sources/Auth/Types/Errors/AuthError.swift similarity index 70% rename from Sources/Auth/Types/Error/InternalError.swift rename to Sources/Auth/Types/Errors/AuthError.swift index 2a62472fa..05e7ed2d2 100644 --- a/Sources/Auth/Types/Error/InternalError.swift +++ b/Sources/Auth/Types/Errors/AuthError.swift @@ -1,29 +1,34 @@ import Foundation -public enum InternalError: Codable, Equatable, Error { +public enum AuthError: Codable, Equatable, Error { + case userRejeted case malformedResponseParams case malformedRequestParams case messageCompromised case messageVerificationFailed } -extension InternalError: Reason { +extension AuthError: Reason { public var code: Int { switch self { + case .userRejeted: + return 14001 case .malformedResponseParams: - return 1001 + return 12001 case .malformedRequestParams: - return 1002 + return 12002 case .messageCompromised: - return 1003 + return 12003 case .messageVerificationFailed: - return 1004 + return 12004 } } public var message: String { switch self { + case .userRejeted: + return "Auth request rejected by user" case .malformedResponseParams: return "Response params malformed" case .malformedRequestParams: diff --git a/Sources/Auth/Types/Error/Reason.swift b/Sources/Auth/Types/Errors/Reason.swift similarity index 100% rename from Sources/Auth/Types/Error/Reason.swift rename to Sources/Auth/Types/Errors/Reason.swift