Skip to content

Commit

Permalink
Merge pull request #1040 from WalletConnect/present-request-on-repairing
Browse files Browse the repository at this point in the history
[Pair] Present request when reparing
  • Loading branch information
alexander-lsvk authored Sep 5, 2023
2 parents 49725d0 + c8c2065 commit e4556ef
Show file tree
Hide file tree
Showing 18 changed files with 146 additions and 58 deletions.
12 changes: 6 additions & 6 deletions Example/IntegrationTests/Auth/AuthTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ final class AuthTests: XCTestCase {
let uri = try! await appPairingClient.create()
try! await appAuthClient.request(RequestParams.stub(), topic: uri.topic)

try! await walletPairingClient.pair(uri: uri)
try? await walletPairingClient.pair(uri: uri)
walletAuthClient.authRequestPublisher.sink { _ in
requestExpectation.fulfill()
}.store(in: &publishers)
Expand All @@ -88,7 +88,7 @@ final class AuthTests: XCTestCase {
let uri = try! await appPairingClient.create()
try! await appAuthClient.request(RequestParams.stub(), topic: uri.topic)

try! await walletPairingClient.pair(uri: uri)
try? await walletPairingClient.pair(uri: uri)
walletAuthClient.authRequestPublisher.sink { [unowned self] request in
Task(priority: .high) {
let signerFactory = DefaultSignerFactory()
Expand Down Expand Up @@ -126,7 +126,7 @@ final class AuthTests: XCTestCase {
resources: nil
), topic: uri.topic)

try! await walletPairingClient.pair(uri: uri)
try? await walletPairingClient.pair(uri: uri)
walletAuthClient.authRequestPublisher.sink { [unowned self] request in
Task(priority: .high) {
let signature = CacaoSignature(t: .eip1271, s: eip1271Signature)
Expand All @@ -147,7 +147,7 @@ final class AuthTests: XCTestCase {
let uri = try! await appPairingClient.create()
try! await appAuthClient.request(RequestParams.stub(), topic: uri.topic)

try! await walletPairingClient.pair(uri: uri)
try? await walletPairingClient.pair(uri: uri)
walletAuthClient.authRequestPublisher.sink { [unowned self] request in
Task(priority: .high) {
let signature = CacaoSignature(t: .eip1271, s: eip1271Signature)
Expand All @@ -168,7 +168,7 @@ final class AuthTests: XCTestCase {
let uri = try! await appPairingClient.create()
try! await appAuthClient.request(RequestParams.stub(), topic: uri.topic)

try! await walletPairingClient.pair(uri: uri)
try? await walletPairingClient.pair(uri: uri)
walletAuthClient.authRequestPublisher.sink { [unowned self] request in
Task(priority: .high) {
try! await walletAuthClient.reject(requestId: request.0.id)
Expand All @@ -189,7 +189,7 @@ final class AuthTests: XCTestCase {
let uri = try! await appPairingClient.create()
try! await appAuthClient.request(RequestParams.stub(), topic: uri.topic)

try! await walletPairingClient.pair(uri: uri)
try? await walletPairingClient.pair(uri: uri)
walletAuthClient.authRequestPublisher.sink { [unowned self] request in
Task(priority: .high) {
let invalidSignature = "438effc459956b57fcd9f3dac6c675f9cee88abf21acab7305e8e32aa0303a883b06dcbd956279a7a2ca21ffa882ff55cc22e8ab8ec0f3fe90ab45f306938cfa1b"
Expand Down
4 changes: 2 additions & 2 deletions Example/IntegrationTests/Pairing/PairingTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ final class PairingTests: XCTestCase {
let expectation = expectation(description: "expects ping response")
makeWalletClients()
let uri = try! await appPairingClient.create()
try! await walletPairingClient.pair(uri: uri)
try? await walletPairingClient.pair(uri: uri)
try! await walletPairingClient.ping(topic: uri.topic)
walletPairingClient.pingResponsePublisher
.sink { topic in
Expand All @@ -131,7 +131,7 @@ final class PairingTests: XCTestCase {

let uri = try! await appPairingClient.create()

try! await walletPairingClient.pair(uri: uri)
try? await walletPairingClient.pair(uri: uri)

try! await appAuthClient.request(RequestParams.stub(), topic: uri.topic)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,14 @@ final class SessionProposalInteractor {
let supportedChains = [Blockchain("eip155:1")!, Blockchain("eip155:137")!]
let supportedAccounts = [Account(blockchain: Blockchain("eip155:1")!, address: ETHSigner.address)!, Account(blockchain: Blockchain("eip155:137")!, address: ETHSigner.address)!]
*/
do {
let sessionNamespaces = try AutoNamespaces.build(
sessionProposal: proposal,
chains: Array(supportedChains),
methods: Array(supportedMethods),
events: Array(supportedEvents),
accounts: supportedAccounts
)
try await Web3Wallet.instance.approve(proposalId: proposal.id, namespaces: sessionNamespaces, sessionProperties: proposal.sessionProperties)
} catch {
print(error)
}
let sessionNamespaces = try AutoNamespaces.build(
sessionProposal: proposal,
chains: Array(supportedChains),
methods: Array(supportedMethods),
events: Array(supportedEvents),
accounts: supportedAccounts
)
try await Web3Wallet.instance.approve(proposalId: proposal.id, namespaces: sessionNamespaces, sessionProperties: proposal.sessionProperties)
}

func reject(proposal: Session.Proposal) async throws {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ final class SessionProposalPresenter: ObservableObject {
let sessionProposal: Session.Proposal
let verified: Bool?

@Published var showError = false
@Published var errorMessage = "Error"

private var disposeBag = Set<AnyCancellable>()

init(
Expand All @@ -30,14 +33,24 @@ final class SessionProposalPresenter: ObservableObject {

@MainActor
func onApprove() async throws {
try await interactor.approve(proposal: sessionProposal, account: importAccount.account)
router.dismiss()
do {
try await interactor.approve(proposal: sessionProposal, account: importAccount.account)
router.dismiss()
} catch {
errorMessage = error.localizedDescription
showError.toggle()
}
}

@MainActor
func onReject() async throws {
try await interactor.reject(proposal: sessionProposal)
router.dismiss()
do {
try await interactor.reject(proposal: sessionProposal)
router.dismiss()
} catch {
errorMessage = error.localizedDescription
showError.toggle()
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,9 @@ struct SessionProposalView: View {
Spacer()
}
}
.alert(presenter.errorMessage, isPresented: $presenter.showError) {
Button("OK", role: .cancel) {}
}
.edgesIgnoringSafeArea(.all)
}
//private func sessionProposalView(chain: String) -> some View {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion Sources/Auth/AuthClientFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
5 changes: 1 addition & 4 deletions Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,7 @@ class WalletRequestSubscriber {
.sink { [unowned self] (payload: RequestSubscriptionPayload<AuthRequestParams>) 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)

Expand Down
25 changes: 18 additions & 7 deletions Sources/Auth/Services/Wallet/WalletRespondService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,24 @@ actor WalletRespondService {
private let verifyContextStore: CodableStore<VerifyContext>
private let logger: ConsoleLogging
private let walletErrorResponder: WalletErrorResponder
private let pairingRegisterer: PairingRegisterer

init(networkingInteractor: NetworkInteracting,
logger: ConsoleLogging,
kms: KeyManagementService,
rpcHistory: RPCHistory,
verifyContextStore: CodableStore<VerifyContext>,
walletErrorResponder: WalletErrorResponder) {
init(
networkingInteractor: NetworkInteracting,
logger: ConsoleLogging,
kms: KeyManagementService,
rpcHistory: RPCHistory,
verifyContextStore: CodableStore<VerifyContext>,
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 {
Expand All @@ -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)
}

Expand Down
3 changes: 2 additions & 1 deletion Sources/WalletConnectNetworking/NetworkInteracting.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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<Request: Codable>(
on request: ProtocolMethod
) -> AnyPublisher<RequestSubscriptionPayload<Request>, Never>
Expand Down
4 changes: 4 additions & 0 deletions Sources/WalletConnectNetworking/NetworkingInteractor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,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 {
Expand Down
3 changes: 2 additions & 1 deletion Sources/WalletConnectPairing/PairingClientFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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<WCPairing>(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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
}
Expand All @@ -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"
}
}
Expand Down
35 changes: 35 additions & 0 deletions Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ final class ApproveEngine {
case pairingNotFound
case sessionNotFound
case agreementMissingOrInvalid
case networkNotConnected
}

var onSessionProposal: ((Session.Proposal, VerifyContext?) -> Void)?
Expand Down Expand Up @@ -63,6 +64,11 @@ final class ApproveEngine {
guard let payload = try proposalPayloadsStore.get(key: proposerPubKey) else {
throw Errors.wrongRequestParams
}

let networkConnectionStatus = await resolveNetworkConnectionStatus()
guard networkConnectionStatus == .connected else {
throw Errors.networkNotConnected
}

let proposal = payload.request
let pairingTopic = payload.topic
Expand Down Expand Up @@ -300,6 +306,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 {
Expand Down Expand Up @@ -373,4 +384,28 @@ private extension ApproveEngine {
}
onSessionSettle?(session.publicRepresentation())
}

func resolveNetworkConnectionStatus() async -> NetworkConnectionStatus {
return await withCheckedContinuation { continuation in
let cancellable = networkingInteractor.networkConnectionStatusPublisher.sink { value in
continuation.resume(returning: value)
}

Task(priority: .high) {
await withTaskCancellationHandler {
cancellable.cancel()
} onCancel: { }
}
}
}
}

// MARK: - LocalizedError
extension ApproveEngine.Errors: LocalizedError {
var errorDescription: String? {
switch self {
case .networkNotConnected: return "Action failed. You seem to be offline"
default: return ""
}
}
}
2 changes: 1 addition & 1 deletion Tests/AuthTests/WalletRequestSubscriberTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ class WalletRequestSubscriberTests: XCTestCase {
pairingRegisterer.subject.send(payload)

wait(for: [messageExpectation], timeout: defaultTimeout)
XCTAssertTrue(pairingRegisterer.isActivateCalled)
XCTAssertTrue(pairingRegisterer.isReceivedCalled)
XCTAssertEqual(requestPayload, expectedPayload)
XCTAssertEqual(requestId, expectedRequestId)
}
Expand Down
Loading

0 comments on commit e4556ef

Please sign in to comment.