From 91f6b7b53be915625459e6638b6eeb8224c170bc Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Fri, 27 Sep 2024 18:30:55 -0600 Subject: [PATCH] Update to preview `zcash-light-client-ffi` release fix for with note commitment tree corruption. --- .../xcshareddata/swiftpm/Package.resolved | 46 +++++++++---------- Package.resolved | 26 +++++------ Package.swift | 2 +- .../Block/Actions/RewindAction.swift | 20 ++++++-- .../Block/CompactBlockProcessor.swift | 30 ++++++------ .../Rust/ZcashRustBackend.swift | 31 ++++--------- .../Rust/ZcashRustBackendWelding.swift | 27 ++++++----- 7 files changed, 92 insertions(+), 90 deletions(-) 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 3840d78df..ba3fac5c0 100644 --- a/Example/ZcashLightClientSample/ZcashLightClientSample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Example/ZcashLightClientSample/ZcashLightClientSample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -5,8 +5,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/grpc/grpc-swift.git", "state" : { - "revision" : "6a90b7e77e29f9bda6c2b3a4165a40d6c02cfda1", - "version" : "1.23.0" + "revision" : "07123ed731671e800ab8d641006613612e954746", + "version" : "1.23.1" } }, { @@ -77,8 +77,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-collections.git", "state" : { - "revision" : "ee97538f5b81ae89698fd95938896dec5217b148", - "version" : "1.1.1" + "revision" : "671108c96644956dddcd89dd59c203dcdb36cec7", + "version" : "1.1.4" } }, { @@ -95,8 +95,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-http-types", "state" : { - "revision" : "9bee2fdb79cc740081abd8ebd80738063d632286", - "version" : "1.1.0" + "revision" : "ae67c8178eb46944fd85e4dc6dd970e1f3ed6ccd", + "version" : "1.3.0" } }, { @@ -104,8 +104,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-log.git", "state" : { - "revision" : "e97a6fcb1ab07462881ac165fdbb37f067e205d5", - "version" : "1.5.4" + "revision" : "9cb486020ebf03bfa5b5df985387a14a98744537", + "version" : "1.6.1" } }, { @@ -113,8 +113,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-nio.git", "state" : { - "revision" : "9428f62793696d9a0cc1f26a63f63bb31da0516d", - "version" : "2.66.0" + "revision" : "665206000b8307cab5ac51203d29b0f232d7e31b", + "version" : "2.74.0" } }, { @@ -122,8 +122,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-nio-extras.git", "state" : { - "revision" : "a3b640d7dc567225db7c94386a6e71aded1bfa63", - "version" : "1.22.0" + "revision" : "d1ead62745cc3269e482f1c51f27608057174379", + "version" : "1.24.0" } }, { @@ -131,8 +131,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-nio-http2.git", "state" : { - "revision" : "8d8eb609929aee75336a0a3d2417280786265868", - "version" : "1.32.0" + "revision" : "b5f7062b60e4add1e8c343ba4eb8da2e324b3a94", + "version" : "1.34.0" } }, { @@ -140,8 +140,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-nio-ssl.git", "state" : { - "revision" : "2b09805797f21c380f7dc9bedaab3157c5508efb", - "version" : "2.27.0" + "revision" : "7b84abbdcef69cc3be6573ac12440220789dcd69", + "version" : "2.27.2" } }, { @@ -158,8 +158,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-protobuf.git", "state" : { - "revision" : "9f0c76544701845ad98716f3f6a774a892152bcb", - "version" : "1.26.0" + "revision" : "ebc7251dd5b37f627c93698e4374084d98409633", + "version" : "1.28.2" } }, { @@ -167,17 +167,17 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-system.git", "state" : { - "revision" : "f9266c85189c2751589a50ea5aec72799797e471", - "version" : "1.3.0" + "revision" : "d2ba781702a1d8285419c15ee62fd734a9437ff5", + "version" : "1.3.2" } }, { "identity" : "zcash-light-client-ffi", "kind" : "remoteSourceControl", - "location" : "https://github.com/zcash-hackworks/zcash-light-client-ffi", + "location" : "https://github.com/Electric-Coin-Company/zcash-light-client-ffi", "state" : { - "revision" : "8ed5b08d59ff5e7e11240be29b084dedbdf2f268", - "version" : "0.9.1" + "revision" : "be1575f8d2dce5b7502448ef1f8619e2ca9359d9", + "version" : "0.10.0" } } ], diff --git a/Package.resolved b/Package.resolved index 7d3ab1dd4..f19deceae 100644 --- a/Package.resolved +++ b/Package.resolved @@ -5,8 +5,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/grpc/grpc-swift.git", "state" : { - "revision" : "6ade19f0b57f5fc436dfecfced83f3c84d1095b9", - "version" : "1.21.0" + "revision" : "07123ed731671e800ab8d641006613612e954746", + "version" : "1.23.1" } }, { @@ -14,8 +14,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/stephencelis/SQLite.swift.git", "state" : { - "revision" : "7a2e3cd27de56f6d396e84f63beefd0267b55ccb", - "version" : "0.14.1" + "revision" : "a95fc6df17d108bd99210db5e8a9bac90fe984b8", + "version" : "0.15.3" } }, { @@ -59,8 +59,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-nio.git", "state" : { - "revision" : "635b2589494c97e48c62514bc8b37ced762e0a62", - "version" : "2.63.0" + "revision" : "1b33db2dea6a64d5b619b9e888175133c6d7f410", + "version" : "2.73.0" } }, { @@ -77,8 +77,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-nio-http2.git", "state" : { - "revision" : "0904bf0feb5122b7e5c3f15db7df0eabe623dd87", - "version" : "1.30.0" + "revision" : "b5f7062b60e4add1e8c343ba4eb8da2e324b3a94", + "version" : "1.34.0" } }, { @@ -104,8 +104,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-protobuf.git", "state" : { - "revision" : "65e8f29b2d63c4e38e736b25c27b83e012159be8", - "version" : "1.25.2" + "revision" : "edb6ed4919f7756157fe02f2552b7e3850a538e5", + "version" : "1.28.1" } }, { @@ -120,10 +120,10 @@ { "identity" : "zcash-light-client-ffi", "kind" : "remoteSourceControl", - "location" : "https://github.com/zcash-hackworks/zcash-light-client-ffi", + "location" : "https://github.com/Electric-Coin-Company/zcash-light-client-ffi", "state" : { - "revision" : "8ed5b08d59ff5e7e11240be29b084dedbdf2f268", - "version" : "0.9.1" + "revision" : "be1575f8d2dce5b7502448ef1f8619e2ca9359d9", + "version" : "0.10.0" } } ], diff --git a/Package.swift b/Package.swift index 9c97098bb..e59bce2d1 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.23.0"), .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.3"), - .package(url: "https://github.com/zcash-hackworks/zcash-light-client-ffi", exact: "0.9.1") + .package(url: "https://github.com/Electric-Coin-Company/zcash-light-client-ffi", from: "0.10.0") ], targets: [ .target( diff --git a/Sources/ZcashLightClientKit/Block/Actions/RewindAction.swift b/Sources/ZcashLightClientKit/Block/Actions/RewindAction.swift index d5654d6d0..09345b005 100644 --- a/Sources/ZcashLightClientKit/Block/Actions/RewindAction.swift +++ b/Sources/ZcashLightClientKit/Block/Actions/RewindAction.swift @@ -30,14 +30,28 @@ extension RewindAction: Action { var removeBlocksCacheWhenFailed: Bool { false } func run(with context: ActionContext, didUpdate: @escaping (CompactBlockProcessor.Event) async -> Void) async throws -> ActionContext { - guard let rewindHeight = await context.requestedRewindHeight else { + guard let requestedRewindHeight = await context.requestedRewindHeight else { return await update(context: context) } + var rewindHeight = BlockHeight(requestedRewindHeight) logger.debug("Executing rewind.") + let rewindResult = try await rustBackend.rewindToHeight(height: rewindHeight) + switch rewindResult { + case let .success(height): + rewindHeight = height + case let .requestedHeightTooLow(safeHeight): + let retryResult = try await rustBackend.rewindToHeight(height: safeHeight) + switch retryResult { + case let .success(height): + rewindHeight = height + default: + throw ZcashError.rustRewindToHeight(Int32(safeHeight), lastErrorMessage(fallback: "`rewindToHeight` unable to rewind")) + } + } + await downloader.rewind(latestDownloadedBlockHeight: rewindHeight) - try await rustBackend.rewindToHeight(height: Int32(rewindHeight)) - + // clear cache try await downloaderService.rewind(to: rewindHeight) diff --git a/Sources/ZcashLightClientKit/Block/CompactBlockProcessor.swift b/Sources/ZcashLightClientKit/Block/CompactBlockProcessor.swift index b353aa423..0c629af20 100644 --- a/Sources/ZcashLightClientKit/Block/CompactBlockProcessor.swift +++ b/Sources/ZcashLightClientKit/Block/CompactBlockProcessor.swift @@ -320,22 +320,22 @@ extension CompactBlockProcessor { private func doRewind(context: AfterSyncHooksManager.RewindContext) async throws { logger.debug("Executing rewind.") let lastDownloaded = await latestBlocksDataProvider.maxScannedHeight - let height = Int32(context.height ?? lastDownloaded) - - let nearestHeight: Int32 - do { - nearestHeight = try await rustBackend.getNearestRewindHeight(height: height) - } catch { - await failure(error) - return await context.completion(.failure(error)) - } - - // FIXME: [#719] this should be done on the rust layer, https://github.com/zcash/ZcashLightClientKit/issues/719 - let rewindHeight = max(Int32(nearestHeight - 1), Int32(config.walletBirthday)) - + var rewindHeight = BlockHeight(Int32(context.height ?? lastDownloaded) - 10) do { - try await rewindDownloadBlockAction(to: BlockHeight(rewindHeight)) - try await rustBackend.rewindToHeight(height: rewindHeight) + let rewindResult = try await rustBackend.rewindToHeight(height: rewindHeight) + switch rewindResult { + case let .success(height): + rewindHeight = height + case let .requestedHeightTooLow(safeHeight): + let retryResult = try await rustBackend.rewindToHeight(height: safeHeight) + switch retryResult { + case let .success(height): + rewindHeight = height + default: + throw ZcashError.rustRewindToHeight(Int32(safeHeight), lastErrorMessage(fallback: "`rewindToHeight` unable to rewind")) + } + } + try await rewindDownloadBlockAction(to: rewindHeight) } catch { await failure(error) return await context.completion(.failure(error)) diff --git a/Sources/ZcashLightClientKit/Rust/ZcashRustBackend.swift b/Sources/ZcashLightClientKit/Rust/ZcashRustBackend.swift index 04fde49f1..4d409e41b 100644 --- a/Sources/ZcashLightClientKit/Rust/ZcashRustBackend.swift +++ b/Sources/ZcashLightClientKit/Rust/ZcashRustBackend.swift @@ -243,22 +243,6 @@ struct ZcashRustBackend: ZcashRustBackendWelding { return UnifiedAddress(validatedEncoding: address, networkType: networkType) } - @DBActor - func getNearestRewindHeight(height: Int32) async throws -> Int32 { - let result = zcashlc_get_nearest_rewind_height( - dbData.0, - dbData.1, - height, - networkType.networkId - ) - - guard result > 0 else { - throw ZcashError.rustGetNearestRewindHeight(lastErrorMessage(fallback: "`getNearestRewindHeight` failed with unknown error")) - } - - return result - } - @DBActor func getNextAvailableAddress(account: Int32) async throws -> UnifiedAddress { let addressCStr = zcashlc_get_next_available_address( @@ -501,11 +485,16 @@ struct ZcashRustBackend: ZcashRustBackendWelding { } @DBActor - func rewindToHeight(height: Int32) async throws { - let result = zcashlc_rewind_to_height(dbData.0, dbData.1, height, networkType.networkId) - - guard result else { - throw ZcashError.rustRewindToHeight(height, lastErrorMessage(fallback: "`rewindToHeight` failed with unknown error")) + func rewindToHeight(height: BlockHeight) async throws -> RewindResult { + var safeRewindHeight: Int64 = -1; + let result = zcashlc_rewind_to_height(dbData.0, dbData.1, UInt32(height), networkType.networkId, &safeRewindHeight) + + if result >= 0 { + return .success(BlockHeight(result)) + } else if result == -1 && safeRewindHeight > 0 { + return .requestedHeightTooLow(BlockHeight(safeRewindHeight)) + } else { + throw ZcashError.rustRewindToHeight(Int32(height), lastErrorMessage(fallback: "`rewindToHeight` failed with unknown error")) } } diff --git a/Sources/ZcashLightClientKit/Rust/ZcashRustBackendWelding.swift b/Sources/ZcashLightClientKit/Rust/ZcashRustBackendWelding.swift index 890543617..941981105 100644 --- a/Sources/ZcashLightClientKit/Rust/ZcashRustBackendWelding.swift +++ b/Sources/ZcashLightClientKit/Rust/ZcashRustBackendWelding.swift @@ -21,6 +21,17 @@ public enum DbInitResult { case seedNotRelevant } +/// Enumeration of potential return states for database rewind. +/// +public enum RewindResult { + /// The rewind succeeded. The associated block height indicates the maximum height of + /// stored block data retained by the database; this may be less than the block height that + /// was requested. + case success(BlockHeight) + /// The rewind did not succeed but the caller may re-attempt given the associated block height. + case requestedHeightTooLow(BlockHeight) +} + protocol ZcashRustBackendWelding { /// Returns a list of the accounts in the wallet. func listAccounts() async throws -> [Int32] @@ -64,18 +75,6 @@ protocol ZcashRustBackendWelding { /// - `rustGetCurrentAddressInvalidAddress` if generated unified address isn't valid. func getCurrentAddress(account: Int32) async throws -> UnifiedAddress - /// Wallets might need to be rewound because of a reorg, or by user request. - /// There are times where the wallet could get out of sync for many reasons and - /// users might be asked to rescan their wallets in order to fix that. This function - /// returns the nearest height where a rewind is possible. Currently pruning gets rid - /// of sapling witnesses older than 100 blocks. So in order to reconstruct the witness - /// tree that allows to spend notes from the given wallet the rewind can't be more than - /// 100 blocks or back to the oldest unspent note that this wallet contains. - /// - parameter height: height you would like to rewind to. - /// - Returns: the blockheight of the nearest rewind height. - /// - Throws: `rustGetNearestRewindHeight`. - func getNearestRewindHeight(height: Int32) async throws -> Int32 - /// Returns a newly-generated unified payment address for the specified account, with the next available diversifier. /// - parameter account: index of the given account /// - Throws: @@ -123,11 +122,11 @@ protocol ZcashRustBackendWelding { /// Resets the state of the database to only contain block and transaction information up to the given height. clears up all derived data as well /// - parameter height: height to rewind to. /// - Throws: `rustRewindToHeight` if rust layer returns error. - func rewindToHeight(height: Int32) async throws + func rewindToHeight(height: BlockHeight) async throws -> RewindResult /// Resets the state of the FsBlock database to only contain block and transaction information up to the given height. /// - Note: this does not delete the files. Only rolls back the database. - /// - parameter height: height to rewind to. DON'T PASS ARBITRARY HEIGHT. Use `getNearestRewindHeight` when unsure + /// - parameter height: height to rewind to. This should be the height returned by a successful `rewindToHeight` call. /// - Throws: `rustRewindCacheToHeight` if rust layer returns error. func rewindCacheToHeight(height: Int32) async throws