# Prove and Verify

> Register a program, prove a run, verify the block, and keep the proof. Heights, shards in flight, the ceremony powers a proof needs, and the two values a verifier must hold for itself.

## The three calls

```rust title="Setup, prove, verify"
let params = program::ProgramParams::defaults();
let ptau = std::path::Path::new("assets/ptau/ppot_0080_24.ptau");
let srs = srs::Srs::from_ptau(ptau, 22).expect("ceremony");

let setup = host::setup(&elf, &params, srs).expect("registers");          // once per program
let proven = host::prove(&setup, &io, 4).expect("proves");                // at most 4 shards in flight
host::verify(&setup.vk, &proven.block).expect("verifies");

assert_eq!(setup.vk.identity.to_bytes(), registered); // from your own channel, never the proof
assert_eq!(proven.exit_code, 0);
```

- **`host::setup`** loads the ELF, decodes it into its family tables and `VmConfig`, commits the setup columns under the ceremony, and builds the verifying key. Its cost is per program and per choice of heights, not per run.
- **`host::prove`** executes the guest twice and proves every shard ([below](#two-passes)). It returns a `Proven`: the `BlockProof`, the exit code, the cycle count, the journal and a report of the run.
- **`host::verify`** checks the block against the key, using the statement the block carries. It compares neither the identity nor the SRS digest with anything, so that comparison is yours.

The [Quickstart](https://apogee.gweb3networks.com/docs/launch/quickstart#prove) runs exactly this code over a small guest, with its real output.

## What a verifier must hold for itself

Two values come from a channel the prover does not control:

1. **The program identity.** Against an identity the prover supplied, a proof shows only that *some* program ran. A verifier registers the identity of the release it trusts and compares it with the key's.
2. **The SRS digest of the ceremony.** A key loads under whatever digest its own points give. One built over a known `τ` could open anything, and is refused only by comparing its digest with the ceremony's.

The verifying key itself may come from anyone, including the prover: loading it recomputes the identity and the SRS digest from its own contents and holds its circuits to the verifier's own registry. Then read the statement: the exit status first, then the journal.

## Heights

Each family has a **height**, the number of rows in one of its shards, chosen from `2^8, 2^12, 2^16, 2^18, 2^20, 2^22`. Heights are part of the program, not of a run: every height is bound into the identity.

| Family group | Default | Floor | Notes |
| --- | --- | --- | --- |
| The seven instruction families | `2^22`, or `2^20` for `MUL_DIV` and `ATOMICS` | `2^20` | the floor of their timestamp range checks |
| `INIT_TEARDOWN`, `ZERO_WINDOWS`, `ADVICE_WINDOWS` | `2^22` | `2^16` | one shared window height; window 0 must hold every file-backed byte of the image |
| `PUBLIC_INPUT`, `PUBLIC_OUTPUT` | `2^12` | pinned | the height places their windows |
| Delegation families | see [Delegations](https://apogee.gweb3networks.com/docs/launch/delegations#cost) | per family | |

A family with rows costs at least one whole shard of its height, so a short run wastes less at smaller heights, and a long one needs fewer shards at larger heights. A decoded table must also be tall enough to reach the family's last instruction: `2^20` reaches 1.9375 MiB of code and `2^22` reaches 7.9375 MiB. The Ethereum guest proves at `2^20` for every family whose height is a choice.

```rust title="Instruction families at their floor, RAM windows at 2^16"
use constants::family;

let mut params = program::ProgramParams::defaults();
for f in 0..7 {
    params.heights[f] = 1 << 20;
}
for f in [family::INIT_TEARDOWN, family::ZERO_WINDOWS, family::ADVICE_WINDOWS] {
    params.heights[f as usize] = 1 << 16; // window 0 is then 256 KiB: the image must fit in it
}
```

The ceremony must supply as many powers as the tallest family has rows, and at least `2^18` for the generic lookup table: `Srs::from_ptau(path, k)` with `2^k` at least the largest height.

## Shards in flight

The third argument of `host::prove` is `max_in_flight`, the number of shards proved at once. It is the one knob that trades memory for time:

- **Memory follows the shards in flight**, not the cycle count. Each shard in flight holds its rows, its forward pass and its proof as it grows. A `2^20` shard of the widest instruction family holds about 8.4 GiB in its forward pass; a `2^18` `KECCAK_F` shard about 42 GiB.
- **Time follows how many shards run side by side**, up to the cores you have. Within a shard, the work runs on all cores.
- **The proof does not depend on it.** The block is byte-identical at 1 and 8 in flight.

Start low on a laptop, two or four, and raise it on a server until memory, not cores, is the limit. `bench prove` defaults to 8.

## The two passes

`host::prove` streams. It never holds the whole execution trace, which at about 300 bytes a cycle would be the largest object in the system.

1. **Pass 1** executes the guest and, as each shard fills, commits its memory columns, keeps the commitments and drops the rows. At the exit it builds the statement and draws the challenges every shard shares.
2. **Pass 2** executes again. The emulator is deterministic, so it cuts the same shards. Each is filled, proved and dropped as it arrives, and only its proof is kept.

That is why proving takes two executions' time and memory bounded by the shards in flight. [The streaming prover](https://apogee.gweb3networks.com/docs/architecture/streaming) explains it in depth.

## Keep the proof

`host::proof_archive::write_proof(dir, stem, vk, block)` writes four files, each its type's plain bytes:

```text
<stem>.vk         the verifying key
<stem>.identity   the key's identity, 64 lowercase hex digits and a newline
<stem>.public     the statement: input, journal, exit status, the execution's record
<stem>.block      the block proof
```

`read_proof(dir, stem)` reads them back. The `.identity` file records what the run claimed; a verifier still compares against its own copy. The `verifier` command-line tool checks an archive:

```sh
cargo run --release -p verifier -- block <stem>.vk  <stem>.public <stem>.block
```

It exits 0 when everything verifies, 1 naming the first refusal, and 2 on a usage error or a malformed identity. It compares the identity you pass with the key's, and takes the SRS digest from the key file.

## When a proof fails

An honest prover never produces a proof that fails, so a failure means an input it should not have accepted, or a bug. Rebuild with the prover's debug log on and rerun:

```sh
cargo run --release -p bench --features prover/debug-info -- prove ...
APOGEE_DEBUG=detail <the same run> 2>&1 | tee run.log
grep -E 'FAIL|NOT CANONICAL|UNBALANCED|OVER the|NAMES NO|DISAGREES|NOT LOOPING|ABORTED' run.log
```

The log names the shard that died, and `self_check FAILED` names the first gate a row breaks, with every operand's value. [Tools](https://apogee.gweb3networks.com/docs/reference/tools#s3) lists every marker. The log exists only in builds with the `debug-info` feature and changes no proof byte.

Next: [Settle on-chain](https://apogee.gweb3networks.com/docs/launch/on-chain).
