# Schnellstart

> Vom leeren Crate zum verifizierten Beweis. Ein Gastprogramm aus drei Zeilen, gebaut, ausgeführt, inspiziert und bewiesen, mit der echten Ausgabe jedes Schritts.

Diese Seite geht den gesamten Ablauf einmal mit dem kleinsten Gastprogramm (Guest) durch, das etwas tut: Es liest seine öffentliche Eingabe und veröffentlicht sie als sein Journal. Jede Ausgabe unten ist entstanden, indem genau diese Befehle auf Apogee v1.0.0 ausgeführt wurden.

> [!NOTE]
> **Was Sie brauchen.** Einen Checkout des Repositorys von Apogee VM auf dem Stand v1.0.0 und `rustup`; alles andere legt das Repository fest. Befehle werden im Wurzelverzeichnis des Repositorys ausgeführt, sofern ein Schritt nicht das Verzeichnis wechselt. Die Schritte 5 und 6 brauchen außerdem die Zeremoniedatei `assets/ptau/ppot_0080_24.ptau`, Schritt 6 zudem eine Maschine mit Dutzenden GiB Arbeitsspeicher. [Umgebung einrichten](https://apogee.gweb3networks.com/docs/launch/setup) behandelt beides.

### Das Gastprogramm anlegen

Ein Gastprogramm ist ein `no_std`-Binary-Crate im Workspace `guests/`. Legen Sie `guests/hello` an:

```toml title="guests/hello/Cargo.toml"
[package]
name = "hello"
version.workspace = true
edition.workspace = true
publish.workspace = true

[dependencies]
guest-sdk.workspace = true
```

```rust title="guests/hello/src/main.rs"
#![no_std]
#![no_main]

guest_sdk::entry!(main);

fn main() {
    // The public input is memory: a slice, with no ecall and no cursor.
    guest_sdk::commit(guest_sdk::public_input());
}
```

`#![no_std]`, weil das Target Bare Metal ist. `#![no_main]` mit `entry!(main)`, weil der Startup-Code des SDK den Stack-Pointer setzt, `.bss` mit Nullen füllt und ein Symbol `main` aufruft, das das Makro als Wrapper um Ihre Funktion exportiert. Eine Rückkehr aus ihr ist gleichbedeutend mit `exit(0)`.

### Im Gastprogramm-Workspace eintragen

Hängen Sie `"hello"` an die Liste `members` in `guests/Cargo.toml` an:

```toml title="guests/Cargo.toml"
members = ["fib", "echo", … , "recursion", "hello"]
```

### Bauen

Aus dem eigenen Verzeichnis des Gastprogramms, ohne weiteres Flag außer dem Target:

```sh
cd guests/hello
cargo build --release --target riscv32imac-unknown-none-elf
cd ../..
```

Das ELF landet unter `guests/target/riscv32imac-unknown-none-elf/release/hello`. Der Gastprogramm-Workspace liefert das Linker-Skript und `--no-relax`; es gibt also nichts weiter zu übergeben.

### Ausführen

Der Profiler führt ein Gastprogramm im Emulator von Apogee aus, ohne Beweis, und meldet, wohin die Zyklen gegangen sind:

```sh
printf 'hello, apogee' > /tmp/hello.in
cargo run --release -p profiler -- elf guests/target/riscv32imac-unknown-none-elf/release/hello --input /tmp/hello.in
```

```text
workload
  label                        hello
  guest                        hello
  guest cycles                 114
  exit status                  0
  journal bytes                13

cycles by family
  ADD_SUB_LUI_AUIPC            64
  JUMP_BRANCH_SLT              21
  MEM_WORD                     3
  MEM_SUBWORD                  26
```

114 Befehle liefen, und jeder davon wird eine bewiesene Zeile sein. Die 13 Bytes im Journal sind das Echo der Eingabe. Die 26 `MEM_SUBWORD`-Zeilen sind `commit`, das die Eingabe Byte für Byte mit `lbu` und `sb` kopiert.

### Ansehen, was die VM beweisen wird

```sh
cargo run --release -p artifact-dump -- tables \
    guests/target/riscv32imac-unknown-none-elf/release/hello \
    --ptau assets/ptau/ppot_0080_24.ptau
```

```text
program identity  9ead85cee880df30daa8eba657316215107a075640a64ccf2424a054b758b802

VmConfig
--------
  id  family              height     live rows  columns
   0  ADD_SUB_LUI_AUIPC     4194304         33  pc next_pc rs1 rs2 rd imm extra_mask
   1  JUMP_BRANCH_SLT       4194304         12  pc next_pc rs1 rs2 rd imm extra_mask
   4  MEM_WORD              4194304          3  pc next_pc rs1 rs2 rd imm extra_mask
   5  MEM_SUBWORD           4194304          3  pc next_pc rs1 rs2 rd imm extra_mask
   7  INIT_TEARDOWN         4194304          0  none: claims no pc
   8  ZERO_WINDOWS          4194304          0  none: claims no pc
  12  PUBLIC_INPUT             4096          0  none: claims no pc
  13  PUBLIC_OUTPUT            4096          0  none: claims no pc
  14  ADVICE_WINDOWS        4194304          0  none: claims no pc
```

Das ist die statische Form des Programms bei den Standardhöhen: die vier Befehlsfamilien, die sein Code verwendet, jede mit einer dekodierten Tabelle, und die fünf Fensterfamilien, die jedes Programm hat. Die **Programmidentität** ist ein einziges Körperelement, das all dies in einem Digest zusammenfasst. Ihre Identität wird abweichen: Ein ELF bettet absolute Pfade in seine Panic-Strings ein, ein Build auf einer anderen Maschine ist also ein anderes Image, und jede Änderung der Höhen ergibt eine andere Identität.

### Beweisen und verifizieren

Ein Host-Programm fordert den Beweis an. Legen Sie es als Beispiel neben das Host-SDK:

```rust title="crates/host/examples/prove_hello.rs"
use constants::family;
use emulator::GuestIo;
use program::ProgramParams;
use srs::Srs;

fn main() {
    let elf = std::fs::read("guests/target/riscv32imac-unknown-none-elf/release/hello")
        .expect("build the guest with --release first");

    // Small heights for a small program: the seven instruction families at
    // their 2^20 floor, the three RAM-window families at 2^16. Every choice of
    // heights is its own program identity.
    let mut params = 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;
    }

    // As many ceremony powers as the tallest family has rows: 2^20 here.
    let ptau = std::path::Path::new("assets/ptau/ppot_0080_24.ptau");
    let srs = Srs::from_ptau(ptau, 20).expect("the ceremony file reads");
    let setup = host::setup(&elf, &params, srs).expect("the program registers");

    let io = GuestIo { input: b"hello, apogee".to_vec(), advice: Vec::new() };
    let proven = host::prove(&setup, &io, 2).expect("the run proves"); // two shards in flight
    host::verify(&setup.vk, &proven.block).expect("the block verifies");

    assert_eq!(proven.exit_code, 0);
    assert_eq!(proven.journal, b"hello, apogee");
    let id: String = setup.vk.identity.to_bytes().iter().map(|b| format!("{b:02x}")).collect();
    println!("identity  {id}");
    println!("cycles    {}", proven.cycles);
    println!("shards    {}", proven.report.shards);
    println!("journal   {:?}", core::str::from_utf8(&proven.journal).unwrap());
}
```

```sh
cargo run --release -p host --example prove_hello
```

```text
identity  606d1f1d720459cc1a078787381656b29c9fce5a9e539b36f899e62b64129c14
cycles    114
shards    7
journal   "hello, apogee"
```

Auf einem Laptop mit 18 Kernen und 48 GiB dauerte das 52 Sekunden, bei einer Spitze von 18 GB Speicher, fast alles davon für die zwei gleichzeitig bearbeiteten `2^20`-Shards. Die Identität weicht von der aus Schritt 5 ab, weil die Höhen abweichen: Die Identität bindet jede Höhe.

### Die Identität festhalten

Ein Verifier übernimmt die Identität nie aus dem Beweis, dem Schlüssel oder vom Prover. Er besitzt eine eigene Kopie, bezogen von demjenigen, der das Release gebaut hat, und vergleicht:

```rust
assert_eq!(setup.vk.identity.to_bytes(), registered); // `registered` from your own channel
```

Gegenüber einer vom Prover gelieferten Identität zeigt ein Beweis nur, dass *irgendein* Programm gelaufen ist.

## Was gerade passiert ist

Der Emulator hat die 114 Befehle zweimal ausgeführt. Der erste Durchlauf hat die Speicherspalten jedes Shards committet und die Aussage festgelegt. Der zweite hat jeden Shard befüllt und bewiesen. Es waren sieben Shards: je einer für jede der vier Befehlsfamilien, die ausgeführt wurden, einer für das Speicherfenster, das das Image des Programms enthält, und je einer für die öffentliche Eingabe und das Journal. Dieses Gastprogramm hat seinen Stack nie berührt, also brauchte kein weiteres Fenster einen Shard; bei einem typischen Programm kommt der Shard des Stacks hinzu. Jeder Shard wurde vom GKR-Schaltkreis seiner Familie bewiesen und mit einem einzigen Mercury-Beweis geöffnet, und der Verifier hat die Lese- und Schreibzugriffe auf den Speicher aller sieben in einer einzigen Gleichung abgeglichen. [Die Architekturübersicht](https://apogee.gweb3networks.com/docs/architecture) verfolgt denselben Weg im Detail.

## Nächste Schritte

- [Ein Gastprogramm schreiben](https://apogee.gweb3networks.com/docs/launch/write): Crate-Aufbau, Abhängigkeiten, der Heap und Tests auf dem Host.

- [Eingaben, Hilfsdaten und Journal](https://apogee.gweb3networks.com/docs/launch/io): Wie Daten hinein- und herausgelangen und was der Beweis bindet.

- [On-Chain abwickeln](https://apogee.gweb3networks.com/docs/launch/on-chain): Von einem Blockbeweis zu einem Contract, der true sagt.
