From ef75976172db02e43a9102e917595b03aa1a65d7 Mon Sep 17 00:00:00 2001 From: Alexander Lisovik Date: Wed, 16 Aug 2023 17:15:46 +0400 Subject: [PATCH] Present request for pending requests on pair --- .../Wallet/Wallet/WalletPresenter.swift | 7 +--- Sources/Auth/AuthClientFactory.swift | 2 +- .../Wallet/WalletRequestSubscriber.swift | 5 +-- .../Wallet/WalletRespondService.swift | 25 +++++++++---- .../NetworkInteracting.swift | 3 +- .../NetworkingInteractor.swift | 4 ++ .../PairingClientFactory.swift | 3 +- .../Services/Wallet/WalletPairService.swift | 37 ++++++++++++++----- .../Engine/Common/ApproveEngine.swift | 5 +++ 9 files changed, 62 insertions(+), 29 deletions(-) diff --git a/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletPresenter.swift b/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletPresenter.swift index 917d1dc94..c8a5baee1 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletPresenter.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletPresenter.swift @@ -40,12 +40,7 @@ final class WalletPresenter: ObservableObject { func onAppear() { showPairingLoading = app.requestSent removePairingIndicator() - - let proposals = interactor.getPendingProposals() - if let proposal = proposals.last { - router.present(sessionProposal: proposal.proposal, importAccount: importAccount, sessionContext: proposal.context) - } - + let pendingRequests = interactor.getPendingRequests() if let request = pendingRequests.first(where: { $0.context != nil }) { router.present(sessionRequest: request.request, importAccount: importAccount, sessionContext: request.context) diff --git a/Sources/Auth/AuthClientFactory.swift b/Sources/Auth/AuthClientFactory.swift index 1faf925ff..dbddf2a53 100644 --- a/Sources/Auth/AuthClientFactory.swift +++ b/Sources/Auth/AuthClientFactory.swift @@ -48,7 +48,7 @@ public struct AuthClientFactory { let appRespondSubscriber = AppRespondSubscriber(networkingInteractor: networkingClient, logger: logger, rpcHistory: history, signatureVerifier: signatureVerifier, pairingRegisterer: pairingRegisterer, messageFormatter: messageFormatter) let walletErrorResponder = WalletErrorResponder(networkingInteractor: networkingClient, logger: logger, kms: kms, rpcHistory: history) let walletRequestSubscriber = WalletRequestSubscriber(networkingInteractor: networkingClient, logger: logger, kms: kms, walletErrorResponder: walletErrorResponder, pairingRegisterer: pairingRegisterer, verifyClient: verifyClient, verifyContextStore: verifyContextStore) - let walletRespondService = WalletRespondService(networkingInteractor: networkingClient, logger: logger, kms: kms, rpcHistory: history, verifyContextStore: verifyContextStore, walletErrorResponder: walletErrorResponder) + let walletRespondService = WalletRespondService(networkingInteractor: networkingClient, logger: logger, kms: kms, rpcHistory: history, verifyContextStore: verifyContextStore, walletErrorResponder: walletErrorResponder, pairingRegisterer: pairingRegisterer) let pendingRequestsProvider = PendingRequestsProvider(rpcHistory: history, verifyContextStore: verifyContextStore) return AuthClient( diff --git a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift index 02ee542d9..c2a7030c0 100644 --- a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift +++ b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift @@ -37,10 +37,7 @@ class WalletRequestSubscriber { .sink { [unowned self] (payload: RequestSubscriptionPayload) in logger.debug("WalletRequestSubscriber: Received request") - pairingRegisterer.activate( - pairingTopic: payload.topic, - peerMetadata: payload.request.requester.metadata - ) + pairingRegisterer.setReceived(pairingTopic: payload.topic) let request = AuthRequest(id: payload.id, topic: payload.topic, payload: payload.request.payloadParams) diff --git a/Sources/Auth/Services/Wallet/WalletRespondService.swift b/Sources/Auth/Services/Wallet/WalletRespondService.swift index 8d5e78b0c..a06a16027 100644 --- a/Sources/Auth/Services/Wallet/WalletRespondService.swift +++ b/Sources/Auth/Services/Wallet/WalletRespondService.swift @@ -11,19 +11,24 @@ actor WalletRespondService { private let verifyContextStore: CodableStore private let logger: ConsoleLogging private let walletErrorResponder: WalletErrorResponder + private let pairingRegisterer: PairingRegisterer - init(networkingInteractor: NetworkInteracting, - logger: ConsoleLogging, - kms: KeyManagementService, - rpcHistory: RPCHistory, - verifyContextStore: CodableStore, - walletErrorResponder: WalletErrorResponder) { + init( + networkingInteractor: NetworkInteracting, + logger: ConsoleLogging, + kms: KeyManagementService, + rpcHistory: RPCHistory, + verifyContextStore: CodableStore, + walletErrorResponder: WalletErrorResponder, + pairingRegisterer: PairingRegisterer + ) { self.networkingInteractor = networkingInteractor self.logger = logger self.kms = kms self.rpcHistory = rpcHistory self.verifyContextStore = verifyContextStore self.walletErrorResponder = walletErrorResponder + self.pairingRegisterer = pairingRegisterer } func respond(requestId: RPCID, signature: CacaoSignature, account: Account) async throws { @@ -34,10 +39,16 @@ actor WalletRespondService { let header = CacaoHeader(t: "eip4361") let payload = try authRequestParams.payloadParams.cacaoPayload(address: account.address) - let responseParams = AuthResponseParams(h: header, p: payload, s: signature) + let responseParams = AuthResponseParams(h: header, p: payload, s: signature) let response = RPCResponse(id: requestId, result: responseParams) try await networkingInteractor.respond(topic: topic, response: response, protocolMethod: AuthRequestProtocolMethod(), envelopeType: .type1(pubKey: keys.publicKey.rawRepresentation)) + + pairingRegisterer.activate( + pairingTopic: topic, + peerMetadata: authRequestParams.requester.metadata + ) + verifyContextStore.delete(forKey: requestId.string) } diff --git a/Sources/WalletConnectNetworking/NetworkInteracting.swift b/Sources/WalletConnectNetworking/NetworkInteracting.swift index 507e82603..54987272c 100644 --- a/Sources/WalletConnectNetworking/NetworkInteracting.swift +++ b/Sources/WalletConnectNetworking/NetworkInteracting.swift @@ -13,7 +13,8 @@ public protocol NetworkInteracting { func respond(topic: String, response: RPCResponse, protocolMethod: ProtocolMethod, envelopeType: Envelope.EnvelopeType) async throws func respondSuccess(topic: String, requestId: RPCID, protocolMethod: ProtocolMethod, envelopeType: Envelope.EnvelopeType) async throws func respondError(topic: String, requestId: RPCID, protocolMethod: ProtocolMethod, reason: Reason, envelopeType: Envelope.EnvelopeType) async throws - + func handleHistoryRequest(topic: String, request: RPCRequest) + func requestSubscription( on request: ProtocolMethod ) -> AnyPublisher, Never> diff --git a/Sources/WalletConnectNetworking/NetworkingInteractor.swift b/Sources/WalletConnectNetworking/NetworkingInteractor.swift index 76135c8fa..41a1aabe3 100644 --- a/Sources/WalletConnectNetworking/NetworkingInteractor.swift +++ b/Sources/WalletConnectNetworking/NetworkingInteractor.swift @@ -152,6 +152,10 @@ public class NetworkingInteractor: NetworkInteracting { logger.debug("Networking Interactor - Received unknown object type from networking relay") } } + + public func handleHistoryRequest(topic: String, request: RPCRequest) { + requestPublisherSubject.send((topic, request, Data(), Date(), nil)) + } private func handleRequest(topic: String, request: RPCRequest, decryptedPayload: Data, publishedAt: Date, derivedTopic: String?) { do { diff --git a/Sources/WalletConnectPairing/PairingClientFactory.swift b/Sources/WalletConnectPairing/PairingClientFactory.swift index 9706702af..ba49bb6af 100644 --- a/Sources/WalletConnectPairing/PairingClientFactory.swift +++ b/Sources/WalletConnectPairing/PairingClientFactory.swift @@ -12,8 +12,9 @@ public struct PairingClientFactory { public static func create(logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, networkingClient: NetworkingInteractor) -> PairingClient { let pairingStore = PairingStorage(storage: SequenceStore(store: .init(defaults: keyValueStorage, identifier: PairStorageIdentifiers.pairings.rawValue))) let kms = KeyManagementService(keychain: keychainStorage) + let history = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage) let appPairService = AppPairService(networkingInteractor: networkingClient, kms: kms, pairingStorage: pairingStore) - let walletPairService = WalletPairService(networkingInteractor: networkingClient, kms: kms, pairingStorage: pairingStore) + let walletPairService = WalletPairService(networkingInteractor: networkingClient, kms: kms, pairingStorage: pairingStore, history: history) let pairingRequestsSubscriber = PairingRequestsSubscriber(networkingInteractor: networkingClient, pairingStorage: pairingStore, logger: logger) let pairingsProvider = PairingsProvider(pairingStorage: pairingStore) let cleanupService = PairingCleanupService(pairingStore: pairingStore, kms: kms) diff --git a/Sources/WalletConnectPairing/Services/Wallet/WalletPairService.swift b/Sources/WalletConnectPairing/Services/Wallet/WalletPairService.swift index d14d82e3c..c962b8a41 100644 --- a/Sources/WalletConnectPairing/Services/Wallet/WalletPairService.swift +++ b/Sources/WalletConnectPairing/Services/Wallet/WalletPairService.swift @@ -9,18 +9,23 @@ actor WalletPairService { let networkingInteractor: NetworkInteracting let kms: KeyManagementServiceProtocol private let pairingStorage: WCPairingStorage + private let history: RPCHistory - init(networkingInteractor: NetworkInteracting, - kms: KeyManagementServiceProtocol, - pairingStorage: WCPairingStorage) { + init( + networkingInteractor: NetworkInteracting, + kms: KeyManagementServiceProtocol, + pairingStorage: WCPairingStorage, + history: RPCHistory + ) { self.networkingInteractor = networkingInteractor self.kms = kms self.pairingStorage = pairingStorage + self.history = history } func pair(_ uri: WalletConnectURI) async throws { - guard !hasPairing(for: uri.topic) else { - throw Errors.pairingAlreadyExist(topic: uri.topic) + guard try !pairingHasPendingRequest(for: uri.topic) else { + return } let pairing = WCPairing(uri: uri) @@ -39,9 +44,23 @@ actor WalletPairService { // MARK: - Private functions extension WalletPairService { - func hasPairing(for topic: String) -> Bool { - if let pairing = pairingStorage.getPairing(forTopic: topic) { - return pairing.requestReceived + func pairingHasPendingRequest(for topic: String) throws -> Bool { + guard let pairing = pairingStorage.getPairing(forTopic: topic), pairing.requestReceived else { + return false + } + + if pairing.active { + throw Errors.pairingAlreadyExist(topic: topic) + } + + let pendingRequests = history.getPending() + .compactMap { record -> RPCRequest? in + (record.topic == pairing.topic) ? record.request : nil + } + + if let pendingRequest = pendingRequests.first { + networkingInteractor.handleHistoryRequest(topic: topic, request: pendingRequest) + return true } return false } @@ -65,7 +84,7 @@ extension WalletPairService { extension WalletPairService.Errors: LocalizedError { var errorDescription: String? { switch self { - case .pairingAlreadyExist(let topic): return "Pairing with topic (\(topic)) already exists. Use 'Web3Wallet.instance.getPendingProposals()' or 'Web3Wallet.instance.getPendingRequests()' in order to receive proposals or requests." + case .pairingAlreadyExist(let topic): return "Pairing with topic (\(topic)) is already active" case .networkNotConnected: return "Pairing failed. You seem to be offline" } } diff --git a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift index 3071e12e6..402a9144b 100644 --- a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift @@ -300,6 +300,11 @@ private extension ApproveEngine { pairingRegisterer.setReceived(pairingTopic: payload.topic) + if let verifyContext = try? verifyContextStore.get(key: proposal.proposer.publicKey) { + onSessionProposal?(proposal.publicRepresentation(pairingTopic: payload.topic), verifyContext) + return + } + Task(priority: .high) { let assertionId = payload.decryptedPayload.sha256().toHexString() do {