> 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/chain/chain-api.md).

# Chain API

This page documents the on-chain data structures and conventions for voting sessions, proposals, and vote decisions.

***

## Voting Session Structure

A voting session is created via `MsgCreateVotingSession` and defines all parameters for a voting round: snapshot references, ZKP verification keys, the election authority key, and a list of proposals.

Each session is identified by a `voting_round_id` derived from `Poseidon(snapshot_height, snapshot_blockhash, proposals_hash, vote_end_time, nullifier_imt_root, nc_root)`. The 6 fields are encoded as 8 Pallas Fp elements (the two 32-byte hashes are each split into two 128-bit limbs), and the output is a canonical 32-byte Pallas Fp element.

***

## Proposals & Vote Options

Each voting session contains 1–15 **proposals**. Each proposal declares:

| Field         | Type                  | Description                            |
| ------------- | --------------------- | -------------------------------------- |
| `id`          | `uint32`              | 1-indexed, sequential (1, 2, 3, …)     |
| `title`       | `string`              | Human-readable title                   |
| `description` | `string`              | Longer description of the proposal     |
| `options`     | `repeated VoteOption` | 2–8 named choices voters can pick from |

Each `VoteOption` has:

| Field   | Type     | Description                                     |
| ------- | -------- | ----------------------------------------------- |
| `index` | `uint32` | 0-indexed, sequential (0, 1, 2, …)              |
| `label` | `string` | Human-readable label (e.g. "Support", "Oppose") |

### Validation Rules

* Proposals must be numbered sequentially starting at 1.
* Each proposal must have between 2 and 8 options.
* Option indices must be sequential starting at 0.
* Option labels must be non-empty and contain only ASCII characters.

***

## Convention: Binary Votes

For standard binary votes (support/oppose), the convention is:

| Index | Label     |
| ----- | --------- |
| 0     | "Support" |
| 1     | "Oppose"  |

This is just a convention — there is nothing special about these indices at the protocol level.

***

## Multi-Choice Votes

Proposals can define arbitrary options beyond binary support/oppose. For example, an election proposal might declare:

```
options: [
  { index: 0, label: "Candidate A" },
  { index: 1, label: "Candidate B" },
  { index: 2, label: "Candidate C" },
  { index: 3, label: "Candidate D" },
]
```

The protocol supports up to 8 options per proposal.

***

## `vote_decision` Encoding

Throughout the system, `vote_decision` is a `uint32` that indexes into the proposal's `options` array. A `vote_decision` of `2` means the voter chose `options[2]`.

The chain validates that `vote_decision < len(proposal.options)` when processing `MsgRevealShare` and `MsgSubmitTally`.

***

## How Decisions Flow Through the System

1. **Session creation** — `MsgCreateVotingSession` defines each proposal and its options on-chain.
2. **Voting (ZKP #2)** — The voter's `vote_decision` is committed inside the Vote Commitment alongside `proposal_id`. The `vote_decision` value is not range-checked by the circuit — it is validated out-of-circuit by the chain when processing `MsgRevealShare`.
3. **Share reveal (ZKP #3)** — `MsgRevealShare` publishes `(proposal_id, vote_decision)` alongside the encrypted share. The chain validates `vote_decision` against the proposal's declared options and accumulates the encrypted share into the tally bucket for that `(proposal_id, vote_decision)` pair.
4. **Tally** — `MsgSubmitTally` provides decrypted totals for each `(proposal_id, vote_decision)` pair that has accumulated shares. Each entry's `vote_decision` is validated against the proposal's options.

***

## Querying Results

After tally finalization, query:

```
grpc: TallyResults(vote_round_id) → repeated TallyResult
```

This returns all `TallyResult` entries, each containing:

| Field           | Type     | Description                             |
| --------------- | -------- | --------------------------------------- |
| `proposal_id`   | `uint32` | Which proposal                          |
| `vote_decision` | `uint32` | Index into the proposal's options       |
| `total_value`   | `uint64` | Decrypted aggregate weight (in zatoshi) |

To display results, the client maps each `vote_decision` back to the corresponding option label from the session's proposal definition:

```
label = session.proposals[proposal_id - 1].options[vote_decision].label
```
