# 示例客户程序

> 代码仓库中的客户程序，每一个都是客户程序 SDK 或机器某一部分的完整示例。需要某种模式时，该去哪里找。

`guests/` 工作空间包含代码仓库构建和测试的所有客户程序（guest）。每一个都是为了检验某样东西而存在的，这使它们成为你即将编写的模式的最佳参考。它们都可以在各自的目录下用 `cargo build --target riscv32imac-unknown-none-elf` 构建。

## 从这里开始

| 客户程序 | 展示的内容 |
| --- | --- |
| `public-io` | 同时使用三个内存区域：在提交任何东西之前，先对照公开输入检查证明者提示（advice）。用一个小程序展示整个 I/O 模型 |
| `fib` | 最小的 SDK 客户程序：输入一个 `u32`，输出一个 `u32`，使用回绕算术 |
| `echo`、`heap` | 分配器：证明者提示经由堆缓冲区复制，`Vec` 和 `Box` 在 bump 分配器中反复分配和丢弃 |

## 应用模式

| 客户程序 | 展示的内容 |
| --- | --- |
| `amm`、`orderbook` | 不使用堆的 128 位和 256 位整数；`BTreeMap`、排序，以及作为证明者提示提供、经检查而非计算得到的排序顺序 |
| `vault`、`recursion-ops` | 在客户程序中使用 `crates/field` 和 `crates/transcript`，无需指名任何 shim 就委托给 `FR_ARITH` 和 `POSEIDON2` |
| `revm-block` | 基于 revm 的以太坊区块：二进制程序 `revm-block`（一个录制好的 mini-block）和 `revm-block-stateless`（无状态校验器） |

## 委托

| 客户程序 | 展示的内容 |
| --- | --- |
| `keccak-test`、`sha256-ops`、`mod-mul-ops`、`ec-ops` | `KECCAK_F`、`SHA256_COMP`、`MOD_MUL` 和 `EC_ADD`，每一个都在客户程序内部对照独立的值检查 |
| `keccak-unused`、`recursion-unused` | 链接了但从未调用的 shim：这些电路族已声明，证明零个分片 |

## 机器本身

| 客户程序 | 展示的内容 |
| --- | --- |
| `atomics` | `core::sync::atomic` 生成的每一条 A 扩展指令。只有一个 hart，意味着每一条都只是普通的读-改-写；这个客户程序是为了测试该电路族而存在的，而不是为了推荐这种做法 |
| `opcodes` | 每一条 RV32IMAC 指令 |
| `rvc-dense` | 压缩指令展开：同一段指令序列分别以压缩和非压缩方式汇编 |
| `addsub`、`control`、`alu`、`mem`、`shards` | 手写汇编，有自己的 `_start`，不使用 SDK，以其结果退出。`shards` 会填满两个 `2^20` 分片 |
| `recursion` | 递归树的验证者程序，二进制程序 `leaf` 和 `node` |

## 最小的实用客户程序

`fib` 读取一个 `u32`，用回绕算术执行同样多步的斐波那契迭代，然后提交结果：

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

guest_sdk::entry!(main);

fn main() {
    let mut n = [0u8; 4];
    assert_eq!(
        guest_sdk::read_input(&mut n),
        4,
        "fib: the public input is one u32"
    );
    let n = u32::from_le_bytes(n);

    let mut a: u32 = 0;
    let mut b: u32 = 1;
    for _ in 0..n {
        let next = a.wrapping_add(b);
        a = b;
        b = next;
    }
    guest_sdk::commit(&a.to_le_bytes());
}
```

输入过短是错误，而不是默认值：在一个只填了一部分的缓冲区上继续执行的客户程序，证明的是一个关于零的陈述。加法有意采用回绕，所以大于 47 的 `n`（超出了能放进 32 位的最后一项）是一个有普通答案的普通输入，而不是一次失败的运行。这里也没有证明者提示，因为对验证者来说，检查 `f_n` 和计算它的成本一样高：通过证明者提示给出的答案，必须重新计算一遍才能让人相信。
