Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CANCUN hard fork impelemtation #39

Merged
merged 22 commits into from
Jun 11, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
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
11 changes: 6 additions & 5 deletions benches/loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,18 @@ fn run_loop_contract() {
chain_id: U256::one(),
block_base_fee_per_gas: U256::zero(),
block_randomness: None,
blob_base_fee: None,
};

let mut state = BTreeMap::new();
state.insert(
H160::from_str("0x1000000000000000000000000000000000000000").unwrap(),
MemoryAccount {
nonce: U256::one(),
balance: U256::from(10000000),
storage: BTreeMap::new(),
code: hex::decode("6080604052348015600f57600080fd5b506004361060285760003560e01c80630f14a40614602d575b600080fd5b605660048036036020811015604157600080fd5b8101908080359060200190929190505050606c565b6040518082815260200191505060405180910390f35b6000806000905060005b83811015608f5760018201915080806001019150506076565b508091505091905056fea26469706673582212202bc9ec597249a9700278fe4ce78da83273cb236e76d4d6797b441454784f901d64736f6c63430007040033").unwrap(),
}
nonce: U256::one(),
balance: U256::from(10000000),
storage: BTreeMap::new(),
code: hex::decode("6080604052348015600f57600080fd5b506004361060285760003560e01c80630f14a40614602d575b600080fd5b605660048036036020811015604157600080fd5b8101908080359060200190929190505050606c565b6040518082815260200191505060405180910390f35b6000806000905060005b83811015608f5760018201915080806001019150506076565b508091505091905056fea26469706673582212202bc9ec597249a9700278fe4ce78da83273cb236e76d4d6797b441454784f901d64736f6c63430007040033").unwrap(),
},
);
state.insert(
H160::from_str("0xf000000000000000000000000000000000000000").unwrap(),
Expand Down
2 changes: 1 addition & 1 deletion core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ mod external;
mod memory;
mod opcode;
mod stack;
mod utils;
pub mod utils;
mod valids;

pub use crate::error::{Capture, ExitError, ExitFatal, ExitReason, ExitRevert, ExitSucceed, Trap};
Expand Down
25 changes: 25 additions & 0 deletions core/src/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,31 @@ impl Memory {
Ok(())
}

/// Copy memory region form `src` to `dst` with length.
/// `copy_within` used `memmove` to avoid DoS attacks.
mrLSD marked this conversation as resolved.
Show resolved Hide resolved
pub fn copy(
&mut self,
src_offset: usize,
dst_offset: usize,
length: usize,
mrLSD marked this conversation as resolved.
Show resolved Hide resolved
) -> Result<(), ExitFatal> {
// Get maximum offset
let offset = core::cmp::max(src_offset, dst_offset);
let offset_length = offset.checked_add(length).ok_or(ExitFatal::NotSupported)?;
aleksuss marked this conversation as resolved.
Show resolved Hide resolved
if offset_length > self.limit {
return Err(ExitFatal::NotSupported);
}

// Resize data memory
if self.data.len() < offset_length {
self.data.resize(offset_length, 0);
}

self.data
.copy_within(src_offset..src_offset + length, dst_offset);
Ok(())
}

/// Copy `data` into the memory, of given `len`.
pub fn copy_large(
&mut self,
Expand Down
11 changes: 11 additions & 0 deletions core/src/opcode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,13 @@ impl Opcode {
/// `JUMPDEST`
pub const JUMPDEST: Opcode = Opcode(0x5b);

/// `TLOAD` - EIP-1153
pub const TLOAD: Opcode = Opcode(0x5c);
/// `TSTORE` - EIP-1153
pub const TSTORE: Opcode = Opcode(0x5d);
/// `MCOPY` - EIP-5656
pub const MCOPY: Opcode = Opcode(0x5e);

/// `PUSHn`
pub const PUSH0: Opcode = Opcode(0x5f);
pub const PUSH1: Opcode = Opcode(0x60);
Expand Down Expand Up @@ -191,6 +198,10 @@ impl Opcode {
pub const SELFBALANCE: Opcode = Opcode(0x47);
/// `BASEFEE`
pub const BASEFEE: Opcode = Opcode(0x48);
/// `BLOBHASH` - EIP-4844
pub const BLOBHASH: Opcode = Opcode(0x49);
/// `BLOBBASEFEE` - EIP-7516
pub const BLOBBASEFEE: Opcode = Opcode(0x4a);
/// `ORIGIN`
pub const ORIGIN: Opcode = Opcode(0x32);
/// `CALLER`
Expand Down
74 changes: 72 additions & 2 deletions core/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,78 @@ use core::cmp::Ordering;
use core::ops::{Div, Rem};
use primitive_types::U256;

/// EIP-4844 constants
/// Gas consumption of a single data blob (== blob byte size).
pub const GAS_PER_BLOB: u64 = 1 << 17;
/// Target number of the blob per block.
pub const TARGET_BLOB_NUMBER_PER_BLOCK: u64 = 3;
/// Max number of blobs per block
pub const MAX_BLOB_NUMBER_PER_BLOCK: u64 = 2 * TARGET_BLOB_NUMBER_PER_BLOCK;
/// Maximum consumable blob gas for data blobs per block.
pub const MAX_BLOB_GAS_PER_BLOCK: u64 = MAX_BLOB_NUMBER_PER_BLOCK * GAS_PER_BLOB;
/// Target consumable blob gas for data blobs per block (for 1559-like pricing).
pub const TARGET_BLOB_GAS_PER_BLOCK: u64 = TARGET_BLOB_NUMBER_PER_BLOCK * GAS_PER_BLOB;
/// Minimum gas price for data blobs.
pub const MIN_BLOB_GASPRICE: u64 = 1;
mrLSD marked this conversation as resolved.
Show resolved Hide resolved
/// Controls the maximum rate of change for blob gas price.
pub const BLOB_GASPRICE_UPDATE_FRACTION: u64 = 3338477;
mrLSD marked this conversation as resolved.
Show resolved Hide resolved
/// First version of the blob.
pub const VERSIONED_HASH_VERSION_KZG: u8 = 0x01;

/// Calculates the `excess_blob_gas` from the parent header's `blob_gas_used` and `excess_blob_gas`.
///
/// See also [the EIP-4844 helpers]<https://eips.ethereum.org/EIPS/eip-4844#helpers>
/// (`calc_excess_blob_gas`).
#[inline]
pub const fn calc_excess_blob_gas(parent_excess_blob_gas: u64, parent_blob_gas_used: u64) -> u64 {
(parent_excess_blob_gas + parent_blob_gas_used).saturating_sub(TARGET_BLOB_GAS_PER_BLOCK)
}

/// Approximates `factor * e ** (numerator / denominator)` using Taylor expansion.
///
/// This is used to calculate the blob price.
///
/// See also [the EIP-4844 helpers](https://eips.ethereum.org/EIPS/eip-4844#helpers)
/// (`fake_exponential`).
///
/// # Panics
///
/// This function panics if `denominator` is zero.
#[inline]
pub fn fake_exponential(factor: u64, numerator: u64, denominator: u64) -> u128 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I worry about overflow in this function.

The implementation in the EIP is written in Python which uses arbitrary precision integers. The geth implementation uses arbitrary precision integers as well.

The Erigon implementation uses 256-bit integers, but still checks for overflow and returns an error in that case. I think we should follow this example and also use U256 with overflow-checked arithmetic.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The source is here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But I agree, it seems like should be refactored.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually... this function can be removed. As it's for test KZG-precompiles

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure if it's not needed then please remove.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done: 12b1dd0

assert_ne!(denominator, 0, "attempt to divide by zero");
let factor = factor as u128;
let numerator = numerator as u128;
let denominator = denominator as u128;

let mut i = 1;
let mut output = 0;
let mut numerator_accum = factor * denominator;
while numerator_accum > 0 {
output += numerator_accum;

// Denominator is asserted as not zero at the start of the function.
numerator_accum = (numerator_accum * numerator) / (denominator * i);
i += 1;
}
output / denominator
}

/// Calculates the blob gas price from the header's excess blob gas field.
///
/// See also [the EIP-4844 helpers](https://eips.ethereum.org/EIPS/eip-4844#helpers)
/// (`get_blob_gasprice`).
#[inline]
pub fn calc_blob_gasprice(excess_blob_gas: u64) -> u128 {
fake_exponential(
MIN_BLOB_GASPRICE,
excess_blob_gas,
BLOB_GASPRICE_UPDATE_FRACTION,
)
}

#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum Sign {
pub(crate) enum Sign {
Plus,
Minus,
Zero,
Expand All @@ -17,7 +87,7 @@ const SIGN_BIT_MASK: U256 = U256([
]);

#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub struct I256(pub Sign, pub U256);
pub(crate) struct I256(pub Sign, pub U256);

impl I256 {
/// Zero value of I256.
Expand Down
4 changes: 4 additions & 0 deletions evm-tests/ethjson/src/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,9 @@ pub struct Env {
#[serde(rename = "currentRandom")]
#[serde(default)]
pub random: Option<Uint>,
/// EIP-7516: Blob base fee
#[serde(default)]
pub blob_base_fee: Option<u128>,
}

#[cfg(test)]
Expand Down Expand Up @@ -199,6 +202,7 @@ mod tests {
timestamp: Uint(1.into()),
block_base_fee_per_gas: Uint(0.into()),
random: Some(Uint(1.into())),
blob_base_fee: None,
}
);
assert_eq!(
Expand Down
1 change: 1 addition & 0 deletions evm-tests/jsontests/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ impl Test {
chain_id: U256::one(),
block_base_fee_per_gas,
block_randomness,
blob_base_fee: self.0.env.blob_base_fee,
})
}
}
Expand Down
1 change: 1 addition & 0 deletions evm-tests/jsontests/src/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ impl Test {
chain_id: U256::zero(),
block_base_fee_per_gas: self.0.transaction.gas_price.into(),
block_randomness,
blob_base_fee: None,
}
}

Expand Down
4 changes: 4 additions & 0 deletions gasometer/src/costs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,10 @@ pub const fn sload_cost(is_cold: bool, config: &Config) -> u64 {
}
}

pub const fn storage_read_warm(config: &Config) -> u64 {
config.gas_storage_read_warm
}

#[allow(clippy::collapsible_else_if)]
pub fn sstore_cost(
original: H256,
Expand Down
22 changes: 21 additions & 1 deletion gasometer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ extern crate alloc;
pub mod prelude {
pub use alloc::vec::Vec;
}

#[cfg(feature = "std")]
pub mod prelude {
pub use std::vec::Vec;
Expand Down Expand Up @@ -527,7 +528,7 @@ pub fn static_opcode_cost(opcode: Opcode) -> Option<u32> {
}

/// Calculate the opcode cost.
#[allow(clippy::nonminimal_bool)]
#[allow(clippy::nonminimal_bool, clippy::cognitive_complexity)]
pub fn dynamic_opcode_cost<H: Handler>(
address: H160,
opcode: Opcode,
Expand Down Expand Up @@ -557,6 +558,23 @@ pub fn dynamic_opcode_cost<H: Handler>(
Opcode::BASEFEE if config.has_base_fee => GasCost::Base,
Opcode::BASEFEE => GasCost::Invalid(opcode),

Opcode::BLOBBASEFEE if config.has_blob_base_fee => GasCost::Base,
Opcode::BLOBBASEFEE => GasCost::Invalid(opcode),

Opcode::BLOBHASH if config.has_shard_blob_transactions => GasCost::VeryLow,
Opcode::BLOBHASH => GasCost::Invalid(opcode),

Opcode::TLOAD if config.has_transient_storage => GasCost::WarmStorageRead,
Opcode::TLOAD => GasCost::Invalid(opcode),

Opcode::TSTORE if !is_static && config.has_transient_storage => GasCost::WarmStorageRead,
Opcode::TSTORE => GasCost::Invalid(opcode),

Opcode::MCOPY if config.has_mcopy => GasCost::VeryLowCopy {
len: stack.peek(2)?,
},
Opcode::MCOPY => GasCost::Invalid(opcode),

Opcode::EXTCODESIZE => {
let target = stack.peek_h256(0)?.into();
storage_target = StorageTarget::Address(target);
Expand Down Expand Up @@ -915,6 +933,7 @@ impl<'config> Inner<'config> {
self.config.gas_ext_code_hash,
self.config,
),
GasCost::WarmStorageRead => costs::storage_read_warm(self.config),
})
}

Expand Down Expand Up @@ -1071,6 +1090,7 @@ pub enum GasCost {
/// True if target has not been previously accessed in this transaction
target_is_cold: bool,
},
WarmStorageRead,
}

/// Storage opcode will access. Used for tracking accessed storage (EIP-2929).
Expand Down
7 changes: 6 additions & 1 deletion runtime/src/eval/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ pub fn eval<H: Handler>(state: &mut Runtime, opcode: Opcode, handler: &mut H) ->
Opcode::LOG2 => system::log(state, 2, handler),
Opcode::LOG3 => system::log(state, 3, handler),
Opcode::LOG4 => system::log(state, 4, handler),
Opcode::SUICIDE => system::suicide(state, handler),
Opcode::SUICIDE => system::selfdestruct(state, handler),
Opcode::CREATE => system::create(state, false, handler),
Opcode::CREATE2 => system::create(state, true, handler),
Opcode::CALL => system::call(state, CallScheme::Call, handler),
Expand All @@ -59,6 +59,11 @@ 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::BLOBBASEFEE => system::blob_base_fee(state, handler),
Opcode::BLOBHASH => system::blob_hash(state, handler),
Opcode::TLOAD => system::tload(state, handler),
Opcode::TSTORE => system::tstore(state, handler),
Opcode::MCOPY => system::mcopy(state, handler),
_ => handle_other(state, opcode, handler),
}
}
Expand Down
Loading