> 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/ballot-scaling.md).

# Ballot Scaling

**Used in:** ZKP #1 (condition 8)

## What it does

Converts a raw zatoshi balance into a ballot count by floor-dividing by 12,500,000, and enforces that the result is at least 1. This serves two purposes:

1. **Dust prevention** — delegations worth less than 0.125 ZEC cannot produce a ballot, preventing dust-delegations from bloating chain state.
2. **Bit-width reduction** — expressing vote values in ballots instead of raw zatoshi lowers the number of bits downstream computations operate on, making El-Gamal encryption, El-Gamal decryption (bounded discrete-log search), and several range checks in later ZKPs significantly faster.

## Circuit

Given `snapshot_balance = sum(v_i for all notes)`:

```
num_ballots * 12_500_000 + remainder = snapshot_balance
0 < num_ballots <= 2^30
0 <= remainder < 2^24
```

The prover supplies `num_ballots` and `remainder` as witnesses. The circuit checks:

1. **Reconstruction** — `num_ballots * 12_500_000 + remainder = snapshot_balance`.
2. **`remainder` is a 24-bit number** — 24-bit range check (see [implementation note](#implementation-note-10-bit-lookup-range-checks) below).
3. **`num_ballots` is non-zero and bounded** — proved by witnessing `nb_minus_one = num_ballots - 1`, constraining `nb_minus_one + 1 == num_ballots`, and range-checking `nb_minus_one` to 30 bits. This single check enforces both `num_ballots > 0` and `num_ballots <= 2^30`. If `num_ballots = 0`, `nb_minus_one` wraps to `p - 1 ≈ 2^254`, which fails the range check.

The 30-bit upper bound allows up to `2^30 ≈ 1.07 billion` ballots, equivalent to `2^30 * 0.125 ZEC ≈ 134 million ZEC` — well above the 21M ZEC supply cap.

## Implementation note: 10-bit lookup range checks

The halo2 circuit uses a **10-bit lookup table** for all range checks, which decomposes a value into 10-bit words and verifies each word is in `[0, 1024)`. Because the lookup operates in fixed 10-bit words, it can only directly enforce bit-widths that are multiples of 10.

To enforce an exact N-bit range where N is not a multiple of 10, the circuit **multiplies the value by `2^(k - N)`** (where `k` is the next multiple of 10 ≥ N) before feeding it into the range check. This left-shift ensures any value ≥ 2^N overflows the k-bit window and fails.

For this gadget:

| Value          | Target bits | Shift | Check                    |
| -------------- | ----------- | ----- | ------------------------ |
| `nb_minus_one` | 30          | none  | 3 words (30 bits) direct |
| `remainder`    | 24          | × 2^6 | 3 words (30 bits)        |

## Note on the remainder range

24 bits allows `remainder` to be up to 16,777,215, which is larger than 12,500,000. A prover could set a remainder greater than 12,500,000, which effectively shorts themselves one ballot. This is harmless — `num_ballots` is not in any governance nullifier, so the only effect is the prover voting with less weight than they could.
