Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
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
4 changes: 4 additions & 0 deletions core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,10 @@ pub enum ExitError {
/// Other normal errors.
#[cfg_attr(feature = "with-codec", codec(index = 13))]
Other(Cow<'static, str>),

/// Init code exceeds limit (runtime).
#[cfg_attr(feature = "with-codec", codec(index = 7))]
InitCodeLimit,
}

impl From<ExitError> for ExitReason {
Expand Down
1 change: 1 addition & 0 deletions core/src/opcode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ impl Opcode {
pub const JUMPDEST: Opcode = Opcode(0x5b);

/// `PUSHn`
pub const PUSH0: Opcode = Opcode(0x5f);
pub const PUSH1: Opcode = Opcode(0x60);
pub const PUSH2: Opcode = Opcode(0x61);
pub const PUSH3: Opcode = Opcode(0x62);
Expand Down
19 changes: 17 additions & 2 deletions gasometer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,12 +238,17 @@ impl<'config> Gasometer<'config> {
non_zero_data_len,
access_list_address_len,
access_list_storage_len,
initcode_cost,
} => {
self.config.gas_transaction_create
let mut cost = self.config.gas_transaction_create
+ zero_data_len as u64 * self.config.gas_transaction_zero_data
+ non_zero_data_len as u64 * self.config.gas_transaction_non_zero_data
+ access_list_address_len as u64 * self.config.gas_access_list_address
+ access_list_storage_len as u64 * self.config.gas_access_list_storage_key
+ access_list_storage_len as u64 * self.config.gas_access_list_storage_key;
if self.config.max_initcode_size.is_some() {
cost += initcode_cost;
}
cost
}
};

Expand Down Expand Up @@ -293,15 +298,21 @@ pub fn create_transaction_cost(data: &[u8], access_list: &[(H160, Vec<H256>)]) -
let zero_data_len = data.iter().filter(|v| **v == 0).count();
let non_zero_data_len = data.len() - zero_data_len;
let (access_list_address_len, access_list_storage_len) = count_access_list(access_list);
let initcode_cost = init_code_cost(data);

TransactionCost::Create {
zero_data_len,
non_zero_data_len,
access_list_address_len,
access_list_storage_len,
initcode_cost,
}
}

pub fn init_code_cost(data: &[u8]) -> u64 {
2 * (data.len() as u64 / 32)
}

/// Counts the number of addresses and storage keys in the access list
fn count_access_list(access_list: &[(H160, Vec<H256>)]) -> (usize, usize) {
let access_list_address_len = access_list.len();
Expand Down Expand Up @@ -610,6 +621,8 @@ pub fn dynamic_opcode_cost<H: Handler>(
}
}

Opcode::PUSH0 if config.has_push0 => GasCost::Base,

_ => GasCost::Invalid(opcode),
};

Expand Down Expand Up @@ -1021,6 +1034,8 @@ pub enum TransactionCost {
access_list_address_len: usize,
/// Total number of storage keys in transaction access list (see EIP-2930)
access_list_storage_len: usize,
/// Cost of initcode = 2 * ceil(len(initcode) / 32) (see EIP-3860)
initcode_cost: u64,
},
}

Expand Down
1 change: 1 addition & 0 deletions runtime/src/eval/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ pub fn eval<H: Handler>(state: &mut Runtime, opcode: Opcode, handler: &mut H) ->
Opcode::STATICCALL => system::call(state, CallScheme::StaticCall, handler),
Opcode::CHAINID => system::chainid(state, handler),
Opcode::BASEFEE => system::base_fee(state, handler),
Opcode::PUSH0 => system::push0(state),
_ => handle_other(state, opcode, handler),
}
}
6 changes: 6 additions & 0 deletions runtime/src/eval/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,12 @@ pub fn base_fee<H: Handler>(runtime: &mut Runtime, handler: &H) -> Control<H> {
Control::Continue
}

pub fn push0<H: Handler>(runtime: &mut Runtime) -> Control<H> {
Comment thread
tgmichel marked this conversation as resolved.
Outdated
push!(runtime, H256::default());

Control::Continue
}

pub fn extcodesize<H: Handler>(runtime: &mut Runtime, handler: &H) -> Control<H> {
pop!(runtime, address);
push_u256!(runtime, handler.code_size(address.into()));
Expand Down
46 changes: 46 additions & 0 deletions runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,8 @@ pub struct Config {
pub decrease_clears_refund: bool,
/// EIP-3541
pub disallow_executable_format: bool,
/// EIP-3651
pub warm_coinbase_address: bool,
/// Whether to throw out of gas error when
/// CALL/CALLCODE/DELEGATECALL requires more than maximum amount
/// of gas.
Expand All @@ -233,6 +235,8 @@ pub struct Config {
pub call_stack_limit: usize,
/// Create contract limit.
pub create_contract_limit: Option<usize>,
/// EIP-3860, maximum size limit of init_code.
pub max_initcode_size: Option<usize>,
/// Call stipend.
pub call_stipend: u64,
/// Has delegate call.
Expand All @@ -253,6 +257,8 @@ pub struct Config {
pub has_ext_code_hash: bool,
/// Has ext block fee. See [EIP-3198](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-3198.md)
pub has_base_fee: bool,
/// Has PUSH0 opcode. See [EIP-3855](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-3855.md)
pub has_push0: bool,
/// Whether the gasometer is running in estimate mode.
pub estimate: bool,
}
Expand Down Expand Up @@ -287,6 +293,7 @@ impl Config {
increase_state_access_gas: false,
decrease_clears_refund: false,
disallow_executable_format: false,
warm_coinbase_address: false,
err_on_call_with_more_gas: true,
empty_considered_exists: true,
create_increase_nonce: false,
Expand All @@ -295,6 +302,7 @@ impl Config {
memory_limit: usize::MAX,
call_stack_limit: 1024,
create_contract_limit: None,
max_initcode_size: None,
call_stipend: 2300,
has_delegate_call: false,
has_create2: false,
Expand All @@ -305,6 +313,7 @@ impl Config {
has_self_balance: false,
has_ext_code_hash: false,
has_base_fee: false,
has_push0: false,
estimate: false,
}
}
Expand Down Expand Up @@ -338,6 +347,7 @@ impl Config {
increase_state_access_gas: false,
decrease_clears_refund: false,
disallow_executable_format: false,
warm_coinbase_address: false,
err_on_call_with_more_gas: false,
empty_considered_exists: false,
create_increase_nonce: true,
Expand All @@ -346,6 +356,7 @@ impl Config {
memory_limit: usize::MAX,
call_stack_limit: 1024,
create_contract_limit: Some(0x6000),
max_initcode_size: None,
call_stipend: 2300,
has_delegate_call: true,
has_create2: true,
Expand All @@ -356,6 +367,7 @@ impl Config {
has_self_balance: true,
has_ext_code_hash: true,
has_base_fee: false,
has_push0: false,
estimate: false,
}
}
Expand All @@ -370,14 +382,22 @@ impl Config {
Self::config_with_derived_values(DerivedConfigInputs::london())
}

/// Shanghai hard fork configuration.
pub const fn shanghai() -> Config {
Self::config_with_derived_values(DerivedConfigInputs::shanghai())
}

const fn config_with_derived_values(inputs: DerivedConfigInputs) -> Config {
let DerivedConfigInputs {
gas_storage_read_warm,
gas_sload_cold,
gas_access_list_storage_key,
decrease_clears_refund,
has_base_fee,
has_push0,
disallow_executable_format,
warm_coinbase_address,
max_initcode_size,
} = inputs;

// See https://eips.ethereum.org/EIPS/eip-2929
Expand Down Expand Up @@ -419,6 +439,7 @@ impl Config {
increase_state_access_gas: true,
decrease_clears_refund,
disallow_executable_format,
warm_coinbase_address,
err_on_call_with_more_gas: false,
empty_considered_exists: false,
create_increase_nonce: true,
Expand All @@ -427,6 +448,7 @@ impl Config {
memory_limit: usize::MAX,
call_stack_limit: 1024,
create_contract_limit: Some(0x6000),
max_initcode_size,
call_stipend: 2300,
has_delegate_call: true,
has_create2: true,
Expand All @@ -437,6 +459,7 @@ impl Config {
has_self_balance: true,
has_ext_code_hash: true,
has_base_fee,
has_push0,
estimate: false,
}
}
Expand All @@ -450,7 +473,10 @@ struct DerivedConfigInputs {
gas_access_list_storage_key: u64,
decrease_clears_refund: bool,
has_base_fee: bool,
has_push0: bool,
disallow_executable_format: bool,
warm_coinbase_address: bool,
max_initcode_size: Option<usize>,
}

impl DerivedConfigInputs {
Expand All @@ -461,7 +487,10 @@ impl DerivedConfigInputs {
gas_access_list_storage_key: 1900,
decrease_clears_refund: false,
has_base_fee: false,
has_push0: false,
disallow_executable_format: false,
warm_coinbase_address: false,
max_initcode_size: None,
}
}

Expand All @@ -472,7 +501,24 @@ impl DerivedConfigInputs {
gas_access_list_storage_key: 1900,
decrease_clears_refund: true,
has_base_fee: true,
has_push0: false,
disallow_executable_format: true,
warm_coinbase_address: false,
max_initcode_size: None,
}
}

const fn shanghai() -> Self {
Self {
gas_storage_read_warm: 100,
gas_sload_cold: 2100,
gas_access_list_storage_key: 1900,
decrease_clears_refund: true,
has_base_fee: true,
has_push0: true,
disallow_executable_format: true,
warm_coinbase_address: true,
max_initcode_size: Some(0xC000),
}
}
}
46 changes: 44 additions & 2 deletions src/executor/stack/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,14 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet>
address: self.create_address(CreateScheme::Legacy { caller }),
});

if let Some(limit) = self.config.max_initcode_size {
if init_code.len() > limit {
self.state.metadata_mut().gasometer.fail();
let _ = self.exit_substate(StackExitKind::Failed);
return emit_exit!(ExitError::InitCodeLimit.into(), Vec::new());
}
}

if let Err(e) = self.record_create_transaction_cost(&init_code, &access_list) {
return emit_exit!(e.into(), Vec::new());
}
Expand Down Expand Up @@ -486,6 +494,14 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet>
gas_limit: u64,
access_list: Vec<(H160, Vec<H256>)>, // See EIP-2930
) -> (ExitReason, Vec<u8>) {
if let Some(limit) = self.config.max_initcode_size {
if init_code.len() > limit {
self.state.metadata_mut().gasometer.fail();
let _ = self.exit_substate(StackExitKind::Failed);
return emit_exit!(ExitError::InitCodeLimit.into(), Vec::new());
}
}

let code_hash = H256::from_slice(Keccak256::digest(&init_code).as_slice());
event!(TransactCreate2 {
caller,
Expand Down Expand Up @@ -554,8 +570,16 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet>

// Initialize initial addresses for EIP-2929
if self.config.increase_state_access_gas {
let addresses = core::iter::once(caller).chain(core::iter::once(address));
self.state.metadata_mut().access_addresses(addresses);
if self.config.warm_coinbase_address {
// Warm coinbase address for EIP-3651
let addresses = core::iter::once(caller)
.chain(core::iter::once(address))
.chain(core::iter::once(self.block_coinbase()));
self.state.metadata_mut().access_addresses(addresses);
} else {
let addresses = core::iter::once(caller).chain(core::iter::once(address));
self.state.metadata_mut().access_addresses(addresses);
}

self.initialize_with_access_list(access_list);
}
Expand Down Expand Up @@ -1116,6 +1140,24 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> Handler
init_code: Vec<u8>,
target_gas: Option<u64>,
) -> Capture<(ExitReason, Option<H160>, Vec<u8>), Self::CreateInterrupt> {
if let Some(limit) = self.config.max_initcode_size {
// EIP-3860
if init_code.len() > limit {
self.state.metadata_mut().gasometer.fail();
let _ = self.exit_substate(StackExitKind::Failed);
emit_exit!(ExitError::OutOfGas.into(), Vec::new());
return Capture::Exit((ExitError::OutOfGas.into(), None, Vec::new()));
}
if let Err(e) = self
.state
.metadata_mut()
.gasometer
.record_cost(gasometer::init_code_cost(&init_code))
{
return Capture::Exit((e.into(), None, Vec::new()));
}
}

let capture = self.create_inner(caller, scheme, value, init_code, target_gas, true);

if let Capture::Exit((ref reason, _, ref return_value)) = capture {
Expand Down