Skip to content

Proof Types

Prerequisite Reading: Request a Proof, Proof Lifecycle

Default: Merkle Inclusion Proofs

By default, Boundless delivers proofs on-chain as merkle inclusion proofs:

  1. Proofs are batched together are aggregated into a single Groth16 proof.
  2. The aggregated proof is verified once on-chain
  3. Individual proofs are verified through cheap merkle inclusion proofs into this root

This design is what makes Boundless cost-effective for on-chain verification.

Requesting a specific proof type

While merkle inclusion proofs are efficient for on-chain verification, there may be cases where you need to access the underlying proof instead of a merkle inclusion proof. For example:

  1. Cross-chain verification where you need to verify the proof on a different chain
  2. Integration with other systems that expect a specific proof type
  3. Custom verification logic that requires the full proof

Boundless supports requesting a raw Groth16 proof instead of a merkle inclusion proof. You can specify this in your proof request by setting the proof_type to ProofType::Groth16:









let request = client.new_request()
    .with_program(program)
    .with_stdin(input)
    .with_groth16_proof()  // Request raw Groth16 proof
    .with_offer(
        OfferParams::builder()
            .min_price(parse_ether("0.001")?)
            .max_price(parse_ether("0.002")?)
            .timeout(1000)
            .lock_timeout(1000)
    );

Considerations

When choosing between proof types, consider:

  1. Gas Costs
    • Merkle inclusion proofs are much cheaper to verify on-chain
    • Raw Groth16 proofs require full SNARK verification each time. This will increase the price of the proof
  2. Use Case Requirements
    • If you only need on-chain verification, use the default merkle inclusion proof
    • If you need cross-chain verification or raw proof data, use Groth16
    • If you need to compose the proof by verifying it within another zkVM guest program, use Groth16

Example: Proof Composition using Proof Types

In the Proof Composition example, we demonstrate how to compose a proof from multiple proofs.

Composing a proof requires us to verify a previously generated Groth16 proof within the zkVM guest program. This requires us to request a raw Groth16 proof from the Boundless Market.

In the composition example, we first request a raw Groth16 proof from the Boundless Market using the ECHO guest program.









let request = client.new_request()
    .with_program(program)
    .with_stdin(input)
    .with_groth16_proof()  // Request raw Groth16 proof
    .with_offer(
        OfferParams::builder()
            .min_price(parse_ether("0.001")?)
            .max_price(parse_ether("0.002")?)
            .timeout(1000)
            .lock_timeout(1000)
    );

We then provide the Groth16 proof as input to the IDENTITY zkVM guest program, and verify the proof.

use risc0_zkvm::guest::env;
use risc0_zkvm::{Digest, Receipt};
 
fn main() {
    let (image_id, receipt): (Digest, Receipt) = env::read();
    let claim = receipt.claim().unwrap();
    receipt.verify(image_id).unwrap();
    ....
}