> For the complete documentation index, see [llms.txt](https://valargroup.gitbook.io/shielded-vote-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://valargroup.gitbook.io/shielded-vote-docs/circuit-components/shares-range-check.md).

# Shares Range Check

**Used in:** ZKP #2 (condition 9)

## What it does

Constrains each voting share to be in the range `[0, 2^30)`. Shares are denominated in ballots (1 ballot = 0.125 ZEC; converted from zatoshi by ZKP #1 condition 8).

## Circuit

For each `shares_j` (j = 0..15):

```
0 <= shares_j < 2^30
```

Implemented as a 3 × 10-bit lookup running-sum check using `LookupRangeCheckConfig::copy_check` (strict mode). The prover commits to the share value and the circuit decomposes it into three 10-bit words; the strict flag forces the final running sum `z_3 = 0`, proving no higher bits are set.

## Why range check at all

The range check is required for two reasons:

1. **Base/scalar field correspondence (soundness).** The shares sum constraint (condition 8) holds in the Pallas base field F\_p, but El Gamal encryption (condition 11) operates in the scalar field F\_q via `share_i * G`. Since p ≠ q for Pallas, a large base-field element (e.g. `p − 50`) reduces to a different value mod q, breaking the link between the constrained sum and the encrypted tally values. Bounding each share to `[0, 2^30)` ensures both field representations agree (no modular reduction in either field), so the homomorphic tally faithfully reflects condition 8's sum. Without this, a malicious prover could craft shares that satisfy the base-field sum while producing arbitrary scalars in the El Gamal ciphertexts.
2. **DLOG recovery at tally (performance).** After homomorphic accumulation, the EA decrypts to `total_value * G` and must solve a bounded discrete log (baby-step giant-step, O(√n)) to recover `total_value`. Bounded shares keep the per-decision aggregate small enough for efficient recovery.

## Why 30 bits

`halo2_gadgets` v0.3's `short_range_check` is `pub(crate)` and not accessible from outside the library, so exact non-10-bit-aligned bounds (e.g. 24-bit) are unavailable. The next available 10-bit-aligned bound is 3 × 10 = 30 bits. This is more than sufficient: `2^30` ballots ≈ 134M ZEC — well above the 21M ZEC supply — so the bound is never binding in practice. Max sum of 16 shares ≈ 17.2B ballots, well within the Pallas field.

## Note on decomposition sharing with condition 11

The range decomposition produced here cannot be shared with condition 11's `[shares_i] * G` fixed-base scalar multiplication. The two gadgets use incompatible schemes:

* **Condition 9** (`LookupRangeCheckConfig`): `advices[9]` column, 10-bit lookup words, 3 words → `[z_0, z_1, z_2, z_3]`
* **Condition 11** (`FixedPointBaseField::mul`): dedicated ECC `window` column, 3-bit range-gate windows, 85 windows → `[z_0, …, z_85]`

The returned `ScalarVar::BaseFieldElem` exposes only the original `AssignedCell`, not the 86-element internal running sum. Sharing would require a custom short-fixed-base gadget or an upstream halo2\_gadgets API change.
