Skip to content

Commit

Permalink
shanghai eips 3651, 3855, 3860 (#152)
Browse files Browse the repository at this point in the history
* EIP-3651 Warm COINBASE

* EIP-3855 PUSH0 instruction

* EIP-3860 Limit and meter initcode

* fmt

* Fix PUSH0 for older configurations

* Move push0 handling to core

* Record init code cost all features
  • Loading branch information
tgmichel committed Mar 8, 2023
1 parent 8ac1908 commit 2e9c3b7
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 4 deletions.
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
5 changes: 5 additions & 0 deletions core/src/eval/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,10 @@ fn eval_jumpdest(_state: &mut Machine, _opcode: Opcode, _position: usize) -> Con
Control::Continue(1)
}

fn eval_push0(state: &mut Machine, _opcode: Opcode, position: usize) -> Control {
self::misc::push(state, 0, position)
}

fn eval_push1(state: &mut Machine, _opcode: Opcode, position: usize) -> Control {
self::misc::push(state, 1, position)
}
Expand Down Expand Up @@ -494,6 +498,7 @@ pub fn eval(state: &mut Machine, opcode: Opcode, position: usize) -> Control {
table[Opcode::MSIZE.as_usize()] = eval_msize as _;
table[Opcode::JUMPDEST.as_usize()] = eval_jumpdest as _;

table[Opcode::PUSH0.as_usize()] = eval_push0 as _;
table[Opcode::PUSH1.as_usize()] = eval_push1 as _;
table[Opcode::PUSH2.as_usize()] = eval_push2 as _;
table[Opcode::PUSH3.as_usize()] = eval_push3 as _;
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
46 changes: 46 additions & 0 deletions runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,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 @@ -261,6 +263,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 @@ -281,6 +285,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 @@ -315,6 +321,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 @@ -323,6 +330,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 @@ -333,6 +341,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 @@ -366,6 +375,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 @@ -374,6 +384,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 @@ -384,6 +395,7 @@ impl Config {
has_self_balance: true,
has_ext_code_hash: true,
has_base_fee: false,
has_push0: false,
estimate: false,
}
}
Expand All @@ -398,14 +410,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 @@ -447,6 +467,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 @@ -455,6 +476,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 @@ -465,6 +487,7 @@ impl Config {
has_self_balance: true,
has_ext_code_hash: true,
has_base_fee,
has_push0,
estimate: false,
}
}
Expand All @@ -478,7 +501,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 @@ -489,7 +515,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 @@ -500,7 +529,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),
}
}
}
57 changes: 55 additions & 2 deletions src/executor/stack/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,23 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet>
gasometer.record_transaction(transaction_cost)
}

fn maybe_record_init_code_cost(&mut self, init_code: &[u8]) -> Result<(), ExitError> {
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);
return Err(ExitError::OutOfGas);
}
return self
.state
.metadata_mut()
.gasometer
.record_cost(gasometer::init_code_cost(init_code));
}
Ok(())
}

/// Execute a `CREATE` transaction.
pub fn transact_create(
&mut self,
Expand All @@ -416,6 +433,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 @@ -449,6 +474,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 @@ -522,8 +555,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 @@ -1095,6 +1136,12 @@ 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 Err(e) = self.maybe_record_init_code_cost(&init_code) {
let reason: ExitReason = e.into();
emit_exit!(reason.clone());
return Capture::Exit((reason, None, Vec::new()));
}

self.create_inner(caller, scheme, value, init_code, target_gas, true)
}

Expand All @@ -1107,6 +1154,12 @@ 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 Err(e) = self.maybe_record_init_code_cost(&init_code) {
let reason: ExitReason = e.into();
emit_exit!(reason.clone());
return Capture::Exit((reason, 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

0 comments on commit 2e9c3b7

Please sign in to comment.