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

[Refactor] Generic NetworkInteractor #483

Merged
merged 5 commits into from
Sep 5, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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: AuthNetworkRequest.request.tag)
flypaper0 marked this conversation as resolved.
Show resolved Hide resolved
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.responceErrorSubscription(on: AuthNetworkRequest.request)
flypaper0 marked this conversation as resolved.
Show resolved Hide resolved
.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: AuthNetworkRequest.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: AuthNetworkRequest.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 = AuthNetworkRequest.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: AuthNetworkRequest.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 = AuthNetworkRequest.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/AuthNetworkRequest.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import Foundation
import WalletConnectNetworking

enum AuthNetworkRequest: NetworkRequest {
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: ChatRequest.message.method, params: message)
try await networkingInteractor.request(request, topic: topic, tag: ChatRequest.message.tag)
Task(priority: .background) {
await messagesStore.add(message)
onMessage?(message)
}
}

private func setUpResponseHandling() {
networkingInteractor.responsePublisher
.sink { [unowned self] payload in
networkingInteractor.responseSubscription(on: ChatRequest.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: ChatRequest.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: ChatRequest.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: ChatRequest.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: ChatRequest.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: ChatRequest.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