# Guest SDK Reference

> Every public item of the guest-sdk crate, with its exact signature and behaviour. Only exit and the delegation shims issue an ecall; everything else is loads and stores.

`crates/guest-sdk` is a guest's entire runtime: the startup code, the entry macro, the allocator, the panic handler and the ecall shims. It compiles only for `riscv32imac-unknown-none-elf`.

## Entry

```rust
guest_sdk::entry!(main);
```

Exports the `main` symbol the startup code calls, as a wrapper calling your function, which takes no arguments and returns `()`. Your function keeps its own name and may itself be called `main`. Returning from it is `exit(0)`.

## The regions

| Item | Signature | Behaviour |
| --- | --- | --- |
| `public_input` | `fn public_input() -> &'static [u8]` | The public input payload, its length word clamped to the window. No copy, no ecall |
| `read_input` | `fn read_input(buf: &mut [u8]) -> usize` | Copies `min(buf.len(), public_input().len())` bytes and returns the count. It may return short |
| `advice` | `fn advice() -> &'static [u8]` | The advice payload, its length clamped to the region. Bound by nothing, so the guest checks it. Fatal `OutOfBounds` on a run given no advice |
| `commit` | `fn commit(bytes: &[u8])` | Appends to the journal and updates its length word. Exits 70 rather than overflow the 16,380-byte window |
| `journal` | `fn journal() -> &'static [u8]` | Everything committed so far |
| `exit` | `fn exit(code: i32) -> !` | Ends the run with `code` as the statement's exit status. Publishes nothing beyond what was committed |

## Hashing

| Item | Signature | Behaviour |
| --- | --- | --- |
| `keccak256` | `fn keccak256(input: &[u8]) -> [u8; 32]` | Ethereum's Keccak-256, not SHA3-256. The sponge and padding run in guest code; each keccak-f[1600] round is one `KECCAK_F` call. Software fallback if the first call answers `-ENOSYS` |
| `sha256` | `fn sha256(input: &[u8]) -> [u8; 32]` | FIPS 180-4 SHA-256. Padding and the block loop run in guest code; each compression is sixteen `SHA256_COMP` calls. Software fallback as above |
| `poseidon2_permute` | `fn poseidon2_permute(state: &mut [u8; 96]) -> bool` | The width-3 Poseidon2 permutation over three canonical little-endian `Fr` lanes, in place, through `POSEIDON2`. Returns `false` on `-ENOSYS`, for the caller's own software path |

## Elliptic curves

```rust
pub type ProjectivePoint = [[u32; 8]; 3];
```

A point in **homogeneous projective** coordinates, `x = X/Z` and `y = Y/Z`, each coordinate eight little-endian 32-bit limbs below the curve's field modulus. It is not Jacobian: arkworks' `Projective` is, so a caller converting from it maps `(X·Z, Y·Z², Z)` in and `(X·Z, Y, Z³)` out. The identity is `(0 : 1 : 0)`.

| Item | Signature | Behaviour |
| --- | --- | --- |
| `ec_add` | `fn ec_add(codes: &[u32; 3], p: &ProjectivePoint, q: &ProjectivePoint) -> Option<ProjectivePoint>` | `p + q` by the complete formula, through three `EC_ADD` calls in group order. `None` on `-ENOSYS` |
| `ec_mul` | `fn ec_mul(codes: &[u32; 3], p: &ProjectivePoint, k: &[u32; 8]) -> Option<ProjectivePoint>` | `k·p` by double-and-add from the top bit. `k` is used as given; reducing it modulo the group order is the caller's business |
| `ec_identity` | `fn ec_identity() -> ProjectivePoint` | `(0 : 1 : 0)` |
| `recursion::SECP256K1_GROUPS`, `recursion::BN254_GROUPS` | `[u32; 3]` | The `codes` argument: which curve, as the three group selectors of one addition |

The formula proves arithmetic, not curve membership: check points taken from advice yourself.

## Raw delegation shims

`guest_sdk::recursion` holds the shims over word-aligned frame types. Each frame type is `#[repr(C, align(4))]`, so its alignment is the type's and not wherever the code generator put a local. A base-format shim returns `false` on exactly `-ENOSYS`; any other nonzero answer exits 72.

| Item | Purpose |
| --- | --- |
| `mod_mul(&mut ModMulFrame) -> bool` | One `a·b mod m`. Build the frame with `ModMulFrame::of(modulus, &a, &b)` and read `frame.result()`. The modulus codes are `SECP256K1_P`, `SECP256K1_N`, `BN254_P` and `BN254_R`, and both operands must already be below the modulus |
| `sha256_comp(&mut Sha256Frame) -> bool` | One whole compression: sixteen calls in order. `Sha256Frame::of(&state, &block)`, then `frame.working()`; adding the result to the chaining value is the caller's |
| `ec_add_complete(&mut EcAddFrame, &[u32; 3]) -> bool` | One complete addition: three calls in group order. `EcAddFrame::of(&codes, &p, &q)`, then `frame.result()` |
| `poseidon2(&mut Poseidon2Frame) -> bool`, `fr_arith(&mut FrArithFrame) -> bool` | The permutation and one `Fr` operation over byte frames; `field` and `transcript` call these for you |
| `sha256_rounds`, `ec_add` | Single steps of the operations above. A step in the wrong order is not refused, it computes something else, so prefer the whole-operation functions |
| `fr_op`, `p2_field`, `field_io`, `fq_op`, `import`, `import_run`, `replay` | The recursion format's coprocessor calls, used by the recursion tree's own programs. They have no software path |

Each shim reads its ecall number from its family's declaration record, a 12-byte `static` in its own linker section. Linking a shim declares the family; a declared family that is never called proves zero shards.

## Runtime behaviour

| Piece | Behaviour |
| --- | --- |
| Startup | `_start` at `0x0001_0000` points `sp` at `__stack_top` (`0x8000_0000`), zeroes `.bss` byte by byte, calls `main`, and exits 0 if it returns |
| Allocator | Bumps up from `__heap_start`, never frees. Exits 71 when a block would end above `__stack_top − 8 MiB` or above the live `sp` |
| Panic handler | Exits 101 and writes nothing. A panicking guest is provable and has published what it committed |
| Exit statuses | 70 journal overflow, 71 out of heap, 72 a delegation answered an error, 101 panic |

## Transparent delegation

Two library crates of the repository delegate on the guest target without naming the SDK, through a target-only dependency on it:

- `field::Fr`: addition, Montgomery multiplication (`*`, `square`, `pow` and the conversions) and nonzero `inverse` call `FR_ARITH`. A guest using `Fr` arithmetic declares that family.
- `transcript::poseidon2_permute` calls `POSEIDON2`.

The vendored `k256`, `ark-ff` and `revm-precompile` do the same for secp256k1, BN254 and the EVM precompiles: [Delegations](https://apogee.gweb3networks.com/docs/launch/delegations#vendored).

The specification of the ABI underneath all of this is [Guest ABI](https://apogee.gweb3networks.com/docs/auditors/spec/ecall-abi).
