Skip to content

MOB-1678: Migrate CHP voting to the v1.3.0 chain (poly-aware PIR, auth v2, round-bound shares) - #2425

Merged
github-actions[bot] merged 7 commits into
mainfrom
bump/mob-1678-coinholder-polling
Aug 18, 2026
Merged

MOB-1678: Migrate CHP voting to the v1.3.0 chain (poly-aware PIR, auth v2, round-bound shares)#2425
github-actions[bot] merged 7 commits into
mainfrom
bump/mob-1678-coinholder-polling

Conversation

@nesence-m

Copy link
Copy Markdown
Contributor

Summary

  • Migrates coinholder polling to the v1.3.0 vote chain (prod flipped 2026-08-14, no backward-serving). Android's 2.0-generation stack currently 502s on every tier-1 PIR query (hardcoded poly length 2048 vs the new 4096 dataset) and drops all rounds as missingRound (v1-only round authenticator vs v2-signed rounds).
  • VotingPirLayout gains polyLen (wire key pir_layout.poly_len from the signed dynamic config; decode-tolerant for cached pre-3.0 configs), threaded through every delegation entry point into the SDK's new pirPolyLen parameters with a fail-closed requireKnownPolyLen() guard reusing existing config-error copy.
  • RoundAuthenticator rewritten 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 chain ea_pk binding check is kept. Tests port the crate's golden vectors plus a full negative suite.
  • Helper share submissions now forward the crate-authoritative 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 stale all_enc_shares field the wire struct removed in 2.0-rc.4. A wire-contract test pins that nothing is injected app-side.
  • Maps the four new 3.0 error fingerprints ("PIR poly_len mismatch", "unsupported PIR layout", vote-tree VAN mismatch/absence) onto existing error copy — no new strings.
  • Repoints SDK_COMMIT_PIN to 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-voting unit tests 123/123 (incl. new RoundAuthenticatorTest golden vectors — independently cross-checked against the crate's own test vector — and SharePostBodyWireContractTest)
  • ktlint + detektAll clean
  • Full :app:assembleZcashtestnetInternalDebug against the companion SDK worktree
  • Live stage e2e on-device: v2 rounds authenticate (poll list repopulates), tier-1 PIR queries 200 on the 4096 dataset, full delegation → vote → shares round accepted (pending)

🤖 Generated with Claude Code

nesence-m and others added 3 commits August 14, 2026 16:21
…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>
@linear

linear Bot commented Aug 14, 2026

Copy link
Copy Markdown

MOB-1678

nesence-m and others added 3 commits August 17, 2026 13:25
…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
nesence-m requested a review from noop-sk August 18, 2026 08:02
@nesence-m
nesence-m disabled auto-merge August 18, 2026 08:30
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions
github-actions Bot enabled auto-merge (squash) August 18, 2026 10:47
@github-actions
github-actions Bot merged commit ff3cbd3 into main Aug 18, 2026
14 of 15 checks passed
@github-actions
github-actions Bot deleted the bump/mob-1678-coinholder-polling branch August 18, 2026 11:44
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.

2 participants