diff --git a/CHANGELOG.md b/CHANGELOG.md index 27bb9f01..20e7a520 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,29 @@ Possible errors: - `ZcashError.rustProposeTransferFromURI` - Other errors that `sentToAddress` can throw +## Removed + +- `SDKSynchronizer.latestUTXOs` + +## Checkpoints + +Mainnet + +```` +Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/2430000.json +... +Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/2447500.json +```` + + +Testnet + +```` +Sources/ZcashLightClientKit/Resources/checkpoints/testnet/2750000.json +... +Sources/ZcashLightClientKit/Resources/checkpoints/testnet/2770000.json +```` + # 2.0.11 - 2024-03-08 ## Changed diff --git a/Example/ZcashLightClientSample/ZcashLightClientSample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Example/ZcashLightClientSample/ZcashLightClientSample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 15d91fe3..4e776bb1 100644 --- a/Example/ZcashLightClientSample/ZcashLightClientSample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Example/ZcashLightClientSample/ZcashLightClientSample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -176,8 +176,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/zcash-hackworks/zcash-light-client-ffi", "state" : { - "revision" : "7c801be1f445402a433b32835a50d832e8a50437", - "version" : "0.6.0" + "revision" : "c7e5158edf5e62af15492d30237163b78af35ce9", + "version" : "0.7.1" } } ], diff --git a/Package.resolved b/Package.resolved index 6ed2aaaf..cb45488b 100644 --- a/Package.resolved +++ b/Package.resolved @@ -122,8 +122,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/zcash-hackworks/zcash-light-client-ffi", "state" : { - "revision" : "7c801be1f445402a433b32835a50d832e8a50437", - "version" : "0.6.0" + "revision" : "c7e5158edf5e62af15492d30237163b78af35ce9", + "version" : "0.7.1" } } ], diff --git a/Package.swift b/Package.swift index f86b18e9..cf2b1d4c 100644 --- a/Package.swift +++ b/Package.swift @@ -16,7 +16,7 @@ let package = Package( dependencies: [ .package(url: "https://github.com/grpc/grpc-swift.git", from: "1.19.1"), .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.14.1"), - .package(url: "https://github.com/zcash-hackworks/zcash-light-client-ffi", exact: "0.6.0") + .package(url: "https://github.com/zcash-hackworks/zcash-light-client-ffi", exact: "0.7.1") ], targets: [ .target( diff --git a/Sources/ZcashLightClientKit/Block/CompactBlockProcessor.swift b/Sources/ZcashLightClientKit/Block/CompactBlockProcessor.swift index 4eb567ef..81f48917 100644 --- a/Sources/ZcashLightClientKit/Block/CompactBlockProcessor.swift +++ b/Sources/ZcashLightClientKit/Block/CompactBlockProcessor.swift @@ -28,7 +28,6 @@ actor CompactBlockProcessor { private let configProvider: ConfigProvider private var afterSyncHooksManager = AfterSyncHooksManager() - private let accountRepository: AccountRepository var blockDownloaderService: BlockDownloaderService private var latestBlocksDataProvider: LatestBlocksDataProvider private let logger: Logger @@ -142,20 +141,6 @@ actor CompactBlockProcessor { } } - /// Initializes a CompactBlockProcessor instance - /// - Parameters: - /// - service: concrete implementation of `LightWalletService` protocol - /// - storage: concrete implementation of `CompactBlockRepository` protocol - /// - backend: a class that complies to `ZcashRustBackendWelding` - /// - config: `Configuration` struct for this processor - init(container: DIContainer, config: Configuration) { - self.init( - container: container, - config: config, - accountRepository: AccountRepositoryBuilder.build(dataDbURL: config.dataDb, readOnly: true, logger: container.resolve(Logger.self)) - ) - } - /// Initializes a CompactBlockProcessor instance from an Initialized object /// - Parameters: /// - initializer: an instance that complies to CompactBlockDownloading protocol @@ -171,20 +156,23 @@ actor CompactBlockProcessor { saplingParamsSourceURL: initializer.saplingParamsSourceURL, walletBirthdayProvider: walletBirthdayProvider, network: initializer.network - ), - accountRepository: initializer.accountRepository + ) ) } + /// Initializes a CompactBlockProcessor instance + /// - Parameters: + /// - service: concrete implementation of `LightWalletService` protocol + /// - storage: concrete implementation of `CompactBlockRepository` protocol + /// - backend: a class that complies to `ZcashRustBackendWelding` + /// - config: `Configuration` struct for this processor init( container: DIContainer, - config: Configuration, - accountRepository: AccountRepository + config: Configuration ) { Dependencies.setupCompactBlockProcessor( in: container, - config: config, - accountRepository: accountRepository + config: config ) let configProvider = ConfigProvider(config: config) @@ -200,7 +188,6 @@ actor CompactBlockProcessor { self.storage = container.resolve(CompactBlockRepository.self) self.config = config self.transactionRepository = container.resolve(TransactionRepository.self) - self.accountRepository = accountRepository self.fileManager = container.resolve(ZcashFileManager.self) self.configProvider = configProvider } diff --git a/Sources/ZcashLightClientKit/Block/FetchUnspentTxOutputs/UTXOFetcher.swift b/Sources/ZcashLightClientKit/Block/FetchUnspentTxOutputs/UTXOFetcher.swift index bbff3986..f9c51229 100644 --- a/Sources/ZcashLightClientKit/Block/FetchUnspentTxOutputs/UTXOFetcher.swift +++ b/Sources/ZcashLightClientKit/Block/FetchUnspentTxOutputs/UTXOFetcher.swift @@ -23,7 +23,6 @@ protocol UTXOFetcher { } struct UTXOFetcherImpl { - let accountRepository: AccountRepository let blockDownloaderService: BlockDownloaderService let config: UTXOFetcherConfig let rustBackend: ZcashRustBackendWelding @@ -37,8 +36,7 @@ extension UTXOFetcherImpl: UTXOFetcher { ) async throws -> (inserted: [UnspentTransactionOutputEntity], skipped: [UnspentTransactionOutputEntity]) { try Task.checkCancellation() - let accounts = try accountRepository.getAll() - .map { $0.account } + let accounts = try await rustBackend.listAccounts() var tAddresses: [TransparentAddress] = [] for account in accounts { diff --git a/Sources/ZcashLightClientKit/Block/Scan/BlockScanner.swift b/Sources/ZcashLightClientKit/Block/Scan/BlockScanner.swift index cf203f6b..48d38d4d 100644 --- a/Sources/ZcashLightClientKit/Block/Scan/BlockScanner.swift +++ b/Sources/ZcashLightClientKit/Block/Scan/BlockScanner.swift @@ -23,6 +23,7 @@ protocol BlockScanner { struct BlockScannerImpl { let config: BlockScannerConfig let rustBackend: ZcashRustBackendWelding + let service: LightWalletService let transactionRepository: TransactionRepository let metrics: SDKMetrics let logger: Logger @@ -56,7 +57,9 @@ extension BlockScannerImpl: BlockScanner { let scanSummary: ScanSummary let scanStartTime = Date() do { - scanSummary = try await self.rustBackend.scanBlocks(fromHeight: Int32(startHeight), limit: batchSize) + let fromState = try await service.getTreeState(BlockID(height: startHeight - 1)) + + scanSummary = try await self.rustBackend.scanBlocks(fromHeight: Int32(startHeight), fromState: fromState, limit: batchSize) } catch { logger.debug("block scanning failed with error: \(String(describing: error))") throw error diff --git a/Sources/ZcashLightClientKit/DAO/UnspentTransactionOutputDao.swift b/Sources/ZcashLightClientKit/DAO/UnspentTransactionOutputDao.swift index e5561c74..ce3a32c4 100644 --- a/Sources/ZcashLightClientKit/DAO/UnspentTransactionOutputDao.swift +++ b/Sources/ZcashLightClientKit/DAO/UnspentTransactionOutputDao.swift @@ -8,25 +8,12 @@ import Foundation struct UTXO: Decodable, Encodable { - enum CodingKeys: String, CodingKey { - case id = "id_utxo" - case address - case prevoutTxId = "prevout_txid" - case prevoutIndex = "prevout_idx" - case script - case valueZat = "value_zat" - case height - case spentInTx = "spent_in_tx" - } - - let id: Int? let address: String var prevoutTxId: Data var prevoutIndex: Int let script: Data let valueZat: Int let height: Int - let spentInTx: Int? } extension UTXO: UnspentTransactionOutputEntity { @@ -48,145 +35,3 @@ extension UTXO: UnspentTransactionOutputEntity { } } } - -extension UnspentTransactionOutputEntity { - /** - As UTXO, with id and spentIntTx set to __nil__ - */ - func asUTXO() -> UTXO { - UTXO( - id: nil, - address: address, - prevoutTxId: txid, - prevoutIndex: index, - script: script, - valueZat: valueZat, - height: height, - spentInTx: nil - ) - } -} -import SQLite -class UnspentTransactionOutputSQLDAO: UnspentTransactionOutputRepository { - enum TableColumns { - static let id = Expression("id_utxo") - static let address = Expression("address") - static let txid = Expression("prevout_txid") - static let index = Expression("prevout_idx") - static let script = Expression("script") - static let valueZat = Expression("value_zat") - static let height = Expression("height") - static let spentInTx = Expression("spent_in_tx") - } - - let table = Table("utxos") - - let dbProvider: ConnectionProvider - - init(dbProvider: ConnectionProvider) { - self.dbProvider = dbProvider - } - - /// - Throws: `unspentTransactionOutputDAOCreateTable` if creation table fails. - func initialise() async throws { - try await createTableIfNeeded() - } - - private func createTableIfNeeded() async throws { - let stringStatement = - """ - CREATE TABLE IF NOT EXISTS utxos ( - id_utxo INTEGER PRIMARY KEY, - address TEXT NOT NULL, - prevout_txid BLOB NOT NULL, - prevout_idx INTEGER NOT NULL, - script BLOB NOT NULL, - value_zat INTEGER NOT NULL, - height INTEGER NOT NULL, - spent_in_tx INTEGER, - FOREIGN KEY (spent_in_tx) REFERENCES transactions(id_tx), - CONSTRAINT tx_outpoint UNIQUE (prevout_txid, prevout_idx) - ) - """ - do { - globalDBLock.lock() - defer { globalDBLock.unlock() } - - try dbProvider.connection().run(stringStatement) - } catch { - throw ZcashError.unspentTransactionOutputDAOCreateTable(error) - } - } - - /// - Throws: `unspentTransactionOutputDAOStore` if sqlite query fails. - func store(utxos: [UnspentTransactionOutputEntity]) async throws { - do { - globalDBLock.lock() - defer { globalDBLock.unlock() } - - let db = try dbProvider.connection() - try db.transaction { - for utxo in utxos.map({ $0 as? UTXO ?? $0.asUTXO() }) { - try db.run(table.insert(utxo)) - } - } - } catch { - throw ZcashError.unspentTransactionOutputDAOStore(error) - } - } - - /// - Throws: `unspentTransactionOutputDAOClearAll` if sqlite query fails. - func clearAll(address: String?) async throws { - do { - globalDBLock.lock() - defer { globalDBLock.unlock() } - - if let tAddr = address { - try dbProvider.connection().run(table.filter(TableColumns.address == tAddr).delete()) - } else { - try dbProvider.connection().run(table.delete()) - } - } catch { - throw ZcashError.unspentTransactionOutputDAOClearAll(error) - } - } - - /// - Throws: - /// - `unspentTransactionOutputDAOClearAll` if the data fetched from the DB can't be decoded to `UTXO` object. - /// - `unspentTransactionOutputDAOGetAll` if sqlite query fails. - func getAll(address: String?) async throws -> [UnspentTransactionOutputEntity] { - do { - if let tAddress = address { - let allTxs: [UTXO] = try dbProvider.connection() - .prepare(table.filter(TableColumns.address == tAddress)) - .map { row in - do { - return try row.decode() - } catch { - throw ZcashError.unspentTransactionOutputDAOGetAllCantDecode(error) - } - } - return allTxs - } else { - let allTxs: [UTXO] = try dbProvider.connection() - .prepare(table) - .map { row in - try row.decode() - } - return allTxs - } - } catch { - if let error = error as? ZcashError { - throw error - } else { - throw ZcashError.unspentTransactionOutputDAOGetAll(error) - } - } - } -} - -enum UTXORepositoryBuilder { - static func build(initializer: Initializer) -> UnspentTransactionOutputRepository { - return UnspentTransactionOutputSQLDAO(dbProvider: SimpleConnectionProvider(path: initializer.dataDbURL.path)) - } -} diff --git a/Sources/ZcashLightClientKit/Entity/AccountEntity.swift b/Sources/ZcashLightClientKit/Entity/AccountEntity.swift index 32e7c710..17d83742 100644 --- a/Sources/ZcashLightClientKit/Entity/AccountEntity.swift +++ b/Sources/ZcashLightClientKit/Entity/AccountEntity.swift @@ -14,11 +14,6 @@ protocol AccountEntity { } struct DbAccount: AccountEntity, Encodable, Decodable { - enum CodingKeys: String, CodingKey { - case account - case ufvk - } - let account: Int let ufvk: String } @@ -38,171 +33,3 @@ extension DbAccount: Hashable { return true } } - -protocol AccountRepository { - func getAll() throws -> [AccountEntity] - func findBy(account: Int) throws -> AccountEntity? - func update(_ account: AccountEntity) throws -} - -class AccountSQDAO: AccountRepository { - enum TableColums { - static let account = Expression("account") - static let extfvk = Expression("ufvk") - } - - let table = Table("accounts") - - let dbProvider: ConnectionProvider - let logger: Logger - - init(dbProvider: ConnectionProvider, logger: Logger) { - self.dbProvider = dbProvider - self.logger = logger - } - - /// - Throws: - /// - `accountDAOGetAllCantDecode` if account data fetched from the db can't be decoded to the `Account` object. - /// - `accountDAOGetAll` if sqlite query fetching account data failed. - func getAll() throws -> [AccountEntity] { - do { - globalDBLock.lock() - defer { globalDBLock.unlock() } - - return try dbProvider.connection() - .prepare(table) - .map { row -> DbAccount in - do { - return try row.decode() - } catch { - throw ZcashError.accountDAOGetAllCantDecode(error) - } - } - } catch { - if let error = error as? ZcashError { - throw error - } else { - throw ZcashError.accountDAOGetAll(error) - } - } - } - - /// - Throws: - /// - `accountDAOFindByCantDecode` if account data fetched from the db can't be decoded to the `Account` object. - /// - `accountDAOFindBy` if sqlite query fetching account data failed. - func findBy(account: Int) throws -> AccountEntity? { - let query = table.filter(TableColums.account == account).limit(1) - do { - globalDBLock.lock() - defer { globalDBLock.unlock() } - - return try dbProvider.connection() - .prepare(query) - .map { - do { - return try $0.decode() as DbAccount - } catch { - throw ZcashError.accountDAOFindByCantDecode(error) - } - } - .first - } catch { - if let error = error as? ZcashError { - throw error - } else { - throw ZcashError.accountDAOFindBy(error) - } - } - } - - /// - Throws: - /// - `accountDAOUpdate` if sqlite query updating account failed. - /// - `accountDAOUpdatedZeroRows` if sqlite query updating account pass but it affects 0 rows. - func update(_ account: AccountEntity) throws { - guard let acc = account as? DbAccount else { - throw ZcashError.accountDAOUpdateInvalidAccount - } - - let updatedRows: Int - do { - globalDBLock.lock() - defer { globalDBLock.unlock() } - - updatedRows = try dbProvider.connection().run(table.filter(TableColums.account == acc.account).update(acc)) - } catch { - throw ZcashError.accountDAOUpdate(error) - } - - if updatedRows == 0 { - logger.error("attempted to update pending transactions but no rows were updated") - throw ZcashError.accountDAOUpdatedZeroRows - } - } -} - -class CachingAccountDao: AccountRepository { - let dao: AccountRepository - lazy var cache: [Int: AccountEntity] = { - var accountCache: [Int: AccountEntity] = [:] - guard let all = try? dao.getAll() else { - return accountCache - } - - for acc in all { - accountCache[acc.account] = acc - } - - return accountCache - }() - - init(dao: AccountRepository) { - self.dao = dao - } - - func getAll() throws -> [AccountEntity] { - guard cache.isEmpty else { - return cache.values.sorted(by: { $0.account < $1.account }) - } - - let all = try dao.getAll() - - for acc in all { - cache[acc.account] = acc - } - - return all - } - - func findBy(account: Int) throws -> AccountEntity? { - if let acc = cache[account] { - return acc - } - - let acc = try dao.findBy(account: account) - cache[account] = acc - - return acc - } - - func update(_ account: AccountEntity) throws { - try dao.update(account) - } -} - -enum AccountRepositoryBuilder { - static func build(dataDbURL: URL, readOnly: Bool = false, caching: Bool = false, logger: Logger) -> AccountRepository { - if caching { - return CachingAccountDao( - dao: AccountSQDAO( - dbProvider: SimpleConnectionProvider(path: dataDbURL.path, readonly: readOnly), - logger: logger - ) - ) - } else { - return AccountSQDAO( - dbProvider: SimpleConnectionProvider(path: dataDbURL.path, readonly: readOnly), - logger: logger - ) - } - } -} diff --git a/Sources/ZcashLightClientKit/Entity/TransactionEntity.swift b/Sources/ZcashLightClientKit/Entity/TransactionEntity.swift index f52f816c..2a2189b5 100644 --- a/Sources/ZcashLightClientKit/Entity/TransactionEntity.swift +++ b/Sources/ZcashLightClientKit/Entity/TransactionEntity.swift @@ -65,6 +65,7 @@ public enum ZcashTransaction { public enum Pool { case transaparent case sapling + case orchard case other(Int) init(rawValue: Int) { switch rawValue { @@ -72,6 +73,8 @@ public enum ZcashTransaction { self = .transaparent case 2: self = .sapling + case 3: + self = .orchard default: self = .other(rawValue) } @@ -101,8 +104,8 @@ extension ZcashTransaction.Output { static let rawID = Expression("txid") static let pool = Expression("output_pool") static let index = Expression("output_index") - static let toAccount = Expression("to_account") - static let fromAccount = Expression("from_account") + static let toAccount = Expression("to_account_id") + static let fromAccount = Expression("from_account_id") static let toAddress = Expression("to_address") static let value = Expression("value") static let isChange = Expression("is_change") diff --git a/Sources/ZcashLightClientKit/Error/ZcashError.swift b/Sources/ZcashLightClientKit/Error/ZcashError.swift index 069ef7c3..40a48134 100644 --- a/Sources/ZcashLightClientKit/Error/ZcashError.swift +++ b/Sources/ZcashLightClientKit/Error/ZcashError.swift @@ -325,6 +325,14 @@ public enum ZcashError: Equatable, Error { /// - `rustError` contains error generated by the rust layer. /// ZRUST0057 case rustProposeTransferFromURI(_ rustError: String) + /// Error from rust layer when calling ZcashRustBackend. + /// - `rustError` contains error generated by the rust layer. + /// ZRUST0058 + case rustListAccounts(_ rustError: String) + /// Error from rust layer when calling ZcashRustBackend.rustIsSeedRelevantToAnyDerivedAccount + /// - `rustError` contains error generated by the rust layer. + /// ZRUST0059 + case rustIsSeedRelevantToAnyDerivedAccount(_ rustError: String) /// SQLite query failed when fetching all accounts from the database. /// - `sqliteError` is error produced by SQLite library. /// ZADAO0001 @@ -681,6 +689,8 @@ public enum ZcashError: Equatable, Error { case .rustScanProgressOutOfRange: return "Rust layer's call ZcashRustBackend.getScanProgress returned values that after computation are outside of allowed range 0-100%." case .rustGetWalletSummary: return "Error from rust layer when calling ZcashRustBackend.getWalletSummary" case .rustProposeTransferFromURI: return "Error from rust layer when calling ZcashRustBackend." + case .rustListAccounts: return "Error from rust layer when calling ZcashRustBackend." + case .rustIsSeedRelevantToAnyDerivedAccount: return "Error from rust layer when calling ZcashRustBackend.rustIsSeedRelevantToAnyDerivedAccount" case .accountDAOGetAll: return "SQLite query failed when fetching all accounts from the database." case .accountDAOGetAllCantDecode: return "Fetched accounts from SQLite but can't decode them." case .accountDAOFindBy: return "SQLite query failed when seaching for accounts in the database." @@ -856,6 +866,8 @@ public enum ZcashError: Equatable, Error { case .rustScanProgressOutOfRange: return .rustScanProgressOutOfRange case .rustGetWalletSummary: return .rustGetWalletSummary case .rustProposeTransferFromURI: return .rustProposeTransferFromURI + case .rustListAccounts: return .rustListAccounts + case .rustIsSeedRelevantToAnyDerivedAccount: return .rustIsSeedRelevantToAnyDerivedAccount case .accountDAOGetAll: return .accountDAOGetAll case .accountDAOGetAllCantDecode: return .accountDAOGetAllCantDecode case .accountDAOFindBy: return .accountDAOFindBy diff --git a/Sources/ZcashLightClientKit/Error/ZcashErrorCode.swift b/Sources/ZcashLightClientKit/Error/ZcashErrorCode.swift index 8290036a..58203333 100644 --- a/Sources/ZcashLightClientKit/Error/ZcashErrorCode.swift +++ b/Sources/ZcashLightClientKit/Error/ZcashErrorCode.swift @@ -177,6 +177,10 @@ public enum ZcashErrorCode: String { case rustGetWalletSummary = "ZRUST0056" /// Error from rust layer when calling ZcashRustBackend. case rustProposeTransferFromURI = "ZRUST0057" + /// Error from rust layer when calling ZcashRustBackend. + case rustListAccounts = "ZRUST0058" + /// Error from rust layer when calling ZcashRustBackend.rustIsSeedRelevantToAnyDerivedAccount + case rustIsSeedRelevantToAnyDerivedAccount = "ZRUST0059" /// SQLite query failed when fetching all accounts from the database. case accountDAOGetAll = "ZADAO0001" /// Fetched accounts from SQLite but can't decode them. diff --git a/Sources/ZcashLightClientKit/Error/ZcashErrorCodeDefinition.swift b/Sources/ZcashLightClientKit/Error/ZcashErrorCodeDefinition.swift index de2c84b3..a5fde898 100644 --- a/Sources/ZcashLightClientKit/Error/ZcashErrorCodeDefinition.swift +++ b/Sources/ZcashLightClientKit/Error/ZcashErrorCodeDefinition.swift @@ -352,6 +352,14 @@ enum ZcashErrorDefinition { /// - `rustError` contains error generated by the rust layer. // sourcery: code="ZRUST0057" case rustProposeTransferFromURI(_ rustError: String) + /// Error from rust layer when calling ZcashRustBackend. + /// - `rustError` contains error generated by the rust layer. + // sourcery: code="ZRUST0058" + case rustListAccounts(_ rustError: String) + /// Error from rust layer when calling ZcashRustBackend.rustIsSeedRelevantToAnyDerivedAccount + /// - `rustError` contains error generated by the rust layer. + // sourcery: code="ZRUST0059" + case rustIsSeedRelevantToAnyDerivedAccount(_ rustError: String) // MARK: - Account DAO diff --git a/Sources/ZcashLightClientKit/Initializer.swift b/Sources/ZcashLightClientKit/Initializer.swift index db4e3c2a..d8af044b 100644 --- a/Sources/ZcashLightClientKit/Initializer.swift +++ b/Sources/ZcashLightClientKit/Initializer.swift @@ -120,7 +120,6 @@ public class Initializer { let saplingParamsSourceURL: SaplingParamsSourceURL var lightWalletService: LightWalletService let transactionRepository: TransactionRepository - let accountRepository: AccountRepository let storage: CompactBlockRepository var blockDownloaderService: BlockDownloaderService let network: ZcashNetwork @@ -272,12 +271,6 @@ public class Initializer { self.alias = alias self.lightWalletService = container.resolve(LightWalletService.self) self.transactionRepository = container.resolve(TransactionRepository.self) - self.accountRepository = AccountRepositoryBuilder.build( - dataDbURL: urls.dataDbURL, - readOnly: true, - caching: true, - logger: container.resolve(Logger.self) - ) self.storage = container.resolve(CompactBlockRepository.self) self.blockDownloaderService = container.resolve(BlockDownloaderService.self) self.network = network @@ -419,7 +412,7 @@ public class Initializer { self.walletBirthday = checkpoint.height // If there are no accounts it must be created, the default amount of accounts is 1 - if let seed, try accountRepository.getAll().isEmpty { + if let seed, try await rustBackend.listAccounts().isEmpty { var chainTip: UInt32? if walletMode == .restoreWallet { diff --git a/Sources/ZcashLightClientKit/Model/WalletSummary.swift b/Sources/ZcashLightClientKit/Model/WalletSummary.swift index 6f837cea..c0c7a9ed 100644 --- a/Sources/ZcashLightClientKit/Model/WalletSummary.swift +++ b/Sources/ZcashLightClientKit/Model/WalletSummary.swift @@ -21,9 +21,10 @@ public struct PoolBalance: Equatable { public struct AccountBalance: Equatable { public let saplingBalance: PoolBalance + public let orchardBalance: PoolBalance public let unshielded: Zatoshi - static let zero = AccountBalance(saplingBalance: .zero, unshielded: .zero) + static let zero = AccountBalance(saplingBalance: .zero, orchardBalance: .zero, unshielded: .zero) } struct ScanProgress: Equatable { @@ -53,4 +54,5 @@ struct WalletSummary: Equatable { let fullyScannedHeight: BlockHeight let scanProgress: ScanProgress? let nextSaplingSubtreeIndex: UInt32 + let nextOrchardSubtreeIndex: UInt32 } diff --git a/Sources/ZcashLightClientKit/Modules/Service/GRPC/LightWalletGRPCService.swift b/Sources/ZcashLightClientKit/Modules/Service/GRPC/LightWalletGRPCService.swift index c430786e..02d2b534 100644 --- a/Sources/ZcashLightClientKit/Modules/Service/GRPC/LightWalletGRPCService.swift +++ b/Sources/ZcashLightClientKit/Modules/Service/GRPC/LightWalletGRPCService.swift @@ -223,14 +223,12 @@ extension LightWalletGRPCService: LightWalletService { do { guard let reply = try await iterator.next() else { return nil } return UTXO( - id: nil, address: reply.address, prevoutTxId: reply.txid, prevoutIndex: Int(reply.index), script: reply.script, valueZat: Int(reply.valueZat), - height: Int(reply.height), - spentInTx: nil + height: Int(reply.height) ) } catch { let serviceError = error.mapToServiceError() @@ -278,6 +276,10 @@ extension LightWalletGRPCService: LightWalletService { } } + func getTreeState(_ id: BlockID) async throws -> TreeState { + try await compactTxStreamer.getTreeState(id) + } + func closeConnection() { _ = channel.close() } diff --git a/Sources/ZcashLightClientKit/Modules/Service/LightWalletService.swift b/Sources/ZcashLightClientKit/Modules/Service/LightWalletService.swift index b6f3cf60..9680588f 100644 --- a/Sources/ZcashLightClientKit/Modules/Service/LightWalletService.swift +++ b/Sources/ZcashLightClientKit/Modules/Service/LightWalletService.swift @@ -196,4 +196,6 @@ protocol LightWalletService: AnyObject { /// - Parameters: /// - request: Request to send to GetSubtreeRoots. func getSubtreeRoots(_ request: GetSubtreeRootsArg) -> AsyncThrowingStream + + func getTreeState(_ id: BlockID) async throws -> TreeState } diff --git a/Sources/ZcashLightClientKit/Repository/UnspentTransactionOutputRepository.swift b/Sources/ZcashLightClientKit/Repository/UnspentTransactionOutputRepository.swift deleted file mode 100644 index 348a69aa..00000000 --- a/Sources/ZcashLightClientKit/Repository/UnspentTransactionOutputRepository.swift +++ /dev/null @@ -1,15 +0,0 @@ -// -// UnspentTransactionOutputRepository.swift -// ZcashLightClientKit -// -// Created by Francisco Gindre on 12/11/20. -// - -import Foundation - -protocol UnspentTransactionOutputRepository { - func initialise() async throws - func getAll(address: String?) async throws -> [UnspentTransactionOutputEntity] - func store(utxos: [UnspentTransactionOutputEntity]) async throws - func clearAll(address: String?) async throws -} diff --git a/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/2430000.json b/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/2430000.json new file mode 100644 index 00000000..3ef2f6ed --- /dev/null +++ b/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/2430000.json @@ -0,0 +1,8 @@ +{ + "network": "main", + "height": "2430000", + "hash": "0000000000125ca44e9fcf4cc3dcca62ea97fe57675de5f4eefff14019da9c59", + "time": 1710019165, + "saplingTree": "0180dc86263144bf0578faef14fe255918b3daa7f6575e3219a45f84c58aadb565001a00013f49b7b4e8dab0710793531f5b17721583682b01b3ddf268b1a1683fbff52506015523cd9344fe1ef83e00b455846e900a1b5deb413cb6a55103358e5b5294222d00000159eca2abc6624a885838426a0b57bf19334e730f02d2496365c5aac0f81a6c0901ce005d7ec39cdff90a74856266d2a1a299651274faa215522ee152facc7296670000010f496f195cbf0b25829ab6822834a2aff38ef2ddabc4554b3c743c0b4e9277320001330ac2cf864d6286bc980e689309f15471c479d5ce5349e47329459c834f8b0e0178f9d1c5155d10cb72343cdba0308148656de82cacf5665a8d8bd13b7c9a932500000132c525343fc4ebe79ab6515e9d9fceb916d920394bad5926a1afe7f46badef420001d1b36bbba8e6e1be8f09baf2b829bafc4ccd89ad25fb730d2b8a995b60fc3a6701d8ccea507421a590ed38116b834189cdc22421b4764179fa4e364803dfa66d56018cf6f5034a55fed59d1d832620916a43c756d2379e50ef9440fc4c3e7a29aa2300011619f99023a69bb647eab2d2aa1a73c3673c74bb033c3c4930eacda19e6fd93b0000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e", + "orchardTree": "0156a89f3f7f90d5f432cda7fff64aebdf9985682f367ea3487a39a214519cd42a0148de740daab68ea109932f8fa094c0c366fed18b1303a4dd21477a0f319d762e1f0143c1a60d9f30927b79e9cb6c811d5d51bbd35618d126e0af5bef10510c5b541c0131343cbd09085b36a3c659324542f02edbbe0c15c1f112267e075f1d6c57c4200001716145dbe0fc2ba71a8d9ad9441fbff828b681a93f6396be7b07698428c7af1800000100306d1f79c74ca0425b6c4885f58e72a4d696489e05d4ed223cded613f3040c000141b0bc00892aa321cef46ca03e784f6c521834e77944776244b76c3ab566fc2f01c27e5a58efba5a37af4d6b5e9838249f972672372a6b9a85346be924e64e5d1c000133ebf1946b8daef9af45b0105f8ddc87f406da6552ce60cb837e9b6323df8e020001dc8f099e807208a39136acdae660ffd7fa2c9fca740c623f73c152cf2958563d0001d386508c9fabdc60836bfe3c7251fcbdd4617180a804d40fa29dc25fb9c0aa3401cf3bf92f69798e68555548afcce1648add1fb2548d64fa9a1ec22a3e26e7890101e637281deb58dff0c44ba13149b784a95da1b493005efd057e6f4ac20ef5d81d000001cc2dcaa338b312112db04b435a706d63244dd435238f0aa1e9e1598d35470810012dcc4273c8a0ed2337ecf7879380a07e7d427c7f9d82e538002bd1442978402c01daf63debf5b40df902dae98dadc029f281474d190cddecef1b10653248a234150001e2bca6a8d987d668defba89dc082196a922634ed88e065c669e526bb8815ee1b000000000000" +} diff --git a/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/2432500.json b/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/2432500.json new file mode 100644 index 00000000..fdf433e5 --- /dev/null +++ b/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/2432500.json @@ -0,0 +1,8 @@ +{ + "network": "main", + "height": "2432500", + "hash": "00000000006bab4115f085ee76d0db4f993a10be75d234718b13fdff118f592d", + "time": 1710207121, + "saplingTree": "01f801cbf5eb3c278d735c90c3d016d86d781521c85d23f094da5b95bd7f5e7601014efb10c23ae510a46478c2a8effacd37bb695d8e5eb23d7a2b45b406c585e1621a0001c031081d455edcccc55e591c8c2047f59ee6b17fac5d60922a5dab1615e65f41018ea8aac2c80557b07c35844b0a5f994511975dde8ae2a93a78799f2d5bb01f0a01d777f1bb4e1c42d6888a983d8443c203d9d91c193c391e7b18ce15161a34ba5c0001954cc72390c04c20e0f4b6d9af7f370fc7341985f60f95d6bc894ee5bdea8572019f85951f4496c4a96844f9de83606be8a9aa09c0d5f26aa1393a98505398fc4200000001592739b2ff134389decac775f21d3aa2a0d67168e3a45add204fb70e25115d6201330ac2cf864d6286bc980e689309f15471c479d5ce5349e47329459c834f8b0e0178f9d1c5155d10cb72343cdba0308148656de82cacf5665a8d8bd13b7c9a932500000132c525343fc4ebe79ab6515e9d9fceb916d920394bad5926a1afe7f46badef420001d1b36bbba8e6e1be8f09baf2b829bafc4ccd89ad25fb730d2b8a995b60fc3a6701d8ccea507421a590ed38116b834189cdc22421b4764179fa4e364803dfa66d56018cf6f5034a55fed59d1d832620916a43c756d2379e50ef9440fc4c3e7a29aa2300011619f99023a69bb647eab2d2aa1a73c3673c74bb033c3c4930eacda19e6fd93b0000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e", + "orchardTree": "0131bcbe72d425937ecf12a1b1422712c41b7db05ce31d628055c60fad4a3cba19001f0000000001b5d9ddd00c0a0e0152884719008b94484a34e23596e12fe227941268eb1a5f2001614a42b07e005bb98d37927ad44a63b8683ed4dccb9ff5953ff1c8d356598a2a01c9e9f49555ca08e75c03b3c8be117042bfbd04642437c01ce740428dead53c2d012661350c44f41f1adf0ee682fbda495de5d5b228cb063acd360d9ffe18188b390000019b11e54b92195de900cea1584a17b7238cabdb2605c04202ff900405eab301220133ebf1946b8daef9af45b0105f8ddc87f406da6552ce60cb837e9b6323df8e020001dc8f099e807208a39136acdae660ffd7fa2c9fca740c623f73c152cf2958563d0001d386508c9fabdc60836bfe3c7251fcbdd4617180a804d40fa29dc25fb9c0aa3401cf3bf92f69798e68555548afcce1648add1fb2548d64fa9a1ec22a3e26e7890101e637281deb58dff0c44ba13149b784a95da1b493005efd057e6f4ac20ef5d81d000001cc2dcaa338b312112db04b435a706d63244dd435238f0aa1e9e1598d35470810012dcc4273c8a0ed2337ecf7879380a07e7d427c7f9d82e538002bd1442978402c01daf63debf5b40df902dae98dadc029f281474d190cddecef1b10653248a234150001e2bca6a8d987d668defba89dc082196a922634ed88e065c669e526bb8815ee1b000000000000" +} diff --git a/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/2435000.json b/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/2435000.json new file mode 100644 index 00000000..e2d15ee1 --- /dev/null +++ b/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/2435000.json @@ -0,0 +1,8 @@ +{ + "network": "main", + "height": "2435000", + "hash": "00000000009bd2c651d8ae1c17d68044331d1fc31706e319653a6987f7ebf6ec", + "time": 1710395535, + "saplingTree": "0173b53d084813832ff9f2bb94af02621366d65cf5cb6199b7efc4bb37a4f9b72d014502f1327e3618b1358faf277fb1ceae01350ac12decaeba47150536cc04a3641a01e5e8fa0ca6544b3e2c6963efa86ad4013c8bff3e534a8f2c18fac7e8fcc643640103bde549304b260340bac0123c6defc3c8038e23e54161803ff10e7bfbf2e523013a7b35839d4c73f402370a538ef270a3a560ae93171f7395c65e6fa39381254901a549da8bea62adb1667f4ff4c67d0563d32e31b66f261430bd743952c126626e01026de477715a88c4129cddd86ef5c2c08b644a604a3aa6bcf5a3a1b4d15d2f4f019152f8070c760fbeadfbd65c5123aacd3f6d5cf8e4d2a127b4689d2085b714460188fd1ca704f246922548a7fe0a31ea0d9fc59a8a1d65775173dd226dc89f336a000001c2fdb92d6aa7abfa3412e7e064391ac65080907fe6e6277b48c290f58825da3a01592739b2ff134389decac775f21d3aa2a0d67168e3a45add204fb70e25115d6201330ac2cf864d6286bc980e689309f15471c479d5ce5349e47329459c834f8b0e0178f9d1c5155d10cb72343cdba0308148656de82cacf5665a8d8bd13b7c9a932500000132c525343fc4ebe79ab6515e9d9fceb916d920394bad5926a1afe7f46badef420001d1b36bbba8e6e1be8f09baf2b829bafc4ccd89ad25fb730d2b8a995b60fc3a6701d8ccea507421a590ed38116b834189cdc22421b4764179fa4e364803dfa66d56018cf6f5034a55fed59d1d832620916a43c756d2379e50ef9440fc4c3e7a29aa2300011619f99023a69bb647eab2d2aa1a73c3673c74bb033c3c4930eacda19e6fd93b0000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e", + "orchardTree": "012106c64514bac7ec475a8a3d956d1b252914cfd5b8e9a7e9a31e45c7b3706a38001f013875f1718df2e5825687dbd3c34e42506c4229032859a54404089bee053bfe37016fa782e38939d93fbbd1ec50d5e01e2381fbdc7ea453cd1aee635d62c40e8b200001e405453d111dc46d380c5ad29b8e28949f56a0f6c9e78265816441f392fac9230001bf291a84889c2f239a02cc81fc4851dad66d2c05f078fa3dfabf8bde4c0e840900015820a68951f0c8c823490b8fff96bf4964a2e0a87a823edcbadce0086480cd3800019b71db011e11c95751c9a7c91bf62b9a92f665e3067924281647a93b614c7327019b11e54b92195de900cea1584a17b7238cabdb2605c04202ff900405eab301220133ebf1946b8daef9af45b0105f8ddc87f406da6552ce60cb837e9b6323df8e020001dc8f099e807208a39136acdae660ffd7fa2c9fca740c623f73c152cf2958563d0001d386508c9fabdc60836bfe3c7251fcbdd4617180a804d40fa29dc25fb9c0aa3401cf3bf92f69798e68555548afcce1648add1fb2548d64fa9a1ec22a3e26e7890101e637281deb58dff0c44ba13149b784a95da1b493005efd057e6f4ac20ef5d81d000001cc2dcaa338b312112db04b435a706d63244dd435238f0aa1e9e1598d35470810012dcc4273c8a0ed2337ecf7879380a07e7d427c7f9d82e538002bd1442978402c01daf63debf5b40df902dae98dadc029f281474d190cddecef1b10653248a234150001e2bca6a8d987d668defba89dc082196a922634ed88e065c669e526bb8815ee1b000000000000" +} diff --git a/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/2437500.json b/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/2437500.json new file mode 100644 index 00000000..e13879ae --- /dev/null +++ b/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/2437500.json @@ -0,0 +1,8 @@ +{ + "network": "main", + "height": "2437500", + "hash": "0000000001660722f6b67144e7f46c6839bcf186fd271e9d8a2c9ee52c507141", + "time": 1710584940, + "saplingTree": "01d933677fe8d163bdd3d862ac9630ec9767a9a8df71720d565bd6cba48f2d8d5d011e1c64cc7ce901d49d02585d9321611cfc7491a8d3922e6414b57fb72abd20421a01414c13577c3343a61ba87c0cd147a813aa802505e9ebf69260a7d2d445498c540000010cd4f39dfe5d870f200d8170677fa72cd61589eaf27c9a3f643de0e5f17cee0d016c53ad3a61d97ec6459d1ccdfd305bb0954e6b5242b52579f4c58b3a448f380301384a3a811c1f78c671187e2124e06a0f3f3aa35f725917d92b9b3b557621e72e01ebc8afd1ee40675e5f7390e7943d196d7d4157185c888ef6c01f2d8b178a777000000000000001e3644c42c5d7ecd832a2c662dcd397824930ebe37b37bc61ec5af84f724c2571000132c525343fc4ebe79ab6515e9d9fceb916d920394bad5926a1afe7f46badef420001d1b36bbba8e6e1be8f09baf2b829bafc4ccd89ad25fb730d2b8a995b60fc3a6701d8ccea507421a590ed38116b834189cdc22421b4764179fa4e364803dfa66d56018cf6f5034a55fed59d1d832620916a43c756d2379e50ef9440fc4c3e7a29aa2300011619f99023a69bb647eab2d2aa1a73c3673c74bb033c3c4930eacda19e6fd93b0000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e", + "orchardTree": "010f988985056887ac24576fb86acddff6c668c69f72160aaeebc5bbfd8fc3f60c01dbdd7a0fea320e6902a517af8e719befd858e8aab433275f72ff8099dfaa09141f0000017d84bd6e8417ae9c490b51635ee59175611d6223b530ed8b4cc301bb836e4b35014b6da84ed7616c286ae41932575be5a18b22c4a650376426a4a3c450b13d542801786e7a1e8da6e69955ce02a79a121354eb11530ae5fb40b4340e17ce0ea5093d0000000000000001b317414af14283ac9ab63130ea6700eda06ec5f0702aa83a4b168a0e4eba773d01dc8f099e807208a39136acdae660ffd7fa2c9fca740c623f73c152cf2958563d0001d386508c9fabdc60836bfe3c7251fcbdd4617180a804d40fa29dc25fb9c0aa3401cf3bf92f69798e68555548afcce1648add1fb2548d64fa9a1ec22a3e26e7890101e637281deb58dff0c44ba13149b784a95da1b493005efd057e6f4ac20ef5d81d000001cc2dcaa338b312112db04b435a706d63244dd435238f0aa1e9e1598d35470810012dcc4273c8a0ed2337ecf7879380a07e7d427c7f9d82e538002bd1442978402c01daf63debf5b40df902dae98dadc029f281474d190cddecef1b10653248a234150001e2bca6a8d987d668defba89dc082196a922634ed88e065c669e526bb8815ee1b000000000000" +} diff --git a/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/2440000.json b/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/2440000.json new file mode 100644 index 00000000..c058a89a --- /dev/null +++ b/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/2440000.json @@ -0,0 +1,8 @@ +{ + "network": "main", + "height": "2440000", + "hash": "00000000018aa1792888e7f52aaa4a491f9fbc8b2b3cbf2f7c61bce55925fe1a", + "time": 1710773380, + "saplingTree": "01fb1ad6730d051bdce7784d776ffff105824535c01bf4542d76b924198a69704a011f5c0e0f6e2c9bc44c22261c15dec4ef3810faf7491769a8e055a5000278650f1a015df95e48fc0ad9928390fd6c3ae09902c837fbaee45b4c603b88f67fdb5d962a0001d3918aa18fa1e5d1d7fd19f19ad8439d724c4d5c19a980c7761c688a3289810201494d3878deb5d888ceb5686851f72473527e8f8e4ce04fcbeae9df0c02d90301000000000001686eb402fba0131374aefd1da61e310a7b378c7b3085c0216234b0c9073fa32c00000001e3644c42c5d7ecd832a2c662dcd397824930ebe37b37bc61ec5af84f724c2571000132c525343fc4ebe79ab6515e9d9fceb916d920394bad5926a1afe7f46badef420001d1b36bbba8e6e1be8f09baf2b829bafc4ccd89ad25fb730d2b8a995b60fc3a6701d8ccea507421a590ed38116b834189cdc22421b4764179fa4e364803dfa66d56018cf6f5034a55fed59d1d832620916a43c756d2379e50ef9440fc4c3e7a29aa2300011619f99023a69bb647eab2d2aa1a73c3673c74bb033c3c4930eacda19e6fd93b0000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e", + "orchardTree": "0147a6f9d105380685acaf06121f4a4e73255cf306c7489f47e4637d4bce0e350201d6de40cf0031826655d3f106cd06b0ecf624d37040e38ec3c06fbd55547fda251f00012b8bc0c564cd98f599d12a041ef1d914cbe56d9d9208485d5ebe9ccb580bc60201072a949f5f31122af6b80e1e62396506d6a5cbabf08345481e221e55ad05f23b0000013a8bd26f29c49b7c8f0aa02ef6f95284b145e1b281eaf357e0042f83a0430624000163efdc4ca36aec225b76e1a60a69faf152d119f78afc277979696b91ab1ec62d012d2d92046864657193f8957b2a17382634c4197cd6de95d316e721260c99193300000001b317414af14283ac9ab63130ea6700eda06ec5f0702aa83a4b168a0e4eba773d01dc8f099e807208a39136acdae660ffd7fa2c9fca740c623f73c152cf2958563d0001d386508c9fabdc60836bfe3c7251fcbdd4617180a804d40fa29dc25fb9c0aa3401cf3bf92f69798e68555548afcce1648add1fb2548d64fa9a1ec22a3e26e7890101e637281deb58dff0c44ba13149b784a95da1b493005efd057e6f4ac20ef5d81d000001cc2dcaa338b312112db04b435a706d63244dd435238f0aa1e9e1598d35470810012dcc4273c8a0ed2337ecf7879380a07e7d427c7f9d82e538002bd1442978402c01daf63debf5b40df902dae98dadc029f281474d190cddecef1b10653248a234150001e2bca6a8d987d668defba89dc082196a922634ed88e065c669e526bb8815ee1b000000000000" +} diff --git a/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/2442500.json b/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/2442500.json new file mode 100644 index 00000000..1f24f7f5 --- /dev/null +++ b/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/2442500.json @@ -0,0 +1,8 @@ +{ + "network": "main", + "height": "2442500", + "hash": "0000000000e01a9fdf001f3e7ce37f97297dfda65565cee2a5647e1dabf6e58d", + "time": 1710961233, + "saplingTree": "013c37d2664456cbe5c5e48c64dfea53f515ecfa2348b8f8d059f18fb2b622d5200126cc3c024b182b258d5ac3f1bb6c8047ff3d21a5f90d757e29badd8b9e2e120c1a000187b7910243390d4a57950e3726c20cc6196c9f3de6cc35ddeb05cf3a91b3b22300010d5f518de07315aa39a8f19c8ec857fc701f1b1d036acb57080868e4d23b94190001096a541cee2d3c64952753d284a757ce67d4ca13ac9b66d89d046c4b62d0e91a01d9ce6bbf0494ab1efa765d32e6f0431e6aded3fa205070aa19531aa7d1bffa06017190666f6837d3449d345f4077c8e6aa5b53a0e0c52c002f1047215e98d269060145acf941b65c7966c03a210db51db76756886d6f3c6671b9ed2df37db53ab74701686eb402fba0131374aefd1da61e310a7b378c7b3085c0216234b0c9073fa32c00000001e3644c42c5d7ecd832a2c662dcd397824930ebe37b37bc61ec5af84f724c2571000132c525343fc4ebe79ab6515e9d9fceb916d920394bad5926a1afe7f46badef420001d1b36bbba8e6e1be8f09baf2b829bafc4ccd89ad25fb730d2b8a995b60fc3a6701d8ccea507421a590ed38116b834189cdc22421b4764179fa4e364803dfa66d56018cf6f5034a55fed59d1d832620916a43c756d2379e50ef9440fc4c3e7a29aa2300011619f99023a69bb647eab2d2aa1a73c3673c74bb033c3c4930eacda19e6fd93b0000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e", + "orchardTree": "01a27c9df6e1009152c5db2fffc3856f7c8cadb89a09f0833c96c1dfa2fc99cf08015ec5a59c4dff47ede70f4cbfc27d7227ee6c0938d8443c8306f64a3df13d56311f01f62d858afe1f4d37fa975b07789c65b1ef16e67cb2d4301990d190fd05da4e3d000198f59f323fdb55dc69ce636f9055de6cf4d0e6110343f747455c2cb9e417563b0001091ca87e38804d889c942f751df05c73c66c898cdf612cd32fc1d0924acf8113000107d607af6e0d90e878cab9c443ccc3c2ffb2d1787ff4a98cf393cb41bf4314330001636f6be10fb282255101364ff07e3d36455ca24a681654340b185edfb3d5f92f019b57450e5ceaa159777d6ba338f6932d560e5e1d0fb7e0b9f6851e4aeff6d220000001b317414af14283ac9ab63130ea6700eda06ec5f0702aa83a4b168a0e4eba773d01dc8f099e807208a39136acdae660ffd7fa2c9fca740c623f73c152cf2958563d0001d386508c9fabdc60836bfe3c7251fcbdd4617180a804d40fa29dc25fb9c0aa3401cf3bf92f69798e68555548afcce1648add1fb2548d64fa9a1ec22a3e26e7890101e637281deb58dff0c44ba13149b784a95da1b493005efd057e6f4ac20ef5d81d000001cc2dcaa338b312112db04b435a706d63244dd435238f0aa1e9e1598d35470810012dcc4273c8a0ed2337ecf7879380a07e7d427c7f9d82e538002bd1442978402c01daf63debf5b40df902dae98dadc029f281474d190cddecef1b10653248a234150001e2bca6a8d987d668defba89dc082196a922634ed88e065c669e526bb8815ee1b000000000000" +} diff --git a/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/2445000.json b/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/2445000.json new file mode 100644 index 00000000..55de725e --- /dev/null +++ b/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/2445000.json @@ -0,0 +1,8 @@ +{ + "network": "main", + "height": "2445000", + "hash": "000000000032ba3efc3e8c1c10a6dad5ddc4db01889b5cf8c3b83287c00bc2ae", + "time": 1711150054, + "saplingTree": "01efe79fb76d25d5b8aa76362acfa7adc18c52768681b30bffa8957d7dd1952d17001a00000000000001809e9ee7a27e8dcca3be914d59ae9c6aa569381ab12ec681cd94755169b83a2f01c1e8caa49f92f0b9998e63055056856ed7c243f3c4b01d079a7c400e73dbee6e016a4e2d8a2667d5f784a6ad1837e0c294884b4277daa35487510c95236aee69070001661a4760490eaebe48d77a020c789562c029750e16c01cfd195b960a1d7f9248000001e3644c42c5d7ecd832a2c662dcd397824930ebe37b37bc61ec5af84f724c2571000132c525343fc4ebe79ab6515e9d9fceb916d920394bad5926a1afe7f46badef420001d1b36bbba8e6e1be8f09baf2b829bafc4ccd89ad25fb730d2b8a995b60fc3a6701d8ccea507421a590ed38116b834189cdc22421b4764179fa4e364803dfa66d56018cf6f5034a55fed59d1d832620916a43c756d2379e50ef9440fc4c3e7a29aa2300011619f99023a69bb647eab2d2aa1a73c3673c74bb033c3c4930eacda19e6fd93b0000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e", + "orchardTree": "01e6226f66172d9b52de8f3efd1722cf3d9de4878df0ca0230ddf95f87e1e5ae31001f0001c949193b4d40d7b25181b2885dbb0ee41bf0bc53b37ac2f1cbddd6e00ceca4100001c2220a8a49fdb47edadd405a43039ca69c9158fde74ccbb5c6838464cc563b260001f46913c82ab9854fbb2a2207cca3e397a8f23903c0be58e3d26536e1b08b110b01c12cf5c3940490167a7753ede643275e47e137902c9287b1be84ca962da9f310010b4d4cb8f25e5ac4228aaa6b84ca38ce7e1563c26a594bcc7944794bce41fa2e000001818f8b592633baf0d32d20b6bdf8fc1f0383a20c95f949fdf871466c288789140001b317414af14283ac9ab63130ea6700eda06ec5f0702aa83a4b168a0e4eba773d01dc8f099e807208a39136acdae660ffd7fa2c9fca740c623f73c152cf2958563d0001d386508c9fabdc60836bfe3c7251fcbdd4617180a804d40fa29dc25fb9c0aa3401cf3bf92f69798e68555548afcce1648add1fb2548d64fa9a1ec22a3e26e7890101e637281deb58dff0c44ba13149b784a95da1b493005efd057e6f4ac20ef5d81d000001cc2dcaa338b312112db04b435a706d63244dd435238f0aa1e9e1598d35470810012dcc4273c8a0ed2337ecf7879380a07e7d427c7f9d82e538002bd1442978402c01daf63debf5b40df902dae98dadc029f281474d190cddecef1b10653248a234150001e2bca6a8d987d668defba89dc082196a922634ed88e065c669e526bb8815ee1b000000000000" +} diff --git a/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/2447500.json b/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/2447500.json new file mode 100644 index 00000000..36bdc8c9 --- /dev/null +++ b/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/2447500.json @@ -0,0 +1,8 @@ +{ + "network": "main", + "height": "2447500", + "hash": "00000000012db8b5bdd6c44c6fbeeee7b24bbb7f14a214cbf678510a59cadf3f", + "time": 1711338099, + "saplingTree": "0121c86d5e6ffa93ef5a3734a836b560782a6e700129e8b9feb9052042fb87be2a012f9ac3bdebbbe0387ec38dba15faeabac24e6d136a5f2d8f8a9a811c596adc3c1a0001d876c965043694af1e4e8413d8aeea1e53517b3959796eb93b3dcfdba2b60168018dd52ff0b5454c74f69c84cf4ef73f2370811ff361cdf3f170f3700f0c3d110e0180955ea26df3668f83bac3a8f2961db643070703421e9132385c8ad43a9ea2460000010f6dbc8b6394ebb5950a4fd6b5796771ce2f6ed93aee9c44b0066cb994a137560001895d803e6d1a96ffed6e66c8d5b1f9611b330bcf3d8c77fc828619f239236610010ebeeae59b247f37eeb6aa57dc2e52f4b965c69864809b1f6cafcd0534d0221601661a4760490eaebe48d77a020c789562c029750e16c01cfd195b960a1d7f9248000001e3644c42c5d7ecd832a2c662dcd397824930ebe37b37bc61ec5af84f724c2571000132c525343fc4ebe79ab6515e9d9fceb916d920394bad5926a1afe7f46badef420001d1b36bbba8e6e1be8f09baf2b829bafc4ccd89ad25fb730d2b8a995b60fc3a6701d8ccea507421a590ed38116b834189cdc22421b4764179fa4e364803dfa66d56018cf6f5034a55fed59d1d832620916a43c756d2379e50ef9440fc4c3e7a29aa2300011619f99023a69bb647eab2d2aa1a73c3673c74bb033c3c4930eacda19e6fd93b0000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e", + "orchardTree": "019a450901c143c43ea51cdbcf8b6de8b5a8d05f6d55845cb5f15d50663f819217015be7f0cffa838a09fa378bf699d218fc7674dc7bd99a525261adca4bfe9e002e1f00010a7ae37530c3b644773eebafaf8a9a15e46d7bbeed3e902b084529978976e53900017baad349203e05ed63da22471b555136cfd8bed26590ac9f7826442c568bc923000000012288799495392e86b155ddd18069b8eafb2402a33af2a1b5b16093a0c48bfa2d0001c81341a707a972d0f388802e56c99c58257eae60b6dbd44b537e70d097579a3301818f8b592633baf0d32d20b6bdf8fc1f0383a20c95f949fdf871466c288789140001b317414af14283ac9ab63130ea6700eda06ec5f0702aa83a4b168a0e4eba773d01dc8f099e807208a39136acdae660ffd7fa2c9fca740c623f73c152cf2958563d0001d386508c9fabdc60836bfe3c7251fcbdd4617180a804d40fa29dc25fb9c0aa3401cf3bf92f69798e68555548afcce1648add1fb2548d64fa9a1ec22a3e26e7890101e637281deb58dff0c44ba13149b784a95da1b493005efd057e6f4ac20ef5d81d000001cc2dcaa338b312112db04b435a706d63244dd435238f0aa1e9e1598d35470810012dcc4273c8a0ed2337ecf7879380a07e7d427c7f9d82e538002bd1442978402c01daf63debf5b40df902dae98dadc029f281474d190cddecef1b10653248a234150001e2bca6a8d987d668defba89dc082196a922634ed88e065c669e526bb8815ee1b000000000000" +} diff --git a/Sources/ZcashLightClientKit/Resources/checkpoints/testnet/2750000.json b/Sources/ZcashLightClientKit/Resources/checkpoints/testnet/2750000.json new file mode 100644 index 00000000..1791b006 --- /dev/null +++ b/Sources/ZcashLightClientKit/Resources/checkpoints/testnet/2750000.json @@ -0,0 +1,8 @@ +{ + "network": "test", + "height": "2750000", + "hash": "0032ee34dff008317a8b7078198e3e62a30d41b041bc821c07f2481d93fbcb59", + "time": 1710015877, + "saplingTree": "01dd6271359272245882abb3e2bb9d5500d92153adcff74977dcd8d00b38bb280c01cd5d69cb79696e6183abb1884a64aeecbdc6636daed5760b0c3dbb8239c8616e100000000000016dd9892b9c2e5daf14b579e607b515374d5b29f44bfd1eda0cc44902e7ce4d11016526e08f5ea3f5e89c8434a0b0249642b86354dc00c32f0c104e802a0e0eaf6000000001447d6b9100cddd5f80c8cf4ddee2b87eba053bd987465aec2293bd0514e68b0d015f6c95e75f4601a0a31670a7deb970fc8988c611685161d2e1629d0a1a0ebd07015f8b9205e0514fa235d75c150b87e23866b882b39786852d1ab42aab11d31a4a0117ddeb3a5f8d2f6b2d0a07f28f01ab25e03a05a9319275bb86d72fcaef6fc01501f08f39275112dd8905b854170b7f247cf2df18454d4fa94e6e4f9320cca05f24011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39", + "orchardTree": "01e8865e5c0fec206805c50225066d28461cec4791744c8a4269fff48caf8c563f015f41071dacad598328cdb0603154ed656b9ec65d00295cf93afc6a392c666f241f0171152098d19f906049dfd98c777a5ccd5f9d3f8b80d4ced4dac79d125a0a3e13013488aaf56dde9d60d789c8bfc346baf09cd209070ded9d15f2d455cc86c44b040000000001fcb318566b2a3c4812c1ac1ffeb99226c2c653094bedf9608cf409ffc54e18200172316ba203fdc9df151f6101a287f203befcd8bdecac5cf43a2f421d6a467f080000011e6191f91b3fceb62dc881a156e1b9d2e88e09dca25093cf9c4936c8869fb41a013bf8b923e4187754e85175748d9cce4824a6787e4258977b5bfe1ba59012c032000001f3bbdc62260c4fca5c84bf3487246d4542da48eeeec8ec40c1029b6908eef83c00000000000000000000000000000000" +} diff --git a/Sources/ZcashLightClientKit/Resources/checkpoints/testnet/2760000.json b/Sources/ZcashLightClientKit/Resources/checkpoints/testnet/2760000.json new file mode 100644 index 00000000..02b4797e --- /dev/null +++ b/Sources/ZcashLightClientKit/Resources/checkpoints/testnet/2760000.json @@ -0,0 +1,8 @@ +{ + "network": "test", + "height": "2760000", + "hash": "001e7783656e6d9801bbd49483da9798b321626ebdf606993a5c68b40f2e636f", + "time": 1710643466, + "saplingTree": "0137552d8794f50ca3e6d8c04b79641c61531137a36e0c16797c7b4042dcfb2922001001efd0586745505fa5c1358a4976f0511cb08d32f808ce45e0874ce84bc03767010001b73c4685a80e900cc7f3f31b0808ae5e31f744a934bf530870f762daaf55a6440114fdc3f78cb10a1daa80e89f40d4834e7964e7fbe0f12cd3d346a23739d3eb2600016dd9892b9c2e5daf14b579e607b515374d5b29f44bfd1eda0cc44902e7ce4d11016526e08f5ea3f5e89c8434a0b0249642b86354dc00c32f0c104e802a0e0eaf6000000001447d6b9100cddd5f80c8cf4ddee2b87eba053bd987465aec2293bd0514e68b0d015f6c95e75f4601a0a31670a7deb970fc8988c611685161d2e1629d0a1a0ebd07015f8b9205e0514fa235d75c150b87e23866b882b39786852d1ab42aab11d31a4a0117ddeb3a5f8d2f6b2d0a07f28f01ab25e03a05a9319275bb86d72fcaef6fc01501f08f39275112dd8905b854170b7f247cf2df18454d4fa94e6e4f9320cca05f24011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39", + "orchardTree": "01def56c50751a5c5fbdc117450890b01c1bf37fa05daa9e1d318a09213080b70201980479154da09ad5cdd7b184a66b89cb544c9b46d83cfa4e9da11a5e0d935d0a1f000000000001061089b21464cd2d65ac65d509f398014d89f00a996ec0807a1da1f2ac88cc0d01fcb318566b2a3c4812c1ac1ffeb99226c2c653094bedf9608cf409ffc54e18200172316ba203fdc9df151f6101a287f203befcd8bdecac5cf43a2f421d6a467f080000011e6191f91b3fceb62dc881a156e1b9d2e88e09dca25093cf9c4936c8869fb41a013bf8b923e4187754e85175748d9cce4824a6787e4258977b5bfe1ba59012c032000001f3bbdc62260c4fca5c84bf3487246d4542da48eeeec8ec40c1029b6908eef83c00000000000000000000000000000000" +} diff --git a/Sources/ZcashLightClientKit/Resources/checkpoints/testnet/2770000.json b/Sources/ZcashLightClientKit/Resources/checkpoints/testnet/2770000.json new file mode 100644 index 00000000..6c00a30b --- /dev/null +++ b/Sources/ZcashLightClientKit/Resources/checkpoints/testnet/2770000.json @@ -0,0 +1,8 @@ +{ + "network": "test", + "height": "2770000", + "hash": "00065215d6079640ddf94ac7414130f35efabc9d24c9fa8feefa86030f5e5dcf", + "time": 1711329173, + "saplingTree": "01df4ab40e947423f0d6e527bdf8f4bfb33bd9487d28632b1480dc454a51174f600010014af622a06b0138e40355cfb8905dcb0d3a24d8e0793d886763b0c53275b75b1b0001668c0700dbe3d5efa57cc583bd2ff8a0e547b31044f216f11f02e0982d74fe2e0000010c96e4cc8a6a80fba0d41e4eb3070d80769104dc33fb61133b1304c15bf9e23e000107114fe4bb4cd08b47f6ae47477c182d5da9fe5c189061808c1091e9bf3b4524000001447d6b9100cddd5f80c8cf4ddee2b87eba053bd987465aec2293bd0514e68b0d015f6c95e75f4601a0a31670a7deb970fc8988c611685161d2e1629d0a1a0ebd07015f8b9205e0514fa235d75c150b87e23866b882b39786852d1ab42aab11d31a4a0117ddeb3a5f8d2f6b2d0a07f28f01ab25e03a05a9319275bb86d72fcaef6fc01501f08f39275112dd8905b854170b7f247cf2df18454d4fa94e6e4f9320cca05f24011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39", + "orchardTree": "01071ec35db52f66ce2ae4a131ba489adae60f7d028b928111abdcaa5784c597320150da3f6787f2d6ae4f48892554efb2e64d26b4e56f3d6ffe819770bee8ee851e1f0000000001c4ac2643a1b48b270cebdc0d99da3f8f4e52bcd6a81b2a681919661e91c6610901061089b21464cd2d65ac65d509f398014d89f00a996ec0807a1da1f2ac88cc0d01fcb318566b2a3c4812c1ac1ffeb99226c2c653094bedf9608cf409ffc54e18200172316ba203fdc9df151f6101a287f203befcd8bdecac5cf43a2f421d6a467f080000011e6191f91b3fceb62dc881a156e1b9d2e88e09dca25093cf9c4936c8869fb41a013bf8b923e4187754e85175748d9cce4824a6787e4258977b5bfe1ba59012c032000001f3bbdc62260c4fca5c84bf3487246d4542da48eeeec8ec40c1029b6908eef83c00000000000000000000000000000000" +} diff --git a/Sources/ZcashLightClientKit/Rust/ZcashRustBackend.swift b/Sources/ZcashLightClientKit/Rust/ZcashRustBackend.swift index bb169dfd..5eb048ef 100644 --- a/Sources/ZcashLightClientKit/Rust/ZcashRustBackend.swift +++ b/Sources/ZcashLightClientKit/Rust/ZcashRustBackend.swift @@ -49,6 +49,31 @@ actor ZcashRustBackend: ZcashRustBackendWelding { } } + func listAccounts() async throws -> [Int32] { + globalDBLock.lock() + let accountsPtr = zcashlc_list_accounts( + dbData.0, + dbData.1, + networkType.networkId + ) + globalDBLock.unlock() + + guard let accountsPtr else { + throw ZcashError.rustListAccounts(lastErrorMessage(fallback: "`listAccounts` failed with unknown error")) + } + + defer { zcashlc_free_accounts(accountsPtr) } + + var accounts: [Int32] = [] + + for i in (0 ..< Int(accountsPtr.pointee.len)) { + let account = accountsPtr.pointee.ptr.advanced(by: i).pointee + accounts.append(Int32(account.account_index)) + } + + return accounts + } + func createAccount(seed: [UInt8], treeState: TreeState, recoverUntil: UInt32?) async throws -> UnifiedSpendingKey { var rUntil: Int64 = -1 @@ -80,6 +105,26 @@ actor ZcashRustBackend: ZcashRustBackendWelding { return ffiBinaryKeyPtr.pointee.unsafeToUnifiedSpendingKey(network: networkType) } + func isSeedRelevantToAnyDerivedAccount(seed: [UInt8]) async throws -> Bool { + globalDBLock.lock() + let result = zcashlc_is_seed_relevant_to_any_derived_account( + dbData.0, + dbData.1, + seed, + UInt(seed.count), + networkType.networkId + ) + globalDBLock.unlock() + + // -1 is the error sentinel. + guard result >= 0 else { + throw ZcashError.rustIsSeedRelevantToAnyDerivedAccount(lastErrorMessage(fallback: "`isSeedRelevantToAnyDerivedAccount` failed with unknown error")) + } + + // 0 is false, 1 is true. + return result != 0 + } + func proposeTransfer( account: Int32, to address: String, @@ -298,6 +343,8 @@ actor ZcashRustBackend: ZcashRustBackendWelding { return DbInitResult.success case 1: return DbInitResult.seedRequired + case 2: + return DbInitResult.seedNotRelevant default: throw ZcashError.rustInitDataDb(lastErrorMessage(fallback: "`initDataDb` failed with unknown error")) } @@ -589,7 +636,8 @@ actor ZcashRustBackend: ZcashRustBackendWelding { chainTipHeight: BlockHeight(summaryPtr.pointee.chain_tip_height), fullyScannedHeight: BlockHeight(summaryPtr.pointee.fully_scanned_height), scanProgress: summaryPtr.pointee.scan_progress?.pointee.toScanProgress(), - nextSaplingSubtreeIndex: UInt32(summaryPtr.pointee.next_sapling_subtree_index) + nextSaplingSubtreeIndex: UInt32(summaryPtr.pointee.next_sapling_subtree_index), + nextOrchardSubtreeIndex: UInt32(summaryPtr.pointee.next_orchard_subtree_index) ) } @@ -623,9 +671,20 @@ actor ZcashRustBackend: ZcashRustBackendWelding { return scanRanges } - func scanBlocks(fromHeight: Int32, limit: UInt32 = 0) async throws -> ScanSummary { + func scanBlocks(fromHeight: Int32, fromState: TreeState, limit: UInt32 = 0) async throws -> ScanSummary { + let fromStateBytes = try fromState.serializedData(partial: false).bytes + globalDBLock.lock() - let summaryPtr = zcashlc_scan_blocks(fsBlockDbRoot.0, fsBlockDbRoot.1, dbData.0, dbData.1, fromHeight, limit, networkType.networkId) + let summaryPtr = zcashlc_scan_blocks( + fsBlockDbRoot.0, + fsBlockDbRoot.1, + dbData.0, + dbData.1, + fromHeight, + fromStateBytes, + UInt(fromStateBytes.count), + limit, + networkType.networkId) globalDBLock.unlock() guard let summaryPtr else { @@ -841,6 +900,7 @@ extension FfiAccountBalance { func toAccountBalance() -> AccountBalance { .init( saplingBalance: self.sapling_balance.toPoolBalance(), + orchardBalance: self.orchard_balance.toPoolBalance(), unshielded: Zatoshi(self.unshielded) ) } @@ -856,6 +916,7 @@ extension FfiScanProgress { } } +// swiftlint:disable large_tuple line_length struct FfiTxId { var tuple: (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8) var array: [UInt8] { diff --git a/Sources/ZcashLightClientKit/Rust/ZcashRustBackendWelding.swift b/Sources/ZcashLightClientKit/Rust/ZcashRustBackendWelding.swift index 64e9793b..5f0fb115 100644 --- a/Sources/ZcashLightClientKit/Rust/ZcashRustBackendWelding.swift +++ b/Sources/ZcashLightClientKit/Rust/ZcashRustBackendWelding.swift @@ -11,15 +11,21 @@ import Foundation enum ZcashRustBackendWeldingConstants { static let validChain: Int32 = -1 } -/// Enumeration of potential return states for database initialization. If `seedRequired` -/// is returned, the caller must re-attempt initialization providing the seed + +/// Enumeration of potential return states for database initialization. +/// +/// If `seedRequired` is returned, the caller must re-attempt initialization providing the seed. public enum DbInitResult { case success case seedRequired + case seedNotRelevant } // sourcery: mockActor protocol ZcashRustBackendWelding { + /// Returns a list of the accounts in the wallet. + func listAccounts() async throws -> [Int32] + /// Adds the next available account-level spend authority, given the current set of [ZIP 316] /// account identifiers known, to the wallet database. /// @@ -41,6 +47,11 @@ protocol ZcashRustBackendWelding { /// - Throws: `rustCreateAccount`. func createAccount(seed: [UInt8], treeState: TreeState, recoverUntil: UInt32?) async throws -> UnifiedSpendingKey + /// Checks whether the given seed is relevant to any of the derived accounts in the wallet. + /// + /// - parameter seed: byte array of the seed + func isSeedRelevantToAnyDerivedAccount(seed: [UInt8]) async throws -> Bool + /// Scans a transaction for any information that can be decrypted by the accounts in the wallet, and saves it to the wallet. /// - parameter tx: the transaction to decrypt /// - parameter minedHeight: height on which this transaction was mined. this is used to fetch the consensus branch ID. @@ -173,9 +184,10 @@ protocol ZcashRustBackendWelding { /// cache, an error will be signalled. /// /// - parameter fromHeight: scan starting from the given height. + /// - parameter fromState: The TreeState Protobuf object for the height prior to `fromHeight` /// - parameter limit: scan up to limit blocks. /// - Throws: `rustScanBlocks` if rust layer returns error. - func scanBlocks(fromHeight: Int32, limit: UInt32) async throws -> ScanSummary + func scanBlocks(fromHeight: Int32, fromState: TreeState, limit: UInt32) async throws -> ScanSummary /// Upserts a UTXO into the data db database /// - parameter txid: the txid bytes for the UTXO diff --git a/Sources/ZcashLightClientKit/Synchronizer.swift b/Sources/ZcashLightClientKit/Synchronizer.swift index 2e1788c6..0fae1a29 100644 --- a/Sources/ZcashLightClientKit/Synchronizer.swift +++ b/Sources/ZcashLightClientKit/Synchronizer.swift @@ -351,6 +351,11 @@ public protocol Synchronizer: AnyObject { /// - Throws: ZcashError when failures occur and related to `synchronizer.start(retry: Bool)`, it's the only throwing operation /// during the whole endpoint change. func switchTo(endpoint: LightWalletEndpoint) async throws + + /// Checks whether the given seed is relevant to any of the derived accounts in the wallet. + /// + /// - parameter seed: byte array of the seed + func isSeedRelevantToAnyDerivedAccount(seed: [UInt8]) async throws -> Bool } public enum SyncStatus: Equatable { diff --git a/Sources/ZcashLightClientKit/Synchronizer/Dependencies.swift b/Sources/ZcashLightClientKit/Synchronizer/Dependencies.swift index 05c48053..dd32a14f 100644 --- a/Sources/ZcashLightClientKit/Synchronizer/Dependencies.swift +++ b/Sources/ZcashLightClientKit/Synchronizer/Dependencies.swift @@ -100,8 +100,7 @@ enum Dependencies { static func setupCompactBlockProcessor( in container: DIContainer, - config: CompactBlockProcessor.Configuration, - accountRepository: AccountRepository + config: CompactBlockProcessor.Configuration ) { container.register(type: BlockDownloader.self, isSingleton: true) { di in let service = di.resolve(LightWalletService.self) @@ -120,6 +119,7 @@ enum Dependencies { } container.register(type: BlockScanner.self, isSingleton: true) { di in + let service = di.resolve(LightWalletService.self) let rustBackend = di.resolve(ZcashRustBackendWelding.self) let transactionRepository = di.resolve(TransactionRepository.self) let metrics = di.resolve(SDKMetrics.self) @@ -133,6 +133,7 @@ enum Dependencies { return BlockScannerImpl( config: blockScannerConfig, rustBackend: rustBackend, + service: service, transactionRepository: transactionRepository, metrics: metrics, logger: logger @@ -163,7 +164,6 @@ enum Dependencies { let logger = di.resolve(Logger.self) return UTXOFetcherImpl( - accountRepository: accountRepository, blockDownloaderService: blockDownloaderService, config: utxoFetcherConfig, rustBackend: rustBackend, diff --git a/Sources/ZcashLightClientKit/Synchronizer/SDKSynchronizer.swift b/Sources/ZcashLightClientKit/Synchronizer/SDKSynchronizer.swift index 8025d9e0..ea80173e 100644 --- a/Sources/ZcashLightClientKit/Synchronizer/SDKSynchronizer.swift +++ b/Sources/ZcashLightClientKit/Synchronizer/SDKSynchronizer.swift @@ -40,7 +40,6 @@ public class SDKSynchronizer: Synchronizer { public let network: ZcashNetwork private let transactionEncoder: TransactionEncoder private let transactionRepository: TransactionRepository - private let utxoRepository: UnspentTransactionOutputRepository private let syncSessionIDGenerator: SyncSessionIDGenerator private let syncSession: SyncSession @@ -55,7 +54,6 @@ public class SDKSynchronizer: Synchronizer { initializer: initializer, transactionEncoder: WalletTransactionEncoder(initializer: initializer), transactionRepository: initializer.transactionRepository, - utxoRepository: UTXORepositoryBuilder.build(initializer: initializer), blockProcessor: CompactBlockProcessor( initializer: initializer, walletBirthdayProvider: { initializer.walletBirthday } @@ -69,7 +67,6 @@ public class SDKSynchronizer: Synchronizer { initializer: Initializer, transactionEncoder: TransactionEncoder, transactionRepository: TransactionRepository, - utxoRepository: UnspentTransactionOutputRepository, blockProcessor: CompactBlockProcessor, syncSessionTicker: SessionTicker ) { @@ -78,7 +75,6 @@ public class SDKSynchronizer: Synchronizer { self.initializer = initializer self.transactionEncoder = transactionEncoder self.transactionRepository = transactionRepository - self.utxoRepository = utxoRepository self.blockProcessor = blockProcessor self.network = initializer.network self.metrics = initializer.container.resolve(SDKMetrics.self) @@ -137,8 +133,6 @@ public class SDKSynchronizer: Synchronizer { throw error } - try await utxoRepository.initialise() - if case .seedRequired = try await self.initializer.initialize(with: seed, walletBirthday: walletBirthday, for: walletMode) { return .seedRequired } @@ -309,8 +303,8 @@ public class SDKSynchronizer: Synchronizer { uri, accountIndex: accountIndex ) - } catch ZcashError.rustCreateToAddress(let e) { - throw ZcashError.rustProposeTransferFromURI(e) + } catch ZcashError.rustCreateToAddress(let error) { + throw ZcashError.rustProposeTransferFromURI(error) } catch { throw error } @@ -502,25 +496,6 @@ public class SDKSynchronizer: Synchronizer { try await blockProcessor.latestHeight() } - public func latestUTXOs(address: String) async throws -> [UnspentTransactionOutputEntity] { - try throwIfUnprepared() - - guard initializer.isValidTransparentAddress(address) else { - throw ZcashError.synchronizerLatestUTXOsInvalidTAddress - } - - let stream = initializer.lightWalletService.fetchUTXOs(for: address, height: network.constants.saplingActivationHeight) - - // swiftlint:disable:next array_constructor - var utxos: [UnspentTransactionOutputEntity] = [] - for try await transactionEntity in stream { - utxos.append(transactionEntity) - } - try await self.utxoRepository.clearAll(address: address) - try await self.utxoRepository.store(utxos: utxos) - return utxos - } - public func refreshUTXOs(address: TransparentAddress, from height: BlockHeight) async throws -> RefreshedUTXOs { try throwIfUnprepared() return try await blockProcessor.refreshUTXOs(tAddress: address, startHeight: height) @@ -628,7 +603,11 @@ public class SDKSynchronizer: Synchronizer { return subject.eraseToAnyPublisher() } - + + public func isSeedRelevantToAnyDerivedAccount(seed: [UInt8]) async throws -> Bool { + try await initializer.rustBackend.isSeedRelevantToAnyDerivedAccount(seed: seed) + } + // MARK: Server switch public func switchTo(endpoint: LightWalletEndpoint) async throws { @@ -692,8 +671,7 @@ public class SDKSynchronizer: Synchronizer { // CompactBlockProcessor dependency update Dependencies.setupCompactBlockProcessor( in: initializer.container, - config: await blockProcessor.config, - accountRepository: initializer.accountRepository + config: await blockProcessor.config ) // INITIALIZER diff --git a/Tests/DarksideTests/PaymentURIFulfillmentTests.swift b/Tests/DarksideTests/PaymentURIFulfillmentTests.swift index 22f54394..3da93841 100644 --- a/Tests/DarksideTests/PaymentURIFulfillmentTests.swift +++ b/Tests/DarksideTests/PaymentURIFulfillmentTests.swift @@ -315,6 +315,7 @@ class PaymentURIFulfillmentTests: ZcashTestCase { */ let memo = "VGhpcyBpcyBhIHNpbXBsZSBtZW1vLg" // "This is a simple memo." + // swiftlint:disable:next line_length let paymentURI = "zcash:zecIsGreat17mg40levjezevuhdp5pqrd52zere7r7vrjgdwn5sj4xsqtm20euwahv9anxmwr3y3kmwuz8k55a?amount=0.0002&memo=\(memo)&message=Thank%20you%20for%20your%20purchase&label=Your%20Purchase" do { diff --git a/Tests/OfflineTests/ClosureSynchronizerOfflineTests.swift b/Tests/OfflineTests/ClosureSynchronizerOfflineTests.swift index c54e631b..9be8c336 100644 --- a/Tests/OfflineTests/ClosureSynchronizerOfflineTests.swift +++ b/Tests/OfflineTests/ClosureSynchronizerOfflineTests.swift @@ -638,7 +638,7 @@ class ClosureSynchronizerOfflineTests: XCTestCase { } func testGetTransparentBalanceSucceed() { - let expectedBalance = AccountBalance(saplingBalance: .zero, unshielded: Zatoshi(200)) + let expectedBalance = AccountBalance(saplingBalance: .zero, orchardBalance: .zero, unshielded: Zatoshi(200)) synchronizerMock.getAccountBalanceAccountIndexClosure = { receivedAccountIndex in XCTAssertEqual(receivedAccountIndex, 3) @@ -685,7 +685,13 @@ class ClosureSynchronizerOfflineTests: XCTestCase { PoolBalance( spendableValue: Zatoshi(333), changePendingConfirmation: .zero, - valuePendingSpendability: .zero), unshielded: .zero + valuePendingSpendability: .zero), + orchardBalance: + PoolBalance( + spendableValue: Zatoshi(333), + changePendingConfirmation: .zero, + valuePendingSpendability: .zero), + unshielded: .zero ) synchronizerMock.getAccountBalanceAccountIndexClosure = { receivedAccountIndex in @@ -733,7 +739,13 @@ class ClosureSynchronizerOfflineTests: XCTestCase { PoolBalance( spendableValue: .zero, changePendingConfirmation: Zatoshi(333), - valuePendingSpendability: .zero), unshielded: .zero + valuePendingSpendability: .zero), + orchardBalance: + PoolBalance( + spendableValue: .zero, + changePendingConfirmation: Zatoshi(333), + valuePendingSpendability: .zero), + unshielded: .zero ) synchronizerMock.getAccountBalanceAccountIndexClosure = { receivedAccountIndex in diff --git a/Tests/OfflineTests/CombineSynchronizerOfflineTests.swift b/Tests/OfflineTests/CombineSynchronizerOfflineTests.swift index 07cec75a..b12a4e7f 100644 --- a/Tests/OfflineTests/CombineSynchronizerOfflineTests.swift +++ b/Tests/OfflineTests/CombineSynchronizerOfflineTests.swift @@ -748,7 +748,7 @@ class CombineSynchronizerOfflineTests: XCTestCase { } func testGetTransparentBalanceSucceed() { - let expectedBalance = AccountBalance(saplingBalance: .zero, unshielded: Zatoshi(100)) + let expectedBalance = AccountBalance(saplingBalance: .zero, orchardBalance: .zero, unshielded: Zatoshi(100)) synchronizerMock.getAccountBalanceAccountIndexClosure = { receivedAccountIndex in XCTAssertEqual(receivedAccountIndex, 3) @@ -809,6 +809,11 @@ class CombineSynchronizerOfflineTests: XCTestCase { spendableValue: .zero, changePendingConfirmation: Zatoshi(333), valuePendingSpendability: .zero), + orchardBalance: + PoolBalance( + spendableValue: .zero, + changePendingConfirmation: Zatoshi(333), + valuePendingSpendability: .zero), unshielded: .zero ) @@ -871,6 +876,11 @@ class CombineSynchronizerOfflineTests: XCTestCase { spendableValue: Zatoshi(333), changePendingConfirmation: .zero, valuePendingSpendability: .zero), + orchardBalance: + PoolBalance( + spendableValue: Zatoshi(333), + changePendingConfirmation: .zero, + valuePendingSpendability: .zero), unshielded: .zero ) diff --git a/Tests/OfflineTests/SynchronizerOfflineTests.swift b/Tests/OfflineTests/SynchronizerOfflineTests.swift index 826e98ae..3d701a14 100644 --- a/Tests/OfflineTests/SynchronizerOfflineTests.swift +++ b/Tests/OfflineTests/SynchronizerOfflineTests.swift @@ -29,10 +29,13 @@ class SynchronizerOfflineTests: ZcashTestCase { } func testCallPrepareWithAlreadyUsedAliasThrowsError() async throws { + // Pick a testnet height for which both Sapling and Orchard are active. + let walletBirthday = 1900000 + let firstTestCoordinator = try await TestCoordinator( alias: .custom("alias"), container: mockContainer, - walletBirthday: 10, + walletBirthday: walletBirthday, network: network, callPrepareInConstructor: false ) @@ -40,7 +43,7 @@ class SynchronizerOfflineTests: ZcashTestCase { let secondTestCoordinator = try await TestCoordinator( alias: .custom("alias"), container: mockContainer, - walletBirthday: 10, + walletBirthday: walletBirthday, network: network, callPrepareInConstructor: false ) @@ -58,10 +61,13 @@ class SynchronizerOfflineTests: ZcashTestCase { } func testWhenSynchronizerIsDeallocatedAliasIsntUsedAnymore() async throws { + // Pick a testnet height for which both Sapling and Orchard are active. + let walletBirthday = 1900000 + var testCoordinator: TestCoordinator! = try await TestCoordinator( alias: .default, container: mockContainer, - walletBirthday: 10, + walletBirthday: walletBirthday, network: network, callPrepareInConstructor: false ) @@ -75,7 +81,7 @@ class SynchronizerOfflineTests: ZcashTestCase { testCoordinator = try await TestCoordinator( alias: .default, container: mockContainer, - walletBirthday: 10, + walletBirthday: walletBirthday, network: network, callPrepareInConstructor: false ) @@ -88,10 +94,13 @@ class SynchronizerOfflineTests: ZcashTestCase { } func testCallWipeWithAlreadyUsedAliasThrowsError() async throws { + // Pick a testnet height for which both Sapling and Orchard are active. + let walletBirthday = 1900000 + let firstTestCoordinator = try await TestCoordinator( alias: .default, container: mockContainer, - walletBirthday: 10, + walletBirthday: walletBirthday, network: network, callPrepareInConstructor: false ) @@ -99,7 +108,7 @@ class SynchronizerOfflineTests: ZcashTestCase { let secondTestCoordinator = try await TestCoordinator( alias: .default, container: mockContainer, - walletBirthday: 10, + walletBirthday: walletBirthday, network: network, callPrepareInConstructor: false ) @@ -146,10 +155,13 @@ class SynchronizerOfflineTests: ZcashTestCase { } func testPrepareCanBeCalledAfterWipeWithSameInstanceOfSDKSynchronizer() async throws { + // Pick a testnet height for which both Sapling and Orchard are active. + let walletBirthday = 1900000 + let testCoordinator = try await TestCoordinator( alias: .default, container: mockContainer, - walletBirthday: 10, + walletBirthday: walletBirthday, network: network, callPrepareInConstructor: false ) @@ -180,10 +192,13 @@ class SynchronizerOfflineTests: ZcashTestCase { } func testSendToAddressCalledWithoutPrepareThrowsError() async throws { + // Pick a testnet height for which both Sapling and Orchard are active. + let walletBirthday = 1900000 + let testCoordinator = try await TestCoordinator( alias: .default, container: mockContainer, - walletBirthday: 10, + walletBirthday: walletBirthday, network: network, callPrepareInConstructor: false ) @@ -205,10 +220,13 @@ class SynchronizerOfflineTests: ZcashTestCase { } func testShieldFundsCalledWithoutPrepareThrowsError() async throws { + // Pick a testnet height for which both Sapling and Orchard are active. + let walletBirthday = 1900000 + let testCoordinator = try await TestCoordinator( alias: .default, container: mockContainer, - walletBirthday: 10, + walletBirthday: walletBirthday, network: network, callPrepareInConstructor: false ) @@ -229,10 +247,13 @@ class SynchronizerOfflineTests: ZcashTestCase { } func testRefreshUTXOCalledWithoutPrepareThrowsError() async throws { + // Pick a testnet height for which both Sapling and Orchard are active. + let walletBirthday = 1900000 + let testCoordinator = try await TestCoordinator( alias: .default, container: mockContainer, - walletBirthday: 10, + walletBirthday: walletBirthday, network: network, callPrepareInConstructor: false ) @@ -249,10 +270,13 @@ class SynchronizerOfflineTests: ZcashTestCase { } func testRewindCalledWithoutPrepareThrowsError() async throws { + // Pick a testnet height for which both Sapling and Orchard are active. + let walletBirthday = 1900000 + let testCoordinator = try await TestCoordinator( alias: .default, container: mockContainer, - walletBirthday: 10, + walletBirthday: walletBirthday, network: network, callPrepareInConstructor: false ) diff --git a/Tests/OfflineTests/UnifiedTypecodesTests.swift b/Tests/OfflineTests/UnifiedTypecodesTests.swift index d93345a2..7ff58307 100644 --- a/Tests/OfflineTests/UnifiedTypecodesTests.swift +++ b/Tests/OfflineTests/UnifiedTypecodesTests.swift @@ -32,7 +32,7 @@ final class UnifiedTypecodesTests: XCTestCase { let typecodes = try DerivationTool(networkType: .testnet).receiverTypecodesFromUnifiedAddress(address) - XCTAssertEqual(typecodes, [.sapling, .p2pkh]) + XCTAssertEqual(typecodes, [.orchard, .sapling, .p2pkh]) } func testUnifiedAddressHasTransparentSaplingReceivers() throws { @@ -51,6 +51,7 @@ final class UnifiedTypecodesTests: XCTestCase { XCTAssertEqual( Set(typecodes), Set([ + .orchard, .sapling, .p2pkh ]) diff --git a/Tests/OfflineTests/ZcashRustBackendTests.swift b/Tests/OfflineTests/ZcashRustBackendTests.swift index 4b77e426..8293facb 100644 --- a/Tests/OfflineTests/ZcashRustBackendTests.swift +++ b/Tests/OfflineTests/ZcashRustBackendTests.swift @@ -126,6 +126,11 @@ class ZcashRustBackendTests: XCTestCase { XCTFail("not enough transparent receivers") return } + + // The first address in the wallet is created when the account is created, using + // the default receivers specified inside `zcash_client_sqlite`. The remaining + // addresses are generated here, using the receivers specified in the Swift SDK's + // FFI backend. var uAddresses: [UnifiedAddress] = [] for i in 0...2 { uAddresses.append( diff --git a/Tests/TestUtils/DarkSideWalletService.swift b/Tests/TestUtils/DarkSideWalletService.swift index 60e3f90e..2b1fbb56 100644 --- a/Tests/TestUtils/DarkSideWalletService.swift +++ b/Tests/TestUtils/DarkSideWalletService.swift @@ -190,6 +190,10 @@ class DarksideWalletService: LightWalletService { func getSubtreeRoots(_ request: ZcashLightClientKit.GetSubtreeRootsArg) -> AsyncThrowingStream { service.getSubtreeRoots(request) } + + func getTreeState(_ id: BlockID) async throws -> TreeState { + try await service.getTreeState(id) + } } enum DarksideWalletDConstants: NetworkConstants { diff --git a/Tests/TestUtils/FakeService.swift b/Tests/TestUtils/FakeService.swift index 07e2d74b..3358f519 100644 --- a/Tests/TestUtils/FakeService.swift +++ b/Tests/TestUtils/FakeService.swift @@ -82,4 +82,8 @@ class MockLightWalletService: LightWalletService { func getSubtreeRoots(_ request: ZcashLightClientKit.GetSubtreeRootsArg) -> AsyncThrowingStream { service.getSubtreeRoots(request) } + + func getTreeState(_ id: BlockID) async throws -> TreeState { + try await service.getTreeState(id) + } } diff --git a/Tests/TestUtils/Sourcery/GeneratedMocks/AutoMockable.generated.swift b/Tests/TestUtils/Sourcery/GeneratedMocks/AutoMockable.generated.swift index 51612a02..f99690a5 100644 --- a/Tests/TestUtils/Sourcery/GeneratedMocks/AutoMockable.generated.swift +++ b/Tests/TestUtils/Sourcery/GeneratedMocks/AutoMockable.generated.swift @@ -963,6 +963,30 @@ class LightWalletServiceMock: LightWalletService { } } + // MARK: - getTreeState + + var getTreeStateThrowableError: Error? + var getTreeStateCallsCount = 0 + var getTreeStateCalled: Bool { + return getTreeStateCallsCount > 0 + } + var getTreeStateReceivedId: BlockID? + var getTreeStateReturnValue: TreeState! + var getTreeStateClosure: ((BlockID) async throws -> TreeState)? + + func getTreeState(_ id: BlockID) async throws -> TreeState { + if let error = getTreeStateThrowableError { + throw error + } + getTreeStateCallsCount += 1 + getTreeStateReceivedId = id + if let closure = getTreeStateClosure { + return try await closure(id) + } else { + return getTreeStateReturnValue + } + } + } class LightWalletdInfoMock: LightWalletdInfo { @@ -1776,6 +1800,30 @@ class SynchronizerMock: Synchronizer { try await switchToEndpointClosure!(endpoint) } + // MARK: - isSeedRelevantToAnyDerivedAccount + + var isSeedRelevantToAnyDerivedAccountSeedThrowableError: Error? + var isSeedRelevantToAnyDerivedAccountSeedCallsCount = 0 + var isSeedRelevantToAnyDerivedAccountSeedCalled: Bool { + return isSeedRelevantToAnyDerivedAccountSeedCallsCount > 0 + } + var isSeedRelevantToAnyDerivedAccountSeedReceivedSeed: [UInt8]? + var isSeedRelevantToAnyDerivedAccountSeedReturnValue: Bool! + var isSeedRelevantToAnyDerivedAccountSeedClosure: (([UInt8]) async throws -> Bool)? + + func isSeedRelevantToAnyDerivedAccount(seed: [UInt8]) async throws -> Bool { + if let error = isSeedRelevantToAnyDerivedAccountSeedThrowableError { + throw error + } + isSeedRelevantToAnyDerivedAccountSeedCallsCount += 1 + isSeedRelevantToAnyDerivedAccountSeedReceivedSeed = seed + if let closure = isSeedRelevantToAnyDerivedAccountSeedClosure { + return try await closure(seed) + } else { + return isSeedRelevantToAnyDerivedAccountSeedReturnValue + } + } + } class TransactionRepositoryMock: TransactionRepository { @@ -2213,6 +2261,37 @@ actor ZcashRustBackendWeldingMock: ZcashRustBackendWelding { self.consensusBranchIdForHeightClosure = consensusBranchIdForHeightClosure } + // MARK: - listAccounts + + var listAccountsThrowableError: Error? + func setListAccountsThrowableError(_ param: Error?) async { + listAccountsThrowableError = param + } + var listAccountsCallsCount = 0 + var listAccountsCalled: Bool { + return listAccountsCallsCount > 0 + } + var listAccountsReturnValue: [Int32]! + func setListAccountsReturnValue(_ param: [Int32]) async { + listAccountsReturnValue = param + } + var listAccountsClosure: (() async throws -> [Int32])? + func setListAccountsClosure(_ param: (() async throws -> [Int32])?) async { + listAccountsClosure = param + } + + func listAccounts() async throws -> [Int32] { + if let error = listAccountsThrowableError { + throw error + } + listAccountsCallsCount += 1 + if let closure = listAccountsClosure { + return try await closure() + } else { + return listAccountsReturnValue + } + } + // MARK: - createAccount var createAccountSeedTreeStateRecoverUntilThrowableError: Error? @@ -2246,6 +2325,39 @@ actor ZcashRustBackendWeldingMock: ZcashRustBackendWelding { } } + // MARK: - isSeedRelevantToAnyDerivedAccount + + var isSeedRelevantToAnyDerivedAccountSeedThrowableError: Error? + func setIsSeedRelevantToAnyDerivedAccountSeedThrowableError(_ param: Error?) async { + isSeedRelevantToAnyDerivedAccountSeedThrowableError = param + } + var isSeedRelevantToAnyDerivedAccountSeedCallsCount = 0 + var isSeedRelevantToAnyDerivedAccountSeedCalled: Bool { + return isSeedRelevantToAnyDerivedAccountSeedCallsCount > 0 + } + var isSeedRelevantToAnyDerivedAccountSeedReceivedSeed: [UInt8]? + var isSeedRelevantToAnyDerivedAccountSeedReturnValue: Bool! + func setIsSeedRelevantToAnyDerivedAccountSeedReturnValue(_ param: Bool) async { + isSeedRelevantToAnyDerivedAccountSeedReturnValue = param + } + var isSeedRelevantToAnyDerivedAccountSeedClosure: (([UInt8]) async throws -> Bool)? + func setIsSeedRelevantToAnyDerivedAccountSeedClosure(_ param: (([UInt8]) async throws -> Bool)?) async { + isSeedRelevantToAnyDerivedAccountSeedClosure = param + } + + func isSeedRelevantToAnyDerivedAccount(seed: [UInt8]) async throws -> Bool { + if let error = isSeedRelevantToAnyDerivedAccountSeedThrowableError { + throw error + } + isSeedRelevantToAnyDerivedAccountSeedCallsCount += 1 + isSeedRelevantToAnyDerivedAccountSeedReceivedSeed = seed + if let closure = isSeedRelevantToAnyDerivedAccountSeedClosure { + return try await closure(seed) + } else { + return isSeedRelevantToAnyDerivedAccountSeedReturnValue + } + } + // MARK: - decryptAndStoreTransaction var decryptAndStoreTransactionTxBytesMinedHeightThrowableError: Error? @@ -2761,34 +2873,34 @@ actor ZcashRustBackendWeldingMock: ZcashRustBackendWelding { // MARK: - scanBlocks - var scanBlocksFromHeightLimitThrowableError: Error? - func setScanBlocksFromHeightLimitThrowableError(_ param: Error?) async { - scanBlocksFromHeightLimitThrowableError = param + var scanBlocksFromHeightFromStateLimitThrowableError: Error? + func setScanBlocksFromHeightFromStateLimitThrowableError(_ param: Error?) async { + scanBlocksFromHeightFromStateLimitThrowableError = param } - var scanBlocksFromHeightLimitCallsCount = 0 - var scanBlocksFromHeightLimitCalled: Bool { - return scanBlocksFromHeightLimitCallsCount > 0 + var scanBlocksFromHeightFromStateLimitCallsCount = 0 + var scanBlocksFromHeightFromStateLimitCalled: Bool { + return scanBlocksFromHeightFromStateLimitCallsCount > 0 } - var scanBlocksFromHeightLimitReceivedArguments: (fromHeight: Int32, limit: UInt32)? - var scanBlocksFromHeightLimitReturnValue: ScanSummary! - func setScanBlocksFromHeightLimitReturnValue(_ param: ScanSummary) async { - scanBlocksFromHeightLimitReturnValue = param + var scanBlocksFromHeightFromStateLimitReceivedArguments: (fromHeight: Int32, fromState: TreeState, limit: UInt32)? + var scanBlocksFromHeightFromStateLimitReturnValue: ScanSummary! + func setScanBlocksFromHeightFromStateLimitReturnValue(_ param: ScanSummary) async { + scanBlocksFromHeightFromStateLimitReturnValue = param } - var scanBlocksFromHeightLimitClosure: ((Int32, UInt32) async throws -> ScanSummary)? - func setScanBlocksFromHeightLimitClosure(_ param: ((Int32, UInt32) async throws -> ScanSummary)?) async { - scanBlocksFromHeightLimitClosure = param + var scanBlocksFromHeightFromStateLimitClosure: ((Int32, TreeState, UInt32) async throws -> ScanSummary)? + func setScanBlocksFromHeightFromStateLimitClosure(_ param: ((Int32, TreeState, UInt32) async throws -> ScanSummary)?) async { + scanBlocksFromHeightFromStateLimitClosure = param } - func scanBlocks(fromHeight: Int32, limit: UInt32) async throws -> ScanSummary { - if let error = scanBlocksFromHeightLimitThrowableError { + func scanBlocks(fromHeight: Int32, fromState: TreeState, limit: UInt32) async throws -> ScanSummary { + if let error = scanBlocksFromHeightFromStateLimitThrowableError { throw error } - scanBlocksFromHeightLimitCallsCount += 1 - scanBlocksFromHeightLimitReceivedArguments = (fromHeight: fromHeight, limit: limit) - if let closure = scanBlocksFromHeightLimitClosure { - return try await closure(fromHeight, limit) + scanBlocksFromHeightFromStateLimitCallsCount += 1 + scanBlocksFromHeightFromStateLimitReceivedArguments = (fromHeight: fromHeight, fromState: fromState, limit: limit) + if let closure = scanBlocksFromHeightFromStateLimitClosure { + return try await closure(fromHeight, fromState, limit) } else { - return scanBlocksFromHeightLimitReturnValue + return scanBlocksFromHeightFromStateLimitReturnValue } } diff --git a/Tests/TestUtils/Stubs.swift b/Tests/TestUtils/Stubs.swift index f5f33521..69a118ee 100644 --- a/Tests/TestUtils/Stubs.swift +++ b/Tests/TestUtils/Stubs.swift @@ -102,8 +102,8 @@ class RustBackendMockHelper { try await rustBackend.suggestScanRanges() } - await rustBackendMock.setScanBlocksFromHeightLimitClosure() { fromHeight, limit in - try await rustBackend.scanBlocks(fromHeight: fromHeight, limit: limit) + await rustBackendMock.setScanBlocksFromHeightFromStateLimitClosure { fromHeight, fromState, limit in + try await rustBackend.scanBlocks(fromHeight: fromHeight, fromState: fromState, limit: limit) } } @@ -145,7 +145,7 @@ extension SynchronizerState { static var mock: SynchronizerState { SynchronizerState( syncSessionID: .nullID, - accountBalance: AccountBalance(saplingBalance: .zero, unshielded: Zatoshi(200)), + accountBalance: AccountBalance(saplingBalance: .zero, orchardBalance: .zero, unshielded: Zatoshi(200)), internalSyncStatus: .syncing(0), latestBlockHeight: 222222 ) diff --git a/Tests/TestUtils/TestDbBuilder.swift b/Tests/TestUtils/TestDbBuilder.swift index 46082fc7..1f6b2d3a 100644 --- a/Tests/TestUtils/TestDbBuilder.swift +++ b/Tests/TestUtils/TestDbBuilder.swift @@ -63,6 +63,8 @@ enum TestDbBuilder { case .success: return provider case .seedRequired: throw ZcashError.compactBlockProcessorDataDbInitFailed("Seed value required to initialize the wallet database") + case .seedNotRelevant: + throw ZcashError.compactBlockProcessorDataDbInitFailed("Relevant seed value required") } } diff --git a/Tests/TestUtils/TestVector.swift b/Tests/TestUtils/TestVector.swift index 7edf0071..4407c4c0 100644 --- a/Tests/TestUtils/TestVector.swift +++ b/Tests/TestUtils/TestVector.swift @@ -169,9 +169,9 @@ public let testVector: [[Any?]] = [ ["From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/unified_address.py"], ["p2pkh_bytes, p2sh_bytes, sapling_raw_addr, orchard_raw_addr, unknown_typecode, unknown_bytes, unified_addr, root_seed, account, diversifier_index"], - ["7bb83570b8fae146e03c5331a020b1e0892f631d", nil, "d8ef8293d26de832e7193f296ba1922d90f122c6135bc231eebd91efdb03b1a8606771cd4fd6480574d43e", nil, nil, nil, "u1l8xunezsvhq8fgzfl7404m450nwnd76zshscn6nfys7vyz2ywyh4cc5daaq0c7q2su5lqfh23sp7fkf3kt27ve5948mzpfdvckzaect2jtte308mkwlycj2u0eac077wu70vqcetkxf", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0, 0], - ["a7244a362f49f29644a955cf0039b88a61657861", nil, "435b0bbc95b5b7d52531a3944f2b85603ee22aaf850963bc156eb561edf2cbe7cf0e770e393ae5d7049026", nil, nil, nil, "u1fl5mprj0t9p4jg92hjjy8q5myvwc60c9wv0xachauqpn3c3k4xwzlaueafq27dcg7tzzzaz5jl8tyj93wgs983y0jq0qfhzu6n4r8rakpv5f4gg2lrw4z6pyqqcrcqx04d38yunc6je", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0, 3], - ["e256dcb03e05dde7c91212b47a7461311c415059", nil, "69a25a38699708e5f6e76e54e6a7a2ab84dcf288df0d1f2563670168d6c44ace0ef11155c60d5c225e9dec", nil, nil, nil, "u1qxqf8ctkxlsdh7xdcgkdtyw4mku7dxma8tsz45xd6ttgs322gdk7kazg3sdn52z7na3tzcrzf7lt3xrdtfp9d4pccderalchvvxk8hghduxrky5guzqlw65fmgp6x7aj4k8v5jkgwuw", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0, 4], + ["7bb83570b8fae146e03c5331a020b1e0892f631d", nil, "d8ef8293d26de832e7193f296ba1922d90f122c6135bc231eebd91efdb03b1a8606771cd4fd6480574d43e", nil, nil, nil, "u1m2p65qdnhexpfcmejjf6hjqd485xwfsrlf3cvc6wx5xhwrcq8myrdlqyhdpklz5d87ct0epfty0cr9d6q9fqtycx0j3vdhc2tzmkeejhtdqj3zrjqk3dd5ufpqkmueg89e6a6alvpaaxcx4fxsxqk4yj7g8dayn94d3afrsx66m3vq4rk03hc0ufmtkcm8wca7xja42f9kjau9708z7", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0, 0], + ["a7244a362f49f29644a955cf0039b88a61657861", nil, "435b0bbc95b5b7d52531a3944f2b85603ee22aaf850963bc156eb561edf2cbe7cf0e770e393ae5d7049026", nil, nil, nil, "u196qfugjlzacex3pnhj5hgkj7ppjxeg3egcwkn364rnt59nx7p2f4pp35h4v677hla3negz4w0quy9ul6an2azqv0lt7f5n4dz5pspk0uvhkqfft4ntarat6mf0zfc46s5rsvmrd63lvnp2y8kyyes2r0k439de6je9ugy85qkq9ldc7hkkvjav69gzyfl5sen8kfxk3gyhjpzn39xj0", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0, 3], + ["e256dcb03e05dde7c91212b47a7461311c415059", nil, "69a25a38699708e5f6e76e54e6a7a2ab84dcf288df0d1f2563670168d6c44ace0ef11155c60d5c225e9dec", nil, nil, nil, "u1vs0kanmdm4vtp2jy5lfsfspty0l7umph3ny6v9xwqpamvw2adshzsh24ukvc2hamwd8wky7sfalzv4szttsrss26m5yvxnh2r0h9u5vtksr0puhw89h7gm202h5qhn53cm2pt9uj7cwnymmgp26jxe9c0lt5z3egvl03hmdrg6r923t5xudetg2f6kzzn5h6xhaye8wmufnguaj8pws", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0, 4], ["cad268758c5e71493066446b98e71df9d1d6a5ca", nil, "9f6e0bf90a18fc0b9b83ae9f23ad4358648638482b5def8975635b66fd8a708335f9235a3186ec0f033f84", "cecbe5e689a453a3fe10ccf7617e6c1fb382819d7fc9200a1f42092ac84a30378f8c1fb90dff71a6d5042d", nil, nil, "u1pg2aaph7jp8rpf6yhsza25722sg5fcn3vaca6ze27hqjw7jvvhhuxkpcg0ge9xh6drsgdkda8qjq5chpehkcpxf87rnjryjqwymdheptpvnljqqrjqzjwkc2ma6hcq666kgwfytxwac8eyex6ndgr6ezte66706e3vaqrd25dzvzkc69kw0jgywtd0cmq52q5lkw6uh7hyvzjse8ksx", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 1, 3], ["8d653347a0fd3cd0842a790a5eaf89d8e3854659", nil, "e1adf156a07d56bcac91bdb2f7bb3ea7c44569dcfee54273c09e8065807b6823faa94a77219554d0f6e017", "24f8a60cbd97e012618d56054ad39241411a28fdd50ee35efa91152f60d5fa21172e5d458ddbcb6b709896", nil, nil, "u19mzuf4l37ny393m59v4mxx4t3uyxkh7qpqjdfvlfk9f504cv9w4fpl7cql0kqvssz8jay8mgl8lnrtvg6yzh9pranjj963acc3h2z2qt7007du0lsmdf862dyy40c3wmt0kq35k5z836tfljgzsqtdsccchayfjpygqzkx24l77ga3ngfgskqddyepz8we7ny4ggmt7q48cgvgu57mz", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 1, 7], ["e511f439b5f96cf824cd5e0e6b2eb8ee1bc83cb7", nil, "60ba572f8e379312d86897025decdd64b4b95e2c4afa9d13726b8cc393edb4988c51b976028f890f108bd2", "1f24294ed1b405c7b3b1c3f13db5b9b27b5d0f2aca9d589a69e5be00eb978621e6776e87ea326d47a34c1a", nil, nil, "u1mtxw5nras5glkxz093282sv3n2h8qs7cpxcmmaxj96vtzjzl6rmdaxs4e9es7mxwmd0h3k5wz3ce4ll5g4jz2pn9su4pufq74pxhp4t235n6j7aed3hh8ss7pf3sekf7apsf6vtg84ue5zcq2k9q3xv5yth3q50fu4czdm8sn8q4de3m5k76g2vwwyjsf50hqfxgmwxqxu0rsy22ktw", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 1, 8],