# Guests

Guests contain functions for Jolt to prove. Making a function provable is as easy as ensuring it is inside the `guest` package and adding the `jolt::provable` macro above it.

Let's take a look at a simple guest program to better understand it.
```rust
#![cfg_attr(feature = "guest", no_std)]

#[jolt::provable]
fn add(x: u32, y: u32) -> u32 {
    x + y
}
```

As we can see, the guest looks like a normal `no_std` Rust library. The only major change is the addition of the `jolt::provable` macro, which lets Jolt know of the function's existence. The only requirement of these functions is that its inputs are serializable and outputs are deserializable with `serde`. Fortunately `serde` is prevalent throughout the Rust ecosystem, so most types will support it by default.

There is no requirement that just a single function lives within the guest, and we are free to add as many as we need. Additionally, we can import any `no_std` compatible library just as we normally would in Rust.
```rust
#![cfg_attr(feature = "guest", no_std)]

use sha2::{Sha256, Digest};
use sha3::{Keccak256, Digest};

#[jolt::provable]
fn sha2(input: &[u8]) -> [u8; 32] {
    let mut hasher = Sha256::new();
    hasher.update(input);
    let result = hasher.finalize();
    Into::<[u8; 32]>::into(result)
}

#[jolt::provable]
fn sha3(input: &[u8]) -> [u8; 32] {
    let mut hasher = Keccak256::new();
    hasher.update(input);
    let result = hasher.finalize();
    Into::<[u8; 32]>::into(result)
}
```

## Prover and Verifier Views of Guest Program
A guest program consists of two main components: program code and inputs. Both the prover and verifier know the program code (and how it compiles to RISC-V instructions).

Jolt supports three types of inputs:

1. **Public Input**: These are inputs known to both the prover and verifier. The prover proves that given the bytecode and these inputs, the claimed output is correct.

2. **Untrusted Advice** (`jolt::UntrustedAdvice<T>`): These inputs are known to the prover for generating the proof, but the verifier does not receive them. The prover proves that there exists some input that produces the claimed output. **Note:** `UntrustedAdvice` does not guarantee privacy — without the `zk` feature, polynomial evaluations are revealed in the clear and the inputs may be extractable from the proof.

3. **Private Input** (`jolt::PrivateInput<T>`): Equivalent to `UntrustedAdvice<T>` but signals that the input should be cryptographically hidden from the verifier via the [BlindFold](../../how/blindfold.md) protocol. The `zk` feature must be enabled on `jolt-sdk` in the **host** crate; the guest needs no feature flag. The `#[jolt::provable]` macro enforces this at compile time.

4. **Trusted Advice** (`jolt::TrustedAdvice<T>`): Similar to untrusted advice, but the verifier has a commitment to the input generated by an external party (not the prover). The prover proves that there exists an input matching the given commitment that, when executed with the program code, produces the claimed output.

A program can use any combination of these input types. To access the inner value, dereference the wrapper with `*`.

### Private inputs (ZK)

When you need inputs that are cryptographically hidden from the verifier, use `PrivateInput<T>`. Enable the `zk` feature on `jolt-sdk` in the **host** crate — the guest needs no feature flag. You can scaffold a ZK-ready project with `jolt new my-project --zk`.

Guest `Cargo.toml` (no `zk` feature needed):

```toml
[dependencies]
jolt = { package = "jolt-sdk" }
```

Host `Cargo.toml`:

```toml
[dependencies]
jolt-sdk = { features = ["host", "zk"] }
```

If the guest uses `PrivateInput<T>` but the host doesn't enable `zk`, compilation fails with a clear error message.

Guest (`guest/src/lib.rs`):

```rust
#![cfg_attr(feature = "guest", no_std)]
use jolt::PrivateInput;

#[jolt::provable(heap_size = 32768, max_trace_length = 65536)]
fn fib(n: PrivateInput<u32>) -> u128 {
    let mut a: u128 = 0;
    let mut b: u128 = 1;
    for _ in 1..*n {
        let sum = a + b;
        a = b;
        b = sum;
    }
    b
}
```

Host (`src/main.rs`):

```rust
use jolt_sdk::PrivateInput;

let verifier_setup = prover_preprocessing.generators.to_verifier_setup();
let blindfold_setup = prover_preprocessing.blindfold_setup();
let verifier_preprocessing =
    guest::preprocess_verifier_fib(shared_preprocessing, verifier_setup, Some(blindfold_setup));

// Prover receives the private input
let (output, proof, io_device) = prove_fib(PrivateInput::new(50));

// Verifier does not — it only sees the output and proof
let is_valid = verify_fib(output, io_device.panic, proof);
```

The generated verifier closure omits private and advice parameters — only public inputs appear in the verifier's signature.

For a complete example of advice inputs, see the [merkle-tree example](https://github.com/a16z/jolt/tree/main/examples/merkle-tree).

### Zero knowledge

By default, the prover reveals polynomial evaluations in the clear. To produce **zero-knowledge** proofs where the verifier learns nothing beyond the validity of the output, enable the `zk` feature on `jolt-sdk` in the **host** crate. This activates the [BlindFold](../../how/blindfold.md) protocol, which commits all sumcheck round polynomials via Pedersen and proves correctness via Nova folding + Spartan.

`PrivateInput<T>` is always available in the guest. If you use `UntrustedAdvice<T>` without `zk`, the verifier won't receive the inputs, but the proof itself does not hide them cryptographically.

The generated `preprocess_verifier_*` function always takes three arguments: `shared`, `generators`, and `Option<BlindfoldSetup<Bn254Curve>>`. Pass `Some(blindfold_setup)` when using `zk` mode, or `None` otherwise.

### Advice inputs vs. runtime advice

Advice inputs described above (`UntrustedAdvice<T>`, `PrivateInput<T>`, `TrustedAdvice<T>`) are *host-provided*: the host supplies the data before execution, and it is written into a preallocated memory region that the guest reads from. The guest program runs once, and the values are fixed before execution begins.

[Runtime advice](../runtime_advice.md) is a separate mechanism where the *guest itself* computes advice values during a first-pass execution, and those values are fed back via an advice tape for the proving pass. This is useful when the advice depends on the guest's own execution (e.g. computing a modular inverse or finding factors) and checking the result is cheaper than computing it. Runtime advice uses `#[jolt::advice]` functions rather than wrapper-type parameters on `#[jolt::provable]`.

Both mechanisms signal that the data is untrusted and must be verified by the guest, but they differ in where the data originates and how it is serialized (advice inputs use `serde`; runtime advice uses `AdviceTapeIO`).

## Security model

Beyond the usual Rust rules around `unsafe` and undefined behavior, Jolt guest programs are subject to a few zkVM-specific rules:

- **Self-modifying programs are undefined behavior.** Jolt commits to the program's bytecode at preprocessing time. A guest that writes to its own code region can still produce a valid proof, but the semantics of that proof are undefined — the proof attests to execution against the committed bytecode, not the modified version.

- **Direct invocation of Jolt's custom RISC-V instructions is unsafe.** Jolt extends the RISC-V ISA with custom instructions used internally (e.g. for inline cryptographic primitives and runtime advice). Invoking these instructions directly from guest code, such as via inline assembly, bypasses the safety checks performed by the SDK and may produce incorrect proofs or cause proving to fail.

- **Prover-supplied advice must be validated by the guest.** `UntrustedAdvice<T>` and `PrivateInput<T>` are supplied by the prover and are not constrained by the proof system itself. The guest is responsible for verifying that any advice it consumes satisfies the properties the program relies on (e.g. that a claimed factor actually divides the input, or that a claimed Merkle path matches a known root). `TrustedAdvice<T>` carries an external commitment, but the data itself may still require validation.

- **No source of randomness or wall clock.** The guest has no entropy source and no clock. The platform exposes `sys_rand` (see `jolt-platform/src/random.rs`), but it is a **deterministic PRNG seeded with a fixed constant** — its output is fully predictable, including by the verifier — and is not suitable for cryptographic use. Any value that must be unpredictable (nonces, keys, sampling weights) has to be supplied as a public input. Supplying it via advice does *not* solve the problem: advice is chosen by the prover, so a "key" derived from advice is a key the prover picked.

- **Bytecode is public.** The compiled guest ELF is committed at preprocessing time and is known to the verifier. The `zk` feature protects *inputs*, not the program. Do not embed secrets in guest code — API keys, hardcoded credentials, or proprietary algorithms you do not want disclosed.

- **Outputs and the panic flag are public.** The return value and `io_device.panic` are revealed to the verifier and are what the proof attests to. Returning a value derived from a private input leaks that value; panicking conditionally on a private input leaks one bit per panic site. Guests handling secret data should be written so that the output and panic behavior depend only on public information.

- **Output byte representation.** The output memory region is zero-initialized, and both prover and verifier strip trailing zero bytes before binding outputs to the Fiat-Shamir transcript. As a result, byte strings that differ only in trailing zeros (e.g., `[0x41]` vs `[0x41, 0x00]`) produce identical proofs. Typed outputs are unaffected — the SDK restores the full serialized length before deserialization — but protocols that consume `program_io.outputs` as raw bytes must not treat trailing zeros as semantically meaningful, or must encode an explicit length.

- **No side-channel resistance.** The `zk` feature, via the [BlindFold](../../how/blindfold.md) protocol, makes proofs zero-knowledge with respect to the verifier. However, Jolt's prover implementation is **not constant-time** and makes no claims of resistance to side-channel attacks. A party that observes prover execution (timing, memory access patterns, power consumption, etc.) may learn information about private inputs. Do not run the prover on secret data in adversarial environments without additional mitigations.

## Standard Library
Jolt supports the Rust standard library. To enable support, simply add the `guest-std` feature to the Jolt import in the guest's `Cargo.toml` file and remove the `#![cfg_attr(feature = "guest", no_std)]` directive from the guest code.

### Example
```rust
[package]
name = "guest"
version = "0.1.0"
edition = "2021"

[features]
guest = []

[dependencies]
jolt = { package = "jolt-sdk", git = "https://github.com/a16z/jolt", features = ["guest-std"] }
```

```rust

#[jolt::provable]
fn int_to_string(n: i32) -> String {
    n.to_string()
}
```

## alloc

Jolt provides an allocator which supports most containers such as `Vec` and `Box`. This is useful for Jolt users who would like to write `no_std` code rather than using Jolt's standard library support. To use these containers, they must be explicitly imported from `alloc`. The `alloc` crate is automatically provided by rust and does not need to be added to the `Cargo.toml` file.

### Example
```rust
#![cfg_attr(feature = "guest", no_std)]

extern crate alloc;
use alloc::vec::Vec;

#[jolt::provable]
fn alloc(n: u32) -> u32 {
    let mut v = Vec::<u32>::new();
    for i in 0..100 {
        v.push(i);
    }

    v[n as usize]
}
```

## Print statements
Jolt supports standard `print!` and `println!` macros in guest programs.

### Example

```rust
#[jolt::provable(heap_size = 10240, max_trace_length = 65536)]
fn int_to_string(n: i32) -> String {
    print!("Hello, ");
    println!("from int_to_string({n})!");
    n.to_string()
}
```

The printed strings are written to stdout during RISC-V emulation of the guest.

## Optimization level

By default, the guest program is compiled with optimization level 3 to attempt to minimize the number of RISC-V instructions executed during emulation.

To change this optimization level, set the `JOLT_GUEST_OPT` environment variable to one of the allowed values: `0`, `1`, `2`, `3`, `s`, or `z`.

A common choice is `z` which optimizes for code size, at the cost of a larger number of instructions executed.
