// // MainService.swift // PayCash // // Created by Igor on 05.03.2021. // Copyright © 2021 AM. All rights reserved. // import UIKit import Alamofire import EosioSwift import struct Resolver.Injected import class Resolver.ResolverScope class RuntimeConfiguration { // var serverTime: Date = Date() var serverTimeDiff: Double = 0 var currentTimestamp: Double { Date().timeIntervalSince1970 + serverTimeDiff } var dealsActiveOnly = true } extension Main { class Service { @Injected var runtimeConfiguration: RuntimeConfiguration func loadNodes() async { let nodes = try? await AF.request( Network.servers.current.paycashQraphql, method: .post, parameters: GetEOSNodesGraphQLRequest(), encoder: JSONParameterEncoder.default ) .serializingDecodable(GraphQLResponse.self) .value.data?.getEOSNodes?.nodes.map({ $0.url }) ?? [] if let nodes = nodes, !nodes.isEmpty { // Network.servers.current.nodes = nodes Network.servers.update(nodes: nodes) } } func loadVersion() async -> String? { let results = await withTaskGroup(of: (String, TimeInterval, Int, Int, Int, Int).self) { taskGroup -> [(String, TimeInterval, Int, Int, Int, Int)] in let systemName = await UIDevice.current.systemName for node in Network.servers.current.nodes { taskGroup.addTask { let startTime = Date().timeIntervalSince1970 let result = await Network.Service.Blockchain .queryRaw(endpoint: node, contract: .appSettings, scope: Network.servers.current.contract(.appSettings), table: "build") let timeSpent = Date().timeIntervalSince1970 - startTime print("Node \(node) loaded version in \(timeSpent) seconds") if let rows = result.value, let minBuildVersionString = rows.compactMap({ $0["min_version_build"] as? String }).first, let minBuildVersionJson = (try? JSONSerialization.jsonObject(with: minBuildVersionString.data(using: .utf8) ?? Data(), options: [])) as? [String: Any], let minBuildVersion = minBuildVersionJson[systemName] as? String, let major = Int(minBuildVersion.split(separator: ".").first ?? ""), let minor = Int(minBuildVersion.split(separator: ".").dropFirst().first ?? ""), let patch = Int(minBuildVersion.split(separator: ".").dropFirst().dropFirst().first ?? ""), let build = Int(minBuildVersion.split(separator: ".").dropFirst().dropFirst().dropFirst().first ?? "0") { return (node, timeSpent, major, minor, patch, build) } return (node, 1000, 1000, 1000, 1000, 1000) } } var childTaskResults = [(String, TimeInterval, Int, Int, Int, Int)]() for await result in taskGroup { if !childTaskResults.isEmpty { print("Too late for version from \(result.0) some node was faster \(childTaskResults.sorted(by: { $0.1 < $1.1 }).map({ $0.0 }).joined(separator: ", "))") } childTaskResults.append(result) if result.2 < 1000 { Network.servers.update(nodes: [result.0] + Network.servers.current.nodes.filter({ $0 != result.0 })) return [result] } } return childTaskResults } // let node = results.sorted(by: { $0.1 < $1.1 }) if let result = results.first(where: { $0.2 < 1000 }) { if Bundle.main.major < result.2 { return L10n.Main.wrongVersion } else if Bundle.main.major == result.2 { if Bundle.main.minor < result.3 { return L10n.Main.wrongVersion } else if Bundle.main.minor == result.3 { if Bundle.main.patch < result.4 { return L10n.Main.wrongVersion } else if Bundle.main.patch == result.4 { if Bundle.main.build < result.5 { return L10n.Main.wrongVersion } } } } } else { return L10n.Main.noVersion } return nil } func loadServerTime() { EosioRpcProvider(endpoint: URL(string: Network.servers.current.node)!).getInfo(completion: { [weak self] (result: EosioResult) in switch result { case let .success(response): // self?.runtimeConfiguration.serverTime = response.headBlockTime.asISODate() ?? Date() self?.runtimeConfiguration.serverTimeDiff = (response.headBlockTime.asISODate() ?? Date()).timeIntervalSince1970 - Date().timeIntervalSince1970 case .failure: break } }) } func loadHyperions() { let groupFailed = DispatchGroup() let queue = DispatchQueue(label: "") var success = false for node in Network.servers.current.nodes { groupFailed.enter() let startTime = Date().timeIntervalSince1970 Network.Service.Blockchain.queryRaw(endpoint: node, contract: .appSettings, scope: Network.servers.current.contract(.appSettings), table: "data", closure: { switch $0 { case let .success(rows): DispatchQueue.main.async { print("Node \(node) loaded hyperions in \(Date().timeIntervalSince1970 - startTime) seconds") } queue.async { print(rows) print(rows.compactMap({ $0["endpoint"] as? String })) if rows.compactMap({ $0["endpoint"] as? String }).count > 0 { guard !success else { print("Too late for hyperions from \(node) some node was faster \(Network.servers.current.node)") return } success = true Network.servers.update(hyperions: rows.compactMap({ $0["endpoint"] as? String }).shuffled()) Network.servers.update(nodes: [node] + Network.servers.current.nodes.filter({ $0 != node })) DispatchQueue.main.async { self.loadServerTime() Account.Service.History.shared.fetch() ResolverScope.userSession.reset() Notification.post(name: .didChangeAccount) } } else { groupFailed.leave() } } case let .failure(error): print(error) groupFailed.leave() } }) } groupFailed.notify(queue: .main) { print("All Tasks are failed") Alert.success(text: L10n.Main.noHyperion) } } } }