From d5df1ef823bf7c2e109d602ccde78ab2b46027ca Mon Sep 17 00:00:00 2001 From: ant013 Date: Fri, 21 Apr 2023 14:03:35 +0600 Subject: [PATCH] Update zcash to last verion. Fix and refactoring code. --- .../project.pbxproj | 22 +---- .../Core/Adapters/ZcashAdapter.swift | 96 +++++++++++-------- .../Core/Adapters/ZcashTransactionPool.swift | 70 -------------- .../Core/Managers/MetadataMonitor.swift | 6 +- .../UnstoppableWallet/Models/AppError.swift | 3 +- 5 files changed, 62 insertions(+), 135 deletions(-) diff --git a/UnstoppableWallet/UnstoppableWallet.xcodeproj/project.pbxproj b/UnstoppableWallet/UnstoppableWallet.xcodeproj/project.pbxproj index 269f573b43..ea555457bb 100644 --- a/UnstoppableWallet/UnstoppableWallet.xcodeproj/project.pbxproj +++ b/UnstoppableWallet/UnstoppableWallet.xcodeproj/project.pbxproj @@ -2077,7 +2077,6 @@ D09200C6293F21720091981A /* RestoreNonStandardViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D09200C0293F21710091981A /* RestoreNonStandardViewModel.swift */; }; D09200C8293F21720091981A /* RestoreNonStandardModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D09200C1293F21720091981A /* RestoreNonStandardModule.swift */; }; D09200C9293F21720091981A /* RestoreNonStandardModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D09200C1293F21720091981A /* RestoreNonStandardModule.swift */; }; - D0C87E66298D17D5000CA9BE /* libzcashlc in Frameworks */ = {isa = PBXBuildFile; productRef = D0C87E65298D17D5000CA9BE /* libzcashlc */; }; D0D5BCBC2976CB9F00587FDB /* PasswordInputView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D5BCBB2976CB9F00587FDB /* PasswordInputView.swift */; }; D0D5BCBD2976CB9F00587FDB /* PasswordInputView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D5BCBB2976CB9F00587FDB /* PasswordInputView.swift */; }; D0D5BCC02976D3B300587FDB /* PsswordInputCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D5BCBF2976D3B300587FDB /* PsswordInputCell.swift */; }; @@ -3497,7 +3496,6 @@ D3604E8828F03D9E0066C366 /* DashKit in Frameworks */, D3C187B32907A60800FE1900 /* HdWalletKit in Frameworks */, D3604E5028F02AE70066C366 /* UniswapKit in Frameworks */, - D0C87E66298D17D5000CA9BE /* libzcashlc in Frameworks */, D3604E7328F03B0A0066C366 /* ScanQrKit in Frameworks */, D3BF1E63274CBBCE00229A00 /* DeepDiff in Frameworks */, D3993DA528F4229F008720FB /* ZcashLightClientKit in Frameworks */, @@ -6645,7 +6643,6 @@ D3C187DD290FCFE400FE1900 /* StorageKit */, D339A93C29126D0F00B895BE /* HsCryptoKit */, 6B423FD32913785800EE5E70 /* BitcoinCore */, - D0C87E65298D17D5000CA9BE /* libzcashlc */, 6BDA29AC29D6F384003847ED /* ECashKit */, 6BDA29AF29D6F934003847ED /* HsToolKit */, ); @@ -6727,7 +6724,6 @@ D3C187DC290FCFE400FE1900 /* XCRemoteSwiftPackageReference "StorageKit.Swift" */, D339A93B29126D0E00B895BE /* XCRemoteSwiftPackageReference "HsCryptoKit.Swift" */, 6B423FD22913785800EE5E70 /* XCRemoteSwiftPackageReference "BitcoinCore.Swift" */, - D0C87E64298D17D5000CA9BE /* XCRemoteSwiftPackageReference "zcash-light-client-ffi" */, 6BDA29A929D6EA9B003847ED /* XCRemoteSwiftPackageReference "ECashKit.Swift" */, 6BDA29AE29D6F934003847ED /* XCRemoteSwiftPackageReference "HsToolKit.Swift" */, ); @@ -9294,15 +9290,6 @@ version = 1.0.2; }; }; - D0C87E64298D17D5000CA9BE /* XCRemoteSwiftPackageReference "zcash-light-client-ffi" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/zcash-hackworks/zcash-light-client-ffi"; - requirement = { - kind = versionRange; - maximumVersion = 1.0.0; - minimumVersion = 0.2.0; - }; - }; D0DA740B272A6EFC0072BE86 /* XCRemoteSwiftPackageReference "UnicodeURL" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/nysander/UnicodeURL.git"; @@ -9475,8 +9462,8 @@ isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/zcash/ZcashLightClientKit"; requirement = { - kind = revision; - revision = 690b5aa67f67dfa414f1b4aea07a1bf11fa9687e; + kind = exactVersion; + version = "0.21.0-beta"; }; }; D3993DAA28F42549008720FB /* XCRemoteSwiftPackageReference "WalletConnectSwiftV2" */ = { @@ -9624,11 +9611,6 @@ package = 6BDA29AE29D6F934003847ED /* XCRemoteSwiftPackageReference "HsToolKit.Swift" */; productName = HsToolKit; }; - D0C87E65298D17D5000CA9BE /* libzcashlc */ = { - isa = XCSwiftPackageProductDependency; - package = D0C87E64298D17D5000CA9BE /* XCRemoteSwiftPackageReference "zcash-light-client-ffi" */; - productName = libzcashlc; - }; D0DA740C272A6EFC0072BE86 /* IDNSDK */ = { isa = XCSwiftPackageProductDependency; package = D0DA740B272A6EFC0072BE86 /* XCRemoteSwiftPackageReference "UnicodeURL" */; diff --git a/UnstoppableWallet/UnstoppableWallet/Core/Adapters/ZcashAdapter.swift b/UnstoppableWallet/UnstoppableWallet/Core/Adapters/ZcashAdapter.swift index 01e88ad1e0..bc1a1058a3 100644 --- a/UnstoppableWallet/UnstoppableWallet/Core/Adapters/ZcashAdapter.swift +++ b/UnstoppableWallet/UnstoppableWallet/Core/Adapters/ZcashAdapter.swift @@ -10,6 +10,7 @@ import HsExtensions import Combine class ZcashAdapter { + private static let endPoint = "lightwalletd.electriccoin.co" //"mainnet.lightwalletd.com" private let queue = DispatchQueue(label: "io.horizontalsystems.unstoppable.zcash-adapter", qos: .userInitiated) private let disposeBag = DisposeBag() @@ -28,9 +29,9 @@ class ZcashAdapter { private let uniqueId: String private let seedData: [UInt8] private let birthday: BlockHeight - private let viewingKey: UnifiedFullViewingKey // this being a single account does not need to be an array - private let spendingKey: UnifiedSpendingKey - private let logger: HsToolKit.Logger? + private var viewingKey: UnifiedFullViewingKey? // this being a single account does not need to be an array + private var spendingKey: UnifiedSpendingKey? + private var logger: HsToolKit.Logger? private(set) var network: ZcashNetwork private(set) var fee: Decimal @@ -86,7 +87,7 @@ class ZcashAdapter { } init(wallet: Wallet, restoreSettings: RestoreSettings) throws { - logger = /*App.shared.logger.scoped(with: "ZCashKit")*/HsToolKit.Logger(minLogLevel: .debug)// + logger = App.shared.logger.scoped(with: "ZCashKit") //HsToolKit.Logger(minLogLevel: .debug) guard let seed = wallet.account.type.mnemonicSeed else { throw AdapterError.unsupportedAccount @@ -95,8 +96,6 @@ class ZcashAdapter { network = ZcashNetworkBuilder.network(for: .mainnet) fee = network.constants.defaultFee().decimalValue.decimalValue - let endPoint = "lightwalletd.electriccoin.co" //"mainnet.lightwalletd.com" - token = wallet.token transactionSource = wallet.transactionSource uniqueId = wallet.account.id @@ -115,30 +114,8 @@ class ZcashAdapter { let seedData = [UInt8](seed) self.seedData = seedData - let derivationTool = DerivationTool(networkType: network.networkType) - - guard let unifiedSpendingKey = try? derivationTool.deriveUnifiedSpendingKey(seed: seedData, accountIndex: 0), - let unifiedViewingKey = try? unifiedSpendingKey.deriveFullViewingKey() else { - throw AppError.ZcashError.noReceiveAddress - } - - let initializer = Initializer( - cacheDbURL: nil, - fsBlockDbRoot: try ZcashAdapter.fsBlockDbRootURL(uniqueId: uniqueId, network: network), - dataDbURL: try ZcashAdapter.dataDbURL(uniqueId: uniqueId, network: network), - pendingDbURL: try ZcashAdapter.pendingDbURL(uniqueId: uniqueId, network: network), - endpoint: LightWalletEndpoint(address: endPoint, port: 9067, secure: true, streamingCallTimeoutInMillis: 10 * 60 * 60 * 1000), - network: network, - spendParamsURL: try ZcashAdapter.spendParamsURL(uniqueId: uniqueId), - outputParamsURL: try ZcashAdapter.outputParamsURL(uniqueId: uniqueId), - saplingParamsSourceURL: SaplingParamsSourceURL.default, - alias: .custom(uniqueId), - logLevel: .error - ) - - spendingKey = unifiedSpendingKey - viewingKey = unifiedViewingKey + let initializer = try ZcashAdapter.initializer(network: network, uniqueId: uniqueId) synchronizer = SDKSynchronizer(initializer: initializer) // subscribe on sync states @@ -159,16 +136,27 @@ class ZcashAdapter { NotificationCenter.default.addObserver(self, selector: #selector(didEnterBackground), name: UIApplication.didEnterBackgroundNotification, object: nil) subscribe(disposeBag, saplingDownloader.stateObservable) { [weak self] in self?.downloaderStatusUpdated(state: $0) } - prepare(seedData: seedData, viewingKeys: [unifiedViewingKey], walletBirthday: birthday) + prepare(initializer: initializer, seedData: seedData, walletBirthday: birthday) } - private func prepare(seedData: [UInt8]?, viewingKeys: [UnifiedFullViewingKey], walletBirthday: BlockHeight) { + private func prepare(initializer: Initializer, seedData: [UInt8], walletBirthday: BlockHeight) { preparing = true state = .preparing Task { do { - let result = try await synchronizer.prepare(with: seedData, viewingKeys: viewingKeys, walletBirthday: walletBirthday) + let tool = initializer.makeDerivationTool() + guard let unifiedSpendingKey = try? await tool.deriveUnifiedSpendingKey(seed: seedData, accountIndex: 0), + let unifiedViewingKey = try? await tool.deriveUnifiedFullViewingKey(from: unifiedSpendingKey) else { + + throw AppError.ZcashError.cantCreateKeys + } + + spendingKey = unifiedSpendingKey + viewingKey = unifiedViewingKey + + + let result = try await synchronizer.prepare(with: seedData, viewingKeys: [unifiedViewingKey], walletBirthday: walletBirthday) if case .seedRequired = result { throw AppError.ZcashError.seedRequired } @@ -293,7 +281,7 @@ class ZcashAdapter { case .foundTransactions(let transactions, let inRange): logger?.log(level: .debug, message: "found \(transactions.count) mined txs in range: \(inRange)") transactions.forEach { overview in - logger?.log(level: .debug, message: "tx: \(overview.value.decimalValue.decimalString) : \(overview.fee?.decimalString()) : \(overview.raw?.hs.hex)") + logger?.log(level: .debug, message: "tx: v =\(overview.value.decimalValue.decimalString) : fee = \(overview.fee?.decimalString() ?? "N/A") : height = \(overview.minedHeight?.description ?? "N/A")") } Task { let newTxs = await transactionPool?.sync(transactions: transactions) ?? [] @@ -302,7 +290,7 @@ class ZcashAdapter { }) } case .minedTransaction(let pendingEntity): - logger?.log(level: .debug, message: "found pending tx") + logger?.log(level: .debug, message: "found pending tx: v =\(pendingEntity.value.decimalValue.decimalString) : fee = \(pendingEntity.fee?.decimalString() ?? "N/A")") update(transactions: [pendingEntity]) default: logger?.log(level: .debug, message: "Event: \(event)") @@ -478,9 +466,9 @@ class ZcashAdapter { deinit { NotificationCenter.default.removeObserver(self) - Task { - await synchronizer.stop() - logger?.log(level: .debug, message: "Synchronizer Was Stopped") + Task { [weak self] in + await self?.synchronizer.stop() + self?.logger?.log(level: .debug, message: "Synchronizer Was Stopped") } } @@ -491,6 +479,22 @@ extension ZcashAdapter { BlockHeight.ofLatestCheckpoint(network: network) } + static func initializer(network: ZcashNetwork, uniqueId: String) throws -> Initializer { + Initializer( + cacheDbURL: nil, + fsBlockDbRoot: try fsBlockDbRootURL(uniqueId: uniqueId, network: network), + dataDbURL: try dataDbURL(uniqueId: uniqueId, network: network), + pendingDbURL: try pendingDbURL(uniqueId: uniqueId, network: network), + endpoint: LightWalletEndpoint(address: endPoint, port: 9067, secure: true, streamingCallTimeoutInMillis: 10 * 60 * 60 * 1000), + network: network, + spendParamsURL: try spendParamsURL(uniqueId: uniqueId), + outputParamsURL: try outputParamsURL(uniqueId: uniqueId), + saplingParamsSourceURL: SaplingParamsSourceURL.default, + alias: .custom(uniqueId), + logLevel: .error + ) + } + private static func dataDirectoryUrl() throws -> URL { let fileManager = FileManager.default @@ -553,9 +557,15 @@ extension ZcashAdapter: IAdapter { return } - guard zAddress != nil else { // else we need to try prepare library again + if zAddress == nil { // else we need to try prepare library again logger?.log(level: .debug, message: "No address, try to prepare kit again!") - prepare(seedData: seedData, viewingKeys: [viewingKey], walletBirthday: birthday) + do { + let initializer = try Self.initializer(network: network, uniqueId: uniqueId) + prepare(initializer: initializer, seedData: seedData, walletBirthday: birthday) + } catch { + logger?.log(level: .error, message: "Can't start adapter: \(error.localizedDescription)") + } + return } @@ -619,7 +629,7 @@ extension ZcashAdapter: IAdapter { return """ ZcashAdapter z-address: \(String(describing: zAddress)) - spendingKeys: \(spendingKey.description) + spendingKeys: \(spendingKey?.description ?? "N/A") balanceState: \(balanceState) """ } @@ -729,7 +739,10 @@ extension ZcashAdapter: ISendZcashAdapter { } func sendSingle(amount: Decimal, address: Recipient, memo: Memo?) -> Single<()> { - let spendingKey = spendingKey + guard let spendingKey else { + return .error(AppError.ZcashError.noReceiveAddress) + } + return Single.create { [weak self] observer in guard let self else { observer(.error(AppError.unknownError)) @@ -744,6 +757,7 @@ extension ZcashAdapter: ISendZcashAdapter { memo: memo) self.logger?.log(level: .debug, message: "Successful send TX: \(pendingEntity.createTime) : \(pendingEntity.value.decimalValue.description) : \(pendingEntity.recipient.asString ?? "") : \(pendingEntity.memo?.encodedString ?? "NoMemo")") self.reSyncPending() + observer(.success(())) } catch { observer(.error(error)) } diff --git a/UnstoppableWallet/UnstoppableWallet/Core/Adapters/ZcashTransactionPool.swift b/UnstoppableWallet/UnstoppableWallet/Core/Adapters/ZcashTransactionPool.swift index e1826d3a05..1ca94b2431 100644 --- a/UnstoppableWallet/UnstoppableWallet/Core/Adapters/ZcashTransactionPool.swift +++ b/UnstoppableWallet/UnstoppableWallet/Core/Adapters/ZcashTransactionPool.swift @@ -47,61 +47,12 @@ class ZcashTransactionPool { return wrapped } - private func zcashTransactions(_ transactions: [ZcashTransaction.Sent]) async -> [ZcashTransactionWrapper] { - var wrapped = [ZcashTransactionWrapper]() - for tx in transactions { - if let tx = try? await transactionWithAdditional(tx: tx) { - wrapped.append(tx) - } - } - return wrapped - } - - private func zcashTransactions(_ transactions: [ZcashTransaction.Received]) async -> [ZcashTransactionWrapper] { - var wrapped = [ZcashTransactionWrapper]() - for tx in transactions { - if let tx = try? await transactionWithAdditional(tx: tx) { - wrapped.append(tx) - } - } - return wrapped - } - private func transactionWithAdditional(tx: ZcashTransaction.Overview) async throws -> ZcashTransactionWrapper? { let memos: [Memo] = (try? await synchronizer.getMemos(for: tx)) ?? [] let recipients = await synchronizer.getRecipients(for: tx) - print("OVERVIEW TX: TXID: \(tx.id) \(tx.value.decimalString())") - memos.forEach { memo in - print("Found Memo: \(memo)") - } - recipients.forEach { recipient in - print("Found Recipient: \(recipient)") - } - return ZcashTransactionWrapper(tx: tx, memo: memos.first, recipient: recipients.first) - } - - private func transactionWithAdditional(tx: ZcashTransaction.Sent) async throws -> ZcashTransactionWrapper? { - let memos: [Memo] = (try? await synchronizer.getMemos(for: tx)) ?? [] - let recipients = await synchronizer.getRecipients(for: tx) - print("OVERVIEW TX: TXID: \(tx.id) \(tx.value.decimalString())") - memos.forEach { memo in - print("Found Memo: \(memo)") - } - recipients.forEach { recipient in - print("Found Recipient: \(recipient)") - } return ZcashTransactionWrapper(tx: tx, memo: memos.first, recipient: recipients.first) } - private func transactionWithAdditional(tx: ZcashTransaction.Received) async throws -> ZcashTransactionWrapper? { - let memos: [Memo] = (try? await synchronizer.getMemos(for: tx)) ?? [] - print("OVERVIEW TX: TXID: \(tx.id) \(tx.value.decimalString())") - memos.forEach { memo in - print("Found Memo: \(memo)") - } - return ZcashTransactionWrapper(tx: tx, memo: memos.first, recipient: nil) - } - @discardableResult private func sync(own: inout Set, incoming: [ZcashTransactionWrapper]) -> [ZcashTransactionWrapper] { var newTxs = [ZcashTransactionWrapper]() incoming.forEach { transaction in @@ -115,30 +66,9 @@ class ZcashTransactionPool { func initTransactions() async { let overviews = await synchronizer.clearedTransactions let pending = await synchronizer.pendingTransactions - let sent = await synchronizer.sentTransactions - let received = await synchronizer.receivedTransactions - print("Found | overviews: \(overviews.count) | pending: \(pending.count) | sent: \(sent.count) | received: \(received.count)") pendingTransactions = Set(zcashTransactions(pending)) confirmedTransactions = Set(await zcashTransactions(overviews)) - - let sentWrapped = await zcashTransactions(sent) - let receivedWrapped = await zcashTransactions(received) - - print("===> Overviews:") - confirmedTransactions.forEach { tx in - print("ID: \(tx.id ?? "N/A") | Value: \(tx.value) | Fee: \(tx.fee?.decimalString() ?? "N/A") | Recipient: \(tx.toAddress ?? "N/A")") - } - - print("===> Sent:") - sentWrapped.forEach { tx in - print("ID: \(tx.id ?? "N/A") | Value: \(tx.value) | Fee: \(tx.fee?.decimalString() ?? "N/A") | Recipient: \(tx.toAddress ?? "N/A")") - } - - print("===> received:") - receivedWrapped.forEach { tx in - print("ID: \(tx.id ?? "N/A") | Value: \(tx.value) | Fee: \(tx.fee?.decimalString() ?? "N/A") | Recipient: \(tx.toAddress ?? "N/A")") - } } func sync(transactions: [PendingTransactionEntity]) -> [ZcashTransactionWrapper] { diff --git a/UnstoppableWallet/UnstoppableWallet/Core/Managers/MetadataMonitor.swift b/UnstoppableWallet/UnstoppableWallet/Core/Managers/MetadataMonitor.swift index 88611f5da3..5fdc5cc599 100644 --- a/UnstoppableWallet/UnstoppableWallet/Core/Managers/MetadataMonitor.swift +++ b/UnstoppableWallet/UnstoppableWallet/Core/Managers/MetadataMonitor.swift @@ -119,11 +119,11 @@ class MetadataMonitor { // Get the file URLs, to wait for them below. let urls = results.compactMap { item -> URL? in // check if file really changed in time because query returns 3 times same file - logger?.debug("=> MONITOR : url : \((item.value(forAttribute: NSMetadataItemURLKey) as? URL)?.path)") + logger?.debug("=> MONITOR : url : \((item.value(forAttribute: NSMetadataItemURLKey) as? URL)?.path ?? "N/A")") if let url = item.value(forAttribute: NSMetadataItemURLKey) as? URL { - logger?.debug("=> MONITOR : changeTime : \(item.value(forAttribute: NSMetadataItemFSContentChangeDateKey) as? Date)") + logger?.debug("=> MONITOR : changeTime : \(String(describing: item.value(forAttribute: NSMetadataItemFSContentChangeDateKey) as? Date))") let changeTime = item.value(forAttribute: NSMetadataItemFSContentChangeDateKey) as? Date - logger?.debug("=> MONITOR : lastChangeTime : \(fileChangedTime[url])") + logger?.debug("=> MONITOR : lastChangeTime : \(String(describing: fileChangedTime[url]))") if let changeTime, let lastChangeTime = fileChangedTime[url], changeTime == lastChangeTime { diff --git a/UnstoppableWallet/UnstoppableWallet/Models/AppError.swift b/UnstoppableWallet/UnstoppableWallet/Models/AppError.swift index 3f9ebc8ffe..4036a5955d 100644 --- a/UnstoppableWallet/UnstoppableWallet/Models/AppError.swift +++ b/UnstoppableWallet/UnstoppableWallet/Models/AppError.swift @@ -21,6 +21,7 @@ enum AppError: Error { enum ZcashError: Error { case sendToSelf + case cantCreateKeys case noReceiveAddress case seedRequired } @@ -59,7 +60,7 @@ extension AppError: LocalizedError { switch reason { case .sendToSelf: return "error.send.self_transfer".localized case .noReceiveAddress: return "send.error.invalid_address".localized - case .seedRequired: return "Seed Required" + case .seedRequired, .cantCreateKeys: return "Seed Required" } case .ethereum(let reason): switch reason {