> 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/proposal-authority-decrement.md).

# Proposal Authority Decrement

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

## What it does

Verifies that the new VAN's proposal authority bitmask has exactly one bit cleared — the bit corresponding to the proposal being voted on — and all other bits unchanged.

## Circuit

**Inputs**

Private witnesses

* `proposal_authority_old` — field element (u16 range), the old bitmask
* `one_shifted` — field element, the prover-supplied value `2^proposal_id`
  * (range-checked via the `(proposal_id, one_shifted)` lookup table, which encodes entries `(p, 2^p)` for `p ∈ [0, 15]`)

Public

* `proposal_id` — field element in `[1, 15]`, which proposal is being voted on (0-indexed bit position)
  * (`0` is rejected by the `proposal_id ≠ 0` gate; the lookup table enforces the upper bound)

**Output (circuit-computed)**

* `proposal_authority_new` — the bit-recomposition `Σ b_new_i · 2^i`, fully constrained by the bitwise decomposition gadget

### Step 1: Decompose old authority into bits

Split `proposal_authority_old` into 16 individual bit-wires, constraining each to be boolean:

```
b_0, b_1, ..., b_15 = bits of proposal_authority_old

for i in 0..16:
    constrain b_i * (1 - b_i) = 0          // each b_i is 0 or 1

constrain sum(b_i * 2^i for i in 0..16) = proposal_authority_old   // recomposition check
```

### Step 2: Select the target bit and assert it is set

Pick out `b_{proposal_id}` without branching. Use a one-hot selector and a weighted accumulator:

```
for i in 0..16:
    sel_i = (proposal_id == i) ? 1 : 0    // indicator wire

    // Constrain sel_i to be boolean
    constrain sel_i * (1 - sel_i) = 0

    // Accumulate the power-of-two weight of any active selector
    rsel_pow[i] = rsel_pow[i-1] + sel_i * 2^i

// The weighted sum must equal one_shifted = 2^proposal_id.
// Because each sel_i ∈ {0,1} and the 2^i weights are distinct powers of two,
// uniqueness of binary representation guarantees exactly one sel_i = 1,
// at position i = proposal_id.
constrain rsel_pow[15] = one_shifted    // copy constraint

// The selected bit must be 1 (voter has authority for this proposal)
selected_bit = sum(sel_i * b_i for i in 0..16)
constrain selected_bit = 1
```

### Step 3: Clear the bit and recompose

Build the new bit vector by zeroing out the selected bit, then recompose into `proposal_authority_new`:

```
for i in 0..16:
    b_new_i = b_i * (1 - sel_i)            // b_i unchanged if sel_i=0; forced to 0 if sel_i=1

constrain sum(b_new_i * 2^i for i in 0..16) = proposal_authority_new
```

### Summary

The full constraint set is:

1. **Bit decomposition** — `proposal_authority_old` splits into 16 boolean wires that recompose to the original value
2. **Selector** — `rsel_pow[15] = one_shifted` proves exactly one `sel_i` is 1, at position `proposal_id`
3. **Authority check** — the selected bit is 1 (voter hasn't already voted on this proposal)
4. **Bit clear + recompose** — zero the selected bit, recompose, and constrain equal to `proposal_authority_new`

## Example

Suppose `proposal_authority_old = 0b1111_1111_1111_1111` (full authority, 65535) and the voter votes on `proposal_id = 4` (0-indexed bit 4):

```
one_shifted            = 2^4 = 16
proposal_authority_new = 0b1111_1111_1110_1111 = 65519
```

Bit 4 is cleared; all other bits remain set.

## Context

The `proposal_authority` field in a VAN is a 16-bit bitmask encoding which proposals the voter is authorized to vote on. Bit `i` (0-indexed) corresponds to proposal with `proposal_id = i`.

`MAX_PROPOSAL_AUTHORITY = 2^16 - 1 = 65535` (full authority for all 16 proposals).

## Combined with VAN integrity checks

This condition works together with:

* **Condition 2 (VAN Integrity)** — the old VAN has `proposal_authority_old`
* **Condition 6 (New VAN Integrity)** — the new VAN has `proposal_authority_new`, with all other fields unchanged

This ensures the VAN is correctly "spent" and a new one is produced with the voted proposal's bit cleared.

## Implementation Details

The chip occupies a 17-row × 8-advice-column region (columns a\[0]–a\[7]; a\[8]–a\[9] are unused).

### Cell layout

| Row | Active selectors                             | a\[0]        | a\[1]        | a\[2]      | a\[3]             | a\[4]      | a\[5]      | a\[6]      | a\[7] |
| --- | -------------------------------------------- | ------------ | ------------ | ---------- | ----------------- | ---------- | ---------- | ---------- | ----- |
| 0   | q\_cond\_6                                   | proposal\_id | one\_shifted | pid\_inv   | 0 (seed)\*        | 0 (seed)\* | 0 (seed)\* | 0 (seed)\* | —     |
| 1   | q\_cond\_6\_init                             | b\_0         | sel\_0       | b\_new\_0  | rsel\_pow\[0]     | rseld\[0]  | rold\[0]   | rnew\[0]   | 1     |
| 2   | q\_cond\_6\_bits                             | b\_1         | sel\_1       | b\_new\_1  | rsel\_pow\[1]     | rseld\[1]  | rold\[1]   | rnew\[1]   | 2     |
| …   | …                                            | …            | …            | …          | …                 | …          | …          | …          | …     |
| 16  | q\_cond\_6\_bits + q\_cond\_6\_selected\_one | b\_15        | sel\_15      | b\_new\_15 | rsel\_pow=one\_sh | rseld=1    | rold\[15]  | rnew\[15]  | 32768 |

\* seeded as field-constant 0

Note: `a[0]` holds `proposal_id` on row 0 (for the lookup) and `b_i` on rows 1–16. `a[2]` holds `pid_inv` on row 0 and `b_new_i` on rows 1–16.

Column abbreviations:

| Symbol        | Meaning                                                                           |
| ------------- | --------------------------------------------------------------------------------- |
| b\_i          | i-th bit of proposal\_authority\_old; constrained ∈ {0, 1}                        |
| sel\_i        | one-hot selector: 1 iff i == proposal\_id                                         |
| b\_new\_i     | b\_i \* (1 − sel\_i): old bit with selected position zeroed                       |
| pid\_inv      | proposal\_id⁻¹ (row 0 only, reuses a\[2])                                         |
| rsel\_pow\[i] | running weighted sum Σ sel\_j · 2^j for j = 0..i; equals one\_shifted at last row |
| rseld\[i]     | running sum of sel\_j · b\_j for j = 0..i                                         |
| rold\[i]      | running recomposition of proposal\_authority\_old up to bit i                     |
| rnew\[i]      | running recomposition of proposal\_authority\_new up to bit i                     |

### Gate summary

| Rows | Selector                  | Constraints enforced                                                                                              |
| ---- | ------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| 0    | q\_cond\_6                | Lookup (proposal\_id, one\_shifted) in table `{(p, 2^p) \| p ∈ [0,15]}`; proposal\_id ≠ 0; seed accumulators to 0 |
| 1    | q\_cond\_6\_init          | two\_pow\_i = 1 + shared constraints                                                                              |
| 2–16 | q\_cond\_6\_bits          | two\_pow\_i = 2 · two\_pow\_i\_prev + shared constraints                                                          |
| 16   | q\_cond\_6\_selected\_one | rseld = 1 (the selected bit was set in old authority)                                                             |

Post-region copy constraints (permutation argument):

* `rsel_pow[15] = one_shifted` — proves sel is one-hot at position proposal\_id
* `rold[15] = proposal_authority_old` — proves the bit decomposition matches the claimed old value

Shared constraints (rows 1–16):

* b\_i ∈ {0, 1} and sel\_i ∈ {0, 1}
* b\_new\_i = b\_i · (1 − sel\_i)
* rsel\_pow\[i] = rsel\_pow\[i−1] + sel\_i · 2^i
* rseld\[i] = rseld\[i−1] + sel\_i · b\_i
* rold\[i] = rold\[i−1] + b\_i · 2^i
* rnew\[i] = rnew\[i−1] + b\_new\_i · 2^i

### Worked example

```
proposal_authority_old = 13 = 0b0000_0000_0000_1101  (bits 0, 2, 3 set)
proposal_id            =  2  →  one_shifted = 4 = 2²
proposal_authority_new =  9 = 0b0000_0000_0000_1001  (bit 2 cleared)
```

Accumulator trace:

| Row  | i    | b\_i | sel\_i | b\_new\_i | rsel\_pow | rseld | rold | rnew | 2^i |
| ---- | ---- | ---- | ------ | --------- | --------- | ----- | ---- | ---- | --- |
| 0    | —    | —    | —      | —         | 0\*       | 0\*   | 0\*  | 0\*  | —   |
| 1    | 0    | 1    | 0      | 1         | 0         | 0     | 1    | 1    | 1   |
| 2    | 1    | 0    | 0      | 0         | 0         | 0     | 1    | 1    | 2   |
| 3    | 2    | 1    | 1      | 0         | 4         | 1     | 5    | 1    | 4   |
| 4    | 3    | 1    | 0      | 1         | 4         | 1     | 13   | 9    | 8   |
| 5–16 | 4–15 | 0    | 0      | 0         | 4         | 1     | 13   | 9    | …   |

\* seeded as field-constant 0

Row 3 (i=2) is highlighted — sel\_i = 1 because `proposal_id = 2 = i`, b\_new\_i is forced to 0, and rnew stops accumulating that bit.

Row 16 terminal check: rseld = 1 ✓ (bit 2 was set in old authority).

Post-region copy constraints: rsel\_pow\[15] = 4 = one\_shifted ✓ (selector fired exactly at bit 2), rold\[15] = 13 = proposal\_authority\_old ✓.
