Skip to content
Merged
Show file tree
Hide file tree
Changes from 61 commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
7375575
first approach
tomip01 Oct 7, 2025
34713c8
Merge branch 'main' into fusaka_blobs
tomip01 Oct 7, 2025
5918d8f
compute cell proofs
tomip01 Oct 8, 2025
4a86ee2
use version 1
tomip01 Oct 8, 2025
934e407
use cell proofs
tomip01 Oct 8, 2025
9775650
Merge branch 'main' into fusaka_blobs
tomip01 Oct 8, 2025
44ceffa
make eth client expose eth_config
tomip01 Oct 9, 2025
c46220a
default to osaka if rpc responds correctly
tomip01 Oct 9, 2025
9c5d320
remove comment
tomip01 Oct 9, 2025
d4b2b11
add issue link and rever genesis changes
tomip01 Oct 9, 2025
c82d7c8
revert makefile changes
tomip01 Oct 9, 2025
2d05121
Merge branch 'main' into fusaka_blobs
tomip01 Oct 9, 2025
a82fe68
first approach to maintain compatibility
tomip01 Oct 13, 2025
184aab6
handle osaka proof
tomip01 Oct 13, 2025
57a6150
Merge branch 'main' into fusaka_blobs
tomip01 Oct 13, 2025
1fc0c28
fix compile c_kzg
tomip01 Oct 13, 2025
5145ba5
use flag
tomip01 Oct 13, 2025
41ed5e7
use another crate for verification
tomip01 Oct 14, 2025
b2599af
Merge branch 'main' into fusaka_blobs
tomip01 Oct 14, 2025
1a78153
Revert "use another crate for verification"
tomip01 Oct 14, 2025
984d1fd
Merge branch 'main' into fusaka_blobs
tomip01 Oct 15, 2025
e17483e
Change precompute to 0
gianbelinche Oct 13, 2025
ca2c4d7
Add risc0 feature to kzg
gianbelinche Oct 14, 2025
2c5b849
include c-kzg risc0
tomip01 Oct 15, 2025
36be0a2
fix lint
tomip01 Oct 15, 2025
977ab1f
Merge branch 'fix_risc0' into fusaka_blobs
tomip01 Oct 16, 2025
963fcf4
Merge branch 'main' into fusaka_blobs
tomip01 Oct 16, 2025
7270ab6
use correct method?
tomip01 Oct 16, 2025
2883247
Merge branch 'main' into fusaka_blobs
tomip01 Oct 17, 2025
230e0e6
Merge branch 'main' into fusaka_blobs
tomip01 Oct 21, 2025
4b1a4e3
remove comment
tomip01 Oct 21, 2025
0ff2504
Merge branch 'main' into fusaka_blobs
tomip01 Oct 24, 2025
9c93781
fix merge main
tomip01 Oct 24, 2025
09a1b6c
suggestion: check for the proof count be exactly equal
tomip01 Oct 24, 2025
8777d9f
suggestion: better readability in blob version
tomip01 Oct 24, 2025
456cc7c
compute cells only
xqft Oct 24, 2025
665bb46
Fix kzg.rs
Oct 24, 2025
849dd1b
use precompute constant
tomip01 Oct 24, 2025
79aa56c
Merge branch 'fusaka_blobs' of github.com:lambdaclass/ethrex into fus…
tomip01 Oct 24, 2025
894e5eb
Merge branch 'main' into fusaka_blobs
tomip01 Oct 27, 2025
ad842b7
suggestion: rename l1 fork function to better one
tomip01 Oct 27, 2025
dc58cbb
add unit test for blob and fork
tomip01 Oct 27, 2025
d1f58d7
fix lint
tomip01 Oct 27, 2025
6207fab
fix commit failure
tomip01 Oct 27, 2025
1f3d221
fix l2 dev job
tomip01 Oct 27, 2025
02ad4db
move osaka activation time to eth config
tomip01 Oct 28, 2025
d44e347
Merge branch 'main' into fusaka_blobs
tomip01 Oct 28, 2025
965d484
add var and comment on timestamp
tomip01 Oct 28, 2025
0a89dca
blob proof -> blob proofs
tomip01 Oct 28, 2025
04e0646
fork -> l1_fork
tomip01 Oct 28, 2025
a232960
use wrapper version inside generic tx
tomip01 Oct 28, 2025
68c8644
fix field name
tomip01 Oct 28, 2025
3d2903a
fix name again
tomip01 Oct 28, 2025
e7189fa
Merge branch 'main' into fusaka_blobs
tomip01 Oct 28, 2025
dfcc52e
revert no-monitor
tomip01 Oct 28, 2025
7383d23
fix(l2): verify a single blob proof in the guest program (#5081)
ilitteri Oct 28, 2025
4195a6c
Update cmd/ethrex/l2/options.rs
tomip01 Oct 28, 2025
4d72085
Merge branch 'main' into fusaka_blobs
tomip01 Oct 28, 2025
d39ef2e
Merge branch 'main' into fusaka_blobs
ManuelBilbao Oct 30, 2025
2ffc530
Merge branch 'main' into fusaka_blobs
ilitteri Oct 31, 2025
acf0df4
Merge branch 'main' into fusaka_blobs
ManuelBilbao Nov 1, 2025
73723e9
Update crates/l2/Makefile
ManuelBilbao Nov 1, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions cmd/ethrex/l2/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ impl TryFrom<SequencerOptions> for SequencerConfig {
maximum_allowed_max_fee_per_blob_gas: opts
.eth_opts
.maximum_allowed_max_fee_per_blob_gas,
osaka_activation_time: opts.eth_opts.osaka_activation_time,
},
l1_watcher: L1WatcherConfig {
bridge_address: opts
Expand Down Expand Up @@ -339,6 +340,13 @@ pub struct EthOptions {
help_heading = "Eth options"
)]
pub max_retry_delay: u64,
#[clap(
long,
value_name = "UINT64",
env = "ETHREX_OSAKA_ACTIVATION_TIME",
help = "Block timestamp at which the Osaka fork is activated on L1. If not set, it will assume Osaka is already active."
)]
pub osaka_activation_time: Option<u64>,
}

impl Default for EthOptions {
Expand All @@ -353,6 +361,7 @@ impl Default for EthOptions {
backoff_factor: BACKOFF_FACTOR,
min_retry_delay: MIN_RETRY_DELAY,
max_retry_delay: MAX_RETRY_DELAY,
osaka_activation_time: None,
}
}
}
Expand Down
36 changes: 30 additions & 6 deletions crates/common/crypto/kzg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,11 @@ pub fn verify_cell_kzg_proof_batch(
let c_kzg_settings = c_kzg::ethereum_kzg_settings(KZG_PRECOMPUTE);
let mut cells = Vec::new();
for blob in blobs {
cells.extend(c_kzg_settings.compute_cells(&(*blob).into())?.into_iter());
let blob: c_kzg::Blob = (*blob).into();
let cells_blob = c_kzg_settings
.compute_cells(&blob)
.map_err(KzgError::CKzg)?;
cells.extend(*cells_blob);
}
c_kzg::KzgSettings::verify_cell_kzg_proof_batch(
c_kzg_settings,
Expand Down Expand Up @@ -180,15 +184,35 @@ pub fn verify_kzg_proof(
pub fn blob_to_kzg_commitment_and_proof(blob: &Blob) -> Result<(Commitment, Proof), KzgError> {
let blob: c_kzg::Blob = (*blob).into();

let commitment = c_kzg::KzgSettings::blob_to_kzg_commitment(
c_kzg::ethereum_kzg_settings(KZG_PRECOMPUTE),
&blob,
)?;
let commitment_bytes = commitment.to_bytes();
let c_kzg_settings = c_kzg::ethereum_kzg_settings(KZG_PRECOMPUTE);

let commitment = c_kzg::KzgSettings::blob_to_kzg_commitment(c_kzg_settings, &blob)?;

let commitment_bytes = commitment.to_bytes();
let proof = c_kzg_settings.compute_blob_kzg_proof(&blob, &commitment_bytes)?;

let proof_bytes = proof.to_bytes();

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<Proof>), KzgError> {
let c_kzg_settings = c_kzg::ethereum_kzg_settings(KZG_PRECOMPUTE);

let blob: c_kzg::Blob = (*blob).into();

let commitment = c_kzg::KzgSettings::blob_to_kzg_commitment(c_kzg_settings, &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()))
}
107 changes: 99 additions & 8 deletions crates/common/types/blobs_bundle.rs
Original file line number Diff line number Diff line change
@@ -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};

Expand Down Expand Up @@ -80,23 +82,34 @@ 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<Blob>) -> Result<Self, BlobsBundleError> {
use ethrex_crypto::kzg::blob_to_kzg_commitment_and_proof;
pub fn create_from_blobs(
blobs: &Vec<Blob>,
wrapper_version: Option<u8>,
) -> Result<Self, BlobsBundleError> {
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 wrapper_version.unwrap_or(0) == 0 {
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: wrapper_version.unwrap_or(0),
})
}

Expand Down Expand Up @@ -127,6 +140,10 @@ impl BlobsBundle {
return Err(BlobsBundleError::BlobBundleEmptyError);
}

if self.version == 0 && fork >= Fork::Osaka || self.version != 0 && fork < Fork::Osaka {
return Err(BlobsBundleError::InvalidBlobVersionForFork);
}

// Check if the blob versioned hashes and blobs bundle content length mismatch
if blob_count != self.commitments.len()
|| (self.version == 0 && blob_count != self.proofs.len())
Expand Down Expand Up @@ -231,6 +248,8 @@ pub enum BlobsBundleError {
BlobToCommitmentAndProofError,
#[error("Max blobs per block exceeded")]
MaxBlobsExceeded,
#[error("Invalid blob version for the current fork")]
InvalidBlobVersionForFork,
#[cfg(feature = "c-kzg")]
#[error("KZG related error: {0}")]
Kzg(#[from] ethrex_crypto::kzg::KzgError),
Expand Down Expand Up @@ -259,7 +278,7 @@ mod tests {
})
.collect();

let blobs_bundle = crate::types::BlobsBundle::create_from_blobs(&blobs)
let blobs_bundle = crate::types::BlobsBundle::create_from_blobs(&blobs, None)
.expect("Failed to create blobs bundle");

let blob_versioned_hashes = blobs_bundle.generate_versioned_hashes();
Expand All @@ -284,6 +303,78 @@ mod tests {
));
}

#[test]
#[cfg(feature = "c-kzg")]
fn transaction_with_valid_blobs_should_pass_on_osaka() {
let blobs = vec!["Hello, world!".as_bytes(), "Goodbye, world!".as_bytes()]
.into_iter()
.map(|data| {
crate::types::blobs_bundle::blob_from_bytes(data.into())
.expect("Failed to create blob")
})
.collect();

let blobs_bundle = crate::types::BlobsBundle::create_from_blobs(&blobs, Some(1))
.expect("Failed to create blobs bundle");

let blob_versioned_hashes = blobs_bundle.generate_versioned_hashes();

let tx = crate::types::transaction::EIP4844Transaction {
nonce: 3,
max_priority_fee_per_gas: 0,
max_fee_per_gas: 0,
max_fee_per_blob_gas: 0.into(),
gas: 15_000_000,
to: crate::Address::from_low_u64_be(1), // Normal tx
value: crate::U256::zero(), // Value zero
data: crate::Bytes::default(), // No data
access_list: Default::default(), // No access list
blob_versioned_hashes,
..Default::default()
};

assert!(matches!(
blobs_bundle.validate(&tx, crate::types::Fork::Osaka),
Ok(())
));
}

#[test]
#[cfg(feature = "c-kzg")]
fn transaction_with_invalid_fork_should_fail() {
let blobs = vec!["Hello, world!".as_bytes(), "Goodbye, world!".as_bytes()]
.into_iter()
.map(|data| {
crate::types::blobs_bundle::blob_from_bytes(data.into())
.expect("Failed to create blob")
})
.collect();

let blobs_bundle = crate::types::BlobsBundle::create_from_blobs(&blobs, Some(1))
.expect("Failed to create blobs bundle");

let blob_versioned_hashes = blobs_bundle.generate_versioned_hashes();

let tx = crate::types::transaction::EIP4844Transaction {
nonce: 3,
max_priority_fee_per_gas: 0,
max_fee_per_gas: 0,
max_fee_per_blob_gas: 0.into(),
gas: 15_000_000,
to: crate::Address::from_low_u64_be(1), // Normal tx
value: crate::U256::zero(), // Value zero
data: crate::Bytes::default(), // No data
access_list: Default::default(), // No access list
blob_versioned_hashes,
..Default::default()
};

assert!(!matches!(
blobs_bundle.validate(&tx, crate::types::Fork::Prague),
Ok(())
));
}

#[test]
#[cfg(feature = "c-kzg")]
fn transaction_with_invalid_proofs_should_fail() {
Expand Down Expand Up @@ -396,7 +487,7 @@ mod tests {
let blobs =
std::iter::repeat_n(blob, super::MAX_BLOB_COUNT_ELECTRA + 1).collect::<Vec<_>>();

let blobs_bundle = crate::types::BlobsBundle::create_from_blobs(&blobs)
let blobs_bundle = crate::types::BlobsBundle::create_from_blobs(&blobs, None)
.expect("Failed to create blobs bundle");

let blob_versioned_hashes = blobs_bundle.generate_versioned_hashes();
Expand Down
14 changes: 12 additions & 2 deletions crates/common/types/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2167,6 +2167,7 @@ mod serde_impl {
pub authorization_list: Option<Vec<AuthorizationTupleEntry>>,
#[serde(default)]
pub blob_versioned_hashes: Vec<H256>,
pub wrapper_version: Option<u8>,
#[serde(default, with = "crate::serde_utils::bytes::vec")]
pub blobs: Vec<Bytes>,
#[serde(default, with = "crate::serde_utils::u64::hex_str_opt")]
Expand Down Expand Up @@ -2232,6 +2233,7 @@ mod serde_impl {
authorization_list: None,
blob_versioned_hashes: vec![],
blobs: vec![],
wrapper_version: None,
chain_id: Some(value.chain_id),
from: Address::default(),
}
Expand Down Expand Up @@ -2286,6 +2288,7 @@ mod serde_impl {
authorization_list: None,
blob_versioned_hashes: value.blob_versioned_hashes,
blobs: vec![],
wrapper_version: None,
chain_id: Some(value.chain_id),
from: Address::default(),
}
Expand All @@ -2308,10 +2311,11 @@ mod serde_impl {
})
.collect();

let wrapper_version = value.wrapper_version;
Ok(Self {
tx: value.try_into()?,
wrapper_version: None,
blobs_bundle: BlobsBundle::create_from_blobs(&blobs)?,
wrapper_version,
blobs_bundle: BlobsBundle::create_from_blobs(&blobs, wrapper_version)?,
})
}
}
Expand Down Expand Up @@ -2374,6 +2378,7 @@ mod serde_impl {
),
blob_versioned_hashes: vec![],
blobs: vec![],
wrapper_version: None,
chain_id: Some(value.chain_id),
from: Address::default(),
}
Expand Down Expand Up @@ -2401,6 +2406,7 @@ mod serde_impl {
authorization_list: None,
blob_versioned_hashes: vec![],
blobs: vec![],
wrapper_version: None,
chain_id: Some(value.chain_id),
from: value.from,
}
Expand Down Expand Up @@ -2451,6 +2457,7 @@ mod serde_impl {
authorization_list: None,
blob_versioned_hashes: vec![],
blobs: vec![],
wrapper_version: None,
chain_id: None,
input: value.data,
}
Expand Down Expand Up @@ -2481,6 +2488,7 @@ mod serde_impl {
authorization_list: None,
blob_versioned_hashes: vec![],
blobs: vec![],
wrapper_version: None,
chain_id: Some(value.chain_id),
input: value.data,
}
Expand Down Expand Up @@ -2831,6 +2839,7 @@ mod tests {
}],
blob_versioned_hashes: Default::default(),
blobs: Default::default(),
wrapper_version: None,
chain_id: Default::default(),
authorization_list: None,
};
Expand Down Expand Up @@ -2884,6 +2893,7 @@ mod tests {
}],
blob_versioned_hashes: Default::default(),
blobs: Default::default(),
wrapper_version: None,
chain_id: Default::default(),
authorization_list: None,
};
Expand Down
8 changes: 6 additions & 2 deletions crates/l2/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@ L1_AUTH_PORT=8551
L1_RPC_ADDRESS=0.0.0.0
L2_RPC_ADDRESS=0.0.0.0
PROOF_COORDINATOR_ADDRESS?=127.0.0.1
ETHREX_BLOCK_PRODUCER_OPERATOR_FEE_PER_GAS?=1000000000 \
ETHREX_BLOCK_PRODUCER_OPERATOR_FEE_PER_GAS?=1000000000
# Timestamp for Osaka activation in the hoodi. Sufficient to start network with Prague for tests.
Copy link

Copilot AI Nov 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Corrected spelling of 'hoodi' to 'Holesky' (or 'hood' if referring to a different network).

Suggested change
# Timestamp for Osaka activation in the hoodi. Sufficient to start network with Prague for tests.
# Timestamp for Osaka activation in the Holesky. Sufficient to start network with Prague for tests.

Copilot uses AI. Check for mistakes.
# Future: initialize network with Osaka activated once supported.
ETHREX_OSAKA_ACTIVATION_TIME?=1761677592

# Matches the ports used by the blockchain/metrics dir
L2_PROMETHEUS_METRICS_PORT = 3702
Expand Down Expand Up @@ -140,6 +143,7 @@ init-l2: ## 🚀 Initializes an L2 Lambda ethrex Client
--l1.bridge-address ${DEFAULT_BRIDGE_ADDRESS} \
--l1.on-chain-proposer-address ${DEFAULT_ON_CHAIN_PROPOSER_ADDRESS} \
--eth.rpc-url ${L1_RPC_URL} \
--osaka-activation-time ${ETHREX_OSAKA_ACTIVATION_TIME} \
--block-producer.coinbase-address 0x0007a881CD95B1484fca47615B64803dad620C8d \
--block-producer.base-fee-vault-address 0x000c0d6b7c4516a5b274c51ea331a9410fe69127 \
--block-producer.operator-fee-vault-address 0xd5d2a85751b6F158e5b9B8cD509206A865672362 \
Expand All @@ -151,7 +155,7 @@ init-l2: ## 🚀 Initializes an L2 Lambda ethrex Client
init-l2-dev: ## 🚀 Initializes an L1 and L2 Lambda ethrex Client
COMPILE_CONTRACTS=true \
cargo run --release --features l2,l2-sql --manifest-path ../../Cargo.toml -- \
l2 --dev
l2 --dev --osaka-activation-time ${ETHREX_OSAKA_ACTIVATION_TIME}

init-metrics: ## 🚀 Initializes Grafana and Prometheus with containers
L1_RPC_URL=${L1_RPC_URL} \
Expand Down
Loading
Loading