> ## 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.

# check_advice! Macros

> Macros for verifying advice correctness in Jolt zkVM

The `check_advice!` and `check_advice_eq!` macros enforce constraints on untrusted advice values. They generate efficient virtual assertions that are proved within the zkVM.

## Overview

Advice values are provided by the prover and must be verified for correctness. These macros generate `VirtualAssertEQ` RISC-V custom instructions that the Jolt prover includes in the proof.

**On RISC-V targets** (guest code): Generates custom instruction for ZK proof

**On non-RISC-V targets** (native execution): Falls back to standard `assert!` / `assert_eq!`

## check\_advice!

Asserts that a boolean condition holds.

### Syntax

```rust theme={null}
jolt::check_advice!(condition)
jolt::check_advice!(condition, "error message")
```

<ParamField path="condition" type="bool" required>
  Boolean expression that must evaluate to `true`.
</ParamField>

<ParamField path="error message" type="&str">
  Optional error message. Only used in native mode; not included in guest binary.
</ParamField>

### Examples

#### Basic Validation

```rust theme={null}
let (a, b) = *factor(n);
jolt::check_advice!(a * b == n);
jolt::check_advice!(a > 1 && b > 1);
```

#### With Error Message

```rust theme={null}
jolt::check_advice!(
    indices.len() == values.len(),
    "indices and values must have same length"
);
```

#### Range Checks

```rust theme={null}
jolt::check_advice!(index < array.len());
jolt::check_advice!(1 < a && a <= b && b < n, "factors out of range");
```

#### Capacity Checks

```rust theme={null}
let len = usize::new_from_advice_tape();
let capacity = usize::new_from_advice_tape();
jolt::check_advice!(capacity >= len);
```

## check\_advice\_eq!

Asserts that two register-sized values are equal. More efficient than `check_advice!` for equality checks.

### Syntax

```rust theme={null}
jolt::check_advice_eq!(left, right)
jolt::check_advice_eq!(left, right, "error message")
```

<ParamField path="left" type="register-sized" required>
  First value. Must fit in a RISC-V register (u8, u16, u32, u64, usize, or signed equivalents).
</ParamField>

<ParamField path="right" type="register-sized" required>
  Second value. Must fit in a RISC-V register.
</ParamField>

<ParamField path="error message" type="&str">
  Optional error message. Only used in native mode.
</ParamField>

### Examples

#### Verify Multiplication

```rust theme={null}
let (a, b) = *factor_u8(n);
jolt::check_advice_eq!((a as u16) * (b as u16), n as u16);
```

#### Verify Array Length

```rust theme={null}
let indices = &*subset_index(a, b);
jolt::check_advice_eq!(indices.len() as u64, a.len() as u64);
```

#### Verify Struct Fields

```rust theme={null}
let frobnitz = &*frobnitz_advice();
jolt::check_advice_eq!(frobnitz.x as u64, 42u64);
jolt::check_advice_eq!(frobnitz.y, 9999u64);
jolt::check_advice_eq!(frobnitz.z.len() as u64, 5u64);
```

#### With Custom Message

```rust theme={null}
jolt::check_advice_eq!(
    computed_hash,
    expected_hash,
    "hash mismatch"
);
```

## Differences

| Feature          | `check_advice!`                   | `check_advice_eq!`             |
| ---------------- | --------------------------------- | ------------------------------ |
| Evaluates        | Boolean expression                | Two values                     |
| Instruction      | `VirtualAssertEQ(cond as u64, 1)` | `VirtualAssertEQ(left, right)` |
| Efficiency       | Evaluates condition first         | Direct register comparison     |
| Type requirement | Any `bool`                        | Register-sized integers        |
| Use case         | Complex conditions                | Equality checks                |

## When to Use

### Use `check_advice!` for:

* Complex boolean conditions
* Multiple conditions with `&&` / `||`
* Range checks and comparisons
* Conditions involving non-register types (e.g., `u128`)

```rust theme={null}
jolt::check_advice!(1 < a && a <= b && b < n);
jolt::check_advice!((a as u128) * (b as u128) == (n as u128));
jolt::check_advice!(index < array.len() && array[index] == value);
```

### Use `check_advice_eq!` for:

* Simple equality checks
* Verifying computed values against expected values
* Register-sized comparisons (u8, u16, u32, u64, usize)

```rust theme={null}
jolt::check_advice_eq!(a * b, n);
jolt::check_advice_eq!(len as u64, 10u64);
jolt::check_advice_eq!(computed, expected);
```

## Implementation Details

### RISC-V Custom Instruction

On `riscv32` or `riscv64` targets, both macros generate a `VirtualAssertEQ` instruction:

```rust theme={null}
core::arch::asm!(
    ".insn b {opcode}, {funct3}, {rs1}, {rs2}, 0",
    opcode = const CUSTOM_OPCODE,     // 0x5B
    funct3 = const FUNCT3_VIRTUAL_ASSERT_EQ,  // 0b001
    rs1 = in(reg) left_value,
    rs2 = in(reg) right_value,
    options(nostack)
);
```

The instruction asserts `rs1 == rs2`. If the assertion fails, the proof generation fails.

### Native Fallback

On non-RISC-V targets (e.g., during native testing or host execution):

```rust theme={null}
assert!(condition, "error message");
assert_eq!(left, right, "error message");
```

## Error Messages

Error messages are **only active in native mode**. When compiling for RISC-V:

* The error message string is not included in the guest binary
* Only the assertion logic is preserved
* This keeps the guest code size minimal

For debugging, run your guest function natively first:

```rust theme={null}
#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_verify_factors() {
        // This will show error messages if assertions fail
        verify_composite(15);
    }
}
```

## Common Patterns

### Verify Factorization

```rust theme={null}
#[jolt::advice]
fn factor(n: u64) -> jolt::UntrustedAdvice<(u64, u64)> {
    // ... compute factors ...
}

fn verify_composite(n: u64) {
    let (a, b) = *factor(n);
    jolt::check_advice_eq!(a * b, n);
    jolt::check_advice!(1 < a && a <= b && b < n);
}
```

### Verify Subset Membership

```rust theme={null}
#[jolt::advice]
fn subset_index(a: &[usize], b: &[usize]) -> jolt::UntrustedAdvice<Vec<usize>> {
    // ... find indices ...
}

fn verify_subset(a: &[usize], b: &[usize]) {
    let indices = &*subset_index(a, b);
    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);
    }
}
```

### Verify Sorted Indices

```rust theme={null}
fn verify_sorted(x: &[u32], indices: jolt::UntrustedAdvice<Vec<usize>>) {
    let idx = &*indices;
    jolt::check_advice_eq!(idx.len() as u64, x.len() as u64);
    for i in 0..idx.len() {
        jolt::check_advice!(idx[i] < x.len());
    }
    for i in 0..idx.len() - 1 {
        jolt::check_advice!(x[idx[i]] <= x[idx[i + 1]]);
    }
}
```

### Verify Geometric Properties

```rust theme={null}
fn verify_triangle(area: u32) {
    let adv = triangle_from_area(area);
    // Shoelace formula
    let double = (adv.p1.x as i64 * (adv.p2.y as i64 - adv.p3.y as i64)
        + adv.p2.x as i64 * (adv.p3.y as i64 - adv.p1.y as i64)
        + adv.p3.x as i64 * (adv.p1.y as i64 - adv.p2.y as i64)).abs();
    jolt::check_advice_eq!(double as u32, 2 * area);
}
```

## Best Practices

### 1. Always Verify Advice

Every piece of advice must be constrained:

```rust theme={null}
// BAD: Trusting advice without verification
let (a, b) = *factor(n);
return a > 1 && b > 1;  // Prover could cheat!

// GOOD: Verify the advice
let (a, b) = *factor(n);
jolt::check_advice_eq!(a * b, n);
jolt::check_advice!(a > 1 && b > 1);
```

### 2. Widen Types to Avoid Overflow

When multiplying advice values, widen to prevent overflow:

```rust theme={null}
// For u8 factors
jolt::check_advice_eq!((a as u16) * (b as u16), n as u16);

// For u32 factors
jolt::check_advice_eq!((a as u64) * (b as u64), n as u64);

// For u64 factors (check_advice_eq! doesn't support u128)
jolt::check_advice!((a as u128) * (b as u128) == (n as u128));
```

### 3. Check Bounds Before Indexing

```rust theme={null}
for i in 0..indices.len() {
    let idx = indices[i];
    jolt::check_advice!(idx < array.len());
    let value = array[idx];
    // ... use value ...
}
```

### 4. Verify Lengths Match

When advice provides parallel arrays:

```rust theme={null}
let indices = &*subset_index(a, b);
jolt::check_advice_eq!(indices.len() as u64, a.len() as u64);
for (i, &item) in a.iter().enumerate() {
    // Safe: lengths match
    let index = indices[i];
    // ...
}
```

## Limitations

### Register Size Constraint

`check_advice_eq!` requires both values fit in a RISC-V register:

```rust theme={null}
// OK: u64 fits in register
jolt::check_advice_eq!(a as u64, b as u64);

// ERROR: u128 doesn't fit in register
// jolt::check_advice_eq!(a as u128, b as u128);  // Won't compile

// Use check_advice! instead
jolt::check_advice!((a as u128) == (b as u128));
```

### Compile-Time vs Runtime

The macros check conditions at **runtime during proof generation**, not at compile time:

```rust theme={null}
// This compiles fine but will fail during proving if n != a*b
jolt::check_advice_eq!(a * b, n);
```

## Related

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