Proof composition

Proof composition is explained in RISC Zero documentation, the verifiable computation tooling used by vlayer. For more details, refer to their resources:

This page aims to describe it from a practical perspective focusing on our use-case.

Usage

We use proof composition in Chain Proofs. The trie is correct if:

  • the previous trie was correct;
  • the operation executed is correct.

In order to verify first point - we need to verify a ZK proof (correctness of the previous step) from within a ZK proof (correctness of this step).

Implementation

Proofs that we store in the DB are bincode serialized Receipts.

Receipt contains:

  • Journal - proof output: Bytes
  • Inner receipt - polymorphic receipt
enum InnerReceipt {
    /// Linear size receipt. We don't use that
    Composite,
    /// Constant size STARK receipt
    Succinct,
    /// Constant size SNARK receipt
    Groth16,
    /// Fake receipt
    Fake,
}

In order to use one proof within another in ZKVM - we need to convert a Receipt into an Assumption. This is trivial as AssumptionReceipt implements From<Receipt>.

executor_env_builder.add_assumption(receipt.into());

Within Guest - one should use env::verify function:

use risc0_zkvm::guest::env;

env::verify(HELLO_WORLD_ID, b"journal".as_slice()).unwrap();

This function accepts guest ID, journal and not the proof as all the available proofs are stored within env.

Important Proof composition only works on Succinct proofs and not Groth16 proofs.

In Chain Proofs - we store all proofs as Succinct receipts. Chain Proof gets injected into Call Proof as Succinct receipt. In the end Call Proof gets converted into a Groth16 receipt to be verified in a Smart Contract