philips-software/cogito

View on GitHub
workspaces/cogito-ios-app/Cogito/Sign transaction service/TransactionSigner.swift

Summary

Maintainability
A
1 hr
Test Coverage
F
34%
import ReSwift
import BigInt

struct TransactionSignerBuilder {
    let transaction: [String: Any]
    let dispatch: DispatchFunction
    let getState: () -> AppState?
    let responseId: JsonRpcId
    let channel: TelepathChannel

    func build() -> TransactionSigner {
        guard let transaction = UnsignedTransaction(from: transaction) else {
            return TransactionSignerInvalid(dispatch: dispatch,
                                            responseId: responseId,
                                            channel: channel,
                                            error: SignTransactionError.invalidTransaction)
        }
        guard let identity = getState()?.diamond.findIdentity(address: transaction.from) else {
            return TransactionSignerInvalid(dispatch: dispatch,
                                            responseId: responseId,
                                            channel: channel,
                                            error: SignTransactionError.unknownIdentity)
        }
        return TransactionSignerValid(transaction: transaction,
                                      dispatch: dispatch,
                                      getState: getState,
                                      responseId: responseId,
                                      channel: channel,
                                      identity: identity)
    }
}

protocol TransactionSigner {
    var dispatch: DispatchFunction { get }
    func execute()
}

struct TransactionSignerInvalid: TransactionSigner {
    let dispatch: DispatchFunction
    let responseId: JsonRpcId
    let channel: TelepathChannel
    let error: SignTransactionError

    func execute() {
        dispatch(TelepathActions.Send(id: responseId,
                                      error: error,
                                      on: channel))
    }
}

struct TransactionSignerValid: TransactionSigner {
    let transaction: UnsignedTransaction
    let dispatch: DispatchFunction
    let getState: () -> AppState?
    let responseId: JsonRpcId
    let channel: TelepathChannel
    let identity: Identity

    func execute() {
        let storyBoard = UIStoryboard(name: "SignTransaction", bundle: nil)
        // swiftlint:disable force_cast
        let viewController = storyBoard.instantiateInitialViewController() as! UINavigationController
        let explanationViewController = viewController.topViewController! as! ExplanationViewController
        // swiftlint:enable force_cast
        let signingDone = { viewController.dismiss(animated: true) }

        guard let state = self.getState(), let appName = channel.appName else {
            self.dispatch(TelepathActions.Send(id: self.responseId,
                                               error: SignTransactionError.signingFailed,
                                               on: self.channel))
            signingDone()
            return
        }
        explanationViewController.appName = appName
        explanationViewController.actionDescription = "sign a blockchain transaction"
        explanationViewController.identity = identity

        explanationViewController.onReject = {
            self.dispatch(TelepathActions.Send(id: self.responseId,
                                               error: SignTransactionError.userRejected,
                                               on: self.channel))
            signingDone()
        }
        explanationViewController.onSign = {
            let keyStore = state.keyStore.keyStore!
            let identity = explanationViewController.identity!
            keyStore.sign(
                transaction: self.transaction,
                identity: identity
            ) { (signedTransaction, error) in
                guard let signedTx = signedTransaction else {
                    print("[error] sign transaction failed: \(error ?? "unknown error")")
                    self.dispatch(TelepathActions.Send(id: self.responseId,
                                                       error: SignTransactionError.signingFailed,
                                                       on: self.channel))
                    signingDone()
                    return
                }
                self.dispatch(TelepathActions.Send(id: self.responseId,
                                                   result: signedTx,
                                                   on: self.channel))
                signingDone()
            }
        }
        UIApplication.shared.keyWindow?.currentViewController?.present(viewController, animated: true)
    }
}