> 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/van-nullifier.md).

# VAN Nullifier

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

## What it does

Derives a nullifier for a Vote Authority Note being spent. This prevents the same VAN from being used to vote on the same proposal twice.

## Circuit

```
van_nullifier = Poseidon(vsk.nk, "vote authority spend", voting_round_id, vote_authority_note_old)
```

A single `ConstantLength<4>` Poseidon call with 4 explicit inputs:

* `vsk.nk` — the nullifier deriving key from the FVK (same field element as `nk` in ZKP #1; "vsk" denotes voting-context role, not distinct key material)
* `"vote authority spend"` — domain tag encoded as a 20-byte UTF-8 string, zero-padded to 32 bytes, interpreted as a little-endian Pallas field element. Assigned via `assign_advice_from_constant` so the value is baked into the verification key.
* `voting_round_id` — scopes the nullifier to this round
* `vote_authority_note_old` — the VAN commitment being consumed

## Key difference from governance nullifiers and domain separation

`vsk.nk` and `nk` (ZKP #1) are the **same field element** — both are the nullifier deriving key from the FVK. The "vsk" prefix denotes role in the voting key hierarchy, not distinct key material.

Because the keying material is shared, cross-circuit collision uniqueness relies entirely on the nullifier domains being distinct. ZKP #2 uses a VK-baked constant (`"vote authority spend"`). ZKP #1 uses a public input `dom` derived out-of-circuit as `Poseidon("governance authorization", vote_round_id)`.

| Circuit | Domain mechanism                    | Personalization                                       |
| ------- | ----------------------------------- | ----------------------------------------------------- |
| ZKP #1  | `dom` (public input, per-round)     | `Poseidon("governance authorization", vote_round_id)` |
| ZKP #2  | `domain_van_nullifier()` (constant) | `"vote authority spend"`                              |

Because `dom` incorporates the protocol identifier `"governance authorization"`, governance nullifiers and VAN nullifiers cannot collide even though they share the same `nk`.

## Out-of-circuit check

The vote chain verifies that `van_nullifier` has not been seen before, preventing double-voting with the same authority.
