Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Concurrency #206

Merged
merged 13 commits into from
May 12, 2022
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 @@

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we going to migrate PairingEngines code in here over time?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not think so, I would rather split PairingEngine for more classes in the future.
like Controller/ProposeEngine NonController/ProposeEngine`. one class for each protocol method segregated by controller/nonController. Probably we could agree on better naming.

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