# Beweisen und verifizieren

> Ein Programm registrieren, einen Lauf beweisen, den Block verifizieren und den Beweis aufbewahren. Höhen, gleichzeitig bearbeitete Shards, die Potenzen der Zeremonie, die ein Beweis braucht, und die zwei Werte, die ein Verifier selbst besitzen muss.

## Die drei Aufrufe

```rust title="Setup, Beweis, Verifikation"
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`** lädt das ELF, dekodiert es in seine Familientabellen und seine `VmConfig`, committet die Setup-Spalten unter der Zeremonie und baut den Verifikationsschlüssel. Seine Kosten fallen pro Programm und pro Wahl der Höhen an, nicht pro Lauf.
- **`host::prove`** führt das Gastprogramm (Guest) zweimal aus und beweist jeden Shard ([unten](#two-passes)). Es gibt ein `Proven` zurück: den `BlockProof`, den Exit-Code, die Zyklenzahl, das Journal und einen Bericht über den Lauf.
- **`host::verify`** prüft den Block gegen den Schlüssel, anhand der Aussage, die der Block mitführt. Es vergleicht weder die Identität noch den SRS-Digest mit irgendetwas; dieser Vergleich ist also Ihre Aufgabe.

Der [Schnellstart](https://apogee.gweb3networks.com/docs/launch/quickstart#prove) führt genau diesen Code über einem kleinen Gastprogramm aus, mit seiner echten Ausgabe.

## Was ein Verifier selbst besitzen muss

Zwei Werte stammen aus einem Kanal, den der Prover nicht kontrolliert:

1. **Die Programmidentität.** Gegenüber einer vom Prover gelieferten Identität zeigt ein Beweis nur, dass *irgendein* Programm gelaufen ist. Ein Verifier registriert die Identität des Release, dem er vertraut, und vergleicht sie mit der des Schlüssels.
2. **Der SRS-Digest der Zeremonie.** Ein Schlüssel wird unter jedem Digest geladen, den seine eigenen Punkte ergeben. Einer, der über einem bekannten `τ` gebaut wurde, könnte alles öffnen und wird nur durch den Vergleich seines Digests mit dem der Zeremonie zurückgewiesen.

Der Verifikationsschlüssel selbst darf von beliebiger Seite stammen, auch vom Prover: Beim Laden werden die Identität und der SRS-Digest aus seinem eigenen Inhalt neu berechnet, und seine Schaltkreise werden mit der eigenen Registry des Verifiers abgeglichen. Lesen Sie dann die Aussage: zuerst den Exit-Status, dann das Journal.

## Höhen

Jede Familie hat eine **Höhe**, die Zahl der Zeilen in einem ihrer Shards, gewählt aus `2^8, 2^12, 2^16, 2^18, 2^20, 2^22`. Höhen sind Teil des Programms, nicht eines Laufs: Jede Höhe ist in die Identität eingebunden.

| Familiengruppe | Standard | Untergrenze | Anmerkungen |
| --- | --- | --- | --- |
| Die sieben Befehlsfamilien | `2^22`, bzw. `2^20` für `MUL_DIV` und `ATOMICS` | `2^20` | die Untergrenze ihrer Range-Checks für Zeitstempel |
| `INIT_TEARDOWN`, `ZERO_WINDOWS`, `ADVICE_WINDOWS` | `2^22` | `2^16` | eine gemeinsame Fensterhöhe; Fenster 0 muss jedes in der Datei enthaltene Byte des Images aufnehmen |
| `PUBLIC_INPUT`, `PUBLIC_OUTPUT` | `2^12` | fest | die Höhe bestimmt die Lage ihrer Fenster |
| Delegationsfamilien | siehe [Delegationen](https://apogee.gweb3networks.com/docs/launch/delegations#cost) | je Familie | |

Eine Familie mit Zeilen kostet mindestens einen ganzen Shard ihrer Höhe; ein kurzer Lauf verschwendet also bei kleineren Höhen weniger, und ein langer braucht bei größeren Höhen weniger Shards. Eine dekodierte Tabelle muss außerdem hoch genug sein, um den letzten Befehl der Familie zu erreichen: `2^20` reicht für 1,9375 MiB Code und `2^22` für 7,9375 MiB. Das Ethereum-Gastprogramm beweist bei `2^20` für jede Familie, deren Höhe wählbar ist.

```rust title="Befehlsfamilien an ihrer Untergrenze, RAM-Fenster bei 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
}
```

Die Zeremonie muss so viele Potenzen liefern, wie die höchste Familie Zeilen hat, und mindestens `2^18` für die generische Lookup-Tabelle: `Srs::from_ptau(path, k)` mit `2^k` mindestens so groß wie die größte Höhe.

## Gleichzeitig bearbeitete Shards

Das dritte Argument von `host::prove` ist `max_in_flight`, die Zahl der gleichzeitig bewiesenen Shards. Es ist der eine Regler, der Speicher gegen Zeit tauscht:

- **Der Speicherbedarf folgt den gleichzeitig bearbeiteten Shards**, nicht der Zyklenzahl. Jeder gerade bearbeitete Shard hält seine Zeilen, seinen Vorwärtsdurchlauf und seinen Beweis, während dieser wächst. Ein `2^20`-Shard der breitesten Befehlsfamilie hält in seinem Vorwärtsdurchlauf etwa 8,4 GiB; ein `KECCAK_F`-Shard der Höhe `2^18` etwa 42 GiB.
- **Die Zeit folgt der Zahl der nebeneinander laufenden Shards**, bis zur Zahl Ihrer Kerne. Innerhalb eines Shards läuft die Arbeit auf allen Kernen.
- **Der Beweis hängt nicht davon ab.** Der Block ist bei 1 und bei 8 gleichzeitig bearbeiteten Shards byte-identisch.

Beginnen Sie auf einem Laptop niedrig, mit zwei oder vier, und erhöhen Sie den Wert auf einem Server, bis der Speicher und nicht die Kerne die Grenze bilden. `bench prove` verwendet standardmäßig 8.

## Die zwei Durchläufe

`host::prove` arbeitet im Streaming-Verfahren. Es hält nie den gesamten Ausführungs-Trace, der mit etwa 300 Byte pro Zyklus das größte Objekt im System wäre.

1. **Durchlauf 1** führt das Gastprogramm aus und committet, sobald ein Shard gefüllt ist, dessen Speicherspalten, behält die Commitments und verwirft die Zeilen. Beim Exit baut er die Aussage und zieht die Challenges, die alle Shards teilen.
2. **Durchlauf 2** führt erneut aus. Der Emulator ist deterministisch und schneidet daher dieselben Shards. Jeder wird bei seinem Eintreffen befüllt, bewiesen und verworfen, und nur sein Beweis wird behalten.

Deshalb kostet das Beweisen die Zeit zweier Ausführungen und einen Speicherbedarf, der durch die gleichzeitig bearbeiteten Shards beschränkt ist. [Der Streaming-Prover](https://apogee.gweb3networks.com/docs/architecture/streaming) erklärt das ausführlich.

## Den Beweis aufbewahren

`host::proof_archive::write_proof(dir, stem, vk, block)` schreibt vier Dateien, jede mit den reinen Bytes ihres Typs:

```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)` liest sie zurück. Die Datei `.identity` hält fest, was der Lauf behauptet hat; ein Verifier vergleicht trotzdem mit seiner eigenen Kopie. Das Kommandozeilenwerkzeug `verifier` prüft ein Archiv:

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

Es endet mit 0, wenn die Verifikation vollständig gelingt, mit 1 unter Nennung der ersten Zurückweisung und mit 2 bei einem Aufruffehler oder einer fehlerhaft formatierten Identität. Es vergleicht die übergebene Identität mit der des Schlüssels und entnimmt den SRS-Digest der Schlüsseldatei.

## Wenn ein Beweis fehlschlägt

Ein ehrlicher Prover erzeugt nie einen Beweis, der fehlschlägt; ein Fehlschlag bedeutet also eine Eingabe, die er nicht hätte annehmen dürfen, oder einen Bug. Bauen Sie mit aktiviertem Debug-Log des Provers neu und führen Sie erneut aus:

```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
```

Das Log nennt den Shard, der abgebrochen ist, und `self_check FAILED` nennt das erste Gate, das eine Zeile verletzt, mit dem Wert jedes Operanden. [Werkzeuge und CLIs](https://apogee.gweb3networks.com/docs/reference/tools#s3) listet jeden Marker auf. Das Log existiert nur in Builds mit dem Feature `debug-info` und ändert kein Byte des Beweises.

Weiter: [On-Chain abwickeln](https://apogee.gweb3networks.com/docs/launch/on-chain).
