Skip to content

[DO NOT MERGE] zcash-android-wallet-sdk v2.6.0 release stabilization preview#1947

Draft
nuttycom wants to merge 212 commits into
release/v2.6.0from
main
Draft

[DO NOT MERGE] zcash-android-wallet-sdk v2.6.0 release stabilization preview#1947
nuttycom wants to merge 212 commits into
release/v2.6.0from
main

Conversation

@nuttycom

Copy link
Copy Markdown
Contributor

This branch serves as a preview of the full change set that will go into the zcash-swift-wallet-sdk version 2.6.0 release. It should be used for review of the unified changeset, and will be merged once main is deemed ready for release stabilization.

czarcas7ic and others added 30 commits April 5, 2026 01:03
When creating a new wallet, fetch the current chain tip's tree state
from the server instead of using the bundled checkpoint. This sets the
wallet birthday to the chain tip height, producing zero scan ranges
and eliminating unnecessary block scanning for wallets that have no
transaction history.

Falls back to the bundled checkpoint if the server is unreachable.
…pshot-accessors

[#1921] Expose Synchronizer snapshot accessors
Bumps [actions/cache](https://github.com/actions/cache) from 5.0.4 to 5.0.5.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](actions/cache@6682284...27d5ce7)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-version: 5.0.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Bring the Android Rust backend onto the released zcash_voting dependency line needed for shielded voting while keeping the initial JNI surface intentionally narrow.

The exposed VotingRustBackend.computeShareNullifier symbol is a pure linkage check over caller-supplied bytes. It proves Android can resolve, link, and dispatch into zcash_voting without adding wallet database access, voting database lifecycle, PIR or tree-sync networking, or a public SDK voting API.

orchard is pinned to =0.13.1 with the unstable-voting-circuits feature enabled, matching the Swift foundation split shape. No fork URLs or patch entries are introduced.

Document voting dependency sources

Rephrase voting dependency comments
[#1924] Add zcash_voting dependency foundation
Addresses PR #1938 review comment #2.
Addresses PR #1938 review comment #3.
Addresses PR #1938 review comment #5.
Addresses PR #1938 review comment #6.
Addresses PR #1938 review comment #7.
Addresses PR #1938 review comment #8.
Cosmos-Harry and others added 30 commits June 14, 2026 01:17
The legacy Synchronizer.createProposedTransactions and
Synchronizer.createTransactionFromPczt APIs bypassed
PendingSubmitPlanStore, leaving their transactions at the null plan
state. The sync loop's resubmitUnminedTransactions step would then hit
the null-fallback branch and call txManager.submit() directly within
milliseconds of the user's original submit, racing mempool gossip and
producing "transaction already exists in mempool" errors that surfaced
as TransactionSubmitResult.Failure.

The legacy helpers now wrap creation in createAndMarkAwaitingSubmitPlan
and call addSubmitEndpoint before submit — same machinery the public
Broadcaster API already uses. The resubmit loop correctly skips txs in
AwaitingPlan state and uses SubmitPlanExecutor for Ready ones.
- Update version to 2.6.3
- Update CHANGELOG
- Generate new checkpoints
The earlier substring-on-error-message approach was fragile in two
ways: error texts can change across node releases, and the specific
phrases matched are only emitted by Zebra-backed lightwalletd (zcashd's
sendrawtransaction silently re-broadcasts duplicates and returns
success).

Replace with server-side verification: when toSubmitResult classifies
a non-gRPC Failure, the submit extension now calls fetchTransaction on
the same lightwalletd. If the server reports the tx is known (anything
other than a notFound-style response), the broadcast already landed and
we reclassify as Success. Otherwise the original Failure propagates
unchanged.

This works for Zebra's MempoolError::InMempool / AlreadyQueued,
zcashd's RPC_VERIFY_ALREADY_IN_CHAIN, and any future "already known"
variant without depending on backend-specific error codes or message
text. The verification call costs one extra RPC on the rare submit-
failure path and falls back to surfacing the original failure if the
verification itself fails, so the worst case matches today's behaviour.
Copilot review on this PR caught that addSubmitEndpoint(...) was being
called before transactionSubmitter.submit(...). That ordering
transitioned the plan store from AwaitingPlan → Ready(endpoints)
before the actual submit RPC fired, leaving an in-flight window
where the sync loop's resubmitUnminedTransactions step no longer
saw AwaitingPlan and could race the in-flight broadcast.

Swap the order on both legacy helpers AND the public
Broadcaster.submit so the plan-store registration happens after the
submit returns. The same race window existed in the public path
even though it isn't the focus of this PR — fixing it here keeps
the three call sites consistent.

Rename the existing "mark_awaiting_plan_before_submit_completes"
test to honestly describe what it covers (the create-in-progress
mutex window) and add two new tests that actually pause the
submitter mid-flight to verify the plan stays AwaitingPlan during
the submit RPC — one for the legacy helper, one for the public
Broadcaster.submit.
- Update version to 2.6.4
- Update CHANGELOG
- Generate new checkpoints
…pool-as-success

Treat "already in mempool" submit rejections as success
…bmit-via-plan-store

# Conflicts:
#	CHANGELOG.md
- Remove needless borrows on format! calls in eip681.rs (clippy::needless_borrows_for_generic_args)
- Replace .to_vec() with .as_ref() in delegation.rs test helpers (clippy::unnecessary_to_owned)
- Suppress too_many_arguments on build_governance_pczt_for_bundle (10 args, threshold 7)
Runs cargo clippy --tests --all-features with -W clippy::all -D warnings
against the backend-lib crate on every pull request.
…rding

Code review on this PR caught three things in the recent reorder-to-
record-after-submit change:

1) If transactionSubmitter.submit(...) throws (realistically only
   CancellationException — app teardown, scope cancel — since gRPC
   errors are mapped to Response.Failure), addSubmitEndpoint never
   runs. The tx is persisted at AwaitingPlan by createAndMark...,
   survives a process kill, and the resubmit loop skips AwaitingPlan
   forever. Wrap addSubmitEndpoint in try/finally with NonCancellable,
   matching the EndpointTransactionSubmitter dispose pattern already
   used in this file. The three call sites fold into a private
   recordingEndpointAfterSubmit helper.

2) createSubmitResultFlow short-circuits on the first Failure and
   subsequent txs are returned as NotAttempted, so addSubmitEndpoint
   is not called for them and they stay AwaitingPlan. For dependent
   multi-step proposals (shield → spend) this is the desired behavior;
   add a one-liner so future readers don't read it as a latent bug.

3) The post-submit comment said SubmitPlanExecutor "dedupes against
   the endpoints recorded here." It doesn't — it just iterates
   submitPlan.endpoints.distinct() and stops on first non-Failure.
   Replace with "retries through the endpoints recorded here."

New test broadcaster_submit_records_endpoint_even_if_cancelled_mid_submit
pins the cancellation-survival contract.
…tion

Review on this PR pointed out that try/finally + NonCancellable runs
addSubmitEndpoint on every exit path, not just CancellationException
— and asked whether transitioning a non-cancellation-throw plan from
AwaitingPlan → Ready is intentional.

It is, and "stay AwaitingPlan on non-cancellation throws" would re-
introduce the same stranding bug the cancellation fix was meant to
avoid: the resubmit loop skips AwaitingPlan entries, treating them as
never-submitted. Recording the endpoint transitions to Ready, which
the resubmit loop retries through SubmitPlanExecutor.

Lock in the contract with a new test that has the submitter throw an
IOException mid-submit and asserts the plan is at Ready afterward.
Tighten the comment on the helper to make the "every exit path"
intent explicit.
@nuttycom noted on review that resubmitUnminedTransactions doesn't
actually query the mempool — it's DB-driven (findUnminedTransactions
WithinExpiry). So a tx that's been accepted into the server's mempool
but not yet mined will still be re-broadcast on the next sync tick,
even with the plan-store dance. The original CHANGELOG wording made
the fix sound like it eliminated mempool-duplication entirely; it
doesn't.

What this PR actually closes: the in-flight window during the active
submit() RPC. Before this PR the legacy paths didn't write to the
plan store at all, so a sync-loop tick during the few-second submit
RPC could fire a second submit concurrently. AwaitingPlan now blocks
the sync loop during that window.

The mempool-pending (post-submit, pre-mining) duplication case is
handled by the verify-against-server reclassification that already
landed on main (the second ## Fixed bullet) — it re-checks the server
via fetchTransaction and reclassifies "already in mempool" as
Success.

Update the bullet to scope the claim to the in-flight race
specifically and cross-reference the mempool-duplication remediation.
…n-store

[MOB-1339] Route legacy create-and-submit through PendingSubmitPlanStore
Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.3 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](actions/checkout@df4cb1c...9c091bb)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
…/checkout-7.0.0

chore(deps): bump actions/checkout from 6.0.3 to 7.0.0
Harmonize rust toolchain versions across CI and local dev
chore: add shellcheck job to pull-request CI workflow
Fix clippy warnings + add a job in the CI
Replace the hand-rolled rustup install/default/target/component sequence
in the setup composite action with the dtolnay/rust-toolchain action, as
librustzcash does. The action installs the pinned toolchain, the Android
targets, and clippy, and sets it as the rustup default, so behaviour is
unchanged. Pinned to a commit SHA per the repo's action-pinning
convention.
CI: use dtolnay/rust-toolchain action instead of manual rustup
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.