Skip to content

Commit

Permalink
Merge pull request #483 from WalletConnect/feature/genaric-networkint…
Browse files Browse the repository at this point in the history
…eractor

[Refactor] Generic NetworkInteractor
  • Loading branch information
flypaper0 authored Sep 5, 2022
2 parents 0cb9124 + eff673e commit 7cb497c
Show file tree
Hide file tree
Showing 28 changed files with 386 additions and 323 deletions.
2 changes: 1 addition & 1 deletion Sources/Auth/Services/App/AppRequestService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ actor AppRequestService {
let request = RPCRequest(method: "wc_authRequest", params: params)
try kms.setPublicKey(publicKey: pubKey, for: responseTopic)
logger.debug("AppRequestService: Subscribibg for response topic: \(responseTopic)")
try await networkingInteractor.requestNetworkAck(request, topic: topic, tag: AuthRequestParams.tag)
try await networkingInteractor.requestNetworkAck(request, topic: topic, tag: AuthProtocolMethod.request.tag)
try await networkingInteractor.subscribe(topic: responseTopic)
}
}
49 changes: 22 additions & 27 deletions Sources/Auth/Services/App/AppRespondSubscriber.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,41 +32,36 @@ class AppRespondSubscriber {
}

private func subscribeForResponse() {
networkingInteractor.responsePublisher.sink { [unowned self] subscriptionPayload in
let response = subscriptionPayload.response
guard
let requestId = response.id,
let request = rpcHistory.get(recordId: requestId)?.request,
let requestParams = request.params, request.method == "wc_authRequest"
else { return }
networkingInteractor.responseErrorSubscription(on: AuthProtocolMethod.request)
.sink { [unowned self] payload in
guard let error = AuthError(code: payload.error.code) else { return }
onResponse?(payload.id, .failure(error))
}.store(in: &publishers)

activatePairingIfNeeded(id: requestId)
networkingInteractor.unsubscribe(topic: subscriptionPayload.topic)
networkingInteractor.responseSubscription(on: AuthProtocolMethod.request)
.sink { [unowned self] (payload: ResponseSubscriptionPayload<AuthRequestParams, Cacao>) in

if let errorResponse = response.error,
let error = AuthError(code: errorResponse.code) {
onResponse?(requestId, .failure(error))
return
}
activatePairingIfNeeded(id: payload.id)
networkingInteractor.unsubscribe(topic: payload.topic)

guard
let cacao = try? response.result?.get(Cacao.self),
let address = try? DIDPKH(iss: cacao.payload.iss).account.address,
let message = try? messageFormatter.formatMessage(from: cacao.payload)
else { self.onResponse?(requestId, .failure(.malformedResponseParams)); return }
let requestId = payload.id
let cacao = payload.response
let requestPayload = payload.request

guard let requestPayload = try? requestParams.get(AuthRequestParams.self)
else { self.onResponse?(requestId, .failure(.malformedRequestParams)); return }
guard
let address = try? DIDPKH(iss: cacao.payload.iss).account.address,
let message = try? messageFormatter.formatMessage(from: cacao.payload)
else { self.onResponse?(requestId, .failure(.malformedResponseParams)); return }

guard messageFormatter.formatMessage(from: requestPayload.payloadParams, address: address) == message
else { self.onResponse?(requestId, .failure(.messageCompromised)); return }
guard messageFormatter.formatMessage(from: requestPayload.payloadParams, address: address) == message
else { self.onResponse?(requestId, .failure(.messageCompromised)); return }

guard let _ = try? signatureVerifier.verify(signature: cacao.signature, message: message, address: address)
else { self.onResponse?(requestId, .failure(.signatureVerificationFailed)); return }
guard let _ = try? signatureVerifier.verify(signature: cacao.signature, message: message, address: address)
else { self.onResponse?(requestId, .failure(.signatureVerificationFailed)); return }

onResponse?(requestId, .success(cacao))
onResponse?(requestId, .success(cacao))

}.store(in: &publishers)
}.store(in: &publishers)
}

private func activatePairingIfNeeded(id: RPCID) {
Expand Down
22 changes: 7 additions & 15 deletions Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,27 +30,19 @@ class WalletRequestSubscriber {
private func subscribeForRequest() {
guard let address = address else { return }

networkingInteractor.requestPublisher.sink { [unowned self] payload in

logger.debug("WalletRequestSubscriber: Received request")

guard let requestId = payload.request.id, payload.request.method == "wc_authRequest"
else { return }

guard let authRequestParams = try? payload.request.params?.get(AuthRequestParams.self)
else { return respondError(.malformedRequestParams, topic: payload.topic, requestId: requestId) }

let message = messageFormatter.formatMessage(from: authRequestParams.payloadParams, address: address)

onRequest?(.init(id: requestId, message: message))
}.store(in: &publishers)
networkingInteractor.requestSubscription(on: AuthProtocolMethod.request)
.sink { [unowned self] (payload: RequestSubscriptionPayload<AuthRequestParams>) in
logger.debug("WalletRequestSubscriber: Received request")
let message = messageFormatter.formatMessage(from: payload.request.payloadParams, address: address)
onRequest?(.init(id: payload.id, message: message))
}.store(in: &publishers)
}

private func respondError(_ error: AuthError, topic: String, requestId: RPCID) {
guard let pubKey = kms.getAgreementSecret(for: topic)?.publicKey
else { return logger.error("Agreement key for topic \(topic) not found") }

let tag = AuthResponseParams.tag
let tag = AuthProtocolMethod.request.tag
let envelopeType = Envelope.EnvelopeType.type1(pubKey: pubKey.rawRepresentation)

Task(priority: .high) {
Expand Down
4 changes: 2 additions & 2 deletions Sources/Auth/Services/Wallet/WalletRespondService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ actor WalletRespondService {
let didpkh = DIDPKH(account: account)
let cacao = CacaoFormatter().format(authRequestParams, signature, didpkh)
let response = RPCResponse(id: requestId, result: cacao)
try await networkingInteractor.respond(topic: topic, response: response, tag: AuthResponseParams.tag, envelopeType: .type1(pubKey: keys.publicKey.rawRepresentation))
try await networkingInteractor.respond(topic: topic, response: response, tag: AuthProtocolMethod.request.tag, envelopeType: .type1(pubKey: keys.publicKey.rawRepresentation))
}

func respondError(requestId: RPCID) async throws {
Expand All @@ -42,7 +42,7 @@ actor WalletRespondService {

try kms.setAgreementSecret(keys, topic: topic)

let tag = AuthResponseParams.tag
let tag = AuthProtocolMethod.request.tag
let error = AuthError.userRejeted
let envelopeType = Envelope.EnvelopeType.type1(pubKey: keys.publicKey.rawRepresentation)
try await networkingInteractor.respondError(topic: topic, requestId: requestId, tag: tag, reason: error, envelopeType: envelopeType)
Expand Down
14 changes: 14 additions & 0 deletions Sources/Auth/Types/AuthProtocolMethod.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import Foundation
import WalletConnectNetworking

enum AuthProtocolMethod: ProtocolMethod {
case request

var method: String {
return "wc_authRequest"
}

var tag: Int {
return 3001
}
}
7 changes: 1 addition & 6 deletions Sources/Auth/Types/ProtocolRPCParams/AuthRequestParams.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,7 @@ import WalletConnectUtils
/// wc_authRequest RPC method request param
struct AuthRequestParams: Codable, Equatable {
let requester: Requester
let payloadParams: AuthPayload

static var tag: Int {
return 3000
}
}
let payloadParams: AuthPayload}

extension AuthRequestParams {
struct Requester: Codable, Equatable {
Expand Down
4 changes: 0 additions & 4 deletions Sources/Auth/Types/ProtocolRPCParams/AuthResponseParams.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,4 @@ struct AuthResponseParams: Codable, Equatable {
let header: CacaoHeader
let payload: CacaoPayload
let signature: CacaoSignature

static var tag: Int {
return 3001
}
}
11 changes: 3 additions & 8 deletions Sources/Chat/ChatClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public class ChatClient {
private let kms: KeyManagementService
private let threadStore: Database<Thread>
private let messagesStore: Database<Message>
private let invitePayloadStore: CodableStore<RequestSubscriptionPayload>
private let invitePayloadStore: CodableStore<RequestSubscriptionPayload<Invite>>

public let socketConnectionStatusPublisher: AnyPublisher<SocketConnectionStatus, Never>

Expand Down Expand Up @@ -48,7 +48,7 @@ public class ChatClient {
kms: KeyManagementService,
threadStore: Database<Thread>,
messagesStore: Database<Message>,
invitePayloadStore: CodableStore<RequestSubscriptionPayload>,
invitePayloadStore: CodableStore<RequestSubscriptionPayload<Invite>>,
socketConnectionStatusPublisher: AnyPublisher<SocketConnectionStatus, Never>
) {
self.registry = registry
Expand Down Expand Up @@ -120,12 +120,7 @@ public class ChatClient {
}

public func getInvites(account: Account) -> [Invite] {
var invites = [Invite]()
invitePayloadStore.getAll().forEach {
guard let invite = try? $0.request.params?.get(Invite.self) else {return}
invites.append(invite)
}
return invites
return invitePayloadStore.getAll().map { $0.request }
}

public func getThreads() async -> [Thread] {
Expand Down
2 changes: 1 addition & 1 deletion Sources/Chat/ChatClientFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public struct ChatClientFactory {
let serialiser = Serializer(kms: kms)
let rpcHistory = RPCHistory(keyValueStore: CodableStore<RPCHistory.Record>(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.jsonRpcHistory.rawValue))
let networkingInteractor = NetworkingInteractor(relayClient: relayClient, serializer: serialiser, logger: logger, rpcHistory: rpcHistory)
let invitePayloadStore = CodableStore<RequestSubscriptionPayload>(defaults: keyValueStorage, identifier: StorageDomainIdentifiers.invite.rawValue)
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)
let resubscriptionService = ResubscriptionService(networkingInteractor: networkingInteractor, threadStore: threadStore, logger: logger)
Expand Down
28 changes: 10 additions & 18 deletions Sources/Chat/ProtocolServices/Common/MessagingService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,41 +33,33 @@ class MessagingService {
guard let authorAccount = thread?.selfAccount else { throw Errors.threadDoNotExist}
let timestamp = Int64(Date().timeIntervalSince1970 * 1000)
let message = Message(topic: topic, message: messageString, authorAccount: authorAccount, timestamp: timestamp)
let request = RPCRequest(method: Message.method, params: message)
try await networkingInteractor.request(request, topic: topic, tag: Message.tag)
let request = RPCRequest(method: ChatProtocolMethod.message.method, params: message)
try await networkingInteractor.request(request, topic: topic, tag: ChatProtocolMethod.message.tag)
Task(priority: .background) {
await messagesStore.add(message)
onMessage?(message)
}
}

private func setUpResponseHandling() {
networkingInteractor.responsePublisher
.sink { [unowned self] payload in
networkingInteractor.responseSubscription(on: ChatProtocolMethod.message)
.sink { [unowned self] (payload: ResponseSubscriptionPayload<AnyCodable, AnyCodable>) in
logger.debug("Received Message response")
}.store(in: &publishers)
}

private func setUpRequestHandling() {
networkingInteractor.requestPublisher.sink { [unowned self] payload in
do {
guard
let requestId = payload.request.id, payload.request.method == Message.method,
var message = try payload.request.params?.get(Message.self)
else { return }

networkingInteractor.requestSubscription(on: ChatProtocolMethod.message)
.sink { [unowned self] (payload: RequestSubscriptionPayload<Message>) in
var message = payload.request
message.topic = payload.topic

handleMessage(message, topic: payload.topic, requestId: requestId)
} catch {
logger.debug("Handling message response has failed")
}
}.store(in: &publishers)
handleMessage(message, topic: payload.topic, requestId: payload.id)
}.store(in: &publishers)
}

private func handleMessage(_ message: Message, topic: String, requestId: RPCID) {
Task(priority: .background) {
try await networkingInteractor.respondSuccess(topic: topic, requestId: requestId, tag: Message.tag)
try await networkingInteractor.respondSuccess(topic: topic, requestId: requestId, tag: ChatProtocolMethod.message.tag)
await messagesStore.add(message)
logger.debug("Received message")
onMessage?(message)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class InvitationHandlingService {
var onInvite: ((Invite) -> Void)?
var onNewThread: ((Thread) -> Void)?
private let networkingInteractor: NetworkInteracting
private let invitePayloadStore: CodableStore<RequestSubscriptionPayload>
private let invitePayloadStore: CodableStore<RequestSubscriptionPayload<Invite>>
private let topicToRegistryRecordStore: CodableStore<RegistryRecord>
private let registry: Registry
private let logger: ConsoleLogging
Expand All @@ -26,7 +26,7 @@ class InvitationHandlingService {
kms: KeyManagementService,
logger: ConsoleLogging,
topicToRegistryRecordStore: CodableStore<RegistryRecord>,
invitePayloadStore: CodableStore<RequestSubscriptionPayload>,
invitePayloadStore: CodableStore<RequestSubscriptionPayload<Invite>>,
threadsStore: Database<Thread>) {
self.registry = registry
self.kms = kms
Expand All @@ -45,14 +45,11 @@ class InvitationHandlingService {

let inviteResponse = InviteResponse(publicKey: selfThreadPubKey.hexRepresentation)

guard let requestId = payload.request.id, let invite = try? payload.request.params?.get(Invite.self)
else { return }
let response = RPCResponse(id: payload.id, result: inviteResponse)
let responseTopic = try getInviteResponseTopic(requestTopic: payload.topic, invite: payload.request)
try await networkingInteractor.respond(topic: responseTopic, response: response, tag: ChatProtocolMethod.invite.tag)

let response = RPCResponse(id: requestId, result: inviteResponse)
let responseTopic = try getInviteResponseTopic(requestTopic: payload.topic, invite: invite)
try await networkingInteractor.respond(topic: responseTopic, response: response, tag: Invite.tag)

let threadAgreementKeys = try kms.performKeyAgreement(selfPublicKey: selfThreadPubKey, peerPublicKey: invite.publicKey)
let threadAgreementKeys = try kms.performKeyAgreement(selfPublicKey: selfThreadPubKey, peerPublicKey: payload.request.publicKey)
let threadTopic = threadAgreementKeys.derivedTopic()
try kms.setSymmetricKey(threadAgreementKeys.sharedKey, for: threadTopic)
try await networkingInteractor.subscribe(topic: threadTopic)
Expand All @@ -61,7 +58,7 @@ class InvitationHandlingService {

// TODO - derive account
let selfAccount = try! topicToRegistryRecordStore.get(key: payload.topic)!.account
let thread = Thread(topic: threadTopic, selfAccount: selfAccount, peerAccount: invite.account)
let thread = Thread(topic: threadTopic, selfAccount: selfAccount, peerAccount: payload.request.account)
await threadsStore.add(thread)

invitePayloadStore.delete(forKey: inviteId)
Expand All @@ -72,28 +69,20 @@ class InvitationHandlingService {
func reject(inviteId: String) async throws {
guard let payload = try invitePayloadStore.get(key: inviteId) else { throw Error.inviteForIdNotFound }

guard let requestId = payload.request.id, let invite = try? payload.request.params?.get(Invite.self)
else { return }

let responseTopic = try getInviteResponseTopic(requestTopic: payload.topic, invite: invite)
let responseTopic = try getInviteResponseTopic(requestTopic: payload.topic, invite: payload.request)

try await networkingInteractor.respondError(topic: responseTopic, requestId: requestId, tag: Invite.tag, reason: ChatError.userRejected)
try await networkingInteractor.respondError(topic: responseTopic, requestId: payload.id, tag: ChatProtocolMethod.invite.tag, reason: ChatError.userRejected)

invitePayloadStore.delete(forKey: inviteId)
}

private func setUpRequestHandling() {
networkingInteractor.requestPublisher.sink { [unowned self] payload in
guard payload.request.method == "wc_chatInvite"
else { return }

guard let invite = try? payload.request.params?.get(Invite.self)
else { return }

logger.debug("did receive an invite")
invitePayloadStore.set(payload, forKey: invite.publicKey)
onInvite?(invite)
}.store(in: &publishers)
networkingInteractor.requestSubscription(on: ChatProtocolMethod.invite)
.sink { [unowned self] (payload: RequestSubscriptionPayload<Invite>) in
logger.debug("did receive an invite")
invitePayloadStore.set(payload, forKey: payload.request.publicKey)
onInvite?(payload.request)
}.store(in: &publishers)
}

private func getInviteResponseTopic(requestTopic: String, invite: Invite) throws -> String {
Expand Down
Loading

0 comments on commit 7cb497c

Please sign in to comment.