diff --git a/cmd/ethrex/l2/options.rs b/cmd/ethrex/l2/options.rs index 518fb42268..45a09c898e 100644 --- a/cmd/ethrex/l2/options.rs +++ b/cmd/ethrex/l2/options.rs @@ -103,6 +103,13 @@ pub struct SequencerOptions { help_heading = "Monitor options" )] pub no_monitor: bool, + #[clap( + long, + value_name = "UINT64", + env = "ETHREX_OSAKA_ACTIVATION_TIME", + help = "Block timestamp at which the Osaka fork activates. If not set, it will assume Osaka is already active." + )] + pub osaka_activation_time: Option, } pub fn parse_signer( @@ -174,6 +181,7 @@ impl TryFrom for SequencerConfig { arbitrary_base_blob_gas_price: opts.committer_opts.arbitrary_base_blob_gas_price, signer: committer_signer, validium: opts.validium, + osaka_activation_time: opts.osaka_activation_time, }, eth: EthConfig { rpc_url: opts.eth_opts.rpc_url, @@ -205,6 +213,7 @@ impl TryFrom for SequencerConfig { .proof_coordinator_tdx_private_key, qpl_tool_path: opts.proof_coordinator_opts.proof_coordinator_qpl_tool_path, validium: opts.validium, + osaka_activation_time: opts.osaka_activation_time, }, based: BasedConfig { enabled: opts.based, @@ -219,6 +228,7 @@ impl TryFrom for SequencerConfig { block_fetcher: BlockFetcherConfig { fetch_interval_ms: opts.based_opts.block_fetcher.fetch_interval_ms, fetch_block_step: opts.based_opts.block_fetcher.fetch_block_step, + osaka_activation_time: opts.osaka_activation_time, }, }, aligned: AlignedConfig { @@ -235,6 +245,7 @@ impl TryFrom for SequencerConfig { enabled: !opts.no_monitor, tick_rate: opts.monitor_opts.tick_rate, batch_widget_height: opts.monitor_opts.batch_widget_height, + osaka_activation_time: opts.osaka_activation_time, }, admin_server: AdminConfig { listen_ip: opts.admin_opts.admin_listen_ip, diff --git a/crates/common/crypto/kzg.rs b/crates/common/crypto/kzg.rs index 5f87483c2d..5090424383 100644 --- a/crates/common/crypto/kzg.rs +++ b/crates/common/crypto/kzg.rs @@ -148,3 +148,20 @@ pub fn blob_to_kzg_commitment_and_proof(blob: &Blob) -> Result<(Commitment, Proo Ok((commitment_bytes.into_inner(), proof_bytes.into_inner())) } + +#[cfg(feature = "c-kzg")] +pub fn blob_to_commitment_and_cell_proofs( + blob: &Blob, +) -> Result<(Commitment, Vec), KzgError> { + let c_kzg_settings = c_kzg::ethereum_kzg_settings(8); + let blob: c_kzg::Blob = (*blob).into(); + let commitment = + c_kzg::KzgSettings::blob_to_kzg_commitment(c_kzg::ethereum_kzg_settings(8), &blob)?; + let commitment_bytes = commitment.to_bytes(); + + let (_cells, cell_proofs) = c_kzg_settings + .compute_cells_and_kzg_proofs(&blob) + .map_err(KzgError::CKzg)?; + let cell_proofs = cell_proofs.map(|p| p.to_bytes().into_inner()); + Ok((commitment_bytes.into_inner(), cell_proofs.to_vec())) +} diff --git a/crates/common/types/blobs_bundle.rs b/crates/common/types/blobs_bundle.rs index 725a51a3ae..1cfd4bda17 100644 --- a/crates/common/types/blobs_bundle.rs +++ b/crates/common/types/blobs_bundle.rs @@ -1,6 +1,8 @@ use std::ops::AddAssign; use crate::serde_utils; +#[cfg(feature = "c-kzg")] +use crate::types::Fork; use crate::types::constants::VERSIONED_HASH_VERSION_KZG; use crate::{Bytes, H256}; @@ -80,23 +82,31 @@ impl BlobsBundle { // In the future we might want to provide a new method that calculates the commitments and proofs using the following. #[cfg(feature = "c-kzg")] - pub fn create_from_blobs(blobs: &Vec) -> Result { - use ethrex_crypto::kzg::blob_to_kzg_commitment_and_proof; + pub fn create_from_blobs(blobs: &Vec, fork: Fork) -> Result { + use ethrex_crypto::kzg::{ + blob_to_commitment_and_cell_proofs, blob_to_kzg_commitment_and_proof, + }; let mut commitments = Vec::new(); let mut proofs = Vec::new(); // Populate the commitments and proofs for blob in blobs { - let (commitment, proof) = blob_to_kzg_commitment_and_proof(blob)?; - commitments.push(commitment); - proofs.push(proof); + if fork < Fork::Osaka { + let (commitment, proof) = blob_to_kzg_commitment_and_proof(blob)?; + commitments.push(commitment); + proofs.push(proof); + } else { + let (commitment, cell_proofs) = blob_to_commitment_and_cell_proofs(blob)?; + commitments.push(commitment); + proofs.extend(cell_proofs); + } } Ok(Self { blobs: blobs.clone(), commitments, proofs, - version: 0, + version: if fork <= Fork::Prague { 0 } else { 1 }, }) } @@ -266,8 +276,9 @@ mod tests { }) .collect(); - let blobs_bundle = crate::types::BlobsBundle::create_from_blobs(&blobs) - .expect("Failed to create blobs bundle"); + let blobs_bundle = + crate::types::BlobsBundle::create_from_blobs(&blobs, crate::types::Fork::Prague) + .expect("Failed to create blobs bundle"); let blob_versioned_hashes = blobs_bundle.generate_versioned_hashes(); @@ -403,8 +414,9 @@ mod tests { let blobs = std::iter::repeat_n(blob, super::MAX_BLOB_COUNT_ELECTRA + 1).collect::>(); - let blobs_bundle = crate::types::BlobsBundle::create_from_blobs(&blobs) - .expect("Failed to create blobs bundle"); + let blobs_bundle = + crate::types::BlobsBundle::create_from_blobs(&blobs, crate::types::Fork::Prague) + .expect("Failed to create blobs bundle"); let blob_versioned_hashes = blobs_bundle.generate_versioned_hashes(); diff --git a/crates/common/types/transaction.rs b/crates/common/types/transaction.rs index a8df1275fb..7ea6c52058 100644 --- a/crates/common/types/transaction.rs +++ b/crates/common/types/transaction.rs @@ -1498,9 +1498,9 @@ mod serde_impl { use serde_json::Value; use std::{collections::HashMap, str::FromStr}; - #[cfg(feature = "c-kzg")] - use crate::types::BYTES_PER_BLOB; use crate::types::{AccessListItem, AuthorizationTuple, BlobsBundleError}; + #[cfg(feature = "c-kzg")] + use crate::types::{BYTES_PER_BLOB, Fork}; use super::*; @@ -2302,10 +2302,11 @@ mod serde_impl { } #[cfg(feature = "c-kzg")] - impl TryFrom for WrappedEIP4844Transaction { - type Error = GenericTransactionError; - - fn try_from(value: GenericTransaction) -> Result { + impl WrappedEIP4844Transaction { + pub fn from_generic_tx( + value: GenericTransaction, + fork: Fork, + ) -> Result { let blobs = value .blobs .iter() @@ -2319,8 +2320,8 @@ mod serde_impl { Ok(Self { tx: value.try_into()?, - wrapper_version: None, - blobs_bundle: BlobsBundle::create_from_blobs(&blobs)?, + wrapper_version: (fork > Fork::Prague).then_some(1), + blobs_bundle: BlobsBundle::create_from_blobs(&blobs, fork)?, }) } } diff --git a/crates/l2/based/block_fetcher.rs b/crates/l2/based/block_fetcher.rs index 15240974da..2db237b6db 100644 --- a/crates/l2/based/block_fetcher.rs +++ b/crates/l2/based/block_fetcher.rs @@ -13,7 +13,9 @@ use ethrex_l2_common::{ privileged_transactions::compute_privileged_transactions_hash, state_diff::prepare_state_diff, }; -use ethrex_l2_sdk::{get_last_committed_batch, get_last_fetched_l1_block}; +use ethrex_l2_sdk::{ + get_last_committed_batch, get_last_fetched_l1_block, is_osaka_activated_on_l1, +}; use ethrex_rlp::decode::RLPDecode; use ethrex_rpc::{EthClient, types::receipt::RpcLog}; use ethrex_storage::Store; @@ -93,6 +95,7 @@ pub struct BlockFetcher { fetch_interval_ms: u64, last_l1_block_fetched: U256, fetch_block_step: U256, + osaka_activation_time: Option, } impl BlockFetcher { @@ -118,6 +121,7 @@ impl BlockFetcher { fetch_interval_ms: cfg.based.block_fetcher.fetch_interval_ms, last_l1_block_fetched, fetch_block_step: cfg.based.block_fetcher.fetch_block_step.into(), + osaka_activation_time: cfg.based.block_fetcher.osaka_activation_time, }) } @@ -388,8 +392,11 @@ impl BlockFetcher { ) .map_err(|_| BlockFetcherError::BlobBundleError)?; - let (blobs_bundle, _) = - generate_blobs_bundle(&state_diff).map_err(|_| BlockFetcherError::BlobBundleError)?; + let fork = is_osaka_activated_on_l1(&self.eth_client, self.osaka_activation_time) + .await + .map_err(BlockFetcherError::EthClientError)?; + let (blobs_bundle, _) = generate_blobs_bundle(&state_diff, fork) + .map_err(|_| BlockFetcherError::BlobBundleError)?; Ok(Batch { number: batch_number.as_u64(), diff --git a/crates/l2/monitor/app.rs b/crates/l2/monitor/app.rs index 3cb1bd28a7..793c36a39c 100644 --- a/crates/l2/monitor/app.rs +++ b/crates/l2/monitor/app.rs @@ -71,6 +71,8 @@ pub struct EthrexMonitorWidget { pub rollup_store: StoreRollup, pub last_scroll: Instant, pub overview_selected_widget: usize, + + pub osaka_activation_time: Option, } #[derive(Clone, Debug)] @@ -215,6 +217,7 @@ impl EthrexMonitorWidget { rollup_store, last_scroll: Instant::now(), overview_selected_widget: 0, + osaka_activation_time: cfg.monitor.osaka_activation_time, }; monitor_widget.selected_table().selected(true); monitor_widget.on_tick().await?; @@ -324,7 +327,11 @@ impl EthrexMonitorWidget { .await?; self.mempool.on_tick(&self.rollup_client).await?; self.batches_table - .on_tick(&self.eth_client, &self.rollup_store) + .on_tick( + &self.eth_client, + &self.rollup_store, + self.osaka_activation_time, + ) .await?; self.blocks_table.on_tick(&self.store).await?; self.l1_to_l2_messages diff --git a/crates/l2/monitor/widget/batches.rs b/crates/l2/monitor/widget/batches.rs index 855298b161..6fb7ff4610 100644 --- a/crates/l2/monitor/widget/batches.rs +++ b/crates/l2/monitor/widget/batches.rs @@ -1,5 +1,8 @@ -use ethrex_common::{Address, H256, types::batch::Batch}; -use ethrex_l2_sdk::get_last_committed_batch; +use ethrex_common::{ + Address, H256, + types::{Fork, batch::Batch}, +}; +use ethrex_l2_sdk::{get_last_committed_batch, is_osaka_activated_on_l1}; use ethrex_rpc::EthClient; use ethrex_storage_rollup::StoreRollup; use ratatui::{ @@ -50,27 +53,37 @@ impl BatchesTable { &mut self, eth_client: &EthClient, rollup_store: &StoreRollup, + osaka_activation_time: Option, ) -> Result<(), MonitorError> { let mut new_latest_batches = Self::fetch_new_items( &mut self.last_l1_block_fetched, self.on_chain_proposer_address, eth_client, rollup_store, + osaka_activation_time, ) .await?; new_latest_batches.truncate(BATCH_WINDOW_SIZE); + let fork = is_osaka_activated_on_l1(eth_client, osaka_activation_time) + .await + .map_err(MonitorError::EthClientError)?; + let n_new_latest_batches = new_latest_batches.len(); self.items .truncate(BATCH_WINDOW_SIZE - n_new_latest_batches); - self.refresh_items(rollup_store).await?; + self.refresh_items(rollup_store, fork).await?; self.items.extend_from_slice(&new_latest_batches); self.items.rotate_right(n_new_latest_batches); Ok(()) } - async fn refresh_items(&mut self, rollup_store: &StoreRollup) -> Result<(), MonitorError> { + async fn refresh_items( + &mut self, + rollup_store: &StoreRollup, + fork: Fork, + ) -> Result<(), MonitorError> { if self.items.is_empty() { return Ok(()); } @@ -83,7 +96,7 @@ impl BatchesTable { } else { let batch_number = batch.number; let new_batch = rollup_store - .get_batch(batch_number) + .get_batch(batch_number, fork) .await .map_err(|e| MonitorError::GetBatchByNumber(batch_number, e))? .ok_or(MonitorError::BatchNotFound(batch_number))?; @@ -104,6 +117,7 @@ impl BatchesTable { on_chain_proposer_address: Address, eth_client: &EthClient, rollup_store: &StoreRollup, + osaka_activation_time: Option, ) -> Result, MonitorError> { let last_l2_batch_number = get_last_committed_batch(eth_client, on_chain_proposer_address) .await @@ -116,9 +130,17 @@ impl BatchesTable { .map_err(|_| MonitorError::BatchWindow)?, ), ); + let fork = is_osaka_activated_on_l1(eth_client, osaka_activation_time) + .await + .map_err(MonitorError::EthClientError)?; - let new_batches = - Self::get_batches(last_l2_batch_fetched, last_l2_batch_number, rollup_store).await?; + let new_batches = Self::get_batches( + last_l2_batch_fetched, + last_l2_batch_number, + rollup_store, + fork, + ) + .await?; Ok(Self::process_batches(new_batches)) } @@ -127,12 +149,13 @@ impl BatchesTable { from: &mut u64, to: u64, rollup_store: &StoreRollup, + fork: Fork, ) -> Result, MonitorError> { let mut new_batches = Vec::new(); for batch_number in *from + 1..=to { let batch = rollup_store - .get_batch(batch_number) + .get_batch(batch_number, fork) .await .map_err(|e| MonitorError::GetBatchByNumber(batch_number, e))? .ok_or(MonitorError::BatchNotFound(batch_number))?; diff --git a/crates/l2/networking/rpc/l2/batch.rs b/crates/l2/networking/rpc/l2/batch.rs index 5a1ae0add9..22fd533dd0 100644 --- a/crates/l2/networking/rpc/l2/batch.rs +++ b/crates/l2/networking/rpc/l2/batch.rs @@ -92,7 +92,13 @@ impl RpcHandler for GetBatchByBatchNumberRequest { async fn handle(&self, context: RpcApiContext) -> Result { debug!("Requested batch with number: {}", self.batch_number); - let Some(batch) = context.rollup_store.get_batch(self.batch_number).await? else { + // TODO: review this choice of fork + let fork = context.l1_ctx.blockchain.current_fork().await?; + let Some(batch) = context + .rollup_store + .get_batch(self.batch_number, fork) + .await? + else { return Ok(Value::Null); }; let rpc_batch = RpcBatch::build(batch, self.block_hashes, &context.l1_ctx.storage).await?; diff --git a/crates/l2/prover/src/guest_program/src/execution.rs b/crates/l2/prover/src/guest_program/src/execution.rs index a9e9aa8319..3ec9cd5100 100644 --- a/crates/l2/prover/src/guest_program/src/execution.rs +++ b/crates/l2/prover/src/guest_program/src/execution.rs @@ -155,7 +155,7 @@ pub fn stateless_validation_l2( execution_witness: ExecutionWitness, elasticity_multiplier: u64, blob_commitment: Commitment, - blob_proof: Proof, + blob_proof: Vec, chain_id: u64, ) -> Result { let initial_db = execution_witness.clone(); @@ -183,7 +183,7 @@ pub fn stateless_validation_l2( )?; // TODO: this could be replaced with something like a ProverConfig in the future. - let validium = (blob_commitment, blob_proof) == ([0; 48], [0; 48]); + let validium = (blob_commitment, &blob_proof) == ([0; 48], &vec![[0; 48]]); // Check state diffs are valid let blob_versioned_hash = if !validium { @@ -435,14 +435,22 @@ fn compute_l1messages_and_privileged_transactions_digests( fn verify_blob( state_diff: StateDiff, commitment: Commitment, - proof: Proof, + proof: Vec, ) -> Result { - use ethrex_crypto::kzg::verify_blob_kzg_proof; + use ethrex_crypto::kzg::{verify_blob_kzg_proof, verify_cell_kzg_proof_batch}; let encoded_state_diff = state_diff.encode()?; let blob_data = blob_from_bytes(encoded_state_diff)?; - if !verify_blob_kzg_proof(blob_data, commitment, proof)? { + let proof_is_valid = if proof.len() == 1 { + // Prior to Osaka type proof + verify_blob_kzg_proof(blob_data, commitment, proof[0])? + } else { + // Osaka type proof + verify_cell_kzg_proof_batch(&[blob_data], &[commitment], &proof)? + }; + + if !proof_is_valid { return Err(StatelessExecutionError::InvalidBlobProof); } diff --git a/crates/l2/prover/src/guest_program/src/input.rs b/crates/l2/prover/src/guest_program/src/input.rs index 79f5b485b0..8d12f397c3 100644 --- a/crates/l2/prover/src/guest_program/src/input.rs +++ b/crates/l2/prover/src/guest_program/src/input.rs @@ -22,8 +22,8 @@ pub struct ProgramInput { pub blob_commitment: blobs_bundle::Commitment, #[cfg(feature = "l2")] /// KZG opening for a challenge over the blob commitment - #[serde_as(as = "[_; 48]")] - pub blob_proof: blobs_bundle::Proof, + #[serde_as(as = "Vec<[_; 48]>")] + pub blob_proof: Vec, } impl Default for ProgramInput { @@ -35,7 +35,7 @@ impl Default for ProgramInput { #[cfg(feature = "l2")] blob_commitment: [0; 48], #[cfg(feature = "l2")] - blob_proof: [0; 48], + blob_proof: vec![[0u8; 48]], } } } diff --git a/crates/l2/sdk/src/sdk.rs b/crates/l2/sdk/src/sdk.rs index 5625b92243..1aa8b8fc59 100644 --- a/crates/l2/sdk/src/sdk.rs +++ b/crates/l2/sdk/src/sdk.rs @@ -1,6 +1,7 @@ use bytes::Bytes; use calldata::encode_calldata; use ethereum_types::{H160, H256, U256}; +use ethrex_common::types::Fork; use ethrex_common::utils::keccak; use ethrex_common::{ Address, @@ -669,7 +670,8 @@ pub async fn send_generic_transaction( signed_tx.encode(&mut encoded_tx); } TxType::EIP4844 => { - let mut tx: WrappedEIP4844Transaction = generic_tx.try_into()?; + let fork = is_osaka_activated_on_l1(client, None).await?; // TODO: Change this + let mut tx = WrappedEIP4844Transaction::from_generic_tx(generic_tx, fork)?; tx.tx .sign_inplace(signer) .await @@ -980,6 +982,26 @@ pub async fn get_pending_privileged_transactions( from_hex_string_to_h256_array(&response) } +// TODO: This is a work around for now, issue: https://github.com/lambdaclass/ethrex/issues/4828 +pub async fn is_osaka_activated_on_l1( + client: &EthClient, + activation_time: Option, +) -> Result { + let Some(osaka_activation_time) = activation_time else { + return Ok(Fork::Osaka); + }; + let current_timestamp = client + .get_block_by_number(BlockIdentifier::Tag(BlockTag::Latest), false) + .await? + .header + .timestamp; + if current_timestamp < osaka_activation_time { + Ok(Fork::Prague) + } else { + Ok(Fork::Osaka) + } +} + async fn _generic_call( client: &EthClient, selector: &[u8], diff --git a/crates/l2/sequencer/configs.rs b/crates/l2/sequencer/configs.rs index a16890eaf0..3fecd4d89e 100644 --- a/crates/l2/sequencer/configs.rs +++ b/crates/l2/sequencer/configs.rs @@ -36,6 +36,7 @@ pub struct CommitterConfig { pub arbitrary_base_blob_gas_price: u64, pub validium: bool, pub signer: Signer, + pub osaka_activation_time: Option, } #[derive(Clone, Debug)] @@ -66,6 +67,7 @@ pub struct ProofCoordinatorConfig { pub validium: bool, pub tdx_private_key: Option, pub qpl_tool_path: Option, + pub osaka_activation_time: Option, } #[derive(Clone, Debug)] @@ -85,6 +87,7 @@ pub struct StateUpdaterConfig { pub struct BlockFetcherConfig { pub fetch_interval_ms: u64, pub fetch_block_step: u64, + pub osaka_activation_time: Option, } #[derive(Clone, Debug)] @@ -104,6 +107,8 @@ pub struct MonitorConfig { pub tick_rate: u64, /// height in lines of the batch widget pub batch_widget_height: Option, + /// Timestamp at which the Osaka fork activates. If not set, it will assume Osaka is already active. + pub osaka_activation_time: Option, } #[derive(Clone, Debug)] diff --git a/crates/l2/sequencer/l1_committer.rs b/crates/l2/sequencer/l1_committer.rs index cb7d7380ef..845c7adbf8 100644 --- a/crates/l2/sequencer/l1_committer.rs +++ b/crates/l2/sequencer/l1_committer.rs @@ -12,7 +12,7 @@ use ethrex_blockchain::{Blockchain, vm::StoreVmDatabase}; use ethrex_common::{ Address, H256, U256, types::{ - AccountUpdate, BLOB_BASE_FEE_UPDATE_FRACTION, BlobsBundle, Block, BlockNumber, + AccountUpdate, BLOB_BASE_FEE_UPDATE_FRACTION, BlobsBundle, Block, BlockNumber, Fork, MIN_BASE_FEE_PER_BLOB_GAS, TxType, batch::Batch, blobs_bundle, fake_exponential_checked, }, }; @@ -29,7 +29,7 @@ use ethrex_l2_common::{ use ethrex_l2_rpc::signer::{Signer, SignerHealth}; use ethrex_l2_sdk::{ build_generic_tx, calldata::encode_calldata, get_last_committed_batch, - send_tx_bump_gas_exponential_backoff, + is_osaka_activated_on_l1, send_tx_bump_gas_exponential_backoff, }; #[cfg(feature = "metrics")] use ethrex_metrics::l2::metrics::{METRICS, MetricsBlockType}; @@ -103,6 +103,7 @@ pub struct L1Committer { last_committed_batch: u64, /// Cancellation token for the next inbound InMessage::Commit cancellation_token: Option, + osaka_activation_time: Option, } #[derive(Clone, Serialize)] @@ -163,6 +164,7 @@ impl L1Committer { last_committed_batch_timestamp: 0, last_committed_batch, cancellation_token: None, + osaka_activation_time: committer_config.osaka_activation_time, }) } @@ -206,7 +208,10 @@ impl L1Committer { get_last_committed_batch(&self.eth_client, self.on_chain_proposer_address).await?; let batch_to_commit = last_committed_batch_number + 1; - let batch = match self.rollup_store.get_batch(batch_to_commit).await? { + let fork = is_osaka_activated_on_l1(&self.eth_client, self.osaka_activation_time) + .await + .map_err(CommitterError::EthClientError)?; + let batch = match self.rollup_store.get_batch(batch_to_commit, fork).await? { Some(batch) => batch, None => { let last_committed_blocks = self @@ -448,7 +453,10 @@ impl L1Committer { &acc_privileged_txs, acc_account_updates.clone().into_values().collect(), )?; - generate_blobs_bundle(&state_diff) + let fork = is_osaka_activated_on_l1(&self.eth_client, self.osaka_activation_time) + .await + .map_err(CommitterError::EthClientError)?; + generate_blobs_bundle(&state_diff, fork) } else { Ok((BlobsBundle::default(), 0_usize)) }; @@ -778,6 +786,7 @@ impl GenServer for L1Committer { /// Generate the blob bundle necessary for the EIP-4844 transaction. pub fn generate_blobs_bundle( state_diff: &StateDiff, + fork: Fork, ) -> Result<(BlobsBundle, usize), CommitterError> { let blob_data = state_diff.encode().map_err(CommitterError::from)?; @@ -786,7 +795,7 @@ pub fn generate_blobs_bundle( let blob = blobs_bundle::blob_from_bytes(blob_data).map_err(CommitterError::from)?; Ok(( - BlobsBundle::create_from_blobs(&vec![blob]).map_err(CommitterError::from)?, + BlobsBundle::create_from_blobs(&vec![blob], fork).map_err(CommitterError::from)?, blob_size, )) } diff --git a/crates/l2/sequencer/proof_coordinator.rs b/crates/l2/sequencer/proof_coordinator.rs index 8eb0073106..f1aa2f78a8 100644 --- a/crates/l2/sequencer/proof_coordinator.rs +++ b/crates/l2/sequencer/proof_coordinator.rs @@ -6,13 +6,14 @@ use crate::{ }; use bytes::Bytes; use ethrex_blockchain::Blockchain; -use ethrex_common::types::BlobsBundle; use ethrex_common::types::block_execution_witness::ExecutionWitness; +use ethrex_common::types::{BlobsBundle, CELLS_PER_EXT_BLOB, Fork}; use ethrex_common::{ Address, types::{Block, blobs_bundle}, }; use ethrex_l2_common::prover::{BatchProof, ProverType}; +use ethrex_l2_sdk::is_osaka_activated_on_l1; use ethrex_metrics::metrics; use ethrex_rpc::clients::eth::EthClient; use ethrex_storage::Store; @@ -47,8 +48,8 @@ pub struct ProverInputData { #[serde_as(as = "[_; 48]")] pub blob_commitment: blobs_bundle::Commitment, #[cfg(feature = "l2")] - #[serde_as(as = "[_; 48]")] - pub blob_proof: blobs_bundle::Proof, + #[serde_as(as = "Vec<[_; 48]>")] + pub blob_proof: Vec, } /// Enum for the ProverServer <--> ProverClient Communication Protocol. @@ -184,6 +185,7 @@ pub struct ProofCoordinator { #[cfg(feature = "metrics")] request_timestamp: Arc>>, qpl_tool_path: Option, + osaka_activation_time: Option, } impl ProofCoordinator { @@ -234,6 +236,7 @@ impl ProofCoordinator { #[cfg(feature = "metrics")] request_timestamp: Arc::new(Mutex::new(HashMap::new())), qpl_tool_path: config.qpl_tool_path.clone(), + osaka_activation_time: config.osaka_activation_time, }) } @@ -481,22 +484,37 @@ impl ProofCoordinator { // Get blobs bundle cached by the L1 Committer (blob, commitment, proof) let (blob_commitment, blob_proof) = if self.validium { - ([0; 48], [0; 48]) + ([0; 48], vec![[0; 48]]) } else { let blob = self .rollup_store .get_blobs_by_batch(batch_number) .await? .ok_or(ProofCoordinatorError::MissingBlob(batch_number))?; + let fork = is_osaka_activated_on_l1(&self.eth_client, self.osaka_activation_time) + .await + .map_err(ProofCoordinatorError::EthClientError)?; let BlobsBundle { mut commitments, mut proofs, .. - } = BlobsBundle::create_from_blobs(&blob)?; - match (commitments.pop(), proofs.pop()) { - (Some(commitment), Some(proof)) => (commitment, proof), - _ => return Err(ProofCoordinatorError::MissingBlob(batch_number)), + } = BlobsBundle::create_from_blobs(&blob, fork)?; + let proof_count = if fork < Fork::Osaka { + 1 + } else { + CELLS_PER_EXT_BLOB + }; + let commitment = commitments + .pop() + .ok_or_else(|| ProofCoordinatorError::MissingBlob(batch_number))?; + + if proofs.len() < proof_count { + return Err(ProofCoordinatorError::MissingBlob(batch_number)); } + + let proof = proofs.split_off(proofs.len() - proof_count); + + (commitment, proof) }; debug!("Created prover input for batch {batch_number}"); diff --git a/crates/l2/storage/src/store.rs b/crates/l2/storage/src/store.rs index 62df78a2a9..873b5ee2c4 100644 --- a/crates/l2/storage/src/store.rs +++ b/crates/l2/storage/src/store.rs @@ -7,7 +7,7 @@ use crate::store_db::in_memory::Store as InMemoryStore; use crate::store_db::sql::SQLStore; use ethrex_common::{ H256, - types::{AccountUpdate, Blob, BlobsBundle, BlockNumber, batch::Batch}, + types::{AccountUpdate, Blob, BlobsBundle, BlockNumber, Fork, batch::Batch}, }; use ethrex_l2_common::prover::{BatchProof, ProverType}; use tracing::info; @@ -153,7 +153,11 @@ impl Store { self.engine.get_last_batch_number().await } - pub async fn get_batch(&self, batch_number: u64) -> Result, RollupStoreError> { + pub async fn get_batch( + &self, + batch_number: u64, + fork: Fork, + ) -> Result, RollupStoreError> { let Some(blocks) = self.get_block_numbers_by_batch(batch_number).await? else { return Ok(None); }; @@ -181,7 +185,8 @@ impl Store { &self .get_blobs_by_batch(batch_number) .await? - .unwrap_or_default() + .unwrap_or_default(), + fork, ).map_err(|e| { RollupStoreError::Custom(format!("Failed to create blobs bundle from blob while getting batch from database: {e}. This is a bug")) })?; diff --git a/crates/l2/tee/quote-gen/Cargo.lock b/crates/l2/tee/quote-gen/Cargo.lock index 508fb285b1..011602868a 100644 --- a/crates/l2/tee/quote-gen/Cargo.lock +++ b/crates/l2/tee/quote-gen/Cargo.lock @@ -2071,6 +2071,7 @@ name = "ethrex-blockchain" version = "0.1.0" dependencies = [ "bytes", + "cfg-if 1.0.3", "ethrex-common", "ethrex-metrics", "ethrex-rlp", @@ -2089,15 +2090,14 @@ name = "ethrex-common" version = "0.1.0" dependencies = [ "bytes", + "c-kzg", "crc32fast", "ethereum-types 0.15.1", - "ethrex-crypto", "ethrex-rlp", "ethrex-trie", "hex", "kzg-rs", "lazy_static", - "libc", "once_cell", "rayon", "rkyv", @@ -2126,11 +2126,6 @@ dependencies = [ [[package]] name = "ethrex-crypto" version = "0.1.0" -dependencies = [ - "c-kzg", - "kzg-rs", - "thiserror 2.0.16", -] [[package]] name = "ethrex-dev" @@ -2159,6 +2154,7 @@ dependencies = [ "axum", "bincode", "bytes", + "cfg-if 1.0.3", "chrono", "clap", "color-eyre", @@ -2356,6 +2352,7 @@ dependencies = [ "axum", "axum-extra", "bytes", + "cfg-if 1.0.3", "envy", "ethereum-types 0.15.1", "ethrex-blockchain", @@ -2488,6 +2485,7 @@ version = "0.1.0" dependencies = [ "bincode", "bytes", + "cfg-if 1.0.3", "derive_more 1.0.0", "dyn-clone", "ethereum-types 0.15.1", @@ -2866,7 +2864,6 @@ dependencies = [ "bytes", "ethrex-blockchain", "ethrex-common", - "ethrex-crypto", "ethrex-l2-common", "ethrex-rlp", "ethrex-storage", diff --git a/crates/networking/p2p/rlpx/l2/l2_connection.rs b/crates/networking/p2p/rlpx/l2/l2_connection.rs index c8d975869d..2f2989a1b9 100644 --- a/crates/networking/p2p/rlpx/l2/l2_connection.rs +++ b/crates/networking/p2p/rlpx/l2/l2_connection.rs @@ -427,7 +427,12 @@ pub(crate) async fn send_sealed_batch( { return Ok(()); } - let Some(batch) = l2_state.store_rollup.get_batch(next_batch_to_send).await? else { + let fork = established.blockchain.current_fork().await?; + let Some(batch) = l2_state + .store_rollup + .get_batch(next_batch_to_send, fork) + .await? + else { return Ok(()); }; match l2_state diff --git a/crates/networking/rpc/clients/eth/errors.rs b/crates/networking/rpc/clients/eth/errors.rs index f51ea3eb0b..8345dea0a4 100644 --- a/crates/networking/rpc/clients/eth/errors.rs +++ b/crates/networking/rpc/clients/eth/errors.rs @@ -45,8 +45,10 @@ pub enum EthClientError { GetWitnessError(#[from] GetWitnessError), #[error("eth_maxPriorityFeePerGas request error: {0}")] GetMaxPriorityFeeError(#[from] GetMaxPriorityFeeError), + #[error("eth_config request error: {0}")] + GetEthConfigError(#[from] GetEthConfigError), #[error("Unreachable nonce")] - UnrecheableNonce, + UnreachableNonce, #[error("Error: {0}")] Custom(String), #[error("Failed to encode calldata: {0}")] @@ -302,3 +304,11 @@ pub enum GetBatchByNumberError { #[error("{0}")] RPCError(String), } + +#[derive(Debug, thiserror::Error)] +pub enum GetEthConfigError { + #[error("{0}")] + SerdeJSONError(#[from] serde_json::Error), + #[error("{0}")] + RPCError(String), +} diff --git a/crates/networking/rpc/clients/eth/mod.rs b/crates/networking/rpc/clients/eth/mod.rs index d5595ce11d..493cb17342 100644 --- a/crates/networking/rpc/clients/eth/mod.rs +++ b/crates/networking/rpc/clients/eth/mod.rs @@ -1,8 +1,11 @@ use std::{collections::BTreeMap, fmt}; use crate::{ - clients::eth::errors::{CallError, GetPeerCountError, GetWitnessError, TxPoolContentError}, + clients::eth::errors::{ + CallError, GetEthConfigError, GetPeerCountError, GetWitnessError, TxPoolContentError, + }, debug::execution_witness::RpcExecutionWitness, + eth::client::EthConfigResponse, mempool::MempoolContent, types::{ block::RpcBlock, @@ -579,6 +582,19 @@ impl EthClient { } } + pub async fn get_eth_config(&self) -> Result { + let request = RpcRequest::new("eth_config", None); + + match self.send_request(request).await? { + RpcResponse::Success(result) => serde_json::from_value(result.result) + .map_err(GetEthConfigError::SerdeJSONError) + .map_err(EthClientError::from), + RpcResponse::Error(error_response) => { + Err(GetEthConfigError::RPCError(error_response.error.message).into()) + } + } + } + pub async fn get_code( &self, address: Address, diff --git a/crates/networking/rpc/eth/client.rs b/crates/networking/rpc/eth/client.rs index 4472ceb856..fdf9e98ed1 100644 --- a/crates/networking/rpc/eth/client.rs +++ b/crates/networking/rpc/eth/client.rs @@ -87,7 +87,7 @@ struct EthConfigObject { #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -struct EthConfigResponse { +pub struct EthConfigResponse { current: EthConfigObject, next: Option, last: Option, diff --git a/tooling/genesis/src/genesis.rs b/tooling/genesis/src/genesis.rs index 50c2788e9e..c81fb6ca26 100644 --- a/tooling/genesis/src/genesis.rs +++ b/tooling/genesis/src/genesis.rs @@ -27,6 +27,7 @@ fn sort_config(genesis_map: &mut Map) -> Result