diff --git a/crates/interpreter/src/gas/constants.rs b/crates/interpreter/src/gas/constants.rs index b8bd7d0544..560c9c2d3e 100644 --- a/crates/interpreter/src/gas/constants.rs +++ b/crates/interpreter/src/gas/constants.rs @@ -39,3 +39,6 @@ pub const WARM_STORAGE_READ_COST: u64 = 100; pub const INITCODE_WORD_COST: u64 = 2; pub const CALL_STIPEND: u64 = 2300; + +// EIP-3074: AUTH and AUTHCALL +pub const AUTH: u64 = 3100; diff --git a/crates/interpreter/src/instruction_result.rs b/crates/interpreter/src/instruction_result.rs index 4214dc7288..0261eeaae5 100644 --- a/crates/interpreter/src/instruction_result.rs +++ b/crates/interpreter/src/instruction_result.rs @@ -24,6 +24,7 @@ pub enum InstructionResult { OpcodeNotFound, CallNotAllowedInsideStatic, StateChangeDuringStaticCall, + ActiveAccountUnsetAuthCall, InvalidFEOpcode, InvalidJump, NotActivated, @@ -169,6 +170,9 @@ impl From for SuccessOrHalt { InstructionResult::StateChangeDuringStaticCall => { Self::Halt(Halt::StateChangeDuringStaticCall) } + InstructionResult::ActiveAccountUnsetAuthCall => { + Self::Halt(Halt::ActiveAccountUnsetAuthCall) + } InstructionResult::InvalidFEOpcode => Self::Halt(Halt::InvalidFEOpcode), InstructionResult::InvalidJump => Self::Halt(Halt::InvalidJump), InstructionResult::NotActivated => Self::Halt(Halt::NotActivated), diff --git a/crates/interpreter/src/instructions/host.rs b/crates/interpreter/src/instructions/host.rs index 443becc4fc..ec15b5283a 100644 --- a/crates/interpreter/src/instructions/host.rs +++ b/crates/interpreter/src/instructions/host.rs @@ -373,11 +373,17 @@ pub fn auth(interpreter: &mut Interpreter<'_>, host: &mut H // Pop the signature offset and length off of the stack pop!(interpreter, signature_offset, signature_len); - // Grab the memory region for the input data - let mem_slice = interpreter.shared_memory.slice( - signature_offset.try_into().unwrap_or_default(), - signature_len.try_into().unwrap_or_default(), - ); + let signature_offset = as_usize_or_fail!(interpreter, signature_offset); + let signature_len = as_usize_or_fail!(interpreter, signature_len); + + // Charge the fixed cost for the `AUTH` opcode + gas!(interpreter, gas::AUTH); + + // Grab the memory region for the input data and charge for any memory expansion + shared_memory_resize!(interpreter, signature_offset, signature_len); + let mem_slice = interpreter + .shared_memory + .slice(signature_offset, signature_len); // Pull the signature from the first 65 bytes of the memory region. let signature = { @@ -398,14 +404,15 @@ pub fn auth(interpreter: &mut Interpreter<'_>, host: &mut H }); // Build the original auth message and compute the hash. - let mut message = [0u8; 96]; - message[0..32].copy_from_slice( + let mut message = [0u8; 97]; + message[0] = 0x03; // AUTH_MAGIC + message[1..33].copy_from_slice( U256::from(host.env().cfg.chain_id) .to_be_bytes::<32>() .as_ref(), ); - message[32..64].copy_from_slice(interpreter.contract().address.into_word().as_ref()); - message[64..96].copy_from_slice(commit.unwrap_or_default().as_ref()); + message[33..65].copy_from_slice(interpreter.contract().address.into_word().as_ref()); + message[65..97].copy_from_slice(commit.unwrap_or_default().as_ref()); let message_hash = revm_primitives::keccak256(&message); // Verify the signature @@ -415,7 +422,7 @@ pub fn auth(interpreter: &mut Interpreter<'_>, host: &mut H push!(interpreter, U256::ZERO); } else { push!(interpreter, U256::from(1)); - // TODO: Set `authorized` context. + interpreter.active_account = Some(authority); } } @@ -454,15 +461,14 @@ fn prepare_call_inputs( CallScheme::AuthCall => { pop!(interpreter, value); value - }, + } }; if scheme == CallScheme::AuthCall { pop!(interpreter, ext_value); - // ext_value must be 0 or it reverts + // If `ext_value` is non-zero, then the `AUTHCALL` opcode immediately returns 0. if ext_value != U256::ZERO { - // TODO: new InstructionResult enum - interpreter.instruction_result = InstructionResult::FatalExternalError; + push!(interpreter, U256::ZERO); return; } }; @@ -510,13 +516,17 @@ fn prepare_call_inputs( scheme, }, CallScheme::AuthCall => { - CallContext { - address: interpreter.contract.address, - // TODO: Should be auth caller, need context var - caller: interpreter.contract.caller, - code_address: to, - apparent_value: interpreter.contract.value, // TODO: Prob not correct, check spec - scheme, + if let Some(account) = interpreter.active_account { + CallContext { + address: interpreter.contract.address, + caller: account, + code_address: to, + apparent_value: value, + scheme, + } + } else { + interpreter.instruction_result = InstructionResult::ActiveAccountUnsetAuthCall; + return; } } }; @@ -533,6 +543,12 @@ fn prepare_call_inputs( target: interpreter.contract.address, value, } + } else if scheme == CallScheme::AuthCall { + Transfer { + source: interpreter.active_account.unwrap_or_default(), + target: to, + value, + } } else { //this is dummy send for StaticCall and DelegateCall, it should do nothing and dont touch anything. Transfer { @@ -549,6 +565,7 @@ fn prepare_call_inputs( }; let is_new = !exist; + // TODO(clabby): EIP-3074 - AuthCall needs coverage gas!( interpreter, gas::call_cost::( @@ -597,6 +614,8 @@ pub fn call_inner( CallScheme::DelegateCall => check!(interpreter, HOMESTEAD), // EIP-214: New opcode STATICCALL CallScheme::StaticCall => check!(interpreter, BYZANTIUM), + // EIP-3074: New opcode AUTHCALL + CallScheme::AuthCall => check!(interpreter, PRAGUE), _ => (), } interpreter.return_data_buffer = Bytes::new(); diff --git a/crates/interpreter/src/instructions/opcode.rs b/crates/interpreter/src/instructions/opcode.rs index 317d028981..4ab69c55f5 100644 --- a/crates/interpreter/src/instructions/opcode.rs +++ b/crates/interpreter/src/instructions/opcode.rs @@ -834,8 +834,8 @@ const fn opcode_gas_info(opcode: u8, spec: SpecId) -> OpInfo { RETURN => OpInfo::gas_block_end(0), DELEGATECALL => OpInfo::gas_block_end(0), CREATE2 => OpInfo::gas_block_end(0), - 0xF6 => OpInfo::none(), - 0xF7 => OpInfo::none(), + AUTH => OpInfo::dynamic_gas(), + AUTHCALL => OpInfo::gas_block_end(0), // TODO: Reassess once AUTHCALL is impl'd 0xF8 => OpInfo::none(), 0xF9 => OpInfo::none(), STATICCALL => OpInfo::gas_block_end(0), @@ -911,6 +911,7 @@ pub const fn spec_opcode_gas(spec_id: SpecId) -> &'static [OpInfo; 256] { MERGE, SHANGHAI, CANCUN, + PRAGUE, LATEST, ) } diff --git a/crates/interpreter/src/interpreter.rs b/crates/interpreter/src/interpreter.rs index e4ce89f7ad..b8cec015d5 100644 --- a/crates/interpreter/src/interpreter.rs +++ b/crates/interpreter/src/interpreter.rs @@ -5,6 +5,7 @@ mod stack; pub use analysis::BytecodeLocked; pub use contract::Contract; +use revm_primitives::Address; pub use shared_memory::{next_multiple_of_32, SharedMemory}; pub use stack::{Stack, STACK_LIMIT}; @@ -45,6 +46,8 @@ pub struct Interpreter<'a> { pub return_len: usize, /// Whether the interpreter is in "staticcall" mode, meaning no state changes can happen. pub is_static: bool, + /// EIP-3074: Active account for `AUTHCALL` instructions in the current execution frame. + pub active_account: Option
, } impl<'a> Interpreter<'a> { @@ -66,6 +69,7 @@ impl<'a> Interpreter<'a> { return_offset: 0, shared_memory, stack: Stack::new(), + active_account: None, } } diff --git a/crates/precompile/src/lib.rs b/crates/precompile/src/lib.rs index c279baf85f..8924846715 100644 --- a/crates/precompile/src/lib.rs +++ b/crates/precompile/src/lib.rs @@ -130,7 +130,7 @@ impl SpecId { BYZANTIUM | CONSTANTINOPLE | PETERSBURG => Self::BYZANTIUM, ISTANBUL | MUIR_GLACIER => Self::ISTANBUL, BERLIN | LONDON | ARROW_GLACIER | GRAY_GLACIER | MERGE | SHANGHAI => Self::BERLIN, - CANCUN => Self::CANCUN, + CANCUN | PRAGUE => Self::CANCUN, LATEST => Self::LATEST, #[cfg(feature = "optimism")] BEDROCK | REGOLITH | CANYON => Self::BERLIN, diff --git a/crates/primitives/src/result.rs b/crates/primitives/src/result.rs index 6688269277..e0e4899dd4 100644 --- a/crates/primitives/src/result.rs +++ b/crates/primitives/src/result.rs @@ -363,6 +363,7 @@ pub enum Halt { /* Internal Halts that can be only found inside Inspector */ OverflowPayment, StateChangeDuringStaticCall, + ActiveAccountUnsetAuthCall, CallNotAllowedInsideStatic, OutOfFund, CallTooDeep, diff --git a/crates/primitives/src/specification.rs b/crates/primitives/src/specification.rs index 2c14faa0cb..476c94eea1 100644 --- a/crates/primitives/src/specification.rs +++ b/crates/primitives/src/specification.rs @@ -28,6 +28,7 @@ pub enum SpecId { MERGE = 15, // Paris/Merge 15537394 (TTD: 58750000000000000000000) SHANGHAI = 16, // Shanghai 17034870 (TS: 1681338455) CANCUN = 17, // Cancun TBD + PRAGUE = 18, // Prague TBD LATEST = u8::MAX, } @@ -60,6 +61,7 @@ pub enum SpecId { SHANGHAI = 18, CANYON = 19, CANCUN = 20, + PRAGUE = 21, LATEST = u8::MAX, } @@ -92,6 +94,7 @@ impl From<&str> for SpecId { "Merge" => Self::MERGE, "Shanghai" => Self::SHANGHAI, "Cancun" => Self::CANCUN, + "Prague" => Self::PRAGUE, #[cfg(feature = "optimism")] "Bedrock" => SpecId::BEDROCK, #[cfg(feature = "optimism")] @@ -143,6 +146,7 @@ spec!(LONDON, LondonSpec); spec!(MERGE, MergeSpec); spec!(SHANGHAI, ShanghaiSpec); spec!(CANCUN, CancunSpec); +spec!(PRAGUE, PragueSpec); spec!(LATEST, LatestSpec); // Optimism Hardforks diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index f0b2ac5fec..044ad7bfbb 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -216,6 +216,7 @@ pub fn evm_inner<'a, DB: Database>( SpecId::MERGE => create_evm!(MergeSpec), SpecId::SHANGHAI => create_evm!(ShanghaiSpec), SpecId::CANCUN => create_evm!(CancunSpec), + SpecId::PRAGUE => create_evm!(PragueSpec), SpecId::LATEST => create_evm!(LatestSpec), #[cfg(feature = "optimism")] SpecId::BEDROCK => create_evm!(BedrockSpec),