Skip to content

Commit 8a3a644

Browse files
MarcosNicolauJuArceMauroToscano
authored
docs: aggregation mode (#1964)
Co-authored-by: Julian Arce <[email protected]> Co-authored-by: Mauro Toscano <[email protected]>
1 parent d6c5ffd commit 8a3a644

File tree

4 files changed

+327
-0
lines changed

4 files changed

+327
-0
lines changed

aggregation_mode/src/backend/mod.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,32 @@ impl ProofAggregator {
187187
.map_err(AggregatedProofSubmissionError::ReceiptError)
188188
}
189189

190+
/// ### Blob capacity
191+
///
192+
/// As dictated in [EIP-4844](https://eips.ethereum.org/EIPS/eip-4844), each blob can hold:
193+
///
194+
/// - `FIELD_ELEMENTS_PER_BLOB = 4096`
195+
/// - `BYTES_PER_FIELD_ELEMENT = 32`
196+
///
197+
/// This gives a total theoretical capacity of:
198+
///
199+
/// `FIELD_ELEMENTS_PER_BLOB * BYTES_PER_FIELD_ELEMENT = 4096 * 32 = 131072 bytes`
200+
///
201+
/// However, this full capacity isn't usable due to the encoding of KZG commitments to elliptic curve points.
202+
/// Specifically:
203+
///
204+
/// - Ethereum uses the BLS12-381 curve, whose scalar field modulus is slightly less than `2^256`
205+
/// (closer to `2^255`).
206+
/// - Therefore, 32-byte field elements can't represent all 256-bit values.
207+
/// - To ensure values are within the field modulus, we **pad with a leading `0x00` byte**,
208+
/// effectively capping values below the modulus.
209+
/// - This reduces the usable payload to **31 bytes per field element**.
210+
///
211+
/// So, the _actual usable capacity_ per blob is:
212+
///
213+
/// `4096 * 31 = 126976 bytes`
214+
///
215+
/// Meaning that we can send as much as 126976 / 32 = 3968 proofs per blob
190216
async fn construct_blob(
191217
&self,
192218
leaves: Vec<[u8; 32]>,
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# Aggregation Mode Deep Dive
2+
3+
The Aggregation Mode runs **once every 24 hours** and performs the following steps:
4+
5+
1. **Fetch Proofs from the Verification Layer**
6+
Queries `NewBatchV3` events from the `AlignedLayerServiceManager` and downloads the batches from `S3`, starting from the last processed block of the previous run.
7+
8+
2. **Filter Proofs**
9+
Filters proofs by supported verifiers and proof types.
10+
11+
3. **Aggregate Proofs in the zkVM**
12+
Selected proofs are aggregated using a zkVM.
13+
14+
4. **Construct the Blob**
15+
A blob is built containing the [commitments](#proof-commitment) of the aggregated proofs.
16+
17+
5. **Send Aggregated Proof**
18+
The final aggregated proof and its blob are sent to the `AlignedProofAggregationService` contract for verification.
19+
20+
> [Note]
21+
> Currently if you want your proof to be verified in the `AggregationMode` you need to submit it via the `VerificationLayer`. In the future, users will be able to decide if they want to use any of the modes in particular or both of them
22+
23+
## Aggregators and Supported Proof Types
24+
25+
Two separate aggregators are run every 24 hours:
26+
27+
- **Risc0**: Aggregates proofs of types `Composite` and `Succinct`.
28+
- **SP1**: Aggregates proofs of type `Compressed`.
29+
30+
## Proof Commitment
31+
32+
The **proof commitment** is a hash that uniquely identifies a proof. It is defined as the keccak of the proof public inputs + program ID:
33+
34+
- **For SP1**:
35+
The commitment is computed as: `keccak(proof_public_inputs_bytes || vk_hash_bytes)`
36+
- **For Risc0**:
37+
The commitment is computed as: `keccack(receipt_public_inputs_bytes || image_id_bytes)`
38+
39+
## Multilayer Aggregation
40+
41+
To scale aggregation without exhausting zkVM memory, aggregation is split in two programs:
42+
43+
1. **User Proof Aggregator**
44+
Processes chunks of `n` user proofs. Each run creates an aggregated proof that commits to a Merkle root of the user proofs inputs. This step is repeated for as many chunks as needed. Usually each chunks contains `256` proofs but it can be lowered based on the machine specs.
45+
46+
2. **Chunk Aggregator**
47+
Aggregates all chunk-level proofs into a single final proof. It receives:
48+
49+
- The chunked proofs
50+
- The original [proofs commitments](#proof-commitment) included each chunk received
51+
52+
During verification, it checks that each chunk’s committed Merkle root matches the reconstructed root to ensure input correctness. The final Merkle root, representing all user [proofs commitments](#proof-commitment), is then committed as a public input.
53+
54+
## Verification
55+
56+
Once aggregated, the proof is sent to Ethereum and verified via the `AlignedProofAggregationService` contract. Depending on the proving system, the contract invokes:
57+
58+
- `verifySP1` for SP1 proofs
59+
- `verifyRisc0` for Risc0 proofs
60+
61+
Each function receives:
62+
63+
- The public inputs
64+
- The proof binary
65+
66+
The program ID is hardcoded in the contract to ensure only trusted aggregation programs (`chunk_aggregator`) are accepted.
67+
68+
If verification succeeds, the new proof is added to the `aggregatedProofs` map in contract storage.
69+
70+
### Proof Inclusion Verification
71+
72+
To verify a user’s proof on-chain, the following must be provided:
73+
74+
- The proof bytes
75+
- The proof public inputs
76+
- The program ID
77+
- A Merkle proof
78+
79+
The Merkle root is computed and checked for existence in the contract using the `verifyProofInclusion` function of the `ProofAggregationServiceContract`, which:
80+
81+
1. Computes the merkle root
82+
2. Returns `true` or `false` depending if there exists an `aggregatedProof` with the computed root.
83+
84+
## Data Availability
85+
86+
When submitting the aggregated proof to Ethereum, we include a **blob** that contains the [commitments](#proof-commitment) of all the individual proofs that were aggregated. This blob serves two main purposes:
87+
88+
- It makes the [proof commitments](#proof-commitment) publicly available for **18 days**.
89+
- It allows users to:
90+
- Inspect which proofs were aggregated
91+
- Get a Merkle proof to verify that their proof is included in the aggregated proof
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
## Aggregation Mode L2 integration example
2+
3+
This guide demonstrates how to build a dummy L2 application that integrates with Aligned Aggregation Mode. The L2 does not post state diffs or any data to Ethereum, only commitments. The prover has to prove that:
4+
5+
1. The state database used in the proof must match the commitment stored in the on-chain contract. This is validated by computing the commitment of the received data in the zkvm and then exposing it as a public input.
6+
2. The users performing the transfers have enough balance
7+
8+
After processing the transfers, the vm computes the commitment of the post state, which is exposed as a public input. The smart contract then updates the on-chain state root. If a user later wants to retrieve their state, the application must return it along with a Merkle proof, so they can verify it against the contract’s state root.
9+
10+
Notice a lot of checks that a real L2 should have are missing, since the focus are on the integration of Aligned.
11+
12+
The code can be viewed at `examples/l2`.
13+
14+
## L2 workflow overview
15+
16+
This Layer 2 (L2) system operates in two main steps:
17+
18+
- Off-chain execution and proof generation + verification with Aligned Verification Layer (a.k.a Fast Mode).
19+
- On-chain state update via proof verification with Aligned Aggregation Mode.
20+
21+
In Step 1, we execute user transfers and generate a zkVM-based proof of the state transition, which is submitted to Aligned’s verification layer.
22+
23+
In Step 2, once the proof is aggregated (every 24 hours), it is verified on-chain to update the global state.
24+
25+
### Step 1: Off-Chain Execution + Proof Generation
26+
27+
1. Initialize State: Load or initialize the current system state.
28+
2. Load Transfers: Retrieve or receive the user transfer data for this batch.
29+
3. Execute in zkVM: Run the zkVM with the loaded transfers to compute the new state.
30+
4. Generate Proof: Produce a zk-proof for the executed state transition committing the commitment of the received + the commitment of the new state.
31+
5. Submit Proof to Aligned: Send the proof to Aligned Verification Layer
32+
6. Save the binary proof locally for later on-chain verification.
33+
34+
### Step 2: Proof Verification + On-Chain State Update
35+
36+
7. Load the proof binary: Retrieve the saved proof binary from disk.
37+
8. Update On-Chain State: Call the smart contract method `updateStateTransition`, which:
38+
39+
- Internally calls `verifyProofInclusion` on AlignedProofAggregationService which:
40+
1. Computes the proof commitment from the proof `public_inputs` and `program_id`.
41+
2. Uses the Merkle proof to reconstruct and validate the Merkle root.
42+
3. Confirms whether there exists and aggregated proof with that root.
43+
- Validates that the `initial_state_root` proof public input matches the on-chain state.
44+
- If valid, updates the on-chain state root to the `post_state_root`.
45+
46+
# Usage
47+
48+
### Requirements
49+
50+
1. [Rust](https://www.rust-lang.org/tools/install): we have tested in v1.85.1
51+
2. [Foundry](https://book.getfoundry.sh/getting-started/installation)
52+
3. [Docker](https://docs.docker.com/engine/): for SP1 prover
53+
54+
Submodules of the repo should be imported by running on the root folder:
55+
56+
```shell
57+
make submodules
58+
```
59+
60+
You can run the example on:
61+
62+
- [Holesky](#setup-holeksy)
63+
- [Localnet](#setup-localnet)
64+
65+
## Setup Holeksy
66+
67+
### 1. Create keystore
68+
69+
You can use cast to create a local keystore. If you already have one you can skip this step.
70+
71+
```bash
72+
cast wallet new-mnemonic
73+
```
74+
75+
Then you can import your created keystore using:
76+
77+
```bash
78+
cast wallet import --interactive <path_to_keystore.json>
79+
```
80+
81+
Then you need to obtain some funds to pay for gas and proof verification.
82+
You can do this by using this [faucet](https://cloud.google.com/application/web3/faucet/ethereum/holesky)
83+
84+
_This same wallet is used to send the proof via aligned, so you'll also need to fund it on aligned. Follow this [guide](https://docs.alignedlayer.com/guides/0_submitting_proofs#id-2.-send-funds-to-aligned)._
85+
86+
### 2. Deploy the contract
87+
88+
- Generate the base `.env`:
89+
90+
```shell
91+
make gen_env_contract_holesky
92+
```
93+
94+
- Get the program ID of the l2 program you are proving:
95+
96+
```shell
97+
make generate_program_id
98+
```
99+
100+
- Complete the following fields `contracts/.env` file:
101+
102+
- `PROGRAM_ID=` (use the previously generated ID, you can re check with a `cat ./crates/l2/programs_ids.json` )
103+
- `PRIVATE_KEY`: the private key used for the deployment, it needs to have some funds to pay for the deployment.
104+
- `OWNER_ADDRESS`: you have to provide the _address of the wallet created in step `1.`_.
105+
106+
- Deploy the contracts with:
107+
108+
```shell
109+
make deploy_contract
110+
```
111+
112+
_Save the output contract address._
113+
114+
### 3. Setup the L2
115+
116+
- Generate the base `.env` run:
117+
118+
```shell
119+
make gen_env_l2_holesky
120+
```
121+
122+
- Complete the missing fields on the `.env`:
123+
124+
- `PRIVATE_KEY_STORE_PATH`: The path to the keystore created in `1.`.
125+
- `PRIVATE_KEY_STORE_PASSWORD`: The password of the keystore crated in step `1.`.
126+
- `STATE_TRANSITION_CONTRACT_ADDRESS`: The address of the contract deployed in step `2.`
127+
128+
Finally [run the l2](#running-the-l2).
129+
130+
## Setup Localnet
131+
132+
You can also run this example on a local devnet. To get started, navigate to the root of the Aligned repository
133+
134+
- Start Ethereum package and the Batcher
135+
136+
```shell
137+
# This will start the local net
138+
make ethereum_package_start
139+
# Start the batcher
140+
make batcher_start_ethereum_package
141+
```
142+
143+
- Navigate back to the example directory:
144+
145+
```shell
146+
cd examples/l2
147+
```
148+
149+
- Generate the `.env` files for the contracts and L2:
150+
151+
```shell
152+
make gen_env_contract_devnet
153+
make gen_env_l2_devnet
154+
```
155+
156+
- Generate a pre funded wallet (or create one as specified [previously here](#1-create-keystore)):
157+
158+
```shell
159+
# This will generate the keystore and fund it on aligned
160+
make gen_devnet_owner_wallet
161+
```
162+
163+
- Generate the program ID of the program that is going to be proven:
164+
165+
```shell
166+
make generate_program_id
167+
```
168+
169+
- Set the generated program ID on `contracts/.env`.
170+
171+
- Deploy the contract
172+
173+
```shell
174+
make deploy_contract
175+
```
176+
177+
- Set the output address of the contract in `.env`
178+
179+
- [run the l2](#running-the-l2)
180+
181+
## Running the L2
182+
183+
- Set up the initial State
184+
185+
```shell
186+
make init_state
187+
```
188+
189+
- Perform the L2 account updates and prove them in the zkvm:
190+
191+
```shell
192+
make prove_state_transition
193+
```
194+
195+
- Wait 24 hs for the proof to be aggregated, or if running locally, run the aggregator with either:
196+
197+
```make start_proof_aggregator_ethereum_package AGGREGATOR=sp1```
198+
199+
or with cuda:
200+
`make start_proof_aggregator_gpu_ethereum_package AGGREGATOR=sp1`
201+
202+
- Update state transition on chain:
203+
204+
```shell
205+
make update_state_on_chain
206+
```
207+
208+
You should see a transaction receipt in the console and after the stateRoot updated on-chain.

docs/SUMMARY.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,15 @@
2020
* [Aggregator](./2_architecture/components/5_aggregator.md)
2121
* [Explorer](./2_architecture/components/6_explorer.md)
2222
* [Aggregation mode](2_architecture/2_aggregation_mode.md)
23+
* [Deep Dive](2_architecture/agg_mode_components/1_deep_dive.md)
2324

2425
## Guides
2526

2627
* [Submitting proofs](3_guides/0_submitting_proofs.md)
2728
* [Build your first Aligned Application](3_guides/2_build_your_first_aligned_application.md)
2829
* [Modify ZkQuiz Questions](3_guides/2.2_modify_zkquiz_questions.md)
2930
* [Validating public input](3_guides/3_validating_public_input.md)
31+
* [Aggregation Mode](3_guides/3.1_aggregation_mode.md)
3032
* [SDK Intro](3_guides/1_SDK_how_to.md)
3133
* [SDK API Reference](3_guides/1.2_SDK_api_reference.md)
3234
* [Generating proofs for Aligned](3_guides/4_generating_proofs.md)

0 commit comments

Comments
 (0)