# Eingaben, Hilfsdaten und Journal

> Ein Gastprogramm hat keine I/O-Systemaufrufe. Seine öffentliche Eingabe, die Hilfsdaten des Provers und sein Journal sind drei Speicherbereiche. Was jeder enthält, was der Beweis bindet und das Muster, dem jedes Gastprogramm folgt, das Daten entgegennimmt.

Ein Gastprogramm (Guest) von Apogee hat keine Dateideskriptoren, keine Streams und keinen I/O-Syscall. Seine Eingaben und Ausgaben sind drei Speicherbereiche, gelesen und geschrieben mit gewöhnlichen Lade- und Speicherbefehlen, und der Beweis bindet zwei davon.

## Die drei Speicherbereiche

| Bereich | SDK | Enthält | Größe | Durch den Beweis gebunden |
| --- | --- | --- | --- | --- |
| Öffentliche Eingabe | `public_input()`, `read_input(buf)` | die Bytes der Aussage, gewählt von demjenigen, der den Beweis anfordert | höchstens 16.380 Byte | ja, ihr Anfangsinhalt |
| Hilfsdaten (Advice) | `advice()` | Bytes, die der Prover wählt | bis zu 2 GiB | **nein** |
| Journal | `commit(bytes)`, `journal()` | was das Gastprogramm angehängt hat | höchstens 16.380 Byte | ja, sein Endinhalt |

```rust
let input: &[u8] = guest_sdk::public_input(); // a slice over the input window, no copy
let data: &[u8] = guest_sdk::advice();        // a slice over the advice region
guest_sdk::commit(b"result");                  // appends to the journal
```

- `public_input()` und `advice()` geben Slices über Speicher zurück; nichts wird kopiert. `read_input(buf)` kopiert `min(buf.len(), input.len())` Bytes und gibt die Anzahl zurück; es kann also weniger liefern als angefordert.
- `commit` hängt an und führt ein Längenwort mit, und genau das sorgt dafür, dass der Beweis einen exakten Byte-String bindet statt eines mit Nullen aufgefüllten Fensters. Es beendet den Lauf mit Status **70**, statt das Fenster überlaufen zu lassen.
- `advice()` in einem Lauf ohne Hilfsdaten ist ein fataler `OutOfBounds`-Fehler, kein leerer Slice: Ein Lauf ohne Hilfsdaten hat überhaupt keinen Bereich für Hilfsdaten und zahlt nichts dafür.

## Was „gebunden“ bedeutet

Die Aussage, die ein Beweis begründet, enthält die Bytes der öffentlichen Eingabe, die Bytes des Journals und den Exit-Status. Der Beweis zeigt, dass das Eingabefenster vor dem ersten Zugriff des Gastprogramms genau die Eingabe der Aussage enthielt und dass das Journal-Fenster beim Exit des Gastprogramms genau die Ausgabe der Aussage enthielt. Das beruht auf dem Speicherargument, nicht auf irgendetwas, das das Gastprogramm tut: Es gibt keinen Hash, den das Gastprogramm berechnen, und keine Konvention, der es folgen muss.

Bei Hilfsdaten ist es anders. Der Anfangsinhalt des Bereichs der Hilfsdaten ist das, was der Prover dort hineingeschrieben hat, und nichts verknüpft ihn mit der Programmidentität, der Aussage oder irgendeinem Gate. Ein Beweis sagt aus, dass es *irgendwelche* Hilfsdaten gibt, unter denen das Programm mit dieser Eingabe dieses Journal veröffentlicht hat. Das ist genauso stark wie die eigenen Prüfungen des Gastprogramms an den Hilfsdaten.

## Das Muster: committen, liefern, prüfen

Ein Gastprogramm mit einer großen Eingabe nimmt den Großteil als Hilfsdaten entgegen, für die die öffentliche Eingabe ein Commitment enthält, und prüft das eine gegen das andere, bevor irgendetwas aus den Hilfsdaten Abgeleitetes ins Journal gelangt:

```rust title="Die Hilfsdaten prüfen, bevor Sie ihnen vertrauen"
fn main() {
    let want = guest_sdk::public_input(); // 32 bytes: keccak256 of the advice
    let data = guest_sdk::advice();       // the prover's bytes, bound by nothing
    if guest_sdk::keccak256(data).as_slice() != want {
        guest_sdk::exit(1); // refused before anything derived from it is committed
    }
    let sum = data.iter().fold(0u32, |s, b| s.wrapping_add(u32::from(*b)));
    guest_sdk::commit(&sum.to_le_bytes()); // the journal: what the proof publishes
}
```

Die Prüfung muss kein Hash über die gesamten Hilfsdaten sein. Sie kann ein Merkle-Pfad sein, geprüft gegen eine Wurzel, die die Eingabe mitbringt, wie im [Hauptbuch-Beispiel](https://apogee.gweb3networks.com/docs/blockchain-native#ledger-native), oder eine Signatur über die Daten, oder eine Bedingung, die das Ergebnis selbst erfüllt, etwa eine behauptete Sortierreihenfolge, die das Gastprogramm in einem Durchgang verifiziert, statt zu sortieren. Entscheidend ist, dass das, wogegen geprüft wird, gebunden ist.

> [!CAUTION]
> Wer irgendeine Funktion ungeprüfter Hilfsdaten festschreibt, veröffentlicht einen Wert, den der Prover gewählt hat. Das ist der häufigste Weg, ein Gastprogramm zu schreiben, dessen Beweis nichts bedeutet.

## Strukturierte Daten

Kodieren Sie strukturierte Eingaben mit einem `no_std`-Serialisierer wie `postcard` über `serde` mit dem Feature `alloc`; genau das verwendet das Ethereum-Gastprogramm des Repositorys für seinen Block-Witness. Zwei Gewohnheiten halten ein Format ehrlich:

- **Verwenden Sie Ganzzahlen fester Breite.** `u32` und `u64`, nie `usize`, dessen Breite sich zwischen Ihrem Host und dem Gastprogramm unterscheidet.
- **Bestehen Sie auf einer einzigen Kodierung pro Wert**, wenn es darauf ankommt. Ein Deserialisierer, der überzählige Bytes am Ende oder nicht minimale Varints akzeptiert, lässt zwei Byte-Strings für einen Wert zu. Wo Eindeutigkeit zählt, dekodieren Sie, kodieren erneut und vergleichen, wie es `BlockWitness::decode` im Ethereum-Gastprogramm tut.

## Ausgaben, die wachsen

Das Journal fasst 16.380 Byte. Eine Ausgabe, die mit der Arbeit wächst, etwa ein Datensatz pro Transaktion, hat keine feste Schranke und endet irgendwann mit Exit 70. Veröffentlichen Sie stattdessen einen Digest: Hashen Sie die Datensätze, während Sie sie erzeugen, schreiben Sie das 32-Byte-Ergebnis fest, und lassen Sie jeden, der die Datensätze braucht, sie nativ neu berechnen und vergleichen. Der zustandslose Ethereum-Validator des Repositorys veröffentlicht auf diese Weise ein 43-Byte-Journal für einen ganzen Block.

Für einen Beweis, der auf Ethereum geprüft wird, halten Sie beide öffentlichen Werte auf **fester Länge**. Der bereitgestellte Verifier-Contract ist für eine Eingabelänge und eine Ausgabelänge gebaut und weist alles andere zurück ([On-Chain abwickeln](https://apogee.gweb3networks.com/docs/launch/on-chain#shape)).

## Der Exit-Status

Beim Exit wird nichts über das Festgeschriebene hinaus veröffentlicht, und ein Lauf, der einen Panic auslöst oder mit einem Status ungleich null endet, hat einen gültigen Beweis dessen, was er getan hat. Deshalb liest ein Verifier den Exit-Status, bevor er das Journal liest. On-chain nimmt der Verifier-Contract den erwarteten Status als Argument entgegen, und eine Anwendung übergibt `0`. Die eigenen Statuswerte des SDK:

| Status | Bedeutung |
| --- | --- |
| 0 | `main` ist zurückgekehrt, oder `exit(0)` |
| 70 | `commit` hätte 16.380 Byte überschritten |
| 71 | eine Allokation hätte den Stack erreicht: siehe [den Heap](https://apogee.gweb3networks.com/docs/launch/write#heap) |
| 72 | eine Delegation hat etwas geantwortet, das ihr Shim zurückweist |
| 101 | ein Panic, der nichts ausgibt |

Wählen Sie Ihre eigenen Fehlercodes außerhalb dieser Werte, wie es das Hauptbuch-Beispiel mit 1, 2 und 3 tut.

## Was der Beweis nicht aussagt

- **Nichts ordnet die Schreibzugriffe auf das Journal**, und nichts zwingt ein Gastprogramm, seine Eingabe zu lesen. Der Beweis bindet die Inhalte der Fenster, nicht die Zugriffe, die sie erzeugt haben.
- **Hilfsdaten sind beschreibbar.** Ein Schreibzugriff auf den Bereich der Hilfsdaten ist ein gewöhnlicher Schreibzugriff. Ungebunden bleiben sie so oder so.
- **Das Journal ist der gesamte Endinhalt des Fensters.** `commit` hält diese Form ein. Ein Gastprogramm, das das Fenster direkt beschreibt, muss sie einhalten: eine Länge von höchstens 16.380, so viele Bytes, dann Nullen.

Die Spezifikation legt all dies präzise fest: [Öffentliche Werte und Hilfsdaten](https://apogee.gweb3networks.com/docs/auditors/spec/public-values).
