Ironwood (NU6.3) v1 on master — CocoaPods, no vendored SwiftPM deps - #72
Merged
Conversation
Points update-sources at the Swift SDK's 2.7.0-rc.4 tag - the release the Zcash team confirmed production-ready for Ironwood - and bumps the Android SDK (and the incubator) from 2.5.2 to the same release. The Swift side is pinned by commit rather than tag name, so the checkout is immutable even if the tag is ever moved. The Android release ships Kotlin 2.3 metadata, which the app already provides. update-sources no longer builds the FFI's download URL from a version string. It reads the url/checksum pair out of the pinned checkout's own binaryTarget, which is the one place upstream writes down which FFI build matches this source - so a source/binary mismatch is unrepresentable. LIBZCASHLC_XCFRAMEWORK_SHA256 remains as the reviewed copy of that checksum, and the build stops if the two disagree, so bumping the SDK pin has to change native code deliberately rather than silently. Also carries the two other build inputs Ironwood needs. TreeState.ironwoodTree (field 7) in the bundled lightwalletd proto: update-checkpoints writes the GetTreeState response verbatim, so a field missing from our proto is never decoded and silently never written - every checkpoint generated after activation would have shipped without Ironwood tree state. Pre-activation output is unchanged, so existing checkpoints need no regeneration. And the regtest checkpoint directory now resolves through Bundle.main, since the SwiftPM-only Bundle.module accessor does not exist in the CocoaPods build.
Reports the Ironwood pool alongside transparent/sapling/orchard on both platforms, and folds it into the deprecated summed fields so funds do not appear to vanish from them once value starts crossing into Ironwood.
Detection plus the v1 sweep, identical on both platforms. Synchronizer.proposeOrchardToIronwoodMigration builds the sweep: the SDK spends every Orchard note to the account's own address with the fee chosen so no Orchard change remains, leaving Sapling and transparent funds untouched, and it executes through the ordinary createTransfer pipeline. Neither SDK reports a payment value on the proposal, so both platforms quote the crossing amount the same way - the Orchard balance minus that fee - and fail rather than quote a figure they cannot source. Tools.getIronwoodActivationHeight is stateless and served from consensus constants (ZIP 258), which neither SDK exposes, so it answers on both platforms without an SDK call. There is deliberately no migration state to poll: the app decides whether to offer a sweep from the activation height, sync state and Orchard balance, and broadcasting the sweep spends those notes, which withdraws the offer on its own.
The synchronizer's event stream only carries transactions newly found in scanned blocks or newly mined, and the native module drops every event until JavaScript attaches a listener. A transaction that settled while nothing was listening - mined while the app was closed, or during a failed sync - is therefore neither newly found nor newly mined on the next launch, so it was never reported again: it stayed at height 0, "pending", forever. Synchronizer.subscribe now asks native for the current transaction set once its listeners are attached. Driving it from JavaScript is what makes the delivery reliable: emitting during initialize would race the app's own subscribe call and lose, because hasListeners is still false there. Both platforms are affected. iOS never dumps the existing set outside its restart path. Android's collector does deliver the full list, but on its first emission during initialize, which is the same race - one it usually wins, which is not the same as being correct. Re-sending known transactions is harmless: the app updates only those whose height or amount actually changed, and its new-transaction notification path is not reached for transactions it already has.
Contributor
|
Approved, make sure we get the latest ironwood checkpoint |
j0ntz
approved these changes
Aug 1, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
CHANGELOG
Does this branch warrant an entry to the CHANGELOG?
Dependencies
none — this branch sits directly on
master.Description
Ironwood (NU6.3) v1 on master, CocoaPods only
The same four Ironwood commits as the earlier stacked work, rebased onto
masterand with the SwiftPM vendored-deps machinery removed. No stacked base, nobuildVendoredDeps.ts, nolibZcashDeps.xcframework— the podspec keeps master'sgRPC-Swift ~> 1.8unchanged.The motivation:
#71was based onzcash-spm-deps-binaryrather thanmaster, so merging it landed Ironwood on that stack branch and never onmaster. The 0.13.0 publish that followed was cut frommasterand therefore shipped without any Ironwood code (verified against the published tarball: zero occurrences ofproposeOrchardToIronwoodMigration, andzcash-android-sdk:2.5.2). This branch is mergeable tomasteron its own.What's here
update-sourcesno longer builds the FFI download URL from a version string: it reads the url/checksum pair out of the pinned checkout's ownbinaryTarget, so a source/binary mismatch is unrepresentable, cross-checked against the reviewedLIBZCASHLC_XCFRAMEWORK_SHA256. Also carriesTreeState.ironwoodTree(field 7) for checkpoint generation, and resolves the regtest checkpoint directory throughBundle.mainsince the SwiftPM-onlyBundle.moduleaccessor does not exist in the CocoaPods build.Synchronizer.proposeOrchardToIronwoodMigrationbuilds the sweep (every Orchard note to the wallet's own address, fee chosen so no Orchard change remains, Sapling and transparent untouched, all-or-nothing).Tools.getIronwoodActivationHeightanswers from consensus constants (ZIP 258). No migration state to poll.subscribenow asks native for the current set once its listeners are attached. Not Ironwood-specific; could land independently.Verification
update-sourcesregenerated from the rc.4 tag (fb9f6cf4 Prepare release 2.7.0-rc.4); FFI downloaded and checksum-verified; noios/vendoredproduced.pod installresolves gRPC-Swift 1.8.0 + SwiftNIO 2.x from CocoaPods.react-native-zcashpod target: BUILD SUCCEEDED (arm64 + x86_64 simulator).Edge.debug.dylib—proposeOrchardToIronwoodMigration(118),ironwoodActivationHeight(6),emitExistingTransactions(30), gRPC/NIO linked from pods (55,535). NolibZcashDepsanywhere in the bundle.eslintandrollup + tscclean.This confirms rc.4 compiles against CocoaPods gRPC-Swift 1.8, matching the earlier rc.2 finding that the SDK's declared grpc-swift 1.24.2 dependency is not reflected in the API surface its generated code actually uses.
Known gaps
android/,ios/andsrc/, so behaviour should be unchanged, but the runtime flows have not been re-run on a pods build.zcash-spm-deps-binary(0bc93ba), which rewrites that checkpoint with Ironwood tree state. Worth cherry-picking before merge.