Proof Composition
Prerequisite Reading: Request a Proof, Proof Lifecycle
Overview
Proof composition enables you to build upon existing proofs by verifying them within new zkVM guest programs. This is particularly useful when you want to prove a sequence of related statements without reproving each step.
For example, let's say you have:
- A proof that block 
nis valid - You want to prove that both blocks 
nandn+1are valid 
Instead of reproving block n, you can:
- Use the existing proof of block 
n - Prove only block 
n+1under the assumption thatnis valid - Resolve this assumption by verifying the previous proof within your new proof
 
This approach is more efficient than reproving everything from scratch.
How it Works
Proof composition works by:
- Requesting a raw Groth16 proof from the Boundless Market
 - Using this proof as input to a new zkVM guest program
 - Verifying the proof within the guest program
 - Building upon the verified result to prove new statements
 
Example: Composing Echo and Identity Proofs
The Proof Composition example demonstrates how to compose proofs using the Echo and Identity guest programs.
First, we request a raw Groth16 proof from the Echo guest program:
let mut requirements = Requirements::new(Predicate::digest_match(image_id, journal.digest()));
if groth16 {
    requirements = requirements.with_groth16_proof();
}We then use this proof as input to the Identity guest program:
// Build the IDENTITY input from the ECHO receipt
let identity_input = (Digest::from(ECHO_ID), echo_receipt);
let identity_guest_env =
    RequestInput::builder().write_frame(&postcard::to_allocvec(&identity_input)?).build_env();
 
// Request a proof from the Boundless market using the IDENTITY guest
let (identity_journal, identity_seal) =
    boundless_proof(&boundless_client, IDENTITY_ELF, identity_guest_env, false)
        .await
        .context("failed to prove IDENTITY")?;
Finally, we can use the composed proof to interact with a smart contract:
alloy::sol! {
    #[sol(rpc)]
    interface ICounter {
        function increment(bytes calldata seal, bytes32 imageId, bytes32 journalDigest) external;
    }
}
 
// Interact with the Counter contract using the composed proof
let counter_address = address!("0x000000000000000000000000000000000c0077e5");
let counter = ICounter::ICounterInstance::new(counter_address, boundless_client.provider().clone());
let journal_digest = B256::from_slice(identity_journal.digest().as_bytes());
let image_id = B256::from_slice(Digest::from(IDENTITY_ID).as_bytes());
let call_increment =
    counter.increment(identity_seal, image_id, journal_digest).from(boundless_client.caller());
 
// Execute the transaction
let pending_tx = call_increment.send().await?;
let tx_hash = pending_tx
    .with_timeout(Some(TX_TIMEOUT))
    .watch()
    .await?;
Relevant links: Proof Composition Example