diff --git a/crates/evm/src/context.cairo b/crates/evm/src/context.cairo index 70ba05dca..8917abb52 100644 --- a/crates/evm/src/context.cairo +++ b/crates/evm/src/context.cairo @@ -320,4 +320,22 @@ impl ExecutionContextImpl of ExecutionContextTrait { fn append_event(ref self: ExecutionContext, event: Event) { self.events.append(event); } + + fn origin(ref self: ExecutionContext) -> EthAddress { + if (self.is_root()) { + return self.call_ctx().caller(); + } + // If the current execution context is not root, then it MUST have a parent_context + // We're able to deref the nullable pointer without risk of panic + let mut parent_context = self.parent_ctx.deref(); + + // Entering a recursion + let origin = parent_context.origin(); + + // Recursively reboxing parent contexts + self.parent_ctx = NullableTrait::new(parent_context); + + // Return self.call_context().caller() where self is the root context + origin + } } diff --git a/crates/evm/src/instructions/environmental_information.cairo b/crates/evm/src/instructions/environmental_information.cairo index 0b3fb9ed2..066295933 100644 --- a/crates/evm/src/instructions/environmental_information.cairo +++ b/crates/evm/src/instructions/environmental_information.cairo @@ -28,7 +28,7 @@ impl EnvironmentInformationImpl of EnvironmentInformationTrait { /// Get execution origination address. /// # Specification: https://www.evm.codes/#32?fork=shanghai fn exec_origin(ref self: Machine) -> Result<(), EVMError> { - Result::Ok(()) + self.stack.push(self.origin().into()) } /// 0x33 - CALLER diff --git a/crates/evm/src/machine.cairo b/crates/evm/src/machine.cairo index 01fb23cbb..1aea9ad71 100644 --- a/crates/evm/src/machine.cairo +++ b/crates/evm/src/machine.cairo @@ -183,6 +183,14 @@ impl MachineCurrentContextImpl of MachineCurrentContextTrait { current_call_ctx.caller() } + #[inline(always)] + fn origin(ref self: Machine) -> EthAddress { + let mut current_execution_ctx = self.current_ctx.unbox(); + let origin = current_execution_ctx.origin(); + self.current_ctx = BoxTrait::new(current_execution_ctx); + origin + } + #[inline(always)] fn read_only(ref self: Machine) -> bool { let current_call_ctx = self.call_ctx(); diff --git a/crates/evm/src/tests/test_execution_context.cairo b/crates/evm/src/tests/test_execution_context.cairo index 24797683d..35422cac1 100644 --- a/crates/evm/src/tests/test_execution_context.cairo +++ b/crates/evm/src/tests/test_execution_context.cairo @@ -6,7 +6,10 @@ use evm::context::{ use evm::memory::{Memory, MemoryTrait}; use evm::model::Event; use evm::stack::{Stack, StackTrait}; -use evm::tests::test_utils::{setup_call_context, setup_execution_context, CallContextPartialEq}; +use evm::tests::test_utils::{ + setup_call_context, setup_execution_context, setup_nested_execution_context, + CallContextPartialEq +}; use evm::tests::test_utils; use starknet::testing::{set_contract_address, set_caller_address}; use starknet::{EthAddress, ContractAddress}; @@ -166,3 +169,17 @@ fn test_child_return_data() { // Then assert(child_return_data == array![1, 2, 3].span(), 'wrong child_return_data'); } + + +#[test] +#[available_gas(300000)] +fn test_origin() { + // Given + let mut execution_context = setup_nested_execution_context(); + + // When + let origin = execution_context.origin(); + + // Then + assert(origin == evm_address(), 'wrong child_return_data'); +} diff --git a/crates/evm/src/tests/test_instructions/test_environment_information.cairo b/crates/evm/src/tests/test_instructions/test_environment_information.cairo index b1765bfd8..d5f2e15b9 100644 --- a/crates/evm/src/tests/test_instructions/test_environment_information.cairo +++ b/crates/evm/src/tests/test_instructions/test_environment_information.cairo @@ -5,7 +5,8 @@ use evm::machine::{Machine, MachineCurrentContextTrait}; use evm::memory::{InternalMemoryTrait, MemoryTrait}; use evm::stack::StackTrait; use evm::tests::test_utils::{ - setup_machine, setup_machine_with_calldata, setup_machine_with_bytecode, evm_address, callvalue + setup_machine, setup_machine_with_calldata, setup_machine_with_bytecode, evm_address, callvalue, + setup_machine_with_nested_execution_context, other_evm_address }; use integer::u32_overflowing_add; @@ -61,6 +62,41 @@ fn test_caller() { } +// ************************************************************************* +// 0x32: ORIGIN +// ************************************************************************* +#[test] +#[available_gas(20000000)] +fn test_origin() { + // Given + let mut machine = setup_machine_with_nested_execution_context(); + + // When + machine.exec_origin(); + + // Then + assert(machine.stack.len() == 1, 'stack should have one element'); + assert(machine.stack.peek().unwrap() == evm_address().into(), 'should be `evm_address`'); + + // And + assert(machine.caller() == other_evm_address(), 'should be another_evm_address'); +} + +#[test] +#[available_gas(20000000)] +fn test_origin_nested_ctx() { + // Given + let mut machine = setup_machine(); + + // When + machine.exec_origin(); + + // Then + assert(machine.stack.len() == 1, 'stack should have one element'); + assert(machine.stack.peek().unwrap() == evm_address().into(), 'should be `evm_address`'); +} + + // ************************************************************************* // 0x34: CALLVALUE // ************************************************************************* diff --git a/crates/evm/src/tests/test_utils.cairo b/crates/evm/src/tests/test_utils.cairo index 1bc97d047..8383f52c6 100644 --- a/crates/evm/src/tests/test_utils.cairo +++ b/crates/evm/src/tests/test_utils.cairo @@ -13,6 +13,10 @@ fn evm_address() -> EthAddress { 'evm_address'.try_into().unwrap() } +fn other_evm_address() -> EthAddress { + 0xabde1.try_into().unwrap() +} + fn zero_address() -> ContractAddress { 0.try_into().unwrap() } @@ -52,6 +56,21 @@ fn setup_execution_context() -> ExecutionContext { ) } +fn setup_nested_execution_context() -> ExecutionContext { + let mut parent_context = setup_execution_context(); + + // Second Execution Context + let context_id = 1; + let mut child_context = setup_execution_context(); + child_context.id = context_id; + child_context.parent_ctx = NullableTrait::new(parent_context); + let mut call_ctx = child_context.call_ctx.unbox(); + call_ctx.caller = other_evm_address(); + child_context.call_ctx = BoxTrait::new(call_ctx); + + return child_context; +} + fn setup_call_context_with_bytecode(bytecode: Span) -> CallContext { let calldata: Span = array![4, 5, 6].span(); let value: u256 = callvalue(); @@ -161,3 +180,14 @@ fn setup_machine_with_read_only() -> Machine { machine.current_ctx = BoxTrait::new(current_ctx); machine } + +fn setup_machine_with_nested_execution_context() -> Machine { + let current_ctx = BoxTrait::new(setup_nested_execution_context()); + Machine { + current_ctx, + ctx_count: 1, + stack: Default::default(), + memory: Default::default(), + storage_journal: Default::default(), + } +}