diff --git a/crates/vm/levm/src/gas_cost.rs b/crates/vm/levm/src/gas_cost.rs index 454bc0b91f..2ef5fed528 100644 --- a/crates/vm/levm/src/gas_cost.rs +++ b/crates/vm/levm/src/gas_cost.rs @@ -125,6 +125,7 @@ pub const EXTCODECOPY_WARM_DYNAMIC: u64 = DEFAULT_WARM_DYNAMIC; pub const CALL_STATIC: u64 = DEFAULT_STATIC; pub const CALL_COLD_DYNAMIC: u64 = DEFAULT_COLD_DYNAMIC; pub const CALL_WARM_DYNAMIC: u64 = DEFAULT_WARM_DYNAMIC; +pub const CALL_PRE_BERLIN: u64 = 700; pub const CALL_POSITIVE_VALUE: u64 = 9000; pub const CALL_POSITIVE_VALUE_STIPEND: u64 = 2300; pub const CALL_TO_EMPTY_ACCOUNT: u64 = 25000; @@ -158,6 +159,7 @@ pub const CREATE_BASE_COST: u64 = 32000; // Calldata costs pub const CALLDATA_COST_ZERO_BYTE: u64 = 4; pub const CALLDATA_COST_NON_ZERO_BYTE: u64 = 16; +pub const CALLDATA_COST_NON_ZERO_BYTE_PRE_ISTANBUL: u64 = 68; pub const STANDARD_TOKEN_COST: u64 = 4; // Blob gas costs @@ -186,6 +188,8 @@ pub const MODEXP_STATIC_COST: u64 = 200; pub const MODEXP_DYNAMIC_BASE: u64 = 200; pub const MODEXP_DYNAMIC_QUOTIENT: u64 = 3; +pub const MODEXP_DYNAMIC_QUOTIENT_PRE_BERLIN: u64 = 20; + pub const ECADD_COST: u64 = 150; pub const ECMUL_COST: u64 = 6000; @@ -528,19 +532,26 @@ pub fn selfdestruct( Ok(gas_cost) } -pub fn tx_calldata(calldata: &Bytes) -> Result { +pub fn tx_calldata(calldata: &Bytes, spec_id: SpecId) -> Result { // This cost applies both for call and create // 4 gas for each zero byte in the transaction data 16 gas for each non-zero byte in the transaction. let mut calldata_cost: u64 = 0; for byte in calldata { - if *byte != 0 { - calldata_cost = calldata_cost - .checked_add(CALLDATA_COST_NON_ZERO_BYTE) - .ok_or(OutOfGasError::GasUsedOverflow)?; + calldata_cost = if *byte != 0 { + if spec_id >= SpecId::ISTANBUL { + calldata_cost + .checked_add(CALLDATA_COST_NON_ZERO_BYTE) + .ok_or(OutOfGasError::GasUsedOverflow)? + } else { + // EIP-2028 + calldata_cost + .checked_add(CALLDATA_COST_NON_ZERO_BYTE_PRE_ISTANBUL) + .ok_or(OutOfGasError::GasUsedOverflow)? + } } else { - calldata_cost = calldata_cost + calldata_cost .checked_add(CALLDATA_COST_ZERO_BYTE) - .ok_or(OutOfGasError::GasUsedOverflow)?; + .ok_or(OutOfGasError::GasUsedOverflow)? } } Ok(calldata_cost) @@ -633,6 +644,7 @@ pub fn extcodehash(address_was_cold: bool) -> Result { ) } +#[allow(clippy::too_many_arguments)] pub fn call( new_memory_size: usize, current_memory_size: usize, @@ -641,6 +653,7 @@ pub fn call( value_to_transfer: U256, gas_from_stack: U256, gas_left: u64, + spec_id: SpecId, ) -> Result<(u64, u64), VMError> { let memory_expansion_cost = memory::expansion_cost(new_memory_size, current_memory_size)?; @@ -648,7 +661,12 @@ pub fn call( address_was_cold, CALL_STATIC, CALL_COLD_DYNAMIC, - CALL_WARM_DYNAMIC, + if spec_id >= SpecId::BERLIN { + CALL_WARM_DYNAMIC + } else { + //https://eips.ethereum.org/EIPS/eip-2929 + CALL_PRE_BERLIN + }, )?; let positive_value_cost = if !value_to_transfer.is_zero() { CALL_POSITIVE_VALUE @@ -807,11 +825,120 @@ pub fn identity(data_size: usize) -> Result { precompile(data_size, IDENTITY_STATIC_COST, IDENTITY_DYNAMIC_BASE) } +//https://eips.ethereum.org/EIPS/eip-2565 +pub fn modexp_eip2565( + max_length: u64, + exponent_first_32_bytes: &BigUint, + exponent_size: u64, +) -> Result { + let words = (max_length + .checked_add(7) + .ok_or(OutOfGasError::GasCostOverflow)?) + .checked_div(8) + .ok_or(InternalError::DivisionError)?; + let multiplication_complexity = words.checked_pow(2).ok_or(OutOfGasError::GasCostOverflow)?; + + let calculate_iteration_count = + if exponent_size <= 32 && *exponent_first_32_bytes != BigUint::ZERO { + exponent_first_32_bytes + .bits() + .checked_sub(1) + .ok_or(InternalError::ArithmeticOperationUnderflow)? + } else if exponent_size > 32 { + let extra_size = (exponent_size + .checked_sub(32) + .ok_or(InternalError::ArithmeticOperationUnderflow)?) + .checked_mul(8) + .ok_or(OutOfGasError::GasCostOverflow)?; + extra_size + .checked_add(exponent_first_32_bytes.bits().max(1)) + .ok_or(OutOfGasError::GasCostOverflow)? + .checked_sub(1) + .ok_or(InternalError::ArithmeticOperationUnderflow)? + } else { + 0 + } + .max(1); + + let cost = MODEXP_STATIC_COST.max( + multiplication_complexity + .checked_mul(calculate_iteration_count) + .ok_or(OutOfGasError::GasCostOverflow)? + / MODEXP_DYNAMIC_QUOTIENT, + ); + Ok(cost) +} + +//https://eips.ethereum.org/EIPS/eip-198 +pub fn modexp_eip198( + max_length: u64, + exponent_first_32_bytes: &BigUint, + exponent_size: u64, +) -> Result { + let multiplication_complexity = if max_length <= 64 { + max_length + .checked_pow(2) + .ok_or(OutOfGasError::GasCostOverflow)? + } else if max_length <= 1024 { + max_length + .checked_pow(2) + .ok_or(OutOfGasError::GasCostOverflow)? + .checked_div(4) + .ok_or(OutOfGasError::GasCostOverflow)? + .checked_add( + max_length + .checked_mul(96) + .ok_or(OutOfGasError::GasCostOverflow)?, + ) + .ok_or(OutOfGasError::GasCostOverflow)? + .checked_sub(3072) + .ok_or(OutOfGasError::GasCostOverflow)? + } else { + max_length + .checked_pow(2) + .ok_or(OutOfGasError::GasCostOverflow)? + .checked_div(16) + .ok_or(OutOfGasError::GasCostOverflow)? + .checked_add( + max_length + .checked_mul(480) + .ok_or(OutOfGasError::GasCostOverflow)?, + ) + .ok_or(OutOfGasError::GasCostOverflow)? + .checked_sub(199680) + .ok_or(OutOfGasError::GasCostOverflow)? + }; + + let calculate_iteration_count = if exponent_size < 32 { + exponent_first_32_bytes.bits().saturating_sub(1) + } else { + let extra_size = (exponent_size + .checked_sub(32) + .ok_or(InternalError::ArithmeticOperationUnderflow)?) + .checked_mul(8) + .ok_or(OutOfGasError::GasCostOverflow)?; + + let bits_part = exponent_first_32_bytes.bits().saturating_sub(1); + + extra_size + .checked_add(bits_part) + .ok_or(OutOfGasError::GasCostOverflow)? + } + .max(1); + + let cost = multiplication_complexity + .checked_mul(calculate_iteration_count) + .ok_or(OutOfGasError::GasCostOverflow)? + / MODEXP_DYNAMIC_QUOTIENT_PRE_BERLIN; + Ok(cost) +} + pub fn modexp( exponent_first_32_bytes: &BigUint, base_size: usize, exponent_size: usize, modulus_size: usize, + spec_id: SpecId, ) -> Result { let base_size: u64 = base_size .try_into() @@ -824,43 +951,12 @@ pub fn modexp( .map_err(|_| PrecompileError::ParsingInputError)?; let max_length = base_size.max(modulus_size); - let words = (max_length - .checked_add(7) - .ok_or(OutOfGasError::GasCostOverflow)?) - .checked_div(8) - .ok_or(InternalError::DivisionError)?; - let multiplication_complexity = words.checked_pow(2).ok_or(OutOfGasError::GasCostOverflow)?; - - let iteration_count = if exponent_size <= 32 && *exponent_first_32_bytes != BigUint::ZERO { - exponent_first_32_bytes - .bits() - .checked_sub(1) - .ok_or(InternalError::ArithmeticOperationUnderflow)? - } else if exponent_size > 32 { - let extra_size = (exponent_size - .checked_sub(32) - .ok_or(InternalError::ArithmeticOperationUnderflow)?) - .checked_mul(8) - .ok_or(OutOfGasError::GasCostOverflow)?; - extra_size - .checked_add(exponent_first_32_bytes.bits().max(1)) - .ok_or(OutOfGasError::GasCostOverflow)? - .checked_sub(1) - .ok_or(InternalError::ArithmeticOperationUnderflow)? + if spec_id >= SpecId::BERLIN { + modexp_eip2565(max_length, exponent_first_32_bytes, exponent_size) } else { - 0 - }; - let calculate_iteration_count = iteration_count.max(1); - - let cost = MODEXP_STATIC_COST.max( - multiplication_complexity - .checked_mul(calculate_iteration_count) - .ok_or(OutOfGasError::GasCostOverflow)? - / MODEXP_DYNAMIC_QUOTIENT, - ); - - Ok(cost) + modexp_eip198(max_length, exponent_first_32_bytes, exponent_size) + } } fn precompile(data_size: usize, static_cost: u64, dynamic_base: u64) -> Result { diff --git a/crates/vm/levm/src/opcode_handlers/system.rs b/crates/vm/levm/src/opcode_handlers/system.rs index bcf66fb89e..884cfff1e2 100644 --- a/crates/vm/levm/src/opcode_handlers/system.rs +++ b/crates/vm/levm/src/opcode_handlers/system.rs @@ -64,6 +64,7 @@ impl VM { value_to_transfer, gas, gas_left, + self.env.spec_id, )?; self.increase_consumed_gas(current_call_frame, cost)?; diff --git a/crates/vm/levm/src/precompiles.rs b/crates/vm/levm/src/precompiles.rs index fd0361eba9..e9a36c6041 100644 --- a/crates/vm/levm/src/precompiles.rs +++ b/crates/vm/levm/src/precompiles.rs @@ -105,7 +105,10 @@ pub fn is_precompile(callee_address: &Address, spec_id: SpecId) -> bool { PRECOMPILES.contains(callee_address) } -pub fn execute_precompile(current_call_frame: &mut CallFrame) -> Result { +pub fn execute_precompile( + current_call_frame: &mut CallFrame, + spec_id: SpecId, +) -> Result { let callee_address = current_call_frame.code_address; let calldata = current_call_frame.calldata.clone(); let gas_for_call = current_call_frame @@ -123,7 +126,9 @@ pub fn execute_precompile(current_call_frame: &mut CallFrame) -> Result { ripemd_160(&calldata, gas_for_call, consumed_gas)? } - address if address == MODEXP_ADDRESS => modexp(&calldata, gas_for_call, consumed_gas)?, + address if address == MODEXP_ADDRESS => { + modexp(&calldata, gas_for_call, consumed_gas, spec_id)? + } address if address == ECADD_ADDRESS => ecadd(&calldata, gas_for_call, consumed_gas)?, address if address == ECMUL_ADDRESS => ecmul(&calldata, gas_for_call, consumed_gas)?, address if address == ECPAIRING_ADDRESS => { @@ -277,73 +282,83 @@ pub fn modexp( calldata: &Bytes, gas_for_call: u64, consumed_gas: &mut u64, + spec_id: SpecId, ) -> Result { // If calldata does not reach the required length, we should fill the rest with zeros let calldata = fill_with_zeros(calldata, 96)?; - let b_size = U256::from_big_endian( + let base_size = U256::from_big_endian( calldata .get(0..32) .ok_or(PrecompileError::ParsingInputError)?, ); - let e_size = U256::from_big_endian( + let exponent_size = U256::from_big_endian( calldata .get(32..64) .ok_or(PrecompileError::ParsingInputError)?, ); - let m_size = U256::from_big_endian( + let modulus_size = U256::from_big_endian( calldata .get(64..96) .ok_or(PrecompileError::ParsingInputError)?, ); - if b_size == U256::zero() && m_size == U256::zero() { + if base_size == U256::zero() && modulus_size == U256::zero() { increase_precompile_consumed_gas(gas_for_call, MODEXP_STATIC_COST, consumed_gas)?; return Ok(Bytes::new()); } // Because on some cases conversions to usize exploded before the check of the zero value could be done - let b_size = usize::try_from(b_size).map_err(|_| PrecompileError::ParsingInputError)?; - let e_size = usize::try_from(e_size).map_err(|_| PrecompileError::ParsingInputError)?; - let m_size = usize::try_from(m_size).map_err(|_| PrecompileError::ParsingInputError)?; + let base_size = usize::try_from(base_size).map_err(|_| PrecompileError::ParsingInputError)?; + let exponent_size = + usize::try_from(exponent_size).map_err(|_| PrecompileError::ParsingInputError)?; + let modulus_size = + usize::try_from(modulus_size).map_err(|_| PrecompileError::ParsingInputError)?; - let base_limit = b_size + let base_limit = base_size .checked_add(96) .ok_or(InternalError::ArithmeticOperationOverflow)?; - let exponent_limit = e_size + let exponent_limit = exponent_size .checked_add(base_limit) .ok_or(InternalError::ArithmeticOperationOverflow)?; - let modulus_limit = m_size + let modulus_limit = modulus_size .checked_add(exponent_limit) .ok_or(InternalError::ArithmeticOperationOverflow)?; - let b = get_slice_or_default(&calldata, 96, base_limit, b_size)?; + let b = get_slice_or_default(&calldata, 96, base_limit, base_size)?; let base = BigUint::from_bytes_be(&b); - let e = get_slice_or_default(&calldata, base_limit, exponent_limit, e_size)?; + let e = get_slice_or_default(&calldata, base_limit, exponent_limit, exponent_size)?; let exponent = BigUint::from_bytes_be(&e); - let m = get_slice_or_default(&calldata, exponent_limit, modulus_limit, m_size)?; + let m = get_slice_or_default(&calldata, exponent_limit, modulus_limit, modulus_size)?; let modulus = BigUint::from_bytes_be(&m); // First 32 bytes of exponent or exponent if e_size < 32 - let bytes_to_take = 32.min(e_size); + let bytes_to_take = 32.min(exponent_size); // Use of unwrap_or_default because if e == 0 get_slice_or_default returns an empty vec let exp_first_32 = BigUint::from_bytes_be(e.get(0..bytes_to_take).unwrap_or_default()); - let gas_cost = gas_cost::modexp(&exp_first_32, b_size, e_size, m_size)?; + let gas_cost = gas_cost::modexp( + &exp_first_32, + base_size, + exponent_size, + modulus_size, + spec_id, + )?; + increase_precompile_consumed_gas(gas_for_call, gas_cost, consumed_gas)?; let result = mod_exp(base, exponent, modulus); let res_bytes = result.to_bytes_be(); - let res_bytes = increase_left_pad(&Bytes::from(res_bytes), m_size)?; + let res_bytes = increase_left_pad(&Bytes::from(res_bytes), modulus_size)?; - Ok(res_bytes.slice(..m_size)) + Ok(res_bytes.slice(..modulus_size)) } /// This function returns the slice between the lower and upper limit of the calldata (as a vector), diff --git a/crates/vm/levm/src/vm.rs b/crates/vm/levm/src/vm.rs index fb2077ee76..271774922e 100644 --- a/crates/vm/levm/src/vm.rs +++ b/crates/vm/levm/src/vm.rs @@ -256,7 +256,7 @@ impl VM { ); if is_precompile(¤t_call_frame.code_address, self.env.spec_id) { - let precompile_result = execute_precompile(current_call_frame); + let precompile_result = execute_precompile(current_call_frame, self.env.spec_id); match precompile_result { Ok(output) => { @@ -547,8 +547,8 @@ impl VM { // Calldata Cost // 4 gas for each zero byte in the transaction data 16 gas for each non-zero byte in the transaction. - let calldata_cost = - gas_cost::tx_calldata(&initial_call_frame.calldata).map_err(VMError::OutOfGas)?; + let calldata_cost = gas_cost::tx_calldata(&initial_call_frame.calldata, self.env.spec_id) + .map_err(VMError::OutOfGas)?; intrinsic_gas = intrinsic_gas .checked_add(calldata_cost) diff --git a/crates/vm/levm/tests/tests.rs b/crates/vm/levm/tests/tests.rs index 028f5a44f7..5a5840d756 100644 --- a/crates/vm/levm/tests/tests.rs +++ b/crates/vm/levm/tests/tests.rs @@ -4549,7 +4549,13 @@ fn modexp_test() { let calldata = Bytes::from(calldata); let mut consumed_gas = 0; - let result = modexp(&calldata, 10000, &mut consumed_gas).unwrap(); + let result = modexp( + &calldata, + 10000, + &mut consumed_gas, + ethrex_levm::SpecId::CANCUN, + ) + .unwrap(); let expected_result = Bytes::from(hex::decode("08").unwrap()); @@ -4564,7 +4570,13 @@ fn modexp_test_2() { let calldata = Bytes::from(calldata); let mut consumed_gas = 0; - let result = modexp(&calldata, 10000, &mut consumed_gas).unwrap(); + let result = modexp( + &calldata, + 10000, + &mut consumed_gas, + ethrex_levm::SpecId::CANCUN, + ) + .unwrap(); let expected_result = Bytes::from( hex::decode("3b01b01ac41f2d6e917c6d6a221ce793802469026d9ab7578fa2e79e4da6aaab").unwrap(),