Skip to content

Commit

Permalink
Merge pull request #206 from WalletConnect/async-pair
Browse files Browse the repository at this point in the history
Add Concurrency
  • Loading branch information
André Vants committed May 12, 2022
2 parents 3fd3b4b + 2c84694 commit 6f9397e
Show file tree
Hide file tree
Showing 22 changed files with 253 additions and 199 deletions.
7 changes: 3 additions & 4 deletions Example/DApp/Connect/ConnectViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,9 @@ class ConnectViewController: UIViewController, UITableViewDataSource, UITableVie
let blockchains: Set<Blockchain> = [Blockchain("eip155:1")!, Blockchain("eip155:137")!]
let methods: Set<String> = ["eth_sendTransaction", "personal_sign", "eth_signTypedData"]
let namespaces: Set<Namespace> = [Namespace(chains: blockchains, methods: methods, events: [])]
DispatchQueue.global().async {
ClientDelegate.shared.client.connect(namespaces: namespaces, topic: pairingTopic) { [weak self] _ in
self?.connectWithExampleWallet()
}
Task {
_ = try await ClientDelegate.shared.client.connect(namespaces: namespaces, topic: pairingTopic)
connectWithExampleWallet()
}
}
}
12 changes: 3 additions & 9 deletions Example/DApp/SelectChain/SelectChainViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,9 @@ class SelectChainViewController: UIViewController, UITableViewDataSource {
let methods: Set<String> = ["eth_sendTransaction", "personal_sign", "eth_signTypedData"]
let blockchains: Set<Blockchain> = [Blockchain("eip155:1")!, Blockchain("eip155:137")!]
let namespaces: Set<Namespace> = [Namespace(chains: blockchains, methods: methods, events: [])]
DispatchQueue.global().async { [weak self] in
self?.client.connect(namespaces: namespaces) { result in
switch result {
case .success(let uri):
self?.showConnectScreen(uriString: uri!)
case .failure(let error):
print("[PROPOSER] Pairing connect error: \(error)")
}
}
Task {
let uri = try await client.connect(namespaces: namespaces)
showConnectScreen(uriString: uri!)
}
}

Expand Down
12 changes: 10 additions & 2 deletions Example/ExampleApp/SceneDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,16 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
return
}
let wcUri = incomingURL.absoluteString.deletingPrefix("https://walletconnect.com/wc?uri=")
let client = ((window!.rootViewController as! UINavigationController).viewControllers[0] as! WalletViewController).client
try? client.pair(uri: wcUri)
let vc = ((window!.rootViewController as! UINavigationController).viewControllers[0] as! WalletViewController)
vc.onClientConnected = {
Task {
do {
try await vc.client.pair(uri: wcUri)
} catch {
print(error)
}
}
}
}
}

Expand Down
14 changes: 9 additions & 5 deletions Example/ExampleApp/Wallet/WalletViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ final class WalletViewController: UIViewController {
lazy var account = Signer.privateKey.address.hex(eip55: true)
var sessionItems: [ActiveSessionItem] = []
var currentProposal: Session.Proposal?
var onClientConnected: (()->())?

private let walletView: WalletView = {
WalletView()
Expand Down Expand Up @@ -94,10 +95,12 @@ final class WalletViewController: UIViewController {

private func pairClient(uri: String) {
print("[RESPONDER] Pairing to: \(uri)")
do {
try client.pair(uri: uri)
} catch {
print("[PROPOSER] Pairing connect error: \(error)")
Task {
do {
try await client.pair(uri: uri)
} catch {
print("[PROPOSER] Pairing connect error: \(error)")
}
}
}
}
Expand Down Expand Up @@ -152,7 +155,7 @@ extension WalletViewController: ScannerViewControllerDelegate {
}

extension WalletViewController: ProposalViewControllerDelegate {

func didApproveSession() {
print("[RESPONDER] Approving session...")
let proposal = currentProposal!
Expand All @@ -171,6 +174,7 @@ extension WalletViewController: ProposalViewControllerDelegate {

extension WalletViewController: WalletConnectClientDelegate {
func didConnect() {
onClientConnected?()
print("Client connected")
}

Expand Down
18 changes: 15 additions & 3 deletions Sources/Relayer/Relayer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,8 @@ public final class Relayer {
return request.id
}

@discardableResult public func subscribe(topic: String, completion: @escaping (Error?) -> ()) -> Int64 {
@available(*, renamed: "subscribe(topic:)")
public func subscribe(topic: String, completion: @escaping (Error?) -> ()) {
logger.debug("waku: Subscribing on Topic: \(topic)")
let params = RelayJSONRPC.SubscribeParams(topic: topic)
let request = JSONRPCRequest(method: RelayJSONRPC.Method.subscribe.rawValue, params: params)
Expand All @@ -154,11 +155,22 @@ public final class Relayer {
self?.concurrentQueue.async(flags: .barrier) {
self?.subscriptions[topic] = subscriptionResponse.result
}
completion(nil)
}
return request.id
}

public func subscribe(topic: String) async throws {
return try await withCheckedThrowingContinuation { continuation in
subscribe(topic: topic) { error in
if let error = error {
continuation.resume(throwing: error)
return
}
continuation.resume(returning: ())
}
}
}


@discardableResult public func unsubscribe(topic: String, completion: @escaping ((Error?) -> ())) -> Int64? {
guard let subscriptionId = subscriptions[topic] else {
completion(RelyerError.subscriptionIdNotFound)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,23 +59,19 @@ final class PairingEngine {
.map {Pairing(topic: $0.topic, peer: $0.peerMetadata, expiryDate: $0.expiryDate)}
}

func create() -> WalletConnectURI? {
func create() async throws -> WalletConnectURI {
let topic = topicInitializer()
try await networkingInteractor.subscribe(topic: topic)
let symKey = try! kms.createSymmetricKey(topic)
let pairing = WCPairing(topic: topic)
let uri = WalletConnectURI(topic: topic, symKey: symKey.hexRepresentation, relay: pairing.relay)
pairingStore.setPairing(pairing)
networkingInteractor.subscribe(topic: topic)
return uri
}
func propose(pairingTopic: String, namespaces: Set<Namespace>, relay: RelayProtocolOptions, completion: @escaping ((Error?) -> ())) {

func propose(pairingTopic: String, namespaces: Set<Namespace>, relay: RelayProtocolOptions) async throws {
logger.debug("Propose Session on topic: \(pairingTopic)")
do {
try Namespace.validate(namespaces)
} catch {
completion(error)
return
}
try Namespace.validate(namespaces)
let publicKey = try! kms.createX25519KeyPair()
let proposer = Participant(
publicKey: publicKey.hexRepresentation,
Expand All @@ -84,24 +80,17 @@ final class PairingEngine {
relays: [relay],
proposer: proposer,
namespaces: namespaces)
networkingInteractor.requestNetworkAck(.wcSessionPropose(proposal), onTopic: pairingTopic) { [unowned self] error in
logger.debug("Received propose acknowledgement from network")
completion(error)
}
}

func pair(_ uri: WalletConnectURI) throws {
guard !hasPairing(for: uri.topic) else {
throw WalletConnectError.pairingAlreadyExist
return try await withCheckedThrowingContinuation { continuation in
networkingInteractor.requestNetworkAck(.wcSessionPropose(proposal), onTopic: pairingTopic) { error in
if let error = error {
continuation.resume(throwing: error)
} else {
continuation.resume()
}
}
}
var pairing = WCPairing(uri: uri)
let symKey = try! SymmetricKey(hex: uri.symKey) // FIXME: Malformed QR code from external source can crash the SDK
try! kms.setSymmetricKey(symKey, for: pairing.topic)
pairing.activate()
networkingInteractor.subscribe(topic: pairing.topic)
pairingStore.setPairing(pairing)
}

func ping(topic: String, completion: @escaping ((Result<Void, Error>) -> ())) {
guard pairingStore.hasPairing(forTopic: topic) else {
logger.debug("Could not find pairing to ping for topic \(topic)")
Expand Down Expand Up @@ -186,7 +175,7 @@ final class PairingEngine {
.sink { [unowned self] (_) in
let topics = pairingStore.getAll()
.map{$0.topic}
topics.forEach{networkingInteractor.subscribe(topic: $0)}
topics.forEach{ topic in Task{try? await networkingInteractor.subscribe(topic: topic)}}
}.store(in: &publishers)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ final class SessionEngine {
}

func setSubscription(topic: String) {
networkingInteractor.subscribe(topic: topic)
Task { try? await networkingInteractor.subscribe(topic: topic) }
}

func hasSession(for topic: String) -> Bool {
Expand Down Expand Up @@ -107,21 +107,16 @@ final class SessionEngine {
}
}

func emit(topic: String, event: SessionType.EventParams.Event, chainId: Blockchain, completion: ((Error?)->())?) {
func emit(topic: String, event: SessionType.EventParams.Event, chainId: Blockchain) async throws {
guard let session = sessionStore.getSession(forTopic: topic), session.acknowledged else {
logger.debug("Could not find session for topic \(topic)")
return
}
let params = SessionType.EventParams(event: event, chainId: chainId)
do {
guard session.hasNamespace(for: chainId, event: event.name) else {
throw WalletConnectError.invalidEvent
}
networkingInteractor.request(.wcSessionEvent(params), onTopic: topic)
} catch let error as WalletConnectError {
logger.error(error)
completion?(error)
} catch {}
guard session.hasNamespace(for: chainId, event: event.name) else {
throw WalletConnectError.invalidEvent
}
try await networkingInteractor.request(.wcSessionEvent(params), onTopic: topic)
}

//MARK: - Private
Expand Down Expand Up @@ -165,9 +160,8 @@ final class SessionEngine {
settleParams: settleParams,
acknowledged: false)
logger.debug("Sending session settle request")
networkingInteractor.subscribe(topic: topic)
Task { try? await networkingInteractor.subscribe(topic: topic) }
sessionStore.setSession(session)

networkingInteractor.request(.wcSessionSettle(settleParams), onTopic: topic)
}

Expand Down Expand Up @@ -262,7 +256,7 @@ final class SessionEngine {
networkingInteractor.transportConnectionPublisher
.sink { [unowned self] (_) in
let topics = sessionStore.getAll().map{$0.topic}
topics.forEach{networkingInteractor.subscribe(topic: $0)}
topics.forEach{ topic in Task { try? await networkingInteractor.subscribe(topic: topic) } }
}.store(in: &publishers)
}

Expand Down
33 changes: 33 additions & 0 deletions Sources/WalletConnect/Engine/Controller/PairEngine.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@

import Foundation
import WalletConnectKMS

actor PairEngine {
private let networkingInteractor: NetworkInteracting
private let kms: KeyManagementServiceProtocol
private let pairingStore: WCPairingStorage

init(networkingInteractor: NetworkInteracting,
kms: KeyManagementServiceProtocol,
pairingStore: WCPairingStorage) {
self.networkingInteractor = networkingInteractor
self.kms = kms
self.pairingStore = pairingStore
}

func pair(_ uri: WalletConnectURI) async throws {
guard !hasPairing(for: uri.topic) else {
throw WalletConnectError.pairingAlreadyExist
}
var pairing = WCPairing(uri: uri)
try await networkingInteractor.subscribe(topic: pairing.topic)
let symKey = try! SymmetricKey(hex: uri.symKey) // FIXME: Malformed QR code from external source can crash the SDK
try! kms.setSymmetricKey(symKey, for: pairing.topic)
pairing.activate()
pairingStore.setPairing(pairing)
}

func hasPairing(for topic: String) -> Bool {
return pairingStore.hasPairing(forTopic: topic)
}
}
10 changes: 3 additions & 7 deletions Sources/WalletConnect/NetworkInteractor/NetworkInteractor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ protocol NetworkInteracting: AnyObject {
func respond(topic: String, response: JsonRpcResult, completion: @escaping ((Error?)->()))
func respondSuccess(for payload: WCRequestSubscriptionPayload)
func respondError(for payload: WCRequestSubscriptionPayload, reason: ReasonCode)
func subscribe(topic: String)
func subscribe(topic: String) async throws
func unsubscribe(topic: String)
}

Expand Down Expand Up @@ -169,12 +169,8 @@ class NetworkInteractor: NetworkInteracting {
respond(topic: payload.topic, response: JsonRpcResult.error(response)) { _ in } // TODO: Move error handling to relayer package
}

func subscribe(topic: String) {
networkRelayer.subscribe(topic: topic) { [weak self] error in
if let error = error {
self?.logger.error(error)
}
}
func subscribe(topic: String) async throws {
try await networkRelayer.subscribe(topic: topic)
}

func unsubscribe(topic: String) {
Expand Down
4 changes: 2 additions & 2 deletions Sources/WalletConnect/NetworkInteractor/NetworkRelaying.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ protocol NetworkRelaying {
func publish(topic: String, payload: String, prompt: Bool) async throws
/// - returns: request id
@discardableResult func publish(topic: String, payload: String, prompt: Bool, onNetworkAcknowledge: @escaping ((Error?)->())) -> Int64
/// - returns: request id
@discardableResult func subscribe(topic: String, completion: @escaping (Error?)->()) -> Int64
func subscribe(topic: String, completion: @escaping (Error?)->())
func subscribe(topic: String) async throws
/// - returns: request id
@discardableResult func unsubscribe(topic: String, completion: @escaping ((Error?)->())) -> Int64?
}
Loading

0 comments on commit 6f9397e

Please sign in to comment.