|
| 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. |
0 commit comments