Skip to content
Open
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,460 changes: 1,242 additions & 218 deletions Cargo.lock

Large diffs are not rendered by default.

20 changes: 19 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ members = [
"crates/context",
"crates/context/interface",
"crates/handler",
# supra extensions
"crates/supra-extension",

# variants
"crates/op-revm",
Expand All @@ -35,6 +37,7 @@ members = [
"examples/my_evm",
"examples/custom_opcodes",
"examples/custom_precompile_journal",

]
resolver = "2"
default-members = ["crates/revm"]
Expand All @@ -56,6 +59,7 @@ context-interface = { path = "crates/context/interface", package = "revm-context
handler = { path = "crates/handler", package = "revm-handler", version = "10.0.0", default-features = false }
op-revm = { path = "crates/op-revm", package = "op-revm", version = "10.0.0", default-features = false }
ee-tests = { path = "crates/ee-tests", package = "revm-ee-tests", version = "0.1.0", default-features = false }
supra-extension = { path = "crates/supra-extension", package = "revm-supra-extension", version = "0.1.0", default-features = false }

# alloy
alloy-eip2930 = { version = "0.2.1", default-features = false }
Expand All @@ -72,6 +76,18 @@ alloy-signer = { version = "1.0.12", default-features = false }
alloy-signer-local = { version = "1.0.12", default-features = false }
alloy-transport = { version = "1.0.12", default-features = false }

alloy-contract = { version = "1.0.19"}
alloy = { version = "1.0.19", features = ["sol-types", "contract"] }
alloy-serde = { version = "1.0.19" }

# libraries required to build supra-contract bindings
# For more detaisl see crates/supra-extension/build.rs
#forge = { git = "https://github.com/foundry-rs/foundry.git", tag="v1.4.1"}
#alloy-chains = { version = "0.2.13" }
#shlex = { version = "1.3.0" }
foundry-compilers = "0.19.14"
toml = { version = "0.9.8"}

# precompiles
ark-bls12-381 = { version = "0.5", default-features = false }
ark-bn254 = { version = "0.5", default-features = false }
Expand Down Expand Up @@ -103,7 +119,7 @@ criterion = { package = "codspeed-criterion-compat", version = "2.10" }

# serde
serde = { version = "1.0", default-features = false }
serde_json = { version = "1.0", default-features = false }
serde_json = { version = "1.0.149", default-features = false }

# misc
auto_impl = "1.3.0"
Expand All @@ -113,6 +129,8 @@ derive-where = { version = "1.5.0", default-features = false }
rand = "0.9"
tokio = "1.45"
either = { version = "1.15.0", default-features = false }
derive_more = { version = "2.0.1" }
derive-getters = { version = "0.5.0" }

# dev-dependencies
anyhow = "1.0.98"
Expand Down
46 changes: 45 additions & 1 deletion crates/context/interface/src/cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,50 @@ use core::fmt::Debug;
use core::hash::Hash;
use primitives::{hardfork::SpecId, Address, TxKind, U256};

/// Describes execution context of the transaction.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, Eq, PartialEq, Default)]
pub enum ExecutionMode {
#[default]
/// Executing user submitted transaction.
User,
/// Executing automated transaction.
Automated,
/// Executing governance sponsored automated transaction.
AutomatedGasless,
/// Executing governance native transaction.
System,
/// When transactions are executed in genesis mode.
Genesis,
}

impl ExecutionMode {
/// Returns true if gas should be charged for execution.
pub fn charges_gas(&self) -> bool {
match self {
ExecutionMode::User | ExecutionMode::Automated => true,
ExecutionMode::AutomatedGasless | ExecutionMode::System | ExecutionMode::Genesis => {
false
}
}
}

/// Returns true if nonce should be updated in case of successful execution.
pub fn updates_nonce(&self) -> bool {
matches!(self, ExecutionMode::User | ExecutionMode::Genesis)
}

/// Returns true if the execution context is for governance native transaction
pub fn is_system(&self) -> bool {
matches!(self, ExecutionMode::System)
}

/// Returns true if the execution context is for governance genesis transaction
pub fn is_genesis(&self) -> bool {
matches!(self, ExecutionMode::Genesis)
}
}

/// Configuration for the EVM.
#[auto_impl(&, &mut, Box, Arc)]
pub trait Cfg {
Expand Down Expand Up @@ -60,7 +104,7 @@ pub trait Cfg {
fn is_priority_fee_check_disabled(&self) -> bool;

/// Returns whether the automation mode is enabled.
fn is_automation_mode(&self) -> bool;
fn execution_mode(&self) -> &ExecutionMode;
}

/// What bytecode analysis to perform
Expand Down
2 changes: 1 addition & 1 deletion crates/context/interface/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub mod result;
pub mod transaction;

pub use block::Block;
pub use cfg::{Cfg, CreateScheme, TransactTo};
pub use cfg::{Cfg, CreateScheme, ExecutionMode, TransactTo};
pub use context::{ContextError, ContextSetters, ContextTr};
pub use database_interface::{DBErrorMarker, Database};
pub use either;
Expand Down
15 changes: 8 additions & 7 deletions crates/context/src/cfg.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
//! This module contains [`CfgEnv`] and implements [`Cfg`] trait for it.
use context_interface::cfg::ExecutionMode;
pub use context_interface::Cfg;

use primitives::{eip170, eip3860, eip7825, hardfork::SpecId};

/// EVM configuration
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, Eq, PartialEq)]
Expand Down Expand Up @@ -104,8 +105,8 @@ pub struct CfgEnv<SPEC = SpecId> {
/// By default, it is set to `false`.
#[cfg(feature = "optional_priority_fee_check")]
pub disable_priority_fee_check: bool,
/// Whether to run the EVM in automation mode.
pub automation_mode: bool,
/// Execution mode the EVM is configured to run.
pub execution_mode: ExecutionMode,
}

impl CfgEnv {
Expand Down Expand Up @@ -161,7 +162,7 @@ impl<SPEC> CfgEnv<SPEC> {
disable_base_fee: false,
#[cfg(feature = "optional_priority_fee_check")]
disable_priority_fee_check: false,
automation_mode: false,
execution_mode: ExecutionMode::User,
}
}

Expand Down Expand Up @@ -209,7 +210,7 @@ impl<SPEC> CfgEnv<SPEC> {
disable_base_fee: self.disable_base_fee,
#[cfg(feature = "optional_priority_fee_check")]
disable_priority_fee_check: self.disable_priority_fee_check,
automation_mode: self.automation_mode,
execution_mode: self.execution_mode,
}
}

Expand Down Expand Up @@ -349,8 +350,8 @@ impl<SPEC: Into<SpecId> + Copy> Cfg for CfgEnv<SPEC> {
}
}

fn is_automation_mode(&self) -> bool {
self.automation_mode
fn execution_mode(&self) -> &ExecutionMode {
&self.execution_mode
}
}

Expand Down
6 changes: 3 additions & 3 deletions crates/handler/src/frame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use primitives::{
constants::CALL_STACK_LIMIT,
hardfork::SpecId::{self, HOMESTEAD, LONDON, SPURIOUS_DRAGON},
};
use primitives::{keccak256, Address, Bytes, B256, U256};
use primitives::{keccak256, Address, Bytes, U256};
use state::Bytecode;
use std::borrow::ToOwned;
use std::boxed::Box;
Expand Down Expand Up @@ -279,7 +279,7 @@ impl EthFrame<EthInterpreter> {
inputs: Box<CreateInputs>,
) -> Result<ItemOrResult<FrameToken, FrameResult>, ERROR> {
let spec = context.cfg().spec().into();
let is_automation = context.cfg().is_automation_mode();
let should_update_nonce = context.cfg().execution_mode().updates_nonce();
let return_error = |e| {
Ok(ItemOrResult::Result(FrameResult::Create(CreateOutcome {
result: InterpreterResult {
Expand Down Expand Up @@ -310,7 +310,7 @@ impl EthFrame<EthInterpreter> {
return return_error(InstructionResult::OutOfFunds);
}
let old_nonce = caller_info.nonce;
if !is_automation {
if should_update_nonce {
// Increase nonce of caller and check if it overflows
let Some(new_nonce) = old_nonce.checked_add(1) else {
return return_error(InstructionResult::Return);
Expand Down
30 changes: 29 additions & 1 deletion crates/handler/src/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use context_interface::{
};
use interpreter::interpreter_action::FrameInit;
use interpreter::{Gas, InitialAndFloorGas, SharedMemory};
use primitives::supra_constants::{is_supra_reserved, is_vm_signer};
use primitives::U256;

/// Trait for errors that can occur during EVM execution.
Expand Down Expand Up @@ -153,13 +154,16 @@ pub trait Handler {
self.execution_result(evm, exec_result)
}

/// Validates the execution environment and transaction parameters.
/// Validates the execution environment, transaction parameters and caller address.
///
/// The transaction caller is verified to not be one of the SUPRA reserved addresses for user transactions.
///
/// Calculates initial and floor gas requirements and verifies they are covered by the gas limit.
///
/// Validation against state is done later in pre-execution phase in deduct_caller function.
#[inline]
fn validate(&self, evm: &mut Self::Evm) -> Result<InitialAndFloorGas, Self::Error> {
self.validate_caller(evm)?;
self.validate_env(evm)?;
self.validate_initial_tx_gas(evm)
}
Expand Down Expand Up @@ -242,6 +246,30 @@ pub trait Handler {
validation::validate_env(evm.ctx())
}

/// Validates caller, to reject user transactions having caller address matching any of
/// the SUPRA reserved addresses.
#[inline]
fn validate_caller(&self, evm: &Self::Evm) -> Result<(), Self::Error> {
let ctx = evm.ctx_ref();
let execution_mode = ctx.cfg().execution_mode();
let caller = ctx.tx().caller();
// Supra reserved address is allowed either in system execution mode or in genesis
if is_supra_reserved(&caller)
&& !(execution_mode.is_system() || execution_mode.is_genesis())
{
Err(Self::Error::from_string(format!(
"Invalid caller: supra reserved address. TxnHash {}",
ctx.tx().tx_hash()
)))
} else if !is_vm_signer(&caller) && execution_mode.is_system() {
Err(Self::Error::from_string(String::from(
"Invalid caller: Expected VM_SIGNER as caller for system transactions.",
)))
} else {
Ok(())
}
}

/// Calculates initial gas costs based on transaction type and input data.
///
/// Includes additional costs for access list and authorization list.
Expand Down
7 changes: 4 additions & 3 deletions crates/handler/src/pre_execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,13 @@ pub fn validate_against_state_and_deduct_caller<
>(
context: &mut CTX,
) -> Result<(), ERROR> {
let automation_mode = context.cfg().is_automation_mode();
let should_update_nonce = context.cfg().execution_mode().updates_nonce();
let basefee = context.block().basefee() as u128;
let blob_price = context.block().blob_gasprice().unwrap_or_default();
let is_balance_check_disabled = context.cfg().is_balance_check_disabled();
let is_eip3607_disabled = context.cfg().is_eip3607_disabled();
let is_nonce_check_disabled = context.cfg().is_nonce_check_disabled();
// nonce check will not be done if it is disabled, or execution mode does not assume nonce-change.
let is_nonce_check_disabled = context.cfg().is_nonce_check_disabled() || !should_update_nonce;

let (tx, journal) = context.tx_journal_mut();

Expand Down Expand Up @@ -167,7 +168,7 @@ pub fn validate_against_state_and_deduct_caller<
caller_account.mark_touch();
caller_account.info.balance = new_balance;

if !automation_mode {
if should_update_nonce {
// Bump the nonce for calls. Nonce for CREATE will be bumped in `make_create_frame`.
if tx.kind().is_call() {
// Nonce is already checked
Expand Down
2 changes: 1 addition & 1 deletion crates/handler/src/precompile_provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ pub trait PrecompileProvider<CTX: ContextTr> {
/// Returned booling will determine if precompile addresses should be injected into the journal.
fn set_spec(&mut self, spec: <CTX::Cfg as Cfg>::Spec) -> bool;

/// Run the precompile.
/// Run precompile.
fn run(
&mut self,
context: &mut CTX,
Expand Down
53 changes: 49 additions & 4 deletions crates/primitives/src/supra_constants.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,50 @@
//! Global constants for Supra EVM
use alloy_primitives::{address, Address};
//! Constants defined by SUPRA to facilitate execution flow extensions.
use alloy_primitives::Address;

/// Address of TX_HASH precompile
pub const TX_HASH_ADDRESS: Address = address!("0x0000000000000000000000000000000053555001");
/// Converts [`u64`] to [`Address`] type.
pub const fn u64_to_address(x: u64) -> Address {
let x = x.to_be_bytes();
Address::new([
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, x[0], x[1], x[2], x[3], x[4], x[5], x[6], x[7],
])
}

/// Supra Reserved address for VM SIGNER,
pub const VM_SIGNER: Address = u64_to_address(0x5355_5000);

/// Supra Reserved address Precompile address to retrieve transaction hash
pub const TX_HASH_ADDRESS: Address = u64_to_address(0x5355_5001);

/// [0x5355_5000, 0x53555_50FF] addresses are reserved as SUPRA special addresses.
const SUPRA_RESERVED_ADDRESSES_PREFIX_UPPER_BOUND: usize = 19;

/// Checks whether specified input address is one of the SUPRA reserved ones.
pub fn is_supra_reserved(address: &Address) -> bool {
VM_SIGNER[..SUPRA_RESERVED_ADDRESSES_PREFIX_UPPER_BOUND]
.eq(&address[..SUPRA_RESERVED_ADDRESSES_PREFIX_UPPER_BOUND])
}

/// Checks whether specified input address is SUPRA reserved VM_SIGNER
pub fn is_vm_signer(address: &Address) -> bool {
VM_SIGNER.eq(address)
}

#[cfg(test)]
mod tests {
use crate::supra_constants::{is_supra_reserved, TX_HASH_ADDRESS, VM_SIGNER};

#[test]
fn check_reserved_addresses() {
let addr5 = super::u64_to_address(0x5355_5005);
let last_reserved = super::u64_to_address(0x5355_50ff);
let any_low_address = super::u64_to_address(0x5355_4fff);
let any_up_address = super::u64_to_address(0x5355_5100);
let any_address = super::u64_to_address(0x1_5355_5000);
assert!(is_supra_reserved(&addr5));
assert!(is_supra_reserved(&VM_SIGNER));
assert!(is_supra_reserved(&TX_HASH_ADDRESS));
assert!(!is_supra_reserved(&any_address));
assert!(!is_supra_reserved(&any_low_address));
assert!(!is_supra_reserved(&any_up_address));
}
}
45 changes: 45 additions & 0 deletions crates/supra-extension/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
[package]
name = "revm-supra-extension"
version = "0.1.0"
license.workspace = true
authors.workspace = true
categories.workspace = true
keywords.workspace = true
repository.workspace = true
documentation.workspace = true
homepage.workspace = true
edition.workspace = true
rust-version.workspace = true

[dependencies]
alloy-sol-types = { workspace = true }
alloy-contract = { workspace = true }
alloy-consensus = { workspace = true }
alloy-eips = { workspace = true }
serde = { workspace = true }
alloy = { workspace = true }
derive_more = { workspace = true, features = ["full"] }
derive-getters = { workspace = true }
thiserror = { workspace = true }
primitives = { workspace = true }
context = { workspace = true }
alloy-serde = {workspace = true, optional = true }
anyhow = { workspace = true }
foundry-compilers = { workspace = true }
serde_json = { workspace = true }

[lints]
workspace = true


[build-dependencies]
#forge = { workspace = true }
#clap = { workspace = true }
#shlex = { workspace = true }
foundry-compilers = { workspace = true }
anyhow = { workspace = true }
toml = { workspace = true }
serde = {workspace = true }

[features]
serde = ["alloy-serde"]
Loading