[#1924] Add Android voting delegation prep and signing#1942
Merged
Conversation
p0mvn
reviewed
May 11, 2026
czarcas7ic
reviewed
May 11, 2026
noop-sk
reviewed
May 11, 2026
noop-sk
reviewed
May 11, 2026
greg0x
added a commit
to valargroup/zcash-android-wallet-sdk
that referenced
this pull request
May 11, 2026
greg0x
added a commit
to valargroup/zcash-android-wallet-sdk
that referenced
this pull request
May 11, 2026
Review-thread: zcash#1942 (comment)
greg0x
added a commit
to valargroup/zcash-android-wallet-sdk
that referenced
this pull request
May 11, 2026
Review-thread: zcash#1942 (comment)
563de2a to
313157f
Compare
d808d3f to
442130a
Compare
zcash_voting 0.5.8 persists bundle note identities and enforces them during governance PCZT construction, so the temporary Android sidecar table is obsolete. Route setup through VotingDb::setup_bundles and let the core library own identity validation, keeping the JNI layer aligned with Swift's integration path.
Review-thread: #1942 (comment)
Review-thread: #1942 (comment)
Review-thread: #1942 (comment)
Review-thread: #1942 (comment)
Review-thread: #1942 (comment)
442130a to
589900b
Compare
noop-sk
reviewed
May 12, 2026
noop-sk
reviewed
May 12, 2026
13 tasks
nullcopy
approved these changes
May 12, 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.
Part of zodl-inc/zodl-android#2193.
Slice of #1924 (umbrella draft PR being split into reviewable pieces).
Depends on #1938 (second slice — voting DB handle and round lifecycle).
Resolves MOB-1109
Why
Third slice of the Android shielded-voting integration. #1938 added the voting DB handle and round lifecycle; this PR adds the prep and signing surface needed before delegation proof generation: bundle setup with per-bundle weights, hotkey derivation, governance PCZT construction, sighash extraction, and spend-auth signature extraction. Proving-cache warmup is included so callers can amortize key loading before the first proof.
Vertical SDK slice — each new JNI symbol has a matching Rust handler, Kotlin
external fun, FFI model, and typesafe wrapper. No app-side wiring yet — that follows once the proof-generation slice lands.What's in
Rust / JNI (
backend-lib/src/main/rust/voting/):notes.rs—computeBundleSetup,setupBundles,generateHotkeyrounds.rs—getBundleCount(added to existing module)delegation.rs—buildGovernancePcztJson,extractPcztSighash,extractSpendAuthSigutil.rs—warmProvingCacheshelpers.rs— width-checked casts (u32_to_jint,u64_to_jlong,usize_to_jint),network_from_id,orchard_fvk_bytes,hotkey_orchard_raw_address_from_wallet_seed,coin_type_for,NU6_BRANCH_ID, FFI builders forFfiVotingHotkeyandFfiBundleSetupResult, round-phase guards (update_round_phase_forward,require_round_phase_for_delegation_construction), bundle helpers (bundle_setup_from_notes,bundled_notes_for_index)json.rs—JsonNoteInfo,JsonGovernancePczt(trimmed envelope), hex enc/dec,json_from_jstringBundle note identity persistence and validation are owned by
zcash_voting 0.5.8(VotingDb::setup_bundles+build_governance_pczt) — the Android side does not maintain a sidecar table.Kotlin (
sdk-lib/.../internal/):jni/VotingRustBackend.kt—external fundeclarations for the new surfaceTypesafeVotingBackend.kt+ impl —suspendwrappers,native { … }helper that loads the library and dispatches onDispatchers.IO, JSON →GovernancePcztResultparsing using the SDK's existingString.fromHexextensionmodel/voting/FfiVotingModels.kt—FfiBundleSetupResult,FfiVotingHotkey(publicKey+addressonly — secret key stays in Rust),HotkeyPublicKey(size-validated constructor)jni/JniConstants.kt—JNI_HOTKEY_PUBLIC_KEY_BYTES_SIZEext/BlockExt.kt—String.fromHex()made strict (rejects odd length and non-hex characters) now that it's on the JNI parse pathNot included
Review focus
Primary review track: core-dev. Please look at:
pczt_bytes/rk/action_index/pczt_sighashcross JNI. The rest of the delegation-data fields (alpha,rseed_*,padded_note_secrets,gov_nullifiers, …) are persisted byzcash_voting'sstore_delegation_dataand read back in the proof-generation slice.java_bytes,java_bytes_at_least,java_bytes32, fixed-widthrequire_len/require_min_len, and width-checkedu32/u64↔jint/jlongcasts.hotkey_orchard_raw_address_from_wallet_seedderives the Orchard raw address from the wallet seed at the suppliedaccount_index, usingScope::Externalat index 0. The 32-byte hotkey secret key is intentionally not surfaced across JNI — it stays insideVotingDband is used internally during PCZT construction / proof generation.update_round_phase_forwarduses an atomic conditional UPDATE (WHERE phase < ?1), so two concurrent callers cannot both advance from the same starting phase; idempotent re-advances returnOk, regressions returnErr.buildGovernancePcztis gated byrequire_round_phase_for_delegation_construction: requiresHotkeyGenerated, rejects if the round has already advanced pastDelegationConstructed.setupBundlesparity / retry contract — the chunker output is recomputed in Rust and compared againstVotingDb::setup_bundles' return values to catch divergence; on mismatch the JNI returnsErr. The DB write has already happened at that point, so callers mustclearRoundand retry; this is a hard-stop bug indicator, not a recoverable error.extractSpendAuthSigfailure mode — returns a descriptiveErrif the action ataction_indexhas nospend_auth_sig(e.g. PCZT not yet signed) oraction_indexis out of bounds; never silently returns zeroed bytes.native { }helper inTypesafeVotingBackendImpl.kt— callsRustBackend.loadLibrary()before every dispatched block; idempotent and matches the pattern inRustDerivationTool/RustEip681Tool/TorClient.internaland minimal.Swift counterpart:
Validation
cargo check --manifest-path backend-lib/Cargo.toml --locked(pinned tozcash_voting 0.5.8)cargo fmt --manifest-path backend-lib/Cargo.toml --check./gradlew :backend-lib:compileReleaseKotlin :sdk-lib:compileReleaseKotlin./gradlew ktlint detektAll./gradlew checkPropertiesgit diff --checkAuthor
Reviewer