// // AccountServiceChangeKey.swift // PayCash // // Created by Alexandr Serpokrylow on 16.02.2022. // Copyright © 2022 AM. All rights reserved. // import Foundation import EosioSwift extension Account.Service { class ChangeKey: Common.Service.Provider { private(set) var accounts = [Account.Model.ChangeKey]() private var requests = 0 func fetch(privateKey: String, keyType: Account.KeyType, _ completion: @escaping Completion.Network) { guard let publicKey = privateKey.publicKey(), let newPrivateKey = try? EOSPrivateKey() else { completion(.error(L10n.Error.Accounts.keyInvalid)) return } Network.accounts.fetch(publicKey: publicKey) { (collection) in if (collection.isEmpty) { completion(.error(L10n.Error.Accounts.empty)) } else { self.requests = collection.count collection.forEach { username in Network.accounts.account(username, completion: { response in DispatchQueue.global().async { guard let accountInfo = response else { completion(.error(L10n.Error.Accounts.empty)) return } let newAccounts = self.prepareAccountInfo(username: username, publicKey: publicKey, keyType: keyType, newPrivateKey: newPrivateKey, accountPermissions: accountInfo.permissions) self.accounts += newAccounts self.requests -= 1 if (self.requests == 0) { DispatchQueue.main.async { completion(nil) } } } }) } } } } private func prepareAccountInfo(username: String, publicKey: String, keyType: Account.KeyType, newPrivateKey: EOSPrivateKey, accountPermissions: [Permission] ) -> [Account.Model.ChangeKey] { var accounts: [Account.Model.ChangeKey] = [] let permissions = accountPermissions.filter { switch keyType { case .active: return $0.permName == Account.KeyType.active.rawValue && $0.requiredAuth.keys.first?.key == publicKey case .owner: return $0.requiredAuth.keys.first?.key == publicKey } } .sorted { $0.permName > $1.permName } permissions.forEach { permission in accounts.append(Account.Model.ChangeKey(username: username, permission: permission, privateKey: newPrivateKey.base58, publicKey: newPrivateKey.publicKey.base58)) } return accounts } func validatePrivateKey(privateKey: String) -> Bool { let privateKey = try? EOSPrivateKey(base58: privateKey) return privateKey != nil } func validatePublicKey(publicKey: String) -> Bool { let publicKey = try? EOSPublicKey(base58: publicKey) return publicKey != nil } func validateKeyPair(privateKey: String, publicKey: String) -> Bool { guard let privateKey = try? EOSPrivateKey(base58: privateKey) else { return false } return privateKey.publicKey.base58 == publicKey } func submit(privateKey: String, accounts: [Account.Model.ChangeKey], password: String, _ completion: @escaping (String?, NSError?) -> Void) { var actions = [(contract: Network.Model.Contract, action: Network.Model.Blockchain.Action, data: Codable)]() for account in accounts { if let data = createChangeKeyData(account: account) { actions.append((contract: .eosio, action: .updateauth, data: data)) } } Network.Service.Blockchain.execute(actions: actions, privateKeys: [privateKey]) { (result) in if case .success(let trxId) = result { Accounts().updateCredentialsIfNeeded(accounts: accounts, password: password) completion(trxId, nil) } else { completion(nil, .error(result)) } } } private func createChangeKeyData(account: Account.Model.ChangeKey) -> Network.Model.Blockchain.UpdateAuth? { var keys: [Network.Model.Blockchain.KeyWeight] = [] guard let eosioPublicKey = (try? Data(eosioPublicKey: account.publicKey).toEosioK1PublicKey) else { return nil } let key = Network.Model.Blockchain.KeyWeight(key: eosioPublicKey, weight: 1) keys.append(key) let auth = Network.Model.Blockchain.Authority(threshold: 1, keys: keys, waits: [], accounts: []) let parent = account.permission.permName == Account.KeyType.active.rawValue ? Account.KeyType.owner.rawValue : "" let permission = account.permission.permName return Network.Model.Blockchain.UpdateAuth(account: account.username, permission: permission, parent: parent, auth: auth) } } }