MOB-1678: Migrate CHP voting to the v1.3.0 chain (poly-aware PIR, auth v2, round-bound shares) - #2425
Merged
Merged
Conversation
…e fixes (MOB-1678)
Follows the SDK's zcash_voting 3.0.0-rc.3 bump to keep the app working against
Valargroup's v1.3.0 vote chain (poly_len-aware PIR, auth-v2-signed rounds).
Config/threading: VotingPirLayout gains polyLen (wire key poly_len, decode-optional
default 0 so cached pre-3.0 configs still parse). Threads through the existing
pirLayout flow into VotingCryptoClient's precomputeDelegationPir/buildAndProveDelegation,
which now pass pirLayout.polyLen as the SDK's new pirPolyLen JNI param. Added a
requireKnownPolyLen() fail-closed guard, called both at each upstream delegation entry
point (PrepareVotingRoundUseCase, SubmitVotesUseCase, VotingKeystoneRepository) for
fail-fast behavior and at the VotingCryptoClient boundary itself as the definitive
"before any FFI call" gate, reusing the existing VotingConfigException copy path.
RoundAuthenticator v2 rewrite: AUTH_VERSION_V1 replaced with AUTH_VERSION_V2; v1
entries are rejected outright (deliberate, no dual-accept). Signature verification now
covers the byte-exact v2 preimage — "zcash-shielded-vote:round-auth:v2" (33B) || round_id
(32B strict-hex-decoded) || ea_pk (32B) || pir_depth || tier0_layers || tier1_layers ||
poly_len, each u32 little-endian — matching zcash_voting::round_auth::RoundAuthPayloadV2.
authenticate()/verifyEntrySignatures() gain roundIdHex/pirLayout params; both call sites
(VotingServiceConfig's retainingRoundsWithValidSignatures and VotingApiProvider's
authenticateVotingSession) pass the pirLayout from the same config object the rounds
came from, since the signature binds them. Ported the crate's two golden vectors
(admin-UI signed entry, wire-format byte layout) plus the full negative suite (v1
rejected, tampered sig, replayed round id, swapped poly_len, missing polyLen,
undecodable round id) into RoundAuthenticatorTest.
Share wire fix: dropped the stale all_enc_shares field from the share-delegation POST
body — the crate's VoteShareWire wire struct removed it in 2.0-rc.4, confirmed against
zcash_voting 3.0.0-rc.3's wire.rs. Deviation from plan: full "forward crate JSON
verbatim" was not implemented — the SDK's buildSharePayloadsNative/JniSharePayload does
not yet expose the crate's SharePayload::to_wire_json() (no vote_round_id field on the
JNI model), so roundIdHex stays app-injected. Pinned the corrected wire shape with
SharePostBodyWireContractTest (iOS's SharePostBodyWireContractTests analog); added a
testImplementation org.json:json dependency since this module's plain-JVM unit tests
stub org.json otherwise.
Error mapping: added the four 3.0 fingerprints ("PIR poly_len mismatch", "unsupported
PIR layout", "does not match its synced vote-tree leaf", "is absent from the synced
vote tree") onto existing VotingErrorMapper copy, mirroring iOS's mapping exactly.
Regenerated the detekt baseline entries for RoundAuthenticator.kt's shifted function
signatures (scoped diff only — unrelated pre-existing baseline drift left untouched).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…OB-1678) Follow-up to the SDK's SharePayload.voteRoundId exposure: the app now sources vote_round_id from the crate-populated field on every SharePayload instead of injecting a caller-supplied roundIdHex into the share POST body. - SharePayload gains voteRoundId: String; VotingSubmissionParsers.toSharePayloads() parses it from the (now crate-sourced) share-payload JSON, and VotingCryptoClient's toSharePayloadsJson() writes it from the SDK's VotingSharePayload.voteRoundId. - SharePayload.toApiBody() drops its roundIdHex parameter and reads voteRoundId off the payload directly. - VotingApiProvider.delegateShares/resubmitShare drop their now-unused roundIdHex parameters; SubmitVotesUseCase.delegateSharesWithRetry and its call site, and TrackVotingSharesUseCase's resubmitShare call, follow suit. - SharePostBodyWireContractTest now pins that vote_round_id in the wire body is the payload's own value verbatim (including a negative case proving nothing app-side can override it), not an injected parameter. - Regenerated the detekt baseline for delegateSharesWithRetry's shifted signature (scoped diff only). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ry (MOB-1678) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
6 tasks
…der-polling # Conflicts: # gradle.properties
…der-polling # Conflicts: # CHANGELOG.md # gradle.properties
The SDK bump/MOB-1678 branch merged origin/main (v3.0.2 back-merge, MOB-1717 submit-plan readback, MOB-1563 semgrep cleanup); the pin moves from the pre-merge head 860fb823 to the pushed merge commit 1eb88749 so CI builds against the SDK state this branch's own main merge expects. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
nesence-m
disabled auto-merge
August 18, 2026 08:30
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
noop-sk
approved these changes
Aug 18, 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.
Summary
missingRound(v1-only round authenticator vs v2-signed rounds).VotingPirLayoutgainspolyLen(wire keypir_layout.poly_lenfrom the signed dynamic config; decode-tolerant for cached pre-3.0 configs), threaded through every delegation entry point into the SDK's newpirPolyLenparameters with a fail-closedrequireKnownPolyLen()guard reusing existing config-error copy.RoundAuthenticatorrewritten to auth v2 only: the Ed25519 signature now covers"zcash-shielded-vote:round-auth:v2" ‖ round_id ‖ ea_pk ‖ pir_depth ‖ tier0_layers ‖ tier1_layers ‖ poly_len(u32 LE), binding each round to the exact PIR geometry it is queried with. v1 entries are rejected outright (crate parity, no dual-accept); the separate chainea_pkbinding check is kept. Tests port the crate's golden vectors plus a full negative suite.vote_round_id(new in zcash_voting 3.0.0-rc.3, exposed by the companion SDK PR) instead of injecting it app-side, and drop the staleall_enc_sharesfield the wire struct removed in 2.0-rc.4. A wire-contract test pins that nothing is injected app-side.SDK_COMMIT_PINto the companion SDK branch head (to be repointed to the merged main sha once the SDK PR lands, per the property's contract).Android analog of the iOS migration (zodl-inc/zodl-ios#1992); reference integration chainapsis/vizor-wallet#506+#507.
Companion SDK PR: zcash/zcash-android-wallet-sdk#2178
Test plan
feature-votingunit tests 123/123 (incl. newRoundAuthenticatorTestgolden vectors — independently cross-checked against the crate's own test vector — andSharePostBodyWireContractTest):app:assembleZcashtestnetInternalDebugagainst the companion SDK worktree🤖 Generated with Claude Code