Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions execution_engine_testing/tests/src/test/explorer/faucet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -663,14 +663,14 @@ fn faucet_costs() {
// This test will fail if execution costs vary. The expected costs should not be updated
// without understanding why the cost has changed. If the costs do change, it should be
// reflected in the "Costs by Entry Point" section of the faucet crate's README.md.
const EXPECTED_FAUCET_INSTALL_COST: u64 = 118_835_785_065;
const EXPECTED_FAUCET_INSTALL_COST: u64 = 119_810_320_929;
const EXPECTED_FAUCET_INSTALL_COST_ALT: u64 = 149_230_872_143;

const EXPECTED_FAUCET_SET_VARIABLES_COST: u64 = 79_790_440;
const EXPECTED_FAUCET_SET_VARIABLES_COST: u64 = 79_749_265;

const EXPECTED_FAUCET_CALL_BY_INSTALLER_COST: u64 = 2_652_954_573;
const EXPECTED_FAUCET_CALL_BY_INSTALLER_COST: u64 = 2_652_913_398;

const EXPECTED_FAUCET_CALL_BY_USER_COST: u64 = 2_558_820_996;
const EXPECTED_FAUCET_CALL_BY_USER_COST: u64 = 2_558_746_881;

let installer_account = AccountHash::new([1u8; 32]);
let user_account: AccountHash = AccountHash::new([2u8; 32]);
Expand Down
51 changes: 51 additions & 0 deletions executor/evm/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

use casper_storage::{
global_state::{error::Error as GlobalStateError, state::StateReader},
tracking_copy::TrackingCopyExt,
TrackingCopy,
};
use casper_types::{evm, CLValue, EvmAddr, Key, StoredValue, U512};
use revm::{
database_interface::Database,
interpreter::{Gas, InstructionResult, InterpreterResult},
primitives::{Address, Bytes, StorageKey, StorageValue, B256, U256},
state::{AccountInfo, Bytecode},
};
Expand Down Expand Up @@ -46,6 +48,55 @@ where
None => Ok(U256::ZERO),
}
}

/// Executes the native EIP-4788 `get` operation.
///
/// The lookup is backed only by the current tracking copy; EVM execution must not depend on
/// locally retained block history.
pub(crate) fn eip4788_get(
&mut self,
input: &[u8],
gas_limit: u64,
reservoir: u64,
) -> Result<InterpreterResult, DbError> {
let revert = || InterpreterResult {
result: InstructionResult::Revert,
output: Bytes::new(),
gas: Gas::new_with_regular_gas_and_reservoir(gas_limit, reservoir),
};

// EIP-4788 accepts one uint256 timestamp. Values wider than u64 must revert rather
// than truncate to a colliding ring-buffer timestamp.
if input.len() != 32 || input[..24].iter().any(|byte| *byte != 0) {
return Ok(revert());
}

let timestamp = u64::from_be_bytes(
input[24..]
.try_into()
.expect("the final 8 bytes of a 32-byte input have fixed length"),
);
if timestamp == 0 {
return Ok(revert());
}

let Some((stored_timestamp, block_hash)) =
self.tracking_copy.get_eip4788_parent_hash(timestamp)?
else {
return Ok(revert());
};
if stored_timestamp != timestamp {
return Ok(revert());
}

Ok(InterpreterResult {
result: InstructionResult::Return,
output: Bytes::copy_from_slice(block_hash.as_ref()),
// Native EIP-4788 reads have no interpreted bytecode or SLOAD cost. Retain the call
// frame's reservoir so EIP-8037 accounting remains unchanged.
gas: Gas::new_with_regular_gas_and_reservoir(gas_limit, reservoir),
})
}
}

impl<R, B> Database for CasperDb<'_, R, B>
Expand Down
24 changes: 21 additions & 3 deletions executor/evm/src/precompiles.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
//! Casper EVM precompile provider.

use casper_storage::global_state::{error::Error as GlobalStateError, state::StateReader};
use casper_storage::{
eip4788::BEACON_ROOTS_ADDRESS,
global_state::{error::Error as GlobalStateError, state::StateReader},
};
use casper_types::{Key, StoredValue};
use revm::{
context_interface::{Cfg, ContextTr},
handler::{EthPrecompiles, PrecompileProvider},
interpreter::{CallInputs, InterpreterResult},
interpreter::{CallInputs, CallScheme, InterpreterResult},
primitives::{hardfork::SpecId, Address},
};

use crate::{db::CasperDb, BlockHashProvider};
use crate::{db::CasperDb, tx, BlockHashProvider};

/// Ethereum precompiles executing with access to Casper-backed state.
#[derive(Clone, Debug)]
Expand Down Expand Up @@ -39,6 +42,21 @@ where
context: &mut CTX,
inputs: &CallInputs,
) -> Result<Option<Self::Output>, String> {
let beacon_roots_address = tx::to_revm_address(BEACON_ROOTS_ADDRESS);
if inputs.target_address == beacon_roots_address
&& inputs.bytecode_address == beacon_roots_address
&& matches!(inputs.scheme, CallScheme::Call | CallScheme::StaticCall)
{
// Copy the input before borrowing the database mutably. The input may be backed by
// revm's shared memory buffer.
let input = inputs.input.bytes(context);
let result = context
.db_mut()
.eip4788_get(&input, inputs.gas_limit, inputs.reservoir)
.map_err(|error| error.to_string())?;
return Ok(Some(result));
}

<EthPrecompiles as PrecompileProvider<CTX>>::run(&mut self.0, context, inputs)
}

Expand Down
Loading
Loading