Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
41 changes: 37 additions & 4 deletions src/chainspec/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Berachain chain specification with Ethereum hardforks plus Prague1 minimum base fee

use crate::{
genesis::{BerachainGenesisConfig, Prague3Config, Prague4Config},
genesis::{BerachainGenesisConfig, OsakaConfig, Prague3Config, Prague4Config},
hardforks::{BerachainHardfork, BerachainHardforks},
primitives::{BerachainHeader, header::BlsPublicKey},
};
Expand Down Expand Up @@ -43,6 +43,7 @@ const BEPOLIA_ETH_GENESIS_JSON: &str =

/// Berachain chain specification wrapping Reth's ChainSpec with Berachain hardforks
#[derive(Debug, Clone, Into, Constructor, PartialEq, Eq, Default)]
#[allow(clippy::too_many_arguments)]
pub struct BerachainChainSpec {
/// The underlying Reth chain specification
pub inner: ChainSpec,
Expand All @@ -57,6 +58,8 @@ pub struct BerachainChainSpec {
pub prague3_config: Option<Prague3Config>,
/// Prague4 configuration (if configured)
pub prague4_config: Option<Prague4Config>,
/// Osaka configuration (if configured)
pub osaka_config: Option<OsakaConfig>,
}

impl BerachainChainSpec {
Expand Down Expand Up @@ -342,7 +345,15 @@ impl EthChainSpec for BerachainChainSpec {
String::new()
};

Box::new(format!("{inner_display}{prague1_details}{prague2_details}{prague3_details}"))
let osaka_details = if let Some(osaka_config) = &self.osaka_config {
format!("\nBerachain Osaka configuration: {{time={}}}", osaka_config.time)
} else {
String::new()
};

Box::new(format!(
"{inner_display}{prague1_details}{prague2_details}{prague3_details}{osaka_details}"
))
}

fn genesis_header(&self) -> &Self::Header {
Expand Down Expand Up @@ -472,6 +483,7 @@ impl BerachainChainSpec {
prague2_minimum_base_fee: 0,
prague3_config: None,
prague4_config: None,
osaka_config: None,
}
}
}
Expand All @@ -489,11 +501,12 @@ impl From<Genesis> for BerachainChainSpec {
return Self::ethereum_fallback(genesis);
}

// Parse Prague1, Prague2, Prague3, and Prague4 configurations if present
// Parse hardfork configurations if present
let prague1_config_opt = berachain_genesis_config.prague1;
let prague2_config_opt = berachain_genesis_config.prague2;
let prague3_config_opt = berachain_genesis_config.prague3;
let prague4_config_opt = berachain_genesis_config.prague4;
let osaka_config_opt = berachain_genesis_config.osaka;

// Both Prague1 and Prague2 are required for Berachain genesis
let (prague1_config, prague2_config) = match (prague1_config_opt, prague2_config_opt) {
Expand Down Expand Up @@ -596,6 +609,19 @@ impl From<Genesis> for BerachainChainSpec {
}
}

// Validate Osaka ordering if configured (must come at or after Prague)
if let Some(osaka_config) = osaka_config_opt.as_ref() {
match genesis.config.prague_time {
Some(prague_time) if osaka_config.time < prague_time => {
panic!(
Comment thread
calbera marked this conversation as resolved.
Outdated
"Osaka hardfork must activate at or after Prague hardfork. Prague time: {}, Osaka time: {}.",
prague_time, osaka_config.time
);
}
_ => {}
}
}

// Berachain networks don't support proof-of-work or non-genesis merge
if let Some(ttd) = genesis.config.terminal_total_difficulty {
if !ttd.is_zero() {
Expand Down Expand Up @@ -685,7 +711,13 @@ impl From<Genesis> for BerachainChainSpec {
));
}

if let Some(osaka_time) = genesis.config.osaka_time {
// Activate EthereumHardfork::Osaka via berachain config or standard genesis config
if let Some(osaka_config) = osaka_config_opt.as_ref() {
hardforks.push((
EthereumHardfork::Osaka.boxed(),
ForkCondition::Timestamp(osaka_config.time),
));
} else if let Some(osaka_time) = genesis.config.osaka_time {
Comment thread
calbera marked this conversation as resolved.
Outdated
hardforks.push((EthereumHardfork::Osaka.boxed(), ForkCondition::Timestamp(osaka_time)));
}

Expand Down Expand Up @@ -773,6 +805,7 @@ impl From<Genesis> for BerachainChainSpec {
prague2_minimum_base_fee,
prague3_config: prague3_config_opt,
prague4_config: prague4_config_opt,
osaka_config: osaka_config_opt,
}
}
}
Expand Down
7 changes: 7 additions & 0 deletions src/evm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,13 @@ mod tests {
SpecId::ISTANBUL, // Later spec - should have this precompile
"BLAKE2F",
),
// P256VERIFY (0x100) was added in Osaka (EIP-7951), should not exist in Prague
(
address!("0x0000000000000000000000000000000000000100"),
SpecId::PRAGUE, // Early spec - should NOT have this precompile
SpecId::OSAKA, // Later spec - should have this precompile
"P256VERIFY",
),
Comment thread
calbera marked this conversation as resolved.
];

for (precompile_addr, early_spec, later_spec, name) in specs_to_test {
Expand Down
15 changes: 15 additions & 0 deletions src/genesis/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,18 @@ pub struct Prague4Config {
/// Unix timestamp when Prague4 activates (ending Prague3 restrictions)
pub time: u64,
}

/// Berachain-specific Osaka hardfork configuration (BRIP-0010)
///
/// Activates EthereumHardfork::Osaka on the EVM side, enabling:
/// - EIP-7951: P-256 (secp256r1) signature verification precompile at 0x100
/// - EIP-7939: CLZ (Count Leading Zeros) opcode
/// - EIP-7823/7883: MODEXP input bounds and gas repricing
/// - EIP-7934/7825: Block and transaction size caps
/// - Contract code size limit increase (24 KB -> 32 KB, initcode 48 KB -> 64 KB)
#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OsakaConfig {
/// Unix timestamp when Osaka activates
pub time: u64,
}
9 changes: 7 additions & 2 deletions src/genesis/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use reth::{revm::primitives::address, rpc::types::serde_helpers::OtherFields};
use serde::{Deserialize, Serialize};
use thiserror::Error;

pub use config::{Prague1Config, Prague2Config, Prague3Config, Prague4Config};
pub use config::{OsakaConfig, Prague1Config, Prague2Config, Prague3Config, Prague4Config};

/// Errors for Berachain genesis configuration parsing
#[derive(Debug, Error)]
Expand Down Expand Up @@ -37,6 +37,8 @@ pub struct BerachainGenesisConfig {
pub prague3: Option<Prague3Config>,
/// Configuration for the Prague4 hardfork, which ends Prague3 restrictions
pub prague4: Option<Prague4Config>,
/// Configuration for the Osaka hardfork (BRIP-0010), which enables Osaka-level EVM features
pub osaka: Option<OsakaConfig>,
}

impl Default for BerachainGenesisConfig {
Expand All @@ -55,6 +57,7 @@ impl Default for BerachainGenesisConfig {
}),
prague3: None, // Not activated by default
prague4: None, // Not activated by default
osaka: None, // Not activated by default
}
}
}
Expand Down Expand Up @@ -119,7 +122,9 @@ impl TryFrom<&OtherFields> for BerachainGenesisConfig {
Ok(cfg)
}
Some(Err(e)) => Err(BerachainConfigError::InvalidConfig(e)),
None => Ok(Self { prague1: None, prague2: None, prague3: None, prague4: None }),
None => {
Ok(Self { prague1: None, prague2: None, prague3: None, prague4: None, osaka: None })
}
}
}
}
Expand Down
12 changes: 12 additions & 0 deletions src/node/evm/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ use std::{borrow::Cow, convert::Infallible, fmt::Debug, sync::Arc};

const BERACHAIN_BLOCK_TIME_SECONDS: u64 = 2;

/// BRIP-0010: Contract code size limit increase from 24 KB to 32 KB (Osaka)
const MAX_CODE_SIZE_OSAKA: usize = 32_768;

/// BRIP-0010: Contract initcode size limit increase from 48 KB to 64 KB (Osaka)
const MAX_INITCODE_SIZE_OSAKA: usize = 65_536;

#[derive(Debug, Clone)]
pub struct BerachainEvmConfig {
/// Receipt builder.
Expand Down Expand Up @@ -129,6 +135,8 @@ impl ConfigureEvm for BerachainEvmConfig {

if self.chain_spec().is_osaka_active_at_timestamp(header.timestamp) {
cfg_env.tx_gas_limit_cap = Some(MAX_TX_GAS_LIMIT_OSAKA);
cfg_env.limit_contract_code_size = Some(MAX_CODE_SIZE_OSAKA);
cfg_env.limit_contract_initcode_size = Some(MAX_INITCODE_SIZE_OSAKA);
}

// derive the EIP-4844 blob fees from the header's `excess_blob_gas` and the current
Expand Down Expand Up @@ -176,6 +184,8 @@ impl ConfigureEvm for BerachainEvmConfig {

if self.chain_spec().is_osaka_active_at_timestamp(attributes.timestamp) {
cfg.tx_gas_limit_cap = Some(MAX_TX_GAS_LIMIT_OSAKA);
cfg.limit_contract_code_size = Some(MAX_CODE_SIZE_OSAKA);
cfg.limit_contract_initcode_size = Some(MAX_INITCODE_SIZE_OSAKA);
}

// if the parent block did not have excess blob gas (i.e. it was pre-cancun), but it is
Expand Down Expand Up @@ -312,6 +322,8 @@ impl ConfigureEngineEvm<BerachainExecutionData> for BerachainEvmConfig {

if self.chain_spec().is_osaka_active_at_timestamp(timestamp) {
cfg_env.tx_gas_limit_cap = Some(MAX_TX_GAS_LIMIT_OSAKA);
cfg_env.limit_contract_code_size = Some(MAX_CODE_SIZE_OSAKA);
cfg_env.limit_contract_initcode_size = Some(MAX_INITCODE_SIZE_OSAKA);
}

// derive the EIP-4844 blob fees from the header's `excess_blob_gas` and the current
Expand Down