Skip to content

Commit

Permalink
Merge pull request #543 from WalletConnect/networking-client
Browse files Browse the repository at this point in the history
[Networking] expose networking client interface
  • Loading branch information
llbartekll authored Oct 13, 2022
2 parents b73ccf1 + a3d6c50 commit 395800a
Show file tree
Hide file tree
Showing 16 changed files with 240 additions and 225 deletions.
2 changes: 0 additions & 2 deletions Example/DApp/Auth/AuthCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ final class AuthCoordinator {
icons: ["https://avatars.githubusercontent.com/u/37784886"])

Pair.configure(metadata: metadata)
Auth.configure(account: nil)

navigationController.viewControllers = [authViewController]
}
}
1 change: 0 additions & 1 deletion Example/IntegrationTests/Sign/SignClientTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ final class SignClientTests: XCTestCase {
logger: logger,
keyValueStorage: keyValueStorage,
keychainStorage: keychain,
relayClient: relayClient,
pairingClient: pairingClient,
networkingClient: networkingClient
)
Expand Down
34 changes: 10 additions & 24 deletions Sources/Auth/Auth.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,38 +18,24 @@ public class Auth {

/// Auth client instance
public static var instance: AuthClient = {
guard let config = Auth.config else {
fatalError("Error - you must call Auth.configure(_:) before accessing the shared instance.")
}

return AuthClientFactory.create(
metadata: config.metadata,
account: config.account,
networkingClient: Networking.instance,
pairingRegisterer: Pair.instance as! PairingRegisterer
metadata: Pair.metadata,
account: config?.account,
networkingClient: Networking.interactor,
pairingRegisterer: Pair.registerer
)
}()


private static var config: Config?

private init() { }

/// Auth instance config method
/// Auth instance wallet config method
/// - Parameters:
/// - metadata: App metadata
/// - account: account that wallet will be authenticating with. Should be nil for non wallet clients.
@available(*, deprecated, message: "Use Pair.configure(metadata:) with Auth.configure(account:) instead")
static public func configure(metadata: AppMetadata, account: Account?) {
Auth.config = Auth.Config(
metadata: metadata,
account: account)
}

/// Auth instance config method
/// - Parameters:
/// - account: account that wallet will be authenticating with. Should be nil for non wallet clients.
static public func configure(account: Account?) {
Auth.config = Auth.Config(
metadata: Pair.metadata,
account: account)
/// - account: account that wallet will be authenticating with.
static public func configure(account: Account) {
Auth.config = Auth.Config(account: account)
}
}
4 changes: 2 additions & 2 deletions Sources/Auth/AuthClientFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ import WalletConnectNetworking

public struct AuthClientFactory {

public static func create(metadata: AppMetadata, account: Account?, networkingClient: NetworkingClient, pairingRegisterer: PairingRegisterer) -> AuthClient {
public static func create(metadata: AppMetadata, account: Account?, networkingClient: NetworkingInteractor, pairingRegisterer: PairingRegisterer) -> AuthClient {
let logger = ConsoleLogger(loggingLevel: .off)
let keyValueStorage = UserDefaults.standard
let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk")
return AuthClientFactory.create(metadata: metadata, account: account, logger: logger, keyValueStorage: keyValueStorage, keychainStorage: keychainStorage, networkingClient: networkingClient, pairingRegisterer: pairingRegisterer)
}

static func create(metadata: AppMetadata, account: Account?, logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, networkingClient: NetworkingClient, pairingRegisterer: PairingRegisterer) -> AuthClient {
static func create(metadata: AppMetadata, account: Account?, logger: ConsoleLogging, keyValueStorage: KeyValueStorage, keychainStorage: KeychainStorageProtocol, networkingClient: NetworkingInteractor, pairingRegisterer: PairingRegisterer) -> AuthClient {
let kms = KeyManagementService(keychain: keychainStorage)
let history = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage)
let messageFormatter = SIWEMessageFormatter()
Expand Down
1 change: 0 additions & 1 deletion Sources/Auth/AuthConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import Foundation

extension Auth {
struct Config {
let metadata: AppMetadata
let account: Account?
}
}
2 changes: 1 addition & 1 deletion Sources/Chat/ChatClientFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public struct ChatClientFactory {
let topicToRegistryRecordStore = CodableStore<RegistryRecord>(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.topicToInvitationPubKey.rawValue)
let serialiser = Serializer(kms: kms)
let rpcHistory = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage)
let networkingInteractor = NetworkingClient(relayClient: relayClient, serializer: serialiser, logger: logger, rpcHistory: rpcHistory)
let networkingInteractor = NetworkingInteractor(relayClient: relayClient, serializer: serialiser, logger: logger, rpcHistory: rpcHistory)
let invitePayloadStore = CodableStore<RequestSubscriptionPayload<Invite>>(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.invite.rawValue)
let registryService = RegistryService(registry: registry, networkingInteractor: networkingInteractor, kms: kms, logger: logger, topicToRegistryRecordStore: topicToRegistryRecordStore)
let threadStore = Database<Thread>(keyValueStorage: keyValueStorage, identifier: StorageDomainIdentifiers.threads.rawValue)
Expand Down
11 changes: 7 additions & 4 deletions Sources/WalletConnectNetworking/Networking.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@ import Foundation
public class Networking {

/// Networking client instance
public static var instance: NetworkingClient = {
guard let config = Networking.config else {
public static var instance: NetworkingClient {
return Networking.interactor
}

public static var interactor: NetworkingInteractor {
guard let _ = Networking.config else {
fatalError("Error - you must call Networking.configure(_:) before accessing the shared instance.")
}

return NetworkingClientFactory.create(relayClient: Relay.instance)
}()
}

private static var config: Config?

Expand Down Expand Up @@ -43,4 +47,3 @@ public class Networking {
socketConnectionType: socketConnectionType)
}
}

171 changes: 4 additions & 167 deletions Sources/WalletConnectNetworking/NetworkingClient.swift
Original file line number Diff line number Diff line change
@@ -1,172 +1,9 @@
import Foundation
import Combine
import JSONRPC
import WalletConnectRelay
import WalletConnectUtils
import WalletConnectKMS

public class NetworkingClient: NetworkInteracting {
private var publishers = Set<AnyCancellable>()
private let relayClient: RelayClient
private let serializer: Serializing
private let rpcHistory: RPCHistory
private let logger: ConsoleLogging

private let requestPublisherSubject = PassthroughSubject<(topic: String, request: RPCRequest), Never>()
private let responsePublisherSubject = PassthroughSubject<(topic: String, request: RPCRequest, response: RPCResponse), Never>()

public var requestPublisher: AnyPublisher<(topic: String, request: RPCRequest), Never> {
requestPublisherSubject.eraseToAnyPublisher()
}

private var responsePublisher: AnyPublisher<(topic: String, request: RPCRequest, response: RPCResponse), Never> {
responsePublisherSubject.eraseToAnyPublisher()
}

public var socketConnectionStatusPublisher: AnyPublisher<SocketConnectionStatus, Never>

public init(
relayClient: RelayClient,
serializer: Serializing,
logger: ConsoleLogging,
rpcHistory: RPCHistory
) {
self.relayClient = relayClient
self.serializer = serializer
self.rpcHistory = rpcHistory
self.logger = logger
self.socketConnectionStatusPublisher = relayClient.socketConnectionStatusPublisher
setupRelaySubscribtion()
}

private func setupRelaySubscribtion() {
relayClient.messagePublisher
.sink { [unowned self] (topic, message) in
manageSubscription(topic, message)
}.store(in: &publishers)
}

public func subscribe(topic: String) async throws {
try await relayClient.subscribe(topic: topic)
}

public func unsubscribe(topic: String) {
relayClient.unsubscribe(topic: topic) { [unowned self] error in
if let error = error {
logger.error(error)
} else {
rpcHistory.deleteAll(forTopic: topic)
}
}
}

public func requestSubscription<RequestParams: Codable>(on request: ProtocolMethod) -> AnyPublisher<RequestSubscriptionPayload<RequestParams>, Never> {
return requestPublisher
.filter { rpcRequest in
return rpcRequest.request.method == request.method
}
.compactMap { topic, rpcRequest in
guard let id = rpcRequest.id, let request = try? rpcRequest.params?.get(RequestParams.self) else { return nil }
return RequestSubscriptionPayload(id: id, topic: topic, request: request)
}
.eraseToAnyPublisher()
}

public func responseSubscription<Request: Codable, Response: Codable>(on request: ProtocolMethod) -> AnyPublisher<ResponseSubscriptionPayload<Request, Response>, Never> {
return responsePublisher
.filter { rpcRequest in
return rpcRequest.request.method == request.method
}
.compactMap { topic, rpcRequest, rpcResponse in
guard
let id = rpcRequest.id,
let request = try? rpcRequest.params?.get(Request.self),
let response = try? rpcResponse.result?.get(Response.self) else { return nil }
return ResponseSubscriptionPayload(id: id, topic: topic, request: request, response: response)
}
.eraseToAnyPublisher()
}

public func responseErrorSubscription<Request: Codable>(on request: ProtocolMethod) -> AnyPublisher<ResponseSubscriptionErrorPayload<Request>, Never> {
return responsePublisher
.filter { $0.request.method == request.method }
.compactMap { (topic, rpcRequest, rpcResponse) in
guard let id = rpcResponse.id, let request = try? rpcRequest.params?.get(Request.self), let error = rpcResponse.error else { return nil }
return ResponseSubscriptionErrorPayload(id: id, topic: topic, request: request, error: error)
}
.eraseToAnyPublisher()
}

public func request(_ request: RPCRequest, topic: String, protocolMethod: ProtocolMethod, envelopeType: Envelope.EnvelopeType) async throws {
try rpcHistory.set(request, forTopic: topic, emmitedBy: .local)
let message = try! serializer.serialize(topic: topic, encodable: request, envelopeType: envelopeType)
try await relayClient.publish(topic: topic, payload: message, tag: protocolMethod.requestConfig.tag, prompt: protocolMethod.requestConfig.prompt, ttl: protocolMethod.requestConfig.ttl)
}

/// Completes with an acknowledgement from the relay network.
/// completes with error if networking client was not able to send a message
/// TODO - relay client should provide async function - continualion should be removed from here
public func requestNetworkAck(_ request: RPCRequest, topic: String, protocolMethod: ProtocolMethod) async throws {
do {
try rpcHistory.set(request, forTopic: topic, emmitedBy: .local)
let message = try serializer.serialize(topic: topic, encodable: request)
return try await withCheckedThrowingContinuation { continuation in
relayClient.publish(topic: topic, payload: message, tag: protocolMethod.requestConfig.tag, prompt: protocolMethod.requestConfig.prompt, ttl: protocolMethod.requestConfig.ttl) { error in
if let error = error {
continuation.resume(throwing: error)
} else {
continuation.resume()
}
}
}
} catch {
logger.error(error)
}
}

public func respond(topic: String, response: RPCResponse, protocolMethod: ProtocolMethod, envelopeType: Envelope.EnvelopeType) async throws {
try rpcHistory.resolve(response)
let message = try! serializer.serialize(topic: topic, encodable: response, envelopeType: envelopeType)
try await relayClient.publish(topic: topic, payload: message, tag: protocolMethod.responseConfig.tag, prompt: protocolMethod.responseConfig.prompt, ttl: protocolMethod.responseConfig.ttl)
}

public func respondSuccess(topic: String, requestId: RPCID, protocolMethod: ProtocolMethod, envelopeType: Envelope.EnvelopeType) async throws {
let response = RPCResponse(id: requestId, result: true)
try await respond(topic: topic, response: response, protocolMethod: protocolMethod, envelopeType: envelopeType)
}

public func respondError(topic: String, requestId: RPCID, protocolMethod: ProtocolMethod, reason: Reason, envelopeType: Envelope.EnvelopeType) async throws {
let error = JSONRPCError(code: reason.code, message: reason.message)
let response = RPCResponse(id: requestId, error: error)
try await respond(topic: topic, response: response, protocolMethod: protocolMethod, envelopeType: envelopeType)
}

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 response: RPCResponse = serializer.tryDeserialize(topic: topic, encodedEnvelope: encodedEnvelope) {
handleResponse(response: response)
} else {
logger.debug("Networking Interactor - Received unknown object type from networking relay")
}
}

private func handleRequest(topic: String, request: RPCRequest) {
do {
try rpcHistory.set(request, forTopic: topic, emmitedBy: .remote)
requestPublisherSubject.send((topic, request))
} catch {
logger.debug(error)
}
}

private func handleResponse(response: RPCResponse) {
do {
try rpcHistory.resolve(response)
let record = rpcHistory.get(recordId: response.id!)!
responsePublisherSubject.send((record.topic, record.request, response))
} catch {
logger.debug("Handle json rpc response error: \(error)")
}
}
public protocol NetworkingClient {
var socketConnectionStatusPublisher: AnyPublisher<SocketConnectionStatus, Never> { get }
func connect() throws
func disconnect(closeCode: URLSessionWebSocketTask.CloseCode) throws
}
6 changes: 3 additions & 3 deletions Sources/WalletConnectNetworking/NetworkingClientFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,21 @@ import WalletConnectUtils

public struct NetworkingClientFactory {

public static func create(relayClient: RelayClient) -> NetworkingClient {
public static func create(relayClient: RelayClient) -> NetworkingInteractor {
let logger = ConsoleLogger(loggingLevel: .off)
let keyValueStorage = UserDefaults.standard
let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk")
return NetworkingClientFactory.create(relayClient: relayClient, logger: logger, keychainStorage: keychainStorage, keyValueStorage: keyValueStorage)
}

public static func create(relayClient: RelayClient, logger: ConsoleLogging, keychainStorage: KeychainStorageProtocol, keyValueStorage: KeyValueStorage) -> NetworkingClient{
public static func create(relayClient: RelayClient, logger: ConsoleLogging, keychainStorage: KeychainStorageProtocol, keyValueStorage: KeyValueStorage) -> NetworkingInteractor{
let kms = KeyManagementService(keychain: keychainStorage)

let serializer = Serializer(kms: kms)

let rpcHistory = RPCHistoryFactory.createForNetwork(keyValueStorage: keyValueStorage)

return NetworkingClient(
return NetworkingInteractor(
relayClient: relayClient,
serializer: serializer,
logger: logger,
Expand Down
Loading

0 comments on commit 395800a

Please sign in to comment.