Skip to content
Snippets Groups Projects
XXNetwork.swift 4.5 KiB
Newer Older
Bruno Muniz's avatar
Bruno Muniz committed
import Shared
import XXLogger
import Keychain
import Foundation
import DependencyInjection

public enum NetworkEnvironment {
    case mainnet
}

public protocol XXNetworking {
    var hasClient: Bool { get }

    func writeLogs()
    func purgeFiles()
    func updateErrors()
    func newClient(ndf: String) throws -> Client
Ahmed Shehata's avatar
Ahmed Shehata committed

    func updateNDF(
        _: @escaping (Result<String, Error>) -> Void
    )

    func loadClient(
        with: Data,
        fromBackup: Bool,
        email: String?,
        phone: String?
    ) throws -> Client

    func newClientFromBackup(
        passphrase: String,
        data: Data,
        ndf: String
    ) throws -> (Client, Data?)
Bruno Muniz's avatar
Bruno Muniz committed
}

public struct XXNetwork<B: BindingsInterface> {
    @Dependency private var logger: XXLogger
    @Dependency private var keychain: KeychainHandling

    public init() {}
}

extension XXNetwork: XXNetworking {
    public var hasClient: Bool {
        guard let files = FileManager.xxContents else { return false }
        return files.count > 0
    }

    public func writeLogs() {
        B.listenLogs()
    }

    public func updateErrors() {
        B.updateErrors()
    }

    public func updateNDF(_ completion: @escaping (Result<String, Error>) -> Void) {
        B.updateNDF(for: .mainnet) {
            switch $0 {
            case .success(let data):
                guard let ndfData = data, let ndf = String(data: ndfData, encoding: .utf8) else {
                    completion(.failure(NSError.create("NDF is empty (?)")))
                    return
                }

                completion(.success(ndf))
            case .failure(let error):
                completion(.failure(error))
            }
        }
    }

    public func purgeFiles() {
        FileManager.xxCleanup()
    }

Ahmed Shehata's avatar
Ahmed Shehata committed
    public func newClientFromBackup(
        passphrase: String,
        data: Data,
        ndf: String
    ) throws -> (Client, Data?) {
        var error: NSError?

        let password = B.secret(32)!
        try keychain.store(password: password)

Ahmed Shehata's avatar
Ahmed Shehata committed
        let backupData = B.fromBackup(
            ndf,
            FileManager.xxPath,
            password,
            "\(passphrase)".data(using: .utf8),
            data,
            &error
        )

        if let error = error { throw error }

        var email: String?
        var phone: String?

        let report = try! JSONDecoder().decode(BackupReport.self, from: backupData!)

        if !report.parameters.isEmpty {
            let params = try! JSONDecoder().decode(BackupParameters.self, from: Data(report.parameters.utf8))
            phone = params.phone
            email = params.email
        }

        let client = try loadClient(with: password, fromBackup: true, email: email, phone: phone)
        return (client, backupData)
    }

Bruno Muniz's avatar
Bruno Muniz committed
    public func newClient(ndf: String) throws -> Client {
        var password: Data!

        if hasClient == false {
            var error: NSError?

            password = B.secret(32)
            try keychain.store(password: password)

            _ = B.new(ndf, FileManager.xxPath, password, nil, &error)
Bruno Muniz's avatar
Bruno Muniz committed
            if let error = error { throw error }
        } else {
            guard let secret = try keychain.getPassword() else {
                throw NSError.create("Empty stored secret")
            }

            password = secret
        }

        return try loadClient(with: password, fromBackup: false, email: nil, phone: nil)
    public func loadClient(
        with secret: Data,
        fromBackup: Bool,
        email: String?,
        phone: String?
    ) throws -> Client {
Bruno Muniz's avatar
Bruno Muniz committed
        var error: NSError?
        let bindings = B.login(FileManager.xxPath, secret, "", &error)
        if let error = error { throw error }

Ahmed Shehata's avatar
Ahmed Shehata committed
        if let defaults = UserDefaults(suiteName: "group.elixxir.messenger") {
Bruno Muniz's avatar
Bruno Muniz committed
            defaults.set(bindings!.receptionId.base64EncodedString(), forKey: "receptionId")
        }

        return Client(bindings!, fromBackup: fromBackup, email: email, phone: phone)
Bruno Muniz's avatar
Bruno Muniz committed
    }
}

extension NetworkEnvironment {
    var url: String {
        switch self {
        case .mainnet:
            return "https://elixxir-bins.s3.us-west-1.amazonaws.com/ndf/mainnet.json"
        }
    }

    var cert: String {
        switch self {
        case .mainnet:
            guard let filepath = Bundle.module.path(forResource: "cert_mainnet", ofType: "txt"),
                  let certString = try? String(contentsOfFile: filepath) else {
                      fatalError("Couldn't retrieve network cert file.")
                  }

            return certString
        }
    }
}