> 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/appendices/share-splits.md).

# Share Splits

## Phase 1: Greedily Fill Denominations

Repeatedly subtract the largest denomination that fits from the balance, consuming at most 9 slots total.

`[10M, 1M, 100K, 10K, 1K, 100, 10, 1]`

## Phase 2: Remainder Distribution

Distribute the remainder across the remaining 7 shares. Aim for each share to carry at least 1 ballot.

This is a PRF-weighted proportional distribution.

Full PRF is keyed on:

```
PRF(sk, domain, round_id, proposal_id, van_commitment, slot_index) 
```

The key inputs are user-controlled, user-private (i.e. sk), round-specific (round\_id), proposal-specific (proposal\_id) and VAN-speicic (van\_commitment).

## Phase 3: Deterministically Randomized Shuffle

Naively, the first share would reveal the denomination magnitude.

**Randomization** ensures that each voter will have a different ordering pattern.

**Determinism** makes it such that the pattern can be reconstructed for crash-recovery without persisting state.

Phase 3 uses the same PRF as Phase 2 but with a different `domain`.

## Example

### Phase 1

4,800 ballots (600 ZEC) → `[1K, 1K, 1K, 1K, 100, 100, 100, 100, 100]`, 300 remaining.

### Phase 2

300 ballots → `[78, 49, 50, 39, 11, 38, 35]`.

### Phase 3

Concatenated (Phase 1 ++ Phase 2):

`[1K, 1K, 1K, 1K, 100, 100, 100, 100, 100, 78, 49, 50, 39, 11, 38, 35]` ╰──── denominations (D) ────╯ ╰──── remainder (R) ───╯

After deterministic Fisher-Yates shuffle:

`[35, 1K, 50, 100, 78, 49, 1K, 38, 100, 100, 1K, 1K, 11, 100, 39, 100]` R D R D R R D R D D D D R D R D

Position no longer reveals whether a share is a standard denomination or a remainder value.

## Circuit Constraints

The circuit enforces only two invariants on the shares:

* `sum(shares) == num_ballots`
* each `share ∈ [0, 2^30)`

The denomination strategy, remainder distribution, and shuffle are builder policy — not verified in-circuit. Any decomposition satisfying the two constraints above is a valid proof.

## FAQ

### Reasons For Budgeting 9 Times for Greedy Fill

* It covers the common single-digit case. A balance whose leading "digit" is 9 (the maximum) uses exactly 9 greedy slots for one denomination. Most real balances use far fewer.
* It leaves the remaining 7 slots (16 - 9), enough to spread the leftover with meaningful randomness
* It's a deliberate tradeoff
  * More greedy slots -> better anonymity tiers but less remainder randomness.
  * Fewer greedy slots -> more remainder randomness but tiers get weaker.

### Reasons For 8 Magnitudes, Up To 10M

#### Max Value Choice

We have 8 ballots per ZEC.

10M ballots = 1\_250\_000 ZEC.

ZEC has a fixed supply cap of 21 million.

21,000,000 × 8 = 168,000,000 ballots.

A single 10M denomination share already represents 1,250,000 ZEC. With 9 greedy slots, we can express up to 90M ballots or 11\_250\_000 ZEC - more than half of the total supply.

#### Choice Of Levels

* Floor of 1 ballot: smallest indivisible unit
* Ceiling of 10M is described above

#### Choice Of Base

**Base 2 (too fine-grained)**

Each denomination is a power of 2, so there are \~24 levels from 1 to 10M. Each one appears at most once (it's binary). A single leaked share narrows the balance to a 2x range. And since each denomination is present or absent, if you manage to link even a handful of shares to one voter, each additional share eliminates half the remaining possibilities. The decomposition is maximally informative.

**Base 100+ (too coarse)**

Only \~4 levels exist (1, 100, 10K, 1M). The denominations are so spread apart that most of the balance can't be expressed in denominations at all — it overflows into the 7 remainder slots, which hold non-standard values. Those remainder values are essentially random-looking numbers unique to each voter. So while each denomination share reveals very little (a 100x range), most of the balance ends up in shares that reveal a lot.

**Base 10 (the sweet spot)** 8 levels, each covering a 10x range. A single leaked share tells you the voter's balance is somewhere in a band like 125–1,250 ZEC — wide enough that many voters fall in the same band. A denomination can appear 1–9 times, so seeing one 1K share doesn't tell you how many more the voter has. And because the denominations are dense enough, the greedy phase absorbs most of the balance, leaving only a small residual for the remainder slots. Less remainder means less fingerprintable material.

Also, base 10's human readability is helpful.

### Anonymity Bands

A single decrypted share reveals the voter's tier but not their exact balance. The 10x spacing between denominations defines the bands:

| Decrypted share       | Voter's balance could be        | Band width       |
| --------------------- | ------------------------------- | ---------------- |
| `1` (0.125 ZEC)       | 0.125 ZEC – 1.125 ZEC           | \~1 ZEC          |
| `10` (1.25 ZEC)       | 1.25 ZEC – 12.375 ZEC           | \~11 ZEC         |
| `100` (12.5 ZEC)      | 12.5 ZEC – 124.875 ZEC          | \~112 ZEC        |
| `1K` (125 ZEC)        | 125 ZEC – 1,249.875 ZEC         | \~1,125 ZEC      |
| `10K` (1,250 ZEC)     | 1,250 ZEC – 12,499.875 ZEC      | \~11,250 ZEC     |
| `100K` (12,500 ZEC)   | 12,500 ZEC – 124,999.875 ZEC    | \~112,500 ZEC    |
| `1M` (125,000 ZEC)    | 125,000 ZEC – 1,249,999.875 ZEC | \~1,125,000 ZEC  |
| `10M` (1,250,000 ZEC) | 1,250,000 ZEC – 21,000,000 ZEC  | \~19,750,000 ZEC |

By contrast, a pure random split into 16 shares would let an observer seeing a share of value 347 estimate the total as \~347 × 16 ≈ 5,500 ballots — effectively an anonymity set of 1.

### Why 16 Shares

The more shares, the better for privacy. However, circuit cost scales linearly.

In the initial benchmarks, 16 was a benchmark-selected choice that balanced proving times with privacy.

### What Is Single-Share Mode?

The builder accepts a `single_share: bool` flag. When set, the entire weight goes into share\[0] and the remaining 15 shares are zero — no greedy fill, no remainder distribution, no shuffle.

This is a deliberate privacy-for-latency tradeoff for voting near a deadline: only one share needs to be revealed to the helper server, so the voter can get their vote counted with minimal round-trips. The circuit constraints (sum = num\_ballots, each share < 2^30) are still satisfied.

The cost is that the single non-zero share fully reveals the voter's balance to anyone who decrypts it. It collapses the anonymity band to a set of one.
