From 8b5e2b4af706b23d209aa019dba4d42863c7579c Mon Sep 17 00:00:00 2001 From: Derek Date: Fri, 5 Aug 2022 13:59:52 +0200 Subject: [PATCH 01/89] fix: intake process does not work for non-public org members --- .github/workflows/intake.yml | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/.github/workflows/intake.yml b/.github/workflows/intake.yml index 4b79bc326..210a8e04e 100644 --- a/.github/workflows/intake.yml +++ b/.github/workflows/intake.yml @@ -1,4 +1,4 @@ -# This workflow moves issues to the Swift board +# This workflow moves issues to the board # when they receive the "accepted" label # When WalletConnect Org members create issues they # are automatically "accepted". @@ -28,15 +28,20 @@ jobs: 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 + - name: Check Core Team membership + uses: tspascoal/get-user-teams-membership@v1 + id: is-core-team 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 }} + team: "Core Team" + GITHUB_TOKEN: ${{ secrets.ASSIGN_TO_PROJECT_GITHUB_TOKEN }} + - name: Print result + env: + CREATOR: ${{ github.event_name != 'pull_request' && github.event.issue.user.login || github.event.sender.login }} + IS_TEAM_MEMBER: ${{ steps.is-core-team.outputs.isTeamMember }} + run: echo "$CREATOR (Core Team Member $IS_TEAM_MEMBER) created this issue/PR" - name: Label issues + if: ${{ steps.is-core-team.outputs.isTeamMember == 'true' }} uses: andymckay/labeler@e6c4322d0397f3240f0e7e30a33b5c5df2d39e90 with: add-labels: "accepted" From 34b87beed294fb7f3d777b4833945ea9d9f614cf Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 8 Aug 2022 11:15:56 +0200 Subject: [PATCH 02/89] Clean protocol codes --- .../Engine/Common/ApproveEngine.swift | 4 +- .../NonControllerSessionStateMachine.swift | 10 +- .../WalletConnectSign/RejectionReason.swift | 6 +- .../WalletConnectSign/Types/ReasonCode.swift | 109 +++++++++--------- ...onControllerSessionStateMachineTests.swift | 2 +- 5 files changed, 66 insertions(+), 65 deletions(-) diff --git a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift index 84d871f48..caca3df48 100644 --- a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift @@ -280,7 +280,7 @@ private extension ApproveEngine { func handleSessionProposeRequest(payload: WCRequestSubscriptionPayload, proposal: SessionType.ProposeParams) throws { logger.debug("Received Session Proposal") - do { try Namespace.validate(proposal.requiredNamespaces) } catch { throw Errors.respondError(payload: payload, reason: .invalidUpdateNamespaceRequest) } + do { try Namespace.validate(proposal.requiredNamespaces) } catch { throw Errors.respondError(payload: payload, reason: .invalidUpdateRequest) } proposalPayloadsStore.set(payload, forKey: proposal.proposer.publicKey) onSessionProposal?(proposal.publicRepresentation()) } @@ -290,7 +290,7 @@ private extension ApproveEngine { logger.debug("Did receive session settle request") guard let proposedNamespaces = settlingProposal?.requiredNamespaces - else { throw Errors.respondError(payload: payload, reason: .invalidUpdateNamespaceRequest) } + else { throw Errors.respondError(payload: payload, reason: .invalidUpdateRequest) } settlingProposal = nil diff --git a/Sources/WalletConnectSign/Engine/NonController/NonControllerSessionStateMachine.swift b/Sources/WalletConnectSign/Engine/NonController/NonControllerSessionStateMachine.swift index 363eb5d88..bb3ad3220 100644 --- a/Sources/WalletConnectSign/Engine/NonController/NonControllerSessionStateMachine.swift +++ b/Sources/WalletConnectSign/Engine/NonController/NonControllerSessionStateMachine.swift @@ -62,18 +62,18 @@ final class NonControllerSessionStateMachine { do { try Namespace.validate(updateParams.namespaces) } catch { - throw Errors.respondError(payload: payload, reason: .invalidUpdateNamespaceRequest) + throw Errors.respondError(payload: payload, reason: .invalidUpdateRequest) } guard var session = sessionStore.getSession(forTopic: payload.topic) else { throw Errors.respondError(payload: payload, reason: .noContextWithTopic(context: .session, topic: payload.topic)) } guard session.peerIsController else { - throw Errors.respondError(payload: payload, reason: .unauthorizedUpdateNamespacesRequest) + throw Errors.respondError(payload: payload, reason: .unauthorizedUpdateRequest) } do { try session.updateNamespaces(updateParams.namespaces, timestamp: payload.timestamp) } catch { - throw Errors.respondError(payload: payload, reason: .invalidUpdateNamespaceRequest) + throw Errors.respondError(payload: payload, reason: .invalidUpdateRequest) } sessionStore.setSession(session) networkingInteractor.respondSuccess(for: payload) @@ -86,12 +86,12 @@ final class NonControllerSessionStateMachine { throw Errors.respondError(payload: payload, reason: .noContextWithTopic(context: .session, topic: topic)) } guard session.peerIsController else { - throw Errors.respondError(payload: payload, reason: .unauthorizedUpdateExpiryRequest) + throw Errors.respondError(payload: payload, reason: .unauthorizedExtendRequest) } do { try session.updateExpiry(to: updateExpiryParams.expiry) } catch { - throw Errors.respondError(payload: payload, reason: .invalidUpdateExpiryRequest) + throw Errors.respondError(payload: payload, reason: .invalidExtendRequest) } sessionStore.setSession(session) networkingInteractor.respondSuccess(for: payload) diff --git a/Sources/WalletConnectSign/RejectionReason.swift b/Sources/WalletConnectSign/RejectionReason.swift index cee66c716..b6b0b3c20 100644 --- a/Sources/WalletConnectSign/RejectionReason.swift +++ b/Sources/WalletConnectSign/RejectionReason.swift @@ -11,11 +11,11 @@ internal extension RejectionReason { func internalRepresentation() -> ReasonCode { switch self { case .disapprovedChains: - return ReasonCode.disapprovedChains + return ReasonCode.userRejectedChains case .disapprovedMethods: - return ReasonCode.disapprovedMethods + return ReasonCode.userRejectedMethods case .disapprovedEventTypes: - return ReasonCode.disapprovedEventTypes + return ReasonCode.userRejectedEvents } } } diff --git a/Sources/WalletConnectSign/Types/ReasonCode.swift b/Sources/WalletConnectSign/Types/ReasonCode.swift index 496818936..c80b00fd8 100644 --- a/Sources/WalletConnectSign/Types/ReasonCode.swift +++ b/Sources/WalletConnectSign/Types/ReasonCode.swift @@ -5,29 +5,29 @@ enum ReasonCode { case session = "session" } - // 0 (Generic) - case generic(message: String) - - // 1000 (Internal) - case missingOrInvalid(String) - case invalidUpdateAccountsRequest - case invalidUpdateNamespaceRequest - case invalidUpdateExpiryRequest + // 1000 - (Internal) + case invalidMethod + case invalidEvent + case invalidUpdateRequest + case invalidExtendRequest case noContextWithTopic(context: Context, topic: String) - // 3000 (Unauthorized) - case unauthorizedTargetChain(String) + // 3000 - (Unauthorized) case unauthorizedMethod(String) case unauthorizedEvent(String) - case unauthorizedUpdateAccountRequest - case unauthorizedUpdateNamespacesRequest - case unauthorizedUpdateExpiryRequest - case unauthorizedMatchingController(isController: Bool) - - // 5000 - case disapprovedChains - case disapprovedMethods - case disapprovedEventTypes + case unauthorizedUpdateRequest + case unauthorizedExtendRequest + case unauthorizedTargetChain(String) + + // 4001 - (EIP-1193) + case userRejectedRequest + + // 5000 - (REJECTED (CAIP-25)) + case userRejected + case userRejectedChains + case userRejectedMethods + case userRejectedEvents + case unsupportedChains case unsupportedMethods case unsupportedEvents @@ -39,25 +39,24 @@ enum ReasonCode { var code: Int { switch self { - case .generic: return 0 - case .missingOrInvalid: return 1000 - - case .invalidUpdateAccountsRequest: return 1003 - case .invalidUpdateNamespaceRequest: return 1004 - case .invalidUpdateExpiryRequest: return 1005 + case .invalidMethod: return 1001 + case .invalidEvent: return 1002 + case .invalidUpdateRequest: return 1003 + case .invalidExtendRequest: return 1004 case .noContextWithTopic: return 1301 - case .unauthorizedTargetChain: return 3000 case .unauthorizedMethod: return 3001 case .unauthorizedEvent: return 3002 + case .unauthorizedUpdateRequest: return 3003 + case .unauthorizedExtendRequest: return 3004 + case .unauthorizedTargetChain: return 3005 - case .unauthorizedUpdateAccountRequest: return 3003 - case .unauthorizedUpdateNamespacesRequest: return 3004 - case .unauthorizedUpdateExpiryRequest: return 3005 - case .unauthorizedMatchingController: return 3100 - case .disapprovedChains: return 5000 - case .disapprovedMethods: return 5001 - case .disapprovedEventTypes: return 5002 + case .userRejectedRequest: return 4001 + + case .userRejected: return 5000 + case .userRejectedChains: return 5001 + case .userRejectedMethods: return 5002 + case .userRejectedEvents: return 5003 case .unsupportedChains: return 5100 case .unsupportedMethods: return 5101 @@ -71,38 +70,40 @@ enum ReasonCode { var message: String { switch self { - case .generic(let message): - return message - case .missingOrInvalid(let name): - return "Missing or invalid \(name)" - case .invalidUpdateAccountsRequest: - return "Invalid update accounts request" - case .invalidUpdateNamespaceRequest: + case .invalidMethod: + return "Invalid Method" + case .invalidEvent: + return "Invalid Event" + case .invalidUpdateRequest: return "Invalid update namespace request" - case .invalidUpdateExpiryRequest: + case .invalidExtendRequest: return "Invalid update expiry request" case .noContextWithTopic(let context, let topic): return "No matching \(context) with topic: \(topic)" - case .unauthorizedTargetChain(let chainId): - return "Unauthorized target chain id requested: \(chainId)" + case .unauthorizedMethod(let method): return "Unauthorized JSON-RPC method requested: \(method)" case .unauthorizedEvent(let type): return "Unauthorized event type requested: \(type)" - case .unauthorizedUpdateAccountRequest: - return "Unauthorized update accounts request" - case .unauthorizedUpdateNamespacesRequest: - return "Unauthorized update namespaces request" - case .unauthorizedUpdateExpiryRequest: - return "Unauthorized update expiry request" - case .unauthorizedMatchingController(let isController): - return "Unauthorized: peer is also \(isController ? "" : "non-")controller" - case .disapprovedChains: + case .unauthorizedUpdateRequest: + return "Unauthorized update request" + case .unauthorizedExtendRequest: + return "Unauthorized extend request" + case .unauthorizedTargetChain(let chainId): + return "Unauthorized target chain id requested: \(chainId)" + + case .userRejectedRequest: + return "User rejected request" + + case .userRejected: + return "User rejected" + case .userRejectedChains: return "User disapproved requested chains" - case .disapprovedMethods: + case .userRejectedMethods: return "User disapproved requested json-rpc methods" - case .disapprovedEventTypes: + case .userRejectedEvents: return "User disapproved requested event types" + case .unsupportedChains: return "Unsupported or empty chains for namespace" case .unsupportedMethods: diff --git a/Tests/WalletConnectSignTests/NonControllerSessionStateMachineTests.swift b/Tests/WalletConnectSignTests/NonControllerSessionStateMachineTests.swift index 5e254eb4c..0edb20e8c 100644 --- a/Tests/WalletConnectSignTests/NonControllerSessionStateMachineTests.swift +++ b/Tests/WalletConnectSignTests/NonControllerSessionStateMachineTests.swift @@ -61,7 +61,7 @@ class NonControllerSessionStateMachineTests: XCTestCase { networkingInteractor.wcRequestPublisherSubject.send(WCRequestSubscriptionPayload.stubUpdateNamespaces(topic: session.topic)) usleep(100) XCTAssertFalse(networkingInteractor.didRespondSuccess) - XCTAssertEqual(networkingInteractor.lastErrorCode, 3004) + XCTAssertEqual(networkingInteractor.lastErrorCode, 3003) } // MARK: - Update Expiry From 5fa75f20cdd1b6a400910555eb43ede2af0e5862 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 9 Aug 2022 08:33:56 +0200 Subject: [PATCH 03/89] savepoint --- .../Wallet/WalletViewController.swift | 2 +- .../Sign/SignClientTests.swift | 2 +- .../Engine/Common/SessionEngine.swift | 8 +++---- .../NonControllerSessionStateMachine.swift | 4 ++-- .../WalletConnectSign/RejectionReason.swift | 15 +++++++------ .../WalletConnectSign/Types/ReasonCode.swift | 21 ++++++++++--------- 6 files changed, 28 insertions(+), 24 deletions(-) diff --git a/Example/ExampleApp/Wallet/WalletViewController.swift b/Example/ExampleApp/Wallet/WalletViewController.swift index cc9b001e6..929aad4b0 100644 --- a/Example/ExampleApp/Wallet/WalletViewController.swift +++ b/Example/ExampleApp/Wallet/WalletViewController.swift @@ -222,7 +222,7 @@ extension WalletViewController: ProposalViewControllerDelegate { func didRejectSession() { let proposal = currentProposal! currentProposal = nil - reject(proposalId: proposal.id, reason: .disapprovedChains) + reject(proposalId: proposal.id, reason: .userRejectedChains) } } diff --git a/Example/IntegrationTests/Sign/SignClientTests.swift b/Example/IntegrationTests/Sign/SignClientTests.swift index cbf6566cf..31204a636 100644 --- a/Example/IntegrationTests/Sign/SignClientTests.swift +++ b/Example/IntegrationTests/Sign/SignClientTests.swift @@ -101,7 +101,7 @@ final class SignClientTests: XCTestCase { wallet.onSessionProposal = { [unowned self] proposal in Task(priority: .high) { do { - try await wallet.client.reject(proposalId: proposal.id, reason: .disapprovedChains) // TODO: Review reason + try await wallet.client.reject(proposalId: proposal.id, reason: .userRejectedChains) // TODO: Review reason store.rejectedProposal = proposal } catch { XCTFail("\(error)") } } diff --git a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift index 5b619f8f6..c535d7ce3 100644 --- a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift @@ -153,7 +153,7 @@ private extension SessionEngine { func onSessionDelete(_ payload: WCRequestSubscriptionPayload, deleteParams: SessionType.DeleteParams) throws { let topic = payload.topic guard sessionStore.hasSession(forTopic: topic) else { - throw Errors.respondError(payload: payload, reason: .noContextWithTopic(context: .session, topic: topic)) + throw Errors.respondError(payload: payload, reason: .noSessionForTopic) } sessionStore.delete(topic: topic) networkingInteractor.unsubscribe(topic: topic) @@ -172,11 +172,11 @@ private extension SessionEngine { chainId: payloadParams.chainId) guard let session = sessionStore.getSession(forTopic: topic) else { - throw Errors.respondError(payload: payload, reason: .noContextWithTopic(context: .session, topic: topic)) + throw Errors.respondError(payload: payload, reason: .noSessionForTopic) } let chain = request.chainId guard session.hasNamespace(for: chain) else { - throw Errors.respondError(payload: payload, reason: .unauthorizedTargetChain(chain.absoluteString)) + throw Errors.respondError(payload: payload, reason: .unauthorizedChain) } guard session.hasPermission(forMethod: request.method, onChain: chain) else { throw Errors.respondError(payload: payload, reason: .unauthorizedMethod(request.method)) @@ -192,7 +192,7 @@ private extension SessionEngine { let event = eventParams.event let topic = payload.topic guard let session = sessionStore.getSession(forTopic: topic) else { - throw Errors.respondError(payload: payload, reason: .noContextWithTopic(context: .session, topic: payload.topic)) + throw Errors.respondError(payload: payload, reason: .noSessionForTopic) } guard session.peerIsController, diff --git a/Sources/WalletConnectSign/Engine/NonController/NonControllerSessionStateMachine.swift b/Sources/WalletConnectSign/Engine/NonController/NonControllerSessionStateMachine.swift index bb3ad3220..0e4e22f66 100644 --- a/Sources/WalletConnectSign/Engine/NonController/NonControllerSessionStateMachine.swift +++ b/Sources/WalletConnectSign/Engine/NonController/NonControllerSessionStateMachine.swift @@ -65,7 +65,7 @@ final class NonControllerSessionStateMachine { throw Errors.respondError(payload: payload, reason: .invalidUpdateRequest) } guard var session = sessionStore.getSession(forTopic: payload.topic) else { - throw Errors.respondError(payload: payload, reason: .noContextWithTopic(context: .session, topic: payload.topic)) + throw Errors.respondError(payload: payload, reason: .noSessionForTopic) } guard session.peerIsController else { throw Errors.respondError(payload: payload, reason: .unauthorizedUpdateRequest) @@ -83,7 +83,7 @@ final class NonControllerSessionStateMachine { private func onSessionUpdateExpiry(_ payload: WCRequestSubscriptionPayload, updateExpiryParams: SessionType.UpdateExpiryParams) throws { let topic = payload.topic guard var session = sessionStore.getSession(forTopic: topic) else { - throw Errors.respondError(payload: payload, reason: .noContextWithTopic(context: .session, topic: topic)) + throw Errors.respondError(payload: payload, reason: .noSessionForTopic) } guard session.peerIsController else { throw Errors.respondError(payload: payload, reason: .unauthorizedExtendRequest) diff --git a/Sources/WalletConnectSign/RejectionReason.swift b/Sources/WalletConnectSign/RejectionReason.swift index b6b0b3c20..338cffe4a 100644 --- a/Sources/WalletConnectSign/RejectionReason.swift +++ b/Sources/WalletConnectSign/RejectionReason.swift @@ -2,19 +2,22 @@ import Foundation /// https://github.com/ChainAgnostic/CAIPs/blob/master/CAIPs/caip-25.md public enum RejectionReason { - case disapprovedChains - case disapprovedMethods - case disapprovedEventTypes + case userRejected + case userRejectedChains + case userRejectedMethods + case userRejectedEvents } internal extension RejectionReason { func internalRepresentation() -> ReasonCode { switch self { - case .disapprovedChains: + case .userRejected: + return ReasonCode.userRejected + case .userRejectedChains: return ReasonCode.userRejectedChains - case .disapprovedMethods: + case .userRejectedMethods: return ReasonCode.userRejectedMethods - case .disapprovedEventTypes: + case .userRejectedEvents: return ReasonCode.userRejectedEvents } } diff --git a/Sources/WalletConnectSign/Types/ReasonCode.swift b/Sources/WalletConnectSign/Types/ReasonCode.swift index c80b00fd8..cdc4f8db5 100644 --- a/Sources/WalletConnectSign/Types/ReasonCode.swift +++ b/Sources/WalletConnectSign/Types/ReasonCode.swift @@ -1,6 +1,6 @@ -enum ReasonCode { +enum ReasonCode: Codable, Equatable { - enum Context: String { + enum Context: String, Codable { case pairing = "pairing" case session = "session" } @@ -10,14 +10,14 @@ enum ReasonCode { case invalidEvent case invalidUpdateRequest case invalidExtendRequest - case noContextWithTopic(context: Context, topic: String) + case noSessionForTopic // 3000 - (Unauthorized) case unauthorizedMethod(String) case unauthorizedEvent(String) case unauthorizedUpdateRequest case unauthorizedExtendRequest - case unauthorizedTargetChain(String) + case unauthorizedChain // 4001 - (EIP-1193) case userRejectedRequest @@ -43,13 +43,12 @@ enum ReasonCode { case .invalidEvent: return 1002 case .invalidUpdateRequest: return 1003 case .invalidExtendRequest: return 1004 - case .noContextWithTopic: return 1301 case .unauthorizedMethod: return 3001 case .unauthorizedEvent: return 3002 case .unauthorizedUpdateRequest: return 3003 case .unauthorizedExtendRequest: return 3004 - case .unauthorizedTargetChain: return 3005 + case .unauthorizedChain: return 3005 case .userRejectedRequest: return 4001 @@ -65,6 +64,8 @@ enum ReasonCode { case .unsupportedNamespaceKey: return 5104 case .userDisconnected: return 6000 + + case .noSessionForTopic: return 7001 } } @@ -78,8 +79,8 @@ enum ReasonCode { return "Invalid update namespace request" case .invalidExtendRequest: return "Invalid update expiry request" - case .noContextWithTopic(let context, let topic): - return "No matching \(context) with topic: \(topic)" + case .noSessionForTopic: + return "No matching session matching topic" case .unauthorizedMethod(let method): return "Unauthorized JSON-RPC method requested: \(method)" @@ -89,8 +90,8 @@ enum ReasonCode { return "Unauthorized update request" case .unauthorizedExtendRequest: return "Unauthorized extend request" - case .unauthorizedTargetChain(let chainId): - return "Unauthorized target chain id requested: \(chainId)" + case .unauthorizedChain: + return "Unauthorized target chain id requested" case .userRejectedRequest: return "User rejected request" From 3fa35b25a215172fb5632b37e7b01f43ec9401bd Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 9 Aug 2022 08:35:35 +0200 Subject: [PATCH 04/89] fix tests --- .../NonControllerSessionStateMachineTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/WalletConnectSignTests/NonControllerSessionStateMachineTests.swift b/Tests/WalletConnectSignTests/NonControllerSessionStateMachineTests.swift index 0edb20e8c..47937bd42 100644 --- a/Tests/WalletConnectSignTests/NonControllerSessionStateMachineTests.swift +++ b/Tests/WalletConnectSignTests/NonControllerSessionStateMachineTests.swift @@ -52,7 +52,7 @@ class NonControllerSessionStateMachineTests: XCTestCase { networkingInteractor.wcRequestPublisherSubject.send(WCRequestSubscriptionPayload.stubUpdateNamespaces(topic: "")) usleep(100) XCTAssertFalse(networkingInteractor.didRespondSuccess) - XCTAssertEqual(networkingInteractor.lastErrorCode, 1301) + XCTAssertEqual(networkingInteractor.lastErrorCode, 7001) } func testUpdateMethodPeerErrorUnauthorized() { From 71a64c937f92fcc1e8b46f6c3a3a3bd335ea543d Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 9 Aug 2022 10:28:23 +0200 Subject: [PATCH 05/89] run lint --- Example/IntegrationTests/Sign/SignClientTests.swift | 3 +-- Sources/Auth/Services/Common/SIWEMessageFormatter.swift | 4 ++-- Sources/Auth/Services/Wallet/AuthRequestSubscriber.swift | 2 +- Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift | 2 +- Tests/AuthTests/SIWEMessageFormatterTests.swift | 1 - 5 files changed, 5 insertions(+), 7 deletions(-) diff --git a/Example/IntegrationTests/Sign/SignClientTests.swift b/Example/IntegrationTests/Sign/SignClientTests.swift index 31204a636..cd79047f4 100644 --- a/Example/IntegrationTests/Sign/SignClientTests.swift +++ b/Example/IntegrationTests/Sign/SignClientTests.swift @@ -165,8 +165,7 @@ final class SignClientTests: XCTestCase { wallet.onSessionProposal = { [unowned self] proposal in Task(priority: .high) { do { - try await wallet.client.approve(proposalId: proposal.id, namespaces: sessionNamespaces) } - catch { + try await wallet.client.approve(proposalId: proposal.id, namespaces: sessionNamespaces) } catch { XCTFail("\(error)") } } diff --git a/Sources/Auth/Services/Common/SIWEMessageFormatter.swift b/Sources/Auth/Services/Common/SIWEMessageFormatter.swift index 733198e33..1861e2808 100644 --- a/Sources/Auth/Services/Common/SIWEMessageFormatter.swift +++ b/Sources/Auth/Services/Common/SIWEMessageFormatter.swift @@ -22,9 +22,9 @@ struct SIWEMessageFormatter: SIWEMessageFormatting { } } -fileprivate struct SIWEMessage: Equatable { +private struct SIWEMessage: Equatable { let domain: String - let uri: String //aud + let uri: String // aud let address: String let version: Int let nonce: String diff --git a/Sources/Auth/Services/Wallet/AuthRequestSubscriber.swift b/Sources/Auth/Services/Wallet/AuthRequestSubscriber.swift index a2a5f9b29..e4acc5b04 100644 --- a/Sources/Auth/Services/Wallet/AuthRequestSubscriber.swift +++ b/Sources/Auth/Services/Wallet/AuthRequestSubscriber.swift @@ -9,7 +9,7 @@ class AuthRequestSubscriber { private let address: String private var publishers = [AnyCancellable]() private let messageFormatter: SIWEMessageFormatting - var onRequest: ((_ id: RPCID, _ message: String)->())? + var onRequest: ((_ id: RPCID, _ message: String)->Void)? init(networkingInteractor: NetworkInteracting, logger: ConsoleLogging, diff --git a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift index caca3df48..229e2e698 100644 --- a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift @@ -207,7 +207,7 @@ private extension ApproveEngine { ) settlingProposal = proposal - Task(priority: .high) { + Task(priority: .high) { try? await networkingInteractor.subscribe(topic: sessionTopic) } } catch { diff --git a/Tests/AuthTests/SIWEMessageFormatterTests.swift b/Tests/AuthTests/SIWEMessageFormatterTests.swift index 98e9945b0..1b14c21a4 100644 --- a/Tests/AuthTests/SIWEMessageFormatterTests.swift +++ b/Tests/AuthTests/SIWEMessageFormatterTests.swift @@ -6,7 +6,6 @@ class SIWEMessageFormatterTests: XCTestCase { var sut: SIWEMessageFormatter! let address = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2" - override func setUp() { sut = SIWEMessageFormatter() } From 9856a19e65cc1dd41f817b6de9fb647f912055a0 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 10 Aug 2022 10:14:28 +0200 Subject: [PATCH 06/89] Update auth client --- Sources/Auth/AuthClient.swift | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/Sources/Auth/AuthClient.swift b/Sources/Auth/AuthClient.swift index a9c751104..b21e5700b 100644 --- a/Sources/Auth/AuthClient.swift +++ b/Sources/Auth/AuthClient.swift @@ -1,6 +1,20 @@ import Foundation +import JSONRPC +import Combine + +typealias RPCID = JSONRPC.RPCID class AuthClient { + 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, cacao: Cacao), Never>() + public var authResponsePublisher: AnyPublisher<(id: RPCID, cacao: Cacao), Never> { + authResponsePublisherSubject.eraseToAnyPublisher() + } + enum Errors: Error { case malformedPairingURI } @@ -16,16 +30,24 @@ class AuthClient { self.walletPairService = walletPairService } - func request(params: RequestParams) async throws -> String { + public func pair(uri: String) async throws { + guard let pairingURI = WalletConnectURI(string: uri) else { + throw Errors.malformedPairingURI + } + try await walletPairService.pair(pairingURI) + } + + public func request(_ params: RequestParams) async throws -> String { let uri = try await appPairService.create() try await appRequestService.request(params: params, topic: uri.topic) return uri.absoluteString } - func pair(uri: String) async throws { - guard let pairingURI = WalletConnectURI(string: uri) else { - throw Errors.malformedPairingURI - } - try await walletPairService.pair(pairingURI) + public func respond(_ params: RespondParams) async throws { + fatalError("not implemented") + } + + public func getPendingRequests() -> [Request] { + fatalError("not implemented") } } From 861ba777baacb08fb9b1343ba938c4ef0c3ed089 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 10 Aug 2022 10:27:36 +0200 Subject: [PATCH 07/89] update auth client rename services --- Sources/Auth/AuthClient.swift | 24 ++++++++++++++----- ...tService.swift => AppRequestService.swift} | 2 +- ...riber.swift => AppRespondSubscriber.swift} | 2 +- ...er.swift => WalletRequestSubscriber.swift} | 2 +- ...rvice.swift => WalletRespondService.swift} | 2 +- Sources/Auth/Types/RPCID.swift | 4 ++++ .../AuthTests/AuthRequstSubscriberTests.swift | 4 ++-- 7 files changed, 28 insertions(+), 12 deletions(-) rename Sources/Auth/Services/App/{AuthRequestService.swift => AppRequestService.swift} (97%) rename Sources/Auth/Services/App/{AuthRespondSubscriber.swift => AppRespondSubscriber.swift} (98%) rename Sources/Auth/Services/Wallet/{AuthRequestSubscriber.swift => WalletRequestSubscriber.swift} (97%) rename Sources/Auth/Services/Wallet/{AuthRespondService.swift => WalletRespondService.swift} (98%) create mode 100644 Sources/Auth/Types/RPCID.swift diff --git a/Sources/Auth/AuthClient.swift b/Sources/Auth/AuthClient.swift index b21e5700b..371d3a436 100644 --- a/Sources/Auth/AuthClient.swift +++ b/Sources/Auth/AuthClient.swift @@ -1,9 +1,6 @@ import Foundation -import JSONRPC import Combine -typealias RPCID = JSONRPC.RPCID - class AuthClient { private var authRequestPublisherSubject = PassthroughSubject<(id: RPCID, message: String), Never>() public var authRequestPublisher: AnyPublisher<(id: RPCID, message: String), Never> { @@ -20,14 +17,25 @@ class AuthClient { } private let appPairService: AppPairService - private let appRequestService: AuthRequestService + private let appRequestService: AppRequestService + private let appRespondSubscriber: AppRespondSubscriber private let walletPairService: WalletPairService + private let walletRequestSubscriber: WalletRequestSubscriber + private let walletRespondService: WalletRespondService - init(appPairService: AppPairService, appRequestService: AuthRequestService, walletPairService: WalletPairService) { + init(appPairService: AppPairService, + appRequestService: AppRequestService, + appRespondSubscriber: AppRespondSubscriber, + walletPairService: WalletPairService, + walletRequestSubscriber: WalletRequestSubscriber, + walletRespondService: WalletRespondService) { self.appPairService = appPairService self.appRequestService = appRequestService self.walletPairService = walletPairService + self.walletRequestSubscriber = walletRequestSubscriber + self.walletRespondService = walletRespondService + self.appRespondSubscriber = appRespondSubscriber } public func pair(uri: String) async throws { @@ -47,7 +55,11 @@ class AuthClient { fatalError("not implemented") } - public func getPendingRequests() -> [Request] { + public func getPendingRequests() -> [AuthRequest] { fatalError("not implemented") } } + +public struct AuthRequest: Equatable, Codable { + +} diff --git a/Sources/Auth/Services/App/AuthRequestService.swift b/Sources/Auth/Services/App/AppRequestService.swift similarity index 97% rename from Sources/Auth/Services/App/AuthRequestService.swift rename to Sources/Auth/Services/App/AppRequestService.swift index b82d5259b..1220a7155 100644 --- a/Sources/Auth/Services/App/AuthRequestService.swift +++ b/Sources/Auth/Services/App/AppRequestService.swift @@ -3,7 +3,7 @@ import WalletConnectUtils import WalletConnectKMS import JSONRPC -actor AuthRequestService { +actor AppRequestService { private let networkingInteractor: NetworkInteracting private let appMetadata: AppMetadata private let kms: KeyManagementService diff --git a/Sources/Auth/Services/App/AuthRespondSubscriber.swift b/Sources/Auth/Services/App/AppRespondSubscriber.swift similarity index 98% rename from Sources/Auth/Services/App/AuthRespondSubscriber.swift rename to Sources/Auth/Services/App/AppRespondSubscriber.swift index a6f1f8b57..d8695b98a 100644 --- a/Sources/Auth/Services/App/AuthRespondSubscriber.swift +++ b/Sources/Auth/Services/App/AppRespondSubscriber.swift @@ -3,7 +3,7 @@ import Foundation import WalletConnectUtils import JSONRPC -class AuthRespondSubscriber { +class AppRespondSubscriber { private let networkingInteractor: NetworkInteracting private let logger: ConsoleLogging private let rpcHistory: RPCHistory diff --git a/Sources/Auth/Services/Wallet/AuthRequestSubscriber.swift b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift similarity index 97% rename from Sources/Auth/Services/Wallet/AuthRequestSubscriber.swift rename to Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift index e4acc5b04..fb5510d1f 100644 --- a/Sources/Auth/Services/Wallet/AuthRequestSubscriber.swift +++ b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift @@ -3,7 +3,7 @@ import Foundation import WalletConnectUtils import JSONRPC -class AuthRequestSubscriber { +class WalletRequestSubscriber { private let networkingInteractor: NetworkInteracting private let logger: ConsoleLogging private let address: String diff --git a/Sources/Auth/Services/Wallet/AuthRespondService.swift b/Sources/Auth/Services/Wallet/WalletRespondService.swift similarity index 98% rename from Sources/Auth/Services/Wallet/AuthRespondService.swift rename to Sources/Auth/Services/Wallet/WalletRespondService.swift index 006459d37..f62e852da 100644 --- a/Sources/Auth/Services/Wallet/AuthRespondService.swift +++ b/Sources/Auth/Services/Wallet/WalletRespondService.swift @@ -3,7 +3,7 @@ import WalletConnectKMS import JSONRPC import WalletConnectUtils -actor AuthRespondService { +actor WalletRespondService { enum Errors: Error { case recordForIdNotFound case malformedAuthRequestParams diff --git a/Sources/Auth/Types/RPCID.swift b/Sources/Auth/Types/RPCID.swift new file mode 100644 index 000000000..ca206cf81 --- /dev/null +++ b/Sources/Auth/Types/RPCID.swift @@ -0,0 +1,4 @@ +import Foundation +import JSONRPC + +typealias RPCID = JSONRPC.RPCID diff --git a/Tests/AuthTests/AuthRequstSubscriberTests.swift b/Tests/AuthTests/AuthRequstSubscriberTests.swift index 990a9cb4f..dbf7bba20 100644 --- a/Tests/AuthTests/AuthRequstSubscriberTests.swift +++ b/Tests/AuthTests/AuthRequstSubscriberTests.swift @@ -8,14 +8,14 @@ import JSONRPC class AuthRequstSubscriberTests: XCTestCase { var networkingInteractor: NetworkingInteractorMock! - var sut: AuthRequestSubscriber! + var sut: WalletRequestSubscriber! var messageFormatter: SIWEMessageFormatterMock! let defaultTimeout: TimeInterval = 0.01 override func setUp() { networkingInteractor = NetworkingInteractorMock() messageFormatter = SIWEMessageFormatterMock() - sut = AuthRequestSubscriber(networkingInteractor: networkingInteractor, + sut = WalletRequestSubscriber(networkingInteractor: networkingInteractor, logger: ConsoleLoggerMock(), messageFormatter: messageFormatter, address: "") } From 66c756e0ac317c96922e0dbb429cac8cb81dd3d5 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 10 Aug 2022 11:39:12 +0200 Subject: [PATCH 08/89] update Auth client --- Sources/Auth/AuthClient.swift | 38 +++++++++++++------ .../Auth/Services/Common/CacaoFormatter.swift | 4 +- .../Wallet/WalletRespondService.swift | 4 +- Sources/Auth/Types/Aliases/Account.swift | 4 ++ .../Types/{ => Aliases}/AppMetadata.swift | 0 Sources/Auth/Types/Aliases/RPCID.swift | 4 ++ .../{ => Aliases}/WalletConnectURI.swift | 0 Sources/Auth/Types/Public/AuthRequest.swift | 7 ++++ Sources/Auth/Types/RPCID.swift | 4 -- Sources/WalletConnectUtils/RPCHistory.swift | 4 ++ 10 files changed, 49 insertions(+), 20 deletions(-) create mode 100644 Sources/Auth/Types/Aliases/Account.swift rename Sources/Auth/Types/{ => Aliases}/AppMetadata.swift (100%) create mode 100644 Sources/Auth/Types/Aliases/RPCID.swift rename Sources/Auth/Types/{ => Aliases}/WalletConnectURI.swift (100%) create mode 100644 Sources/Auth/Types/Public/AuthRequest.swift delete mode 100644 Sources/Auth/Types/RPCID.swift diff --git a/Sources/Auth/AuthClient.swift b/Sources/Auth/AuthClient.swift index 371d3a436..7a3bfd07c 100644 --- a/Sources/Auth/AuthClient.swift +++ b/Sources/Auth/AuthClient.swift @@ -1,7 +1,13 @@ import Foundation import Combine +import JSONRPC +import WalletConnectUtils class AuthClient { + enum Errors: Error { + case malformedPairingURI + case UnknownWalletAddress + } private var authRequestPublisherSubject = PassthroughSubject<(id: RPCID, message: String), Never>() public var authRequestPublisher: AnyPublisher<(id: RPCID, message: String), Never> { authRequestPublisherSubject.eraseToAnyPublisher() @@ -11,10 +17,7 @@ class AuthClient { public var authResponsePublisher: AnyPublisher<(id: RPCID, cacao: Cacao), Never> { authResponsePublisherSubject.eraseToAnyPublisher() } - - enum Errors: Error { - case malformedPairingURI - } + private let rpcHistory: RPCHistory private let appPairService: AppPairService private let appRequestService: AppRequestService @@ -24,18 +27,24 @@ class AuthClient { private let walletRequestSubscriber: WalletRequestSubscriber private let walletRespondService: WalletRespondService + private var account: Account? + init(appPairService: AppPairService, appRequestService: AppRequestService, appRespondSubscriber: AppRespondSubscriber, walletPairService: WalletPairService, walletRequestSubscriber: WalletRequestSubscriber, - walletRespondService: WalletRespondService) { + walletRespondService: WalletRespondService, + account: Account, + rpcHistory: RPCHistory) { self.appPairService = appPairService self.appRequestService = appRequestService self.walletPairService = walletPairService self.walletRequestSubscriber = walletRequestSubscriber self.walletRespondService = walletRespondService self.appRespondSubscriber = appRespondSubscriber + self.account = account + self.rpcHistory = rpcHistory } public func pair(uri: String) async throws { @@ -52,14 +61,19 @@ class AuthClient { } public func respond(_ params: RespondParams) async throws { - fatalError("not implemented") + guard let account = account else { throw Errors.UnknownWalletAddress } + try await walletRespondService.respond(respondParams: params, account: account) } - public func getPendingRequests() -> [AuthRequest] { - fatalError("not implemented") + public func getPendingRequests() throws -> [AuthRequest] { + guard let account = account else { throw Errors.UnknownWalletAddress } + let pendingRequests: [AuthRequest] = rpcHistory.getPending() + .filter {$0.request.method == "wc_authRequest"} + .compactMap { + guard let params = try? $0.request.params?.get(AuthRequestParams.self) else {return nil} + let message = SIWEMessageFormatter().formatMessage(from: params.payloadParams, address: account.address) + return AuthRequest(id: $0.request.id!, message: message) + } + return pendingRequests } } - -public struct AuthRequest: Equatable, Codable { - -} diff --git a/Sources/Auth/Services/Common/CacaoFormatter.swift b/Sources/Auth/Services/Common/CacaoFormatter.swift index 43ddb9058..c35286b87 100644 --- a/Sources/Auth/Services/Common/CacaoFormatter.swift +++ b/Sources/Auth/Services/Common/CacaoFormatter.swift @@ -2,11 +2,11 @@ import Foundation import WalletConnectUtils protocol CacaoFormatting { - func format(_ request: AuthRequestParams, _ signature: CacaoSignature, _ issuer: Account) -> Cacao + func format(_ request: AuthRequestParams, _ signature: CacaoSignature, _ account: Account) -> Cacao } class CacaoFormatter: CacaoFormatting { - func format(_ request: AuthRequestParams, _ signature: CacaoSignature, _ issuer: Account) -> Cacao { + func format(_ request: AuthRequestParams, _ signature: CacaoSignature, _ account: Account) -> Cacao { fatalError("not implemented") } } diff --git a/Sources/Auth/Services/Wallet/WalletRespondService.swift b/Sources/Auth/Services/Wallet/WalletRespondService.swift index f62e852da..81a37db03 100644 --- a/Sources/Auth/Services/Wallet/WalletRespondService.swift +++ b/Sources/Auth/Services/Wallet/WalletRespondService.swift @@ -23,7 +23,7 @@ actor WalletRespondService { self.rpcHistory = rpcHistory } - func respond(respondParams: RespondParams, issuer: Account) async throws { + func respond(respondParams: RespondParams, account: Account) async throws { guard let request = rpcHistory.get(recordId: RPCID(respondParams.id))?.request else { throw Errors.recordForIdNotFound } guard let authRequestParams = try? request.params?.get(AuthRequestParams.self) else { throw Errors.malformedAuthRequestParams } @@ -33,7 +33,7 @@ actor WalletRespondService { let agreementKeys = try kms.performKeyAgreement(selfPublicKey: selfPubKey, peerPublicKey: peerPubKey) try kms.setAgreementSecret(agreementKeys, topic: responseTopic) - let cacao = CacaoFormatter().format(authRequestParams, respondParams.signature, issuer) + let cacao = CacaoFormatter().format(authRequestParams, respondParams.signature, account) let response = RPCResponse(id: request.id!, result: cacao) try await networkingInteractor.respond(topic: respondParams.topic, response: response, tag: AuthResponseParams.tag, envelopeType: .type1(pubKey: selfPubKey.rawRepresentation)) diff --git a/Sources/Auth/Types/Aliases/Account.swift b/Sources/Auth/Types/Aliases/Account.swift new file mode 100644 index 000000000..84759e639 --- /dev/null +++ b/Sources/Auth/Types/Aliases/Account.swift @@ -0,0 +1,4 @@ +import WalletConnectUtils +import Foundation + +typealias Account = WalletConnectUtils.Account diff --git a/Sources/Auth/Types/AppMetadata.swift b/Sources/Auth/Types/Aliases/AppMetadata.swift similarity index 100% rename from Sources/Auth/Types/AppMetadata.swift rename to Sources/Auth/Types/Aliases/AppMetadata.swift diff --git a/Sources/Auth/Types/Aliases/RPCID.swift b/Sources/Auth/Types/Aliases/RPCID.swift new file mode 100644 index 000000000..51cb31cdd --- /dev/null +++ b/Sources/Auth/Types/Aliases/RPCID.swift @@ -0,0 +1,4 @@ +import Foundation +import JSONRPC + +public typealias RPCID = JSONRPC.RPCID diff --git a/Sources/Auth/Types/WalletConnectURI.swift b/Sources/Auth/Types/Aliases/WalletConnectURI.swift similarity index 100% rename from Sources/Auth/Types/WalletConnectURI.swift rename to Sources/Auth/Types/Aliases/WalletConnectURI.swift diff --git a/Sources/Auth/Types/Public/AuthRequest.swift b/Sources/Auth/Types/Public/AuthRequest.swift new file mode 100644 index 000000000..f431ccb4d --- /dev/null +++ b/Sources/Auth/Types/Public/AuthRequest.swift @@ -0,0 +1,7 @@ +import Foundation + +public struct AuthRequest: Equatable, Codable { + public let id: RPCID + /// EIP-4361: Sign-In with Ethereum message + public let message: String +} diff --git a/Sources/Auth/Types/RPCID.swift b/Sources/Auth/Types/RPCID.swift deleted file mode 100644 index ca206cf81..000000000 --- a/Sources/Auth/Types/RPCID.swift +++ /dev/null @@ -1,4 +0,0 @@ -import Foundation -import JSONRPC - -typealias RPCID = JSONRPC.RPCID diff --git a/Sources/WalletConnectUtils/RPCHistory.swift b/Sources/WalletConnectUtils/RPCHistory.swift index 8112cacc3..d0707688d 100644 --- a/Sources/WalletConnectUtils/RPCHistory.swift +++ b/Sources/WalletConnectUtils/RPCHistory.swift @@ -64,4 +64,8 @@ public final class RPCHistory { } } } + + public func getPending() -> [Record] { + storage.getAll().filter {$0.response == nil} + } } From a3518d986b5d218c1649fe819fc510d00584e353 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 10 Aug 2022 13:28:24 +0200 Subject: [PATCH 09/89] update auth client --- Sources/Auth/AuthClient.swift | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Sources/Auth/AuthClient.swift b/Sources/Auth/AuthClient.swift index 7a3bfd07c..69338c7cf 100644 --- a/Sources/Auth/AuthClient.swift +++ b/Sources/Auth/AuthClient.swift @@ -45,6 +45,7 @@ class AuthClient { self.appRespondSubscriber = appRespondSubscriber self.account = account self.rpcHistory = rpcHistory + setUpPublishers() } public func pair(uri: String) async throws { @@ -76,4 +77,15 @@ class AuthClient { } return pendingRequests } + + private func setUpPublishers() { + appRespondSubscriber.onResponse = { [unowned self] (id, cacao) in + authResponsePublisherSubject.send((id, cacao)) + } + + walletRequestSubscriber.onRequest = { [unowned self] (id, message) in + authRequestPublisherSubject.send((id, message)) + } + } + } From d4e115682c3d139321647a9ae97ed0ba11380e0e Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 10 Aug 2022 13:28:58 +0200 Subject: [PATCH 10/89] remove uikit import from sign client --- Sources/WalletConnectSign/Sign/SignClient.swift | 3 --- 1 file changed, 3 deletions(-) diff --git a/Sources/WalletConnectSign/Sign/SignClient.swift b/Sources/WalletConnectSign/Sign/SignClient.swift index 1dc88cae2..43df10383 100644 --- a/Sources/WalletConnectSign/Sign/SignClient.swift +++ b/Sources/WalletConnectSign/Sign/SignClient.swift @@ -3,9 +3,6 @@ import WalletConnectRelay import WalletConnectUtils import WalletConnectKMS import Combine -#if os(iOS) -import UIKit -#endif /// An Object that expose public API to provide interactions with WalletConnect SDK /// From 146192c3973d3ec38aff9684e3590a41e5c034b3 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 10 Aug 2022 13:35:58 +0200 Subject: [PATCH 11/89] Add cleanup service --- Sources/Auth/AuthClient.swift | 16 +++++++++++++-- .../Auth/Services/Common/CleanupService.swift | 20 +++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 Sources/Auth/Services/Common/CleanupService.swift diff --git a/Sources/Auth/AuthClient.swift b/Sources/Auth/AuthClient.swift index 69338c7cf..89445d071 100644 --- a/Sources/Auth/AuthClient.swift +++ b/Sources/Auth/AuthClient.swift @@ -26,6 +26,7 @@ class AuthClient { private let walletPairService: WalletPairService private let walletRequestSubscriber: WalletRequestSubscriber private let walletRespondService: WalletRespondService + private let cleanupService: CleanupService private var account: Account? @@ -36,7 +37,8 @@ class AuthClient { walletRequestSubscriber: WalletRequestSubscriber, walletRespondService: WalletRespondService, account: Account, - rpcHistory: RPCHistory) { + rpcHistory: RPCHistory, + cleanupService: CleanupService) { self.appPairService = appPairService self.appRequestService = appRequestService self.walletPairService = walletPairService @@ -45,6 +47,8 @@ class AuthClient { self.appRespondSubscriber = appRespondSubscriber self.account = account self.rpcHistory = rpcHistory + self.cleanupService = cleanupService + setUpPublishers() } @@ -78,6 +82,15 @@ class AuthClient { return pendingRequests } +#if DEBUG + /// Delete all stored data sach as: pairings, sessions, keys + /// + /// - Note: Doesn't unsubscribe from topics + public func cleanup() throws { + try cleanupService.cleanup() + } +#endif + private func setUpPublishers() { appRespondSubscriber.onResponse = { [unowned self] (id, cacao) in authResponsePublisherSubject.send((id, cacao)) @@ -87,5 +100,4 @@ class AuthClient { authRequestPublisherSubject.send((id, message)) } } - } diff --git a/Sources/Auth/Services/Common/CleanupService.swift b/Sources/Auth/Services/Common/CleanupService.swift new file mode 100644 index 000000000..dac2b63c5 --- /dev/null +++ b/Sources/Auth/Services/Common/CleanupService.swift @@ -0,0 +1,20 @@ +import Foundation +import WalletConnectKMS +import WalletConnectUtils +import WalletConnectPairing + +final class CleanupService { + + private let pairingStore: WCPairingStorage + private let kms: KeyManagementServiceProtocol + + init(pairingStore: WCPairingStorage, kms: KeyManagementServiceProtocol, sessionToPairingTopic: CodableStore) { + self.pairingStore = pairingStore + self.kms = kms + } + + func cleanup() throws { + pairingStore.deleteAll() + try kms.deleteAll() + } +} From 94dd0c08a501730d8756541c1b86e0e09f21ce99 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 10 Aug 2022 13:36:58 +0200 Subject: [PATCH 12/89] update auth client - account optional --- Sources/Auth/AuthClient.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Auth/AuthClient.swift b/Sources/Auth/AuthClient.swift index 89445d071..fd3fbf115 100644 --- a/Sources/Auth/AuthClient.swift +++ b/Sources/Auth/AuthClient.swift @@ -36,7 +36,7 @@ class AuthClient { walletPairService: WalletPairService, walletRequestSubscriber: WalletRequestSubscriber, walletRespondService: WalletRespondService, - account: Account, + account: Account?, rpcHistory: RPCHistory, cleanupService: CleanupService) { self.appPairService = appPairService From ce73441cfa82477dbb4fc9be3378d5ac85c3d743 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 10 Aug 2022 13:48:29 +0200 Subject: [PATCH 13/89] Update request method --- Sources/Auth/AuthClient.swift | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/Sources/Auth/AuthClient.swift b/Sources/Auth/AuthClient.swift index fd3fbf115..9f73d464d 100644 --- a/Sources/Auth/AuthClient.swift +++ b/Sources/Auth/AuthClient.swift @@ -2,11 +2,13 @@ import Foundation import Combine import JSONRPC import WalletConnectUtils +import WalletConnectPairing class AuthClient { enum Errors: Error { case malformedPairingURI - case UnknownWalletAddress + case unknownWalletAddress + case noPairingMatchingTopic } private var authRequestPublisherSubject = PassthroughSubject<(id: RPCID, message: String), Never>() public var authRequestPublisher: AnyPublisher<(id: RPCID, message: String), Never> { @@ -27,6 +29,8 @@ class AuthClient { private let walletRequestSubscriber: WalletRequestSubscriber private let walletRespondService: WalletRespondService private let cleanupService: CleanupService + private let pairingStorage: WCPairingStorage + public let logger: ConsoleLogging private var account: Account? @@ -38,7 +42,9 @@ class AuthClient { walletRespondService: WalletRespondService, account: Account?, rpcHistory: RPCHistory, - cleanupService: CleanupService) { + cleanupService: CleanupService, + logger: ConsoleLogging, + pairingStorage: WCPairingStorage) { self.appPairService = appPairService self.appRequestService = appRequestService self.walletPairService = walletPairService @@ -48,6 +54,8 @@ class AuthClient { self.account = account self.rpcHistory = rpcHistory self.cleanupService = cleanupService + self.logger = logger + self.pairingStorage = pairingStorage setUpPublishers() } @@ -59,19 +67,29 @@ class AuthClient { try await walletPairService.pair(pairingURI) } - public func request(_ params: RequestParams) async throws -> String { - let uri = try await appPairService.create() - try await appRequestService.request(params: params, topic: uri.topic) - return uri.absoluteString + public func request(_ params: RequestParams, topic: String?) async throws -> String? { + logger.debug("Requesting Authentication") + if let topic = topic { + guard pairingStorage.hasPairing(forTopic: topic) else { + throw Errors.noPairingMatchingTopic + } + logger.debug("Requesting on existing pairing") + try await appRequestService.request(params: params, topic: topic) + return nil + } else { + let uri = try await appPairService.create() + try await appRequestService.request(params: params, topic: uri.topic) + return uri.absoluteString + } } public func respond(_ params: RespondParams) async throws { - guard let account = account else { throw Errors.UnknownWalletAddress } + guard let account = account else { throw Errors.unknownWalletAddress } try await walletRespondService.respond(respondParams: params, account: account) } public func getPendingRequests() throws -> [AuthRequest] { - guard let account = account else { throw Errors.UnknownWalletAddress } + guard let account = account else { throw Errors.unknownWalletAddress } let pendingRequests: [AuthRequest] = rpcHistory.getPending() .filter {$0.request.method == "wc_authRequest"} .compactMap { From 2bb37432c97bcd68f7702d2dedeb1ddf2e2efa3a Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 11 Aug 2022 09:00:21 +0200 Subject: [PATCH 14/89] Add pending requests provider split request method into two --- Sources/Auth/AuthClient.swift | 40 ++++++++----------- .../Wallet/PendingRequestsProvider.swift | 22 ++++++++++ 2 files changed, 38 insertions(+), 24 deletions(-) create mode 100644 Sources/Auth/Services/Wallet/PendingRequestsProvider.swift diff --git a/Sources/Auth/AuthClient.swift b/Sources/Auth/AuthClient.swift index 9f73d464d..6ea7bc053 100644 --- a/Sources/Auth/AuthClient.swift +++ b/Sources/Auth/AuthClient.swift @@ -1,6 +1,5 @@ import Foundation import Combine -import JSONRPC import WalletConnectUtils import WalletConnectPairing @@ -19,7 +18,6 @@ class AuthClient { public var authResponsePublisher: AnyPublisher<(id: RPCID, cacao: Cacao), Never> { authResponsePublisherSubject.eraseToAnyPublisher() } - private let rpcHistory: RPCHistory private let appPairService: AppPairService private let appRequestService: AppRequestService @@ -30,6 +28,7 @@ class AuthClient { private let walletRespondService: WalletRespondService private let cleanupService: CleanupService private let pairingStorage: WCPairingStorage + private let pendingRequestsProvider: PendingRequestsProvider public let logger: ConsoleLogging private var account: Account? @@ -41,7 +40,7 @@ class AuthClient { walletRequestSubscriber: WalletRequestSubscriber, walletRespondService: WalletRespondService, account: Account?, - rpcHistory: RPCHistory, + pendingRequestsProvider: PendingRequestsProvider, cleanupService: CleanupService, logger: ConsoleLogging, pairingStorage: WCPairingStorage) { @@ -52,7 +51,7 @@ class AuthClient { self.walletRespondService = walletRespondService self.appRespondSubscriber = appRespondSubscriber self.account = account - self.rpcHistory = rpcHistory + self.pendingRequestsProvider = pendingRequestsProvider self.cleanupService = cleanupService self.logger = logger self.pairingStorage = pairingStorage @@ -67,20 +66,19 @@ class AuthClient { try await walletPairService.pair(pairingURI) } - public func request(_ params: RequestParams, topic: String?) async throws -> String? { + public func request(_ params: RequestParams) async throws -> String { logger.debug("Requesting Authentication") - if let topic = topic { - guard pairingStorage.hasPairing(forTopic: topic) else { - throw Errors.noPairingMatchingTopic - } - logger.debug("Requesting on existing pairing") - try await appRequestService.request(params: params, topic: topic) - return nil - } else { - let uri = try await appPairService.create() - try await appRequestService.request(params: params, topic: uri.topic) - return uri.absoluteString + let uri = try await appPairService.create() + try await appRequestService.request(params: params, topic: uri.topic) + return uri.absoluteString + } + + public func request(_ params: RequestParams, topic: String) async throws { + logger.debug("Requesting Authentication on existing pairing") + guard pairingStorage.hasPairing(forTopic: topic) else { + throw Errors.noPairingMatchingTopic } + try await appRequestService.request(params: params, topic: topic) } public func respond(_ params: RespondParams) async throws { @@ -90,14 +88,7 @@ class AuthClient { public func getPendingRequests() throws -> [AuthRequest] { guard let account = account else { throw Errors.unknownWalletAddress } - let pendingRequests: [AuthRequest] = rpcHistory.getPending() - .filter {$0.request.method == "wc_authRequest"} - .compactMap { - guard let params = try? $0.request.params?.get(AuthRequestParams.self) else {return nil} - let message = SIWEMessageFormatter().formatMessage(from: params.payloadParams, address: account.address) - return AuthRequest(id: $0.request.id!, message: message) - } - return pendingRequests + return try pendingRequestsProvider.getPendingRequests(account: account) } #if DEBUG @@ -119,3 +110,4 @@ class AuthClient { } } } + diff --git a/Sources/Auth/Services/Wallet/PendingRequestsProvider.swift b/Sources/Auth/Services/Wallet/PendingRequestsProvider.swift new file mode 100644 index 000000000..16fe22128 --- /dev/null +++ b/Sources/Auth/Services/Wallet/PendingRequestsProvider.swift @@ -0,0 +1,22 @@ +import Foundation +import JSONRPC +import WalletConnectUtils + +class PendingRequestsProvider { + private let rpcHistory: RPCHistory + + init(rpcHistory: RPCHistory) { + self.rpcHistory = rpcHistory + } + + public func getPendingRequests(account: Account) throws -> [AuthRequest] { + let pendingRequests: [AuthRequest] = rpcHistory.getPending() + .filter {$0.request.method == "wc_authRequest"} + .compactMap { + guard let params = try? $0.request.params?.get(AuthRequestParams.self) else {return nil} + let message = SIWEMessageFormatter().formatMessage(from: params.payloadParams, address: account.address) + return AuthRequest(id: $0.request.id!, message: message) + } + return pendingRequests + } +} From 0913eff4cc1e948c1a22b06c4adf142951cc1b5e Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 11 Aug 2022 09:02:18 +0200 Subject: [PATCH 15/89] remove do catch block --- .../Auth/Services/Wallet/WalletRequestSubscriber.swift | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift index fb5510d1f..54819c1da 100644 --- a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift +++ b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift @@ -29,13 +29,9 @@ class WalletRequestSubscriber { logger.debug("Malformed auth request params") return } - do { - let message = try messageFormatter.formatMessage(from: authRequestParams.payloadParams, address: address) - guard let requestId = subscriptionPayload.request.id else { return } - onRequest?(requestId, message) - } catch { - logger.debug(error) - } + let message = messageFormatter.formatMessage(from: authRequestParams.payloadParams, address: address) + guard let requestId = subscriptionPayload.request.id else { return } + onRequest?(requestId, message) }.store(in: &publishers) } From f2bfd9d9a09b651dd19f0690b4e84b11bb3db367 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Wed, 3 Aug 2022 18:31:31 +0300 Subject: [PATCH 16/89] CacaoSigner --- .../xcshareddata/swiftpm/Package.resolved | 9 --- Package.swift | 18 ++++-- Sources/Auth/Extensions/Data+Keccak256.swift | 9 +++ .../Auth/Services/Signer/CocoaSigner.swift | 37 +++++++++++++ Sources/Auth/Services/Signer/Signer.swift | 25 +++++++++ .../Auth/Services/Signer/SignerAddress.swift | 30 ++++++++++ Sources/Auth/Types/Cacao/CacaoPayload.swift | 4 +- Tests/AuthTests/CacaoSignerTests.swift | 50 +++++++++++++++++ Tests/AuthTests/SignerTests.swift | 55 +++++++++++++++++++ 9 files changed, 222 insertions(+), 15 deletions(-) create mode 100644 Sources/Auth/Extensions/Data+Keccak256.swift create mode 100644 Sources/Auth/Services/Signer/CocoaSigner.swift create mode 100644 Sources/Auth/Services/Signer/Signer.swift create mode 100644 Sources/Auth/Services/Signer/SignerAddress.swift create mode 100644 Tests/AuthTests/CacaoSignerTests.swift create mode 100644 Tests/AuthTests/SignerTests.swift diff --git a/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index f31ed48ca..7faa87840 100644 --- a/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -28,15 +28,6 @@ "version": "6.17.0" } }, - { - "package": "secp256k1", - "repositoryURL": "https://github.com/Boilertalk/secp256k1.swift.git", - "state": { - "branch": null, - "revision": "45e458ec3be46cf0a6eb68bc947f797852dc65d8", - "version": "0.1.6" - } - }, { "package": "Starscream", "repositoryURL": "https://github.com/daltoniam/Starscream", diff --git a/Package.swift b/Package.swift index a94e15017..5ef4cdd86 100644 --- a/Package.swift +++ b/Package.swift @@ -17,10 +17,13 @@ let package = Package( name: "WalletConnectChat", targets: ["Chat"]), .library( - name: "WalletConnectPairing", - targets: ["WalletConnectPairing"]) + name: "WalletConnectAuth", + targets: ["Auth"]), + ], + dependencies: [ + .package(url: "https://github.com/GigaBitcoin/secp256k1.swift.git", .upToNextMajor(from: "0.6.0")), + .package(url: "https://github.com/krzyzanowskim/CryptoSwift", .upToNextMajor(from: "1.5.1")) ], - dependencies: [], targets: [ .target( name: "WalletConnectSign", @@ -32,7 +35,14 @@ let package = Package( path: "Sources/Chat"), .target( name: "Auth", - dependencies: ["WalletConnectRelay", "WalletConnectUtils", "WalletConnectKMS", "WalletConnectPairing"], + dependencies: [ + "WalletConnectRelay", + "WalletConnectUtils", + "WalletConnectKMS", + "WalletConnectPairing", + .product(name: "CryptoSwift", package: "CryptoSwift"), + .product(name: "secp256k1", package: "secp256k1.swift"), + ], path: "Sources/Auth"), .target( name: "WalletConnectRelay", diff --git a/Sources/Auth/Extensions/Data+Keccak256.swift b/Sources/Auth/Extensions/Data+Keccak256.swift new file mode 100644 index 000000000..1bfbc1b8b --- /dev/null +++ b/Sources/Auth/Extensions/Data+Keccak256.swift @@ -0,0 +1,9 @@ +import Foundation +import CryptoSwift + +extension Data { + + var keccak256: Data { + return sha3(.keccak256) + } +} diff --git a/Sources/Auth/Services/Signer/CocoaSigner.swift b/Sources/Auth/Services/Signer/CocoaSigner.swift new file mode 100644 index 000000000..82524e86c --- /dev/null +++ b/Sources/Auth/Services/Signer/CocoaSigner.swift @@ -0,0 +1,37 @@ +import Foundation + +protocol CacaoSignerKeystore { + var privateKey: Data { get async } +} + +actor CacaoSigner { + enum Errors: Error { + case signatureCorrupted + case signatureValidationFailed + } + + private let signer: Signer + private let keystore: CacaoSignerKeystore + + init(signer: Signer, keystore: CacaoSignerKeystore) { + self.signer = signer + self.keystore = keystore + } + + func sign(payload: CacaoPayload) async throws -> CacaoSignature { + let message = try JSONEncoder().encode(payload) + let signature = try await signer.sign(message: message, with: keystore.privateKey) + return CacaoSignature(t: "eip191", s: signature.toHexString(), m: String()) + } + + func verify(signature: CacaoSignature, payload: CacaoPayload) async throws { + let message = try JSONEncoder().encode(payload) + let address = try SignerAddress.from(iss: payload.iss) + + guard let signature = signature.s.data(using: .utf8) + else { throw Errors.signatureCorrupted } + + guard try signer.isValid(signature: signature, message: message, address: address) + else { throw Errors.signatureValidationFailed } + } +} diff --git a/Sources/Auth/Services/Signer/Signer.swift b/Sources/Auth/Services/Signer/Signer.swift new file mode 100644 index 000000000..b6bec4e31 --- /dev/null +++ b/Sources/Auth/Services/Signer/Signer.swift @@ -0,0 +1,25 @@ +import Foundation +import secp256k1 + +fileprivate typealias Signature = secp256k1.Recovery.ECDSASignature +fileprivate typealias RecoveryPublicKey = secp256k1.Recovery.PublicKey +fileprivate typealias SigningPublicKey = secp256k1.Signing.PublicKey +fileprivate typealias SigningPrivateKey = secp256k1.Signing.PrivateKey + +struct Signer { + + func sign(message: Data, with privateKey: Data) throws -> Data { + let key = try SigningPrivateKey(rawRepresentation: privateKey) + let signature = try key.ecdsa.recoverableSignature(for: message) + return signature.rawRepresentation + } + + func isValid(signature: Data, message: Data, address: String) throws -> Bool { + let recoverySignature = try Signature(rawRepresentation: signature) + let publicKey = try RecoveryPublicKey(message, signature: recoverySignature) + let validator = try SigningPublicKey(rawRepresentation: publicKey.rawRepresentation, format: .compressed) + let isValid = try validator.ecdsa.isValidSignature(recoverySignature.normalize, for: message) + let recoveredAddress = try SignerAddress.from(publicKey: publicKey.rawRepresentation) + return isValid && recoveredAddress == address + } +} diff --git a/Sources/Auth/Services/Signer/SignerAddress.swift b/Sources/Auth/Services/Signer/SignerAddress.swift new file mode 100644 index 000000000..6db970b16 --- /dev/null +++ b/Sources/Auth/Services/Signer/SignerAddress.swift @@ -0,0 +1,30 @@ +import Foundation +import WalletConnectUtils + +struct SignerAddress { + + static let didPrefix: String = "did:pkh" + static let lenght: Int = 20 + + enum Errors: Error { + case invalidPublicKey + case invalidDidPkh + } + + static func from(iss: String) throws -> String { + guard iss.starts(with: didPrefix) + else { throw Errors.invalidDidPkh } + + guard let string = iss.components(separatedBy: didPrefix + ":").last, let account = Account(string) + else { throw Errors.invalidDidPkh } + + return account.address.lowercased() + } + + static func from(publicKey: Data) throws -> String { + guard publicKey.count >= lenght + else { throw Errors.invalidPublicKey } + + return "0x" + publicKey.keccak256.suffix(SignerAddress.lenght).toHexString() + } +} diff --git a/Sources/Auth/Types/Cacao/CacaoPayload.swift b/Sources/Auth/Types/Cacao/CacaoPayload.swift index c339bd18d..b38a3206c 100644 --- a/Sources/Auth/Types/Cacao/CacaoPayload.swift +++ b/Sources/Auth/Types/Cacao/CacaoPayload.swift @@ -4,12 +4,12 @@ struct CacaoPayload: Codable, Equatable { let iss: String let domain: String let aud: String - let version: String + let version: Int let nonce: String let iat: String let nbf: String let exp: String let statement: String let requestId: String - let resources: String + let resources: [String] } diff --git a/Tests/AuthTests/CacaoSignerTests.swift b/Tests/AuthTests/CacaoSignerTests.swift new file mode 100644 index 000000000..b83e27661 --- /dev/null +++ b/Tests/AuthTests/CacaoSignerTests.swift @@ -0,0 +1,50 @@ +import Foundation +import XCTest +@testable import Auth +import TestingUtils + +class CacaoSignerTest: XCTestCase { + + let payload = CacaoPayload( + iss: "did:pkh:eip155:1:0xBAc675C310721717Cd4A37F6cbeA1F081b1C2a07", + domain: "localhost:3000", + aud: "http://localhost:3000/login", + version: 1, + nonce: "328917", + iat: "2022-03-10T17:09:21.481+03:00", + nbf: "2022-03-10T17:09:21.481+03:00", + exp: "2022-03-10T18:09:21.481+03:00", + statement: "I accept the ServiceOrg Terms of Service: https://service.org/tos", + requestId: "request-id-random", + resources: [ + "ipfs://bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq", + "https://example.com/my-web2-claim.json" + ] + ) + + let sig = CacaoSignature(t: "eip191", s: "5ccb134ad3d874cbb40a32b399549cd32c953dc5dc87dc64624a3e3dc0684d7d4833043dd7e9f4a6894853f8dc555f97bc7e3c7dd3fcc66409eb982bff3a44671b", m: "") + + + func testCacaoSign() async throws { + let signer = CacaoSigner(signer: Signer(), keystore: MockCacaoKeystore()) + + let signature = try await signer.sign(payload: payload) + + XCTAssertEqual(signature, CacaoSignature(t: "eip191", s: "8ed4211b12ff15435b6735ee58b027294ebb5ebf7b5bd82224b4499fdbc11427c4432851d0e11f43f81df179f675bdc6d1db12ea6f53bd86058a6e9088c7c17a00", m: "")) + } + + func testCacaoVerify() async throws { + let signer = CacaoSigner(signer: Signer(), keystore: MockCacaoKeystore()) + + await XCTAssertNoThrowAsync(try await signer.verify(signature: sig, payload: payload)) + } +} + +struct MockCacaoKeystore: CacaoSignerKeystore { + + var privateKey: Data { + get async { + return Data(base64Encoded: "jcvm9Kvw9VjhyHrRq4ZOnQ+ght2ZfXx8ImFsg3KP6pw=")! + } + } +} diff --git a/Tests/AuthTests/SignerTests.swift b/Tests/AuthTests/SignerTests.swift new file mode 100644 index 000000000..a39dbd4ed --- /dev/null +++ b/Tests/AuthTests/SignerTests.swift @@ -0,0 +1,55 @@ +import Foundation +import XCTest +@testable import Auth +import secp256k1 + +class SignerTest: XCTestCase { + + private let signer = Signer() + + private let message = "Message".data(using: .utf8)! + private let privateKey = Data(base64Encoded: "bm2GjW0qNRFXv+ezXRx5U3+7VLTKS0o5/iXYMzjI6Xo=")! + private let publicKey = Data(base64Encoded: "AxffejKffsmnVBI0fKJ6KPlg6eON+T8ds0ZMlheo6QOg")! + private let signature = Data(base64Encoded: "AGZT3nnrhkXdHMivRbnakJMUUXVwUtxMeNy/DWvHq87sgSVu+NbShQJVaBbfYG83A3BOaT+cpNJQuUnTv/MEUwE=")! + + private var address: String { + return try! SignerAddress.from(publicKey: publicKey) + } + + func testValidSignature() throws { + let result = try signer.sign(message: message, with: privateKey) + + XCTAssertEqual(signature, result) + XCTAssertTrue(try signer.isValid(signature: result, message: message, address: address)) + } + + func testInvalidMessage() throws { + let message = "Message One".data(using: .utf8)! + + XCTAssertFalse(try signer.isValid(signature: signature, message: message, address: address)) + } + + func testInvalidPubKey() throws { + let address = try SignerAddress.from(publicKey: .randomBytes(count: 32)) + + XCTAssertFalse(try signer.isValid(signature: signature, message: message, address: address)) + } + + func testInvalidSignature() throws { + let signature = Data(base64Encoded: "U5BxHfW0zjTeoAqT3f3U45djb2pom3GwN6tZKc7yHg1onDBJ/YoZlkQOl3E641zHRu5XKOaSY2jj+IqaNoREIwA=")! + + XCTAssertFalse(try signer.isValid(signature: signature, message: message, address: address)) + } + + func testSignerAddressFromPublicKey() throws { + let publicKey = Data(hex: "aa931f5ee58735270821b3722866d8882d1948909532cf8ac2b3ef144ae8043363d1d3728b49f10c7cd78c38289c8012477473879f3b53169f2a677b7fbed0c7") + + XCTAssertEqual(try SignerAddress.from(publicKey: publicKey), "0xe16c1623c1aa7d919cd2241d8b36d9e79c1be2a2") + } + + func testSignerAddressFomIss() throws { + let iss = "did:pkh:eip155:1:0xBAc675C310721717Cd4A37F6cbeA1F081b1C2a07" + + XCTAssertEqual(try SignerAddress.from(iss: iss), "0xbac675c310721717cd4a37f6cbea1f081b1c2a07") + } +} From 15ca6bfcb3b6818b04b43bb12c341c756fa05e61 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 4 Aug 2022 17:08:18 +0300 Subject: [PATCH 17/89] Using compact signature --- Package.swift | 2 +- Sources/Auth/Services/Signer/CocoaSigner.swift | 6 +++--- Sources/Auth/Services/Signer/Signer.swift | 13 ++++++++----- Tests/AuthTests/CacaoSignerTests.swift | 17 +++++++++-------- Tests/AuthTests/SignerTests.swift | 12 ++++-------- 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/Package.swift b/Package.swift index 5ef4cdd86..1c03831f1 100644 --- a/Package.swift +++ b/Package.swift @@ -21,7 +21,7 @@ let package = Package( targets: ["Auth"]), ], dependencies: [ - .package(url: "https://github.com/GigaBitcoin/secp256k1.swift.git", .upToNextMajor(from: "0.6.0")), + .package(url: "https://github.com/flypaper0/secp256k1.swift.git", .branch("feature/serialized-compact")), .package(url: "https://github.com/krzyzanowskim/CryptoSwift", .upToNextMajor(from: "1.5.1")) ], targets: [ diff --git a/Sources/Auth/Services/Signer/CocoaSigner.swift b/Sources/Auth/Services/Signer/CocoaSigner.swift index 82524e86c..02b6f6b54 100644 --- a/Sources/Auth/Services/Signer/CocoaSigner.swift +++ b/Sources/Auth/Services/Signer/CocoaSigner.swift @@ -19,7 +19,7 @@ actor CacaoSigner { } func sign(payload: CacaoPayload) async throws -> CacaoSignature { - let message = try JSONEncoder().encode(payload) + let message = try JSONEncoder().encode(payload) // TODO: SIWE encoding let signature = try await signer.sign(message: message, with: keystore.privateKey) return CacaoSignature(t: "eip191", s: signature.toHexString(), m: String()) } @@ -28,10 +28,10 @@ actor CacaoSigner { let message = try JSONEncoder().encode(payload) let address = try SignerAddress.from(iss: payload.iss) - guard let signature = signature.s.data(using: .utf8) + guard let sig = signature.s.data(using: .utf8) else { throw Errors.signatureCorrupted } - guard try signer.isValid(signature: signature, message: message, address: address) + guard try signer.isValid(signature: sig, message: message, address: address) else { throw Errors.signatureValidationFailed } } } diff --git a/Sources/Auth/Services/Signer/Signer.swift b/Sources/Auth/Services/Signer/Signer.swift index b6bec4e31..97aba30f3 100644 --- a/Sources/Auth/Services/Signer/Signer.swift +++ b/Sources/Auth/Services/Signer/Signer.swift @@ -10,15 +10,18 @@ struct Signer { func sign(message: Data, with privateKey: Data) throws -> Data { let key = try SigningPrivateKey(rawRepresentation: privateKey) - let signature = try key.ecdsa.recoverableSignature(for: message) - return signature.rawRepresentation + let signature = try key.ecdsa.recoverableSignature(for: SHA256Digest(message.keccak256.bytes)) + return try signature.compactRepresentation.serialized } func isValid(signature: Data, message: Data, address: String) throws -> Bool { - let recoverySignature = try Signature(rawRepresentation: signature) - let publicKey = try RecoveryPublicKey(message, signature: recoverySignature) + let digest = SHA256Digest(message.keccak256.bytes) + let compact = signature.prefix(signature.count - 1) + let recoveryId = Int32(signature[signature.count - 1]) + let recoverySignature = try Signature(compactRepresentation: compact, recoveryId: recoveryId) + let publicKey = try RecoveryPublicKey(digest, signature: recoverySignature) let validator = try SigningPublicKey(rawRepresentation: publicKey.rawRepresentation, format: .compressed) - let isValid = try validator.ecdsa.isValidSignature(recoverySignature.normalize, for: message) + let isValid = try validator.ecdsa.isValidSignature(recoverySignature.normalize, for: digest) let recoveredAddress = try SignerAddress.from(publicKey: publicKey.rawRepresentation) return isValid && recoveredAddress == address } diff --git a/Tests/AuthTests/CacaoSignerTests.swift b/Tests/AuthTests/CacaoSignerTests.swift index b83e27661..4f9929e7e 100644 --- a/Tests/AuthTests/CacaoSignerTests.swift +++ b/Tests/AuthTests/CacaoSignerTests.swift @@ -22,7 +22,7 @@ class CacaoSignerTest: XCTestCase { ] ) - let sig = CacaoSignature(t: "eip191", s: "5ccb134ad3d874cbb40a32b399549cd32c953dc5dc87dc64624a3e3dc0684d7d4833043dd7e9f4a6894853f8dc555f97bc7e3c7dd3fcc66409eb982bff3a44671b", m: "") + let sig = CacaoSignature(t: "eip191", s: "11f3715374dbacdec7e51611c9f2a8f13c11d14fd62c9556fb87f09235b8acaa2b3cba50968180da3e8c902086430011229375545eec0264bb431708ae92713201", m: "") func testCacaoSign() async throws { @@ -30,21 +30,22 @@ class CacaoSignerTest: XCTestCase { let signature = try await signer.sign(payload: payload) - XCTAssertEqual(signature, CacaoSignature(t: "eip191", s: "8ed4211b12ff15435b6735ee58b027294ebb5ebf7b5bd82224b4499fdbc11427c4432851d0e11f43f81df179f675bdc6d1db12ea6f53bd86058a6e9088c7c17a00", m: "")) + XCTAssertEqual(signature, sig) } - func testCacaoVerify() async throws { - let signer = CacaoSigner(signer: Signer(), keystore: MockCacaoKeystore()) - - await XCTAssertNoThrowAsync(try await signer.verify(signature: sig, payload: payload)) - } +// TODO: Restore test +// func testCacaoVerify() async throws { +// let signer = CacaoSigner(signer: Signer(), keystore: MockCacaoKeystore()) +// +// try await signer.verify(signature: sig, payload: payload) +// } } struct MockCacaoKeystore: CacaoSignerKeystore { var privateKey: Data { get async { - return Data(base64Encoded: "jcvm9Kvw9VjhyHrRq4ZOnQ+ght2ZfXx8ImFsg3KP6pw=")! + return Data(hex: "8dcbe6f4abf0f558e1c87ad1ab864e9d0fa086dd997d7c7c22616c83728fea9c") } } } diff --git a/Tests/AuthTests/SignerTests.swift b/Tests/AuthTests/SignerTests.swift index a39dbd4ed..bf57da824 100644 --- a/Tests/AuthTests/SignerTests.swift +++ b/Tests/AuthTests/SignerTests.swift @@ -8,13 +8,9 @@ class SignerTest: XCTestCase { private let signer = Signer() private let message = "Message".data(using: .utf8)! - private let privateKey = Data(base64Encoded: "bm2GjW0qNRFXv+ezXRx5U3+7VLTKS0o5/iXYMzjI6Xo=")! - private let publicKey = Data(base64Encoded: "AxffejKffsmnVBI0fKJ6KPlg6eON+T8ds0ZMlheo6QOg")! - private let signature = Data(base64Encoded: "AGZT3nnrhkXdHMivRbnakJMUUXVwUtxMeNy/DWvHq87sgSVu+NbShQJVaBbfYG83A3BOaT+cpNJQuUnTv/MEUwE=")! - - private var address: String { - return try! SignerAddress.from(publicKey: publicKey) - } + private let privateKey = Data(hex: "305c6cde3846927892cd32762f6120539f3ec74c9e3a16b9b798b1e85351ae2a") + private let signature = Data(hex: "f7d00a04559bff462f02194874b1ae7d4a8f0461acbe4be73386ebe982a9b9dc599abf31107e1ba708a3ec72499f1fd73dd390c5ca1a3084abe176de0529d00e00") + private var address = "0xc29c1c64576cef6229d501f107faa1090f81ca86" func testValidSignature() throws { let result = try signer.sign(message: message, with: privateKey) @@ -36,7 +32,7 @@ class SignerTest: XCTestCase { } func testInvalidSignature() throws { - let signature = Data(base64Encoded: "U5BxHfW0zjTeoAqT3f3U45djb2pom3GwN6tZKc7yHg1onDBJ/YoZlkQOl3E641zHRu5XKOaSY2jj+IqaNoREIwA=")! + let signature = Data(hex: "86deb09d045608f2753ef12f46e8da5fc2559e3a9162e580df3e62c875df7c3f64433462a59bc4ff38ce52412bff10527f4b99cc078f63ef2bb4a6f7427080aa01") XCTAssertFalse(try signer.isValid(signature: signature, message: message, address: address)) } From 71af65f9bd6b3908d5c9bde81342f863d0418501 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 4 Aug 2022 17:21:53 +0300 Subject: [PATCH 18/89] testCacaoVerify repaired --- Sources/Auth/Services/Signer/CocoaSigner.swift | 5 +---- Tests/AuthTests/CacaoSignerTests.swift | 15 +++++++-------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/Sources/Auth/Services/Signer/CocoaSigner.swift b/Sources/Auth/Services/Signer/CocoaSigner.swift index 02b6f6b54..d51554105 100644 --- a/Sources/Auth/Services/Signer/CocoaSigner.swift +++ b/Sources/Auth/Services/Signer/CocoaSigner.swift @@ -6,7 +6,6 @@ protocol CacaoSignerKeystore { actor CacaoSigner { enum Errors: Error { - case signatureCorrupted case signatureValidationFailed } @@ -25,12 +24,10 @@ actor CacaoSigner { } func verify(signature: CacaoSignature, payload: CacaoPayload) async throws { + let sig = Data(hex: signature.s) let message = try JSONEncoder().encode(payload) let address = try SignerAddress.from(iss: payload.iss) - guard let sig = signature.s.data(using: .utf8) - else { throw Errors.signatureCorrupted } - guard try signer.isValid(signature: sig, message: message, address: address) else { throw Errors.signatureValidationFailed } } diff --git a/Tests/AuthTests/CacaoSignerTests.swift b/Tests/AuthTests/CacaoSignerTests.swift index 4f9929e7e..8cb2c614c 100644 --- a/Tests/AuthTests/CacaoSignerTests.swift +++ b/Tests/AuthTests/CacaoSignerTests.swift @@ -6,7 +6,7 @@ import TestingUtils class CacaoSignerTest: XCTestCase { let payload = CacaoPayload( - iss: "did:pkh:eip155:1:0xBAc675C310721717Cd4A37F6cbeA1F081b1C2a07", + iss: "did:pkh:eip155:1:0x22Fe071b3631f155F0d8f4c9377D3309cB904E10", domain: "localhost:3000", aud: "http://localhost:3000/login", version: 1, @@ -22,7 +22,7 @@ class CacaoSignerTest: XCTestCase { ] ) - let sig = CacaoSignature(t: "eip191", s: "11f3715374dbacdec7e51611c9f2a8f13c11d14fd62c9556fb87f09235b8acaa2b3cba50968180da3e8c902086430011229375545eec0264bb431708ae92713201", m: "") + let sig = CacaoSignature(t: "eip191", s: "ced1849ff778a1a55a9d5516c11f13d8637859c2af370b178e11e40fed5c239465c32db0e52849fc3638507090fc810f73a354c7a5c72f94ab9673db6085c20301", m: "") func testCacaoSign() async throws { @@ -33,12 +33,11 @@ class CacaoSignerTest: XCTestCase { XCTAssertEqual(signature, sig) } -// TODO: Restore test -// func testCacaoVerify() async throws { -// let signer = CacaoSigner(signer: Signer(), keystore: MockCacaoKeystore()) -// -// try await signer.verify(signature: sig, payload: payload) -// } + func testCacaoVerify() async throws { + let signer = CacaoSigner(signer: Signer(), keystore: MockCacaoKeystore()) + + try await signer.verify(signature: sig, payload: payload) + } } struct MockCacaoKeystore: CacaoSignerKeystore { From da61a9ca21eca748a1f79e965505b1ee8eec693e Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Sun, 7 Aug 2022 21:54:40 +0300 Subject: [PATCH 19/89] Signer interface adopted --- .../Services/App/AppRespondSubscriber.swift | 7 +++-- .../Common/CacaoSignatureVerifier.swift | 15 --------- .../Auth/Services/Signer/CocoaSigner.swift | 25 +++++++-------- Tests/AuthTests/CacaoSignerTests.swift | 31 ++++++++++--------- 4 files changed, 33 insertions(+), 45 deletions(-) delete mode 100644 Sources/Auth/Services/Common/CacaoSignatureVerifier.swift diff --git a/Sources/Auth/Services/App/AppRespondSubscriber.swift b/Sources/Auth/Services/App/AppRespondSubscriber.swift index d8695b98a..eaceefc3b 100644 --- a/Sources/Auth/Services/App/AppRespondSubscriber.swift +++ b/Sources/Auth/Services/App/AppRespondSubscriber.swift @@ -7,15 +7,18 @@ class AppRespondSubscriber { private let networkingInteractor: NetworkInteracting private let logger: ConsoleLogging private let rpcHistory: RPCHistory + private let signatureVerifier: CacaoSignatureVerifying private var publishers = [AnyCancellable]() var onResponse: ((_ id: RPCID, _ cacao: Cacao) -> Void)? init(networkingInteractor: NetworkInteracting, logger: ConsoleLogging, - rpcHistory: RPCHistory) { + rpcHistory: RPCHistory, + signatureVerifier: CacaoSignatureVerifying) { self.networkingInteractor = networkingInteractor self.logger = logger self.rpcHistory = rpcHistory + self.signatureVerifier = signatureVerifier subscribeForResponse() } @@ -29,7 +32,7 @@ class AppRespondSubscriber { return } do { - try CacaoSignatureVerifier().verifySignature(cacao) + try signatureVerifier.verifySignature(cacao) onResponse?(subscriptionPayload.request.id!, cacao) } catch { logger.debug("Received response with invalid signature") diff --git a/Sources/Auth/Services/Common/CacaoSignatureVerifier.swift b/Sources/Auth/Services/Common/CacaoSignatureVerifier.swift deleted file mode 100644 index 64f70488b..000000000 --- a/Sources/Auth/Services/Common/CacaoSignatureVerifier.swift +++ /dev/null @@ -1,15 +0,0 @@ -import Foundation - -protocol CacaoSignatureVerifying { - func verifySignature(_ cacao: Cacao) throws -} - -class CacaoSignatureVerifier: CacaoSignatureVerifying { - enum Errors: Error { - case signatureInvalid - } - - func verifySignature(_ cacao: Cacao) throws { - fatalError("not implemented") - } -} diff --git a/Sources/Auth/Services/Signer/CocoaSigner.swift b/Sources/Auth/Services/Signer/CocoaSigner.swift index d51554105..6b28297fa 100644 --- a/Sources/Auth/Services/Signer/CocoaSigner.swift +++ b/Sources/Auth/Services/Signer/CocoaSigner.swift @@ -1,32 +1,31 @@ import Foundation -protocol CacaoSignerKeystore { - var privateKey: Data { get async } +protocol CacaoSignatureVerifying { + func verifySignature(_ cacao: Cacao) throws } -actor CacaoSigner { +struct CacaoSigner: CacaoSignatureVerifying { + enum Errors: Error { case signatureValidationFailed } private let signer: Signer - private let keystore: CacaoSignerKeystore - init(signer: Signer, keystore: CacaoSignerKeystore) { + init(signer: Signer) { self.signer = signer - self.keystore = keystore } - func sign(payload: CacaoPayload) async throws -> CacaoSignature { + func sign(payload: CacaoPayload, privateKey: Data) throws -> CacaoSignature { let message = try JSONEncoder().encode(payload) // TODO: SIWE encoding - let signature = try await signer.sign(message: message, with: keystore.privateKey) - return CacaoSignature(t: "eip191", s: signature.toHexString(), m: String()) + let signature = try signer.sign(message: message, with: privateKey) + return CacaoSignature(t: "eip191", s: signature.toHexString(), m: String()) } - func verify(signature: CacaoSignature, payload: CacaoPayload) async throws { - let sig = Data(hex: signature.s) - let message = try JSONEncoder().encode(payload) - let address = try SignerAddress.from(iss: payload.iss) + func verifySignature(_ cacao: Cacao) throws { + let sig = Data(hex: cacao.signature.s) + let message = try JSONEncoder().encode(cacao.payload) + let address = try SignerAddress.from(iss: cacao.payload.iss) guard try signer.isValid(signature: sig, message: message, address: address) else { throw Errors.signatureValidationFailed } diff --git a/Tests/AuthTests/CacaoSignerTests.swift b/Tests/AuthTests/CacaoSignerTests.swift index 8cb2c614c..b89aea33a 100644 --- a/Tests/AuthTests/CacaoSignerTests.swift +++ b/Tests/AuthTests/CacaoSignerTests.swift @@ -5,6 +5,8 @@ import TestingUtils class CacaoSignerTest: XCTestCase { + let privateKey = Data(hex: "8dcbe6f4abf0f558e1c87ad1ab864e9d0fa086dd997d7c7c22616c83728fea9c") + let payload = CacaoPayload( iss: "did:pkh:eip155:1:0x22Fe071b3631f155F0d8f4c9377D3309cB904E10", domain: "localhost:3000", @@ -22,29 +24,28 @@ class CacaoSignerTest: XCTestCase { ] ) + var cacao: Cacao { + return Cacao( + header: .init(t: ""), + payload: payload, + signature: sig + ) + } + let sig = CacaoSignature(t: "eip191", s: "ced1849ff778a1a55a9d5516c11f13d8637859c2af370b178e11e40fed5c239465c32db0e52849fc3638507090fc810f73a354c7a5c72f94ab9673db6085c20301", m: "") - func testCacaoSign() async throws { - let signer = CacaoSigner(signer: Signer(), keystore: MockCacaoKeystore()) + func testCacaoSign() throws { + let signer = CacaoSigner(signer: Signer()) - let signature = try await signer.sign(payload: payload) + let signature = try signer.sign(payload: payload, privateKey: privateKey) XCTAssertEqual(signature, sig) } - func testCacaoVerify() async throws { - let signer = CacaoSigner(signer: Signer(), keystore: MockCacaoKeystore()) - - try await signer.verify(signature: sig, payload: payload) - } -} - -struct MockCacaoKeystore: CacaoSignerKeystore { + func testCacaoVerify() throws { + let signer = CacaoSigner(signer: Signer()) - var privateKey: Data { - get async { - return Data(hex: "8dcbe6f4abf0f558e1c87ad1ab864e9d0fa086dd997d7c7c22616c83728fea9c") - } + try signer.verifySignature(cacao) } } From cbe6a9a5b60da750b50570ea7b4e0496b388bf2e Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Mon, 8 Aug 2022 12:35:44 +0300 Subject: [PATCH 20/89] XCode 13.4.1 --- .github/workflows/ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 297e36c28..ec9483322 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ concurrency: jobs: build: - runs-on: macos-latest + runs-on: macos-12 strategy: matrix: test-type: [unit-tests, integration-tests, build-example-wallet, build-example-dapp] @@ -27,6 +27,8 @@ jobs: - name: Setup Xcode Version uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: '13.4.1' - uses: actions/cache@v2 with: From ba197181cff239f03004213f469beff78c26025b Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Mon, 8 Aug 2022 23:09:12 +0300 Subject: [PATCH 21/89] Forked web3 package --- .github/workflows/ci.yml | 2 +- Example/ExampleApp.xcodeproj/project.pbxproj | 34 ++++++-------- .../xcshareddata/swiftpm/Package.resolved | 21 ++++++--- Package.swift | 6 +-- Sources/Auth/Services/Signer/Signer.swift | 45 +++++++++++-------- Tests/AuthTests/CacaoSignerTests.swift | 6 +-- Tests/AuthTests/SignerTests.swift | 2 +- 7 files changed, 61 insertions(+), 55 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ec9483322..5159ddabd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ jobs: steps: - uses: actions/checkout@v2 - + - name: Setup Xcode Version uses: maxim-lobanov/setup-xcode@v1 with: diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index 6018e9231..0f9247588 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -29,7 +29,6 @@ 76B6E39F2807A3B6004DF775 /* WalletViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76B6E39E2807A3B6004DF775 /* WalletViewController.swift */; }; 8448F1D427E4726F0000B866 /* WalletConnect in Frameworks */ = {isa = PBXBuildFile; productRef = 8448F1D327E4726F0000B866 /* WalletConnect */; }; 84494388278D9C1B00CC26BB /* UIAlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84494387278D9C1B00CC26BB /* UIAlertController.swift */; }; - 844943A1278EC49700CC26BB /* Web3 in Frameworks */ = {isa = PBXBuildFile; productRef = 844943A0278EC49700CC26BB /* Web3 */; }; 8460DCFC274F98A10081F94C /* RequestViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8460DCFB274F98A10081F94C /* RequestViewController.swift */; }; 84CE641F27981DED00142511 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CE641E27981DED00142511 /* AppDelegate.swift */; }; 84CE642127981DED00142511 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CE642027981DED00142511 /* SceneDelegate.swift */; }; @@ -37,7 +36,6 @@ 84CE642B27981DF000142511 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 84CE642927981DF000142511 /* LaunchScreen.storyboard */; }; 84CE6430279820F600142511 /* AccountsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 761C649926FB7ABB004239D1 /* AccountsViewController.swift */; }; 84CE6431279820F600142511 /* AccountsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7603D74C2703429A00DD27A2 /* AccountsView.swift */; }; - 84CE64392798228D00142511 /* Web3 in Frameworks */ = {isa = PBXBuildFile; productRef = 84CE64382798228D00142511 /* Web3 */; }; 84CE643D2798322600142511 /* ConnectViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CE643C2798322600142511 /* ConnectViewController.swift */; }; 84CE6444279AB5AD00142511 /* SelectChainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CE6443279AB5AD00142511 /* SelectChainViewController.swift */; }; 84CE6448279AE68600142511 /* AccountRequestViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CE6447279AE68600142511 /* AccountRequestViewController.swift */; }; @@ -107,6 +105,7 @@ A5A4FC5C283D1F6700BBEC1E /* SessionDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5A4FC5B283D1F6700BBEC1E /* SessionDetailViewController.swift */; }; A5A4FC5E283D23CA00BBEC1E /* Array.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5A4FC5D283D23CA00BBEC1E /* Array.swift */; }; A5A4FC772840C12C00BBEC1E /* RegressionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5A4FC762840C12C00BBEC1E /* RegressionTests.swift */; }; + A5AE354728A1A2AC0059AE8A /* Web3 in Frameworks */ = {isa = PBXBuildFile; productRef = A5AE354628A1A2AC0059AE8A /* Web3 */; }; A5C2020B287D9DEE007E3188 /* WelcomeModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5C20206287D9DEE007E3188 /* WelcomeModule.swift */; }; A5C2020C287D9DEE007E3188 /* WelcomePresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5C20207287D9DEE007E3188 /* WelcomePresenter.swift */; }; A5C2020D287D9DEE007E3188 /* WelcomeRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5C20208287D9DEE007E3188 /* WelcomeRouter.swift */; }; @@ -303,8 +302,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + A5AE354728A1A2AC0059AE8A /* Web3 in Frameworks */, 764E1D5826F8DBAB00A1FB15 /* WalletConnect in Frameworks */, - 844943A1278EC49700CC26BB /* Web3 in Frameworks */, A5D85226286333D500DAF5C3 /* Starscream in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -314,7 +313,6 @@ buildActionMask = 2147483647; files = ( 8448F1D427E4726F0000B866 /* WalletConnect in Frameworks */, - 84CE64392798228D00142511 /* Web3 in Frameworks */, A5D85228286333E300DAF5C3 /* Starscream in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -931,8 +929,8 @@ name = Wallet; packageProductDependencies = ( 764E1D5726F8DBAB00A1FB15 /* WalletConnect */, - 844943A0278EC49700CC26BB /* Web3 */, A5D85225286333D500DAF5C3 /* Starscream */, + A5AE354628A1A2AC0059AE8A /* Web3 */, ); productName = ExampleApp; productReference = 764E1D3C26F8D3FC00A1FB15 /* WalletConnect Wallet.app */; @@ -952,7 +950,6 @@ ); name = DApp; packageProductDependencies = ( - 84CE64382798228D00142511 /* Web3 */, 8448F1D327E4726F0000B866 /* WalletConnect */, A5D85227286333E300DAF5C3 /* Starscream */, ); @@ -1058,8 +1055,8 @@ ); mainGroup = 764E1D3326F8D3FC00A1FB15; packageReferences = ( - 8449439F278EC49700CC26BB /* XCRemoteSwiftPackageReference "Web3" */, A5D85224286333D500DAF5C3 /* XCRemoteSwiftPackageReference "Starscream" */, + A5AE354528A1A2AC0059AE8A /* XCRemoteSwiftPackageReference "Web3" */, ); productRefGroup = 764E1D3D26F8D3FC00A1FB15 /* Products */; projectDirPath = ""; @@ -1740,12 +1737,12 @@ /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ - 8449439F278EC49700CC26BB /* XCRemoteSwiftPackageReference "Web3" */ = { + A5AE354528A1A2AC0059AE8A /* XCRemoteSwiftPackageReference "Web3" */ = { isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/Boilertalk/Web3.swift"; + repositoryURL = "https://github.com/flypaper0/Web3.swift"; requirement = { - kind = upToNextMajorVersion; - minimumVersion = 0.5.3; + branch = master; + kind = branch; }; }; A5D85224286333D500DAF5C3 /* XCRemoteSwiftPackageReference "Starscream" */ = { @@ -1767,16 +1764,6 @@ isa = XCSwiftPackageProductDependency; productName = WalletConnect; }; - 844943A0278EC49700CC26BB /* Web3 */ = { - isa = XCSwiftPackageProductDependency; - package = 8449439F278EC49700CC26BB /* XCRemoteSwiftPackageReference "Web3" */; - productName = Web3; - }; - 84CE64382798228D00142511 /* Web3 */ = { - isa = XCSwiftPackageProductDependency; - package = 8449439F278EC49700CC26BB /* XCRemoteSwiftPackageReference "Web3" */; - productName = Web3; - }; A5629AE92877F2D600094373 /* WalletConnectChat */ = { isa = XCSwiftPackageProductDependency; productName = WalletConnectChat; @@ -1786,6 +1773,11 @@ package = A5D85224286333D500DAF5C3 /* XCRemoteSwiftPackageReference "Starscream" */; productName = Starscream; }; + A5AE354628A1A2AC0059AE8A /* Web3 */ = { + isa = XCSwiftPackageProductDependency; + package = A5AE354528A1A2AC0059AE8A /* XCRemoteSwiftPackageReference "Web3" */; + productName = Web3; + }; A5D85225286333D500DAF5C3 /* Starscream */ = { isa = XCSwiftPackageProductDependency; package = A5D85224286333D500DAF5C3 /* XCRemoteSwiftPackageReference "Starscream" */; diff --git a/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 7faa87840..ab8883095 100644 --- a/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -24,8 +24,17 @@ "repositoryURL": "https://github.com/mxcl/PromiseKit.git", "state": { "branch": null, - "revision": "3fd8c77ded8a4bbee548e3bd6c987ffe8c1e3574", - "version": "6.17.0" + "revision": "ed3192004c0b00b4b3d7baa9578ee655c66faae3", + "version": "6.18.0" + } + }, + { + "package": "secp256k1", + "repositoryURL": "https://github.com/Boilertalk/secp256k1.swift.git", + "state": { + "branch": null, + "revision": "45e458ec3be46cf0a6eb68bc947f797852dc65d8", + "version": "0.1.6" } }, { @@ -48,11 +57,11 @@ }, { "package": "Web3", - "repositoryURL": "https://github.com/Boilertalk/Web3.swift", + "repositoryURL": "https://github.com/flypaper0/Web3.swift", "state": { - "branch": null, - "revision": "1a6830ecc093f0f19054fed4c135dfee7bebe2b2", - "version": "0.5.3" + "branch": "master", + "revision": "23b6940bbda0769d9147bec6696b33a9fee0b120", + "version": null } } ] diff --git a/Package.swift b/Package.swift index 1c03831f1..3bd3bef00 100644 --- a/Package.swift +++ b/Package.swift @@ -21,8 +21,7 @@ let package = Package( targets: ["Auth"]), ], dependencies: [ - .package(url: "https://github.com/flypaper0/secp256k1.swift.git", .branch("feature/serialized-compact")), - .package(url: "https://github.com/krzyzanowskim/CryptoSwift", .upToNextMajor(from: "1.5.1")) + .package(url: "https://github.com/flypaper0/Web3.swift", .branch("master")) ], targets: [ .target( @@ -40,8 +39,7 @@ let package = Package( "WalletConnectUtils", "WalletConnectKMS", "WalletConnectPairing", - .product(name: "CryptoSwift", package: "CryptoSwift"), - .product(name: "secp256k1", package: "secp256k1.swift"), + .product(name: "Web3", package: "Web3.swift"), ], path: "Sources/Auth"), .target( diff --git a/Sources/Auth/Services/Signer/Signer.swift b/Sources/Auth/Services/Signer/Signer.swift index 97aba30f3..7e509766c 100644 --- a/Sources/Auth/Services/Signer/Signer.swift +++ b/Sources/Auth/Services/Signer/Signer.swift @@ -1,28 +1,35 @@ import Foundation -import secp256k1 - -fileprivate typealias Signature = secp256k1.Recovery.ECDSASignature -fileprivate typealias RecoveryPublicKey = secp256k1.Recovery.PublicKey -fileprivate typealias SigningPublicKey = secp256k1.Signing.PublicKey -fileprivate typealias SigningPrivateKey = secp256k1.Signing.PrivateKey +import Web3 struct Signer { - func sign(message: Data, with privateKey: Data) throws -> Data { - let key = try SigningPrivateKey(rawRepresentation: privateKey) - let signature = try key.ecdsa.recoverableSignature(for: SHA256Digest(message.keccak256.bytes)) - return try signature.compactRepresentation.serialized + typealias Signature = (v: UInt, r: [UInt8], s: [UInt8]) + + func sign(message: Data, with key: Data) throws -> Data { + let privateKey = try EthereumPrivateKey(privateKey: key.bytes) + let signature = try privateKey.sign(message: message.bytes) + return serialized(signature: signature) } func isValid(signature: Data, message: Data, address: String) throws -> Bool { - let digest = SHA256Digest(message.keccak256.bytes) - let compact = signature.prefix(signature.count - 1) - let recoveryId = Int32(signature[signature.count - 1]) - let recoverySignature = try Signature(compactRepresentation: compact, recoveryId: recoveryId) - let publicKey = try RecoveryPublicKey(digest, signature: recoverySignature) - let validator = try SigningPublicKey(rawRepresentation: publicKey.rawRepresentation, format: .compressed) - let isValid = try validator.ecdsa.isValidSignature(recoverySignature.normalize, for: digest) - let recoveredAddress = try SignerAddress.from(publicKey: publicKey.rawRepresentation) - return isValid && recoveredAddress == address + let sig = decompose(signature: signature) + let publicKey = try EthereumPublicKey( + message: message.bytes, + v: EthereumQuantity(quantity: BigUInt(sig.v)), + r: EthereumQuantity(sig.r), + s: EthereumQuantity(sig.s) + ) + return publicKey.address.hex(eip55: false) == address + } + + private func decompose(signature: Data) -> Signature { + let v = signature.bytes[signature.count-1] + let r = signature.bytes[0..<32] + let s = signature.bytes[32..<64] + return (UInt(v), [UInt8](r), [UInt8](s)) + } + + private func serialized(signature: Signature) -> Data { + return Data(signature.r + signature.s + [UInt8(signature.v)]) } } diff --git a/Tests/AuthTests/CacaoSignerTests.swift b/Tests/AuthTests/CacaoSignerTests.swift index b89aea33a..cf09d1f38 100644 --- a/Tests/AuthTests/CacaoSignerTests.swift +++ b/Tests/AuthTests/CacaoSignerTests.swift @@ -5,10 +5,10 @@ import TestingUtils class CacaoSignerTest: XCTestCase { - let privateKey = Data(hex: "8dcbe6f4abf0f558e1c87ad1ab864e9d0fa086dd997d7c7c22616c83728fea9c") + let privateKey = Data(hex: "305c6cde3846927892cd32762f6120539f3ec74c9e3a16b9b798b1e85351ae2a") let payload = CacaoPayload( - iss: "did:pkh:eip155:1:0x22Fe071b3631f155F0d8f4c9377D3309cB904E10", + iss: "did:pkh:eip155:1:0x15bca56b6e2728aec2532df9d436bd1600e86688", domain: "localhost:3000", aud: "http://localhost:3000/login", version: 1, @@ -32,7 +32,7 @@ class CacaoSignerTest: XCTestCase { ) } - let sig = CacaoSignature(t: "eip191", s: "ced1849ff778a1a55a9d5516c11f13d8637859c2af370b178e11e40fed5c239465c32db0e52849fc3638507090fc810f73a354c7a5c72f94ab9673db6085c20301", m: "") + let sig = CacaoSignature(t: "eip191", s: "914b8300e471744f506407aa072cdf9a606fd3fe1a6f2a16c9f78009074c69622143c3009f4ccdedc0fdd421e5579c5e11b3a604e0a3e6ae0cb06b5e380879fb00", m: "") func testCacaoSign() throws { diff --git a/Tests/AuthTests/SignerTests.swift b/Tests/AuthTests/SignerTests.swift index bf57da824..8e3f78db0 100644 --- a/Tests/AuthTests/SignerTests.swift +++ b/Tests/AuthTests/SignerTests.swift @@ -10,7 +10,7 @@ class SignerTest: XCTestCase { private let message = "Message".data(using: .utf8)! private let privateKey = Data(hex: "305c6cde3846927892cd32762f6120539f3ec74c9e3a16b9b798b1e85351ae2a") private let signature = Data(hex: "f7d00a04559bff462f02194874b1ae7d4a8f0461acbe4be73386ebe982a9b9dc599abf31107e1ba708a3ec72499f1fd73dd390c5ca1a3084abe176de0529d00e00") - private var address = "0xc29c1c64576cef6229d501f107faa1090f81ca86" + private var address = "0x15bca56b6e2728aec2532df9d436bd1600e86688" func testValidSignature() throws { let result = try signer.sign(message: message, with: privateKey) From 9259e43bcabad84a651af467e461f559bac5f4cf Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Tue, 9 Aug 2022 15:37:52 +0300 Subject: [PATCH 22/89] Resolve dependencies step --- .github/actions/ci/action.yml | 10 +++++----- .github/workflows/ci.yml | 12 +++++++++--- Example/IntegrationTests/Chat/ChatTests.swift | 4 ++-- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/.github/actions/ci/action.yml b/.github/actions/ci/action.yml index 1e887a78c..75ed3bd4f 100644 --- a/.github/actions/ci/action.yml +++ b/.github/actions/ci/action.yml @@ -15,7 +15,7 @@ runs: run: "xcodebuild \ -project Example/ExampleApp.xcodeproj \ -scheme WalletConnect \ - -clonedSourcePackagesDirPath SourcePackages \ + -clonedSourcePackagesDirPath SourcePackagesCache \ -destination 'platform=iOS Simulator,name=iPhone 13' \ test" @@ -26,7 +26,7 @@ runs: run: "xcodebuild \ -project Example/ExampleApp.xcodeproj \ -scheme IntegrationTests \ - -clonedSourcePackagesDirPath SourcePackages \ + -clonedSourcePackagesDirPath SourcePackagesCache \ -destination 'platform=iOS Simulator,name=iPhone 13' test" # Wallet build @@ -36,7 +36,7 @@ runs: run: "xcodebuild \ -project Example/ExampleApp.xcodeproj \ -scheme Wallet \ - -clonedSourcePackagesDirPath SourcePackages \ + -clonedSourcePackagesDirPath SourcePackagesCache \ -sdk iphonesimulator" # DApp build @@ -46,7 +46,7 @@ runs: run: "xcodebuild \ -project Example/ExampleApp.xcodeproj \ -scheme DApp \ - -clonedSourcePackagesDirPath SourcePackages \ + -clonedSourcePackagesDirPath SourcePackagesCache \ -sdk iphonesimulator" # UI tests @@ -56,6 +56,6 @@ runs: run: "xcodebuild \ -project Example/ExampleApp.xcodeproj \ -scheme UITests \ - -clonedSourcePackagesDirPath SourcePackages \ + -clonedSourcePackagesDirPath SourcePackagesCache \ -destination 'platform=iOS Simulator,name=iPhone 13' test" continue-on-error: true diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5159ddabd..8495f8859 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,17 +24,23 @@ jobs: steps: - uses: actions/checkout@v2 - + - name: Setup Xcode Version uses: maxim-lobanov/setup-xcode@v1 with: xcode-version: '13.4.1' + - name: Resolve Dependencies + shell: bash + run: " + xcodebuild -resolvePackageDependencies -project Example/ExampleApp.xcodeproj -scheme DApp -clonedSourcePackagesDirPath SourcePackagesCache; \ + xcodebuild -resolvePackageDependencies -project Example/ExampleApp.xcodeproj -scheme WalletConnect -clonedSourcePackagesDirPath SourcePackagesCache" + - uses: actions/cache@v2 with: path: | .build - SourcePackages + SourcePackagesCache key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }} restore-keys: | ${{ runner.os }}-spm- @@ -60,7 +66,7 @@ jobs: with: path: | .build - SourcePackages + SourcePackagesCache key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }} restore-keys: | ${{ runner.os }}-spm- diff --git a/Example/IntegrationTests/Chat/ChatTests.swift b/Example/IntegrationTests/Chat/ChatTests.swift index 812b34e05..aa1a6271f 100644 --- a/Example/IntegrationTests/Chat/ChatTests.swift +++ b/Example/IntegrationTests/Chat/ChatTests.swift @@ -66,7 +66,7 @@ final class ChatTests: XCTestCase { let inviteeAccount = Account(chainIdentifier: "eip155:1", address: "0x3627523167367216556273151")! let inviterAccount = Account(chainIdentifier: "eip155:1", address: "0x36275231673672234423f")! - Task(priority: .background) { + Task(priority: .high) { let pubKey = try! await invitee.register(account: inviteeAccount) try! await inviter.invite(publicKey: pubKey, peerAccount: inviteeAccount, openingMessage: "opening message", account: inviterAccount) @@ -94,7 +94,7 @@ final class ChatTests: XCTestCase { let inviteeAccount = Account(chainIdentifier: "eip155:1", address: "0x3627523167367216556273151")! let inviterAccount = Account(chainIdentifier: "eip155:1", address: "0x36275231673672234423f")! - Task(priority: .background) { + Task(priority: .high) { let pubKey = try! await invitee.register(account: inviteeAccount) try! await inviter.invite(publicKey: pubKey, peerAccount: inviteeAccount, openingMessage: "opening message", account: inviterAccount) } From 24c2b844f23db5d5bbc77e53bac188f5a355ab53 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Tue, 9 Aug 2022 16:28:31 +0300 Subject: [PATCH 23/89] DIDPKH object --- .../Auth/Services/Signer/CocoaSigner.swift | 3 +- Sources/Auth/Services/Signer/DIDPKH.swift | 33 +++++++++++++++++++ Sources/Auth/Services/Signer/Signer.swift | 2 +- .../Auth/Services/Signer/SignerAddress.swift | 30 ----------------- Tests/AuthTests/SignerTests.swift | 16 +++++---- 5 files changed, 44 insertions(+), 40 deletions(-) create mode 100644 Sources/Auth/Services/Signer/DIDPKH.swift delete mode 100644 Sources/Auth/Services/Signer/SignerAddress.swift diff --git a/Sources/Auth/Services/Signer/CocoaSigner.swift b/Sources/Auth/Services/Signer/CocoaSigner.swift index 6b28297fa..9aae26e6c 100644 --- a/Sources/Auth/Services/Signer/CocoaSigner.swift +++ b/Sources/Auth/Services/Signer/CocoaSigner.swift @@ -25,8 +25,7 @@ struct CacaoSigner: CacaoSignatureVerifying { func verifySignature(_ cacao: Cacao) throws { let sig = Data(hex: cacao.signature.s) let message = try JSONEncoder().encode(cacao.payload) - let address = try SignerAddress.from(iss: cacao.payload.iss) - + let address = try DIDPKH(iss: cacao.payload.iss).account.address guard try signer.isValid(signature: sig, message: message, address: address) else { throw Errors.signatureValidationFailed } } diff --git a/Sources/Auth/Services/Signer/DIDPKH.swift b/Sources/Auth/Services/Signer/DIDPKH.swift new file mode 100644 index 000000000..fad1a3431 --- /dev/null +++ b/Sources/Auth/Services/Signer/DIDPKH.swift @@ -0,0 +1,33 @@ +import Foundation +import WalletConnectUtils + +struct DIDPKH { + static let didPrefix: String = "did:pkh" + + enum Errors: Error { + case invalidDIDPKH + case invalidAccount + } + + let account: Account + let iss: String + + init(iss: String) throws { + guard iss.starts(with: DIDPKH.didPrefix) + else { throw Errors.invalidDIDPKH } + + guard let string = iss.components(separatedBy: DIDPKH.didPrefix + ":").last + else { throw Errors.invalidDIDPKH } + + guard let account = Account(string) + else { throw Errors.invalidAccount } + + self.iss = iss + self.account = account + } + + init(account: Account) { + self.iss = "\(DIDPKH.didPrefix):\(account.absoluteString)" + self.account = account + } +} diff --git a/Sources/Auth/Services/Signer/Signer.swift b/Sources/Auth/Services/Signer/Signer.swift index 7e509766c..b4b02ebbe 100644 --- a/Sources/Auth/Services/Signer/Signer.swift +++ b/Sources/Auth/Services/Signer/Signer.swift @@ -19,7 +19,7 @@ struct Signer { r: EthereumQuantity(sig.r), s: EthereumQuantity(sig.s) ) - return publicKey.address.hex(eip55: false) == address + return publicKey.address.hex(eip55: false) == address.lowercased() } private func decompose(signature: Data) -> Signature { diff --git a/Sources/Auth/Services/Signer/SignerAddress.swift b/Sources/Auth/Services/Signer/SignerAddress.swift deleted file mode 100644 index 6db970b16..000000000 --- a/Sources/Auth/Services/Signer/SignerAddress.swift +++ /dev/null @@ -1,30 +0,0 @@ -import Foundation -import WalletConnectUtils - -struct SignerAddress { - - static let didPrefix: String = "did:pkh" - static let lenght: Int = 20 - - enum Errors: Error { - case invalidPublicKey - case invalidDidPkh - } - - static func from(iss: String) throws -> String { - guard iss.starts(with: didPrefix) - else { throw Errors.invalidDidPkh } - - guard let string = iss.components(separatedBy: didPrefix + ":").last, let account = Account(string) - else { throw Errors.invalidDidPkh } - - return account.address.lowercased() - } - - static func from(publicKey: Data) throws -> String { - guard publicKey.count >= lenght - else { throw Errors.invalidPublicKey } - - return "0x" + publicKey.keccak256.suffix(SignerAddress.lenght).toHexString() - } -} diff --git a/Tests/AuthTests/SignerTests.swift b/Tests/AuthTests/SignerTests.swift index 8e3f78db0..b5488ddb5 100644 --- a/Tests/AuthTests/SignerTests.swift +++ b/Tests/AuthTests/SignerTests.swift @@ -2,6 +2,8 @@ import Foundation import XCTest @testable import Auth import secp256k1 +import Web3 +import WalletConnectUtils class SignerTest: XCTestCase { @@ -26,7 +28,7 @@ class SignerTest: XCTestCase { } func testInvalidPubKey() throws { - let address = try SignerAddress.from(publicKey: .randomBytes(count: 32)) + let address = "0xBAc675C310721717Cd4A37F6cbeA1F081b1C2a07" XCTAssertFalse(try signer.isValid(signature: signature, message: message, address: address)) } @@ -37,15 +39,15 @@ class SignerTest: XCTestCase { XCTAssertFalse(try signer.isValid(signature: signature, message: message, address: address)) } - func testSignerAddressFromPublicKey() throws { - let publicKey = Data(hex: "aa931f5ee58735270821b3722866d8882d1948909532cf8ac2b3ef144ae8043363d1d3728b49f10c7cd78c38289c8012477473879f3b53169f2a677b7fbed0c7") + func testSignerAddressFromIss() throws { + let iss = "did:pkh:eip155:1:0xBAc675C310721717Cd4A37F6cbeA1F081b1C2a07" - XCTAssertEqual(try SignerAddress.from(publicKey: publicKey), "0xe16c1623c1aa7d919cd2241d8b36d9e79c1be2a2") + XCTAssertEqual(try DIDPKH(iss: iss).account, Account("eip155:1:0xBAc675C310721717Cd4A37F6cbeA1F081b1C2a07")!) } - func testSignerAddressFomIss() throws { - let iss = "did:pkh:eip155:1:0xBAc675C310721717Cd4A37F6cbeA1F081b1C2a07" + func testSignerAddressFromAccount() throws { + let account = Account("eip155:1:0xBAc675C310721717Cd4A37F6cbeA1F081b1C2a07")! - XCTAssertEqual(try SignerAddress.from(iss: iss), "0xbac675c310721717cd4a37f6cbea1f081b1c2a07") + XCTAssertEqual(DIDPKH(account: account).iss, "did:pkh:eip155:1:0xBAc675C310721717Cd4A37F6cbeA1F081b1C2a07") } } From ea7b7178881beabe83a3919ff565d09917d62fed Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Tue, 9 Aug 2022 16:29:23 +0300 Subject: [PATCH 24/89] Lint --- Package.swift | 4 ++-- Sources/Auth/Services/Signer/CocoaSigner.swift | 2 +- Tests/AuthTests/CacaoSignerTests.swift | 1 - 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Package.swift b/Package.swift index 3bd3bef00..a077edd12 100644 --- a/Package.swift +++ b/Package.swift @@ -18,7 +18,7 @@ let package = Package( targets: ["Chat"]), .library( name: "WalletConnectAuth", - targets: ["Auth"]), + targets: ["Auth"]) ], dependencies: [ .package(url: "https://github.com/flypaper0/Web3.swift", .branch("master")) @@ -39,7 +39,7 @@ let package = Package( "WalletConnectUtils", "WalletConnectKMS", "WalletConnectPairing", - .product(name: "Web3", package: "Web3.swift"), + .product(name: "Web3", package: "Web3.swift") ], path: "Sources/Auth"), .target( diff --git a/Sources/Auth/Services/Signer/CocoaSigner.swift b/Sources/Auth/Services/Signer/CocoaSigner.swift index 9aae26e6c..80a1be837 100644 --- a/Sources/Auth/Services/Signer/CocoaSigner.swift +++ b/Sources/Auth/Services/Signer/CocoaSigner.swift @@ -19,7 +19,7 @@ struct CacaoSigner: CacaoSignatureVerifying { func sign(payload: CacaoPayload, privateKey: Data) throws -> CacaoSignature { let message = try JSONEncoder().encode(payload) // TODO: SIWE encoding let signature = try signer.sign(message: message, with: privateKey) - return CacaoSignature(t: "eip191", s: signature.toHexString(), m: String()) + return CacaoSignature(t: "eip191", s: signature.toHexString(), m: String()) } func verifySignature(_ cacao: Cacao) throws { diff --git a/Tests/AuthTests/CacaoSignerTests.swift b/Tests/AuthTests/CacaoSignerTests.swift index cf09d1f38..1336f0ba3 100644 --- a/Tests/AuthTests/CacaoSignerTests.swift +++ b/Tests/AuthTests/CacaoSignerTests.swift @@ -34,7 +34,6 @@ class CacaoSignerTest: XCTestCase { let sig = CacaoSignature(t: "eip191", s: "914b8300e471744f506407aa072cdf9a606fd3fe1a6f2a16c9f78009074c69622143c3009f4ccdedc0fdd421e5579c5e11b3a604e0a3e6ae0cb06b5e380879fb00", m: "") - func testCacaoSign() throws { let signer = CacaoSigner(signer: Signer()) From 50f6fa5779e763d863dcf16e562a6407be026ee0 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Tue, 9 Aug 2022 21:42:57 +0300 Subject: [PATCH 25/89] CacaoSigner fix typo --- .../Auth/Services/Signer/{CocoaSigner.swift => CacaoSigner.swift} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Sources/Auth/Services/Signer/{CocoaSigner.swift => CacaoSigner.swift} (100%) diff --git a/Sources/Auth/Services/Signer/CocoaSigner.swift b/Sources/Auth/Services/Signer/CacaoSigner.swift similarity index 100% rename from Sources/Auth/Services/Signer/CocoaSigner.swift rename to Sources/Auth/Services/Signer/CacaoSigner.swift From aa722f3a84e2238a678887f7d729db8c0ebae396 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Wed, 10 Aug 2022 21:33:03 +0300 Subject: [PATCH 26/89] Sign and verify SIWE message --- .../xcschemes/WalletConnectAuth.xcscheme | 10 ++++ .../Services/App/AppRespondSubscriber.swift | 35 +++++++++---- .../Auth/Services/Signer/CacaoSigner.swift | 32 ------------ .../Auth/Services/Signer/MessageSigner.swift | 36 +++++++++++++ .../Wallet/WalletRequestSubscriber.swift | 29 +++++++---- Sources/Auth/Types/AuthPayload.swift | 15 ++++++ Tests/AuthTests/CacaoSignerTests.swift | 51 ++++++++----------- 7 files changed, 125 insertions(+), 83 deletions(-) delete mode 100644 Sources/Auth/Services/Signer/CacaoSigner.swift create mode 100644 Sources/Auth/Services/Signer/MessageSigner.swift diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/WalletConnectAuth.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/WalletConnectAuth.xcscheme index 5b05c7da2..10b74b10a 100644 --- a/.swiftpm/xcode/xcshareddata/xcschemes/WalletConnectAuth.xcscheme +++ b/.swiftpm/xcode/xcshareddata/xcschemes/WalletConnectAuth.xcscheme @@ -28,6 +28,16 @@ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES"> + + + + Void)? init(networkingInteractor: NetworkInteracting, logger: ConsoleLogging, rpcHistory: RPCHistory, - signatureVerifier: CacaoSignatureVerifying) { + signatureVerifier: MessageSignatureVerifying, + messageFormatter: SIWEMessageFormatting) { self.networkingInteractor = networkingInteractor self.logger = logger self.rpcHistory = rpcHistory self.signatureVerifier = signatureVerifier + self.messageFormatter = messageFormatter subscribeForResponse() } private func subscribeForResponse() { networkingInteractor.responsePublisher.sink { [unowned self] subscriptionPayload in - guard let request = rpcHistory.get(recordId: subscriptionPayload.request.id!)?.request, - request.method == "wc_authRequest" else { return } + guard + let requestId = subscriptionPayload.request.id, + let request = rpcHistory.get(recordId: requestId)?.request, + request.method == "wc_authRequest" else { return } + networkingInteractor.unsubscribe(topic: subscriptionPayload.topic) - guard let cacao = try? subscriptionPayload.request.result?.get(Cacao.self) else { - logger.debug("Malformed auth response params") - return - } + do { - try signatureVerifier.verifySignature(cacao) - onResponse?(subscriptionPayload.request.id!, cacao) + guard let cacao = try subscriptionPayload.request.result?.get(Cacao.self) else { + return logger.debug("Malformed auth response params") + } + + let address = try DIDPKH(iss: cacao.payload.iss).account.address + let payload = AuthPayload(payload: cacao.payload) + let message = messageFormatter.formatMessage(from: payload, address: address) + + try signatureVerifier.verify( + signature: cacao.signature.s, + message: message, + address: cacao.payload.iss + ) + onResponse?(requestId, cacao) } catch { logger.debug("Received response with invalid signature") } diff --git a/Sources/Auth/Services/Signer/CacaoSigner.swift b/Sources/Auth/Services/Signer/CacaoSigner.swift deleted file mode 100644 index 80a1be837..000000000 --- a/Sources/Auth/Services/Signer/CacaoSigner.swift +++ /dev/null @@ -1,32 +0,0 @@ -import Foundation - -protocol CacaoSignatureVerifying { - func verifySignature(_ cacao: Cacao) throws -} - -struct CacaoSigner: CacaoSignatureVerifying { - - enum Errors: Error { - case signatureValidationFailed - } - - private let signer: Signer - - init(signer: Signer) { - self.signer = signer - } - - func sign(payload: CacaoPayload, privateKey: Data) throws -> CacaoSignature { - let message = try JSONEncoder().encode(payload) // TODO: SIWE encoding - let signature = try signer.sign(message: message, with: privateKey) - return CacaoSignature(t: "eip191", s: signature.toHexString(), m: String()) - } - - func verifySignature(_ cacao: Cacao) throws { - let sig = Data(hex: cacao.signature.s) - let message = try JSONEncoder().encode(cacao.payload) - let address = try DIDPKH(iss: cacao.payload.iss).account.address - guard try signer.isValid(signature: sig, message: message, address: address) - else { throw Errors.signatureValidationFailed } - } -} diff --git a/Sources/Auth/Services/Signer/MessageSigner.swift b/Sources/Auth/Services/Signer/MessageSigner.swift new file mode 100644 index 000000000..6ae159058 --- /dev/null +++ b/Sources/Auth/Services/Signer/MessageSigner.swift @@ -0,0 +1,36 @@ +import Foundation + +protocol MessageSignatureVerifying { + func verify(signature: String, message: String, address: String) throws +} + +protocol MessageSignatureSigning { + func sign(message: String, privateKey: Data) throws -> String +} + +struct MessageSigner: MessageSignatureVerifying & MessageSignatureSigning { + + enum Errors: Error { + case signatureValidationFailed + case utf8EncodingFailed + } + + private let signer: Signer + + init(signer: Signer) { + self.signer = signer + } + + func sign(message: String, privateKey: Data) throws -> String { + guard let messageData = message.data(using: .utf8) else { throw Errors.utf8EncodingFailed } + let signature = try signer.sign(message: messageData, with: privateKey) + return signature.toHexString() + } + + func verify(signature: String, message: String, address: String) throws { + guard let messageData = message.data(using: .utf8) else { throw Errors.utf8EncodingFailed } + let signatureData = Data(hex: signature) + guard try signer.isValid(signature: signatureData, message: messageData, address: address) + else { throw Errors.signatureValidationFailed } + } +} diff --git a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift index 54819c1da..ddd28089c 100644 --- a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift +++ b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift @@ -10,7 +10,7 @@ class WalletRequestSubscriber { private var publishers = [AnyCancellable]() private let messageFormatter: SIWEMessageFormatting var onRequest: ((_ id: RPCID, _ message: String)->Void)? - + init(networkingInteractor: NetworkInteracting, logger: ConsoleLogging, messageFormatter: SIWEMessageFormatting, @@ -21,18 +21,27 @@ class WalletRequestSubscriber { self.messageFormatter = messageFormatter subscribeForRequest() } - + private func subscribeForRequest() { networkingInteractor.requestPublisher.sink { [unowned self] subscriptionPayload in - guard subscriptionPayload.request.method == "wc_authRequest" else { return } - guard let authRequestParams = try? subscriptionPayload.request.params?.get(AuthRequestParams.self) else { - logger.debug("Malformed auth request params") - return + 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) } - let message = messageFormatter.formatMessage(from: authRequestParams.payloadParams, address: address) - guard let requestId = subscriptionPayload.request.id else { return } - onRequest?(requestId, message) }.store(in: &publishers) } - + } diff --git a/Sources/Auth/Types/AuthPayload.swift b/Sources/Auth/Types/AuthPayload.swift index 35a4b2338..43332b81e 100644 --- a/Sources/Auth/Types/AuthPayload.swift +++ b/Sources/Auth/Types/AuthPayload.swift @@ -28,4 +28,19 @@ struct AuthPayload: Codable, Equatable { self.requestId = requestParams.requestId self.resources = requestParams.resources } + + init(payload: CacaoPayload) { + self.type = "eip4361" + self.chainId = "1" // TODO: Check this! + self.domain = payload.domain + self.aud = payload.aud + self.version = payload.version + self.nonce = payload.nonce + self.iat = payload.iat + self.nbf = payload.nbf + self.exp = payload.exp + self.statement = payload.statement + self.requestId = payload.requestId + self.resources = payload.resources + } } diff --git a/Tests/AuthTests/CacaoSignerTests.swift b/Tests/AuthTests/CacaoSignerTests.swift index 1336f0ba3..a1e838716 100644 --- a/Tests/AuthTests/CacaoSignerTests.swift +++ b/Tests/AuthTests/CacaoSignerTests.swift @@ -7,44 +7,33 @@ class CacaoSignerTest: XCTestCase { let privateKey = Data(hex: "305c6cde3846927892cd32762f6120539f3ec74c9e3a16b9b798b1e85351ae2a") - let payload = CacaoPayload( - iss: "did:pkh:eip155:1:0x15bca56b6e2728aec2532df9d436bd1600e86688", - domain: "localhost:3000", - aud: "http://localhost:3000/login", - version: 1, - nonce: "328917", - iat: "2022-03-10T17:09:21.481+03:00", - nbf: "2022-03-10T17:09:21.481+03:00", - exp: "2022-03-10T18:09:21.481+03:00", - statement: "I accept the ServiceOrg Terms of Service: https://service.org/tos", - requestId: "request-id-random", - resources: [ - "ipfs://bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq", - "https://example.com/my-web2-claim.json" - ] - ) - - var cacao: Cacao { - return Cacao( - header: .init(t: ""), - payload: payload, - signature: sig - ) - } + let message: String = """ + service.invalid wants you to sign in with your Ethereum account: + 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 - let sig = CacaoSignature(t: "eip191", s: "914b8300e471744f506407aa072cdf9a606fd3fe1a6f2a16c9f78009074c69622143c3009f4ccdedc0fdd421e5579c5e11b3a604e0a3e6ae0cb06b5e380879fb00", m: "") + I accept the ServiceOrg Terms of Service: https://service.invalid/tos - func testCacaoSign() throws { - let signer = CacaoSigner(signer: Signer()) + URI: https://service.invalid/login + Version: 1 + Chain ID: 1 + Nonce: 32891756 + Issued At: 2021-09-30T16:25:24Z + Resources: + - ipfs://bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq/ + - https://example.com/my-web2-claim.json + """ - let signature = try signer.sign(payload: payload, privateKey: privateKey) + let signature = "df33c1bb9d0a7934e6b0861d6286d5d223eb679d059fff89ee03530f30cd8d4a767ad28abdab90268a0052277b43f83b26b45194c2eefc5a46c9de727edc098001" + + func testCacaoSign() throws { + let signer = MessageSigner(signer: Signer()) - XCTAssertEqual(signature, sig) + XCTAssertEqual(try signer.sign(message: message, privateKey: privateKey), signature) } func testCacaoVerify() throws { - let signer = CacaoSigner(signer: Signer()) + let signer = MessageSigner(signer: Signer()) - try signer.verifySignature(cacao) + try signer.verify(signature: signature, message: message, address: "0x15bca56b6e2728aec2532df9d436bd1600e86688") } } From b734184f9d1d14ec51696d9b0fc0ed071fff5184 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 11 Aug 2022 16:57:27 +0300 Subject: [PATCH 27/89] PR suggestions --- .../Services/App/AppRespondSubscriber.swift | 14 ++++-- .../Common/SIWEMessageFormatter.swift | 46 ++++++++++++++----- .../Auth/Services/Signer/MessageSigner.swift | 4 +- Sources/Auth/Types/AuthPayload.swift | 15 ------ .../Mocks/SIWEMessageFormatterMock.swift | 5 ++ 5 files changed, 51 insertions(+), 33 deletions(-) diff --git a/Sources/Auth/Services/App/AppRespondSubscriber.swift b/Sources/Auth/Services/App/AppRespondSubscriber.swift index 6250c7374..9e54fcf24 100644 --- a/Sources/Auth/Services/App/AppRespondSubscriber.swift +++ b/Sources/Auth/Services/App/AppRespondSubscriber.swift @@ -30,7 +30,8 @@ class AppRespondSubscriber { guard let requestId = subscriptionPayload.request.id, let request = rpcHistory.get(recordId: requestId)?.request, - request.method == "wc_authRequest" else { return } + let requestParams = request.params, request.method == "wc_authRequest" + else { return } networkingInteractor.unsubscribe(topic: subscriptionPayload.topic) @@ -39,14 +40,19 @@ class AppRespondSubscriber { return logger.debug("Malformed auth response params") } + let requestPayload = try requestParams.get(AuthRequestParams.self) let address = try DIDPKH(iss: cacao.payload.iss).account.address - let payload = AuthPayload(payload: cacao.payload) - let message = messageFormatter.formatMessage(from: payload, address: address) + let message = try messageFormatter.formatMessage(from: cacao.payload) + let originalMessage = messageFormatter.formatMessage(from: requestPayload.payloadParams, address: address) + + guard originalMessage == message else { + return logger.debug("Original message compromised") + } try signatureVerifier.verify( signature: cacao.signature.s, message: message, - address: cacao.payload.iss + address: address ) onResponse?(requestId, cacao) } catch { diff --git a/Sources/Auth/Services/Common/SIWEMessageFormatter.swift b/Sources/Auth/Services/Common/SIWEMessageFormatter.swift index 1861e2808..64f6a6ced 100644 --- a/Sources/Auth/Services/Common/SIWEMessageFormatter.swift +++ b/Sources/Auth/Services/Common/SIWEMessageFormatter.swift @@ -3,22 +3,44 @@ import WalletConnectUtils protocol SIWEMessageFormatting { func formatMessage(from authPayload: AuthPayload, address: String) -> String + func formatMessage(from payload: CacaoPayload) throws -> String } struct SIWEMessageFormatter: SIWEMessageFormatting { - func formatMessage(from authPayload: AuthPayload, address: String) -> String { - SIWEMessage(domain: authPayload.domain, - uri: authPayload.aud, + func formatMessage(from payload: AuthPayload, address: String) -> String { + let message = SIWEMessage(domain: payload.domain, + uri: payload.aud, address: address, - version: authPayload.version, - nonce: authPayload.nonce, - chainId: authPayload.chainId, - iat: authPayload.iat, - nbf: authPayload.nbf, - exp: authPayload.exp, - statement: authPayload.statement, - requestId: authPayload.requestId, - resources: authPayload.resources).formatted + version: payload.version, + nonce: payload.nonce, + chainId: payload.chainId, + iat: payload.iat, + nbf: payload.nbf, + exp: payload.exp, + statement: payload.statement, + requestId: payload.requestId, + resources: payload.resources + ) + return message.formatted + } + + func formatMessage(from payload: CacaoPayload) throws -> String { + let address = try DIDPKH(iss: payload.iss).account.address + let message = SIWEMessage( + domain: payload.domain, + uri: payload.aud, + address: address, + version: payload.version, + nonce: payload.nonce, + chainId: "1", + iat: payload.iat, + nbf: payload.nbf, + exp: payload.exp, + statement: payload.statement, + requestId: payload.requestId, + resources: payload.resources + ) + return message.formatted } } diff --git a/Sources/Auth/Services/Signer/MessageSigner.swift b/Sources/Auth/Services/Signer/MessageSigner.swift index 6ae159058..a00c9cd0c 100644 --- a/Sources/Auth/Services/Signer/MessageSigner.swift +++ b/Sources/Auth/Services/Signer/MessageSigner.swift @@ -4,11 +4,11 @@ protocol MessageSignatureVerifying { func verify(signature: String, message: String, address: String) throws } -protocol MessageSignatureSigning { +protocol MessageSigning { func sign(message: String, privateKey: Data) throws -> String } -struct MessageSigner: MessageSignatureVerifying & MessageSignatureSigning { +struct MessageSigner: MessageSignatureVerifying & MessageSigning { enum Errors: Error { case signatureValidationFailed diff --git a/Sources/Auth/Types/AuthPayload.swift b/Sources/Auth/Types/AuthPayload.swift index 43332b81e..35a4b2338 100644 --- a/Sources/Auth/Types/AuthPayload.swift +++ b/Sources/Auth/Types/AuthPayload.swift @@ -28,19 +28,4 @@ struct AuthPayload: Codable, Equatable { self.requestId = requestParams.requestId self.resources = requestParams.resources } - - init(payload: CacaoPayload) { - self.type = "eip4361" - self.chainId = "1" // TODO: Check this! - self.domain = payload.domain - self.aud = payload.aud - self.version = payload.version - self.nonce = payload.nonce - self.iat = payload.iat - self.nbf = payload.nbf - self.exp = payload.exp - self.statement = payload.statement - self.requestId = payload.requestId - self.resources = payload.resources - } } diff --git a/Tests/AuthTests/Mocks/SIWEMessageFormatterMock.swift b/Tests/AuthTests/Mocks/SIWEMessageFormatterMock.swift index bce7c4824..086ebba3c 100644 --- a/Tests/AuthTests/Mocks/SIWEMessageFormatterMock.swift +++ b/Tests/AuthTests/Mocks/SIWEMessageFormatterMock.swift @@ -3,7 +3,12 @@ import Foundation class SIWEMessageFormatterMock: SIWEMessageFormatting { var formattedMessage: String! + func formatMessage(from authPayload: AuthPayload, address: String) -> String { return formattedMessage } + + func formatMessage(from payload: CacaoPayload) throws -> String { + return formattedMessage + } } From 959f1f8fc8cd3feed96ac1aee445fbda373e7fcd Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 11 Aug 2022 17:39:23 +0300 Subject: [PATCH 28/89] macos-latest --- .github/workflows/ci.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8495f8859..bcbcf09cf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ concurrency: jobs: build: - runs-on: macos-12 + runs-on: macos-latest strategy: matrix: test-type: [unit-tests, integration-tests, build-example-wallet, build-example-dapp] @@ -27,8 +27,6 @@ jobs: - name: Setup Xcode Version uses: maxim-lobanov/setup-xcode@v1 - with: - xcode-version: '13.4.1' - name: Resolve Dependencies shell: bash From 9051a0b4e57643090ff2a78cd3cc2f65a7939ca4 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Tue, 9 Aug 2022 21:28:15 +0300 Subject: [PATCH 29/89] WalletConnectRouter --- .../DApp/SelectChain/SelectChainView.swift | 19 +++++++++++++- .../SelectChainViewController.swift | 6 +++++ Example/ExampleApp.xcodeproj/project.pbxproj | 7 +++++ .../Wallet/WalletViewController.swift | 7 +++++ Package.swift | 7 +++-- Sources/WalletConnectRouter/Router.m | 26 +++++++++++++++++++ Sources/WalletConnectRouter/include/Router.h | 5 ++++ .../include/module.modulemap | 3 +++ 8 files changed, 77 insertions(+), 3 deletions(-) create mode 100644 Sources/WalletConnectRouter/Router.m create mode 100644 Sources/WalletConnectRouter/include/Router.h create mode 100644 Sources/WalletConnectRouter/include/module.modulemap diff --git a/Example/DApp/SelectChain/SelectChainView.swift b/Example/DApp/SelectChain/SelectChainView.swift index 1e54ad348..934bfe45c 100644 --- a/Example/DApp/SelectChain/SelectChainView.swift +++ b/Example/DApp/SelectChain/SelectChainView.swift @@ -2,12 +2,14 @@ import Foundation import UIKit class SelectChainView: UIView { + let tableView: UITableView = { let tableView = UITableView(frame: .zero, style: .insetGrouped) tableView.backgroundColor = .tertiarySystemBackground tableView.register(UITableViewCell.self, forCellReuseIdentifier: "chain") return tableView }() + let connectButton: UIButton = { let button = UIButton(type: .system) button.setTitle("Connect", for: .normal) @@ -17,12 +19,22 @@ class SelectChainView: UIView { return button }() + let openWallet: UIButton = { + let button = UIButton(type: .system) + button.setTitle("Open Wallet", for: .normal) + button.backgroundColor = .systemFill + button.tintColor = .white + button.layer.cornerRadius = 8 + return button + }() + override init(frame: CGRect) { super.init(frame: frame) tableView.register(UITableViewCell.self, forCellReuseIdentifier: "chain_cell") backgroundColor = .systemBackground addSubview(tableView) addSubview(connectButton) + addSubview(openWallet) subviews.forEach { $0.translatesAutoresizingMaskIntoConstraints = false } @@ -35,7 +47,12 @@ class SelectChainView: UIView { connectButton.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor, constant: -16), connectButton.centerXAnchor.constraint(equalTo: safeAreaLayoutGuide.centerXAnchor), connectButton.heightAnchor.constraint(equalToConstant: 44), - connectButton.widthAnchor.constraint(equalToConstant: 120) + connectButton.widthAnchor.constraint(equalToConstant: 120), + + openWallet.bottomAnchor.constraint(equalTo: connectButton.topAnchor, constant: -16), + openWallet.centerXAnchor.constraint(equalTo: safeAreaLayoutGuide.centerXAnchor), + openWallet.heightAnchor.constraint(equalToConstant: 44), + openWallet.widthAnchor.constraint(equalToConstant: 120) ]) } diff --git a/Example/DApp/SelectChain/SelectChainViewController.swift b/Example/DApp/SelectChain/SelectChainViewController.swift index bfe28634b..4ca4b6c65 100644 --- a/Example/DApp/SelectChain/SelectChainViewController.swift +++ b/Example/DApp/SelectChain/SelectChainViewController.swift @@ -21,6 +21,7 @@ class SelectChainViewController: UIViewController, UITableViewDataSource { navigationItem.title = "Available Chains" selectChainView.tableView.dataSource = self selectChainView.connectButton.addTarget(self, action: #selector(connect), for: .touchUpInside) + selectChainView.openWallet.addTarget(self, action: #selector(openWallet), for: .touchUpInside) Sign.instance.sessionSettlePublisher.sink {[unowned self] session in onSessionSettled?(session) }.store(in: &publishers) @@ -42,6 +43,11 @@ class SelectChainViewController: UIViewController, UITableViewDataSource { } } + @objc + private func openWallet() { + UIApplication.shared.open(URL(string: "walletconnectwallet://")!) + } + private func showConnectScreen(uriString: String) { DispatchQueue.main.async { [unowned self] in let vc = UINavigationController(rootViewController: ConnectViewController(uri: uriString)) diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index 0f9247588..91b9ee090 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -121,6 +121,7 @@ A5C20229287EB34C007E3188 /* AccountStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5C20228287EB34C007E3188 /* AccountStorage.swift */; }; A5C2022B287EB89A007E3188 /* WelcomeInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5C2022A287EB89A007E3188 /* WelcomeInteractor.swift */; }; A5C2022D287EC3F0007E3188 /* RegisterService.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5C2022C287EC3F0007E3188 /* RegisterService.swift */; }; + A5C4DD8728A2DE88006A626D /* WalletConnectRouter in Frameworks */ = {isa = PBXBuildFile; productRef = A5C4DD8628A2DE88006A626D /* WalletConnectRouter */; }; A5D85226286333D500DAF5C3 /* Starscream in Frameworks */ = {isa = PBXBuildFile; productRef = A5D85225286333D500DAF5C3 /* Starscream */; }; A5D85228286333E300DAF5C3 /* Starscream in Frameworks */ = {isa = PBXBuildFile; productRef = A5D85227286333E300DAF5C3 /* Starscream */; }; A5E03DF52864651200888481 /* Starscream in Frameworks */ = {isa = PBXBuildFile; productRef = A5E03DF42864651200888481 /* Starscream */; }; @@ -305,6 +306,7 @@ A5AE354728A1A2AC0059AE8A /* Web3 in Frameworks */, 764E1D5826F8DBAB00A1FB15 /* WalletConnect in Frameworks */, A5D85226286333D500DAF5C3 /* Starscream in Frameworks */, + A5C4DD8728A2DE88006A626D /* WalletConnectRouter in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -931,6 +933,7 @@ 764E1D5726F8DBAB00A1FB15 /* WalletConnect */, A5D85225286333D500DAF5C3 /* Starscream */, A5AE354628A1A2AC0059AE8A /* Web3 */, + A5C4DD8628A2DE88006A626D /* WalletConnectRouter */, ); productName = ExampleApp; productReference = 764E1D3C26F8D3FC00A1FB15 /* WalletConnect Wallet.app */; @@ -1778,6 +1781,10 @@ package = A5AE354528A1A2AC0059AE8A /* XCRemoteSwiftPackageReference "Web3" */; productName = Web3; }; + A5C4DD8628A2DE88006A626D /* WalletConnectRouter */ = { + isa = XCSwiftPackageProductDependency; + productName = WalletConnectRouter; + }; A5D85225286333D500DAF5C3 /* Starscream */ = { isa = XCSwiftPackageProductDependency; package = A5D85224286333D500DAF5C3 /* XCRemoteSwiftPackageReference "Starscream" */; diff --git a/Example/ExampleApp/Wallet/WalletViewController.swift b/Example/ExampleApp/Wallet/WalletViewController.swift index 929aad4b0..5553c823e 100644 --- a/Example/ExampleApp/Wallet/WalletViewController.swift +++ b/Example/ExampleApp/Wallet/WalletViewController.swift @@ -1,6 +1,7 @@ import UIKit import WalletConnectSign import WalletConnectUtils +import WalletConnectRouter import Web3 import CryptoSwift import Combine @@ -23,6 +24,7 @@ final class WalletViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() navigationItem.title = "Wallet" + navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Back", style: .plain, target: self, action: #selector(goBack)) walletView.scanButton.addTarget(self, action: #selector(showScanner), for: .touchUpInside) walletView.pasteButton.addTarget(self, action: #selector(showTextInput), for: .touchUpInside) @@ -48,6 +50,11 @@ final class WalletViewController: UIViewController { present(alert, animated: true) } + @objc + private func goBack() { + Router.goBack() + } + private func showSessionProposal(_ proposal: Proposal) { let proposalViewController = ProposalViewController(proposal: proposal) proposalViewController.delegate = self diff --git a/Package.swift b/Package.swift index a077edd12..46b112754 100644 --- a/Package.swift +++ b/Package.swift @@ -17,8 +17,8 @@ let package = Package( name: "WalletConnectChat", targets: ["Chat"]), .library( - name: "WalletConnectAuth", - targets: ["Auth"]) + name: "WalletConnectRouter", + targets: ["WalletConnectRouter"]), ], dependencies: [ .package(url: "https://github.com/flypaper0/Web3.swift", .branch("master")) @@ -62,6 +62,9 @@ let package = Package( .target( name: "Commons", dependencies: []), + .target( + name: "WalletConnectRouter", + dependencies: []), .testTarget( name: "WalletConnectSignTests", dependencies: ["WalletConnectSign", "TestingUtils"]), diff --git a/Sources/WalletConnectRouter/Router.m b/Sources/WalletConnectRouter/Router.m new file mode 100644 index 000000000..9010aa7e6 --- /dev/null +++ b/Sources/WalletConnectRouter/Router.m @@ -0,0 +1,26 @@ +#import +#import "Router.h" + +@import UIKit; +@import ObjectiveC.runtime; + +@interface UISystemNavigationAction : NSObject +@property(nonatomic, readonly, nonnull) NSArray* destinations; +-(BOOL)sendResponseForDestination:(NSUInteger)destination; +@end + +@implementation Router + ++ (void)goBack { + Ivar sysNavIvar = class_getInstanceVariable(UIApplication.class, "_systemNavigationAction"); + UIApplication* app = UIApplication.sharedApplication; + UISystemNavigationAction* action = object_getIvar(app, sysNavIvar); + if (!action) { + return; + } + NSUInteger destination = action.destinations.firstObject.unsignedIntegerValue; + [action sendResponseForDestination:destination]; +} + +@end + diff --git a/Sources/WalletConnectRouter/include/Router.h b/Sources/WalletConnectRouter/include/Router.h new file mode 100644 index 000000000..222fbe4cc --- /dev/null +++ b/Sources/WalletConnectRouter/include/Router.h @@ -0,0 +1,5 @@ +#import + +@interface Router: NSObject ++ (void)goBack; +@end diff --git a/Sources/WalletConnectRouter/include/module.modulemap b/Sources/WalletConnectRouter/include/module.modulemap new file mode 100644 index 000000000..036065d03 --- /dev/null +++ b/Sources/WalletConnectRouter/include/module.modulemap @@ -0,0 +1,3 @@ +module WalletConnectRouter { + header "Router.h" +} From a1c6e0275768bcaac0015ee2d26ab36c7bba9f38 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Fri, 12 Aug 2022 15:04:18 +0300 Subject: [PATCH 30/89] EIP-155 Signature fix --- Example/ExampleApp.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/swiftpm/Package.resolved | 4 +-- Package.swift | 2 +- Sources/Auth/Services/Signer/Signer.swift | 11 ++++-- Tests/AuthTests/CacaoSignerTests.swift | 35 ++++++++++--------- Tests/AuthTests/SignerTests.swift | 16 +++++++-- 6 files changed, 45 insertions(+), 25 deletions(-) diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index 91b9ee090..d5c5c017c 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -1744,7 +1744,7 @@ isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/flypaper0/Web3.swift"; requirement = { - branch = master; + branch = "feature/eip-155"; kind = branch; }; }; diff --git a/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index ab8883095..1b5f80e67 100644 --- a/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -59,8 +59,8 @@ "package": "Web3", "repositoryURL": "https://github.com/flypaper0/Web3.swift", "state": { - "branch": "master", - "revision": "23b6940bbda0769d9147bec6696b33a9fee0b120", + "branch": "feature/eip-155", + "revision": "92a43a8c279b9df25fe23dd6f8311e6fb0ea06ed", "version": null } } diff --git a/Package.swift b/Package.swift index 46b112754..82d3543a0 100644 --- a/Package.swift +++ b/Package.swift @@ -21,7 +21,7 @@ let package = Package( targets: ["WalletConnectRouter"]), ], dependencies: [ - .package(url: "https://github.com/flypaper0/Web3.swift", .branch("master")) + .package(url: "https://github.com/flypaper0/Web3.swift", .branch("feature/eip-155")) ], targets: [ .target( diff --git a/Sources/Auth/Services/Signer/Signer.swift b/Sources/Auth/Services/Signer/Signer.swift index b4b02ebbe..a1ba7a6e6 100644 --- a/Sources/Auth/Services/Signer/Signer.swift +++ b/Sources/Auth/Services/Signer/Signer.swift @@ -6,15 +6,17 @@ struct Signer { typealias Signature = (v: UInt, r: [UInt8], s: [UInt8]) func sign(message: Data, with key: Data) throws -> Data { + let prefixed = prefixed(message: message) let privateKey = try EthereumPrivateKey(privateKey: key.bytes) - let signature = try privateKey.sign(message: message.bytes) + let signature = try privateKey.sign(message: prefixed.bytes) return serialized(signature: signature) } func isValid(signature: Data, message: Data, address: String) throws -> Bool { let sig = decompose(signature: signature) + let prefixed = prefixed(message: message) let publicKey = try EthereumPublicKey( - message: message.bytes, + message: prefixed.bytes, v: EthereumQuantity(quantity: BigUInt(sig.v)), r: EthereumQuantity(sig.r), s: EthereumQuantity(sig.s) @@ -32,4 +34,9 @@ struct Signer { private func serialized(signature: Signature) -> Data { return Data(signature.r + signature.s + [UInt8(signature.v)]) } + + private func prefixed(message: Data) -> Data { + return "\u{19}Ethereum Signed Message:\n\(message.count)" + .data(using: .utf8)! + message + } } diff --git a/Tests/AuthTests/CacaoSignerTests.swift b/Tests/AuthTests/CacaoSignerTests.swift index a1e838716..a1615a361 100644 --- a/Tests/AuthTests/CacaoSignerTests.swift +++ b/Tests/AuthTests/CacaoSignerTests.swift @@ -7,23 +7,24 @@ class CacaoSignerTest: XCTestCase { let privateKey = Data(hex: "305c6cde3846927892cd32762f6120539f3ec74c9e3a16b9b798b1e85351ae2a") - let message: String = """ - service.invalid wants you to sign in with your Ethereum account: - 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 - - I accept the ServiceOrg Terms of Service: https://service.invalid/tos - - URI: https://service.invalid/login - Version: 1 - Chain ID: 1 - Nonce: 32891756 - Issued At: 2021-09-30T16:25:24Z - Resources: - - ipfs://bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq/ - - https://example.com/my-web2-claim.json - """ - - let signature = "df33c1bb9d0a7934e6b0861d6286d5d223eb679d059fff89ee03530f30cd8d4a767ad28abdab90268a0052277b43f83b26b45194c2eefc5a46c9de727edc098001" + let message: String = + """ + service.invalid wants you to sign in with your Ethereum account: + 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 + + I accept the ServiceOrg Terms of Service: https://service.invalid/tos + + URI: https://service.invalid/login + Version: 1 + Chain ID: 1 + Nonce: 32891756 + Issued At: 2021-09-30T16:25:24Z + Resources: + - ipfs://bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq/ + - https://example.com/my-web2-claim.json + """ + + let signature = "438effc459956b57fcd9f3dac6c675f9cee88abf21acab7305e8e32aa0303a883b06dcbd956279a7a2ca21ffa882ff55cc22e8ab8ec0f3fe90ab45f306938cfa1b" func testCacaoSign() throws { let signer = MessageSigner(signer: Signer()) diff --git a/Tests/AuthTests/SignerTests.swift b/Tests/AuthTests/SignerTests.swift index b5488ddb5..5f546bd08 100644 --- a/Tests/AuthTests/SignerTests.swift +++ b/Tests/AuthTests/SignerTests.swift @@ -11,16 +11,28 @@ class SignerTest: XCTestCase { private let message = "Message".data(using: .utf8)! private let privateKey = Data(hex: "305c6cde3846927892cd32762f6120539f3ec74c9e3a16b9b798b1e85351ae2a") - private let signature = Data(hex: "f7d00a04559bff462f02194874b1ae7d4a8f0461acbe4be73386ebe982a9b9dc599abf31107e1ba708a3ec72499f1fd73dd390c5ca1a3084abe176de0529d00e00") + private let signature = Data(hex: "66121e60cccc01fbf7fcba694a1e08ac5db35fb4ec6c045bedba7860445b95c021cad2c595f0bf68ff896964c7c02bb2f3a3e9540e8e4595c98b737ce264cc541b") private var address = "0x15bca56b6e2728aec2532df9d436bd1600e86688" func testValidSignature() throws { let result = try signer.sign(message: message, with: privateKey) - XCTAssertEqual(signature, result) + XCTAssertEqual(signature.toHexString(), result.toHexString()) XCTAssertTrue(try signer.isValid(signature: result, message: message, address: address)) } + func testEtherscanSignature() throws { + let addressFromEtherscan = "0x6721591d424c18b7173d55895efa1839aa57d9c2" + let message = "[Etherscan.io 12/08/2022 09:26:23] I, hereby verify that I am the owner/creator of the address [0x7e77dcb127f99ece88230a64db8d595f31f1b068]" + let signedMessageFromEtherscan = message.data(using: .utf8)! + let signatureHashFromEtherscan = Data(hex: "60eb9cfe362210f1b4855f4865eafc378bd442c406de22354cc9f643fb84cb265b7f6d9d10b13199e450558c328814a9038884d9993d9feb79b727366736853d1b") + XCTAssertTrue(try signer.isValid( + signature: signatureHashFromEtherscan, + message: signedMessageFromEtherscan, + address: addressFromEtherscan + )) + } + func testInvalidMessage() throws { let message = "Message One".data(using: .utf8)! From c24e74d900743340587f05ff504efed5c29f1a74 Mon Sep 17 00:00:00 2001 From: Maisa Date: Sat, 13 Aug 2022 23:35:25 -0300 Subject: [PATCH 31/89] 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 659ad74c0bd77f24eef6ea12547de5695835c4cb Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Mon, 15 Aug 2022 21:35:10 +0300 Subject: [PATCH 32/89] Relay singleton --- Sources/WalletConnectRelay/Relay.swift | 34 ++++++++++++++++++++ Sources/WalletConnectRelay/RelayConfig.swift | 10 ++++++ Sources/WalletConnectSign/Sign/Sign.swift | 12 +++---- 3 files changed, 48 insertions(+), 8 deletions(-) create mode 100644 Sources/WalletConnectRelay/Relay.swift create mode 100644 Sources/WalletConnectRelay/RelayConfig.swift diff --git a/Sources/WalletConnectRelay/Relay.swift b/Sources/WalletConnectRelay/Relay.swift new file mode 100644 index 000000000..f9282aeed --- /dev/null +++ b/Sources/WalletConnectRelay/Relay.swift @@ -0,0 +1,34 @@ +import Foundation + +public class Relay { + + public static var instance: RelayClient = { + guard let config = Relay.config else { + fatalError("Error - you must call Relay.configure(_:) before accessing the shared instance.") + } + return RelayClient( + relayHost: config.relayHost, + projectId: config.projectId, + socketFactory: config.socketFactory, + socketConnectionType: config.socketConnectionType + ) + }() + + private static var config: Config? + + private init() { } + + static public func configure( + relayHost: String, + projectId: String, + socketFactory: WebSocketFactory, + socketConnectionType: SocketConnectionType + ) { + Relay.config = Relay.Config( + relayHost: relayHost, + projectId: projectId, + socketFactory: socketFactory, + socketConnectionType: socketConnectionType + ) + } +} diff --git a/Sources/WalletConnectRelay/RelayConfig.swift b/Sources/WalletConnectRelay/RelayConfig.swift new file mode 100644 index 000000000..10a224132 --- /dev/null +++ b/Sources/WalletConnectRelay/RelayConfig.swift @@ -0,0 +1,10 @@ +import Foundation + +extension Relay { + public struct Config { + let relayHost: String + let projectId: String + let socketFactory: WebSocketFactory + let socketConnectionType: SocketConnectionType + } +} diff --git a/Sources/WalletConnectSign/Sign/Sign.swift b/Sources/WalletConnectSign/Sign/Sign.swift index f00ae09be..11ef7f8ba 100644 --- a/Sources/WalletConnectSign/Sign/Sign.swift +++ b/Sources/WalletConnectSign/Sign/Sign.swift @@ -11,22 +11,18 @@ public class Sign { private static var config: Config? private let client: SignClient - private let relayClient: RelayClient private init() { guard let config = Sign.config else { fatalError("Error - you must call configure(_:) before accessing the shared instance.") } - relayClient = RelayClient( + Relay.configure( relayHost: "relay.walletconnect.com", projectId: config.projectId, socketFactory: config.socketFactory, socketConnectionType: config.socketConnectionType ) - client = SignClientFactory.create( - metadata: config.metadata, - relayClient: relayClient - ) + client = SignClientFactory.create(metadata: config.metadata, relayClient: Relay.instance) client.delegate = self } @@ -260,11 +256,11 @@ extension Sign { } public func connect() throws { - try relayClient.connect() + try Relay.instance.connect() } public func disconnect(closeCode: URLSessionWebSocketTask.CloseCode) throws { - try relayClient.disconnect(closeCode: closeCode) + try Relay.instance.disconnect(closeCode: closeCode) } #if DEBUG From 9acafc3a60a7a4d41383b94359f851b812bf9f4e Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Mon, 15 Aug 2022 22:32:10 +0300 Subject: [PATCH 33/89] SignClent Delegate removed --- Example/ExampleApp.xcodeproj/project.pbxproj | 8 +- .../Sign/Helpers/ClientDelegate.swift | 82 +++--- Sources/WalletConnectSign/Sign/Sign.swift | 262 ++---------------- .../WalletConnectSign/Sign/SignClient.swift | 117 ++++++-- .../Sign/SignClientDelegate.swift | 67 ----- 5 files changed, 165 insertions(+), 371 deletions(-) delete mode 100644 Sources/WalletConnectSign/Sign/SignClientDelegate.swift diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index 91b9ee090..faa1a2c3c 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -45,6 +45,7 @@ 84CE645527A29D4D00142511 /* ResponseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CE645427A29D4C00142511 /* ResponseViewController.swift */; }; 84F568C2279582D200D0A289 /* Signer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F568C1279582D200D0A289 /* Signer.swift */; }; 84F568C42795832A00D0A289 /* EthereumTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F568C32795832A00D0A289 /* EthereumTransaction.swift */; }; + A50C036528AAD32200FE72D3 /* ClientDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A50C036428AAD32200FE72D3 /* ClientDelegate.swift */; }; A50F3946288005B200064555 /* Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = A50F3945288005B200064555 /* Types.swift */; }; A5629AA92876A23100094373 /* ChatService.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5629AA82876A23100094373 /* ChatService.swift */; }; A5629ABD2876CBC000094373 /* ChatListModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5629AB82876CBC000094373 /* ChatListModule.swift */; }; @@ -126,7 +127,6 @@ A5D85228286333E300DAF5C3 /* Starscream in Frameworks */ = {isa = PBXBuildFile; productRef = A5D85227286333E300DAF5C3 /* Starscream */; }; A5E03DF52864651200888481 /* Starscream in Frameworks */ = {isa = PBXBuildFile; productRef = A5E03DF42864651200888481 /* Starscream */; }; A5E03DFA286465C700888481 /* SignClientTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5E03DF9286465C700888481 /* SignClientTests.swift */; }; - A5E03DFB286465C700888481 /* ClientDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5E03DF8286465C700888481 /* ClientDelegate.swift */; }; A5E03DFD286465D100888481 /* Stubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5E03DFC286465D100888481 /* Stubs.swift */; }; A5E03DFF2864662500888481 /* WalletConnect in Frameworks */ = {isa = PBXBuildFile; productRef = A5E03DFE2864662500888481 /* WalletConnect */; }; A5E03E01286466EA00888481 /* WalletConnectChat in Frameworks */ = {isa = PBXBuildFile; productRef = A5E03E00286466EA00888481 /* WalletConnectChat */; }; @@ -205,6 +205,7 @@ 84CE645427A29D4C00142511 /* ResponseViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResponseViewController.swift; sourceTree = ""; }; 84F568C1279582D200D0A289 /* Signer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Signer.swift; sourceTree = ""; }; 84F568C32795832A00D0A289 /* EthereumTransaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EthereumTransaction.swift; sourceTree = ""; }; + A50C036428AAD32200FE72D3 /* ClientDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClientDelegate.swift; sourceTree = ""; }; A50F3945288005B200064555 /* Types.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Types.swift; sourceTree = ""; }; A5629AA82876A23100094373 /* ChatService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatService.swift; sourceTree = ""; }; A5629AB82876CBC000094373 /* ChatListModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatListModule.swift; sourceTree = ""; }; @@ -282,7 +283,6 @@ A5C2022A287EB89A007E3188 /* WelcomeInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeInteractor.swift; sourceTree = ""; }; A5C2022C287EC3F0007E3188 /* RegisterService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegisterService.swift; sourceTree = ""; }; A5E03DED286464DB00888481 /* IntegrationTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = IntegrationTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - A5E03DF8286465C700888481 /* ClientDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientDelegate.swift; sourceTree = ""; }; A5E03DF9286465C700888481 /* SignClientTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignClientTests.swift; sourceTree = ""; }; A5E03DFC286465D100888481 /* Stubs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Stubs.swift; sourceTree = ""; }; A5E03E02286466F400888481 /* ChatTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatTests.swift; sourceTree = ""; }; @@ -441,7 +441,7 @@ 767DC83328997F7600080FA9 /* Helpers */ = { isa = PBXGroup; children = ( - A5E03DF8286465C700888481 /* ClientDelegate.swift */, + A50C036428AAD32200FE72D3 /* ClientDelegate.swift */, 767DC83428997F8E00080FA9 /* EthSendTransaction.swift */, ); path = Helpers; @@ -1260,9 +1260,9 @@ buildActionMask = 2147483647; files = ( 767DC83528997F8E00080FA9 /* EthSendTransaction.swift in Sources */, - A5E03DFB286465C700888481 /* ClientDelegate.swift in Sources */, A5E03E03286466F400888481 /* ChatTests.swift in Sources */, 7694A5262874296A0001257E /* RegistryTests.swift in Sources */, + A50C036528AAD32200FE72D3 /* ClientDelegate.swift in Sources */, A5E03E0D28646AD200888481 /* RelayClientEndToEndTests.swift in Sources */, A5E03DFA286465C700888481 /* SignClientTests.swift in Sources */, A5E03E0F28646D8A00888481 /* WebSocketFactory.swift in Sources */, diff --git a/Example/IntegrationTests/Sign/Helpers/ClientDelegate.swift b/Example/IntegrationTests/Sign/Helpers/ClientDelegate.swift index b048db4f8..ab7125760 100644 --- a/Example/IntegrationTests/Sign/Helpers/ClientDelegate.swift +++ b/Example/IntegrationTests/Sign/Helpers/ClientDelegate.swift @@ -1,10 +1,8 @@ import Foundation @testable import WalletConnectSign +import Combine -class ClientDelegate: SignClientDelegate { - func didChangeSocketConnectionStatus(_ status: SocketConnectionStatus) { - onConnected?() - } +class ClientDelegate { var client: SignClient var onSessionSettled: ((Session) -> Void)? @@ -15,43 +13,55 @@ class ClientDelegate: SignClientDelegate { var onSessionRejected: ((Session.Proposal, Reason) -> Void)? var onSessionDelete: (() -> Void)? var onSessionUpdateNamespaces: ((String, [String: SessionNamespace]) -> Void)? - var onSessionUpdateEvents: ((String, Set) -> Void)? var onSessionExtend: ((String, Date) -> Void)? var onEventReceived: ((Session.Event, String) -> Void)? - var onPairingUpdate: ((Pairing) -> Void)? - internal init(client: SignClient) { + private var publishers = Set() + + init(client: SignClient) { self.client = client - client.delegate = self + setupSubscriptions() } - func didReject(proposal: Session.Proposal, reason: Reason) { - onSessionRejected?(proposal, reason) - } - func didSettle(session: Session) { - onSessionSettled?(session) - } - func didReceive(sessionProposal: Session.Proposal) { - onSessionProposal?(sessionProposal) - } - func didReceive(sessionRequest: Request) { - onSessionRequest?(sessionRequest) - } - func didDelete(sessionTopic: String, reason: Reason) { - onSessionDelete?() - } - func didUpdate(sessionTopic: String, namespaces: [String: SessionNamespace]) { - onSessionUpdateNamespaces?(sessionTopic, namespaces) - } - func didExtend(sessionTopic: String, to date: Date) { - onSessionExtend?(sessionTopic, date) - } - func didReceive(event: Session.Event, sessionTopic: String, chainId: Blockchain?) { - onEventReceived?(event, sessionTopic) - } - func didReceive(sessionResponse: Response) { - onSessionResponse?(sessionResponse) - } + private func setupSubscriptions() { + client.sessionSettlePublisher.sink { session in + self.onSessionSettled?(session) + }.store(in: &publishers) + + client.socketConnectionStatusPublisher.sink { status in + self.onConnected?() + }.store(in: &publishers) + + client.sessionProposalPublisher.sink { proposal in + self.onSessionProposal?(proposal) + }.store(in: &publishers) + + client.sessionRequestPublisher.sink { request in + self.onSessionRequest?(request) + }.store(in: &publishers) + + client.sessionResponsePublisher.sink { response in + self.onSessionResponse?(response) + }.store(in: &publishers) - func didDisconnect() {} + client.sessionRejectionPublisher.sink { (proposal, reason) in + self.onSessionRejected?(proposal, reason) + }.store(in: &publishers) + + client.sessionDeletePublisher.sink { _ in + self.onSessionDelete?() + }.store(in: &publishers) + + client.sessionUpdatePublisher.sink { (topic, namespaces) in + self.onSessionUpdateNamespaces?(topic, namespaces) + }.store(in: &publishers) + + client.sessionEventPublisher.sink { (event, topic, _) in + self.onEventReceived?(event, topic) + }.store(in: &publishers) + + client.sessionExtendPublisher.sink { (topic, date) in + self.onSessionExtend?(topic, date) + }.store(in: &publishers) + } } diff --git a/Sources/WalletConnectSign/Sign/Sign.swift b/Sources/WalletConnectSign/Sign/Sign.swift index 11ef7f8ba..6d66848c8 100644 --- a/Sources/WalletConnectSign/Sign/Sign.swift +++ b/Sources/WalletConnectSign/Sign/Sign.swift @@ -7,24 +7,20 @@ public typealias Account = WalletConnectUtils.Account public typealias Blockchain = WalletConnectUtils.Blockchain public class Sign { - public static let instance = Sign() - private static var config: Config? - private let client: SignClient - - private init() { + public static var instance: SignClient = { guard let config = Sign.config else { - fatalError("Error - you must call configure(_:) before accessing the shared instance.") + fatalError("Error - you must call Sign.configure(_:) before accessing the shared instance.") } - Relay.configure( - relayHost: "relay.walletconnect.com", - projectId: config.projectId, - socketFactory: config.socketFactory, - socketConnectionType: config.socketConnectionType + return SignClientFactory.create( + metadata: config.metadata, + relayClient: Relay.instance ) - client = SignClientFactory.create(metadata: config.metadata, relayClient: Relay.instance) - client.delegate = self - } + }() + + private static var config: Config? + + private init() { } static public func configure( metadata: AppMetadata, @@ -38,237 +34,11 @@ public class Sign { socketFactory: socketFactory, socketConnectionType: socketConnectionType ) + Relay.configure( + relayHost: "relay.walletconnect.com", + projectId: projectId, + socketFactory: socketFactory, + socketConnectionType: socketConnectionType + ) } - - var sessionProposalPublisherSubject = PassthroughSubject() - public var sessionProposalPublisher: AnyPublisher { - sessionProposalPublisherSubject.eraseToAnyPublisher() - } - - var sessionRequestPublisherSubject = PassthroughSubject() - public var sessionRequestPublisher: AnyPublisher { - sessionRequestPublisherSubject.eraseToAnyPublisher() - } - - var socketConnectionStatusPublisherSubject = PassthroughSubject() - public var socketConnectionStatusPublisher: AnyPublisher { - socketConnectionStatusPublisherSubject.eraseToAnyPublisher() - } - - var sessionSettlePublisherSubject = PassthroughSubject() - public var sessionSettlePublisher: AnyPublisher { - sessionSettlePublisherSubject.eraseToAnyPublisher() - } - - var sessionDeletePublisherSubject = PassthroughSubject<(String, Reason), Never>() - public var sessionDeletePublisher: AnyPublisher<(String, Reason), Never> { - sessionDeletePublisherSubject.eraseToAnyPublisher() - } - - var sessionResponsePublisherSubject = PassthroughSubject() - public var sessionResponsePublisher: AnyPublisher { - sessionResponsePublisherSubject.eraseToAnyPublisher() - } - - var sessionRejectionPublisherSubject = PassthroughSubject<(Session.Proposal, Reason), Never>() - public var sessionRejectionPublisher: AnyPublisher<(Session.Proposal, Reason), Never> { - sessionRejectionPublisherSubject.eraseToAnyPublisher() - } - - var sessionUpdatePublisherSubject = PassthroughSubject<(sessionTopic: String, namespaces: [String: SessionNamespace]), Never>() - public var sessionUpdatePublisher: AnyPublisher<(sessionTopic: String, namespaces: [String: SessionNamespace]), Never> { - sessionUpdatePublisherSubject.eraseToAnyPublisher() - } - - var sessionEventPublisherSubject = PassthroughSubject<(event: Session.Event, sessionTopic: String, chainId: Blockchain?), Never>() - public var sessionEventPublisher: AnyPublisher<(event: Session.Event, sessionTopic: String, chainId: Blockchain?), Never> { - sessionEventPublisherSubject.eraseToAnyPublisher() - } - - var sessionExtendPublisherSubject = PassthroughSubject<(sessionTopic: String, date: Date), Never>() - public var sessionExtendPublisher: AnyPublisher<(sessionTopic: String, date: Date), Never> { - sessionExtendPublisherSubject.eraseToAnyPublisher() - } -} - -extension Sign: SignClientDelegate { - - public func didReceive(sessionProposal: Session.Proposal) { - sessionProposalPublisherSubject.send(sessionProposal) - } - - public func didReceive(sessionRequest: Request) { - sessionRequestPublisherSubject.send(sessionRequest) - } - - public func didReceive(sessionResponse: Response) { - sessionResponsePublisherSubject.send(sessionResponse) - } - - public func didDelete(sessionTopic: String, reason: Reason) { - sessionDeletePublisherSubject.send((sessionTopic, reason)) - } - - public func didUpdate(sessionTopic: String, namespaces: [String: SessionNamespace]) { - sessionUpdatePublisherSubject.send((sessionTopic, namespaces)) - } - - public func didExtend(sessionTopic: String, to date: Date) { - sessionExtendPublisherSubject.send((sessionTopic, date)) - } - - public func didSettle(session: Session) { - sessionSettlePublisherSubject.send(session) - } - - public func didReceive(event: Session.Event, sessionTopic: String, chainId: Blockchain?) { - sessionEventPublisherSubject.send((event, sessionTopic, chainId)) - } - - public func didReject(proposal: Session.Proposal, reason: Reason) { - sessionRejectionPublisherSubject.send((proposal, reason)) - } - - public func didChangeSocketConnectionStatus(_ status: SocketConnectionStatus) { - socketConnectionStatusPublisherSubject.send(status) - } -} - -extension Sign { - - /// For the Proposer to propose a session to a responder. - /// Function will create pending pairing sequence or propose a session on existing pairing. When responder client approves pairing, session is be proposed automatically by your client. - /// - Parameter sessionPermissions: The session permissions the responder will be requested for. - /// - Parameter topic: Optional parameter - use it if you already have an established pairing with peer client. - /// - Returns: Pairing URI that should be shared with responder out of bound. Common way is to present it as a QR code. Pairing URI will be nil if you are going to establish a session on existing Pairing and `topic` function parameter was provided. - public func connect(requiredNamespaces: [String: ProposalNamespace], topic: String? = nil) async throws -> String? { - try await client.connect(requiredNamespaces: requiredNamespaces, topic: topic) - } - - /// For responder to receive a session proposal from a proposer - /// Responder should call this function in order to accept peer's pairing proposal and be able to subscribe for future session proposals. - /// - Parameter uri: Pairing URI that is commonly presented as a QR code by a dapp. - /// - /// Should Error: - /// - When URI is invalid format or missing params - /// - When topic is already in use - public func pair(uri: String) async throws { - try await client.pair(uri: uri) - } - - /// For the responder to approve a session proposal. - /// - Parameters: - /// - proposalId: Session Proposal Public key received from peer client in a WalletConnect delegate function: `didReceive(sessionProposal: Session.Proposal)` - /// - accounts: A Set of accounts that the dapp will be allowed to request methods executions on. - /// - methods: A Set of methods that the dapp will be allowed to request. - /// - events: A Set of events - public func approve(proposalId: String, namespaces: [String: SessionNamespace]) async throws { - try await client.approve(proposalId: proposalId, namespaces: namespaces) - } - - /// For the responder to reject a session proposal. - /// - Parameters: - /// - proposalId: Session Proposal Public key received from peer client in a WalletConnect delegate. - /// - reason: Reason why the session proposal was rejected. Conforms to CAIP25. - public func reject(proposalId: String, reason: RejectionReason) async throws { - try await client.reject(proposalId: proposalId, reason: reason) - } - - /// For the responder to update session methods - /// - Parameters: - /// - topic: Topic of the session that is intended to be updated. - /// - methods: Sets of methods that will replace existing ones. - public func update(topic: String, namespaces: [String: SessionNamespace]) async throws { - try await client.update(topic: topic, namespaces: namespaces) - } - - /// For controller to update expiry of a session - /// - Parameters: - /// - topic: Topic of the Session, it can be a pairing or a session topic. - /// - ttl: Time in seconds that a target session is expected to be extended for. Must be greater than current time to expire and than 7 days - public func extend(topic: String) async throws { - try await client.extend(topic: topic) - } - - /// For the proposer to send JSON-RPC requests to responding peer. - /// - Parameters: - /// - params: Parameters defining request and related session - public func request(params: Request) async throws { - try await client.request(params: params) - } - - /// For the responder to respond on pending peer's session JSON-RPC Request - /// - Parameters: - /// - topic: Topic of the session for which the request was received. - /// - response: Your JSON RPC response or an error. - public func respond(topic: String, response: JsonRpcResult) async throws { - try await client.respond(topic: topic, response: response) - } - - /// Ping method allows to check if client's peer is online and is subscribing for your sequence topic - /// - /// Should Error: - /// - When the session topic is not found - /// - When the response is neither result or error - /// - When the peer fails to respond within timeout - /// - /// - Parameters: - /// - topic: Topic of the sequence, it can be a pairing or a session topic. - /// - completion: Result will be success on response or error on timeout. -- TODO: timeout - public func ping(topic: String, completion: @escaping ((Result) -> Void)) { - client.ping(topic: topic, completion: completion) - } - - /// - Parameters: - /// - topic: Session topic - /// - params: Event Parameters - /// - completion: calls a handler upon completion - public func emit(topic: String, event: Session.Event, chainId: Blockchain) async throws { - try await client.emit(topic: topic, event: event, chainId: chainId) - } - - /// - Parameters: - /// - topic: Session topic that you want to delete - public func disconnect(topic: String) async throws { - try await client.disconnect(topic: topic) - } - - /// - Returns: All sessions - public func getSessions() -> [Session] { - client.getSessions() - } - - /// - Returns: All settled pairings that are active - public func getSettledPairings() -> [Pairing] { - client.getSettledPairings() - } - - /// - Returns: Pending requests received with wc_sessionRequest - /// - Parameter topic: topic representing session for which you want to get pending requests. If nil, you will receive pending requests for all active sessions. - public func getPendingRequests(topic: String? = nil) -> [Request] { - client.getPendingRequests(topic: topic) - } - - /// - Parameter id: id of a wc_sessionRequest jsonrpc request - /// - Returns: json rpc record object for given id or nil if record for give id does not exits - public func getSessionRequestRecord(id: Int64) -> WalletConnectUtils.JsonRpcRecord? { - client.getSessionRequestRecord(id: id) - } - - public func connect() throws { - try Relay.instance.connect() - } - - public func disconnect(closeCode: URLSessionWebSocketTask.CloseCode) throws { - try Relay.instance.disconnect(closeCode: closeCode) - } - -#if DEBUG - /// Delete all stored data sach as: pairings, sessions, keys - /// - /// - Note: Doesn't unsubscribe from topics - public func cleanup() throws { - try client.cleanup() - } -#endif } diff --git a/Sources/WalletConnectSign/Sign/SignClient.swift b/Sources/WalletConnectSign/Sign/SignClient.swift index 43df10383..77e5d6d8a 100644 --- a/Sources/WalletConnectSign/Sign/SignClient.swift +++ b/Sources/WalletConnectSign/Sign/SignClient.swift @@ -12,14 +12,83 @@ import Combine /// let metadata = AppMetadata(name: String?, description: String?, url: String?, icons: [String]?) /// let client = SignClient(metadata: AppMetadata, projectId: String, relayHost: String) /// ``` -/// -/// - Parameters: -/// - delegate: The object that acts as the delegate of WalletConnect Client -/// - logger: An object for logging messages public final class SignClient { - public weak var delegate: SignClientDelegate? + /// Tells the delegate that session proposal has been received. + /// + /// Function is executed on responder client only + public var sessionProposalPublisher: AnyPublisher { + sessionProposalPublisherSubject.eraseToAnyPublisher() + } + + /// Tells the delegate that session payload request has been received + /// + /// In most cases that function is supposed to be called on wallet client. + /// - Parameters: + /// - sessionRequest: Object containing request received from peer client. + public var sessionRequestPublisher: AnyPublisher { + sessionRequestPublisherSubject.eraseToAnyPublisher() + } + + /// Tells the delegate that client has connected WebSocket + public var socketConnectionStatusPublisher: AnyPublisher { + socketConnectionStatusPublisherSubject.eraseToAnyPublisher() + } + + /// Tells the delegate that the client has settled a session. + /// + /// Function is executed on proposer and responder client when both communicating peers have successfully established a session. + public var sessionSettlePublisher: AnyPublisher { + sessionSettlePublisherSubject.eraseToAnyPublisher() + } + + /// Tells the delegate that the peer client has terminated the session. + /// + /// Function can be executed on any type of the client. + public var sessionDeletePublisher: AnyPublisher<(String, Reason), Never> { + sessionDeletePublisherSubject.eraseToAnyPublisher() + } + + /// Tells the delegate that session payload response has been received + /// + /// In most cases that function is supposed to be called on dApp client. + /// - Parameters: + /// - sessionResponse: Object containing response received from peer client. + public var sessionResponsePublisher: AnyPublisher { + sessionResponsePublisherSubject.eraseToAnyPublisher() + } + + /// Tells the delegate that peer client has rejected a session proposal. + /// + /// Function will be executed on proposer client only. + public var sessionRejectionPublisher: AnyPublisher<(Session.Proposal, Reason), Never> { + sessionRejectionPublisherSubject.eraseToAnyPublisher() + } + + /// Tells the delegate that methods has been updated in session + /// + /// Function is executed on controller and non-controller client when both communicating peers have successfully updated methods requested by the controller client. + public var sessionUpdatePublisher: AnyPublisher<(sessionTopic: String, namespaces: [String: SessionNamespace]), Never> { + sessionUpdatePublisherSubject.eraseToAnyPublisher() + } + + /// Tells the delegate that event has been received. + public var sessionEventPublisher: AnyPublisher<(event: Session.Event, sessionTopic: String, chainId: Blockchain?), Never> { + sessionEventPublisherSubject.eraseToAnyPublisher() + } + + /// Tells the delegate that session expiry has been updated + /// + /// Function will be executed on controller and non-controller clients. + public var sessionExtendPublisher: AnyPublisher<(sessionTopic: String, date: Date), Never> { + sessionExtendPublisherSubject.eraseToAnyPublisher() + } + + /// An object for logging messages public let logger: ConsoleLogging + + // MARK: - Private properties + private let relayClient: RelayClient private let pairingEngine: PairingEngine private let pairEngine: PairEngine @@ -29,7 +98,19 @@ public final class SignClient { private let controllerSessionStateMachine: ControllerSessionStateMachine private let history: JsonRpcHistory private let cleanupService: CleanupService - private var publishers = [AnyCancellable]() + + private let sessionProposalPublisherSubject = PassthroughSubject() + private let sessionRequestPublisherSubject = PassthroughSubject() + private let socketConnectionStatusPublisherSubject = PassthroughSubject() + private let sessionSettlePublisherSubject = PassthroughSubject() + private let sessionDeletePublisherSubject = PassthroughSubject<(String, Reason), Never>() + private let sessionResponsePublisherSubject = PassthroughSubject() + private let sessionRejectionPublisherSubject = PassthroughSubject<(Session.Proposal, Reason), Never>() + private let sessionUpdatePublisherSubject = PassthroughSubject<(sessionTopic: String, namespaces: [String: SessionNamespace]), Never>() + private let sessionEventPublisherSubject = PassthroughSubject<(event: Session.Event, sessionTopic: String, chainId: Blockchain?), Never>() + private let sessionExtendPublisherSubject = PassthroughSubject<(sessionTopic: String, date: Date), Never>() + + private var publishers = Set() // MARK: - Initialization @@ -238,43 +319,43 @@ public final class SignClient { private func setUpEnginesCallbacks() { approveEngine.onSessionProposal = { [unowned self] proposal in - delegate?.didReceive(sessionProposal: proposal) + sessionProposalPublisherSubject.send(proposal) } approveEngine.onSessionRejected = { [unowned self] proposal, reason in - delegate?.didReject(proposal: proposal, reason: reason.publicRepresentation()) + sessionRejectionPublisherSubject.send((proposal, reason.publicRepresentation())) } approveEngine.onSessionSettle = { [unowned self] settledSession in - delegate?.didSettle(session: settledSession) + sessionSettlePublisherSubject.send(settledSession) } sessionEngine.onSessionRequest = { [unowned self] sessionRequest in - delegate?.didReceive(sessionRequest: sessionRequest) + sessionRequestPublisherSubject.send(sessionRequest) } sessionEngine.onSessionDelete = { [unowned self] topic, reason in - delegate?.didDelete(sessionTopic: topic, reason: reason.publicRepresentation()) + sessionDeletePublisherSubject.send((topic, reason.publicRepresentation())) } controllerSessionStateMachine.onNamespacesUpdate = { [unowned self] topic, namespaces in - delegate?.didUpdate(sessionTopic: topic, namespaces: namespaces) + sessionUpdatePublisherSubject.send((topic, namespaces)) } controllerSessionStateMachine.onExtend = { [unowned self] topic, date in - delegate?.didExtend(sessionTopic: topic, to: date) + sessionExtendPublisherSubject.send((topic, date)) } nonControllerSessionStateMachine.onNamespacesUpdate = { [unowned self] topic, namespaces in - delegate?.didUpdate(sessionTopic: topic, namespaces: namespaces) + sessionUpdatePublisherSubject.send((topic, namespaces)) } nonControllerSessionStateMachine.onExtend = { [unowned self] topic, date in - delegate?.didExtend(sessionTopic: topic, to: date) + sessionExtendPublisherSubject.send((topic, date)) } sessionEngine.onEventReceived = { [unowned self] topic, event, chainId in - delegate?.didReceive(event: event, sessionTopic: topic, chainId: chainId) + sessionEventPublisherSubject.send((event, topic, chainId)) } sessionEngine.onSessionResponse = { [unowned self] response in - delegate?.didReceive(sessionResponse: response) + sessionResponsePublisherSubject.send(response) } } private func setUpConnectionObserving() { relayClient.socketConnectionStatusPublisher.sink { [weak self] status in - self?.delegate?.didChangeSocketConnectionStatus(status) + self?.socketConnectionStatusPublisherSubject.send(status) }.store(in: &publishers) } diff --git a/Sources/WalletConnectSign/Sign/SignClientDelegate.swift b/Sources/WalletConnectSign/Sign/SignClientDelegate.swift deleted file mode 100644 index 2a3823552..000000000 --- a/Sources/WalletConnectSign/Sign/SignClientDelegate.swift +++ /dev/null @@ -1,67 +0,0 @@ -import Foundation -import WalletConnectUtils -import WalletConnectRelay - -/// A protocol that defines methods that SignClient instance call on it's delegate to handle sequences level events -public protocol SignClientDelegate: AnyObject { - - /// Tells the delegate that session proposal has been received. - /// - /// Function is executed on responder client only - func didReceive(sessionProposal: Session.Proposal) - - /// Tells the delegate that session payload request has been received - /// - /// In most cases that function is supposed to be called on wallet client. - /// - Parameters: - /// - sessionRequest: Object containing request received from peer client. - func didReceive(sessionRequest: Request) - - /// Tells the delegate that session payload response has been received - /// - /// In most cases that function is supposed to be called on dApp client. - /// - Parameters: - /// - sessionResponse: Object containing response received from peer client. - func didReceive(sessionResponse: Response) - - /// Tells the delegate that the peer client has terminated the session. - /// - /// Function can be executed on any type of the client. - func didDelete(sessionTopic: String, reason: Reason) - - /// Tells the delegate that methods has been updated in session - /// - /// Function is executed on controller and non-controller client when both communicating peers have successfully updated methods requested by the controller client. - func didUpdate(sessionTopic: String, namespaces: [String: SessionNamespace]) - - /// Tells the delegate that session expiry has been updated - /// - /// Function will be executed on controller and non-controller clients. - func didExtend(sessionTopic: String, to date: Date) - - /// Tells the delegate that the client has settled a session. - /// - /// Function is executed on proposer and responder client when both communicating peers have successfully established a session. - func didSettle(session: Session) - - /// Tells the delegate that event has been received. - func didReceive(event: Session.Event, sessionTopic: String, chainId: Blockchain?) - - /// Tells the delegate that peer client has rejected a session proposal. - /// - /// Function will be executed on proposer client only. - func didReject(proposal: Session.Proposal, reason: Reason) - - /// Tells the delegate that client has connected WebSocket - func didChangeSocketConnectionStatus(_ status: SocketConnectionStatus) -} - -public extension SignClientDelegate { - func didReceive(event: Session.Event, sessionTopic: String, chainId: Blockchain?) {} - func didReject(proposal: Session.Proposal, reason: Reason) {} - func didReceive(sessionRequest: Request) {} - func didReceive(sessionProposal: Session.Proposal) {} - func didReceive(sessionResponse: Response) {} - func didExtend(sessionTopic: String, to date: Date) {} - func didDisconnect() {} -} From 7cec802d49a80e62bcbb2764823b2ee89fceb497 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Mon, 15 Aug 2022 22:47:21 +0300 Subject: [PATCH 34/89] Replace onMessage with combine publisher --- Sources/Chat/NetworkingInteractor.swift | 7 +++++-- Sources/WalletConnectRelay/RelayClient.swift | 12 +++++++++--- .../NetworkInteractor/NetworkInteractor.swift | 7 ++++--- .../NetworkInteractor/NetworkRelaying.swift | 2 +- Tests/RelayerTests/RelayClientTests.swift | 13 +++++++++---- .../Mocks/MockedRelayClient.swift | 14 ++++++++++---- Tests/WalletConnectSignTests/WCRelayTests.swift | 2 +- 7 files changed, 39 insertions(+), 18 deletions(-) diff --git a/Sources/Chat/NetworkingInteractor.swift b/Sources/Chat/NetworkingInteractor.swift index 946a79dbd..c8bf6176c 100644 --- a/Sources/Chat/NetworkingInteractor.swift +++ b/Sources/Chat/NetworkingInteractor.swift @@ -39,6 +39,8 @@ class NetworkingInteractor: NetworkInteracting { private let responsePublisherSubject = PassthroughSubject() var socketConnectionStatusPublisher: AnyPublisher + private var publishers = Set() + init(relayClient: RelayClient, serializer: Serializing, logger: ConsoleLogging, @@ -49,9 +51,10 @@ class NetworkingInteractor: NetworkInteracting { self.jsonRpcHistory = jsonRpcHistory self.logger = logger self.socketConnectionStatusPublisher = relayClient.socketConnectionStatusPublisher - relayClient.onMessage = { [unowned self] topic, message in + + relayClient.messagePublisher.sink { [unowned self] (topic, message) in manageSubscription(topic, message) - } + }.store(in: &publishers) } func request(_ request: JSONRPCRequest, topic: String, envelopeType: Envelope.EnvelopeType) async throws { diff --git a/Sources/WalletConnectRelay/RelayClient.swift b/Sources/WalletConnectRelay/RelayClient.swift index 6be813de9..0c18c01d7 100644 --- a/Sources/WalletConnectRelay/RelayClient.swift +++ b/Sources/WalletConnectRelay/RelayClient.swift @@ -17,14 +17,18 @@ public final class RelayClient { static let historyIdentifier = "com.walletconnect.sdk.relayer_client.subscription_json_rpc_record" - public var onMessage: ((String, String) -> Void)? - let defaultTtl = 6*Time.hour var subscriptions: [String: String] = [:] + public var messagePublisher: AnyPublisher<(topic: String, message: String), Never> { + messagePublisherSubject.eraseToAnyPublisher() + } + public var socketConnectionStatusPublisher: AnyPublisher { socketConnectionStatusPublisherSubject.eraseToAnyPublisher() } + + private let messagePublisherSubject = PassthroughSubject<(topic: String, message: String), Never>() private let socketConnectionStatusPublisherSubject = PassthroughSubject() private let subscriptionResponsePublisherSubject = PassthroughSubject<(RPCID?, String), Never>() @@ -42,6 +46,8 @@ public final class RelayClient { private let concurrentQueue = DispatchQueue(label: "com.walletconnect.sdk.relay_client", attributes: .concurrent) + // MARK: - Initialization + init( dispatcher: Dispatching, logger: ConsoleLogging, @@ -227,7 +233,7 @@ public final class RelayClient { do { try rpcHistory.set(request, forTopic: params.data.topic, emmitedBy: .remote) try acknowledgeRequest(request) - onMessage?(params.data.topic, params.data.message) + messagePublisherSubject.send((params.data.topic, params.data.message)) } catch { logger.error("[RelayClient] RPC History 'set()' error: \(error)") } diff --git a/Sources/WalletConnectSign/NetworkInteractor/NetworkInteractor.swift b/Sources/WalletConnectSign/NetworkInteractor/NetworkInteractor.swift index 05d7e8ad9..3389771b3 100644 --- a/Sources/WalletConnectSign/NetworkInteractor/NetworkInteractor.swift +++ b/Sources/WalletConnectSign/NetworkInteractor/NetworkInteractor.swift @@ -29,7 +29,7 @@ extension NetworkInteracting { class NetworkInteractor: NetworkInteracting { - private var publishers = [AnyCancellable]() + private var publishers = Set() private var relayClient: NetworkRelaying private let serializer: Serializing @@ -183,9 +183,10 @@ class NetworkInteractor: NetworkInteracting { } }.store(in: &publishers) - relayClient.onMessage = { [unowned self] topic, message in - manageSubscription(topic, message) + relayClient.messagePublisher.sink { [weak self] (topic, message) in + self?.manageSubscription(topic, message) } + .store(in: &publishers) } private func manageSubscription(_ topic: String, _ encodedEnvelope: String) { diff --git a/Sources/WalletConnectSign/NetworkInteractor/NetworkRelaying.swift b/Sources/WalletConnectSign/NetworkInteractor/NetworkRelaying.swift index 2ea62420d..5574cf826 100644 --- a/Sources/WalletConnectSign/NetworkInteractor/NetworkRelaying.swift +++ b/Sources/WalletConnectSign/NetworkInteractor/NetworkRelaying.swift @@ -5,7 +5,7 @@ import Combine extension RelayClient: NetworkRelaying {} protocol NetworkRelaying { - var onMessage: ((_ topic: String, _ message: String) -> Void)? {get set} + var messagePublisher: AnyPublisher<(topic: String, message: String), Never> { get } var socketConnectionStatusPublisher: AnyPublisher { get } func connect() throws func disconnect(closeCode: URLSessionWebSocketTask.CloseCode) throws diff --git a/Tests/RelayerTests/RelayClientTests.swift b/Tests/RelayerTests/RelayClientTests.swift index d45bb61fe..f9c9384c2 100644 --- a/Tests/RelayerTests/RelayClientTests.swift +++ b/Tests/RelayerTests/RelayClientTests.swift @@ -10,6 +10,8 @@ final class RelayClientTests: XCTestCase { var sut: RelayClient! var dispatcher: DispatcherMock! + var publishers = Set() + override func setUp() { dispatcher = DispatcherMock() let logger = ConsoleLogger() @@ -29,11 +31,12 @@ final class RelayClientTests: XCTestCase { let subscription = Subscription(id: subscriptionId, topic: topic, message: message) let request = subscription.asRPCRequest() - sut.onMessage = { subscriptionTopic, subscriptionMessage in + sut.messagePublisher.sink { (subscriptionTopic, subscriptionMessage) in XCTAssertEqual(subscriptionMessage, message) XCTAssertEqual(subscriptionTopic, topic) expectation.fulfill() - } + }.store(in: &publishers) + dispatcher.onMessage?(try! request.asJSONEncodedString()) waitForExpectations(timeout: 0.001, handler: nil) } @@ -79,9 +82,11 @@ final class RelayClientTests: XCTestCase { func testSubscriptionRequestDeliveredOnce() { 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 + + sut.messagePublisher.sink { (_, _) in expectation.fulfill() - } + }.store(in: &publishers) + dispatcher.onMessage?(try! request.asJSONEncodedString()) dispatcher.onMessage?(try! request.asJSONEncodedString()) waitForExpectations(timeout: 0.1, handler: nil) diff --git a/Tests/WalletConnectSignTests/Mocks/MockedRelayClient.swift b/Tests/WalletConnectSignTests/Mocks/MockedRelayClient.swift index 98f57f0ef..eac4dacb5 100644 --- a/Tests/WalletConnectSignTests/Mocks/MockedRelayClient.swift +++ b/Tests/WalletConnectSignTests/Mocks/MockedRelayClient.swift @@ -4,25 +4,31 @@ import Foundation @testable import WalletConnectSign class MockedRelayClient: NetworkRelaying { - func subscribe(topic: String) async throws {} + + var messagePublisherSubject = PassthroughSubject<(topic: String, message: String), Never>() + var messagePublisher: AnyPublisher<(topic: String, message: String), Never> { + messagePublisherSubject.eraseToAnyPublisher() + } var socketConnectionStatusPublisherSubject = PassthroughSubject() var socketConnectionStatusPublisher: AnyPublisher { socketConnectionStatusPublisherSubject.eraseToAnyPublisher() } + var error: Error? + var prompt = false + func publish(topic: String, payload: String, tag: Int, prompt: Bool) async throws { self.prompt = prompt } - 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)) { self.prompt = prompt onNetworkAcknowledge(error) } + func subscribe(topic: String) async throws {} + func subscribe(topic: String, completion: @escaping (Error?) -> Void) { } diff --git a/Tests/WalletConnectSignTests/WCRelayTests.swift b/Tests/WalletConnectSignTests/WCRelayTests.swift index 500967141..a42fbd328 100644 --- a/Tests/WalletConnectSignTests/WCRelayTests.swift +++ b/Tests/WalletConnectSignTests/WCRelayTests.swift @@ -33,7 +33,7 @@ class NetworkingInteractorTests: XCTestCase { requestExpectation.fulfill() }.store(in: &publishers) serializer.deserialized = request - relayClient.onMessage?(topic, testPayload) + relayClient.messagePublisherSubject.send((topic, testPayload)) waitForExpectations(timeout: 1.001, handler: nil) } From cc435d4484054192414253331b3f2dd512da7d91 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Mon, 15 Aug 2022 22:47:38 +0300 Subject: [PATCH 35/89] Lint --- .../Sign/Helpers/ClientDelegate.swift | 2 +- Package.swift | 2 +- Sources/Auth/AuthClient.swift | 1 - Sources/Auth/Services/Signer/MessageSigner.swift | 2 +- .../Services/Wallet/WalletRequestSubscriber.swift | 14 +++++++------- 5 files changed, 10 insertions(+), 11 deletions(-) diff --git a/Example/IntegrationTests/Sign/Helpers/ClientDelegate.swift b/Example/IntegrationTests/Sign/Helpers/ClientDelegate.swift index ab7125760..71ac99ab3 100644 --- a/Example/IntegrationTests/Sign/Helpers/ClientDelegate.swift +++ b/Example/IntegrationTests/Sign/Helpers/ClientDelegate.swift @@ -28,7 +28,7 @@ class ClientDelegate { self.onSessionSettled?(session) }.store(in: &publishers) - client.socketConnectionStatusPublisher.sink { status in + client.socketConnectionStatusPublisher.sink { _ in self.onConnected?() }.store(in: &publishers) diff --git a/Package.swift b/Package.swift index 46b112754..55ed4b155 100644 --- a/Package.swift +++ b/Package.swift @@ -18,7 +18,7 @@ let package = Package( targets: ["Chat"]), .library( name: "WalletConnectRouter", - targets: ["WalletConnectRouter"]), + targets: ["WalletConnectRouter"]) ], dependencies: [ .package(url: "https://github.com/flypaper0/Web3.swift", .branch("master")) diff --git a/Sources/Auth/AuthClient.swift b/Sources/Auth/AuthClient.swift index 6ea7bc053..359021680 100644 --- a/Sources/Auth/AuthClient.swift +++ b/Sources/Auth/AuthClient.swift @@ -110,4 +110,3 @@ class AuthClient { } } } - diff --git a/Sources/Auth/Services/Signer/MessageSigner.swift b/Sources/Auth/Services/Signer/MessageSigner.swift index a00c9cd0c..b9b8dda11 100644 --- a/Sources/Auth/Services/Signer/MessageSigner.swift +++ b/Sources/Auth/Services/Signer/MessageSigner.swift @@ -8,7 +8,7 @@ protocol MessageSigning { func sign(message: String, privateKey: Data) throws -> String } -struct MessageSigner: MessageSignatureVerifying & MessageSigning { +struct MessageSigner: MessageSignatureVerifying, MessageSigning { enum Errors: Error { case signatureValidationFailed diff --git a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift index ddd28089c..c2fc00bcb 100644 --- a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift +++ b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift @@ -9,8 +9,8 @@ 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, _ message: String) -> Void)? + init(networkingInteractor: NetworkInteracting, logger: ConsoleLogging, messageFormatter: SIWEMessageFormatting, @@ -21,27 +21,27 @@ class WalletRequestSubscriber { self.messageFormatter = messageFormatter subscribeForRequest() } - + private func subscribeForRequest() { networkingInteractor.requestPublisher.sink { [unowned self] subscriptionPayload in 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) } }.store(in: &publishers) } - + } From 1ada455f101238f480bb0b346031a606592a6730 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Mon, 15 Aug 2022 23:05:10 +0300 Subject: [PATCH 36/89] Integration tests fix --- .../Relay/RelayClientEndToEndTests.swift | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Example/IntegrationTests/Relay/RelayClientEndToEndTests.swift b/Example/IntegrationTests/Relay/RelayClientEndToEndTests.swift index 44f329ac4..a634f648b 100644 --- a/Example/IntegrationTests/Relay/RelayClientEndToEndTests.swift +++ b/Example/IntegrationTests/Relay/RelayClientEndToEndTests.swift @@ -67,14 +67,16 @@ final class RelayClientEndToEndTests: XCTestCase { expectationA.assertForOverFulfill = false expectationB.assertForOverFulfill = false - relayA.onMessage = { topic, payload in + relayA.messagePublisher.sink { topic, payload in (subscriptionATopic, subscriptionAPayload) = (topic, payload) expectationA.fulfill() - } - relayB.onMessage = { topic, payload in + }.store(in: &publishers) + + relayB.messagePublisher.sink { topic, payload in (subscriptionBTopic, subscriptionBPayload) = (topic, payload) expectationB.fulfill() - } + }.store(in: &publishers) + relayA.socketConnectionStatusPublisher.sink { _ in relayA.publish(topic: randomTopic, payload: payloadA, tag: 0, onNetworkAcknowledge: { error in XCTAssertNil(error) From 4d5ee101518fbf6a17fb4b965431b51472afb13b Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Mon, 15 Aug 2022 23:12:12 +0300 Subject: [PATCH 37/89] Sign instance calls replaced with SignClient --- .../SessionDetails/SessionDetailViewController.swift | 2 +- .../ExampleApp/SessionDetails/SessionDetailViewModel.swift | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Example/ExampleApp/SessionDetails/SessionDetailViewController.swift b/Example/ExampleApp/SessionDetails/SessionDetailViewController.swift index 7c07cca78..4d7d6b39e 100644 --- a/Example/ExampleApp/SessionDetails/SessionDetailViewController.swift +++ b/Example/ExampleApp/SessionDetails/SessionDetailViewController.swift @@ -7,7 +7,7 @@ final class SessionDetailViewController: UIHostingController private let viewModel: SessionDetailViewModel - init(session: Session, client: Sign) { + init(session: Session, client: SignClient) { self.viewModel = SessionDetailViewModel(session: session, client: client) super.init(rootView: SessionDetailView(viewModel: viewModel)) diff --git a/Example/ExampleApp/SessionDetails/SessionDetailViewModel.swift b/Example/ExampleApp/SessionDetails/SessionDetailViewModel.swift index 00e2c18d2..f29dea255 100644 --- a/Example/ExampleApp/SessionDetails/SessionDetailViewModel.swift +++ b/Example/ExampleApp/SessionDetails/SessionDetailViewModel.swift @@ -5,7 +5,7 @@ import WalletConnectSign @MainActor final class SessionDetailViewModel: ObservableObject { private let session: Session - private let client: Sign + private let client: SignClient enum Fields { case accounts @@ -18,7 +18,7 @@ final class SessionDetailViewModel: ObservableObject { @Published var pingSuccess: Bool = false @Published var pingFailed: Bool = false - init(session: Session, client: Sign) { + init(session: Session, client: SignClient) { self.session = session self.client = client self.namespaces = session.namespaces From 356c62fdce4639e4815ee14417d045318b43a2a4 Mon Sep 17 00:00:00 2001 From: Maisa Date: Mon, 15 Aug 2022 22:28:34 -0300 Subject: [PATCH 38/89] 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 fb0509d89e3da2e40e02d2932b6e7cd54bdb8fe6 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 16 Aug 2022 11:05:36 +0200 Subject: [PATCH 39/89] savepoint --- Example/ExampleApp.xcodeproj/project.pbxproj | 12 ++++ Example/IntegrationTests/Auth/AuthTests.swift | 60 +++++++++++++++++++ Sources/Auth/AuthClient.swift | 2 +- Sources/Auth/AuthClientFactory.swift | 49 +++++++++++++++ Sources/Auth/StorageDomainIdentifiers.swift | 6 ++ Sources/Auth/Types/Aliases/Account.swift | 2 +- 6 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 Example/IntegrationTests/Auth/AuthTests.swift create mode 100644 Sources/Auth/AuthClientFactory.swift create mode 100644 Sources/Auth/StorageDomainIdentifiers.swift diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index 91b9ee090..6ed75d168 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -43,6 +43,7 @@ 84CE644E279ED2FF00142511 /* SelectChainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CE644D279ED2FF00142511 /* SelectChainView.swift */; }; 84CE6452279ED42B00142511 /* ConnectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CE6451279ED42B00142511 /* ConnectView.swift */; }; 84CE645527A29D4D00142511 /* ResponseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CE645427A29D4C00142511 /* ResponseViewController.swift */; }; + 84D2A66628A4F51E0088AE09 /* AuthTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84D2A66528A4F51E0088AE09 /* AuthTests.swift */; }; 84F568C2279582D200D0A289 /* Signer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F568C1279582D200D0A289 /* Signer.swift */; }; 84F568C42795832A00D0A289 /* EthereumTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F568C32795832A00D0A289 /* EthereumTransaction.swift */; }; A50F3946288005B200064555 /* Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = A50F3945288005B200064555 /* Types.swift */; }; @@ -203,6 +204,7 @@ 84CE6451279ED42B00142511 /* ConnectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectView.swift; sourceTree = ""; }; 84CE6453279FFE1100142511 /* Wallet.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Wallet.entitlements; sourceTree = ""; }; 84CE645427A29D4C00142511 /* ResponseViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResponseViewController.swift; sourceTree = ""; }; + 84D2A66528A4F51E0088AE09 /* AuthTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthTests.swift; sourceTree = ""; }; 84F568C1279582D200D0A289 /* Signer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Signer.swift; sourceTree = ""; }; 84F568C32795832A00D0A289 /* EthereumTransaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EthereumTransaction.swift; sourceTree = ""; }; A50F3945288005B200064555 /* Types.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Types.swift; sourceTree = ""; }; @@ -520,6 +522,14 @@ path = Connect; sourceTree = ""; }; + 84D2A66728A4F5260088AE09 /* Auth */ = { + isa = PBXGroup; + children = ( + 84D2A66528A4F51E0088AE09 /* AuthTests.swift */, + ); + path = Auth; + sourceTree = ""; + }; A50F3944288005A700064555 /* Types */ = { isa = PBXGroup; children = ( @@ -857,6 +867,7 @@ A5E03E0A28646A8A00888481 /* Stubs */, A5E03E0928646A8100888481 /* Sign */, A5E03E0828646A7B00888481 /* Chat */, + 84D2A66728A4F5260088AE09 /* Auth */, ); path = IntegrationTests; sourceTree = ""; @@ -1262,6 +1273,7 @@ 767DC83528997F8E00080FA9 /* EthSendTransaction.swift in Sources */, A5E03DFB286465C700888481 /* ClientDelegate.swift in Sources */, A5E03E03286466F400888481 /* ChatTests.swift in Sources */, + 84D2A66628A4F51E0088AE09 /* AuthTests.swift in Sources */, 7694A5262874296A0001257E /* RegistryTests.swift in Sources */, A5E03E0D28646AD200888481 /* RelayClientEndToEndTests.swift in Sources */, A5E03DFA286465C700888481 /* SignClientTests.swift in Sources */, diff --git a/Example/IntegrationTests/Auth/AuthTests.swift b/Example/IntegrationTests/Auth/AuthTests.swift new file mode 100644 index 000000000..d8186726a --- /dev/null +++ b/Example/IntegrationTests/Auth/AuthTests.swift @@ -0,0 +1,60 @@ +import Foundation +import XCTest +@testable import Auth +import WalletConnectUtils +@testable import WalletConnectKMS +import WalletConnectRelay +import Combine + +final class AuthTests: XCTestCase { + var registry: KeyValueRegistry! + private var publishers = [AnyCancellable]() + + override func setUp() { + registry = KeyValueRegistry() + invitee = makeClient(prefix: "🦖 Registered") + inviter = makeClient(prefix: "🍄 Inviter") + + waitClientsConnected() + } + + private func waitClientsConnected() { + let expectation = expectation(description: "Wait Clients Connected") + expectation.expectedFulfillmentCount = 2 + + invitee.socketConnectionStatusPublisher.sink { status in + if status == .connected { + expectation.fulfill() + } + }.store(in: &publishers) + + inviter.socketConnectionStatusPublisher.sink { status in + if status == .connected { + expectation.fulfill() + } + }.store(in: &publishers) + + wait(for: [expectation], timeout: 5) + } + + func makeClient(prefix: String) -> ChatClient { + let logger = ConsoleLogger(suffix: prefix, loggingLevel: .debug) + let relayHost = "relay.walletconnect.com" + let projectId = "8ba9ee138960775e5231b70cc5ef1c3a" + let keychain = KeychainStorageMock() + let relayClient = RelayClient(relayHost: relayHost, projectId: projectId, keychainStorage: keychain, socketFactory: SocketFactory(), logger: logger) + return ChatClientFactory.create(registry: registry, relayClient: relayClient, kms: KeyManagementService(keychain: keychain), logger: logger, keyValueStorage: RuntimeKeyValueStorage()) + } + + func testInvite() async { + let inviteExpectation = expectation(description: "invitation expectation") + let inviteeAccount = Account(chainIdentifier: "eip155:1", address: "0x3627523167367216556273151")! + let inviterAccount = Account(chainIdentifier: "eip155:1", address: "0x36275231673672234423f")! + let pubKey = try! await invitee.register(account: inviteeAccount) + try! await inviter.invite(publicKey: pubKey, peerAccount: inviteeAccount, openingMessage: "", account: inviterAccount) + invitee.invitePublisher.sink { _ in + inviteExpectation.fulfill() + }.store(in: &publishers) + wait(for: [inviteExpectation], timeout: 4) + } +} diff --git a/Sources/Auth/AuthClient.swift b/Sources/Auth/AuthClient.swift index 6ea7bc053..1b7569b2e 100644 --- a/Sources/Auth/AuthClient.swift +++ b/Sources/Auth/AuthClient.swift @@ -3,7 +3,7 @@ import Combine import WalletConnectUtils import WalletConnectPairing -class AuthClient { +public class AuthClient { enum Errors: Error { case malformedPairingURI case unknownWalletAddress diff --git a/Sources/Auth/AuthClientFactory.swift b/Sources/Auth/AuthClientFactory.swift new file mode 100644 index 000000000..379fbc471 --- /dev/null +++ b/Sources/Auth/AuthClientFactory.swift @@ -0,0 +1,49 @@ +import Foundation +import WalletConnectRelay +import WalletConnectUtils +import WalletConnectKMS +import WalletConnectPairing + +public struct AuthClientFactory { + + public static func create(metadata: AppMetadata, account: Account, relayClient: RelayClient) -> AuthClient { + + let keyValueStorage = UserDefaults.standard + let historyStorage = CodableStore(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.jsonRpcHistory.rawValue) + + let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk") + let pairingStore = PairingStorage(storage: SequenceStore(store: .init(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.pairings.rawValue))) + + + let logger = ConsoleLogger(loggingLevel: .off) + let kms = KeyManagementService(keychain: keychainStorage) + let serializer = Serializer(kms: kms) + let history = RPCHistory(keyValueStore: historyStorage) + let networkingInteractor = NetworkingInteractor(relayClient: relayClient, serializer: serializer, rpcHistory: history) + + + let appPairService = AppPairService(networkingInteractor: networkingInteractor, kms: kms, pairingStorage: <#T##WCPairingStorage#>) + let appRequestService = AppRequestService(networkingInteractor: networkingInteractor, kms: kms, appMetadata: metadata) + let appRespondSubscriber = AppRespondSubscriber(networkingInteractor: networkingInteractor, logger: <#T##ConsoleLogging#>, rpcHistory: <#T##RPCHistory#>)networkingInteractor + let walletPairService = WalletPairService(networkingInteractor: networkingInteractor, kms: kms, pairingStorage: <#T##WCPairingStorage#>) + let walletRequestSubscriber = WalletRequestSubscriber(networkingInteractor: networkingInteractor, logger: <#T##ConsoleLogging#>, messageFormatter: <#T##SIWEMessageFormatting#>, address: account.address) + let walletRespondService = WalletRespondService(networkingInteractor: networkingInteractor, logger: <#T##ConsoleLogging#>, kms: kms, rpcHistory: <#T##RPCHistory#>) + let account = account + let pendingRequestsProvider = + let cleanupService = + let logger = + let pairingStorage = + + return AuthClient(appPairService: <#T##AppPairService#>, + appRequestService: <#T##AppRequestService#>, + appRespondSubscriber: <#T##AppRespondSubscriber#>, + walletPairService: <#T##WalletPairService#>, + walletRequestSubscriber: <#T##WalletRequestSubscriber#>, + walletRespondService: <#T##WalletRespondService#>, + account: <#T##Account?#>, + pendingRequestsProvider: <#T##PendingRequestsProvider#>, + cleanupService: <#T##CleanupService#>, + logger: <#T##ConsoleLogging#>, + pairingStorage: <#T##WCPairingStorage#>) + } +} diff --git a/Sources/Auth/StorageDomainIdentifiers.swift b/Sources/Auth/StorageDomainIdentifiers.swift new file mode 100644 index 000000000..ed7156c67 --- /dev/null +++ b/Sources/Auth/StorageDomainIdentifiers.swift @@ -0,0 +1,6 @@ +import Foundation + +enum StorageDomainIdentifiers: String { + case jsonRpcHistory = "com.walletconnect.sdk.wc_jsonRpcHistoryRecord" + case pairings = "com.walletconnect.sdk.pairingSequences" +} diff --git a/Sources/Auth/Types/Aliases/Account.swift b/Sources/Auth/Types/Aliases/Account.swift index 84759e639..3ab41d381 100644 --- a/Sources/Auth/Types/Aliases/Account.swift +++ b/Sources/Auth/Types/Aliases/Account.swift @@ -1,4 +1,4 @@ import WalletConnectUtils import Foundation -typealias Account = WalletConnectUtils.Account +public typealias Account = WalletConnectUtils.Account From a697ee815e509c4e242eae97e856c2cc659e461a Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 16 Aug 2022 11:42:40 +0200 Subject: [PATCH 40/89] Add Auth Client Factory --- Sources/Auth/AuthClientFactory.swift | 48 ++++++++----------- .../Auth/Services/Common/CleanupService.swift | 2 +- 2 files changed, 21 insertions(+), 29 deletions(-) diff --git a/Sources/Auth/AuthClientFactory.swift b/Sources/Auth/AuthClientFactory.swift index 379fbc471..fd72c3c02 100644 --- a/Sources/Auth/AuthClientFactory.swift +++ b/Sources/Auth/AuthClientFactory.swift @@ -7,43 +7,35 @@ import WalletConnectPairing public struct AuthClientFactory { public static func create(metadata: AppMetadata, account: Account, relayClient: RelayClient) -> AuthClient { - let keyValueStorage = UserDefaults.standard let historyStorage = CodableStore(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.jsonRpcHistory.rawValue) - let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk") let pairingStore = PairingStorage(storage: SequenceStore(store: .init(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.pairings.rawValue))) - - let logger = ConsoleLogger(loggingLevel: .off) let kms = KeyManagementService(keychain: keychainStorage) let serializer = Serializer(kms: kms) let history = RPCHistory(keyValueStore: historyStorage) let networkingInteractor = NetworkingInteractor(relayClient: relayClient, serializer: serializer, rpcHistory: history) - - - let appPairService = AppPairService(networkingInteractor: networkingInteractor, kms: kms, pairingStorage: <#T##WCPairingStorage#>) + let SIWEMessageFormatter = SIWEMessageFormatter() + let appPairService = AppPairService(networkingInteractor: networkingInteractor, kms: kms, pairingStorage: pairingStore) let appRequestService = AppRequestService(networkingInteractor: networkingInteractor, kms: kms, appMetadata: metadata) - let appRespondSubscriber = AppRespondSubscriber(networkingInteractor: networkingInteractor, logger: <#T##ConsoleLogging#>, rpcHistory: <#T##RPCHistory#>)networkingInteractor - let walletPairService = WalletPairService(networkingInteractor: networkingInteractor, kms: kms, pairingStorage: <#T##WCPairingStorage#>) - let walletRequestSubscriber = WalletRequestSubscriber(networkingInteractor: networkingInteractor, logger: <#T##ConsoleLogging#>, messageFormatter: <#T##SIWEMessageFormatting#>, address: account.address) - let walletRespondService = WalletRespondService(networkingInteractor: networkingInteractor, logger: <#T##ConsoleLogging#>, kms: kms, rpcHistory: <#T##RPCHistory#>) - let account = account - let pendingRequestsProvider = - let cleanupService = - let logger = - let pairingStorage = - - return AuthClient(appPairService: <#T##AppPairService#>, - appRequestService: <#T##AppRequestService#>, - appRespondSubscriber: <#T##AppRespondSubscriber#>, - walletPairService: <#T##WalletPairService#>, - walletRequestSubscriber: <#T##WalletRequestSubscriber#>, - walletRespondService: <#T##WalletRespondService#>, - account: <#T##Account?#>, - pendingRequestsProvider: <#T##PendingRequestsProvider#>, - cleanupService: <#T##CleanupService#>, - logger: <#T##ConsoleLogging#>, - pairingStorage: <#T##WCPairingStorage#>) + let appRespondSubscriber = AppRespondSubscriber(networkingInteractor: networkingInteractor, logger: logger, rpcHistory: history) + let walletPairService = WalletPairService(networkingInteractor: networkingInteractor, kms: kms, pairingStorage: pairingStore) + let walletRequestSubscriber = WalletRequestSubscriber(networkingInteractor: networkingInteractor, logger: logger, messageFormatter: SIWEMessageFormatter, 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) + + return AuthClient(appPairService: appPairService, + appRequestService: appRequestService, + appRespondSubscriber: appRespondSubscriber, + walletPairService: walletPairService, + walletRequestSubscriber: walletRequestSubscriber, + walletRespondService: walletRespondService, + account: account, + pendingRequestsProvider: pendingRequestsProvider, + cleanupService: cleanupService, + logger: logger, + pairingStorage: pairingStore) } } diff --git a/Sources/Auth/Services/Common/CleanupService.swift b/Sources/Auth/Services/Common/CleanupService.swift index dac2b63c5..6a5a01334 100644 --- a/Sources/Auth/Services/Common/CleanupService.swift +++ b/Sources/Auth/Services/Common/CleanupService.swift @@ -8,7 +8,7 @@ final class CleanupService { private let pairingStore: WCPairingStorage private let kms: KeyManagementServiceProtocol - init(pairingStore: WCPairingStorage, kms: KeyManagementServiceProtocol, sessionToPairingTopic: CodableStore) { + init(pairingStore: WCPairingStorage, kms: KeyManagementServiceProtocol) { self.pairingStore = pairingStore self.kms = kms } From d1a226000a7d07dd43a42b9e93a312721e4b9d19 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 16 Aug 2022 11:45:09 +0200 Subject: [PATCH 41/89] Add public access for request and respond params --- Sources/Auth/Types/RequestParams.swift | 2 +- Sources/Auth/Types/RespondParams.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/Auth/Types/RequestParams.swift b/Sources/Auth/Types/RequestParams.swift index 24e7afab9..a3f456add 100644 --- a/Sources/Auth/Types/RequestParams.swift +++ b/Sources/Auth/Types/RequestParams.swift @@ -1,6 +1,6 @@ import Foundation -struct RequestParams { +public struct RequestParams { let domain: String let chainId: String let nonce: String diff --git a/Sources/Auth/Types/RespondParams.swift b/Sources/Auth/Types/RespondParams.swift index fcd9b3293..b3757a64a 100644 --- a/Sources/Auth/Types/RespondParams.swift +++ b/Sources/Auth/Types/RespondParams.swift @@ -1,6 +1,6 @@ import Foundation -struct RespondParams { +public struct RespondParams { let id: Int64 let topic: String let signature: CacaoSignature From e87a3dae5f246a28965f07967f821ae122b0d876 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 16 Aug 2022 12:22:02 +0200 Subject: [PATCH 42/89] update auth test --- Example/ExampleApp.xcodeproj/project.pbxproj | 1 + Example/IntegrationTests/Auth/AuthTests.swift | 25 +++++++++---------- Package.swift | 3 +++ Sources/Auth/AuthClient.swift | 2 +- .../WalletConnectSign/Sign/SignClient.swift | 2 +- 5 files changed, 18 insertions(+), 15 deletions(-) diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index 6ed75d168..3817b2d02 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -1435,6 +1435,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = NO; SDKROOT = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; diff --git a/Example/IntegrationTests/Auth/AuthTests.swift b/Example/IntegrationTests/Auth/AuthTests.swift index d8186726a..b171a3ff0 100644 --- a/Example/IntegrationTests/Auth/AuthTests.swift +++ b/Example/IntegrationTests/Auth/AuthTests.swift @@ -1,52 +1,51 @@ import Foundation import XCTest -@testable import Auth import WalletConnectUtils @testable import WalletConnectKMS import WalletConnectRelay import Combine +@testable import Auth final class AuthTests: XCTestCase { - var registry: KeyValueRegistry! + var app: AuthClient! + var wallet: AuthClient! private var publishers = [AnyCancellable]() override func setUp() { registry = KeyValueRegistry() - invitee = makeClient(prefix: "🦖 Registered") - inviter = makeClient(prefix: "🍄 Inviter") - - waitClientsConnected() - } + app = makeClient(prefix: "👻 App") + wallet = makeClient(prefix: "🤑 Wallet") - private func waitClientsConnected() { let expectation = expectation(description: "Wait Clients Connected") expectation.expectedFulfillmentCount = 2 - invitee.socketConnectionStatusPublisher.sink { status in + app.socketConnectionStatusPublisher.sink { status in if status == .connected { expectation.fulfill() } }.store(in: &publishers) - inviter.socketConnectionStatusPublisher.sink { status in + wallet.socketConnectionStatusPublisher.sink { status in if status == .connected { expectation.fulfill() } }.store(in: &publishers) wait(for: [expectation], timeout: 5) + } - func makeClient(prefix: String) -> ChatClient { + + func makeClient(prefix: String) -> AuthClient { let logger = ConsoleLogger(suffix: prefix, loggingLevel: .debug) let relayHost = "relay.walletconnect.com" let projectId = "8ba9ee138960775e5231b70cc5ef1c3a" let keychain = KeychainStorageMock() let relayClient = RelayClient(relayHost: relayHost, projectId: projectId, keychainStorage: keychain, socketFactory: SocketFactory(), logger: logger) - return ChatClientFactory.create(registry: registry, relayClient: relayClient, kms: KeyManagementService(keychain: keychain), logger: logger, keyValueStorage: RuntimeKeyValueStorage()) + return AuthClientFactory() } - func testInvite() async { + func testRequest() async { let inviteExpectation = expectation(description: "invitation expectation") let inviteeAccount = Account(chainIdentifier: "eip155:1", address: "0x3627523167367216556273151")! let inviterAccount = Account(chainIdentifier: "eip155:1", address: "0x36275231673672234423f")! diff --git a/Package.swift b/Package.swift index 46b112754..4bf5abd95 100644 --- a/Package.swift +++ b/Package.swift @@ -16,6 +16,9 @@ let package = Package( .library( name: "WalletConnectChat", targets: ["Chat"]), + .library( + name: "WalletConnectAurth", + targets: ["Auth"]), .library( name: "WalletConnectRouter", targets: ["WalletConnectRouter"]), diff --git a/Sources/Auth/AuthClient.swift b/Sources/Auth/AuthClient.swift index 1b7569b2e..24f54f50b 100644 --- a/Sources/Auth/AuthClient.swift +++ b/Sources/Auth/AuthClient.swift @@ -92,7 +92,7 @@ public class AuthClient { } #if DEBUG - /// Delete all stored data sach as: pairings, sessions, keys + /// Delete all stored data such as: pairings, keys /// /// - Note: Doesn't unsubscribe from topics public func cleanup() throws { diff --git a/Sources/WalletConnectSign/Sign/SignClient.swift b/Sources/WalletConnectSign/Sign/SignClient.swift index 43df10383..d21285649 100644 --- a/Sources/WalletConnectSign/Sign/SignClient.swift +++ b/Sources/WalletConnectSign/Sign/SignClient.swift @@ -279,7 +279,7 @@ public final class SignClient { } #if DEBUG - /// Delete all stored data sach as: pairings, sessions, keys + /// Delete all stored data such as: pairings, sessions, keys /// /// - Note: Doesn't unsubscribe from topics public func cleanup() throws { From 6dda075dfc720aea4cfafc4e87fc36cb4965ed06 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 16 Aug 2022 14:42:21 +0200 Subject: [PATCH 43/89] add socket connection publisher fix build --- Example/ExampleApp.xcodeproj/project.pbxproj | 7 +++++++ Example/IntegrationTests/Auth/AuthTests.swift | 1 - Package.swift | 2 +- Sources/Auth/AuthClient.swift | 8 +++++++- Sources/Auth/AuthClientFactory.swift | 5 +++-- 5 files changed, 18 insertions(+), 5 deletions(-) diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index 3817b2d02..1b2c1316e 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -44,6 +44,7 @@ 84CE6452279ED42B00142511 /* ConnectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CE6451279ED42B00142511 /* ConnectView.swift */; }; 84CE645527A29D4D00142511 /* ResponseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CE645427A29D4C00142511 /* ResponseViewController.swift */; }; 84D2A66628A4F51E0088AE09 /* AuthTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84D2A66528A4F51E0088AE09 /* AuthTests.swift */; }; + 84DDB4ED28ABB663003D66ED /* WalletConnectAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 84DDB4EC28ABB663003D66ED /* WalletConnectAuth */; }; 84F568C2279582D200D0A289 /* Signer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F568C1279582D200D0A289 /* Signer.swift */; }; 84F568C42795832A00D0A289 /* EthereumTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F568C32795832A00D0A289 /* EthereumTransaction.swift */; }; A50F3946288005B200064555 /* Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = A50F3945288005B200064555 /* Types.swift */; }; @@ -343,6 +344,7 @@ files = ( A5E03DFF2864662500888481 /* WalletConnect in Frameworks */, A5E03DF52864651200888481 /* Starscream in Frameworks */, + 84DDB4ED28ABB663003D66ED /* WalletConnectAuth in Frameworks */, A5E03E01286466EA00888481 /* WalletConnectChat in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1028,6 +1030,7 @@ A5E03DF42864651200888481 /* Starscream */, A5E03DFE2864662500888481 /* WalletConnect */, A5E03E00286466EA00888481 /* WalletConnectChat */, + 84DDB4EC28ABB663003D66ED /* WalletConnectAuth */, ); productName = IntegrationTests; productReference = A5E03DED286464DB00888481 /* IntegrationTests.xctest */; @@ -1780,6 +1783,10 @@ isa = XCSwiftPackageProductDependency; productName = WalletConnect; }; + 84DDB4EC28ABB663003D66ED /* WalletConnectAuth */ = { + isa = XCSwiftPackageProductDependency; + productName = WalletConnectAuth; + }; A5629AE92877F2D600094373 /* WalletConnectChat */ = { isa = XCSwiftPackageProductDependency; productName = WalletConnectChat; diff --git a/Example/IntegrationTests/Auth/AuthTests.swift b/Example/IntegrationTests/Auth/AuthTests.swift index b171a3ff0..da6a42c8e 100644 --- a/Example/IntegrationTests/Auth/AuthTests.swift +++ b/Example/IntegrationTests/Auth/AuthTests.swift @@ -12,7 +12,6 @@ final class AuthTests: XCTestCase { private var publishers = [AnyCancellable]() override func setUp() { - registry = KeyValueRegistry() app = makeClient(prefix: "👻 App") wallet = makeClient(prefix: "🤑 Wallet") diff --git a/Package.swift b/Package.swift index 4bf5abd95..df0509232 100644 --- a/Package.swift +++ b/Package.swift @@ -17,7 +17,7 @@ let package = Package( name: "WalletConnectChat", targets: ["Chat"]), .library( - name: "WalletConnectAurth", + name: "WalletConnectAuth", targets: ["Auth"]), .library( name: "WalletConnectRouter", diff --git a/Sources/Auth/AuthClient.swift b/Sources/Auth/AuthClient.swift index 24f54f50b..d4ce191e9 100644 --- a/Sources/Auth/AuthClient.swift +++ b/Sources/Auth/AuthClient.swift @@ -2,6 +2,7 @@ import Foundation import Combine import WalletConnectUtils import WalletConnectPairing +import WalletConnectRelay public class AuthClient { enum Errors: Error { @@ -19,6 +20,8 @@ public class AuthClient { authResponsePublisherSubject.eraseToAnyPublisher() } + public let socketConnectionStatusPublisher: AnyPublisher + private let appPairService: AppPairService private let appRequestService: AppRequestService private let appRespondSubscriber: AppRespondSubscriber @@ -43,7 +46,9 @@ public class AuthClient { pendingRequestsProvider: PendingRequestsProvider, cleanupService: CleanupService, logger: ConsoleLogging, - pairingStorage: WCPairingStorage) { + pairingStorage: WCPairingStorage, + socketConnectionStatusPublisher: AnyPublisher +) { self.appPairService = appPairService self.appRequestService = appRequestService self.walletPairService = walletPairService @@ -55,6 +60,7 @@ public class AuthClient { self.cleanupService = cleanupService self.logger = logger self.pairingStorage = pairingStorage + self.socketConnectionStatusPublisher = socketConnectionStatusPublisher setUpPublishers() } diff --git a/Sources/Auth/AuthClientFactory.swift b/Sources/Auth/AuthClientFactory.swift index fd72c3c02..4486e126f 100644 --- a/Sources/Auth/AuthClientFactory.swift +++ b/Sources/Auth/AuthClientFactory.swift @@ -19,7 +19,8 @@ public struct AuthClientFactory { let SIWEMessageFormatter = SIWEMessageFormatter() let appPairService = AppPairService(networkingInteractor: networkingInteractor, kms: kms, pairingStorage: pairingStore) let appRequestService = AppRequestService(networkingInteractor: networkingInteractor, kms: kms, appMetadata: metadata) - let appRespondSubscriber = AppRespondSubscriber(networkingInteractor: networkingInteractor, logger: logger, rpcHistory: history) + let messageSigner = MessageSigner(signer: Signer()) + let appRespondSubscriber = AppRespondSubscriber(networkingInteractor: networkingInteractor, logger: logger, rpcHistory: history, signatureVerifier: messageSigner, messageFormatter: SIWEMessageFormatter) let walletPairService = WalletPairService(networkingInteractor: networkingInteractor, kms: kms, pairingStorage: pairingStore) let walletRequestSubscriber = WalletRequestSubscriber(networkingInteractor: networkingInteractor, logger: logger, messageFormatter: SIWEMessageFormatter, address: account.address) let walletRespondService = WalletRespondService(networkingInteractor: networkingInteractor, logger: logger, kms: kms, rpcHistory: history) @@ -36,6 +37,6 @@ public struct AuthClientFactory { pendingRequestsProvider: pendingRequestsProvider, cleanupService: cleanupService, logger: logger, - pairingStorage: pairingStore) + pairingStorage: pairingStore, socketConnectionStatusPublisher: relayClient.socketConnectionStatusPublisher) } } From 8cd5c9701cad768bf8f7a04d5f3afd4ac66b6574 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 16 Aug 2022 14:59:22 +0200 Subject: [PATCH 44/89] fix integration tests build --- Example/IntegrationTests/Auth/AuthTests.swift | 23 ++++++++----------- Sources/Auth/AuthClientFactory.swift | 5 ++-- .../Wallet/WalletRequestSubscriber.swift | 8 +++++-- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Example/IntegrationTests/Auth/AuthTests.swift b/Example/IntegrationTests/Auth/AuthTests.swift index da6a42c8e..99dce1da0 100644 --- a/Example/IntegrationTests/Auth/AuthTests.swift +++ b/Example/IntegrationTests/Auth/AuthTests.swift @@ -13,7 +13,8 @@ final class AuthTests: XCTestCase { override func setUp() { app = makeClient(prefix: "👻 App") - wallet = makeClient(prefix: "🤑 Wallet") + let walletAccount = Account(chainIdentifier: "eip155:1", address: "0x3627523167367216556273151")! + wallet = makeClient(prefix: "🤑 Wallet", account: walletAccount) let expectation = expectation(description: "Wait Clients Connected") expectation.expectedFulfillmentCount = 2 @@ -31,28 +32,24 @@ final class AuthTests: XCTestCase { }.store(in: &publishers) wait(for: [expectation], timeout: 5) - } - func makeClient(prefix: String) -> AuthClient { + func makeClient(prefix: String, account: Account? = nil) -> AuthClient { let logger = ConsoleLogger(suffix: prefix, loggingLevel: .debug) let relayHost = "relay.walletconnect.com" let projectId = "8ba9ee138960775e5231b70cc5ef1c3a" let keychain = KeychainStorageMock() let relayClient = RelayClient(relayHost: relayHost, projectId: projectId, keychainStorage: keychain, socketFactory: SocketFactory(), logger: logger) - return AuthClientFactory() + + return AuthClientFactory.create( + metadata: AppMetadata(name: name, description: "", url: "", icons: [""]), + account: account, + relayClient: relayClient, + logger: logger) } func testRequest() async { - let inviteExpectation = expectation(description: "invitation expectation") - let inviteeAccount = Account(chainIdentifier: "eip155:1", address: "0x3627523167367216556273151")! - let inviterAccount = Account(chainIdentifier: "eip155:1", address: "0x36275231673672234423f")! - let pubKey = try! await invitee.register(account: inviteeAccount) - try! await inviter.invite(publicKey: pubKey, peerAccount: inviteeAccount, openingMessage: "", account: inviterAccount) - invitee.invitePublisher.sink { _ in - inviteExpectation.fulfill() - }.store(in: &publishers) - wait(for: [inviteExpectation], timeout: 4) + } } diff --git a/Sources/Auth/AuthClientFactory.swift b/Sources/Auth/AuthClientFactory.swift index 4486e126f..c3b128e0f 100644 --- a/Sources/Auth/AuthClientFactory.swift +++ b/Sources/Auth/AuthClientFactory.swift @@ -6,12 +6,11 @@ import WalletConnectPairing public struct AuthClientFactory { - public static func create(metadata: AppMetadata, account: Account, relayClient: RelayClient) -> AuthClient { + public static func create(metadata: AppMetadata, account: Account?, relayClient: RelayClient, logger: ConsoleLogging) -> AuthClient { let keyValueStorage = UserDefaults.standard let historyStorage = CodableStore(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.jsonRpcHistory.rawValue) let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk") let pairingStore = PairingStorage(storage: SequenceStore(store: .init(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.pairings.rawValue))) - let logger = ConsoleLogger(loggingLevel: .off) let kms = KeyManagementService(keychain: keychainStorage) let serializer = Serializer(kms: kms) let history = RPCHistory(keyValueStore: historyStorage) @@ -22,7 +21,7 @@ public struct AuthClientFactory { let messageSigner = MessageSigner(signer: Signer()) let appRespondSubscriber = AppRespondSubscriber(networkingInteractor: networkingInteractor, logger: logger, rpcHistory: history, signatureVerifier: messageSigner, messageFormatter: SIWEMessageFormatter) let walletPairService = WalletPairService(networkingInteractor: networkingInteractor, kms: kms, pairingStorage: pairingStore) - let walletRequestSubscriber = WalletRequestSubscriber(networkingInteractor: networkingInteractor, logger: logger, messageFormatter: SIWEMessageFormatter, address: account.address) + let walletRequestSubscriber = WalletRequestSubscriber(networkingInteractor: networkingInteractor, logger: logger, messageFormatter: SIWEMessageFormatter, 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 ddd28089c..2dfad7f3a 100644 --- a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift +++ b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift @@ -6,7 +6,7 @@ import JSONRPC class WalletRequestSubscriber { private let networkingInteractor: NetworkInteracting private let logger: ConsoleLogging - private let address: String + private let address: String? private var publishers = [AnyCancellable]() private let messageFormatter: SIWEMessageFormatting var onRequest: ((_ id: RPCID, _ message: String)->Void)? @@ -14,7 +14,7 @@ class WalletRequestSubscriber { init(networkingInteractor: NetworkInteracting, logger: ConsoleLogging, messageFormatter: SIWEMessageFormatting, - address: String) { + address: String?) { self.networkingInteractor = networkingInteractor self.logger = logger self.address = address @@ -23,6 +23,10 @@ class WalletRequestSubscriber { } private func subscribeForRequest() { + guard let address = address else { + logger.warn("unexpected request") + return + } networkingInteractor.requestPublisher.sink { [unowned self] subscriptionPayload in guard let requestId = subscriptionPayload.request.id, From 64b6d76c66f0e45e8c452057facf30118f5bfcd5 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Tue, 16 Aug 2022 19:07:42 +0300 Subject: [PATCH 45/89] Separate relay configuration --- Example/DApp/SceneDelegate.swift | 3 ++- Example/ExampleApp/SceneDelegate.swift | 3 ++- Sources/WalletConnectRelay/Relay.swift | 4 +-- Sources/WalletConnectRelay/RelayConfig.swift | 2 +- Sources/WalletConnectSign/Sign/Sign.swift | 26 ++++--------------- .../WalletConnectSign/Sign/SignConfig.swift | 23 ---------------- 6 files changed, 12 insertions(+), 49 deletions(-) delete mode 100644 Sources/WalletConnectSign/Sign/SignConfig.swift diff --git a/Example/DApp/SceneDelegate.swift b/Example/DApp/SceneDelegate.swift index 5e2d51a82..4cb1f895e 100644 --- a/Example/DApp/SceneDelegate.swift +++ b/Example/DApp/SceneDelegate.swift @@ -29,7 +29,8 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { url: "wallet.connect", icons: ["https://avatars.githubusercontent.com/u/37784886"]) - Sign.configure(metadata: metadata, projectId: "8ba9ee138960775e5231b70cc5ef1c3a", socketFactory: SocketFactory()) + Relay.configure(projectId: "8ba9ee138960775e5231b70cc5ef1c3a", socketFactory: SocketFactory()) + Sign.configure(metadata: metadata) if CommandLine.arguments.contains("-cleanInstall") { try? Sign.instance.cleanup() diff --git a/Example/ExampleApp/SceneDelegate.swift b/Example/ExampleApp/SceneDelegate.swift index a0dd1b2d6..660e6f195 100644 --- a/Example/ExampleApp/SceneDelegate.swift +++ b/Example/ExampleApp/SceneDelegate.swift @@ -23,7 +23,8 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { url: "example.wallet", icons: ["https://avatars.githubusercontent.com/u/37784886"]) - Sign.configure(metadata: metadata, projectId: "8ba9ee138960775e5231b70cc5ef1c3a", socketFactory: SocketFactory()) + Relay.configure(projectId: "8ba9ee138960775e5231b70cc5ef1c3a", socketFactory: SocketFactory()) + Sign.configure(metadata: metadata) if CommandLine.arguments.contains("-cleanInstall") { try? Sign.instance.cleanup() diff --git a/Sources/WalletConnectRelay/Relay.swift b/Sources/WalletConnectRelay/Relay.swift index f9282aeed..f6f823cf5 100644 --- a/Sources/WalletConnectRelay/Relay.swift +++ b/Sources/WalletConnectRelay/Relay.swift @@ -19,10 +19,10 @@ public class Relay { private init() { } static public func configure( - relayHost: String, + relayHost: String = "relay.walletconnect.com", projectId: String, socketFactory: WebSocketFactory, - socketConnectionType: SocketConnectionType + socketConnectionType: SocketConnectionType = .automatic ) { Relay.config = Relay.Config( relayHost: relayHost, diff --git a/Sources/WalletConnectRelay/RelayConfig.swift b/Sources/WalletConnectRelay/RelayConfig.swift index 10a224132..b716ed9ca 100644 --- a/Sources/WalletConnectRelay/RelayConfig.swift +++ b/Sources/WalletConnectRelay/RelayConfig.swift @@ -1,7 +1,7 @@ import Foundation extension Relay { - public struct Config { + struct Config { let relayHost: String let projectId: String let socketFactory: WebSocketFactory diff --git a/Sources/WalletConnectSign/Sign/Sign.swift b/Sources/WalletConnectSign/Sign/Sign.swift index 6d66848c8..75a384af7 100644 --- a/Sources/WalletConnectSign/Sign/Sign.swift +++ b/Sources/WalletConnectSign/Sign/Sign.swift @@ -9,36 +9,20 @@ public typealias Blockchain = WalletConnectUtils.Blockchain public class Sign { public static var instance: SignClient = { - guard let config = Sign.config else { + guard let metadata = Sign.metadata else { fatalError("Error - you must call Sign.configure(_:) before accessing the shared instance.") } return SignClientFactory.create( - metadata: config.metadata, + metadata: metadata, relayClient: Relay.instance ) }() - private static var config: Config? + private static var metadata: AppMetadata? private init() { } - static public func configure( - metadata: AppMetadata, - projectId: String, - socketFactory: WebSocketFactory, - socketConnectionType: SocketConnectionType = .automatic - ) { - Sign.config = Sign.Config( - metadata: metadata, - projectId: projectId, - socketFactory: socketFactory, - socketConnectionType: socketConnectionType - ) - Relay.configure( - relayHost: "relay.walletconnect.com", - projectId: projectId, - socketFactory: socketFactory, - socketConnectionType: socketConnectionType - ) + static public func configure(metadata: AppMetadata) { + Sign.metadata = metadata } } diff --git a/Sources/WalletConnectSign/Sign/SignConfig.swift b/Sources/WalletConnectSign/Sign/SignConfig.swift deleted file mode 100644 index 4ba4edeaa..000000000 --- a/Sources/WalletConnectSign/Sign/SignConfig.swift +++ /dev/null @@ -1,23 +0,0 @@ -import Foundation -import WalletConnectRelay - -public extension Sign { - struct Config { - let metadata: AppMetadata - let projectId: String - let socketFactory: WebSocketFactory - let socketConnectionType: SocketConnectionType - - public init( - metadata: AppMetadata, - projectId: String, - socketFactory: WebSocketFactory, - socketConnectionType: SocketConnectionType = .automatic - ) { - self.metadata = metadata - self.projectId = projectId - self.socketFactory = socketFactory - self.socketConnectionType = socketConnectionType - } - } -} From 2ed473b84d0ab8d13e0d84b49ddaa90727829b97 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 17 Aug 2022 09:42:40 +0200 Subject: [PATCH 46/89] add error code --- Sources/Auth/Types/ErrorCode.swift | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 Sources/Auth/Types/ErrorCode.swift diff --git a/Sources/Auth/Types/ErrorCode.swift b/Sources/Auth/Types/ErrorCode.swift new file mode 100644 index 000000000..cb2cd7126 --- /dev/null +++ b/Sources/Auth/Types/ErrorCode.swift @@ -0,0 +1,7 @@ + +import Foundation + +struct ErrorCode: Codable, Equatable { + let code: Int + let message: String +} From 851bc663651fbd35f7def9d02de22a4526059f43 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 17 Aug 2022 09:44:51 +0200 Subject: [PATCH 47/89] fix build --- Sources/Auth/Services/App/AppRespondSubscriber.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/Auth/Services/App/AppRespondSubscriber.swift b/Sources/Auth/Services/App/AppRespondSubscriber.swift index 9e54fcf24..67e52c5e0 100644 --- a/Sources/Auth/Services/App/AppRespondSubscriber.swift +++ b/Sources/Auth/Services/App/AppRespondSubscriber.swift @@ -28,7 +28,7 @@ class AppRespondSubscriber { private func subscribeForResponse() { networkingInteractor.responsePublisher.sink { [unowned self] subscriptionPayload in guard - let requestId = subscriptionPayload.request.id, + let requestId = subscriptionPayload.response.id, let request = rpcHistory.get(recordId: requestId)?.request, let requestParams = request.params, request.method == "wc_authRequest" else { return } @@ -36,7 +36,7 @@ class AppRespondSubscriber { networkingInteractor.unsubscribe(topic: subscriptionPayload.topic) do { - guard let cacao = try subscriptionPayload.request.result?.get(Cacao.self) else { + guard let cacao = try subscriptionPayload.response.result?.get(Cacao.self) else { return logger.debug("Malformed auth response params") } From 3a08b411b0b5c90f251998c336995b823e16e11f Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 17 Aug 2022 09:57:42 +0200 Subject: [PATCH 48/89] Add respond result --- Sources/Auth/AuthClient.swift | 4 ++-- .../Auth/Services/Wallet/WalletRespondService.swift | 11 ++++++++++- Sources/Auth/Types/ErrorCode.swift | 3 ++- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/Sources/Auth/AuthClient.swift b/Sources/Auth/AuthClient.swift index d4ce191e9..79005754d 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(_ params: RespondParams) async throws { + public func respond(_ result: Result) async throws { guard let account = account else { throw Errors.unknownWalletAddress } - try await walletRespondService.respond(respondParams: params, account: account) + try await walletRespondService.respond(result: result, account: account) } public func getPendingRequests() throws -> [AuthRequest] { diff --git a/Sources/Auth/Services/Wallet/WalletRespondService.swift b/Sources/Auth/Services/Wallet/WalletRespondService.swift index 81a37db03..cbec060d0 100644 --- a/Sources/Auth/Services/Wallet/WalletRespondService.swift +++ b/Sources/Auth/Services/Wallet/WalletRespondService.swift @@ -22,8 +22,17 @@ actor WalletRespondService { self.kms = kms self.rpcHistory = rpcHistory } + + func respond(result: Result, account: Account) async throws { + switch result { + case .success(let params): + respond(respondParams: params, account: account) + case .failure(let error): + //TODO respond with error + } + } - func respond(respondParams: RespondParams, account: Account) async throws { + private func respond(respondParams: RespondParams, account: Account) async throws { guard let request = rpcHistory.get(recordId: RPCID(respondParams.id))?.request else { throw Errors.recordForIdNotFound } guard let authRequestParams = try? request.params?.get(AuthRequestParams.self) else { throw Errors.malformedAuthRequestParams } diff --git a/Sources/Auth/Types/ErrorCode.swift b/Sources/Auth/Types/ErrorCode.swift index cb2cd7126..0645a2985 100644 --- a/Sources/Auth/Types/ErrorCode.swift +++ b/Sources/Auth/Types/ErrorCode.swift @@ -1,7 +1,8 @@ import Foundation -struct ErrorCode: Codable, Equatable { +public struct ErrorCode: Codable, Equatable, Error { let code: Int let message: String } +x` From acc28eb1ab239f233fa7b7d5964503399c6a43e7 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 17 Aug 2022 10:02:40 +0200 Subject: [PATCH 49/89] add result to on response --- Sources/Auth/AuthClient.swift | 8 ++++---- Sources/Auth/Services/App/AppRespondSubscriber.swift | 5 +++-- Sources/Auth/Services/Wallet/WalletRespondService.swift | 4 ++-- Sources/Auth/Types/ErrorCode.swift | 1 - 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Sources/Auth/AuthClient.swift b/Sources/Auth/AuthClient.swift index 79005754d..d41e07180 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, cacao: Cacao), Never>() - public var authResponsePublisher: AnyPublisher<(id: RPCID, cacao: Cacao), Never> { + private var authResponsePublisherSubject = PassthroughSubject<(id: RPCID, result: Result), Never>() + public var authResponsePublisher: AnyPublisher<(id: RPCID, result: Result), Never> { authResponsePublisherSubject.eraseToAnyPublisher() } @@ -107,8 +107,8 @@ public class AuthClient { #endif private func setUpPublishers() { - appRespondSubscriber.onResponse = { [unowned self] (id, cacao) in - authResponsePublisherSubject.send((id, cacao)) + appRespondSubscriber.onResponse = { [unowned self] (id, result) in + authResponsePublisherSubject.send((id, result)) } walletRequestSubscriber.onRequest = { [unowned self] (id, message) in diff --git a/Sources/Auth/Services/App/AppRespondSubscriber.swift b/Sources/Auth/Services/App/AppRespondSubscriber.swift index 9e54fcf24..0b251ea29 100644 --- a/Sources/Auth/Services/App/AppRespondSubscriber.swift +++ b/Sources/Auth/Services/App/AppRespondSubscriber.swift @@ -10,7 +10,7 @@ class AppRespondSubscriber { private let signatureVerifier: MessageSignatureVerifying private let messageFormatter: SIWEMessageFormatting private var publishers = [AnyCancellable]() - var onResponse: ((_ id: RPCID, _ cacao: Cacao) -> Void)? + var onResponse: ((_ id: RPCID, _ result: Result) -> Void)? init(networkingInteractor: NetworkInteracting, logger: ConsoleLogging, @@ -26,6 +26,7 @@ class AppRespondSubscriber { } private func subscribeForResponse() { + // TODO - handle error response networkingInteractor.responsePublisher.sink { [unowned self] subscriptionPayload in guard let requestId = subscriptionPayload.request.id, @@ -54,7 +55,7 @@ class AppRespondSubscriber { message: message, address: address ) - onResponse?(requestId, cacao) + onResponse?(requestId, .success(cacao)) } catch { logger.debug("Received response with invalid signature") } diff --git a/Sources/Auth/Services/Wallet/WalletRespondService.swift b/Sources/Auth/Services/Wallet/WalletRespondService.swift index cbec060d0..db2a598bc 100644 --- a/Sources/Auth/Services/Wallet/WalletRespondService.swift +++ b/Sources/Auth/Services/Wallet/WalletRespondService.swift @@ -26,9 +26,9 @@ actor WalletRespondService { func respond(result: Result, account: Account) async throws { switch result { case .success(let params): - respond(respondParams: params, account: account) + try await respond(respondParams: params, account: account) case .failure(let error): - //TODO respond with error + fatalError("TODO respond with error") } } diff --git a/Sources/Auth/Types/ErrorCode.swift b/Sources/Auth/Types/ErrorCode.swift index 0645a2985..e56498149 100644 --- a/Sources/Auth/Types/ErrorCode.swift +++ b/Sources/Auth/Types/ErrorCode.swift @@ -5,4 +5,3 @@ public struct ErrorCode: Codable, Equatable, Error { let code: Int let message: String } -x` From 895da5c79da3f79322d2fe0744c39319d78f2a13 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 17 Aug 2022 10:17:32 +0200 Subject: [PATCH 50/89] create request test --- Example/ExampleApp.xcodeproj/project.pbxproj | 4 +++ Example/IntegrationTests/Auth/AuthTests.swift | 9 ++++++- .../Stubs/RequestParams.swift | 25 +++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 Example/IntegrationTests/Stubs/RequestParams.swift diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index 1b2c1316e..cd4858011 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -47,6 +47,7 @@ 84DDB4ED28ABB663003D66ED /* WalletConnectAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 84DDB4EC28ABB663003D66ED /* WalletConnectAuth */; }; 84F568C2279582D200D0A289 /* Signer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F568C1279582D200D0A289 /* Signer.swift */; }; 84F568C42795832A00D0A289 /* EthereumTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F568C32795832A00D0A289 /* EthereumTransaction.swift */; }; + 84FE684628ACDB4700C893FF /* RequestParams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84FE684528ACDB4700C893FF /* RequestParams.swift */; }; A50F3946288005B200064555 /* Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = A50F3945288005B200064555 /* Types.swift */; }; A5629AA92876A23100094373 /* ChatService.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5629AA82876A23100094373 /* ChatService.swift */; }; A5629ABD2876CBC000094373 /* ChatListModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5629AB82876CBC000094373 /* ChatListModule.swift */; }; @@ -208,6 +209,7 @@ 84D2A66528A4F51E0088AE09 /* AuthTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthTests.swift; sourceTree = ""; }; 84F568C1279582D200D0A289 /* Signer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Signer.swift; sourceTree = ""; }; 84F568C32795832A00D0A289 /* EthereumTransaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EthereumTransaction.swift; sourceTree = ""; }; + 84FE684528ACDB4700C893FF /* RequestParams.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestParams.swift; sourceTree = ""; }; A50F3945288005B200064555 /* Types.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Types.swift; sourceTree = ""; }; A5629AA82876A23100094373 /* ChatService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatService.swift; sourceTree = ""; }; A5629AB82876CBC000094373 /* ChatListModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatListModule.swift; sourceTree = ""; }; @@ -898,6 +900,7 @@ A5E03E1028646F8000888481 /* KeychainStorageMock.swift */, A5E03E0E28646D8A00888481 /* WebSocketFactory.swift */, A5E03DFC286465D100888481 /* Stubs.swift */, + 84FE684528ACDB4700C893FF /* RequestParams.swift */, ); path = Stubs; sourceTree = ""; @@ -1277,6 +1280,7 @@ A5E03DFB286465C700888481 /* ClientDelegate.swift in Sources */, A5E03E03286466F400888481 /* ChatTests.swift in Sources */, 84D2A66628A4F51E0088AE09 /* AuthTests.swift in Sources */, + 84FE684628ACDB4700C893FF /* RequestParams.swift in Sources */, 7694A5262874296A0001257E /* RegistryTests.swift in Sources */, A5E03E0D28646AD200888481 /* RelayClientEndToEndTests.swift in Sources */, A5E03DFA286465C700888481 /* SignClientTests.swift in Sources */, diff --git a/Example/IntegrationTests/Auth/AuthTests.swift b/Example/IntegrationTests/Auth/AuthTests.swift index 99dce1da0..5191ca412 100644 --- a/Example/IntegrationTests/Auth/AuthTests.swift +++ b/Example/IntegrationTests/Auth/AuthTests.swift @@ -50,6 +50,13 @@ final class AuthTests: XCTestCase { } func testRequest() async { - + let requestExpectation = expectation(description: "request") + Task(priority: .high) { + try await app.request(RequestParams.stub()) + } + wallet.authRequestPublisher.sink { _ in + requestExpectation.fulfill() + }.store(in: &publishers) + wait(for: [requestExpectation], timeout: 2) } } diff --git a/Example/IntegrationTests/Stubs/RequestParams.swift b/Example/IntegrationTests/Stubs/RequestParams.swift new file mode 100644 index 000000000..89cb11ae9 --- /dev/null +++ b/Example/IntegrationTests/Stubs/RequestParams.swift @@ -0,0 +1,25 @@ + +import Foundation +@testable import Auth + +extension RequestParams { + static func stub(domain: String = "service.invalid", + chainId: String = "1", + nonce: String = "32891756", + aud: String = "https://service.invalid/login", + nbf: String? = nil, + exp: String? = nil, + statement: String? = "I accept the ServiceOrg Terms of Service: https://service.invalid/tos", + requestId: String? = nil, + resources: [String]? = ["ipfs://bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq/", "https://example.com/my-web2-claim.json"]) -> RequestParams { + return RequestParams(domain: domain, + chainId: chainId, + nonce: nonce, + aud: aud, + nbf: nbf, + exp: exp, + statement: statement, + requestId: requestId, + resources: resources) + } +} From 81152cce84a3691aa2218770b472b27a9cae8122 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 17 Aug 2022 10:22:57 +0200 Subject: [PATCH 51/89] fix subscription bug --- .../Auth/Services/Wallet/WalletRequestSubscriber.swift | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift index 2dfad7f3a..7f8cd0516 100644 --- a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift +++ b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift @@ -19,14 +19,12 @@ class WalletRequestSubscriber { self.logger = logger self.address = address self.messageFormatter = messageFormatter - subscribeForRequest() + if let address = address { + subscribeForRequest() + } } private func subscribeForRequest() { - guard let address = address else { - logger.warn("unexpected request") - return - } networkingInteractor.requestPublisher.sink { [unowned self] subscriptionPayload in guard let requestId = subscriptionPayload.request.id, From a29ad06e585e2ece1cdf83b1013158ddfd800cf9 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 17 Aug 2022 10:24:11 +0200 Subject: [PATCH 52/89] fix buiild --- Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift index 7f8cd0516..77c4c2847 100644 --- a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift +++ b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift @@ -19,7 +19,7 @@ class WalletRequestSubscriber { self.logger = logger self.address = address self.messageFormatter = messageFormatter - if let address = address { + if address != nil { subscribeForRequest() } } @@ -36,7 +36,7 @@ class WalletRequestSubscriber { let message = messageFormatter.formatMessage( from: authRequestParams.payloadParams, - address: address + address: address! ) onRequest?(requestId, message) From 824a0eeb23f73617f92d8077c0a3932b485621a4 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 17 Aug 2022 10:35:25 +0200 Subject: [PATCH 53/89] refactor Auth client factory --- Example/IntegrationTests/Auth/AuthTests.swift | 6 ++++-- Sources/Auth/AuthClientFactory.swift | 9 +++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/Example/IntegrationTests/Auth/AuthTests.swift b/Example/IntegrationTests/Auth/AuthTests.swift index 5191ca412..842953fa4 100644 --- a/Example/IntegrationTests/Auth/AuthTests.swift +++ b/Example/IntegrationTests/Auth/AuthTests.swift @@ -45,8 +45,10 @@ final class AuthTests: XCTestCase { return AuthClientFactory.create( metadata: AppMetadata(name: name, description: "", url: "", icons: [""]), account: account, - relayClient: relayClient, - logger: logger) + logger: logger, + keyValueStorage: RuntimeKeyValueStorage(), + keychainStorage: keychain, + relayClient: relayClient) } func testRequest() async { diff --git a/Sources/Auth/AuthClientFactory.swift b/Sources/Auth/AuthClientFactory.swift index c3b128e0f..9948bb638 100644 --- a/Sources/Auth/AuthClientFactory.swift +++ b/Sources/Auth/AuthClientFactory.swift @@ -6,10 +6,15 @@ import WalletConnectPairing public struct AuthClientFactory { - public static func create(metadata: AppMetadata, account: Account?, relayClient: RelayClient, logger: ConsoleLogging) -> AuthClient { + public static func create(metadata: AppMetadata, account: Account?, relayClient: RelayClient) -> AuthClient { + let logger = ConsoleLogger(loggingLevel: .off) let keyValueStorage = UserDefaults.standard - let historyStorage = CodableStore(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.jsonRpcHistory.rawValue) let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk") + return AuthClientFactory.create(metadata: metadata, account: account, logger: logger, keyValueStorage: keyValueStorage, keychainStorage: keychainStorage, relayClient: relayClient) + } + + static func create(metadata: AppMetadata, account: Account?, logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, relayClient: RelayClient) -> AuthClient { + let historyStorage = CodableStore(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.jsonRpcHistory.rawValue) let pairingStore = PairingStorage(storage: SequenceStore(store: .init(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.pairings.rawValue))) let kms = KeyManagementService(keychain: keychainStorage) let serializer = Serializer(kms: kms) From 55a38d260072cec845120590ba0419e7c8bbbe75 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 17 Aug 2022 10:35:25 +0200 Subject: [PATCH 54/89] refactor Auth client factory --- Example/IntegrationTests/Auth/AuthTests.swift | 6 ++++-- Sources/Auth/AuthClientFactory.swift | 9 +++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/Example/IntegrationTests/Auth/AuthTests.swift b/Example/IntegrationTests/Auth/AuthTests.swift index 99dce1da0..1b721162f 100644 --- a/Example/IntegrationTests/Auth/AuthTests.swift +++ b/Example/IntegrationTests/Auth/AuthTests.swift @@ -45,8 +45,10 @@ final class AuthTests: XCTestCase { return AuthClientFactory.create( metadata: AppMetadata(name: name, description: "", url: "", icons: [""]), account: account, - relayClient: relayClient, - logger: logger) + logger: logger, + keyValueStorage: RuntimeKeyValueStorage(), + keychainStorage: keychain, + relayClient: relayClient) } func testRequest() async { diff --git a/Sources/Auth/AuthClientFactory.swift b/Sources/Auth/AuthClientFactory.swift index c3b128e0f..9948bb638 100644 --- a/Sources/Auth/AuthClientFactory.swift +++ b/Sources/Auth/AuthClientFactory.swift @@ -6,10 +6,15 @@ import WalletConnectPairing public struct AuthClientFactory { - public static func create(metadata: AppMetadata, account: Account?, relayClient: RelayClient, logger: ConsoleLogging) -> AuthClient { + public static func create(metadata: AppMetadata, account: Account?, relayClient: RelayClient) -> AuthClient { + let logger = ConsoleLogger(loggingLevel: .off) let keyValueStorage = UserDefaults.standard - let historyStorage = CodableStore(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.jsonRpcHistory.rawValue) let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk") + return AuthClientFactory.create(metadata: metadata, account: account, logger: logger, keyValueStorage: keyValueStorage, keychainStorage: keychainStorage, relayClient: relayClient) + } + + static func create(metadata: AppMetadata, account: Account?, logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, relayClient: RelayClient) -> AuthClient { + let historyStorage = CodableStore(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.jsonRpcHistory.rawValue) let pairingStore = PairingStorage(storage: SequenceStore(store: .init(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.pairings.rawValue))) let kms = KeyManagementService(keychain: keychainStorage) let serializer = Serializer(kms: kms) From a4cb644e8267f980d56fcdd95ebe8b4f7de4ca68 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 17 Aug 2022 10:41:31 +0200 Subject: [PATCH 55/89] update auth test --- Example/IntegrationTests/Auth/AuthTests.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Example/IntegrationTests/Auth/AuthTests.swift b/Example/IntegrationTests/Auth/AuthTests.swift index 842953fa4..6a3151e7b 100644 --- a/Example/IntegrationTests/Auth/AuthTests.swift +++ b/Example/IntegrationTests/Auth/AuthTests.swift @@ -54,7 +54,8 @@ final class AuthTests: XCTestCase { func testRequest() async { let requestExpectation = expectation(description: "request") Task(priority: .high) { - try await app.request(RequestParams.stub()) + let uri = try await app.request(RequestParams.stub()) + try await wallet.pair(uri: uri) } wallet.authRequestPublisher.sink { _ in requestExpectation.fulfill() From 57b5400b411e71922167fa6b9c7a1e4f0eb82bfc Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 17 Aug 2022 11:10:07 +0200 Subject: [PATCH 56/89] fix build --- Sources/Auth/AuthClientFactory.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Auth/AuthClientFactory.swift b/Sources/Auth/AuthClientFactory.swift index 9948bb638..38630ab24 100644 --- a/Sources/Auth/AuthClientFactory.swift +++ b/Sources/Auth/AuthClientFactory.swift @@ -19,7 +19,7 @@ public struct AuthClientFactory { let kms = KeyManagementService(keychain: keychainStorage) let serializer = Serializer(kms: kms) let history = RPCHistory(keyValueStore: historyStorage) - let networkingInteractor = NetworkingInteractor(relayClient: relayClient, serializer: serializer, rpcHistory: history) + let networkingInteractor = NetworkingInteractor(relayClient: relayClient, serializer: serializer, logger: logger, rpcHistory: history) let SIWEMessageFormatter = SIWEMessageFormatter() let appPairService = AppPairService(networkingInteractor: networkingInteractor, kms: kms, pairingStorage: pairingStore) let appRequestService = AppRequestService(networkingInteractor: networkingInteractor, kms: kms, appMetadata: metadata) From b289accd9671c2cc24df67a3fb396d746fa30825 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 17 Aug 2022 11:14:34 +0200 Subject: [PATCH 57/89] pass test request --- Example/IntegrationTests/Auth/AuthTests.swift | 2 +- Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Example/IntegrationTests/Auth/AuthTests.swift b/Example/IntegrationTests/Auth/AuthTests.swift index 6a3151e7b..1f5bd6d78 100644 --- a/Example/IntegrationTests/Auth/AuthTests.swift +++ b/Example/IntegrationTests/Auth/AuthTests.swift @@ -52,7 +52,7 @@ final class AuthTests: XCTestCase { } func testRequest() async { - let requestExpectation = expectation(description: "request") + let requestExpectation = expectation(description: "request delivered to wallet") Task(priority: .high) { let uri = try await app.request(RequestParams.stub()) try await wallet.pair(uri: uri) diff --git a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift index 77c4c2847..30abea341 100644 --- a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift +++ b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift @@ -25,6 +25,7 @@ class WalletRequestSubscriber { } private func subscribeForRequest() { + print("adasdadsadsadsadsadsdscaadsdasdasdsdsds") networkingInteractor.requestPublisher.sink { [unowned self] subscriptionPayload in guard let requestId = subscriptionPayload.request.id, From 3497e47d97676e869fb7f540e0f0afe70573a2d8 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 17 Aug 2022 13:24:33 +0200 Subject: [PATCH 58/89] savepoint respond test --- Example/IntegrationTests/Auth/AuthTests.swift | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/Example/IntegrationTests/Auth/AuthTests.swift b/Example/IntegrationTests/Auth/AuthTests.swift index 1f5bd6d78..8e59d6395 100644 --- a/Example/IntegrationTests/Auth/AuthTests.swift +++ b/Example/IntegrationTests/Auth/AuthTests.swift @@ -53,13 +53,27 @@ final class AuthTests: XCTestCase { func testRequest() async { let requestExpectation = expectation(description: "request delivered to wallet") - Task(priority: .high) { - let uri = try await app.request(RequestParams.stub()) - try await wallet.pair(uri: uri) - } + let uri = try! await app.request(RequestParams.stub()) + try! await wallet.pair(uri: uri) wallet.authRequestPublisher.sink { _ in requestExpectation.fulfill() }.store(in: &publishers) wait(for: [requestExpectation], timeout: 2) } + + func testRespondSuccess() async { + let uri = try! await app.request(RequestParams.stub()) + try! await wallet.pair(uri: uri) + wallet.authRequestPublisher.sink { _ in + Task(priority: .high) { + await wallet.respond(.success(<#T##RespondParams#>)) + } + }.store(in: &publishers) + } +} + +extension RespondParams { + static func stub() -> RespondParams { + + } } From bd6424b9a3be48820bed0a860eebf06ca07dcd9e Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 17 Aug 2022 13:25:26 +0200 Subject: [PATCH 59/89] rename property to messageFormatter --- Sources/Auth/AuthClientFactory.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Auth/AuthClientFactory.swift b/Sources/Auth/AuthClientFactory.swift index 9948bb638..f5e6a6567 100644 --- a/Sources/Auth/AuthClientFactory.swift +++ b/Sources/Auth/AuthClientFactory.swift @@ -20,7 +20,7 @@ public struct AuthClientFactory { let serializer = Serializer(kms: kms) let history = RPCHistory(keyValueStore: historyStorage) let networkingInteractor = NetworkingInteractor(relayClient: relayClient, serializer: serializer, rpcHistory: history) - let SIWEMessageFormatter = SIWEMessageFormatter() + let messageFormatter = SIWEMessageFormatter() let appPairService = AppPairService(networkingInteractor: networkingInteractor, kms: kms, pairingStorage: pairingStore) let appRequestService = AppRequestService(networkingInteractor: networkingInteractor, kms: kms, appMetadata: metadata) let messageSigner = MessageSigner(signer: Signer()) From f36d57165679e3cac5bdfa966fccc21363de871a Mon Sep 17 00:00:00 2001 From: Maisa Date: Thu, 18 Aug 2022 01:06:53 -0300 Subject: [PATCH 60/89] 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 c4f0ffe153f63f51feb903816a3d0b6d9a574f65 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 18 Aug 2022 08:51:55 +0200 Subject: [PATCH 61/89] comment out code --- Example/IntegrationTests/Auth/AuthTests.swift | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Example/IntegrationTests/Auth/AuthTests.swift b/Example/IntegrationTests/Auth/AuthTests.swift index 8e59d6395..20ccc0a00 100644 --- a/Example/IntegrationTests/Auth/AuthTests.swift +++ b/Example/IntegrationTests/Auth/AuthTests.swift @@ -61,19 +61,19 @@ final class AuthTests: XCTestCase { wait(for: [requestExpectation], timeout: 2) } - func testRespondSuccess() async { - let uri = try! await app.request(RequestParams.stub()) - try! await wallet.pair(uri: uri) - wallet.authRequestPublisher.sink { _ in - Task(priority: .high) { - await wallet.respond(.success(<#T##RespondParams#>)) - } - }.store(in: &publishers) - } +// func testRespondSuccess() async { +// let uri = try! await app.request(RequestParams.stub()) +// try! await wallet.pair(uri: uri) +// wallet.authRequestPublisher.sink { _ in +// Task(priority: .high) { +// await wallet.respond(.success(<#T##RespondParams#>)) +// } +// }.store(in: &publishers) +// } } -extension RespondParams { - static func stub() -> RespondParams { - - } -} +//extension RespondParams { +// static func stub() -> RespondParams { +// +// } +//} From 35601828e284f241ba91c552208f355370b4d4cc Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 18 Aug 2022 09:27:00 +0200 Subject: [PATCH 62/89] savepoint --- Example/IntegrationTests/Auth/AuthTests.swift | 45 ++++++++++++------- .../Wallet/WalletRespondService.swift | 2 +- Sources/Auth/Types/RespondParams.swift | 1 - 3 files changed, 31 insertions(+), 17 deletions(-) diff --git a/Example/IntegrationTests/Auth/AuthTests.swift b/Example/IntegrationTests/Auth/AuthTests.swift index 20ccc0a00..bd130920e 100644 --- a/Example/IntegrationTests/Auth/AuthTests.swift +++ b/Example/IntegrationTests/Auth/AuthTests.swift @@ -13,7 +13,7 @@ final class AuthTests: XCTestCase { override func setUp() { app = makeClient(prefix: "👻 App") - let walletAccount = Account(chainIdentifier: "eip155:1", address: "0x3627523167367216556273151")! + let walletAccount = Account(chainIdentifier: "eip155:1", address: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2")! wallet = makeClient(prefix: "🤑 Wallet", account: walletAccount) let expectation = expectation(description: "Wait Clients Connected") @@ -61,19 +61,34 @@ final class AuthTests: XCTestCase { wait(for: [requestExpectation], timeout: 2) } -// func testRespondSuccess() async { -// let uri = try! await app.request(RequestParams.stub()) -// try! await wallet.pair(uri: uri) -// wallet.authRequestPublisher.sink { _ in -// Task(priority: .high) { -// await wallet.respond(.success(<#T##RespondParams#>)) -// } -// }.store(in: &publishers) -// } + func testRespondSuccess() async { + let responseExpectation = expectation(description: "successful response delivered") + let uri = try! await app.request(RequestParams.stub()) + try! await wallet.pair(uri: uri) + wallet.authRequestPublisher.sink { [unowned self] (id, message) in + Task(priority: .high) { + try! await wallet.respond(.success(RespondParams.stub(id: id))) + } + } + .store(in: &publishers) + app.authResponsePublisher.sink { (id, result) in + guard case .success = result else { XCTFail(); return } + responseExpectation.fulfill() + } + .store(in: &publishers) + } } -//extension RespondParams { -// static func stub() -> RespondParams { -// -// } -//} +extension RespondParams { + static func stub(id: Int64) -> RespondParams { + RespondParams( + id: id, + signature: CacaoSignature.stub()) + } +} + +extension CacaoSignature { + static func stub() -> CacaoSignature { + return CacaoSignature( + } +} diff --git a/Sources/Auth/Services/Wallet/WalletRespondService.swift b/Sources/Auth/Services/Wallet/WalletRespondService.swift index db2a598bc..a40bc384c 100644 --- a/Sources/Auth/Services/Wallet/WalletRespondService.swift +++ b/Sources/Auth/Services/Wallet/WalletRespondService.swift @@ -45,6 +45,6 @@ actor WalletRespondService { let cacao = CacaoFormatter().format(authRequestParams, respondParams.signature, account) let response = RPCResponse(id: request.id!, result: cacao) - try await networkingInteractor.respond(topic: respondParams.topic, response: response, tag: AuthResponseParams.tag, envelopeType: .type1(pubKey: selfPubKey.rawRepresentation)) + try await networkingInteractor.respond(topic: responseTopic, response: response, tag: AuthResponseParams.tag, envelopeType: .type1(pubKey: selfPubKey.rawRepresentation)) } } diff --git a/Sources/Auth/Types/RespondParams.swift b/Sources/Auth/Types/RespondParams.swift index b3757a64a..63f0e7d29 100644 --- a/Sources/Auth/Types/RespondParams.swift +++ b/Sources/Auth/Types/RespondParams.swift @@ -2,6 +2,5 @@ import Foundation public struct RespondParams { let id: Int64 - let topic: String let signature: CacaoSignature } From 51b5e54d9576b953c62a0545c58ad7d2fa3e3bd6 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 18 Aug 2022 09:52:40 +0200 Subject: [PATCH 63/89] #400 cacao object formatter --- .../Auth/Services/Common/CacaoFormatter.swift | 8 ++++--- .../Wallet/WalletRespondService.swift | 3 ++- Sources/Auth/Types/Cacao/CacaoPayload.swift | 24 +++++++++++++++---- .../ProtocolRPCParams/AuthRequestParams.swift | 1 + .../AuthResponseParams.swift | 1 + 5 files changed, 28 insertions(+), 9 deletions(-) diff --git a/Sources/Auth/Services/Common/CacaoFormatter.swift b/Sources/Auth/Services/Common/CacaoFormatter.swift index c35286b87..fef3b8a3d 100644 --- a/Sources/Auth/Services/Common/CacaoFormatter.swift +++ b/Sources/Auth/Services/Common/CacaoFormatter.swift @@ -2,11 +2,13 @@ import Foundation import WalletConnectUtils protocol CacaoFormatting { - func format(_ request: AuthRequestParams, _ signature: CacaoSignature, _ account: Account) -> Cacao + func format(_ request: AuthRequestParams, _ signature: CacaoSignature, _ didpkh: DIDPKH) -> Cacao } class CacaoFormatter: CacaoFormatting { - func format(_ request: AuthRequestParams, _ signature: CacaoSignature, _ account: Account) -> Cacao { - fatalError("not implemented") + func format(_ request: AuthRequestParams, _ signature: CacaoSignature, _ didpkh: DIDPKH) -> Cacao { + let header = CacaoHeader(t: "eip191") + let payload = CacaoPayload(params: request.payloadParams, didpkh: didpkh) + return Cacao(header: header, payload: payload, signature: signature) } } diff --git a/Sources/Auth/Services/Wallet/WalletRespondService.swift b/Sources/Auth/Services/Wallet/WalletRespondService.swift index a40bc384c..9effae7fe 100644 --- a/Sources/Auth/Services/Wallet/WalletRespondService.swift +++ b/Sources/Auth/Services/Wallet/WalletRespondService.swift @@ -42,7 +42,8 @@ actor WalletRespondService { let agreementKeys = try kms.performKeyAgreement(selfPublicKey: selfPubKey, peerPublicKey: peerPubKey) try kms.setAgreementSecret(agreementKeys, topic: responseTopic) - let cacao = CacaoFormatter().format(authRequestParams, respondParams.signature, account) + let didpkh = DIDPKH(account: account) + let cacao = CacaoFormatter().format(authRequestParams, respondParams.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)) diff --git a/Sources/Auth/Types/Cacao/CacaoPayload.swift b/Sources/Auth/Types/Cacao/CacaoPayload.swift index b38a3206c..1a71050c7 100644 --- a/Sources/Auth/Types/Cacao/CacaoPayload.swift +++ b/Sources/Auth/Types/Cacao/CacaoPayload.swift @@ -7,9 +7,23 @@ struct CacaoPayload: Codable, Equatable { let version: Int let nonce: String let iat: String - let nbf: String - let exp: String - let statement: String - let requestId: String - let resources: [String] + let nbf: String? + let exp: String? + let statement: String? + let requestId: String? + let resources: [String]? + + init(params: AuthPayload, didpkh: DIDPKH) { + self.iss = didpkh.iss + self.domain = params.domain + self.aud = params.aud + self.version = 1 + self.nonce = params.nonce + self.iat = params.iat + self.nbf = params.nbf + self.exp = params.exp + self.statement = params.statement + self.requestId = params.requestId + self.resources = params.resources + } } diff --git a/Sources/Auth/Types/ProtocolRPCParams/AuthRequestParams.swift b/Sources/Auth/Types/ProtocolRPCParams/AuthRequestParams.swift index c39ba3489..46feee75f 100644 --- a/Sources/Auth/Types/ProtocolRPCParams/AuthRequestParams.swift +++ b/Sources/Auth/Types/ProtocolRPCParams/AuthRequestParams.swift @@ -1,6 +1,7 @@ import Foundation import WalletConnectUtils +/// wc_authRequest RPC method request param struct AuthRequestParams: Codable, Equatable { let requester: Requester let payloadParams: AuthPayload diff --git a/Sources/Auth/Types/ProtocolRPCParams/AuthResponseParams.swift b/Sources/Auth/Types/ProtocolRPCParams/AuthResponseParams.swift index a0d64b152..2dab1477b 100644 --- a/Sources/Auth/Types/ProtocolRPCParams/AuthResponseParams.swift +++ b/Sources/Auth/Types/ProtocolRPCParams/AuthResponseParams.swift @@ -1,6 +1,7 @@ import Foundation import WalletConnectUtils +/// wc_authRequest RPC method respond param struct AuthResponseParams: Codable, Equatable { let header: CacaoHeader let payload: CacaoPayload From 6d463dff743ee890c2950ca2c5decf7bf5bb80af Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 18 Aug 2022 12:54:58 +0200 Subject: [PATCH 64/89] savepoint --- Example/IntegrationTests/Auth/AuthTests.swift | 7 +++++-- Sources/Auth/Services/Common/CacaoFormatter.swift | 2 +- Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift | 5 +++-- Sources/Auth/Services/Wallet/WalletRespondService.swift | 2 +- Sources/Auth/Types/Cacao/CacaoSignature.swift | 2 +- Sources/Auth/Types/RespondParams.swift | 2 +- 6 files changed, 12 insertions(+), 8 deletions(-) diff --git a/Example/IntegrationTests/Auth/AuthTests.swift b/Example/IntegrationTests/Auth/AuthTests.swift index bd130920e..a89765966 100644 --- a/Example/IntegrationTests/Auth/AuthTests.swift +++ b/Example/IntegrationTests/Auth/AuthTests.swift @@ -67,6 +67,7 @@ final class AuthTests: XCTestCase { try! await wallet.pair(uri: uri) wallet.authRequestPublisher.sink { [unowned self] (id, message) in Task(priority: .high) { + print("responding") try! await wallet.respond(.success(RespondParams.stub(id: id))) } } @@ -76,11 +77,13 @@ final class AuthTests: XCTestCase { responseExpectation.fulfill() } .store(in: &publishers) + wait(for: [responseExpectation], timeout: 2) + } } extension RespondParams { - static func stub(id: Int64) -> RespondParams { + static func stub(id: RPCID) -> RespondParams { RespondParams( id: id, signature: CacaoSignature.stub()) @@ -89,6 +92,6 @@ extension RespondParams { extension CacaoSignature { static func stub() -> CacaoSignature { - return CacaoSignature( + return CacaoSignature(t: "eip191", s: "438effc459956b57fcd9f3dac6c675f9cee88abf21acab7305e8e32aa0303a883b06dcbd956279a7a2ca21ffa882ff55cc22e8ab8ec0f3fe90ab45f306938cfa1b") } } diff --git a/Sources/Auth/Services/Common/CacaoFormatter.swift b/Sources/Auth/Services/Common/CacaoFormatter.swift index fef3b8a3d..b8331b577 100644 --- a/Sources/Auth/Services/Common/CacaoFormatter.swift +++ b/Sources/Auth/Services/Common/CacaoFormatter.swift @@ -7,7 +7,7 @@ protocol CacaoFormatting { class CacaoFormatter: CacaoFormatting { func format(_ request: AuthRequestParams, _ signature: CacaoSignature, _ didpkh: DIDPKH) -> Cacao { - let header = CacaoHeader(t: "eip191") + let header = CacaoHeader(t: "eip4361") let payload = CacaoPayload(params: request.payloadParams, didpkh: didpkh) return Cacao(header: header, payload: payload, signature: signature) } diff --git a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift index e678d8f5e..633f2c76d 100644 --- a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift +++ b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift @@ -25,8 +25,9 @@ class WalletRequestSubscriber { } private func subscribeForRequest() { - print("adasdadsadsadsadsadsdscaadsdasdasdsdsds") + 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 } @@ -37,7 +38,7 @@ class WalletRequestSubscriber { let message = messageFormatter.formatMessage( from: authRequestParams.payloadParams, - address: address! + address: address ) onRequest?(requestId, message) diff --git a/Sources/Auth/Services/Wallet/WalletRespondService.swift b/Sources/Auth/Services/Wallet/WalletRespondService.swift index 9effae7fe..ab38a20a4 100644 --- a/Sources/Auth/Services/Wallet/WalletRespondService.swift +++ b/Sources/Auth/Services/Wallet/WalletRespondService.swift @@ -33,7 +33,7 @@ actor WalletRespondService { } private func respond(respondParams: RespondParams, account: Account) async throws { - guard let request = rpcHistory.get(recordId: RPCID(respondParams.id))?.request else { throw Errors.recordForIdNotFound } + 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 } let peerPubKey = authRequestParams.requester.publicKey diff --git a/Sources/Auth/Types/Cacao/CacaoSignature.swift b/Sources/Auth/Types/Cacao/CacaoSignature.swift index 97c04c142..2c6c4c49e 100644 --- a/Sources/Auth/Types/Cacao/CacaoSignature.swift +++ b/Sources/Auth/Types/Cacao/CacaoSignature.swift @@ -3,5 +3,5 @@ import Foundation struct CacaoSignature: Codable, Equatable { let t: String let s: String - let m: String + let m: String? = nil } diff --git a/Sources/Auth/Types/RespondParams.swift b/Sources/Auth/Types/RespondParams.swift index 63f0e7d29..79926445e 100644 --- a/Sources/Auth/Types/RespondParams.swift +++ b/Sources/Auth/Types/RespondParams.swift @@ -1,6 +1,6 @@ import Foundation public struct RespondParams { - let id: Int64 + let id: RPCID let signature: CacaoSignature } From 55864ebb40286b30a08e62104e26747eb0397678 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 18 Aug 2022 13:48:03 +0200 Subject: [PATCH 65/89] Update tests fix key exchange bug --- Example/IntegrationTests/Auth/AuthTests.swift | 1 - Sources/Auth/AuthClientFactory.swift | 2 +- Sources/Auth/Services/App/AppRequestService.swift | 8 ++++++-- Sources/Auth/Services/Wallet/WalletRespondService.swift | 1 - 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Example/IntegrationTests/Auth/AuthTests.swift b/Example/IntegrationTests/Auth/AuthTests.swift index a89765966..ed7796ee6 100644 --- a/Example/IntegrationTests/Auth/AuthTests.swift +++ b/Example/IntegrationTests/Auth/AuthTests.swift @@ -78,7 +78,6 @@ final class AuthTests: XCTestCase { } .store(in: &publishers) wait(for: [responseExpectation], timeout: 2) - } } diff --git a/Sources/Auth/AuthClientFactory.swift b/Sources/Auth/AuthClientFactory.swift index 6d48fa179..cf75c3ce1 100644 --- a/Sources/Auth/AuthClientFactory.swift +++ b/Sources/Auth/AuthClientFactory.swift @@ -22,7 +22,7 @@ public struct AuthClientFactory { let networkingInteractor = NetworkingInteractor(relayClient: relayClient, serializer: serializer, logger: logger, rpcHistory: history) let messageFormatter = SIWEMessageFormatter() let appPairService = AppPairService(networkingInteractor: networkingInteractor, kms: kms, pairingStorage: pairingStore) - let appRequestService = AppRequestService(networkingInteractor: networkingInteractor, kms: kms, appMetadata: metadata) + let appRequestService = AppRequestService(networkingInteractor: networkingInteractor, kms: kms, appMetadata: metadata, logger: logger) let messageSigner = MessageSigner(signer: Signer()) let appRespondSubscriber = AppRespondSubscriber(networkingInteractor: networkingInteractor, logger: logger, rpcHistory: history, signatureVerifier: messageSigner, messageFormatter: messageFormatter) let walletPairService = WalletPairService(networkingInteractor: networkingInteractor, kms: kms, pairingStorage: pairingStore) diff --git a/Sources/Auth/Services/App/AppRequestService.swift b/Sources/Auth/Services/App/AppRequestService.swift index 216038deb..3d2c43443 100644 --- a/Sources/Auth/Services/App/AppRequestService.swift +++ b/Sources/Auth/Services/App/AppRequestService.swift @@ -7,23 +7,27 @@ actor AppRequestService { private let networkingInteractor: NetworkInteracting private let appMetadata: AppMetadata private let kms: KeyManagementService + private let logger: ConsoleLogging init(networkingInteractor: NetworkInteracting, kms: KeyManagementService, - appMetadata: AppMetadata) { + appMetadata: AppMetadata, + logger: ConsoleLogging) { self.networkingInteractor = networkingInteractor self.kms = kms self.appMetadata = appMetadata + self.logger = logger } func request(params: RequestParams, topic: String) async throws { let pubKey = try kms.createX25519KeyPair() - let responseTopic = pubKey.rawRepresentation.sha256().toHexString() + let responseTopic = pubKey.hexRepresentation let requester = AuthRequestParams.Requester(publicKey: pubKey.hexRepresentation, metadata: appMetadata) let issueAt = ISO8601DateFormatter().string(from: Date()) let payload = AuthPayload(requestParams: params, iat: issueAt) let params = AuthRequestParams(requester: requester, payloadParams: payload) let request = RPCRequest(method: "wc_authRequest", params: params) + logger.debug("Subscribibg for response topic: \(responseTopic)") try await networkingInteractor.requestNetworkAck(request, topic: topic, tag: AuthRequestParams.tag) try await networkingInteractor.subscribe(topic: responseTopic) } diff --git a/Sources/Auth/Services/Wallet/WalletRespondService.swift b/Sources/Auth/Services/Wallet/WalletRespondService.swift index ab38a20a4..6fac162ae 100644 --- a/Sources/Auth/Services/Wallet/WalletRespondService.swift +++ b/Sources/Auth/Services/Wallet/WalletRespondService.swift @@ -41,7 +41,6 @@ actor WalletRespondService { let selfPubKey = try kms.createX25519KeyPair() let agreementKeys = try kms.performKeyAgreement(selfPublicKey: selfPubKey, peerPublicKey: peerPubKey) try kms.setAgreementSecret(agreementKeys, topic: responseTopic) - let didpkh = DIDPKH(account: account) let cacao = CacaoFormatter().format(authRequestParams, respondParams.signature, didpkh) let response = RPCResponse(id: request.id!, result: cacao) From a2dc2027c878ad216e593af076fcd57057724171 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 18 Aug 2022 13:57:31 +0200 Subject: [PATCH 66/89] fix protocol issue --- Sources/Auth/Services/App/AppRequestService.swift | 2 +- Sources/Auth/Services/Wallet/WalletRespondService.swift | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Sources/Auth/Services/App/AppRequestService.swift b/Sources/Auth/Services/App/AppRequestService.swift index 3d2c43443..22a79b87c 100644 --- a/Sources/Auth/Services/App/AppRequestService.swift +++ b/Sources/Auth/Services/App/AppRequestService.swift @@ -21,7 +21,7 @@ actor AppRequestService { func request(params: RequestParams, topic: String) async throws { let pubKey = try kms.createX25519KeyPair() - let responseTopic = pubKey.hexRepresentation + let responseTopic = pubKey.rawRepresentation.sha256().toHexString() let requester = AuthRequestParams.Requester(publicKey: pubKey.hexRepresentation, metadata: appMetadata) let issueAt = ISO8601DateFormatter().string(from: Date()) let payload = AuthPayload(requestParams: params, iat: issueAt) diff --git a/Sources/Auth/Services/Wallet/WalletRespondService.swift b/Sources/Auth/Services/Wallet/WalletRespondService.swift index 6fac162ae..6b5030df9 100644 --- a/Sources/Auth/Services/Wallet/WalletRespondService.swift +++ b/Sources/Auth/Services/Wallet/WalletRespondService.swift @@ -36,11 +36,12 @@ actor WalletRespondService { 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 } - let peerPubKey = authRequestParams.requester.publicKey + 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) + let agreementKeys = try kms.performKeyAgreement(selfPublicKey: selfPubKey, peerPublicKey: peerPubKey.hexRepresentation) try kms.setAgreementSecret(agreementKeys, topic: responseTopic) + let didpkh = DIDPKH(account: account) let cacao = CacaoFormatter().format(authRequestParams, respondParams.signature, didpkh) let response = RPCResponse(id: request.id!, result: cacao) From 6d3f46c662269bb162334127bba2e0b34e96894f Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 18 Aug 2022 14:43:04 +0200 Subject: [PATCH 67/89] fix protocol issue - store pub key for response topic --- Sources/Auth/Services/App/AppRequestService.swift | 3 ++- Sources/Auth/Services/Common/NetworkingInteractor.swift | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Sources/Auth/Services/App/AppRequestService.swift b/Sources/Auth/Services/App/AppRequestService.swift index 22a79b87c..7dff38849 100644 --- a/Sources/Auth/Services/App/AppRequestService.swift +++ b/Sources/Auth/Services/App/AppRequestService.swift @@ -21,12 +21,13 @@ actor AppRequestService { func request(params: RequestParams, topic: String) async throws { let pubKey = try kms.createX25519KeyPair() - let responseTopic = pubKey.rawRepresentation.sha256().toHexString() + let responseTopic = pubKey.rawRepresentation.sha256().toHexString() let requester = AuthRequestParams.Requester(publicKey: pubKey.hexRepresentation, metadata: appMetadata) let issueAt = ISO8601DateFormatter().string(from: Date()) let payload = AuthPayload(requestParams: params, iat: issueAt) let params = AuthRequestParams(requester: requester, payloadParams: payload) let request = RPCRequest(method: "wc_authRequest", params: params) + try kms.setPublicKey(publicKey: pubKey, for: responseTopic) logger.debug("Subscribibg for response topic: \(responseTopic)") try await networkingInteractor.requestNetworkAck(request, topic: topic, tag: AuthRequestParams.tag) try await networkingInteractor.subscribe(topic: responseTopic) diff --git a/Sources/Auth/Services/Common/NetworkingInteractor.swift b/Sources/Auth/Services/Common/NetworkingInteractor.swift index d3bc9a3bb..fba8bf01d 100644 --- a/Sources/Auth/Services/Common/NetworkingInteractor.swift +++ b/Sources/Auth/Services/Common/NetworkingInteractor.swift @@ -57,7 +57,7 @@ class NetworkingInteractor: NetworkInteracting { } func unsubscribe(topic: String) { - fatalError("not implemented") +// fatalError("not implemented") } func request(_ request: RPCRequest, topic: String, tag: Int, envelopeType: Envelope.EnvelopeType) async throws { From a3239f871419b2e3d43075d8025a28089f502826 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 18 Aug 2022 15:06:27 +0200 Subject: [PATCH 68/89] update auth e2e test: received successful response --- Example/IntegrationTests/Auth/AuthTests.swift | 7 +++++-- Sources/Auth/Services/App/AppRespondSubscriber.swift | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Example/IntegrationTests/Auth/AuthTests.swift b/Example/IntegrationTests/Auth/AuthTests.swift index ed7796ee6..c5d7046c4 100644 --- a/Example/IntegrationTests/Auth/AuthTests.swift +++ b/Example/IntegrationTests/Auth/AuthTests.swift @@ -13,7 +13,7 @@ final class AuthTests: XCTestCase { override func setUp() { app = makeClient(prefix: "👻 App") - let walletAccount = Account(chainIdentifier: "eip155:1", address: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2")! + let walletAccount = Account(chainIdentifier: "eip155:1", address: "0x724d0D2DaD3fbB0C168f947B87Fa5DBe36F1A8bf")! wallet = makeClient(prefix: "🤑 Wallet", account: walletAccount) let expectation = expectation(description: "Wait Clients Connected") @@ -68,7 +68,10 @@ final class AuthTests: XCTestCase { wallet.authRequestPublisher.sink { [unowned self] (id, message) in Task(priority: .high) { print("responding") - try! await wallet.respond(.success(RespondParams.stub(id: id))) + let prvKey = Data(hex: "462c1dad6832d7d96ccf87bd6a686a4110e114aaaebd5512e552c0e3a87b480f") + let sig = try! MessageSigner(signer: Signer()).sign(message: message, privateKey: prvKey) + let cacaoSig = CacaoSignature(t: "eip191", s: sig) + try! await wallet.respond(.success(RespondParams(id: id, signature: cacaoSig))) } } .store(in: &publishers) diff --git a/Sources/Auth/Services/App/AppRespondSubscriber.swift b/Sources/Auth/Services/App/AppRespondSubscriber.swift index f9005587b..cba492bf3 100644 --- a/Sources/Auth/Services/App/AppRespondSubscriber.swift +++ b/Sources/Auth/Services/App/AppRespondSubscriber.swift @@ -55,6 +55,7 @@ class AppRespondSubscriber { message: message, address: address ) + logger.debug("Received response with valid signature") onResponse?(requestId, .success(cacao)) } catch { logger.debug("Received response with invalid signature") From 70d85d4fbbf45ab6e8802cd4e6614f3bc60db230 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Fri, 19 Aug 2022 10:45:10 +0200 Subject: [PATCH 69/89] update style, logs add unsubscribe to networking interactor --- Example/IntegrationTests/Auth/AuthTests.swift | 23 ++++--------------- .../Auth/Services/App/AppRequestService.swift | 2 +- .../Common/NetworkingInteractor.swift | 8 ++++++- 3 files changed, 12 insertions(+), 21 deletions(-) diff --git a/Example/IntegrationTests/Auth/AuthTests.swift b/Example/IntegrationTests/Auth/AuthTests.swift index c5d7046c4..05cb74088 100644 --- a/Example/IntegrationTests/Auth/AuthTests.swift +++ b/Example/IntegrationTests/Auth/AuthTests.swift @@ -9,6 +9,7 @@ import Combine final class AuthTests: XCTestCase { var app: AuthClient! var wallet: AuthClient! + let prvKey = Data(hex: "462c1dad6832d7d96ccf87bd6a686a4110e114aaaebd5512e552c0e3a87b480f") private var publishers = [AnyCancellable]() override func setUp() { @@ -67,11 +68,9 @@ final class AuthTests: XCTestCase { try! await wallet.pair(uri: uri) wallet.authRequestPublisher.sink { [unowned self] (id, message) in Task(priority: .high) { - print("responding") - let prvKey = Data(hex: "462c1dad6832d7d96ccf87bd6a686a4110e114aaaebd5512e552c0e3a87b480f") - let sig = try! MessageSigner(signer: Signer()).sign(message: message, privateKey: prvKey) - let cacaoSig = CacaoSignature(t: "eip191", s: sig) - try! await wallet.respond(.success(RespondParams(id: id, signature: cacaoSig))) + 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))) } } .store(in: &publishers) @@ -83,17 +82,3 @@ final class AuthTests: XCTestCase { wait(for: [responseExpectation], timeout: 2) } } - -extension RespondParams { - static func stub(id: RPCID) -> RespondParams { - RespondParams( - id: id, - signature: CacaoSignature.stub()) - } -} - -extension CacaoSignature { - static func stub() -> CacaoSignature { - return CacaoSignature(t: "eip191", s: "438effc459956b57fcd9f3dac6c675f9cee88abf21acab7305e8e32aa0303a883b06dcbd956279a7a2ca21ffa882ff55cc22e8ab8ec0f3fe90ab45f306938cfa1b") - } -} diff --git a/Sources/Auth/Services/App/AppRequestService.swift b/Sources/Auth/Services/App/AppRequestService.swift index 7dff38849..b3186e77d 100644 --- a/Sources/Auth/Services/App/AppRequestService.swift +++ b/Sources/Auth/Services/App/AppRequestService.swift @@ -28,7 +28,7 @@ actor AppRequestService { let params = AuthRequestParams(requester: requester, payloadParams: payload) let request = RPCRequest(method: "wc_authRequest", params: params) try kms.setPublicKey(publicKey: pubKey, for: responseTopic) - logger.debug("Subscribibg for response topic: \(responseTopic)") + logger.debug("AppRequestService: Subscribibg for response topic: \(responseTopic)") try await networkingInteractor.requestNetworkAck(request, topic: topic, tag: AuthRequestParams.tag) try await networkingInteractor.subscribe(topic: responseTopic) } diff --git a/Sources/Auth/Services/Common/NetworkingInteractor.swift b/Sources/Auth/Services/Common/NetworkingInteractor.swift index fba8bf01d..62d152ccb 100644 --- a/Sources/Auth/Services/Common/NetworkingInteractor.swift +++ b/Sources/Auth/Services/Common/NetworkingInteractor.swift @@ -57,7 +57,13 @@ class NetworkingInteractor: NetworkInteracting { } func unsubscribe(topic: String) { -// fatalError("not implemented") + relayClient.unsubscribe(topic: topic) { [unowned self] error in + if let error = error { + logger.error(error) + } else { + rpcHistory.deleteAll(forTopic: topic) + } + } } func request(_ request: RPCRequest, topic: String, tag: Int, envelopeType: Envelope.EnvelopeType) async throws { From e9943bc6748a0cd795f774156ec034b7135c9c9a Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Fri, 19 Aug 2022 11:42:40 +0200 Subject: [PATCH 70/89] add Auth instance --- Sources/Auth/Auth.swift | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 Sources/Auth/Auth.swift diff --git a/Sources/Auth/Auth.swift b/Sources/Auth/Auth.swift new file mode 100644 index 000000000..32e40c96c --- /dev/null +++ b/Sources/Auth/Auth.swift @@ -0,0 +1,27 @@ +import Foundation +import WalletConnectUtils +import WalletConnectRelay +import Combine + +public class Auth { + + public static var instance: AuthClient = { + guard let metadata = Auth.metadata else { + fatalError("Error - you must call Sign.configure(_:) before accessing the shared instance.") + } + return AuthClientFactory.create( + metadata: metadata, + account: account, + relayClient: Relay.instance) + }() + + private static var metadata: AppMetadata? + private static var account: Account? + + private init() { } + + static public func configure(metadata: AppMetadata, account: Account?) { + Auth.metadata = metadata + Auth.account = account + } +} From cc840720331635163df4bc22ce07738b845df5ae Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Fri, 19 Aug 2022 11:43:20 +0200 Subject: [PATCH 71/89] remove unnecessary import --- Sources/Auth/Auth.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Sources/Auth/Auth.swift b/Sources/Auth/Auth.swift index 32e40c96c..da4f026de 100644 --- a/Sources/Auth/Auth.swift +++ b/Sources/Auth/Auth.swift @@ -1,5 +1,4 @@ import Foundation -import WalletConnectUtils import WalletConnectRelay import Combine From 10a87cb68b82020b86810ae24ad5e417280bc547 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Fri, 19 Aug 2022 12:41:59 +0200 Subject: [PATCH 72/89] pr review improvements --- Sources/Auth/Auth.swift | 2 +- Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Sources/Auth/Auth.swift b/Sources/Auth/Auth.swift index da4f026de..70262bcd1 100644 --- a/Sources/Auth/Auth.swift +++ b/Sources/Auth/Auth.swift @@ -6,7 +6,7 @@ public class Auth { public static var instance: AuthClient = { guard let metadata = Auth.metadata else { - fatalError("Error - you must call Sign.configure(_:) before accessing the shared instance.") + fatalError("Error - you must call Auth.configure(_:) before accessing the shared instance.") } return AuthClientFactory.create( metadata: metadata, diff --git a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift index 633f2c76d..aa5cdde47 100644 --- a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift +++ b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift @@ -19,9 +19,7 @@ class WalletRequestSubscriber { self.logger = logger self.address = address self.messageFormatter = messageFormatter - if address != nil { - subscribeForRequest() - } + subscribeForRequest() } private func subscribeForRequest() { From 213736c77514c56dbfb6a56928bf60003a01a0a8 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Fri, 19 Aug 2022 12:51:26 +0200 Subject: [PATCH 73/89] add auth config --- Sources/Auth/Auth.swift | 16 ++++++++-------- Sources/Auth/AuthConfig.swift | 8 ++++++++ 2 files changed, 16 insertions(+), 8 deletions(-) create mode 100644 Sources/Auth/AuthConfig.swift diff --git a/Sources/Auth/Auth.swift b/Sources/Auth/Auth.swift index 70262bcd1..ab10c1f39 100644 --- a/Sources/Auth/Auth.swift +++ b/Sources/Auth/Auth.swift @@ -5,22 +5,22 @@ import Combine public class Auth { public static var instance: AuthClient = { - guard let metadata = Auth.metadata else { + guard let config = Auth.config else { fatalError("Error - you must call Auth.configure(_:) before accessing the shared instance.") } return AuthClientFactory.create( - metadata: metadata, - account: account, + metadata: config.metadata, + account: config.account, relayClient: Relay.instance) }() - - private static var metadata: AppMetadata? - private static var account: Account? + + private static var config: Config? private init() { } static public func configure(metadata: AppMetadata, account: Account?) { - Auth.metadata = metadata - Auth.account = account + Auth.config = Auth.Config( + metadata: metadata, + account: account) } } diff --git a/Sources/Auth/AuthConfig.swift b/Sources/Auth/AuthConfig.swift new file mode 100644 index 000000000..b364ec507 --- /dev/null +++ b/Sources/Auth/AuthConfig.swift @@ -0,0 +1,8 @@ +import Foundation + +extension Auth { + struct Config { + let metadata: AppMetadata + let account: Account? + } +} From dab13243a07d9817cbf31cd868c07c97439459b8 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 18 Aug 2022 02:28:47 +0300 Subject: [PATCH 74/89] 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 75/89] 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 76/89] 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 77/89] 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 78/89] 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 79/89] 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 27a13e98add99c07b5a2844d1a9ec7fcaf36db1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Vants?= Date: Fri, 19 Aug 2022 11:42:06 -0300 Subject: [PATCH 80/89] Moved URI tests to utils test target --- .../WalletConnectURITests.swift | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) rename Tests/{WalletConnectSignTests => WalletConnectUtilsTests}/WalletConnectURITests.swift (90%) diff --git a/Tests/WalletConnectSignTests/WalletConnectURITests.swift b/Tests/WalletConnectUtilsTests/WalletConnectURITests.swift similarity index 90% rename from Tests/WalletConnectSignTests/WalletConnectURITests.swift rename to Tests/WalletConnectUtilsTests/WalletConnectURITests.swift index e1e218a4b..bd0210d78 100644 --- a/Tests/WalletConnectSignTests/WalletConnectURITests.swift +++ b/Tests/WalletConnectUtilsTests/WalletConnectURITests.swift @@ -1,11 +1,11 @@ import XCTest -@testable import WalletConnectSign +@testable import WalletConnectUtils private let stubTopic = "8097df5f14871126866252c1b7479a14aefb980188fc35ec97d130d24bd887c8" private let stubSymKey = "587d5484ce2a2a6ee3ba1962fdd7e8588e06200c46823bd18fbd67def96ad303" private let stubProtocol = "irn" -private let stubURI = "wc:7f6e504bfad60b485450578e05678ed3e8e8c4751d3c6160be17160d63ec90f9@2?symKey=587d5484ce2a2a6ee3ba1962fdd7e8588e06200c46823bd18fbd67def96ad303&relay-protocol=irn" +private let stubURI = "wc:auth-\(stubTopic)@2?symKey=\(stubSymKey)&relay-protocol=\(stubProtocol)" final class WalletConnectURITests: XCTestCase { @@ -34,6 +34,8 @@ final class WalletConnectURITests: XCTestCase { XCTAssertEqual(expectedString, outputURIString) } + // MARK: - Init failure cases + func testInitFailsBadScheme() { let inputURIString = stubURI.replacingOccurrences(of: "wc:", with: "") let uri = WalletConnectURI(string: inputURIString) From 478463e1e03cfb7e15e0018f096dfb279ddefb08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Vants?= Date: Fri, 19 Aug 2022 12:03:02 -0300 Subject: [PATCH 81/89] Improved URI tests with randomness --- .../WalletConnectURITests.swift | 38 +++++++++++++------ 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/Tests/WalletConnectUtilsTests/WalletConnectURITests.swift b/Tests/WalletConnectUtilsTests/WalletConnectURITests.swift index bd0210d78..79ad2acaf 100644 --- a/Tests/WalletConnectUtilsTests/WalletConnectURITests.swift +++ b/Tests/WalletConnectUtilsTests/WalletConnectURITests.swift @@ -1,33 +1,48 @@ import XCTest @testable import WalletConnectUtils -private let stubTopic = "8097df5f14871126866252c1b7479a14aefb980188fc35ec97d130d24bd887c8" -private let stubSymKey = "587d5484ce2a2a6ee3ba1962fdd7e8588e06200c46823bd18fbd67def96ad303" -private let stubProtocol = "irn" +final class WalletConnectURITests: XCTestCase { -private let stubURI = "wc:auth-\(stubTopic)@2?symKey=\(stubSymKey)&relay-protocol=\(stubProtocol)" + var stubURI: String! -final class WalletConnectURITests: XCTestCase { + var stubTopic: String! + var stubSymKey: String! + let stubProtocol = "irn" + + override func setUp() { + let topic = Data.randomBytes(count: 32).toHexString() + let symKey = Data.randomBytes(count: 32).toHexString() + stubTopic = topic + stubSymKey = symKey + stubURI = "wc:\(topic)@2?symKey=\(symKey)&relay-protocol=\(stubProtocol)" + } + + override func tearDown() { + stubURI = nil + stubTopic = nil + stubSymKey = nil + } func testInitURIToString() { let inputURI = WalletConnectURI( - topic: "8097df5f14871126866252c1b7479a14aefb980188fc35ec97d130d24bd887c8", - symKey: "19c5ecc857963976fabb98ed6a3e0a6ab6b0d65c018b6e25fbdcd3a164def868", - relay: RelayProtocolOptions(protocol: "irn", data: nil)) + topic: stubTopic, + symKey: stubSymKey, + relay: RelayProtocolOptions(protocol: stubProtocol, data: nil)) let uriString = inputURI.absoluteString let outputURI = WalletConnectURI(string: uriString) XCTAssertEqual(inputURI, outputURI) + XCTAssertEqual(stubURI, outputURI?.absoluteString) } func testInitStringToURI() { - let inputURIString = stubURI + let inputURIString = stubURI! let uri = WalletConnectURI(string: inputURIString) let outputURIString = uri?.absoluteString XCTAssertEqual(inputURIString, outputURIString) } func testInitStringToURIAlternate() { - let expectedString = stubURI + let expectedString = stubURI! let inputURIString = expectedString.replacingOccurrences(of: "wc:", with: "wc://") let uri = WalletConnectURI(string: inputURIString) let outputURIString = uri?.absoluteString @@ -49,7 +64,8 @@ final class WalletConnectURITests: XCTestCase { } func testInitFailsNoSymKeyParam() { - let inputURIString = stubURI.replacingOccurrences(of: "symKey=\(stubSymKey)", with: "") + let symKey = stubSymKey! + let inputURIString = stubURI.replacingOccurrences(of: "symKey=\(symKey)", with: "") let uri = WalletConnectURI(string: inputURIString) XCTAssertNil(uri) } From e49a462d5bc77f286988202a101503ae73975462 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Vants?= Date: Sun, 21 Aug 2022 13:47:06 -0300 Subject: [PATCH 82/89] Implement API prefixing with unit tests --- .../WalletConnectUtils/WalletConnectURI.swift | 67 ++++++++++++---- .../WalletConnectURITests.swift | 79 +++++++++++-------- 2 files changed, 97 insertions(+), 49 deletions(-) diff --git a/Sources/WalletConnectUtils/WalletConnectURI.swift b/Sources/WalletConnectUtils/WalletConnectURI.swift index d149f7f70..18a0a6a66 100644 --- a/Sources/WalletConnectUtils/WalletConnectURI.swift +++ b/Sources/WalletConnectUtils/WalletConnectURI.swift @@ -2,42 +2,60 @@ import Foundation public struct WalletConnectURI: Equatable { + public enum TargetAPI: String, CaseIterable { + case sign + case chat + case auth + } + public let topic: String public let version: String public let symKey: String public let relay: RelayProtocolOptions - public init(topic: String, symKey: String, relay: RelayProtocolOptions) { + public var api: TargetAPI { + apiType ?? .sign + } + + public var absoluteString: String { + if let api = apiType { + return "wc:\(api.rawValue)-\(topic)@\(version)?symKey=\(symKey)&\(relayQuery)" + } + return "wc:\(topic)@\(version)?symKey=\(symKey)&\(relayQuery)" + } + + private let apiType: TargetAPI? + + public init(topic: String, symKey: String, relay: RelayProtocolOptions, api: TargetAPI? = nil) { self.version = "2" self.topic = topic self.symKey = symKey self.relay = relay + self.apiType = api } public init?(string: String) { - guard string.hasPrefix("wc:") else { - return nil - } - let urlString = !string.hasPrefix("wc://") ? string.replacingOccurrences(of: "wc:", with: "wc://") : string - guard let components = URLComponents(string: urlString) else { + guard let components = Self.parseURIComponents(from: string) else { return nil } let query: [String: String]? = components.queryItems?.reduce(into: [:]) { $0[$1.name] = $1.value } - guard let topic = components.user, - let version = components.host, - let symKey = query?["symKey"], - let relayProtocol = query?["relay-protocol"] - else { return nil } + guard + let userString = components.user, + let version = components.host, + let symKey = query?["symKey"], + let relayProtocol = query?["relay-protocol"] + else { + return nil + } + let uriUser = Self.parseURIUser(from: userString) let relayData = query?["relay-data"] + self.version = version - self.topic = topic + self.topic = uriUser.topic self.symKey = symKey self.relay = RelayProtocolOptions(protocol: relayProtocol, data: relayData) - } - - public var absoluteString: String { - return "wc:\(topic)@\(version)?symKey=\(symKey)&\(relayQuery)" + self.apiType = uriUser.api } private var relayQuery: String { @@ -47,4 +65,21 @@ public struct WalletConnectURI: Equatable { } return query } + + private static func parseURIComponents(from string: String) -> URLComponents? { + guard string.hasPrefix("wc:") else { + return nil + } + let urlString = !string.hasPrefix("wc://") ? string.replacingOccurrences(of: "wc:", with: "wc://") : string + return URLComponents(string: urlString) + } + + private static func parseURIUser(from string: String) -> (topic: String, api: TargetAPI?) { + let splits = string.split(separator: "-") + if splits.count == 2, let apiFromPrefix = TargetAPI(rawValue: String(splits[0])) { + return (String(splits[1]), apiFromPrefix) + } else { + return (string, nil) + } + } } diff --git a/Tests/WalletConnectUtilsTests/WalletConnectURITests.swift b/Tests/WalletConnectUtilsTests/WalletConnectURITests.swift index 79ad2acaf..fc2f8c170 100644 --- a/Tests/WalletConnectUtilsTests/WalletConnectURITests.swift +++ b/Tests/WalletConnectUtilsTests/WalletConnectURITests.swift @@ -1,58 +1,70 @@ import XCTest @testable import WalletConnectUtils -final class WalletConnectURITests: XCTestCase { - - var stubURI: String! - - var stubTopic: String! - var stubSymKey: String! - let stubProtocol = "irn" +private func stubURI(api: WalletConnectURI.TargetAPI? = nil) -> (uri: WalletConnectURI, string: String) { + let topic = Data.randomBytes(count: 32).toHexString() + let symKey = Data.randomBytes(count: 32).toHexString() + let protocolName = "irn" + let uriBase = api == nil ? "wc:" : "wc:\(api!.rawValue)-" + let uriString = "\(uriBase)\(topic)@2?symKey=\(symKey)&relay-protocol=\(protocolName)" + let uri = WalletConnectURI( + topic: topic, + symKey: symKey, + relay: RelayProtocolOptions(protocol: protocolName, data: nil), + api: api) + return (uri, uriString) +} - override func setUp() { - let topic = Data.randomBytes(count: 32).toHexString() - let symKey = Data.randomBytes(count: 32).toHexString() - stubTopic = topic - stubSymKey = symKey - stubURI = "wc:\(topic)@2?symKey=\(symKey)&relay-protocol=\(stubProtocol)" - } +final class WalletConnectURITests: XCTestCase { - override func tearDown() { - stubURI = nil - stubTopic = nil - stubSymKey = nil - } + // MARK: - Init URI with string func testInitURIToString() { - let inputURI = WalletConnectURI( - topic: stubTopic, - symKey: stubSymKey, - relay: RelayProtocolOptions(protocol: stubProtocol, data: nil)) - let uriString = inputURI.absoluteString + let input = stubURI() + let uriString = input.uri.absoluteString let outputURI = WalletConnectURI(string: uriString) - XCTAssertEqual(inputURI, outputURI) - XCTAssertEqual(stubURI, outputURI?.absoluteString) + XCTAssertEqual(input.uri, outputURI) + XCTAssertEqual(input.string, outputURI?.absoluteString) } func testInitStringToURI() { - let inputURIString = stubURI! + let inputURIString = stubURI().string let uri = WalletConnectURI(string: inputURIString) let outputURIString = uri?.absoluteString XCTAssertEqual(inputURIString, outputURIString) } func testInitStringToURIAlternate() { - let expectedString = stubURI! + let expectedString = stubURI().string let inputURIString = expectedString.replacingOccurrences(of: "wc:", with: "wc://") let uri = WalletConnectURI(string: inputURIString) let outputURIString = uri?.absoluteString XCTAssertEqual(expectedString, outputURIString) } - // MARK: - Init failure cases + // MARK: - Init URI with prefix API identifier + + func testInitFromPrefixedURIString() { + WalletConnectURI.TargetAPI.allCases.forEach { api in + let uriString = stubURI(api: api).string + let uri = WalletConnectURI(string: uriString) + XCTAssertEqual(uri?.api, api) + XCTAssertEqual(uri?.absoluteString, uriString) + } + } + + func testAbsentPrefixFallbackToSign() { + let input = stubURI() + let uriFromParams = input.uri + let uriFromString = WalletConnectURI(string: input.string) + XCTAssertEqual(uriFromParams.api, .sign) + XCTAssertEqual(uriFromString?.api, .sign) + } + + // MARK: - Init URI failure cases func testInitFailsBadScheme() { - let inputURIString = stubURI.replacingOccurrences(of: "wc:", with: "") + let inputURIString = stubURI().string.replacingOccurrences(of: "wc:", with: "") let uri = WalletConnectURI(string: inputURIString) XCTAssertNil(uri) } @@ -64,14 +76,15 @@ final class WalletConnectURITests: XCTestCase { } func testInitFailsNoSymKeyParam() { - let symKey = stubSymKey! - let inputURIString = stubURI.replacingOccurrences(of: "symKey=\(symKey)", with: "") + let input = stubURI() + let inputURIString = input.string.replacingOccurrences(of: "symKey=\(input.uri.symKey)", with: "") let uri = WalletConnectURI(string: inputURIString) XCTAssertNil(uri) } func testInitFailsNoRelayParam() { - let inputURIString = stubURI.replacingOccurrences(of: "&relay-protocol=\(stubProtocol)", with: "") + let input = stubURI() + let inputURIString = input.string.replacingOccurrences(of: "&relay-protocol=\(input.uri.relay.protocol)", with: "") let uri = WalletConnectURI(string: inputURIString) XCTAssertNil(uri) } From 36adc0214233a6dda032824ebbb2bd79e4f3a00c Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 22 Aug 2022 09:51:27 +0200 Subject: [PATCH 83/89] savepoint --- Example/IntegrationTests/Auth/AuthTests.swift | 22 ++++++++++++++++++- .../Auth/Services/Signer/MessageSigner.swift | 8 +++---- Sources/Auth/Services/Signer/Signer.swift | 2 +- Sources/Auth/Types/Cacao/CacaoSignature.swift | 2 +- 4 files changed, 27 insertions(+), 7 deletions(-) diff --git a/Example/IntegrationTests/Auth/AuthTests.swift b/Example/IntegrationTests/Auth/AuthTests.swift index 05cb74088..0f3e1ff87 100644 --- a/Example/IntegrationTests/Auth/AuthTests.swift +++ b/Example/IntegrationTests/Auth/AuthTests.swift @@ -4,7 +4,7 @@ import WalletConnectUtils @testable import WalletConnectKMS import WalletConnectRelay import Combine -@testable import Auth +import Auth final class AuthTests: XCTestCase { var app: AuthClient! @@ -81,4 +81,24 @@ final class AuthTests: XCTestCase { .store(in: &publishers) wait(for: [responseExpectation], timeout: 2) } + + func testRespondInvalidSignature() async { + let responseExpectation = expectation(description: "invalid signature response delivered") + let uri = try! await app.request(RequestParams.stub()) + try! await wallet.pair(uri: uri) + wallet.authRequestPublisher.sink { [unowned self] (id, message) in + Task(priority: .high) { + let invalidSignature = "43effc459956b57fcd9f3dac6c675f9cee88abf21acab7305e8e32aa0303a883b06dcbd956279a7a2ca21ffa882ff55cc22e8ab8ec0f3fe90ab45f306938cfa1b" + let cacaoSignature = CacaoSignature(t: "eip191", s: invalidSignature) + try! await wallet.respond(.success(RespondParams(id: id, signature: cacaoSignature))) + } + } + .store(in: &publishers) + app.authResponsePublisher.sink { (id, result) in + guard case .success = result else { XCTFail(); return } + responseExpectation.fulfill() + } + .store(in: &publishers) + wait(for: [responseExpectation], timeout: 2) + } } diff --git a/Sources/Auth/Services/Signer/MessageSigner.swift b/Sources/Auth/Services/Signer/MessageSigner.swift index b9b8dda11..d843138e9 100644 --- a/Sources/Auth/Services/Signer/MessageSigner.swift +++ b/Sources/Auth/Services/Signer/MessageSigner.swift @@ -8,7 +8,7 @@ protocol MessageSigning { func sign(message: String, privateKey: Data) throws -> String } -struct MessageSigner: MessageSignatureVerifying, MessageSigning { +public struct MessageSigner: MessageSignatureVerifying, MessageSigning { enum Errors: Error { case signatureValidationFailed @@ -17,17 +17,17 @@ struct MessageSigner: MessageSignatureVerifying, MessageSigning { private let signer: Signer - init(signer: Signer) { + public init(signer: Signer) { self.signer = signer } - func sign(message: String, privateKey: Data) throws -> String { + public func sign(message: String, privateKey: Data) throws -> String { guard let messageData = message.data(using: .utf8) else { throw Errors.utf8EncodingFailed } let signature = try signer.sign(message: messageData, with: privateKey) return signature.toHexString() } - func verify(signature: String, message: String, address: String) throws { + public func verify(signature: String, message: String, address: String) throws { guard let messageData = message.data(using: .utf8) else { throw Errors.utf8EncodingFailed } let signatureData = Data(hex: signature) guard try signer.isValid(signature: signatureData, message: messageData, address: address) diff --git a/Sources/Auth/Services/Signer/Signer.swift b/Sources/Auth/Services/Signer/Signer.swift index a1ba7a6e6..33760312e 100644 --- a/Sources/Auth/Services/Signer/Signer.swift +++ b/Sources/Auth/Services/Signer/Signer.swift @@ -1,7 +1,7 @@ import Foundation import Web3 -struct Signer { +public struct Signer { typealias Signature = (v: UInt, r: [UInt8], s: [UInt8]) diff --git a/Sources/Auth/Types/Cacao/CacaoSignature.swift b/Sources/Auth/Types/Cacao/CacaoSignature.swift index 2c6c4c49e..83a6eeb91 100644 --- a/Sources/Auth/Types/Cacao/CacaoSignature.swift +++ b/Sources/Auth/Types/Cacao/CacaoSignature.swift @@ -1,6 +1,6 @@ import Foundation -struct CacaoSignature: Codable, Equatable { +public struct CacaoSignature: Codable, Equatable { let t: String let s: String let m: String? = nil From ffc14fbd979277622da1ac60c0ea511bb2406316 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 22 Aug 2022 13:35:45 +0200 Subject: [PATCH 84/89] update invalid signature test --- Example/IntegrationTests/Auth/AuthTests.swift | 9 +++++---- Sources/Auth/Services/Signer/MessageSigner.swift | 2 +- Sources/Auth/Services/Signer/Signer.swift | 2 ++ Sources/Auth/Types/Cacao/CacaoSignature.swift | 8 +++++++- Sources/Auth/Types/RespondParams.swift | 7 ++++++- 5 files changed, 21 insertions(+), 7 deletions(-) diff --git a/Example/IntegrationTests/Auth/AuthTests.swift b/Example/IntegrationTests/Auth/AuthTests.swift index 0f3e1ff87..566fc1761 100644 --- a/Example/IntegrationTests/Auth/AuthTests.swift +++ b/Example/IntegrationTests/Auth/AuthTests.swift @@ -4,7 +4,7 @@ import WalletConnectUtils @testable import WalletConnectKMS import WalletConnectRelay import Combine -import Auth +@testable import Auth final class AuthTests: XCTestCase { var app: AuthClient! @@ -68,7 +68,7 @@ final class AuthTests: XCTestCase { try! await wallet.pair(uri: uri) wallet.authRequestPublisher.sink { [unowned self] (id, message) in Task(priority: .high) { - let signature = try! MessageSigner(signer: Signer()).sign(message: message, privateKey: prvKey) + let signature = try! MessageSigner().sign(message: message, privateKey: prvKey) let cacaoSignature = CacaoSignature(t: "eip191", s: signature) try! await wallet.respond(.success(RespondParams(id: id, signature: cacaoSignature))) } @@ -88,14 +88,15 @@ final class AuthTests: XCTestCase { try! await wallet.pair(uri: uri) wallet.authRequestPublisher.sink { [unowned self] (id, message) in Task(priority: .high) { - let invalidSignature = "43effc459956b57fcd9f3dac6c675f9cee88abf21acab7305e8e32aa0303a883b06dcbd956279a7a2ca21ffa882ff55cc22e8ab8ec0f3fe90ab45f306938cfa1b" + let invalidSignature = "438effc459956b57fcd9f3dac6c675f9cee88abf21acab7305e8e32aa0303a883b06dcbd956279a7a2ca21ffa882ff55cc22e8ab8ec0f3fe90ab45f306938cfa1b" let cacaoSignature = CacaoSignature(t: "eip191", s: invalidSignature) try! await wallet.respond(.success(RespondParams(id: id, signature: cacaoSignature))) } } .store(in: &publishers) app.authResponsePublisher.sink { (id, result) in - guard case .success = result else { XCTFail(); return } + guard case .failure(let error) = result else { XCTFail(); return } + // TODO - complete after reason codes are merged responseExpectation.fulfill() } .store(in: &publishers) diff --git a/Sources/Auth/Services/Signer/MessageSigner.swift b/Sources/Auth/Services/Signer/MessageSigner.swift index d843138e9..d07972a5c 100644 --- a/Sources/Auth/Services/Signer/MessageSigner.swift +++ b/Sources/Auth/Services/Signer/MessageSigner.swift @@ -17,7 +17,7 @@ public struct MessageSigner: MessageSignatureVerifying, MessageSigning { private let signer: Signer - public init(signer: Signer) { + public init(signer: Signer = Signer()) { self.signer = signer } diff --git a/Sources/Auth/Services/Signer/Signer.swift b/Sources/Auth/Services/Signer/Signer.swift index 33760312e..3d5903296 100644 --- a/Sources/Auth/Services/Signer/Signer.swift +++ b/Sources/Auth/Services/Signer/Signer.swift @@ -5,6 +5,8 @@ public struct Signer { typealias Signature = (v: UInt, r: [UInt8], s: [UInt8]) + public init() {} + func sign(message: Data, with key: Data) throws -> Data { let prefixed = prefixed(message: message) let privateKey = try EthereumPrivateKey(privateKey: key.bytes) diff --git a/Sources/Auth/Types/Cacao/CacaoSignature.swift b/Sources/Auth/Types/Cacao/CacaoSignature.swift index 83a6eeb91..b34ee1a40 100644 --- a/Sources/Auth/Types/Cacao/CacaoSignature.swift +++ b/Sources/Auth/Types/Cacao/CacaoSignature.swift @@ -3,5 +3,11 @@ import Foundation public struct CacaoSignature: Codable, Equatable { let t: String let s: String - let m: String? = nil + let m: String? + + public init(t: String, s: String, m: String? = nil) { + self.t = t + self.s = s + self.m = m + } } diff --git a/Sources/Auth/Types/RespondParams.swift b/Sources/Auth/Types/RespondParams.swift index 79926445e..f7c4a4af8 100644 --- a/Sources/Auth/Types/RespondParams.swift +++ b/Sources/Auth/Types/RespondParams.swift @@ -1,6 +1,11 @@ import Foundation -public struct RespondParams { +public struct RespondParams: Equatable { let id: RPCID let signature: CacaoSignature + + public init(id: RPCID, signature: CacaoSignature) { + self.id = id + self.signature = signature + } } From 44519cb0dd18c190821c7778ba59f74f78484122 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Mon, 22 Aug 2022 17:40:29 +0300 Subject: [PATCH 85/89] 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 From 1741d5d3b1b9fe8d2f25a1ad073cdeb8de493ea3 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 23 Aug 2022 10:15:26 +0200 Subject: [PATCH 86/89] Add invalidation of background task --- .../AutomaticSocketConnectionHandler.swift | 1 + .../BackgroundTaskRegistering.swift | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/Sources/WalletConnectRelay/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift b/Sources/WalletConnectRelay/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift index 20eab6208..5a5cec5f6 100644 --- a/Sources/WalletConnectRelay/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift +++ b/Sources/WalletConnectRelay/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift @@ -36,6 +36,7 @@ class AutomaticSocketConnectionHandler: SocketConnectionHandler { } appStateObserver.onWillEnterForeground = { [unowned self] in + backgroundTaskRegistrar.invalidate() socket.connect() } } diff --git a/Sources/WalletConnectRelay/SocketConnectionHandler/BackgroundTaskRegistering.swift b/Sources/WalletConnectRelay/SocketConnectionHandler/BackgroundTaskRegistering.swift index 836283339..c98c5cd9f 100644 --- a/Sources/WalletConnectRelay/SocketConnectionHandler/BackgroundTaskRegistering.swift +++ b/Sources/WalletConnectRelay/SocketConnectionHandler/BackgroundTaskRegistering.swift @@ -5,6 +5,7 @@ import UIKit protocol BackgroundTaskRegistering { func register(name: String, completion: @escaping () -> Void) + func invalidate() } class BackgroundTaskRegistrar: BackgroundTaskRegistering { @@ -22,4 +23,11 @@ class BackgroundTaskRegistrar: BackgroundTaskRegistering { } #endif } + + func invalidate() { + if backgroundTaskID != .invalid { + UIApplication.shared.endBackgroundTask(backgroundTaskID) + backgroundTaskID = .invalid + } + } } From dc23b908227650926010efa332391b1ddad5a7e1 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 23 Aug 2022 10:20:01 +0200 Subject: [PATCH 87/89] fix build and tests --- .../SocketConnectionHandler/BackgroundTaskRegistering.swift | 2 ++ Tests/RelayerTests/Mocks/BackgroundTaskRegistrarMock.swift | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/Sources/WalletConnectRelay/SocketConnectionHandler/BackgroundTaskRegistering.swift b/Sources/WalletConnectRelay/SocketConnectionHandler/BackgroundTaskRegistering.swift index c98c5cd9f..7b08c94a6 100644 --- a/Sources/WalletConnectRelay/SocketConnectionHandler/BackgroundTaskRegistering.swift +++ b/Sources/WalletConnectRelay/SocketConnectionHandler/BackgroundTaskRegistering.swift @@ -25,9 +25,11 @@ class BackgroundTaskRegistrar: BackgroundTaskRegistering { } func invalidate() { +#if os(iOS) if backgroundTaskID != .invalid { UIApplication.shared.endBackgroundTask(backgroundTaskID) backgroundTaskID = .invalid } +#endif } } diff --git a/Tests/RelayerTests/Mocks/BackgroundTaskRegistrarMock.swift b/Tests/RelayerTests/Mocks/BackgroundTaskRegistrarMock.swift index d13eefefb..d8f895242 100644 --- a/Tests/RelayerTests/Mocks/BackgroundTaskRegistrarMock.swift +++ b/Tests/RelayerTests/Mocks/BackgroundTaskRegistrarMock.swift @@ -3,7 +3,12 @@ import Foundation class BackgroundTaskRegistrarMock: BackgroundTaskRegistering { var completion: (() -> Void)? + func register(name: String, completion: @escaping () -> Void) { self.completion = completion } + + func invalidate() { + + } } From 7d157356fe2ccea475cadad502df845f935adbf3 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 23 Aug 2022 13:22:46 +0200 Subject: [PATCH 88/89] savepoint --- Example/IntegrationTests/Auth/AuthTests.swift | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Example/IntegrationTests/Auth/AuthTests.swift b/Example/IntegrationTests/Auth/AuthTests.swift index 8e343268f..fd63458af 100644 --- a/Example/IntegrationTests/Auth/AuthTests.swift +++ b/Example/IntegrationTests/Auth/AuthTests.swift @@ -81,6 +81,24 @@ final class AuthTests: XCTestCase { wait(for: [responseExpectation], timeout: 5) } + func testUserRespondError() { + let responseExpectation = expectation(description: "error response delivered") + let uri = try! await app.request(RequestParams.stub()) + try! await wallet.pair(uri: uri) + wallet.authRequestPublisher.sink { [unowned self] (id, message) in + Task(priority: .high) { + try! await wallet.respond(requestId: id, result: .failure(Never()) + } + } + .store(in: &publishers) + app.authResponsePublisher.sink { (id, result) in + guard case .success = result else { XCTFail(); return } + responseExpectation.fulfill() + } + .store(in: &publishers) + wait(for: [responseExpectation], timeout: 5) + } + func testRespondSignatureVerificationFailed() async { let responseExpectation = expectation(description: "invalid signature response delivered") let uri = try! await app.request(RequestParams.stub()) From da0360649f796c89436ab8ab3d63db84b593dfa5 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Tue, 23 Aug 2022 14:34:20 +0200 Subject: [PATCH 89/89] handle error response add testUserRespondError --- Example/IntegrationTests/Auth/AuthTests.swift | 11 ++++++----- Sources/Auth/AuthClient.swift | 9 +++++++-- .../Services/App/AppRespondSubscriber.swift | 11 +++++++++-- .../Services/Wallet/WalletRespondService.swift | 13 ++----------- Sources/Auth/Types/Errors/AuthError.swift | 17 +++++++++++++++++ 5 files changed, 41 insertions(+), 20 deletions(-) diff --git a/Example/IntegrationTests/Auth/AuthTests.swift b/Example/IntegrationTests/Auth/AuthTests.swift index fd63458af..731d74b14 100644 --- a/Example/IntegrationTests/Auth/AuthTests.swift +++ b/Example/IntegrationTests/Auth/AuthTests.swift @@ -69,7 +69,7 @@ final class AuthTests: XCTestCase { Task(priority: .high) { let signature = try! MessageSigner().sign(message: message, privateKey: prvKey) let cacaoSignature = CacaoSignature(t: "eip191", s: signature) - try! await wallet.respond(requestId: id, result: .success(cacaoSignature)) + try! await wallet.respond(requestId: id, signature: cacaoSignature) } } .store(in: &publishers) @@ -81,18 +81,19 @@ final class AuthTests: XCTestCase { wait(for: [responseExpectation], timeout: 5) } - func testUserRespondError() { + func testUserRespondError() async { let responseExpectation = expectation(description: "error response delivered") let uri = try! await app.request(RequestParams.stub()) try! await wallet.pair(uri: uri) wallet.authRequestPublisher.sink { [unowned self] (id, message) in Task(priority: .high) { - try! await wallet.respond(requestId: id, result: .failure(Never()) + try! await wallet.reject(requestId: id) } } .store(in: &publishers) app.authResponsePublisher.sink { (id, result) in - guard case .success = result else { XCTFail(); return } + guard case .failure(let error) = result else { XCTFail(); return } + XCTAssertEqual(error, .userRejeted) responseExpectation.fulfill() } .store(in: &publishers) @@ -107,7 +108,7 @@ final class AuthTests: XCTestCase { Task(priority: .high) { let invalidSignature = "438effc459956b57fcd9f3dac6c675f9cee88abf21acab7305e8e32aa0303a883b06dcbd956279a7a2ca21ffa882ff55cc22e8ab8ec0f3fe90ab45f306938cfa1b" let cacaoSignature = CacaoSignature(t: "eip191", s: invalidSignature) - try! await wallet.respond(requestId: id, result: .success(cacaoSignature)) + try! await wallet.respond(requestId: id, signature: cacaoSignature) } } .store(in: &publishers) diff --git a/Sources/Auth/AuthClient.swift b/Sources/Auth/AuthClient.swift index ad4192861..158e54a2f 100644 --- a/Sources/Auth/AuthClient.swift +++ b/Sources/Auth/AuthClient.swift @@ -4,6 +4,7 @@ import WalletConnectUtils import WalletConnectPairing import WalletConnectRelay + public class AuthClient { enum Errors: Error { case malformedPairingURI @@ -87,9 +88,13 @@ 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, signature: CacaoSignature) async throws { guard let account = account else { throw Errors.unknownWalletAddress } - try await walletRespondService.respond(requestId: requestId, result: result, account: account) + try await walletRespondService.respond(requestId: requestId, signature: signature, account: account) + } + + public func reject(requestId: RPCID) async throws { + try await walletRespondService.respondError(requestId: requestId) } public func getPendingRequests() throws -> [AuthRequest] { diff --git a/Sources/Auth/Services/App/AppRespondSubscriber.swift b/Sources/Auth/Services/App/AppRespondSubscriber.swift index bc7e13c72..a73015b4c 100644 --- a/Sources/Auth/Services/App/AppRespondSubscriber.swift +++ b/Sources/Auth/Services/App/AppRespondSubscriber.swift @@ -28,16 +28,23 @@ class AppRespondSubscriber { private func subscribeForResponse() { networkingInteractor.responsePublisher.sink { [unowned self] subscriptionPayload in + let response = subscriptionPayload.response guard - let requestId = subscriptionPayload.response.id, + let requestId = response.id, let request = rpcHistory.get(recordId: requestId)?.request, let requestParams = request.params, request.method == "wc_authRequest" else { return } networkingInteractor.unsubscribe(topic: subscriptionPayload.topic) + if let errorResponse = response.error, + let error = AuthError(code: errorResponse.code) { + onResponse?(requestId, .failure(error)) + return + } + guard - let cacao = try? subscriptionPayload.response.result?.get(Cacao.self), + let cacao = try? 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 } diff --git a/Sources/Auth/Services/Wallet/WalletRespondService.swift b/Sources/Auth/Services/Wallet/WalletRespondService.swift index 0aba57f14..07e01871c 100644 --- a/Sources/Auth/Services/Wallet/WalletRespondService.swift +++ b/Sources/Auth/Services/Wallet/WalletRespondService.swift @@ -23,16 +23,7 @@ actor WalletRespondService { self.rpcHistory = rpcHistory } - 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: - try await respondError(requestId: requestId) - } - } - - private func respond(requestId: RPCID, signature: CacaoSignature, account: Account) async throws { + func respond(requestId: RPCID, signature: CacaoSignature, account: Account) async throws { let authRequestParams = try getAuthRequestParams(requestId: requestId) let (topic, keys) = try generateAgreementKeys(requestParams: authRequestParams) @@ -44,7 +35,7 @@ actor WalletRespondService { try await networkingInteractor.respond(topic: topic, response: response, tag: AuthResponseParams.tag, envelopeType: .type1(pubKey: keys.publicKey.rawRepresentation)) } - private func respondError(requestId: RPCID) async throws { + func respondError(requestId: RPCID) async throws { let authRequestParams = try getAuthRequestParams(requestId: requestId) let (topic, keys) = try generateAgreementKeys(requestParams: authRequestParams) diff --git a/Sources/Auth/Types/Errors/AuthError.swift b/Sources/Auth/Types/Errors/AuthError.swift index 2b83d6182..f5b52b21a 100644 --- a/Sources/Auth/Types/Errors/AuthError.swift +++ b/Sources/Auth/Types/Errors/AuthError.swift @@ -10,6 +10,23 @@ public enum AuthError: Codable, Equatable, Error { extension AuthError: Reason { + init?(code: Int) { + switch code { + case Self.userRejeted.code: + self = .userRejeted + case Self.malformedResponseParams.code: + self = .malformedResponseParams + case Self.malformedRequestParams.code: + self = .malformedRequestParams + case Self.messageCompromised.code: + self = .messageCompromised + case Self.signatureVerificationFailed.code: + self = .signatureVerificationFailed + default: + return nil + } + } + public var code: Int { switch self { case .userRejeted: