> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/a16z/jolt/llms.txt
> Use this file to discover all available pages before exploring further.

# #[jolt::advice]

> Macro for defining runtime advice functions

The `#[jolt::advice]` macro enables functions to compute values outside the zkVM and provide them as advice to the guest program. This allows expensive computations to run in native code while the guest only verifies the results.

## Overview

Advice functions operate in two modes:

1. **Compute mode** (`compute_advice` feature enabled): Executes the function body and writes the result to the advice tape
2. **Consume mode** (default): Reads the pre-computed result from the advice tape

This dual compilation enables a two-pass proving strategy where expensive computations happen outside the proof.

## Basic Usage

```rust theme={null}
#[jolt::advice]
fn factor(n: u64) -> jolt::UntrustedAdvice<(u64, u64)> {
    // This expensive computation runs outside the zkVM
    for i in 2..n {
        if n % i == 0 {
            return (i, n / i);
        }
    }
    (1, n)
}

fn verify_composite(n: u64) -> bool {
    let (a, b) = *factor(n);
    // CRITICAL: Always verify advice!
    jolt::check_advice_eq!(a * b, n);
    a > 1 && b > 1
}
```

## Requirements

<ParamField path="Return Type" required>
  Must be `jolt::UntrustedAdvice<T>` where `T` implements `AdviceTapeIO`.
</ParamField>

<ParamField path="Parameters" required>
  Must be immutable. No `mut` bindings or `&mut` references allowed.
</ParamField>

<ParamField path="Body" required>
  The function body computes the advice value. Should return `UntrustedAdvice::new(value)` or just `value` (automatic wrapping).
</ParamField>

## Supported Types

Types that implement `AdviceTapeIO` can be used as advice:

### Built-in Types

* Primitive integers: `u8`, `u16`, `u32`, `u64`, `usize`, `i8`, `i16`, `i32`, `i64`
* Arrays: `[T; N]` where `T: Pod`
* Tuples: `(A, B)`, `(A, B, C)`, ... up to 7 elements (where each implements `AdviceTapeIO`)
* Vectors: `Vec<T>` where `T: Pod` (requires `std` or `guest-std` feature)

### Custom Types

Implement `AdviceTapeIO` manually:

```rust theme={null}
struct Witness {
    value: u64,
    proof: Vec<u8>,
}

impl jolt::AdviceTapeIO for Witness {
    fn write_to_advice_tape(&self) {
        self.value.write_to_advice_tape();
        self.proof.write_to_advice_tape();
    }
    
    fn new_from_advice_tape() -> Self {
        Witness {
            value: u64::new_from_advice_tape(),
            proof: Vec::<u8>::new_from_advice_tape(),
        }
    }
}
```

Or use `bytemuck` for POD types:

```rust theme={null}
use bytemuck_derive::{Pod, Zeroable};

#[derive(Copy, Clone, Pod, Zeroable)]
#[repr(C)]
struct Point {
    x: u32,
    y: u32,
}
impl jolt::JoltPod for Point {}

// Now Point automatically implements AdviceTapeIO
```

## Examples

### Integer Factorization

```rust theme={null}
#[jolt::advice]
fn factor_u8(n: u8) -> jolt::UntrustedAdvice<(u8, u8)> {
    let mut a = 1u8;
    let mut b = n;
    for i in 2..=n {
        if n % i == 0 {
            a = i;
            b = n / i;
            break;
        }
    }
    (a, b)
}

fn verify_composite_u8(n: u8) {
    let adv = factor_u8(n);
    let (a, b) = *adv;
    jolt::check_advice_eq!((a as u16) * (b as u16), n as u16);
    jolt::check_advice!(1 < a && a <= b && b < n);
}
```

### Subset Index Finding

```rust theme={null}
#[jolt::advice]
fn subset_index(a: &[usize], b: &[usize]) -> jolt::UntrustedAdvice<Vec<usize>> {
    let mut indices = Vec::new();
    for &item in a.iter() {
        for (i, &b_item) in b.iter().enumerate() {
            if item == b_item {
                indices.push(i);
                break;
            }
        }
    }
    indices
}

fn verify_subset(a: &[usize], b: &[usize]) {
    let adv = subset_index(a, b);
    let indices = &*adv;
    jolt::check_advice!(indices.len() == a.len());
    for (i, &item) in a.iter().enumerate() {
        let index = indices[i];
        jolt::check_advice!(index < b.len() && b[index] == item);
    }
}
```

### Custom Struct with Nested Types

```rust theme={null}
use bytemuck_derive::{Pod, Zeroable};

#[derive(Copy, Clone, Pod, Zeroable)]
#[repr(C)]
struct Triangle {
    x1: u32, y1: u32,
    x2: u32, y2: u32,
    x3: u32, y3: u32,
}
impl jolt::JoltPod for Triangle {}

#[jolt::advice]
fn triangle_from_area(area: u32) -> jolt::UntrustedAdvice<Triangle> {
    let target = 2 * area;
    let mut x = 1;
    let mut y = target;
    for i in 1..=target {
        if target % i == 0 {
            x = i;
            y = target / i;
            break;
        }
    }
    Triangle {
        x1: 0, y1: 0,
        x2: x, y2: 0,
        x3: 0, y3: y,
    }
}

fn verify_triangle(area: u32) {
    let adv = triangle_from_area(area);
    // Shoelace formula: Area = |x1(y2-y3) + x2(y3-y1) + x3(y1-y2)| / 2
    let double = (adv.x1 as i64 * (adv.y2 as i64 - adv.y3 as i64)
        + adv.x2 as i64 * (adv.y3 as i64 - adv.y1 as i64)
        + adv.x3 as i64 * (adv.y1 as i64 - adv.y2 as i64)).abs();
    jolt::check_advice_eq!(double as u32, 2 * area);
}
```

## How It Works

### Two-Pass Compilation

The `#[jolt::provable]` macro compiles the guest program twice:

1. **First pass** (`compute_advice` feature): Advice functions execute their bodies and populate the advice tape
2. **Second pass** (normal): Advice functions read pre-computed values from the tape

This is handled automatically by the generated `prove_*` functions.

### Macro Expansion

The `#[jolt::advice]` macro expands to:

```rust theme={null}
// With compute_advice feature
#[cfg(feature = "compute_advice")]
fn factor(n: u64) -> jolt::UntrustedAdvice<(u64, u64)> {
    let result: (u64, u64) = { /* original body */ };
    <(u64, u64) as AdviceTapeIO>::write_to_advice_tape(&result);
    jolt::UntrustedAdvice::new(result)
}

// Without compute_advice feature
#[cfg(not(feature = "compute_advice"))]
#[allow(unused_variables)]
fn factor(n: u64) -> jolt::UntrustedAdvice<(u64, u64)> {
    let result: (u64, u64) = <(u64, u64) as AdviceTapeIO>::new_from_advice_tape();
    jolt::UntrustedAdvice::new(result)
}
```

## Best Practices

### Always Verify Advice

Advice is untrusted input from the prover. **Always** verify it satisfies the required constraints:

```rust theme={null}
let (a, b) = *factor(n);
// REQUIRED: Verify the advice is correct
jolt::check_advice_eq!(a * b, n);
jolt::check_advice!(a > 1 && b > 1);
```

### Use References for Large Inputs

Advice functions can take references to avoid copying:

```rust theme={null}
#[jolt::advice]
fn process_large_input(data: &[u8]) -> jolt::UntrustedAdvice<u64> {
    // Process data without copying
    data.iter().map(|&x| x as u64).sum()
}
```

### Size Considerations

Set `max_untrusted_advice_size` in `#[jolt::provable]` large enough for all advice values:

```rust theme={null}
#[jolt::provable(max_untrusted_advice_size = 8192)]
fn my_function(input: u32, witness: jolt::UntrustedAdvice<Vec<u8>>) {
    // ...
}
```

## Limitations

* Parameters must be immutable (no `mut` or `&mut`)
* Return type must be `UntrustedAdvice<T>` where `T: AdviceTapeIO`
* The function body is only executed during the first pass (with `compute_advice` feature)
* Reading beyond the advice tape length causes a runtime error during proof generation

## Related

* [#\[jolt::provable\]](/api/macros/provable) - Mark functions as provable
* [check\_advice! macros](/api/macros/check-advice) - Verify advice correctness
* [AdviceReader](/api/types/advice-reader) - Reading advice data from the advice tape
* [AdviceWriter](/api/types/advice-writer) - Writing advice data to the advice tape
