diff --git a/crates/l2/contracts/bin/deployer/main.rs b/crates/l2/contracts/bin/deployer/main.rs index 7894f54bd86..66056e74ac2 100644 --- a/crates/l2/contracts/bin/deployer/main.rs +++ b/crates/l2/contracts/bin/deployer/main.rs @@ -493,20 +493,15 @@ async fn initialize_contracts( .to_str() .ok_or(DeployerError::FailedToGetStringFromPath)?, ); - let sp1_vk_string = read_to_string(&opts.sp1_vk_path).unwrap_or_else(|_| { + + let sp1_vk = std::fs::read(&opts.sp1_vk_path) + .unwrap_or_else(|_| { warn!( path = opts.sp1_vk_path, "Failed to read SP1 verification key file, will use 0x00..00, this is expected in dev mode" ); - "0x00".to_string() - }); - let sp1_vk = hex::decode(sp1_vk_string.trim_start_matches("0x")) - .map_err(|err| { - DeployerError::DecodingError(format!( - "failed to parse sp1_vk ({sp1_vk_string}) from hex: {err}" - )) - })? - .into(); + vec![0u8; 32] + }).into(); let deployer_address = get_address_from_secret_key(&opts.private_key)?; diff --git a/crates/l2/contracts/src/l1/OnChainProposer.sol b/crates/l2/contracts/src/l1/OnChainProposer.sol index d330dc37c7b..e537d3ebf59 100644 --- a/crates/l2/contracts/src/l1/OnChainProposer.sol +++ b/crates/l2/contracts/src/l1/OnChainProposer.sol @@ -355,7 +355,6 @@ contract OnChainProposer is function verifyBatchAligned( uint256 batchNumber, bytes calldata alignedPublicInputs, - bytes32 alignedProgramVKey, bytes32[] calldata alignedMerkleProof ) external override onlySequencer whenNotPaused { require( @@ -377,7 +376,7 @@ contract OnChainProposer is bytes memory callData = abi.encodeWithSignature( "verifyProofInclusion(bytes32[],bytes32,bytes)", alignedMerkleProof, - alignedProgramVKey, + SP1_VERIFICATION_KEY, alignedPublicInputs ); (bool callResult, bytes memory response) = ALIGNEDPROOFAGGREGATOR diff --git a/crates/l2/contracts/src/l1/interfaces/IOnChainProposer.sol b/crates/l2/contracts/src/l1/interfaces/IOnChainProposer.sol index 1906e27c8e2..361e82ce486 100644 --- a/crates/l2/contracts/src/l1/interfaces/IOnChainProposer.sol +++ b/crates/l2/contracts/src/l1/interfaces/IOnChainProposer.sol @@ -84,12 +84,10 @@ interface IOnChainProposer { /// @notice Method used to verify a batch of L2 blocks in Aligned. /// @param alignedPublicInputs The public inputs bytes of the proof. - /// @param alignedProgramVKey The public verifying key. /// @param alignedMerkleProof The Merkle proof (sibling hashes) needed to reconstruct the Merkle root. function verifyBatchAligned( uint256 batchNumber, bytes calldata alignedPublicInputs, - bytes32 alignedProgramVKey, bytes32[] calldata alignedMerkleProof ) external; diff --git a/crates/l2/prover/src/backends/sp1.rs b/crates/l2/prover/src/backends/sp1.rs index a853c1a6e16..0b367f1aa8b 100644 --- a/crates/l2/prover/src/backends/sp1.rs +++ b/crates/l2/prover/src/backends/sp1.rs @@ -97,7 +97,6 @@ pub fn to_batch_proof( BatchProof::ProofBytes(ProofBytes { proof: bincode::serialize(&proof.proof)?, public_values: proof.proof.public_values.to_vec(), - vk: proof.vk.hash_bytes().into(), }) } else { BatchProof::ProofCalldata(to_calldata(proof)) diff --git a/crates/l2/prover/zkvm/interface/build.rs b/crates/l2/prover/zkvm/interface/build.rs index 1eeeea32323..4947299c7e2 100644 --- a/crates/l2/prover/zkvm/interface/build.rs +++ b/crates/l2/prover/zkvm/interface/build.rs @@ -1,5 +1,6 @@ fn main() { println!("cargo::rerun-if-changed=build.rs"); + println!("cargo:rerun-if-env-changed=PROVER_CLIENT_ALIGNED"); #[cfg(feature = "risc0")] build_risc0_program(); @@ -53,8 +54,16 @@ fn build_sp1_program() { .expect("could not read SP1 elf file"); let prover = ProverClient::from_env(); let (_, vk) = prover.setup(&elf); - let vk = vk.vk.bytes32(); - dbg!(&vk); - std::fs::write("./sp1/out/riscv32im-succinct-zkvm-vk", &vk) - .expect("could not write SP1 vk to file"); + + let aligned_mode = std::env::var("PROVER_CLIENT_ALIGNED").unwrap_or("false".to_string()); + + if aligned_mode == "true" { + let vk = vk.vk.hash_bytes(); + std::fs::write("./sp1/out/riscv32im-succinct-zkvm-vk", &vk) + .expect("could not write SP1 vk to file"); + } else { + let vk = vk.vk.bytes32_raw(); + std::fs::write("./sp1/out/riscv32im-succinct-zkvm-vk", &vk) + .expect("could not write SP1 vk to file"); + }; } diff --git a/crates/l2/sequencer/errors.rs b/crates/l2/sequencer/errors.rs index 01947af9f2a..9ed6c739b28 100644 --- a/crates/l2/sequencer/errors.rs +++ b/crates/l2/sequencer/errors.rs @@ -143,8 +143,6 @@ pub enum ProofVerifierError { InternalError(String), #[error("ProofVerifier failed to parse beacon url")] ParseBeaconUrl(String), - #[error("ProofVerifier decoding error: {0}")] - DecodingError(String), #[error("Failed with a SaveStateError: {0}")] SaveStateError(#[from] SaveStateError), #[error("Failed to encode calldata: {0}")] diff --git a/crates/l2/sequencer/l1_proof_verifier.rs b/crates/l2/sequencer/l1_proof_verifier.rs index 29cc6aa885f..e2f7421e8cc 100644 --- a/crates/l2/sequencer/l1_proof_verifier.rs +++ b/crates/l2/sequencer/l1_proof_verifier.rs @@ -23,8 +23,7 @@ use super::{ utils::{send_verify_tx, sleep_random}, }; -const ALIGNED_VERIFY_FUNCTION_SIGNATURE: &str = - "verifyBatchAligned(uint256,bytes,bytes32,bytes32[])"; +const ALIGNED_VERIFY_FUNCTION_SIGNATURE: &str = "verifyBatchAligned(uint256,bytes,bytes32[])"; pub async fn start_l1_proof_verifier(cfg: SequencerConfig) -> Result<(), SequencerError> { let l1_proof_verifier = L1ProofVerifier::new( @@ -46,6 +45,7 @@ struct L1ProofVerifier { on_chain_proposer_address: Address, proof_verify_interval_ms: u64, network: Network, + sp1_vk: [u8; 32], } impl L1ProofVerifier { @@ -57,6 +57,10 @@ impl L1ProofVerifier { ) -> Result { let eth_client = EthClient::new_with_multiple_urls(eth_cfg.rpc_url.clone())?; + let sp1_vk = eth_client + .get_sp1_vk(committer_cfg.on_chain_proposer_address) + .await?; + Ok(Self { eth_client, beacon_url: aligned_cfg.beacon_url.clone(), @@ -65,6 +69,7 @@ impl L1ProofVerifier { l1_private_key: proof_coordinator_cfg.l1_private_key, on_chain_proposer_address: committer_cfg.on_chain_proposer_address, proof_verify_interval_ms: aligned_cfg.aligned_verifier_interval_ms, + sp1_vk, }) } @@ -115,13 +120,9 @@ impl L1ProofVerifier { ) -> Result, ProofVerifierError> { let proof = read_proof(batch_number, StateFileType::BatchProof(ProverType::Aligned))?; let public_inputs = proof.public_values(); - // TODO: use a hardcoded vk - let vk = proof.vk(); let verification_data = AggregationModeVerificationData::SP1 { - vk: vk.clone().try_into().map_err(|e| { - ProofVerifierError::DecodingError(format!("Failed to decode vk: {e:?}")) - })?, + vk: self.sp1_vk, public_inputs: public_inputs.clone(), }; @@ -170,7 +171,6 @@ impl L1ProofVerifier { let calldata_values = [ Value::Uint(U256::from(batch_number)), Value::Bytes(public_inputs.into()), - Value::FixedBytes(vk.into()), Value::Array(merkle_path), ]; diff --git a/crates/l2/utils/prover/proving_systems.rs b/crates/l2/utils/prover/proving_systems.rs index 6de00441a2b..0498525bcd0 100644 --- a/crates/l2/utils/prover/proving_systems.rs +++ b/crates/l2/utils/prover/proving_systems.rs @@ -109,13 +109,6 @@ impl BatchProof { BatchProof::ProofBytes(proof_bytes) => proof_bytes.public_values.clone(), } } - - pub fn vk(&self) -> Vec { - match self { - BatchProof::ProofCalldata(_) => vec![], - BatchProof::ProofBytes(proof_bytes) => proof_bytes.vk.clone(), - } - } } /// Contains the Proof and the public values generated by the prover. @@ -124,8 +117,6 @@ impl BatchProof { pub struct ProofBytes { pub proof: Vec, pub public_values: Vec, - // TODO: use a hardcoded value for the vk - pub vk: Vec, } /// Contains the data ready to be sent to the on-chain verifiers. diff --git a/crates/networking/rpc/clients/eth/mod.rs b/crates/networking/rpc/clients/eth/mod.rs index 986ea10cfb3..3d02c537f6d 100644 --- a/crates/networking/rpc/clients/eth/mod.rs +++ b/crates/networking/rpc/clients/eth/mod.rs @@ -1057,6 +1057,14 @@ impl EthClient { .await } + pub async fn get_sp1_vk( + &self, + on_chain_proposer_address: Address, + ) -> Result<[u8; 32], EthClientError> { + self._call_bytes32_variable(b"SP1_VERIFICATION_KEY()", on_chain_proposer_address) + .await + } + pub async fn get_last_fetched_l1_block( &self, common_bridge_address: Address, @@ -1167,6 +1175,27 @@ impl EthClient { Ok(value) } + async fn _call_bytes32_variable( + &self, + selector: &[u8], + contract_address: Address, + ) -> Result<[u8; 32], EthClientError> { + let hex_string = self._generic_call(selector, contract_address).await?; + + let hex = hex_string.strip_prefix("0x").ok_or(EthClientError::Custom( + "Couldn't strip '0x' prefix from hex string".to_owned(), + ))?; + + let bytes = hex::decode(hex) + .map_err(|e| EthClientError::Custom(format!("Failed to decode hex string: {}", e)))?; + + let arr: [u8; 32] = bytes.try_into().map_err(|_| { + EthClientError::Custom("Failed to convert bytes to [u8; 32]".to_owned()) + })?; + + Ok(arr) + } + pub async fn wait_for_transaction_receipt( &self, tx_hash: H256, diff --git a/docs/l2/aligned_mode.md b/docs/l2/aligned_mode.md index 475621998f9..a9336a3448d 100644 --- a/docs/l2/aligned_mode.md +++ b/docs/l2/aligned_mode.md @@ -7,7 +7,21 @@ This document explains how to run an Ethrex L2 node in **Aligned mode** and high > [!IMPORTANT] > For this guide we assumed that there is an L1 running with all Aligned environment set. -### 1. Deploying L1 Contracts +### 1. Generate the SP1 ELF Program and Verification Key + +Run: + +```bash +cd ethrex/crates/l2 +SP1_PROVER=cuda make build-prover PROVER=sp1 PROVER_CLIENT_ALIGNED=true +``` + +This will generate the SP1 ELF program and verification key under: +- `crates/l2/prover/zkvm/interface/sp1/out/riscv32im-succinct-zkvm-elf` +- `crates/l2/prover/zkvm/interface/sp1/out/riscv32im-succinct-zkvm-vk` + + +### 2. Deploying L1 Contracts In a console with `ethrex/crates/l2` as the current directory, run the following command: @@ -25,14 +39,15 @@ cargo run --release --bin ethrex_l2_l1_deployer --manifest-path contracts/Cargo. --bridge-owner
\ --on-chain-proposer-owner
\ --private-keys-file-path \ - --sequencer-registry-owner
+ --sequencer-registry-owner
\ + --sp1-vk-path ``` > [!NOTE] > In this step we are initiallizing the `OnChainProposer` contract with the `ALIGNED_PROOF_AGGREGATOR_SERVICE_ADDRESS` and skipping the rest of verifiers. > Save the addresses of the deployed proxy contracts, as you will need them to run the L2 node. -### 2. Deposit funds to the `AlignedBatchePaymentService` contract from the proof sender +### 3. Deposit funds to the `AlignedBatcherPaymentService` contract from the proof sender ```bash aligned \ @@ -44,7 +59,7 @@ aligned \ > [!IMPORTANT] > Using the [Aligned CLI](https://docs.alignedlayer.com/guides/9_aligned_cli) -### 3. Running a node +### 4. Running a node In a console with `ethrex/crates/l2` as the current directory, run the following command: @@ -90,6 +105,10 @@ SP1_PROVER=cuda make init-prover PROVER=sp1 PROVER_CLIENT_ALIGNED=true ## How to Run Using an Aligned Dev Environment +> [!IMPORTANT] +> This guide asumes you have already generated the SP1 ELF Program and Verification Key. See: [Generate the SP1 ELF Program and Verification Key](#1-generate-the-sp1-elf-program-and-verification-key) + + ### Set Up the Aligned Environment 1. Clone the Aligned repository and checkout the currently supported release: @@ -207,12 +226,24 @@ SP1_PROVER=cuda make init-prover PROVER=sp1 PROVER_CLIENT_ALIGNED=true ### Aggregate proofs: -After some time, you will see that the `l1_proof_verifier` is waiting for Aligned to aggregate the proofs. You can aggregate them by running: +After some time, you will see that the `l1_proof_verifier` is waiting for Aligned to aggregate the proofs: +``` +2025-06-18T22:03:53.470356Z INFO ethrex_l2::sequencer::l1_proof_verifier: Batch 1 has not yet been aggregated by Aligned. Waiting for 5 seconds +``` + +You can aggregate them by running: ``` cd aligned_layer make start_proof_aggregator AGGREGATOR=sp1 ``` +If successful, the `l1_proof_verifier` will print the following logs: + +``` +INFO ethrex_l2::sequencer::l1_proof_verifier: Proof for batch 1 aggregated by Aligned with commitment 0xa9a0da5a70098b00f97d96cee43867c7aa8f5812ca5388da7378454580af2fb7 and Merkle root 0xa9a0da5a70098b00f97d96cee43867c7aa8f5812ca5388da7378454580af2fb7 +INFO ethrex_l2::sequencer::l1_proof_verifier: Batch 1 verified in AlignedProofAggregatorService, with transaction hash 0x731d27d81b2e0f1bfc0f124fb2dd3f1a67110b7b69473cacb6a61dea95e63321 +``` + ## Behavioral Differences in Aligned Mode ### Prover