Starknet processes transactions in sequential blocks. To efficiently prove the integrity of the entire chain on L1 (e.g., Ethereum), Starknet aggregates the STARK proofs of individual block executions into a single, cumulative STARK proof. This is achieved using a technique called "applicative recursion," where proofs verify computations that themselves include proof verifications.
This document details the end-to-end flow, clarifies how the outputs of verified programs are linked, and provides specific references to the relevant Cairo source code files.
Core Goal: Combine N individual block proofs (P_1, ..., P_N) into a single proof (P_final) that attests to the valid execution and sequential linking of all N blocks.
-
Starknet OS Execution: (Implicit) Executes transactions for a single block
i, producing, once proven:- A STARK proof
P_i. - The public output data
OsOutput_i(inside the proof).
- A STARK proof
-
OS Output (
OsOutput): Defines the public output structure of a Starknet block execution. Crucial for linking blocks.- Definition:
struct OsOutput,struct OsOutputHeaderinsrc/starkware/starknet/core/os/output.cairo - Key Fields:
initial_root,final_root,prev_block_number,new_block_number,prev_block_hash,new_block_hash.
- Definition:
-
Cairo Verifier Program: A Cairo program that verifies a STARK proof (
P_i) generated by another Cairo program (like the Starknet OS).- Source:
src/starkware/cairo/cairo_verifier/layouts/all_cairo/cairo_verifier.cairo - Functionality:
- Takes a
StarkProofas input (mainfunction hint). - Calls
verify_prooffor core STARK verification. - Processes public input in
_verify_public_input. - Calculates
program_hashfrom the program segment data. - Calculates
output_hash(Poseidon hash of the output segment data). - Outputs
CairoVerifierOutput { program_hash, output_hash }.
- Takes a
- Source:
-
Aggregator Program: A Cairo program designed to combine the
OsOutputdata from multiple blocks (N) and check their sequential consistency.- Main Logic:
src/starkware/starknet/core/aggregator/main.cairo - Combining & Linking Logic:
combine_blocks_innerfunction insrc/starkware/starknet/core/aggregator/combine_blocks.cairo - Functionality:
- Receives claimed
OsOutputdata for N tasks via hints (main.cairo). - Calls
combine_blockswhich usescombine_blocks_innerto assert sequential consistency (state roots, block hashes/numbers). - Outputs a serialization of its claimed inputs (
output_blocksinmain.cairo). - Outputs the final combined
OsOutput(serialize_os_outputinmain.cairo).
- Receives claimed
- Main Logic:
-
Simple Bootloader: A Cairo program responsible for executing individual "tasks" (which can be running a regular Cairo program or running the Cairo Verifier program).
- Runner:
src/starkware/cairo/bootloaders/simple_bootloader/run_simple_bootloader.cairo - Task Execution:
execute_taskfunction insrc/starkware/cairo/bootloaders/simple_bootloader/execute_task.cairo - Functionality:
- Takes a list of tasks as input.
- For each task, it loads the specified program (
load_programhint inexecute_task). - Computes the
program_hashof the task's program (compute_program_hashinexecute_task). - Executes the task's main function (
call abs program_entry_pointinexecute_task). - Validates builtin usage (
validate_builtinsinexecute_task). - Outputs the task's result prefixed with
TaskOutputHeader { size, program_hash }.
- Runner:
-
Bootloader (
run_bootloader): Orchestrates running multiple tasks using theSimple Bootloader, potentially handling nested/composite tasks (like verifications).- Source:
src/starkware/cairo/bootloaders/bootloader/run_bootloader.cairo - Functionality:
- Calls
run_simple_bootloaderto execute direct subtasks. - Parses the output of the simple bootloader (
parse_tasks). - Handles
PlainPackedOutput(direct program output) andCompositePackedOutput(output of a verifier task). Seeunpack_plain_packed_taskandunpack_composite_packed_task. - Collects and formats the outputs of the verified tasks into a single contiguous memory segment.
- Calls
- Source:
-
Applicative Bootloader: The top-level program for a single aggregation step. It orchestrates the verification of sub-proofs and the execution of the Aggregator program.
- Source:
src/starkware/cairo/bootloaders/applicative_bootloader/applicative_bootloader.cairo - Functionality:
- Runs the Aggregator program as a task using
run_simple_bootloader. - Runs the recursive verification of the N individual block proofs using
run_bootloader. - Performs the crucial
memcpycheck to ensure the Aggregator's claimed inputs match the verified outputs fromrun_bootloader. - Generates the final aggregated output.
- Runs the Aggregator program as a task using
- Source:
This was a key point of confusion. Here's the precise relationship and verification mechanism:
-
OS Run (Block
i): Produces proofP_iand dataOsOutput_i. The dataOsOutput_iexists in the output segment of the execution trace corresponding toP_i. -
Cairo Verifier Run: When the Cairo Verifier program (from
cairo_verifier.cairo) is executed as a task by theSimple Bootloaderwith proofP_ias input:- It verifies
P_i. - It extracts the output segment data (which is the
OsOutput_idata) fromP_i's public input (extract_rangein_verify_public_input). - It computes the Poseidon hash of this extracted data, resulting in
output_hash_i(poseidon_hash_manyin_verify_public_input). - The output of this Verifier task execution is
CairoVerifierOutput { program_hash_verifier, output_hash_i }.
- It verifies
-
Bootloader's Role (
run_bootloader):- The
run_bootloaderfunction receives the list of original tasks (e.g., N OS runs) and the structure (packed_outputs) indicating how proofs should be verified and potentially nested. - When processing a step that involves verifying proof
P_i, it invokes theSimple Bootloaderto run the Cairo Verifier task. - The
run_bootloaderthen processes the output of this Verifier task. Inside functions likeunpack_composite_packed_task, it implicitly uses theoutput_hash_iproduced by the verifier. - The crucial link: The system relies on the fact that the
output_hash_igenerated by the verifier is the cryptographic commitment to theOsOutput_idata associated with the original blocki. While the Cairo code inrun_bootloader.cairodoesn't explicitly show hashing the originalOsOutput_idata and comparing it to the verifier'soutput_hash_i, the overall structure ensures this consistency. Thepacked_outputshint likely guides this process, ensuring that theOsOutput_idata associated withP_iis correctly linked to theoutput_hash_iobtained from its verification. If the proofP_iwas invalid or tampered with, the Cairo Verifier would fail or produce the wrongoutput_hash_i, breaking the chain. - Once validated (implicitly or explicitly at the layer constructing the
inputs), the
run_bootloaderserializes the validatedOsOutput_idata (along with itsprogram_hashand size) into its final output stream (bootloader_tasks_output_ptrused later byapplicative_bootloader).
- The
In essence: The output_hash produced by the Cairo Verifier serves as the
ground truth hash for the output data (OsOutput) of the program execution
it verified. The bootloader infrastructure ensures this link is maintained when
propagating the OsOutput data.
Here's the step-by-step process for aggregating N block proofs (P_1, ..., P_N):
Initial State:
- N Starknet OS executions completed for blocks 1 to N.
- We have N STARK proofs:
P_1, ..., P_N. - We have the corresponding claimed public outputs:
OsOutput_1, ..., OsOutput_N. - We have the Aggregator program and the Bootloader programs (Simple, Bootloader, Applicative).
Aggregation Step (Executed within the Applicative Bootloader):
-
Run Recursive Verification (
run_bootloader):- The
Applicative Bootloaderinvokesrun_bootloader. - Input: The list of N original tasks (implicitly, proofs P_1..P_N and
their associated claimed OsOutput_1..N data), bootloader configuration,
and the
packed_outputsstructure describing the verification hierarchy.// In applicative_bootloader.cairo // ... prepare bootloader_input containing tasks, config, packed_outputs ... run_bootloader{output_ptr=bootloader_output_ptr}();
- Process:
run_bootloadercallsrun_simple_bootloader.run_simple_bootloaderiterates through tasks. For tasks requiring verification (based onpacked_outputs), it callsexecute_taskto run theCairo Verifierprogram with the relevant proofP_ias input.- The Cairo Verifier (
cairo_verifier.cairo) verifiesP_iand outputsCairoVerifierOutput { program_hash, output_hash_i }. run_bootloader(inparse_tasks/unpack_composite_packed_task) processes this verifier output, implicitly confirming the integrity ofOsOutput_iviaoutput_hash_i.run_bootloaderconcatenates the validated outputs (e.g.,TaskOutputHeader { size, program_hash_os }followed by the serializedOsOutput_idata) for all N tasks into a single memory segment.
- Output: A memory segment starting at
bootloader_output_start, containing bootloader config, total task count, and the concatenated verified task outputs (pointed to bybootloader_tasks_output_ptr).
Loadinggraph TD subgraph run_bootloader Phase A["Input: P_1..N, OsOutput_1..N (claimed)"] --> B{For each task i}; B --> C{Run SimpleBootloader Task: Verify P_i}; C --> D[CairoVerifier Output: output_hash_i]; D --> E["Validate OsOutput_i against output_hash_i (Implicit Check)"]; E -- Success --> F[Append validated OsOutput_i to output stream]; F --> B; B -- Done --> G[Output: Concatenated Verified Outputs]; end - The
-
Run Aggregator Program:
- The
Applicative Bootloaderinvokesrun_simple_bootloaderagain, this time with only the Aggregator program task. - Input: The Aggregator program code and its specific inputs (which
include the claimed
OsOutput_1..Npassed via hints).// In applicative_bootloader.cairo // ... prepare simple_bootloader_input for aggregator_task ... run_simple_bootloader{output_ptr=aggregator_output_ptr}();
- Process:
- The Aggregator program (
aggregator/main.cairo) parses the claimedOsOutput_1..Nfrom hints. - It calls
combine_blocks(aggregator/combine_blocks.cairo), which iterates through theOsOutputdata and performsassertchecks for sequential consistency:// In combine_blocks_inner assert current.header.state_update_output.initial_root = aggregated.header.state_update_output.final_root; assert current.header.prev_block_hash = aggregated.header.new_block_hash; // ... other checks ...
- The Aggregator calculates the final
CombinedOutput. - It serializes its claimed inputs (
output_blocksinmain.cairo) into its output stream (ataggregator_input_ptr). - It serializes the
CombinedOutputinto its output stream (ataggregated_output_ptr).
- The Aggregator program (
- Output: A memory segment (from
aggregator_output_starttoaggregator_output_end) containing the header, the serialized claimed inputs, and the serialized combined output. A proofP_aggis generated for this execution.
Loadinggraph TD subgraph Aggregator Execution Phase H["Input: Claimed OsOutput_1..N (via hints)"] --> I{combine_blocks_inner Loop}; I --> J["Assert OsOutput_i links to OsOutput_{i+1}"]; J -- Success --> I; I -- Done --> K[Compute CombinedOutput]; J -- Failure --> L[Error]; K --> M[Output: Serialized Claimed Inputs & CombinedOutput]; end - The
-
The Final Link (
memcpyCheck):-
This happens within the
Applicative Bootloaderafter both Step 1 and Step 2 have completed. -
It compares the memory segment containing the verified outputs from
run_bootloader(Step 1) with the memory segment where the Aggregator claimed its inputs were (Step 2).// In applicative_bootloader.cairo let bootloader_tasks_output_ptr = &bootloader_output_start[2]; // Points to verified outputs from Step 1 let bootloader_tasks_output_length = bootloader_output_end - bootloader_tasks_output_ptr; // aggregator_input_ptr points into the Aggregator's output stream (Step 2) // where it wrote its *claimed* inputs. memcpy( dst=aggregator_input_ptr, src=bootloader_tasks_output_ptr, len=bootloader_tasks_output_length, );
-
Significance: This
memcpyacts as a powerful assertion. It fails if the number of tasks, program hashes, or any byte of the serializedOsOutputdata differs between whatrun_bootloaderproduced (verified) and what the Aggregator claimed it used. This cryptographically links the Aggregator's computation (including its internal consistency checks) to the outputs derived from the verified proofsP_1...P_N.
-
-
Final Output and Proof:
- The
Applicative Bootloadertakes theCombinedOutputpart from the Aggregator's output stream (now proven to be based on verified inputs) and places it in its own final output stream.// In applicative_bootloader.cairo let aggregated_output_ptr = aggregator_input_ptr + bootloader_tasks_output_length; let aggregated_output_length = aggregator_output_end - aggregated_output_ptr; memcpy(dst=output_ptr, src=aggregated_output_ptr, len=aggregated_output_length);
- A STARK proof,
P_app_boot, is generated for the entire execution of theApplicative Bootloaderprogram.
Loadinggraph TD subgraph Applicative Bootloader Orchestration N["Input: P_1..N, OsOutput_1..N (claimed), Aggregator Task"] --> O["Step 1: Run `run_bootloader`"]; O --> P["Verified Outputs Stream @ `bootloader_tasks_output_ptr`"]; N --> Q["Step 2: Run Aggregator Task"]; Q --> R["Aggregator Output Stream (contains claimed inputs @ `aggregator_input_ptr`, combined output @ `aggregated_output_ptr`)"]; P & R --> S["Step 3: `memcpy` Check"]; S -- Success --> T["Step 4: Copy Combined Output"]; T --> U["Final Output Stream"]; S -- Failure --> V["Error"]; end W["Final Proof: P_app_boot (proves entire Applicative Bootloader execution)"] - The
Result: Verifying the single proof P_app_boot guarantees:
- The Aggregator program executed correctly (
P_aggwas implicitly verified). - The Aggregator's internal chain-linking assertions held
(
combine_blocks_innerchecks). - All individual block proofs
P_1, ..., P_Nwere valid (verified by the recursiverun_bootloader). - The data the Aggregator actually used for its checks was identical to the
verified outputs corresponding to proofs
P_1, ..., P_N(guaranteed by thememcpycheck).
This P_app_boot can then be used as an input to a subsequent aggregation step,
allowing for hierarchical proof combination.
Starknet's proof aggregation leverages multiple Cairo programs working in
concert. The Cairo Verifier provides the core proof-check capability. The Simple
Bootloader executes tasks (like verification). The Bootloader (run_bootloader)
orchestrates verification of multiple proofs and prepares a verified output
stream. The Aggregator checks the logical consistency between block outputs.
Finally, the Applicative Bootloader ties everything together, running the
verification and aggregation steps and performing a critical memcpy check to
ensure the Aggregator operated on the genuinely verified data. The resulting
single proof encapsulates the validity and sequential integrity of all
aggregated blocks.