Skip to content

Commit

Permalink
dev: use checked math
Browse files Browse the repository at this point in the history
  • Loading branch information
enitrat committed Oct 2, 2024
1 parent 0a76b4f commit c7806ad
Show file tree
Hide file tree
Showing 14 changed files with 118 additions and 75 deletions.
17 changes: 12 additions & 5 deletions crates/evm/src/create_helpers.cairo
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use core::num::traits::Bounded;
use core::num::traits::CheckedAdd;
use core::num::traits::Zero;
use core::starknet::EthAddress;
use crate::errors::{ensure, EVMError};
Expand All @@ -18,7 +19,6 @@ use utils::set::SetTrait;
use utils::traits::{
BoolIntoNumeric, EthAddressIntoU256, U256TryIntoResult, SpanU8TryIntoResultEthAddress
};

/// Helper struct to prepare CREATE and CREATE2 opcodes
#[derive(Drop)]
pub struct CreateArgs {
Expand Down Expand Up @@ -46,13 +46,20 @@ pub impl CreateHelpersImpl of CreateHelpers {
self.memory.ensure_length(memory_expansion.new_size);
let init_code_gas = gas::init_code_cost(size);
let charged_gas = match create_type {
CreateType::Create => gas::CREATE + memory_expansion.expansion_cost + init_code_gas,
CreateType::Create => gas::CREATE
.checked_add(memory_expansion.expansion_cost)
.ok_or(EVMError::OutOfGas)?
.checked_add(init_code_gas)
.ok_or(EVMError::OutOfGas)?,
CreateType::Create2 => {
let calldata_words = bytes_32_words_size(size);
gas::CREATE
+ gas::KECCAK256WORD * calldata_words.into()
+ memory_expansion.expansion_cost
+ init_code_gas
.checked_add(gas::KECCAK256WORD * calldata_words.into())
.ok_or(EVMError::OutOfGas)?
.checked_add(memory_expansion.expansion_cost)
.ok_or(EVMError::OutOfGas)?
.checked_add(init_code_gas)
.ok_or(EVMError::OutOfGas)?
},
};
self.charge_gas(charged_gas)?;
Expand Down
3 changes: 2 additions & 1 deletion crates/evm/src/gas.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::errors::EVMError;
use utils::eth_transaction::common::TxKindTrait;
use utils::eth_transaction::eip2930::{AccessListItem};
use utils::eth_transaction::transaction::{Transaction, TransactionTrait};
use utils::helpers::bytes_32_words_size;
use utils::helpers;

//! Gas costs for EVM operations
Expand Down Expand Up @@ -153,7 +154,7 @@ pub fn calculate_message_call_gas(
/// * `total_gas_cost` - The gas cost for storing data in memory.
pub fn calculate_memory_gas_cost(size_in_bytes: usize) -> u64 {
let _512: NonZero<u64> = 512_u64.try_into().unwrap();
let size_in_words = (size_in_bytes + 31) / 32;
let size_in_words = bytes_32_words_size(size_in_bytes);
let linear_cost = size_in_words.into() * MEMORY;

let (q0, r0) = DivRem::div_rem(size_in_words.into(), _512);
Expand Down
3 changes: 2 additions & 1 deletion crates/evm/src/instructions/duplication_operations.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ use crate::stack::StackTrait;

/// Generic DUP operation
#[inline(always)]
fn exec_dup_i(ref self: VM, i: u8) -> Result<(), EVMError> {
fn exec_dup_i(ref self: VM, i: NonZero<u8>) -> Result<(), EVMError> {
self.charge_gas(gas::VERYLOW)?;
let i: u8 = i.into();
let item = self.stack.peek_at((i - 1).into())?;
self.stack.push(item)
}
Expand Down
51 changes: 39 additions & 12 deletions crates/evm/src/instructions/environmental_information.cairo
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use core::num::traits::OverflowingAdd;
use core::num::traits::Zero;
use core::num::traits::{CheckedAdd, CheckedSub};
use crate::errors::{ensure, EVMError};
use crate::gas;
use crate::memory::MemoryTrait;
Expand Down Expand Up @@ -79,12 +80,17 @@ pub impl EnvironmentInformationImpl of EnvironmentInformationTrait {
let calldata_len = calldata.len();

// All bytes after the end of the calldata are set to 0.
if offset >= calldata_len {
return self.stack.push(0);
}
let bytes_len = match calldata_len.checked_sub(offset) {
Option::None => { return self.stack.push(0); },
Option::Some(remaining_len) => {
if remaining_len == 0 {
return self.stack.push(0);
}
core::cmp::min(32, remaining_len)
}
};

// Slice the calldata
let bytes_len = core::cmp::min(32, calldata_len - offset);
let sliced = calldata.slice(offset, bytes_len);

let mut data_to_load: u256 = sliced
Expand Down Expand Up @@ -122,7 +128,13 @@ pub impl EnvironmentInformationImpl of EnvironmentInformationTrait {
self.memory.size(), [(dest_offset, size)].span()
)?;
self.memory.ensure_length(memory_expansion.new_size);
self.charge_gas(gas::VERYLOW + copy_gas_cost + memory_expansion.expansion_cost)?;

let total_cost = gas::VERYLOW
.checked_add(copy_gas_cost)
.ok_or(EVMError::OutOfGas)?
.checked_add(memory_expansion.expansion_cost)
.ok_or(EVMError::OutOfGas)?;
self.charge_gas(total_cost)?;

let calldata: Span<u8> = self.message().data;
copy_bytes_to_memory(ref self, calldata, dest_offset, offset, size);
Expand Down Expand Up @@ -152,7 +164,13 @@ pub impl EnvironmentInformationImpl of EnvironmentInformationTrait {
self.memory.size(), [(dest_offset, size)].span()
)?;
self.memory.ensure_length(memory_expansion.new_size);
self.charge_gas(gas::VERYLOW + copy_gas_cost + memory_expansion.expansion_cost)?;

let total_cost = gas::VERYLOW
.checked_add(copy_gas_cost)
.ok_or(EVMError::OutOfGas)?
.checked_add(memory_expansion.expansion_cost)
.ok_or(EVMError::OutOfGas)?;
self.charge_gas(total_cost)?;

let bytecode: Span<u8> = self.message().code;

Expand Down Expand Up @@ -208,7 +226,12 @@ pub impl EnvironmentInformationImpl of EnvironmentInformationTrait {
self.accessed_addresses.add(evm_address);
gas::COLD_ACCOUNT_ACCESS_COST
};
self.charge_gas(access_gas_cost + copy_gas_cost + memory_expansion.expansion_cost)?;
let total_cost = access_gas_cost
.checked_add(copy_gas_cost)
.ok_or(EVMError::OutOfGas)?
.checked_add(memory_expansion.expansion_cost)
.ok_or(EVMError::OutOfGas)?;
self.charge_gas(total_cost)?;

let bytecode = self.env.state.get_account(evm_address).code;
copy_bytes_to_memory(ref self, bytecode, dest_offset, offset, size);
Expand Down Expand Up @@ -246,7 +269,12 @@ pub impl EnvironmentInformationImpl of EnvironmentInformationTrait {
self.memory.size(), [(dest_offset, size)].span()
)?;
self.memory.ensure_length(memory_expansion.new_size);
self.charge_gas(gas::VERYLOW + copy_gas_cost + memory_expansion.expansion_cost)?;
let total_cost = gas::VERYLOW
.checked_add(copy_gas_cost)
.ok_or(EVMError::OutOfGas)?
.checked_add(memory_expansion.expansion_cost)
.ok_or(EVMError::OutOfGas)?;
self.charge_gas(total_cost)?;

let data_to_copy: Span<u8> = return_data.slice(offset, size);
self.memory.store_n(data_to_copy, dest_offset);
Expand Down Expand Up @@ -287,10 +315,9 @@ pub impl EnvironmentInformationImpl of EnvironmentInformationTrait {
fn copy_bytes_to_memory(
ref self: VM, bytes: Span<u8>, dest_offset: usize, offset: usize, size: usize
) {
let bytes_slice = if offset < bytes.len() {
bytes.slice(offset, core::cmp::min(size, bytes.len() - offset))
} else {
[].span()
let bytes_slice = match bytes.len().checked_sub(offset) {
Option::Some(remaining) => bytes.slice(offset, core::cmp::min(size, remaining)),
Option::None => [].span()
};

self.memory.store_padded_segment(dest_offset, size, bytes_slice);
Expand Down
16 changes: 9 additions & 7 deletions crates/evm/src/instructions/logging_operations.cairo
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Logging Operations.

use core::num::traits::CheckedAdd;
use crate::errors::{EVMError, ensure};
use crate::gas;
use crate::memory::MemoryTrait;
Expand Down Expand Up @@ -68,13 +69,14 @@ fn exec_log_i(ref self: VM, topics_len: u8) -> Result<(), EVMError> {
self.memory.ensure_length(memory_expansion.new_size);

// TODO: avoid addition overflows here. We should use checked arithmetic.
self
.charge_gas(
gas::LOG
+ topics_len.into() * gas::LOGTOPIC
+ size.into() * gas::LOGDATA
+ memory_expansion.expansion_cost
)?;
let total_cost = gas::LOG
.checked_add(topics_len.into() * gas::LOGTOPIC)
.ok_or(EVMError::OutOfGas)?
.checked_add(size.into() * gas::LOGDATA)
.ok_or(EVMError::OutOfGas)?
.checked_add(memory_expansion.expansion_cost)
.ok_or(EVMError::OutOfGas)?;
self.charge_gas(total_cost)?;

let mut data: Array<u8> = Default::default();
self.memory.load_n(size, ref data, offset);
Expand Down
25 changes: 19 additions & 6 deletions crates/evm/src/instructions/memory_operations.cairo
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use core::cmp::max;
use core::num::traits::CheckedAdd;
use crate::backend::starknet_backend::fetch_original_storage;
//! Stack Memory Storage and Flow Operations.
use crate::errors::{EVMError, ensure};
Expand All @@ -9,7 +10,6 @@ use crate::stack::StackTrait;
use crate::state::StateTrait;
use utils::helpers::bytes_32_words_size;
use utils::set::SetTrait;

#[inline(always)]
fn jump(ref self: VM, index: usize) -> Result<(), EVMError> {
match self.message().code.get(index) {
Expand Down Expand Up @@ -42,7 +42,10 @@ pub impl MemoryOperation of MemoryOperationTrait {

let memory_expansion = gas::memory_expansion(self.memory.size(), [(offset, 32)].span())?;
self.memory.ensure_length(memory_expansion.new_size);
self.charge_gas(gas::VERYLOW + memory_expansion.expansion_cost)?;
let total_cost = gas::VERYLOW
.checked_add(memory_expansion.expansion_cost)
.ok_or(EVMError::OutOfGas)?;
self.charge_gas(total_cost)?;

let result = self.memory.load(offset);
self.stack.push(result)
Expand All @@ -58,7 +61,10 @@ pub impl MemoryOperation of MemoryOperationTrait {
let value: u256 = self.stack.pop()?;
let memory_expansion = gas::memory_expansion(self.memory.size(), [(offset, 32)].span())?;
self.memory.ensure_length(memory_expansion.new_size);
self.charge_gas(gas::VERYLOW + memory_expansion.expansion_cost)?;
let total_cost = gas::VERYLOW
.checked_add(memory_expansion.expansion_cost)
.ok_or(EVMError::OutOfGas)?;
self.charge_gas(total_cost)?;

self.memory.store(value, offset);
Result::Ok(())
Expand All @@ -74,7 +80,10 @@ pub impl MemoryOperation of MemoryOperationTrait {

let memory_expansion = gas::memory_expansion(self.memory.size(), [(offset, 1)].span())?;
self.memory.ensure_length(memory_expansion.new_size);
self.charge_gas(gas::VERYLOW + memory_expansion.expansion_cost)?;
let total_cost = gas::VERYLOW
.checked_add(memory_expansion.expansion_cost)
.ok_or(EVMError::OutOfGas)?;
self.charge_gas(total_cost)?;

self.memory.store_byte(value, offset);

Expand Down Expand Up @@ -294,8 +303,12 @@ pub impl MemoryOperation of MemoryOperationTrait {
self.memory.size(), [(max(dest_offset, source_offset), size)].span()
)?;
self.memory.ensure_length(memory_expansion.new_size);
//TODO: handle add overflows
self.charge_gas(gas::VERYLOW + copy_gas_cost + memory_expansion.expansion_cost)?;
let total_cost = gas::VERYLOW
.checked_add(copy_gas_cost)
.ok_or(EVMError::OutOfGas)?
.checked_add(memory_expansion.expansion_cost)
.ok_or(EVMError::OutOfGas)?;
self.charge_gas(total_cost)?;

if size == 0 {
return Result::Ok(());
Expand Down
9 changes: 7 additions & 2 deletions crates/evm/src/instructions/sha3.cairo
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use core::cmp::min;
//! SHA3.
use core::keccak::{cairo_keccak};
use core::num::traits::CheckedAdd;

// Internal imports
use crate::errors::EVMError;
Expand All @@ -11,7 +12,6 @@ use crate::stack::StackTrait;
use utils::helpers::bytes_32_words_size;
use utils::traits::array::ArrayExtTrait;
use utils::traits::integer::U256Trait;

#[generate_trait]
pub impl Sha3Impl of Sha3Trait {
/// SHA3 operation : Hashes n bytes in memory at a given offset in memory
Expand All @@ -32,7 +32,12 @@ pub impl Sha3Impl of Sha3Trait {
let word_gas_cost = gas::KECCAK256WORD * words_size;
let memory_expansion = gas::memory_expansion(self.memory.size(), [(offset, size)].span())?;
self.memory.ensure_length(memory_expansion.new_size);
self.charge_gas(gas::KECCAK256 + word_gas_cost + memory_expansion.expansion_cost)?;
let total_cost = gas::KECCAK256
.checked_add(word_gas_cost)
.ok_or(EVMError::OutOfGas)?
.checked_add(memory_expansion.expansion_cost)
.ok_or(EVMError::OutOfGas)?;
self.charge_gas(total_cost)?;

let mut to_hash: Array<u64> = Default::default();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Stop and Arithmetic Operations.
use core::integer::{u512_safe_div_rem_by_u256};
use core::math::u256_mul_mod_n;
use core::num::traits::CheckedAdd;
use core::num::traits::{OverflowingAdd, OverflowingMul, OverflowingSub};
use crate::errors::EVMError;
use crate::gas;
Expand Down Expand Up @@ -164,7 +165,6 @@ pub impl StopAndArithmeticOperations of StopAndArithmeticOperationsTrait {

let result: u256 = match TryInto::<u256, NonZero<u256>>::try_into(n) {
Option::Some(nonzero_n) => {
// This is more gas efficient than computing (a mod N) + (b mod N) mod N
let sum = u256_wide_add(a, b);
let (_, r) = u512_safe_div_rem_by_u256(sum, nonzero_n);
r
Expand Down Expand Up @@ -204,7 +204,10 @@ pub impl StopAndArithmeticOperations of StopAndArithmeticOperationsTrait {

// Gas
let bytes_used = exponent.bytes_used();
self.charge_gas(gas::EXP + gas::EXP_GAS_PER_BYTE * bytes_used.into())?;
let total_cost = gas::EXP
.checked_add(gas::EXP_GAS_PER_BYTE * bytes_used.into())
.ok_or(EVMError::OutOfGas)?;
self.charge_gas(total_cost)?;

let result = base.wrapping_pow(exponent);

Expand Down Expand Up @@ -238,6 +241,7 @@ pub impl StopAndArithmeticOperations of StopAndArithmeticOperationsTrait {

let result = if b < 32 {
let s = 8 * b + 7;
//TODO: use POW_2 table for optimization
let two_pow_s = 2.pow(s);
// Get v, the t-th bit of x. To do this we bitshift x by s bits to the right and apply a
// mask to get the last bit.
Expand Down
12 changes: 10 additions & 2 deletions crates/evm/src/instructions/system_operations.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,11 @@ pub impl SystemOperations of SystemOperationsTrait {
memory_expansion.expansion_cost,
access_gas_cost + transfer_gas_cost + create_gas_cost
)?;
self.charge_gas(message_call_gas.cost + memory_expansion.expansion_cost)?;
let total_cost = message_call_gas
.cost
.checked_add(memory_expansion.expansion_cost)
.ok_or(EVMError::OutOfGas)?;
self.charge_gas(total_cost)?;
// Only the transfer gas is left to charge.

let read_only = self.message().read_only;
Expand Down Expand Up @@ -213,7 +217,11 @@ pub impl SystemOperations of SystemOperationsTrait {
let message_call_gas = gas::calculate_message_call_gas(
0, gas, self.gas_left(), memory_expansion.expansion_cost, access_gas_cost
)?;
self.charge_gas(message_call_gas.cost + memory_expansion.expansion_cost)?;
let total_cost = message_call_gas
.cost
.checked_add(memory_expansion.expansion_cost)
.ok_or(EVMError::OutOfGas)?;
self.charge_gas(total_cost)?;

self
.generic_call(
Expand Down
2 changes: 1 addition & 1 deletion crates/evm/src/interpreter.cairo
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use contracts::kakarot_core::KakarotCore;
use contracts::kakarot_core::interface::IKakarotCore;
use core::num::traits::{Bounded, Zero};
use core::num::traits::Zero;
use core::ops::SnapshotDeref;
use core::starknet::EthAddress;
use core::starknet::storage::{StoragePointerReadAccess};
Expand Down
5 changes: 3 additions & 2 deletions crates/evm/src/precompiles/identity.cairo
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use core::starknet::EthAddress;
use crate::errors::EVMError;
use crate::precompiles::Precompile;
use utils::helpers::bytes_32_words_size;

const BASE_COST: u64 = 15;
const COST_PER_WORD: u64 = 3;
Expand All @@ -12,8 +13,8 @@ pub impl Identity of Precompile {
}

fn exec(input: Span<u8>) -> Result<(u64, Span<u8>), EVMError> {
let data_word_size = ((input.len() + 31) / 32).into();
let gas = BASE_COST + data_word_size * COST_PER_WORD;
let data_word_size = bytes_32_words_size(input.len());
let gas = BASE_COST + data_word_size.into() * COST_PER_WORD;

return Result::Ok((gas, input));
}
Expand Down
Loading

0 comments on commit c7806ad

Please sign in to comment.