Skip to content

Commit

Permalink
External error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
flypaper0 committed Aug 19, 2022
1 parent 0e08b73 commit 2d59141
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 33 deletions.
12 changes: 6 additions & 6 deletions Sources/Auth/AuthClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ public class AuthClient {
case unknownWalletAddress
case noPairingMatchingTopic
}
private var authRequestPublisherSubject = PassthroughSubject<(id: RPCID, result: Result<String, ErrorCode>), Never>()
public var authRequestPublisher: AnyPublisher<(id: RPCID, result: Result<String, ErrorCode>), Never> {
private var authRequestPublisherSubject = PassthroughSubject<(id: RPCID, message: String), Never>()
public var authRequestPublisher: AnyPublisher<(id: RPCID, message: String), Never> {
authRequestPublisherSubject.eraseToAnyPublisher()
}

private var authResponsePublisherSubject = PassthroughSubject<(id: RPCID, result: Result<Cacao, ErrorCode>), Never>()
public var authResponsePublisher: AnyPublisher<(id: RPCID, result: Result<Cacao, ErrorCode>), Never> {
private var authResponsePublisherSubject = PassthroughSubject<(id: RPCID, result: Result<Cacao, InternalError>), Never>()
public var authResponsePublisher: AnyPublisher<(id: RPCID, result: Result<Cacao, InternalError>), Never> {
authResponsePublisherSubject.eraseToAnyPublisher()
}

Expand Down Expand Up @@ -87,9 +87,9 @@ public class AuthClient {
try await appRequestService.request(params: params, topic: topic)
}

public func respond(_ params: RespondParams) async throws {
public func respond(requestId: RPCID, result: Result<RespondParams, ExternalError>) async throws {
guard let account = account else { throw Errors.unknownWalletAddress }
try await walletRespondService.respond(params: params, account: account)
try await walletRespondService.respond(requestId: requestId, result: result, account: account)
}

public func getPendingRequests() throws -> [AuthRequest] {
Expand Down
2 changes: 1 addition & 1 deletion Sources/Auth/AuthClientFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public struct AuthClientFactory {
let messageSigner = MessageSigner(signer: Signer())
let appRespondSubscriber = AppRespondSubscriber(networkingInteractor: networkingInteractor, logger: logger, rpcHistory: history, signatureVerifier: messageSigner, messageFormatter: messageFormatter)
let walletPairService = WalletPairService(networkingInteractor: networkingInteractor, kms: kms, pairingStorage: pairingStore)
let walletRequestSubscriber = WalletRequestSubscriber(networkingInteractor: networkingInteractor, logger: logger, messageFormatter: messageFormatter, address: account?.address)
let walletRequestSubscriber = WalletRequestSubscriber(networkingInteractor: networkingInteractor, logger: logger, rpcHistory: history, kms: kms, messageFormatter: messageFormatter, address: account?.address)
let walletRespondService = WalletRespondService(networkingInteractor: networkingInteractor, logger: logger, kms: kms, rpcHistory: history)
let pendingRequestsProvider = PendingRequestsProvider(rpcHistory: history)
let cleanupService = CleanupService(pairingStore: pairingStore, kms: kms)
Expand Down
2 changes: 1 addition & 1 deletion Sources/Auth/Services/App/AppRespondSubscriber.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class AppRespondSubscriber {
private let messageFormatter: SIWEMessageFormatting
private var publishers = [AnyCancellable]()

var onResponse: ((_ id: RPCID, _ result: Result<Cacao, ErrorCode>) -> Void)?
var onResponse: ((_ id: RPCID, _ result: Result<Cacao, InternalError>) -> Void)?

init(networkingInteractor: NetworkInteracting,
logger: ConsoleLogging,
Expand Down
30 changes: 24 additions & 6 deletions Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,28 @@ import Combine
import Foundation
import WalletConnectUtils
import JSONRPC
import WalletConnectKMS

class WalletRequestSubscriber {
private let networkingInteractor: NetworkInteracting
private let logger: ConsoleLogging
private let rpcHistory: RPCHistory
private let kms: KeyManagementService
private let address: String?
private var publishers = [AnyCancellable]()
private let messageFormatter: SIWEMessageFormatting
var onRequest: ((_ id: RPCID, _ result: Result<String, ErrorCode>) -> Void)?
var onRequest: ((_ id: RPCID, _ message: String) -> Void)?

init(networkingInteractor: NetworkInteracting,
logger: ConsoleLogging,
rpcHistory: RPCHistory,
kms: KeyManagementService,
messageFormatter: SIWEMessageFormatting,
address: String?) {
self.networkingInteractor = networkingInteractor
self.logger = logger
self.rpcHistory = rpcHistory
self.kms = kms
self.address = address
self.messageFormatter = messageFormatter
subscribeForRequest()
Expand All @@ -27,17 +34,28 @@ class WalletRequestSubscriber {
logger.warn("unexpected request")
return
}
networkingInteractor.requestPublisher.sink { [unowned self] subscriptionPayload in
guard let requestId = subscriptionPayload.request.id, subscriptionPayload.request.method == "wc_authRequest"
networkingInteractor.requestPublisher.sink { [unowned self] payload in
guard let requestId = payload.request.id, payload.request.method == "wc_authRequest"
else { return }

guard let authRequestParams = try? subscriptionPayload.request.params?.get(AuthRequestParams.self)
else { self.onRequest?(requestId, .failure(.malformedRequestParams)); 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?(requestId, .success(message))
onRequest?(requestId, message)
}.store(in: &publishers)
}

private func respondError(_ error: InternalError, 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 envelopeType = Envelope.EnvelopeType.type1(pubKey: pubKey.rawRepresentation)

Task(priority: .high) {
try await networkingInteractor.respondError(topic: topic, requestId: requestId, tag: tag, reason: error, envelopeType: envelopeType)
}
}
}
54 changes: 42 additions & 12 deletions Sources/Auth/Services/Wallet/WalletRespondService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,22 +23,52 @@ actor WalletRespondService {
self.rpcHistory = rpcHistory
}

func respond(params: RespondParams, account: Account) async throws {
guard let request = rpcHistory.get(recordId: RPCID(params.id))?.request
else { throw ErrorCode.malformedRequestParams }
func respond(requestId: RPCID, result: Result<RespondParams, ExternalError>, account: Account) async throws {
switch result {
case .success(let params):
try await respond(requestId: requestId, params: params, account: account)
case .failure(let error):
try await respond(error: error, requestId: requestId)
}
}

guard let authRequestParams = try request.params?.get(AuthRequestParams.self)
else { throw ErrorCode.malformedRequestParams }
private func respond(requestId: RPCID, params: RespondParams, account: Account) async throws {
let authRequestParams = try getAuthRequestParams(requestId: requestId)
let (topic, keys) = try generateAgreementKeys(requestParams: authRequestParams)

let peerPubKey = authRequestParams.requester.publicKey
let responseTopic = peerPubKey.rawRepresentation.sha256().toHexString()
let selfPubKey = try kms.createX25519KeyPair()
let agreementKeys = try kms.performKeyAgreement(selfPublicKey: selfPubKey, peerPublicKey: peerPubKey)
try kms.setAgreementSecret(agreementKeys, topic: responseTopic)
try kms.setAgreementSecret(keys, topic: topic)

let cacao = CacaoFormatter().format(authRequestParams, params.signature, account)
let response = RPCResponse(id: request.id!, result: cacao)
let response = RPCResponse(id: requestId, result: cacao)
try await networkingInteractor.respond(topic: params.topic, response: response, tag: AuthResponseParams.tag, envelopeType: .type1(pubKey: keys.publicKey.rawRepresentation))
}

private func respond(error: ExternalError, requestId: RPCID) async throws {
let authRequestParams = try getAuthRequestParams(requestId: requestId)
let (topic, keys) = try generateAgreementKeys(requestParams: authRequestParams)

try kms.setAgreementSecret(keys, topic: topic)

let tag = AuthResponseParams.tag
let envelopeType = Envelope.EnvelopeType.type1(pubKey: keys.publicKey.rawRepresentation)
try await networkingInteractor.respondError(topic: topic, requestId: requestId, tag: tag, reason: error, envelopeType: envelopeType)
}

private func getAuthRequestParams(requestId: RPCID) throws -> AuthRequestParams {
guard let request = rpcHistory.get(recordId: requestId)?.request
else { throw Errors.recordForIdNotFound }

guard let authRequestParams = try request.params?.get(AuthRequestParams.self)
else { throw Errors.malformedAuthRequestParams }

try await networkingInteractor.respond(topic: params.topic, response: response, tag: AuthResponseParams.tag, envelopeType: .type1(pubKey: selfPubKey.rawRepresentation))
return authRequestParams
}

private func generateAgreementKeys(requestParams: AuthRequestParams) throws -> (topic: String, keys: AgreementKeys) {
let peerPubKey = requestParams.requester.publicKey
let topic = peerPubKey.rawRepresentation.sha256().toHexString()
let selfPubKey = try kms.createX25519KeyPair()
let keys = try kms.performKeyAgreement(selfPublicKey: selfPubKey, peerPublicKey: peerPubKey)
return (topic, keys)
}
}
22 changes: 22 additions & 0 deletions Sources/Auth/Types/Error/ExternalError.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import Foundation

public enum ExternalError: Codable, Equatable, Error {
case userRejeted
}

extension ExternalError: Reason {

public var code: Int {
switch self {
case .userRejeted:
return 2001
}
}

public var message: String {
switch self {
case .userRejeted:
return "Auth request rejected by user"
}
}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
import Foundation

public protocol Reason {
var code: Int { get }
var message: String { get }
}

public enum ErrorCode: Codable, Equatable, Error {
public enum InternalError: Codable, Equatable, Error {
case malformedResponseParams
case malformedRequestParams
case messageCompromised
case messageVerificationFailed
}

extension ErrorCode: Reason {
extension InternalError: Reason {

public var code: Int {
switch self {
Expand Down
7 changes: 7 additions & 0 deletions Sources/Auth/Types/Error/Reason.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import Foundation

protocol Reason {
var code: Int { get }
var message: String { get }
}

0 comments on commit 2d59141

Please sign in to comment.