From 3b4a960e0a70b8f92673f1743a723aca5459df36 Mon Sep 17 00:00:00 2001 From: "Lucas (Kbooz)" Date: Tue, 17 Dec 2024 16:14:29 -0300 Subject: [PATCH 01/44] feat: started implementing stack as enum Co-authored-by: Nuno Carvalho Co-authored-by: abipalli --- crates/interpreter/src/interpreter/stack.rs | 66 +++++++++++++++------ 1 file changed, 47 insertions(+), 19 deletions(-) diff --git a/crates/interpreter/src/interpreter/stack.rs b/crates/interpreter/src/interpreter/stack.rs index 39d84b4627..2607d538e1 100644 --- a/crates/interpreter/src/interpreter/stack.rs +++ b/crates/interpreter/src/interpreter/stack.rs @@ -1,4 +1,5 @@ use crate::InstructionResult; +use compute::prelude::GateIndexVec; use core::{fmt, ptr}; use primitives::{B256, U256}; use std::vec::Vec; @@ -6,12 +7,18 @@ use std::vec::Vec; /// EVM interpreter stack limit. pub const STACK_LIMIT: usize = 1024; +#[derive(Debug, PartialEq, Eq, Hash, Clone, serde::Serialize)] +pub enum StackValueData { + Private(GateIndexVec), + Public(U256), +} + /// EVM stack with [STACK_LIMIT] capacity of words. #[derive(Debug, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(serde::Serialize))] pub struct Stack { /// The underlying data of the stack. - data: Vec, + data: Vec, } impl fmt::Display for Stack { @@ -69,26 +76,26 @@ impl Stack { /// Returns a reference to the underlying data buffer. #[inline] - pub fn data(&self) -> &Vec { + pub fn data(&self) -> &Vec { &self.data } /// Returns a mutable reference to the underlying data buffer. #[inline] - pub fn data_mut(&mut self) -> &mut Vec { + pub fn data_mut(&mut self) -> &mut Vec { &mut self.data } /// Consumes the stack and returns the underlying data buffer. #[inline] - pub fn into_data(self) -> Vec { + pub fn into_data(self) -> Vec { self.data } /// Removes the topmost element from the stack and returns it, or `StackUnderflow` if it is /// empty. #[inline] - pub fn pop(&mut self) -> Result { + pub fn pop(&mut self) -> Result { self.data.pop().ok_or(InstructionResult::StackUnderflow) } @@ -98,7 +105,7 @@ impl Stack { /// /// The caller is responsible for checking the length of the stack. #[inline] - pub unsafe fn pop_unsafe(&mut self) -> U256 { + pub unsafe fn pop_unsafe(&mut self) -> StackValueData { self.data.pop().unwrap_unchecked() } @@ -108,7 +115,7 @@ impl Stack { /// /// The caller is responsible for checking the length of the stack. #[inline] - pub unsafe fn top_unsafe(&mut self) -> &mut U256 { + pub unsafe fn top_unsafe(&mut self) -> &mut StackValueData { let len = self.data.len(); self.data.get_unchecked_mut(len - 1) } @@ -119,7 +126,7 @@ impl Stack { /// /// The caller is responsible for checking the length of the stack. #[inline] - pub unsafe fn pop_top_unsafe(&mut self) -> (U256, &mut U256) { + pub unsafe fn pop_top_unsafe(&mut self) -> (StackValueData, &mut StackValueData) { let pop = self.pop_unsafe(); let top = self.top_unsafe(); (pop, top) @@ -131,7 +138,7 @@ impl Stack { /// /// The caller is responsible for checking the length of the stack. #[inline] - pub unsafe fn pop2_unsafe(&mut self) -> (U256, U256) { + pub unsafe fn pop2_unsafe(&mut self) -> (StackValueData, StackValueData) { let pop1 = self.pop_unsafe(); let pop2 = self.pop_unsafe(); (pop1, pop2) @@ -143,7 +150,9 @@ impl Stack { /// /// The caller is responsible for checking the length of the stack. #[inline] - pub unsafe fn pop2_top_unsafe(&mut self) -> (U256, U256, &mut U256) { + pub unsafe fn pop2_top_unsafe( + &mut self, + ) -> (StackValueData, StackValueData, &mut StackValueData) { let pop1 = self.pop_unsafe(); let pop2 = self.pop_unsafe(); let top = self.top_unsafe(); @@ -157,7 +166,7 @@ impl Stack { /// /// The caller is responsible for checking the length of the stack. #[inline] - pub unsafe fn pop3_unsafe(&mut self) -> (U256, U256, U256) { + pub unsafe fn pop3_unsafe(&mut self) -> (StackValueData, StackValueData, StackValueData) { let pop1 = self.pop_unsafe(); let pop2 = self.pop_unsafe(); let pop3 = self.pop_unsafe(); @@ -171,7 +180,14 @@ impl Stack { /// /// The caller is responsible for checking the length of the stack. #[inline] - pub unsafe fn pop4_unsafe(&mut self) -> (U256, U256, U256, U256) { + pub unsafe fn pop4_unsafe( + &mut self, + ) -> ( + StackValueData, + StackValueData, + StackValueData, + StackValueData, + ) { let pop1 = self.pop_unsafe(); let pop2 = self.pop_unsafe(); let pop3 = self.pop_unsafe(); @@ -186,7 +202,15 @@ impl Stack { /// /// The caller is responsible for checking the length of the stack. #[inline] - pub unsafe fn pop5_unsafe(&mut self) -> (U256, U256, U256, U256, U256) { + pub unsafe fn pop5_unsafe( + &mut self, + ) -> ( + StackValueData, + StackValueData, + StackValueData, + StackValueData, + StackValueData, + ) { let pop1 = self.pop_unsafe(); let pop2 = self.pop_unsafe(); let pop3 = self.pop_unsafe(); @@ -200,7 +224,7 @@ impl Stack { /// returns `StackOverflow` error and leaves the stack unchanged. #[inline] pub fn push_b256(&mut self, value: B256) -> Result<(), InstructionResult> { - self.push(value.into()) + self.push(StackValueData::Public(value.into())) } /// Push a new value onto the stack. @@ -208,7 +232,7 @@ impl Stack { /// If it will exceed the stack limit, returns `StackOverflow` error and leaves the stack /// unchanged. #[inline] - pub fn push(&mut self, value: U256) -> Result<(), InstructionResult> { + pub fn push(&mut self, value: StackValueData) -> Result<(), InstructionResult> { // Allows the compiler to optimize out the `Vec::push` capacity check. assume!(self.data.capacity() == STACK_LIMIT); if self.data.len() == STACK_LIMIT { @@ -222,7 +246,7 @@ impl Stack { /// the stack is at index `0`. If the index is too large, /// `StackError::Underflow` is returned. #[inline] - pub fn peek(&self, no_from_top: usize) -> Result { + pub fn peek(&self, no_from_top: usize) -> Result { if self.data.len() > no_from_top { Ok(self.data[self.data.len() - no_from_top - 1]) } else { @@ -363,7 +387,11 @@ impl Stack { /// stack is at index `0`. If the index is too large, /// `StackError::Underflow` is returned. #[inline] - pub fn set(&mut self, no_from_top: usize, val: U256) -> Result<(), InstructionResult> { + pub fn set( + &mut self, + no_from_top: usize, + val: StackValueData, + ) -> Result<(), InstructionResult> { if self.data.len() > no_from_top { let len = self.data.len(); self.data[len - no_from_top - 1] = val; @@ -380,7 +408,7 @@ impl<'de> serde::Deserialize<'de> for Stack { where D: serde::Deserializer<'de>, { - let mut data = Vec::::deserialize(deserializer)?; + let mut data = Vec::::deserialize(deserializer)?; if data.len() > STACK_LIMIT { return Err(serde::de::Error::custom(std::format!( "stack size exceeds limit: {} > {}", @@ -402,7 +430,7 @@ mod tests { // fill capacity with non-zero values unsafe { stack.data.set_len(STACK_LIMIT); - stack.data.fill(U256::MAX); + stack.data.fill(StackValueData::Public(U256::MAX)); stack.data.set_len(0); } f(&mut stack); From 3dbbdbf065e08eb706d73ced072115f35af4e162 Mon Sep 17 00:00:00 2001 From: "Lucas (Kbooz)" Date: Tue, 17 Dec 2024 18:30:13 -0300 Subject: [PATCH 02/44] fix: support StackValueData --- .../src/instructions/arithmetic.rs | 31 +++---- .../src/instructions/block_info.rs | 33 ++++++-- .../interpreter/src/instructions/contract.rs | 45 +++++----- .../src/instructions/contract/call_helpers.rs | 4 +- .../interpreter/src/instructions/control.rs | 23 +++--- crates/interpreter/src/instructions/data.rs | 48 +++++------ crates/interpreter/src/instructions/host.rs | 2 +- crates/interpreter/src/instructions/memory.rs | 15 ++-- .../interpreter/src/instructions/tx_info.rs | 9 +- crates/interpreter/src/interpreter.rs | 27 +++--- crates/interpreter/src/interpreter/stack.rs | 82 +++++++++++++++---- 11 files changed, 200 insertions(+), 119 deletions(-) diff --git a/crates/interpreter/src/instructions/arithmetic.rs b/crates/interpreter/src/instructions/arithmetic.rs index 591ee19c33..b8e9226e5f 100644 --- a/crates/interpreter/src/instructions/arithmetic.rs +++ b/crates/interpreter/src/instructions/arithmetic.rs @@ -13,44 +13,44 @@ pub fn add(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); pop_top!(interpreter, op1, op2); - let garbled_op1 = ruint_to_garbled_uint(&op1); - let garbled_op2 = ruint_to_garbled_uint(&op2); + let garbled_op1 = ruint_to_garbled_uint(&op1.into()); + let garbled_op2 = ruint_to_garbled_uint(&op2.clone().into()); let result = garbled_op1.add(garbled_op2); - *op2 = garbled_uint_to_ruint(&result); + *op2 = garbled_uint_to_ruint(&result).into(); } pub fn mul(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::LOW); pop_top!(interpreter, op1, op2); - let garbled_op1 = ruint_to_garbled_uint(&op1); - let garbled_op2 = ruint_to_garbled_uint(&op2); + let garbled_op1 = ruint_to_garbled_uint(&op1.into()); + let garbled_op2 = ruint_to_garbled_uint(&op2.clone().into()); let result = garbled_op1.mul(garbled_op2); - *op2 = garbled_uint_to_ruint(&result); + *op2 = garbled_uint_to_ruint(&result).into(); } pub fn sub(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); pop_top!(interpreter, op1, op2); - let garbled_op1 = ruint_to_garbled_uint(&op1); - let garbled_op2 = ruint_to_garbled_uint(&op2); + let garbled_op1 = ruint_to_garbled_uint(&op1.into()); + let garbled_op2 = ruint_to_garbled_uint(&op2.clone().into()); let result = garbled_op2.sub(garbled_op1); - *op2 = garbled_uint_to_ruint(&result); + *op2 = garbled_uint_to_ruint(&result).into(); } pub fn div(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::LOW); pop_top!(interpreter, op1, op2); - if !op2.is_zero() { - let garbled_op1 = ruint_to_garbled_uint(&op1); - let garbled_op2 = ruint_to_garbled_uint(&op2); + if !op2.into().is_zero() { + let garbled_op1 = ruint_to_garbled_uint(&op1.into()); + let garbled_op2 = ruint_to_garbled_uint(&op2.clone().into()); let result = garbled_op1.div(garbled_op2); - *op2 = garbled_uint_to_ruint(&result); + *op2 = garbled_uint_to_ruint(&result).into(); } } @@ -58,7 +58,8 @@ pub fn div(interpreter: &mut Interpreter, _host: &mut H) { pub fn sdiv(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::LOW); pop_top!(interpreter, op1, op2); - *op2 = i256_div(op1, *op2); + let result = i256_div(op1.into(), *op2.into()); + *op2 = result.into(); } pub fn rem(interpreter: &mut Interpreter, _host: &mut H) { @@ -69,7 +70,7 @@ pub fn rem(interpreter: &mut Interpreter, _host: &mut H) { let garbled_op2 = ruint_to_garbled_uint(&op2); let result = garbled_op1.rem(garbled_op2); - *op2 = garbled_uint_to_ruint(&result); + *op2 = garbled_uint_to_ruint(&result).into(); } } diff --git a/crates/interpreter/src/instructions/block_info.rs b/crates/interpreter/src/instructions/block_info.rs index 9cb656fbc5..99114361c6 100644 --- a/crates/interpreter/src/instructions/block_info.rs +++ b/crates/interpreter/src/instructions/block_info.rs @@ -1,4 +1,4 @@ -use crate::{gas, Host, Interpreter}; +use crate::{gas, interpreter::StackValueData, Host, Interpreter}; use primitives::U256; use specification::hardfork::{Spec, SpecId::*}; use wiring::Block; @@ -7,7 +7,7 @@ use wiring::Block; pub fn chainid(interpreter: &mut Interpreter, host: &mut H) { check!(interpreter, ISTANBUL); gas!(interpreter, gas::BASE); - push!(interpreter, U256::from(host.env().cfg.chain_id)); + push!(interpreter, U256::from(host.env().cfg.chain_id).into()); } pub fn coinbase(interpreter: &mut Interpreter, host: &mut H) { @@ -17,12 +17,18 @@ pub fn coinbase(interpreter: &mut Interpreter, host: &mut H) { pub fn timestamp(interpreter: &mut Interpreter, host: &mut H) { gas!(interpreter, gas::BASE); - push!(interpreter, *host.env().block.timestamp()); + push!( + interpreter, + StackValueData::Public(*host.env().block.timestamp()) + ); } pub fn block_number(interpreter: &mut Interpreter, host: &mut H) { gas!(interpreter, gas::BASE); - push!(interpreter, *host.env().block.number()); + push!( + interpreter, + StackValueData::Public(*host.env().block.number()) + ); } pub fn difficulty(interpreter: &mut Interpreter, host: &mut H) { @@ -30,20 +36,29 @@ pub fn difficulty(interpreter: &mut Interpreter, h if SPEC::enabled(MERGE) { push_b256!(interpreter, *host.env().block.prevrandao().unwrap()); } else { - push!(interpreter, *host.env().block.difficulty()); + push!( + interpreter, + StackValueData::Public(*host.env().block.difficulty()) + ); } } pub fn gaslimit(interpreter: &mut Interpreter, host: &mut H) { gas!(interpreter, gas::BASE); - push!(interpreter, *host.env().block.gas_limit()); + push!( + interpreter, + StackValueData::Public(*host.env().block.gas_limit()) + ); } /// EIP-3198: BASEFEE opcode pub fn basefee(interpreter: &mut Interpreter, host: &mut H) { check!(interpreter, LONDON); gas!(interpreter, gas::BASE); - push!(interpreter, *host.env().block.basefee()); + push!( + interpreter, + StackValueData::Public(*host.env().block.basefee()) + ); } /// EIP-7516: BLOBBASEFEE opcode @@ -52,6 +67,8 @@ pub fn blob_basefee(interpreter: &mut Interpreter, gas!(interpreter, gas::BASE); push!( interpreter, - U256::from(host.env().block.blob_gasprice().unwrap_or_default()) + StackValueData::Public(U256::from( + host.env().block.blob_gasprice().unwrap_or_default() + )) ); } diff --git a/crates/interpreter/src/instructions/contract.rs b/crates/interpreter/src/instructions/contract.rs index 17c3f64631..adf4607738 100644 --- a/crates/interpreter/src/instructions/contract.rs +++ b/crates/interpreter/src/instructions/contract.rs @@ -34,7 +34,7 @@ pub fn eofcreate(interpreter: &mut Interpreter, _host: &mut H) .expect("EOF is checked"); // resize memory and get return range. - let Some(input_range) = resize_memory(interpreter, data_offset, data_size) else { + let Some(input_range) = resize_memory(interpreter, data_offset.into(), data_size.into()) else { return; }; @@ -64,7 +64,7 @@ pub fn eofcreate(interpreter: &mut Interpreter, _host: &mut H) let created_address = interpreter .contract .target_address - .create2(salt.to_be_bytes(), keccak256(sub_container)); + .create2(salt.to_u256().to_be_bytes(), keccak256(sub_container)); let gas_limit = interpreter.gas().remaining_63_of_64_parts(); gas!(interpreter, gas_limit); @@ -74,7 +74,7 @@ pub fn eofcreate(interpreter: &mut Interpreter, _host: &mut H) EOFCreateInputs::new_opcode( interpreter.contract.target_address, created_address, - value, + value.into(), eof, gas_limit, input, @@ -149,7 +149,7 @@ pub fn return_contract(interpreter: &mut Interpreter, _host: & pub fn extcall_input(interpreter: &mut Interpreter) -> Option { pop_ret!(interpreter, input_offset, input_size, None); - let return_memory_offset = resize_memory(interpreter, input_offset, input_size)?; + let return_memory_offset = resize_memory(interpreter, input_offset.into(), input_size.into())?; if return_memory_offset.is_empty() { return Some(Bytes::new()); @@ -189,7 +189,7 @@ pub fn extcall_gas_calc( if gas_limit < MIN_CALLEE_GAS { // Push 1 to stack to indicate that call light failed. // It is safe to ignore stack overflow error as we already popped multiple values from stack. - let _ = interpreter.stack_mut().push(U256::from(1)); + let _ = interpreter.stack_mut().push(U256::from(1).into()); interpreter.return_data_buffer.clear(); // Return none to continue execution. return None; @@ -205,7 +205,7 @@ pub fn extcall_gas_calc( #[inline] pub fn pop_extcall_target_address(interpreter: &mut Interpreter) -> Option
{ pop_ret!(interpreter, target_address, None); - let target_address = B256::from(target_address); + let target_address = B256::from(target_address.to_u256()); // Check if target is left padded with zeroes. if target_address[..12].iter().any(|i| *i != 0) { interpreter.instruction_result = InstructionResult::InvalidEXTCALLTarget; @@ -229,7 +229,7 @@ pub fn extcall(interpreter: &mut Interpreter, host }; pop!(interpreter, value); - let has_transfer = !value.is_zero(); + let has_transfer = !value.to_u256().is_zero(); if interpreter.is_static && has_transfer { interpreter.instruction_result = InstructionResult::CallNotAllowedInsideStatic; return; @@ -247,7 +247,7 @@ pub fn extcall(interpreter: &mut Interpreter, host target_address, caller: interpreter.contract.target_address, bytecode_address: target_address, - value: CallValue::Transfer(value), + value: CallValue::Transfer(value.into()), scheme: CallScheme::ExtCall, is_static: interpreter.is_static, is_eof: true, @@ -366,7 +366,7 @@ pub fn create( pop!(interpreter, salt); // SAFETY: len is reasonable in size as gas for it is already deducted. gas_or_fail!(interpreter, gas::create2_cost(len.try_into().unwrap())); - CreateScheme::Create2 { salt } + CreateScheme::Create2 { salt: salt.into() } } else { gas!(interpreter, gas::CREATE); CreateScheme::Create @@ -386,7 +386,7 @@ pub fn create( InterpreterAction::NewFrame(NewFrameAction::Create(Box::new(CreateInputs { caller: interpreter.contract.target_address, scheme, - value, + value: value.into(), init_code: code, gas_limit, }))); @@ -397,10 +397,10 @@ pub fn call(interpreter: &mut Interpreter, host: & pop!(interpreter, local_gas_limit); pop_address!(interpreter, to); // max gas limit is not possible in real ethereum situation. - let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX); + let local_gas_limit = u64::try_from(local_gas_limit.into()).unwrap_or(u64::MAX); pop!(interpreter, value); - let has_transfer = !value.is_zero(); + let has_transfer = !value.to_u256().is_zero(); if interpreter.is_static && has_transfer { interpreter.instruction_result = InstructionResult::CallNotAllowedInsideStatic; return; @@ -435,7 +435,7 @@ pub fn call(interpreter: &mut Interpreter, host: & target_address: to, caller: interpreter.contract.target_address, bytecode_address: to, - value: CallValue::Transfer(value), + value: CallValue::Transfer(value.into()), scheme: CallScheme::Call, is_static: interpreter.is_static, is_eof: false, @@ -448,7 +448,7 @@ pub fn call_code(interpreter: &mut Interpreter, ho pop!(interpreter, local_gas_limit); pop_address!(interpreter, to); // max gas limit is not possible in real ethereum situation. - let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX); + let local_gas_limit = u64::try_from(local_gas_limit.into()).unwrap_or(u64::MAX); pop!(interpreter, value); let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(interpreter) else { @@ -461,16 +461,19 @@ pub fn call_code(interpreter: &mut Interpreter, ho }; // set is_empty to false as we are not creating this account. load.is_empty = false; - let Some(mut gas_limit) = - calc_call_gas::(interpreter, load, !value.is_zero(), local_gas_limit) - else { + let Some(mut gas_limit) = calc_call_gas::( + interpreter, + load, + !value.to_u256().is_zero(), + local_gas_limit, + ) else { return; }; gas!(interpreter, gas_limit); // add call stipend if there is value to be transferred. - if !value.is_zero() { + if !value.to_u256().is_zero() { gas_limit = gas_limit.saturating_add(gas::CALL_STIPEND); } @@ -482,7 +485,7 @@ pub fn call_code(interpreter: &mut Interpreter, ho target_address: interpreter.contract.target_address, caller: interpreter.contract.target_address, bytecode_address: to, - value: CallValue::Transfer(value), + value: CallValue::Transfer(value.into()), scheme: CallScheme::CallCode, is_static: interpreter.is_static, is_eof: false, @@ -496,7 +499,7 @@ pub fn delegate_call(interpreter: &mut Interpreter pop!(interpreter, local_gas_limit); pop_address!(interpreter, to); // max gas limit is not possible in real ethereum situation. - let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX); + let local_gas_limit = u64::try_from(local_gas_limit.into()).unwrap_or(u64::MAX); let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(interpreter) else { return; @@ -536,7 +539,7 @@ pub fn static_call(interpreter: &mut Interpreter, pop!(interpreter, local_gas_limit); pop_address!(interpreter, to); // max gas limit is not possible in real ethereum situation. - let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX); + let local_gas_limit = u64::try_from(local_gas_limit.into()).unwrap_or(u64::MAX); let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(interpreter) else { return; diff --git a/crates/interpreter/src/instructions/contract/call_helpers.rs b/crates/interpreter/src/instructions/contract/call_helpers.rs index 0de9baa548..718678b216 100644 --- a/crates/interpreter/src/instructions/contract/call_helpers.rs +++ b/crates/interpreter/src/instructions/contract/call_helpers.rs @@ -9,14 +9,14 @@ pub fn get_memory_input_and_out_ranges( ) -> Option<(Bytes, Range)> { pop_ret!(interpreter, in_offset, in_len, out_offset, out_len, None); - let in_range = resize_memory(interpreter, in_offset, in_len)?; + let in_range = resize_memory(interpreter, in_offset.into(), in_len.into())?; let mut input = Bytes::new(); if !in_range.is_empty() { input = Bytes::copy_from_slice(interpreter.shared_memory.slice_range(in_range)); } - let ret_range = resize_memory(interpreter, out_offset, out_len)?; + let ret_range = resize_memory(interpreter, out_offset.into(), out_len.into())?; Some((input, ret_range)) } diff --git a/crates/interpreter/src/instructions/control.rs b/crates/interpreter/src/instructions/control.rs index cf4582912c..8b427761d7 100644 --- a/crates/interpreter/src/instructions/control.rs +++ b/crates/interpreter/src/instructions/control.rs @@ -19,7 +19,7 @@ pub fn rjumpi(interpreter: &mut Interpreter, _host: &mut H) { // In spec it is +3 but pointer is already incremented in // `Interpreter::step` so for revm is +2. let mut offset = 2; - if !condition.is_zero() { + if !condition.to_u256().is_zero() { offset += unsafe { read_i16(interpreter.instruction_pointer) } as isize; } @@ -54,14 +54,14 @@ pub fn rjumpv(interpreter: &mut Interpreter, _host: &mut H) { pub fn jump(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::MID); pop!(interpreter, target); - jump_inner(interpreter, target); + jump_inner(interpreter, target.into()); } pub fn jumpi(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::HIGH); pop!(interpreter, target, cond); - if !cond.is_zero() { - jump_inner(interpreter, target); + if !cond.to_u256().is_zero() { + jump_inner(interpreter, target.into()); } } @@ -148,7 +148,10 @@ pub fn jumpf(interpreter: &mut Interpreter, _host: &mut H) { pub fn pc(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::BASE); // - 1 because we have already advanced the instruction pointer in `Interpreter::step` - push!(interpreter, U256::from(interpreter.program_counter() - 1)); + push!( + interpreter, + U256::from(interpreter.program_counter() - 1).into() + ); } #[inline] @@ -235,8 +238,8 @@ mod test { [RJUMPI, 0x00, 0x03, RJUMPI, 0x00, 0x01, STOP, STOP].into(), )); interp.is_eof = true; - interp.stack.push(U256::from(1)).unwrap(); - interp.stack.push(U256::from(0)).unwrap(); + interp.stack.push(U256::from(1).into()).unwrap(); + interp.stack.push(U256::from(0).into()).unwrap(); interp.gas = Gas::new(10000); // dont jump @@ -273,7 +276,7 @@ mod test { interp.gas = Gas::new(1000); // more then max_index - interp.stack.push(U256::from(10)).unwrap(); + interp.stack.push(U256::from(10).into()).unwrap(); interp.step(&table, &mut host); assert_eq!(interp.program_counter(), 6); @@ -285,7 +288,7 @@ mod test { assert_eq!(interp.program_counter(), 0); // jump to first index of vtable - interp.stack.push(U256::from(0)).unwrap(); + interp.stack.push(U256::from(0).into()).unwrap(); interp.step(&table, &mut host); assert_eq!(interp.program_counter(), 7); @@ -296,7 +299,7 @@ mod test { assert_eq!(interp.program_counter(), 0); // jump to second index of vtable - interp.stack.push(U256::from(1)).unwrap(); + interp.stack.push(U256::from(1).into()).unwrap(); interp.step(&table, &mut host); assert_eq!(interp.program_counter(), 8); } diff --git a/crates/interpreter/src/instructions/data.rs b/crates/interpreter/src/instructions/data.rs index a9257205b5..151a038e76 100644 --- a/crates/interpreter/src/instructions/data.rs +++ b/crates/interpreter/src/instructions/data.rs @@ -23,7 +23,7 @@ pub fn data_load(interpreter: &mut Interpreter, _host: &mut H) let mut word = [0u8; 32]; word[..slice.len()].copy_from_slice(slice); - *offset = U256::from_be_bytes(word); + *offset = U256::from_be_bytes(word).into(); } pub fn data_loadn(interpreter: &mut Interpreter, _host: &mut H) { @@ -52,7 +52,7 @@ pub fn data_size(interpreter: &mut Interpreter, _host: &mut H) gas!(interpreter, BASE); let data_size = interpreter.eof().expect("eof").header.data_size; - push!(interpreter, U256::from(data_size)); + push!(interpreter, U256::from(data_size).into()); } pub fn data_copy(interpreter: &mut Interpreter, _host: &mut H) { @@ -119,22 +119,24 @@ mod test { interp.gas = Gas::new(10000); // DATALOAD - interp.stack.push(U256::from(0)).unwrap(); + interp.stack.push(U256::from(0).into()).unwrap(); interp.step(&table, &mut host); - assert_eq!(interp.stack.data(), &vec![U256::from(0x01)]); + assert_eq!(interp.stack.data(), &vec![U256::from(0x01).into()]); interp.stack.pop().unwrap(); // DATALOADN interp.step(&table, &mut host); - assert_eq!(interp.stack.data(), &vec![U256::from(0x01)]); + assert_eq!(interp.stack.data(), &vec![U256::from(0x01).into()]); interp.stack.pop().unwrap(); // DATALOAD (padding) - interp.stack.push(U256::from(35)).unwrap(); + interp.stack.push(U256::from(35).into()).unwrap(); interp.step(&table, &mut host); assert_eq!( interp.stack.data(), - &vec![b256!("0500000000000000000000000000000000000000000000000000000000000000").into()] + &vec![b256!( + "0500000000000000000000000000000000000000000000000000000000000000" + )] ); interp.stack.pop().unwrap(); @@ -147,19 +149,19 @@ mod test { interp.stack.pop().unwrap(); // DATALOAD (out of bounds) - interp.stack.push(U256::from(36)).unwrap(); + interp.stack.push(U256::from(36).into()).unwrap(); interp.step(&table, &mut host); - assert_eq!(interp.stack.data(), &vec![U256::ZERO]); + assert_eq!(interp.stack.data(), &vec![U256::ZERO.into()]); interp.stack.pop().unwrap(); // DATALOADN (out of bounds) interp.step(&table, &mut host); - assert_eq!(interp.stack.data(), &vec![U256::ZERO]); + assert_eq!(interp.stack.data(), &vec![U256::ZERO.into()]); interp.stack.pop().unwrap(); // DATA SIZE interp.step(&table, &mut host); - assert_eq!(interp.stack.data(), &vec![U256::from(36)]); + assert_eq!(interp.stack.data(), &vec![U256::from(36).into()]); } #[test] @@ -173,9 +175,9 @@ mod test { // Data copy // size, offset mem_offset, - interp.stack.push(U256::from(32)).unwrap(); - interp.stack.push(U256::from(0)).unwrap(); - interp.stack.push(U256::from(0)).unwrap(); + interp.stack.push(U256::from(32).into()).unwrap(); + interp.stack.push(U256::from(0).into()).unwrap(); + interp.stack.push(U256::from(0).into()).unwrap(); interp.step(&table, &mut host); assert_eq!( interp.shared_memory.context_memory(), @@ -184,9 +186,9 @@ mod test { // Data copy (Padding) // size, offset mem_offset, - interp.stack.push(U256::from(2)).unwrap(); - interp.stack.push(U256::from(35)).unwrap(); - interp.stack.push(U256::from(1)).unwrap(); + interp.stack.push(U256::from(2).into()).unwrap(); + interp.stack.push(U256::from(35).into()).unwrap(); + interp.stack.push(U256::from(1).into()).unwrap(); interp.step(&table, &mut host); assert_eq!( interp.shared_memory.context_memory(), @@ -195,9 +197,9 @@ mod test { // Data copy (Out of bounds) // size, offset mem_offset, - interp.stack.push(U256::from(2)).unwrap(); - interp.stack.push(U256::from(37)).unwrap(); - interp.stack.push(U256::from(1)).unwrap(); + interp.stack.push(U256::from(2).into()).unwrap(); + interp.stack.push(U256::from(37).into()).unwrap(); + interp.stack.push(U256::from(1).into()).unwrap(); interp.step(&table, &mut host); assert_eq!( interp.shared_memory.context_memory(), @@ -206,9 +208,9 @@ mod test { // Data copy (Size == 0) // mem_offset, offset, size - interp.stack.push(U256::from(0)).unwrap(); - interp.stack.push(U256::from(37)).unwrap(); - interp.stack.push(U256::from(1)).unwrap(); + interp.stack.push(U256::from(0).into()).unwrap(); + interp.stack.push(U256::from(37).into()).unwrap(); + interp.stack.push(U256::from(1).into()).unwrap(); interp.step(&table, &mut host); assert_eq!( interp.shared_memory.context_memory(), diff --git a/crates/interpreter/src/instructions/host.rs b/crates/interpreter/src/instructions/host.rs index adf9c5be95..944b062fe9 100644 --- a/crates/interpreter/src/instructions/host.rs +++ b/crates/interpreter/src/instructions/host.rs @@ -27,7 +27,7 @@ pub fn balance(interpreter: &mut Interpreter, host 20 } ); - push!(interpreter, balance.data); + push!(interpreter, balance.data.into()); } /// EIP-1884: Repricing for trie-size-dependent opcodes diff --git a/crates/interpreter/src/instructions/memory.rs b/crates/interpreter/src/instructions/memory.rs index 0ba63903d5..a5c50ed1c5 100644 --- a/crates/interpreter/src/instructions/memory.rs +++ b/crates/interpreter/src/instructions/memory.rs @@ -1,4 +1,4 @@ -use crate::{gas, Host, Interpreter}; +use crate::{gas, interpreter::StackValueData, Host, Interpreter}; use core::cmp::max; use primitives::U256; use specification::hardfork::Spec; @@ -8,7 +8,7 @@ pub fn mload(interpreter: &mut Interpreter, _host: &mut H) { pop_top!(interpreter, top); let offset = as_usize_or_fail!(interpreter, top); resize_memory!(interpreter, offset, 32); - *top = interpreter.shared_memory.get_u256(offset); + *top = interpreter.shared_memory.get_u256(offset).into(); } pub fn mstore(interpreter: &mut Interpreter, _host: &mut H) { @@ -16,7 +16,7 @@ pub fn mstore(interpreter: &mut Interpreter, _host: &mut H) { pop!(interpreter, offset, value); let offset = as_usize_or_fail!(interpreter, offset); resize_memory!(interpreter, offset, 32); - interpreter.shared_memory.set_u256(offset, value); + interpreter.shared_memory.set_u256(offset, value.into()); } pub fn mstore8(interpreter: &mut Interpreter, _host: &mut H) { @@ -24,12 +24,17 @@ pub fn mstore8(interpreter: &mut Interpreter, _host: &mut H) { pop!(interpreter, offset, value); let offset = as_usize_or_fail!(interpreter, offset); resize_memory!(interpreter, offset, 1); - interpreter.shared_memory.set_byte(offset, value.byte(0)) + interpreter + .shared_memory + .set_byte(offset, value.into().byte(0)) } pub fn msize(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::BASE); - push!(interpreter, U256::from(interpreter.shared_memory.len())); + push!( + interpreter, + U256::from(interpreter.shared_memory.len()).into() + ); } // EIP-5656: MCOPY - Memory copying instruction diff --git a/crates/interpreter/src/instructions/tx_info.rs b/crates/interpreter/src/instructions/tx_info.rs index 52f6fe359e..b159d7c1dc 100644 --- a/crates/interpreter/src/instructions/tx_info.rs +++ b/crates/interpreter/src/instructions/tx_info.rs @@ -1,4 +1,4 @@ -use crate::{gas, Host, Interpreter}; +use crate::{gas, interpreter::StackValueData, Host, Interpreter}; use primitives::U256; use specification::hardfork::Spec; use transaction::Eip4844Tx; @@ -8,7 +8,10 @@ pub fn gasprice(interpreter: &mut Interpreter, host: &mut H) { gas!(interpreter, gas::BASE); let env = host.env(); let basefee = *env.block.basefee(); - push!(interpreter, env.tx.effective_gas_price(basefee)); + push!( + interpreter, + StackValueData::Public(env.tx.effective_gas_price(basefee)) + ); } pub fn origin(interpreter: &mut Interpreter, host: &mut H) { @@ -34,6 +37,6 @@ pub fn blob_hash(interpreter: &mut Interpreter, ho .map(|b| U256::from_be_bytes(*b)) .unwrap_or(U256::ZERO) } else { - U256::ZERO + StackValueData::Public(U256::ZERO) }; } diff --git a/crates/interpreter/src/interpreter.rs b/crates/interpreter/src/interpreter.rs index 8198eec0f2..32ca56a464 100644 --- a/crates/interpreter/src/interpreter.rs +++ b/crates/interpreter/src/interpreter.rs @@ -6,7 +6,7 @@ mod stack; pub use contract::Contract; pub use shared_memory::{num_words, SharedMemory, EMPTY_SHARED_MEMORY}; -pub use stack::{Stack, STACK_LIMIT}; +pub use stack::{Stack, StackValueData, STACK_LIMIT}; use crate::{ gas, push, push_b256, return_ok, return_revert, CallOutcome, CreateOutcome, FunctionStack, Gas, @@ -60,6 +60,7 @@ pub struct Interpreter { /// Set inside CALL or CREATE instructions and RETURN or REVERT instructions. Additionally those instructions will set /// InstructionResult to CallOrCreate/Return/Revert so we know the reason. pub next_action: InterpreterAction, + // pub circuit_builder: WRK17CircuitBuilder, } impl Default for Interpreter { @@ -150,9 +151,9 @@ impl Interpreter { /// Depending on the `InstructionResult` indicated by `create_outcome`, it performs one of the following: /// /// - `Ok`: Pushes the address from `create_outcome` to the stack, updates gas costs, and records any gas refunds. - /// - `Revert`: Pushes `U256::ZERO` to the stack and updates gas costs. + /// - `Revert`: Pushes `StackValueData::Public(U256::ZERO` to the stack and updates gas costs) /// - `FatalExternalError`: Sets the `instruction_result` to `InstructionResult::FatalExternalError`. - /// - `Default`: Pushes `U256::ZERO` to the stack. + /// - `Default`: Pushes `StackValueData::Public(U256::ZERO` to the stack) /// /// # Side Effects /// @@ -180,14 +181,14 @@ impl Interpreter { self.gas.record_refund(create_outcome.gas().refunded()); } return_revert!() => { - push!(self, U256::ZERO); + push!(self, StackValueData::Public(U256::ZERO)); self.gas.erase_cost(create_outcome.gas().remaining()); } InstructionResult::FatalExternalError => { panic!("Fatal external error in insert_create_outcome"); } _ => { - push!(self, U256::ZERO); + push!(self, StackValueData::Public(U256::ZERO)) } } } @@ -214,14 +215,14 @@ impl Interpreter { self.gas.record_refund(create_outcome.gas().refunded()); } return_revert!() => { - push!(self, U256::ZERO); + push!(self, StackValueData::Public(U256::ZERO)); self.gas.erase_cost(create_outcome.gas().remaining()); } InstructionResult::FatalExternalError => { panic!("Fatal external error in insert_eofcreate_outcome"); } _ => { - push!(self, U256::ZERO); + push!(self, StackValueData::Public(U256::ZERO)) } } } @@ -271,9 +272,9 @@ impl Interpreter { push!( self, if self.is_eof { - U256::ZERO + StackValueData::Public(U256::ZERO) } else { - U256::from(1) + StackValueData::Public(U256::from(1)) } ); } @@ -283,9 +284,9 @@ impl Interpreter { push!( self, if self.is_eof { - U256::from(1) + StackValueData::Public(U256::from(1)) } else { - U256::ZERO + StackValueData::Public(U256::ZERO) } ); } @@ -296,9 +297,9 @@ impl Interpreter { push!( self, if self.is_eof { - U256::from(2) + StackValueData::Public(U256::from(2)) } else { - U256::ZERO + StackValueData::Public(U256::ZERO) } ); } diff --git a/crates/interpreter/src/interpreter/stack.rs b/crates/interpreter/src/interpreter/stack.rs index 2607d538e1..1362760b65 100644 --- a/crates/interpreter/src/interpreter/stack.rs +++ b/crates/interpreter/src/interpreter/stack.rs @@ -7,12 +7,50 @@ use std::vec::Vec; /// EVM interpreter stack limit. pub const STACK_LIMIT: usize = 1024; -#[derive(Debug, PartialEq, Eq, Hash, Clone, serde::Serialize)] +// Stack value data. Supports both public and private values. +// - Private values are represented as a vector of gate input indices created via circuit builder +// - Public values are represented as U256 +#[derive(Debug, PartialEq, Eq, Hash, Clone, serde::Serialize, serde::Deserialize)] pub enum StackValueData { Private(GateIndexVec), Public(U256), } +pub trait IntoStackValue { + fn into_stack_value(self) -> StackValueData; +} + +impl IntoStackValue for U256 { + fn into_stack_value(self) -> StackValueData { + StackValueData::Public(self) + } +} + +impl Into for StackValueData { + fn into(self) -> U256 { + match self { + StackValueData::Public(value) => value, + StackValueData::Private(_) => panic!("Cannot convert private value to U256"), + } + } +} + +// Add From implementation for ergonomics +impl From for StackValueData { + fn from(value: U256) -> Self { + StackValueData::Public(value) + } +} + +impl StackValueData { + pub fn to_u256(&self) -> U256 { + match self { + StackValueData::Public(value) => *value, + StackValueData::Private(_) => panic!("Cannot convert private value to U256"), + } + } +} + /// EVM stack with [STACK_LIMIT] capacity of words. #[derive(Debug, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(serde::Serialize))] @@ -28,7 +66,7 @@ impl fmt::Display for Stack { if i > 0 { f.write_str(", ")?; } - write!(f, "{x}")?; + write!(f, "{:?}", x)?; } f.write_str("]") } @@ -248,7 +286,7 @@ impl Stack { #[inline] pub fn peek(&self, no_from_top: usize) -> Result { if self.data.len() > no_from_top { - Ok(self.data[self.data.len() - no_from_top - 1]) + Ok(self.data[self.data.len() - no_from_top - 1].clone()) } else { Err(InstructionResult::StackUnderflow) } @@ -318,8 +356,8 @@ impl Stack { Ok(()) } - /// Pushes an arbitrary length slice of bytes onto the stack, padding the last word with zeros - /// if necessary. + /// Pushes an arbitrary length slice of bytes onto the stack as StackValueData::Public, + /// padding the last word with zeros if necessary. #[inline] pub fn push_slice(&mut self, slice: &[u8]) -> Result<(), InstructionResult> { if slice.is_empty() { @@ -334,6 +372,7 @@ impl Stack { // SAFETY: length checked above. unsafe { + let mut tmp: Vec = vec![0u8; 32]; let dst = self.data.as_mut_ptr().add(self.data.len()).cast::(); self.data.set_len(new_len); @@ -343,8 +382,6 @@ impl Stack { let words = slice.chunks_exact(32); let partial_last_word = words.remainder(); for word in words { - // Note: we unroll `U256::from_be_bytes` here to write directly into the buffer, - // instead of creating a 32 byte array on the stack and then copying it over. for l in word.rchunks_exact(8) { dst.add(i).write(u64::from_be_bytes(l.try_into().unwrap())); i += 1; @@ -378,6 +415,12 @@ impl Stack { if m != 0 { dst.add(i).write_bytes(0, 4 - m); } + + // Convert the last 32 bytes to U256 and push as StackValueData::Public + let copy_len = std::cmp::min(32, slice.len()); + tmp[32 - copy_len..].copy_from_slice(&slice[..copy_len]); + let value = U256::from_be_slice(&tmp); + self.data.push(StackValueData::Public(value)); } Ok(()) @@ -447,38 +490,41 @@ mod tests { // one word run(|stack| { stack.push_slice(&[42]).unwrap(); - assert_eq!(stack.data, [U256::from(42)]); + assert_eq!(stack.data, [U256::from(42).into()]); }); let n = 0x1111_2222_3333_4444_5555_6666_7777_8888_u128; run(|stack| { stack.push_slice(&n.to_be_bytes()).unwrap(); - assert_eq!(stack.data, [U256::from(n)]); + assert_eq!(stack.data, [U256::from(n).into()]); }); // more than one word run(|stack| { let b = [U256::from(n).to_be_bytes::<32>(); 2].concat(); stack.push_slice(&b).unwrap(); - assert_eq!(stack.data, [U256::from(n); 2]); + assert_eq!(stack.data, vec![StackValueData::Public(U256::from(n)); 2]); }); run(|stack| { let b = [&[0; 32][..], &[42u8]].concat(); stack.push_slice(&b).unwrap(); - assert_eq!(stack.data, [U256::ZERO, U256::from(42)]); + assert_eq!(stack.data, [U256::ZERO.into(), U256::from(42).into()]); }); run(|stack| { let b = [&[0; 32][..], &n.to_be_bytes()].concat(); stack.push_slice(&b).unwrap(); - assert_eq!(stack.data, [U256::ZERO, U256::from(n)]); + assert_eq!(stack.data, [U256::ZERO.into(), U256::from(n).into()]); }); run(|stack| { let b = [&[0; 64][..], &n.to_be_bytes()].concat(); stack.push_slice(&b).unwrap(); - assert_eq!(stack.data, [U256::ZERO, U256::ZERO, U256::from(n)]); + assert_eq!( + stack.data, + [U256::ZERO.into(), U256::ZERO.into(), U256::from(n).into()] + ); }); } @@ -494,7 +540,7 @@ mod tests { // Test cloning a partially filled stack let mut partial_stack = Stack::new(); for i in 0..10 { - partial_stack.push(U256::from(i)).unwrap(); + partial_stack.push(U256::from(i).into()).unwrap(); } let mut cloned_partial = partial_stack.clone(); assert_eq!(partial_stack, cloned_partial); @@ -502,7 +548,7 @@ mod tests { assert_eq!(cloned_partial.data().capacity(), STACK_LIMIT); // Test that modifying the clone doesn't affect the original - cloned_partial.push(U256::from(100)).unwrap(); + cloned_partial.push(U256::from(100).into()).unwrap(); assert_ne!(partial_stack, cloned_partial); assert_eq!(partial_stack.len(), 10); assert_eq!(cloned_partial.len(), 11); @@ -510,7 +556,7 @@ mod tests { // Test cloning a full stack let mut full_stack = Stack::new(); for i in 0..STACK_LIMIT { - full_stack.push(U256::from(i)).unwrap(); + full_stack.push(U256::from(i).into()).unwrap(); } let mut cloned_full = full_stack.clone(); assert_eq!(full_stack, cloned_full); @@ -519,11 +565,11 @@ mod tests { // Test push to the full original or cloned stack should return StackOverflow assert_eq!( - full_stack.push(U256::from(100)), + full_stack.push(U256::from(100).into()), Err(InstructionResult::StackOverflow) ); assert_eq!( - cloned_full.push(U256::from(100)), + cloned_full.push(U256::from(100).into()), Err(InstructionResult::StackOverflow) ); } From e7a5ab00138e41e3039e1b0499c37c90eb0a715a Mon Sep 17 00:00:00 2001 From: "Lucas (Kbooz)" Date: Tue, 17 Dec 2024 18:52:15 -0300 Subject: [PATCH 03/44] refactor: simplify stack value handling by removing unnecessary conversions --- .../src/instructions/block_info.rs | 29 ++++-------------- .../interpreter/src/instructions/control.rs | 10 +++---- crates/interpreter/src/instructions/data.rs | 30 +++++++++---------- crates/interpreter/src/instructions/memory.rs | 2 +- crates/interpreter/src/instructions/stack.rs | 20 ++++++------- crates/interpreter/src/instructions/system.rs | 4 +-- .../interpreter/src/instructions/tx_info.rs | 5 +--- crates/interpreter/src/interpreter/stack.rs | 22 +++++++++----- 8 files changed, 55 insertions(+), 67 deletions(-) diff --git a/crates/interpreter/src/instructions/block_info.rs b/crates/interpreter/src/instructions/block_info.rs index 99114361c6..52b916bf64 100644 --- a/crates/interpreter/src/instructions/block_info.rs +++ b/crates/interpreter/src/instructions/block_info.rs @@ -17,18 +17,12 @@ pub fn coinbase(interpreter: &mut Interpreter, host: &mut H) { pub fn timestamp(interpreter: &mut Interpreter, host: &mut H) { gas!(interpreter, gas::BASE); - push!( - interpreter, - StackValueData::Public(*host.env().block.timestamp()) - ); + push!(interpreter, *host.env().block.timestamp()); } pub fn block_number(interpreter: &mut Interpreter, host: &mut H) { gas!(interpreter, gas::BASE); - push!( - interpreter, - StackValueData::Public(*host.env().block.number()) - ); + push!(interpreter, *host.env().block.number()); } pub fn difficulty(interpreter: &mut Interpreter, host: &mut H) { @@ -36,29 +30,20 @@ pub fn difficulty(interpreter: &mut Interpreter, h if SPEC::enabled(MERGE) { push_b256!(interpreter, *host.env().block.prevrandao().unwrap()); } else { - push!( - interpreter, - StackValueData::Public(*host.env().block.difficulty()) - ); + push!(interpreter, *host.env().block.difficulty()); } } pub fn gaslimit(interpreter: &mut Interpreter, host: &mut H) { gas!(interpreter, gas::BASE); - push!( - interpreter, - StackValueData::Public(*host.env().block.gas_limit()) - ); + push!(interpreter, *host.env().block.gas_limit()); } /// EIP-3198: BASEFEE opcode pub fn basefee(interpreter: &mut Interpreter, host: &mut H) { check!(interpreter, LONDON); gas!(interpreter, gas::BASE); - push!( - interpreter, - StackValueData::Public(*host.env().block.basefee()) - ); + push!(interpreter, *host.env().block.basefee()); } /// EIP-7516: BLOBBASEFEE opcode @@ -67,8 +52,6 @@ pub fn blob_basefee(interpreter: &mut Interpreter, gas!(interpreter, gas::BASE); push!( interpreter, - StackValueData::Public(U256::from( - host.env().block.blob_gasprice().unwrap_or_default() - )) + U256::from(host.env().block.blob_gasprice().unwrap_or_default()) ); } diff --git a/crates/interpreter/src/instructions/control.rs b/crates/interpreter/src/instructions/control.rs index 8b427761d7..a3a91de30e 100644 --- a/crates/interpreter/src/instructions/control.rs +++ b/crates/interpreter/src/instructions/control.rs @@ -238,8 +238,8 @@ mod test { [RJUMPI, 0x00, 0x03, RJUMPI, 0x00, 0x01, STOP, STOP].into(), )); interp.is_eof = true; - interp.stack.push(U256::from(1).into()).unwrap(); - interp.stack.push(U256::from(0).into()).unwrap(); + interp.stack.push(U256::from(1)).unwrap(); + interp.stack.push(U256::from(0)).unwrap(); interp.gas = Gas::new(10000); // dont jump @@ -276,7 +276,7 @@ mod test { interp.gas = Gas::new(1000); // more then max_index - interp.stack.push(U256::from(10).into()).unwrap(); + interp.stack.push(U256::from(10)).unwrap(); interp.step(&table, &mut host); assert_eq!(interp.program_counter(), 6); @@ -288,7 +288,7 @@ mod test { assert_eq!(interp.program_counter(), 0); // jump to first index of vtable - interp.stack.push(U256::from(0).into()).unwrap(); + interp.stack.push(U256::from(0)).unwrap(); interp.step(&table, &mut host); assert_eq!(interp.program_counter(), 7); @@ -299,7 +299,7 @@ mod test { assert_eq!(interp.program_counter(), 0); // jump to second index of vtable - interp.stack.push(U256::from(1).into()).unwrap(); + interp.stack.push(U256::from(1)).unwrap(); interp.step(&table, &mut host); assert_eq!(interp.program_counter(), 8); } diff --git a/crates/interpreter/src/instructions/data.rs b/crates/interpreter/src/instructions/data.rs index 151a038e76..2ecc599571 100644 --- a/crates/interpreter/src/instructions/data.rs +++ b/crates/interpreter/src/instructions/data.rs @@ -119,7 +119,7 @@ mod test { interp.gas = Gas::new(10000); // DATALOAD - interp.stack.push(U256::from(0).into()).unwrap(); + interp.stack.push(U256::from(0)).unwrap(); interp.step(&table, &mut host); assert_eq!(interp.stack.data(), &vec![U256::from(0x01).into()]); interp.stack.pop().unwrap(); @@ -130,7 +130,7 @@ mod test { interp.stack.pop().unwrap(); // DATALOAD (padding) - interp.stack.push(U256::from(35).into()).unwrap(); + interp.stack.push(U256::from(35)).unwrap(); interp.step(&table, &mut host); assert_eq!( interp.stack.data(), @@ -149,7 +149,7 @@ mod test { interp.stack.pop().unwrap(); // DATALOAD (out of bounds) - interp.stack.push(U256::from(36).into()).unwrap(); + interp.stack.push(U256::from(36)).unwrap(); interp.step(&table, &mut host); assert_eq!(interp.stack.data(), &vec![U256::ZERO.into()]); interp.stack.pop().unwrap(); @@ -175,9 +175,9 @@ mod test { // Data copy // size, offset mem_offset, - interp.stack.push(U256::from(32).into()).unwrap(); - interp.stack.push(U256::from(0).into()).unwrap(); - interp.stack.push(U256::from(0).into()).unwrap(); + interp.stack.push(U256::from(32)).unwrap(); + interp.stack.push(U256::from(0)).unwrap(); + interp.stack.push(U256::from(0)).unwrap(); interp.step(&table, &mut host); assert_eq!( interp.shared_memory.context_memory(), @@ -186,9 +186,9 @@ mod test { // Data copy (Padding) // size, offset mem_offset, - interp.stack.push(U256::from(2).into()).unwrap(); - interp.stack.push(U256::from(35).into()).unwrap(); - interp.stack.push(U256::from(1).into()).unwrap(); + interp.stack.push(U256::from(2)).unwrap(); + interp.stack.push(U256::from(35)).unwrap(); + interp.stack.push(U256::from(1)).unwrap(); interp.step(&table, &mut host); assert_eq!( interp.shared_memory.context_memory(), @@ -197,9 +197,9 @@ mod test { // Data copy (Out of bounds) // size, offset mem_offset, - interp.stack.push(U256::from(2).into()).unwrap(); - interp.stack.push(U256::from(37).into()).unwrap(); - interp.stack.push(U256::from(1).into()).unwrap(); + interp.stack.push(U256::from(2)).unwrap(); + interp.stack.push(U256::from(37)).unwrap(); + interp.stack.push(U256::from(1)).unwrap(); interp.step(&table, &mut host); assert_eq!( interp.shared_memory.context_memory(), @@ -208,9 +208,9 @@ mod test { // Data copy (Size == 0) // mem_offset, offset, size - interp.stack.push(U256::from(0).into()).unwrap(); - interp.stack.push(U256::from(37).into()).unwrap(); - interp.stack.push(U256::from(1).into()).unwrap(); + interp.stack.push(U256::from(0)).unwrap(); + interp.stack.push(U256::from(37)).unwrap(); + interp.stack.push(U256::from(1)).unwrap(); interp.step(&table, &mut host); assert_eq!( interp.shared_memory.context_memory(), diff --git a/crates/interpreter/src/instructions/memory.rs b/crates/interpreter/src/instructions/memory.rs index a5c50ed1c5..a3f2e14800 100644 --- a/crates/interpreter/src/instructions/memory.rs +++ b/crates/interpreter/src/instructions/memory.rs @@ -1,4 +1,4 @@ -use crate::{gas, interpreter::StackValueData, Host, Interpreter}; +use crate::{gas, Host, Interpreter}; use core::cmp::max; use primitives::U256; use specification::hardfork::Spec; diff --git a/crates/interpreter/src/instructions/stack.rs b/crates/interpreter/src/instructions/stack.rs index 4d2bf5881a..db8f24f86f 100644 --- a/crates/interpreter/src/instructions/stack.rs +++ b/crates/interpreter/src/instructions/stack.rs @@ -105,9 +105,9 @@ mod test { interp.stack.push(U256::from(10)).unwrap(); interp.stack.push(U256::from(20)).unwrap(); interp.step(&table, &mut host); - assert_eq!(interp.stack.pop(), Ok(U256::from(20))); + assert_eq!(interp.stack.pop(), Ok(U256::from(20).into())); interp.step(&table, &mut host); - assert_eq!(interp.stack.pop(), Ok(U256::from(10))); + assert_eq!(interp.stack.pop(), Ok(U256::from(10).into())); interp.step(&table, &mut host); assert_eq!(interp.instruction_result, InstructionResult::StackUnderflow); } @@ -125,11 +125,11 @@ mod test { interp.stack.push(U256::from(20)).unwrap(); interp.stack.push(U256::from(0)).unwrap(); interp.step(&table, &mut host); - assert_eq!(interp.stack.peek(0), Ok(U256::from(20))); - assert_eq!(interp.stack.peek(1), Ok(U256::from(0))); + assert_eq!(interp.stack.peek(0), Ok(U256::from(20).into())); + assert_eq!(interp.stack.peek(1), Ok(U256::from(0).into())); interp.step(&table, &mut host); - assert_eq!(interp.stack.peek(0), Ok(U256::from(10))); - assert_eq!(interp.stack.peek(2), Ok(U256::from(20))); + assert_eq!(interp.stack.peek(0), Ok(U256::from(10).into())); + assert_eq!(interp.stack.peek(2), Ok(U256::from(20).into())); } #[test] @@ -148,10 +148,10 @@ mod test { interp.stack.push(U256::from(0)).unwrap(); interp.step(&table, &mut host); - assert_eq!(interp.stack.peek(1), Ok(U256::from(10))); - assert_eq!(interp.stack.peek(2), Ok(U256::from(15))); + assert_eq!(interp.stack.peek(1), Ok(U256::from(10).into())); + assert_eq!(interp.stack.peek(2), Ok(U256::from(15).into())); interp.step(&table, &mut host); - assert_eq!(interp.stack.peek(2), Ok(U256::from(1))); - assert_eq!(interp.stack.peek(4), Ok(U256::from(15))); + assert_eq!(interp.stack.peek(2), Ok(U256::from(1).into())); + assert_eq!(interp.stack.peek(4), Ok(U256::from(15).into())); } } diff --git a/crates/interpreter/src/instructions/system.rs b/crates/interpreter/src/instructions/system.rs index a07a4f083d..607ee55f40 100644 --- a/crates/interpreter/src/instructions/system.rs +++ b/crates/interpreter/src/instructions/system.rs @@ -320,7 +320,7 @@ mod test { interp.stack.push(U256::from(32)).unwrap(); interp .stack - .push(U256::from(interp.return_data_buffer.len() - 32)) + .push(U256::from(interp.return_data_buffer.len() - 32).into()) .unwrap(); interp.stack.push(U256::from(0)).unwrap(); interp.step(&table, &mut host); @@ -334,7 +334,7 @@ mod test { interp.stack.push(U256::from(32)).unwrap(); interp .stack - .push(U256::from(interp.return_data_buffer.len())) + .push(U256::from(interp.return_data_buffer.len()).into()) .unwrap(); interp.stack.push(U256::from(0)).unwrap(); interp.step(&table, &mut host); diff --git a/crates/interpreter/src/instructions/tx_info.rs b/crates/interpreter/src/instructions/tx_info.rs index b159d7c1dc..755084ba51 100644 --- a/crates/interpreter/src/instructions/tx_info.rs +++ b/crates/interpreter/src/instructions/tx_info.rs @@ -8,10 +8,7 @@ pub fn gasprice(interpreter: &mut Interpreter, host: &mut H) { gas!(interpreter, gas::BASE); let env = host.env(); let basefee = *env.block.basefee(); - push!( - interpreter, - StackValueData::Public(env.tx.effective_gas_price(basefee)) - ); + push!(interpreter, env.tx.effective_gas_price(basefee)); } pub fn origin(interpreter: &mut Interpreter, host: &mut H) { diff --git a/crates/interpreter/src/interpreter/stack.rs b/crates/interpreter/src/interpreter/stack.rs index 1362760b65..be6226a7ad 100644 --- a/crates/interpreter/src/interpreter/stack.rs +++ b/crates/interpreter/src/interpreter/stack.rs @@ -262,7 +262,7 @@ impl Stack { /// returns `StackOverflow` error and leaves the stack unchanged. #[inline] pub fn push_b256(&mut self, value: B256) -> Result<(), InstructionResult> { - self.push(StackValueData::Public(value.into())) + self.push_stack_value_data(StackValueData::Public(value.into())) } /// Push a new value onto the stack. @@ -270,7 +270,10 @@ impl Stack { /// If it will exceed the stack limit, returns `StackOverflow` error and leaves the stack /// unchanged. #[inline] - pub fn push(&mut self, value: StackValueData) -> Result<(), InstructionResult> { + pub fn push_stack_value_data( + &mut self, + value: StackValueData, + ) -> Result<(), InstructionResult> { // Allows the compiler to optimize out the `Vec::push` capacity check. assume!(self.data.capacity() == STACK_LIMIT); if self.data.len() == STACK_LIMIT { @@ -280,6 +283,11 @@ impl Stack { Ok(()) } + #[inline] + pub fn push(&mut self, value: U256) -> Result<(), InstructionResult> { + self.push_stack_value_data(StackValueData::Public(value)) + } + /// Peek a value at given index for the stack, where the top of /// the stack is at index `0`. If the index is too large, /// `StackError::Underflow` is returned. @@ -540,7 +548,7 @@ mod tests { // Test cloning a partially filled stack let mut partial_stack = Stack::new(); for i in 0..10 { - partial_stack.push(U256::from(i).into()).unwrap(); + partial_stack.push(U256::from(i)).unwrap(); } let mut cloned_partial = partial_stack.clone(); assert_eq!(partial_stack, cloned_partial); @@ -548,7 +556,7 @@ mod tests { assert_eq!(cloned_partial.data().capacity(), STACK_LIMIT); // Test that modifying the clone doesn't affect the original - cloned_partial.push(U256::from(100).into()).unwrap(); + cloned_partial.push(U256::from(100)).unwrap(); assert_ne!(partial_stack, cloned_partial); assert_eq!(partial_stack.len(), 10); assert_eq!(cloned_partial.len(), 11); @@ -556,7 +564,7 @@ mod tests { // Test cloning a full stack let mut full_stack = Stack::new(); for i in 0..STACK_LIMIT { - full_stack.push(U256::from(i).into()).unwrap(); + full_stack.push(U256::from(i)).unwrap(); } let mut cloned_full = full_stack.clone(); assert_eq!(full_stack, cloned_full); @@ -565,11 +573,11 @@ mod tests { // Test push to the full original or cloned stack should return StackOverflow assert_eq!( - full_stack.push(U256::from(100).into()), + full_stack.push(U256::from(100)), Err(InstructionResult::StackOverflow) ); assert_eq!( - cloned_full.push(U256::from(100).into()), + cloned_full.push(U256::from(100)), Err(InstructionResult::StackOverflow) ); } From 61dd070185fba85aeae36322e1811693546aa040 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Conserva?= Date: Wed, 18 Dec 2024 14:15:59 -0300 Subject: [PATCH 04/44] fix: update division and remainder operations to use correct zero-checking and cloning --- crates/interpreter/src/instructions/arithmetic.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/interpreter/src/instructions/arithmetic.rs b/crates/interpreter/src/instructions/arithmetic.rs index b8e9226e5f..ddfe198ae4 100644 --- a/crates/interpreter/src/instructions/arithmetic.rs +++ b/crates/interpreter/src/instructions/arithmetic.rs @@ -45,7 +45,7 @@ pub fn sub(interpreter: &mut Interpreter, _host: &mut H) { pub fn div(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::LOW); pop_top!(interpreter, op1, op2); - if !op2.into().is_zero() { + if !op2.to_u256().is_zero() { let garbled_op1 = ruint_to_garbled_uint(&op1.into()); let garbled_op2 = ruint_to_garbled_uint(&op2.clone().into()); let result = garbled_op1.div(garbled_op2); @@ -58,16 +58,16 @@ pub fn div(interpreter: &mut Interpreter, _host: &mut H) { pub fn sdiv(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::LOW); pop_top!(interpreter, op1, op2); - let result = i256_div(op1.into(), *op2.into()); + let result = i256_div(op1.into(), op2.clone().into()); *op2 = result.into(); } pub fn rem(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::LOW); pop_top!(interpreter, op1, op2); - if !op2.is_zero() { - let garbled_op1 = ruint_to_garbled_uint(&op1); - let garbled_op2 = ruint_to_garbled_uint(&op2); + if !op2.to_u256().is_zero() { + let garbled_op1 = ruint_to_garbled_uint(&op1.into()); + let garbled_op2 = ruint_to_garbled_uint(&op2.clone().into()); let result = garbled_op1.rem(garbled_op2); *op2 = garbled_uint_to_ruint(&result).into(); @@ -78,7 +78,7 @@ pub fn rem(interpreter: &mut Interpreter, _host: &mut H) { pub fn smod(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::LOW); pop_top!(interpreter, op1, op2); - *op2 = i256_mod(op1, *op2) + *op2 = i256_mod(op1.into(), op2.clone().into()).into(); } //TODO: Implement circuit for signed addition From 9cc0201749357ccf9ccb6df44a52a37187ca169e Mon Sep 17 00:00:00 2001 From: "Lucas (Kbooz)" Date: Wed, 18 Dec 2024 14:28:30 -0300 Subject: [PATCH 05/44] fix: to stack value --- .../src/instructions/arithmetic.rs | 53 ++++++++++--------- 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/crates/interpreter/src/instructions/arithmetic.rs b/crates/interpreter/src/instructions/arithmetic.rs index b8e9226e5f..66aa1cbb38 100644 --- a/crates/interpreter/src/instructions/arithmetic.rs +++ b/crates/interpreter/src/instructions/arithmetic.rs @@ -14,7 +14,7 @@ pub fn add(interpreter: &mut Interpreter, _host: &mut H) { pop_top!(interpreter, op1, op2); let garbled_op1 = ruint_to_garbled_uint(&op1.into()); - let garbled_op2 = ruint_to_garbled_uint(&op2.clone().into()); + let garbled_op2 = ruint_to_garbled_uint(&op2.to_u256()); let result = garbled_op1.add(garbled_op2); *op2 = garbled_uint_to_ruint(&result).into(); @@ -25,7 +25,7 @@ pub fn mul(interpreter: &mut Interpreter, _host: &mut H) { pop_top!(interpreter, op1, op2); let garbled_op1 = ruint_to_garbled_uint(&op1.into()); - let garbled_op2 = ruint_to_garbled_uint(&op2.clone().into()); + let garbled_op2 = ruint_to_garbled_uint(&op2.to_u256()); let result = garbled_op1.mul(garbled_op2); *op2 = garbled_uint_to_ruint(&result).into(); @@ -36,7 +36,7 @@ pub fn sub(interpreter: &mut Interpreter, _host: &mut H) { pop_top!(interpreter, op1, op2); let garbled_op1 = ruint_to_garbled_uint(&op1.into()); - let garbled_op2 = ruint_to_garbled_uint(&op2.clone().into()); + let garbled_op2 = ruint_to_garbled_uint(&op2.to_u256()); let result = garbled_op2.sub(garbled_op1); *op2 = garbled_uint_to_ruint(&result).into(); @@ -45,9 +45,9 @@ pub fn sub(interpreter: &mut Interpreter, _host: &mut H) { pub fn div(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::LOW); pop_top!(interpreter, op1, op2); - if !op2.into().is_zero() { + if !op2.to_u256().is_zero() { let garbled_op1 = ruint_to_garbled_uint(&op1.into()); - let garbled_op2 = ruint_to_garbled_uint(&op2.clone().into()); + let garbled_op2 = ruint_to_garbled_uint(&op2.to_u256()); let result = garbled_op1.div(garbled_op2); *op2 = garbled_uint_to_ruint(&result).into(); @@ -58,16 +58,16 @@ pub fn div(interpreter: &mut Interpreter, _host: &mut H) { pub fn sdiv(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::LOW); pop_top!(interpreter, op1, op2); - let result = i256_div(op1.into(), *op2.into()); + let result = i256_div(op1.into(), op2.to_u256()); *op2 = result.into(); } pub fn rem(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::LOW); pop_top!(interpreter, op1, op2); - if !op2.is_zero() { - let garbled_op1 = ruint_to_garbled_uint(&op1); - let garbled_op2 = ruint_to_garbled_uint(&op2); + if !op2.to_u256().is_zero() { + let garbled_op1 = ruint_to_garbled_uint(&op1.into()); + let garbled_op2 = ruint_to_garbled_uint(&op2.to_u256()); let result = garbled_op1.rem(garbled_op2); *op2 = garbled_uint_to_ruint(&result).into(); @@ -78,28 +78,28 @@ pub fn rem(interpreter: &mut Interpreter, _host: &mut H) { pub fn smod(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::LOW); pop_top!(interpreter, op1, op2); - *op2 = i256_mod(op1, *op2) + *op2 = i256_mod(op1.into(), op2.to_u256()).into() } //TODO: Implement circuit for signed addition pub fn addmod(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::MID); pop_top!(interpreter, op1, op2, op3); - *op3 = op1.add_mod(op2, *op3) + *op3 = op1.to_u256().add_mod(op2.into(), op3.to_u256()).into() } //TODO: Implement circuit for signed multiplication pub fn mulmod(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::MID); pop_top!(interpreter, op1, op2, op3); - *op3 = op1.mul_mod(op2, *op3) + *op3 = op1.to_u256().mul_mod(op2.into(), op3.to_u256()).into() } //TODO?: Implement circuit for signed exponentiation pub fn exp(interpreter: &mut Interpreter, _host: &mut H) { pop_top!(interpreter, op1, op2); - gas_or_fail!(interpreter, gas::exp_cost(SPEC::SPEC_ID, *op2)); - *op2 = op1.pow(*op2); + gas_or_fail!(interpreter, gas::exp_cost(SPEC::SPEC_ID, op2.to_u256())); + *op2 = op1.to_u256().pow(op2.to_u256()).into(); } /// Implements the `SIGNEXTEND` opcode as defined in the Ethereum Yellow Paper. @@ -122,13 +122,18 @@ pub fn exp(interpreter: &mut Interpreter, _host: & pub fn signextend(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::LOW); pop_top!(interpreter, ext, x); + + let uint_ext = ext.to_u256(); + let uint_x = x.to_u256(); + // For 31 we also don't need to do anything. - if ext < U256::from(31) { - let ext = ext.as_limbs()[0]; - let bit_index = (8 * ext + 7) as usize; - let bit = x.bit(bit_index); + if uint_ext < U256::from(31) { + let uint_ext = uint_ext.as_limbs()[0]; + let bit_index = (8 * uint_ext + 7) as usize; + let bit = uint_x.bit(bit_index); let mask = (U256::from(1) << bit_index) - U256::from(1); - *x = if bit { *x | !mask } else { *x & mask }; + let result = if bit { uint_x | !mask } else { uint_x & mask }; + *x = result.into(); } } @@ -174,7 +179,7 @@ mod tests { let result = interpreter.stack.pop().unwrap(); let expected_result = Uint::<256, 4>::from(18u64); - assert_eq!(result, expected_result); + assert_eq!(result, expected_result.into()); } #[test] @@ -199,7 +204,7 @@ mod tests { let result = interpreter.stack.pop().unwrap(); let expected_result = Uint::<256, 4>::from(70u64); - assert_eq!(result, expected_result); + assert_eq!(result, expected_result.into()); } #[test] @@ -228,7 +233,7 @@ mod tests { let result = interpreter.stack.pop().unwrap(); let expected_result = Uint::<256, 4>::from(2000u64); - assert_eq!(result, expected_result); + assert_eq!(result, expected_result.into()); } #[test] @@ -257,7 +262,7 @@ mod tests { let result = interpreter.stack.pop().unwrap(); let expected_result = Uint::<256, 4>::from(5u64); - assert_eq!(result, expected_result); + assert_eq!(result, expected_result.into()); } #[test] @@ -286,6 +291,6 @@ mod tests { let result = interpreter.stack.pop().unwrap(); let expected_result = Uint::<256, 4>::from(0u64); - assert_eq!(result, expected_result); + assert_eq!(result, expected_result.into()); } } From c7ec41cc6c8d7d55dda73de159500e06567b11c8 Mon Sep 17 00:00:00 2001 From: "Lucas (Kbooz)" Date: Wed, 18 Dec 2024 14:28:43 -0300 Subject: [PATCH 06/44] fix: to stack value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: João Conserva --- .../interpreter/src/instructions/bitwise.rs | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/crates/interpreter/src/instructions/bitwise.rs b/crates/interpreter/src/instructions/bitwise.rs index 9386b5b629..cb7ba7c2ce 100644 --- a/crates/interpreter/src/instructions/bitwise.rs +++ b/crates/interpreter/src/instructions/bitwise.rs @@ -12,93 +12,93 @@ pub fn lt(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); pop_top!(interpreter, op1, op2); - let garbled_op1 = ruint_to_garbled_uint(&op1); - let garbled_op2 = ruint_to_garbled_uint(&op2); + let garbled_op1 = ruint_to_garbled_uint(&op1.into()); + let garbled_op2 = ruint_to_garbled_uint(&op2.to_u256()); - *op2 = U256::from(garbled_op1.lt(&garbled_op2)); + *op2 = U256::from(garbled_op1.lt(&garbled_op2)).into(); } pub fn gt(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); pop_top!(interpreter, op1, op2); - let garbled_op1 = ruint_to_garbled_uint(&op1); - let garbled_op2 = ruint_to_garbled_uint(&op2); + let garbled_op1 = ruint_to_garbled_uint(&op1.into()); + let garbled_op2 = ruint_to_garbled_uint(&op2.to_u256()); - *op2 = U256::from(garbled_op1.gt(&garbled_op2)); + *op2 = U256::from(garbled_op1.gt(&garbled_op2)).into(); } // TODO: Implement in garbled circuits pub fn slt(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); pop_top!(interpreter, op1, op2); - *op2 = U256::from(i256_cmp(&op1, op2) == Ordering::Less); + *op2 = U256::from(i256_cmp(&op1.into(), &op2.to_u256()) == Ordering::Less).into(); } // TODO: Implement in garbled circuits pub fn sgt(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); pop_top!(interpreter, op1, op2); - *op2 = U256::from(i256_cmp(&op1, op2) == Ordering::Greater); + *op2 = U256::from(i256_cmp(&op1.into(), &op2.to_u256()) == Ordering::Greater).into(); } pub fn eq(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); pop_top!(interpreter, op1, op2); - let garbled_op1 = ruint_to_garbled_uint(&op1); - let garbled_op2 = ruint_to_garbled_uint(&op2); + let garbled_op1 = ruint_to_garbled_uint(&op1.into()); + let garbled_op2 = ruint_to_garbled_uint(&op2.to_u256()); - *op2 = U256::from(garbled_op1.eq(&garbled_op2)); + *op2 = U256::from(garbled_op1.eq(&garbled_op2)).into(); } pub fn iszero(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); pop_top!(interpreter, op1); - *op1 = U256::from(op1.is_zero()); + *op1 = U256::from(op1.to_u256().is_zero()).into(); } pub fn bitand(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); pop_top!(interpreter, op1, op2); - let garbled_op1 = ruint_to_garbled_uint(&op1); - let garbled_op2 = ruint_to_garbled_uint(&op2); + let garbled_op1 = ruint_to_garbled_uint(&op1.into()); + let garbled_op2 = ruint_to_garbled_uint(&op2.to_u256()); let result = garbled_op1 & garbled_op2; - *op2 = garbled_uint_to_ruint(&result); + *op2 = garbled_uint_to_ruint(&result).into(); } pub fn bitor(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); pop_top!(interpreter, op1, op2); - let garbled_op1 = ruint_to_garbled_uint(&op1); - let garbled_op2 = ruint_to_garbled_uint(&op2); + let garbled_op1 = ruint_to_garbled_uint(&op1.into()); + let garbled_op2 = ruint_to_garbled_uint(&op2.to_u256()); let result = garbled_op1 | garbled_op2; - *op2 = garbled_uint_to_ruint(&result); + *op2 = garbled_uint_to_ruint(&result).into(); } pub fn bitxor(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); pop_top!(interpreter, op1, op2); - let garbled_op1 = ruint_to_garbled_uint(&op1); - let garbled_op2 = ruint_to_garbled_uint(&op2); + let garbled_op1 = ruint_to_garbled_uint(&op1.into()); + let garbled_op2 = ruint_to_garbled_uint(&op2.to_u256()); let result = garbled_op1 ^ garbled_op2; - *op2 = garbled_uint_to_ruint(&result); + *op2 = garbled_uint_to_ruint(&result).into(); } pub fn not(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); pop_top!(interpreter, op1); - let garbled_op1 = ruint_to_garbled_uint(&op1); + let garbled_op1 = ruint_to_garbled_uint(&op1.to_u256()); let result = !garbled_op1; - *op1 = garbled_uint_to_ruint(&result); + *op1 = garbled_uint_to_ruint(&result).into(); } // TODO: Implement in garbled circuits @@ -109,9 +109,9 @@ pub fn byte(interpreter: &mut Interpreter, _host: &mut H) { let o1 = as_usize_saturated!(op1); *op2 = if o1 < 32 { // `31 - o1` because `byte` returns LE, while we want BE - U256::from(op2.byte(31 - o1)) + U256::from(op2.to_u256().byte(31 - o1)).into() } else { - U256::ZERO + U256::ZERO.into() }; } From 420b819ec997f4998b502336e9599a39f5817755 Mon Sep 17 00:00:00 2001 From: "Lucas (Kbooz)" Date: Wed, 18 Dec 2024 15:23:09 -0300 Subject: [PATCH 07/44] fix: to stack value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: João Conserva --- .../interpreter/src/instructions/bitwise.rs | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/crates/interpreter/src/instructions/bitwise.rs b/crates/interpreter/src/instructions/bitwise.rs index c419a2a841..142f1f1d93 100644 --- a/crates/interpreter/src/instructions/bitwise.rs +++ b/crates/interpreter/src/instructions/bitwise.rs @@ -139,11 +139,11 @@ pub fn shr(interpreter: &mut Interpreter, _host: & pop_top!(interpreter, op1, op2); let shift = as_usize_saturated!(op1); *op2 = if shift < 256 { - let garbled_op2 = ruint_to_garbled_uint(&op2); + let garbled_op2 = ruint_to_garbled_uint(&op2.to_u256()); let shifted_op2 = garbled_op2 >> shift; - garbled_uint_to_ruint(&shifted_op2) + garbled_uint_to_ruint(&shifted_op2.into()).into() } else { - U256::ZERO + U256::ZERO.into() } } @@ -156,11 +156,11 @@ pub fn sar(interpreter: &mut Interpreter, _host: & let shift = as_usize_saturated!(op1); *op2 = if shift < 256 { - op2.arithmetic_shr(shift) - } else if op2.bit(255) { - U256::MAX + op2.to_u256().arithmetic_shr(shift).into() + } else if op2.to_u256().bit(255) { + U256::MAX.into() } else { - U256::ZERO + U256::ZERO.into() }; } @@ -249,7 +249,7 @@ mod tests { push!(interpreter, test.shift); shl::, LatestSpec>(&mut interpreter, &mut host); pop!(interpreter, res); - assert_eq!(res, test.expected); + assert_eq!(res, test.expected.into()); } } @@ -330,7 +330,7 @@ mod tests { push!(interpreter, test.shift); shr::, LatestSpec>(&mut interpreter, &mut host); pop!(interpreter, res); - assert_eq!(res, test.expected); + assert_eq!(res, test.expected.into()); } } @@ -436,7 +436,7 @@ mod tests { push!(interpreter, test.shift); sar::, LatestSpec>(&mut interpreter, &mut host); pop!(interpreter, res); - assert_eq!(res, test.expected); + assert_eq!(res, test.expected.into()); } } @@ -471,7 +471,7 @@ mod tests { push!(interpreter, U256::from(test.index)); byte(&mut interpreter, &mut host); pop!(interpreter, res); - assert_eq!(res, test.expected, "Failed at index: {}", test.index); + assert_eq!(res, test.expected.into(), "Failed at index: {}", test.index); } } } From 2b29dc0dbb10c0f01c8d367252f93888e6accf20 Mon Sep 17 00:00:00 2001 From: "Lucas (Kbooz)" Date: Thu, 19 Dec 2024 09:49:31 -0300 Subject: [PATCH 08/44] feat: use from trait --- .../src/instructions/arithmetic.rs | 19 ++++++++---- .../interpreter/src/instructions/control.rs | 5 +--- crates/interpreter/src/instructions/data.rs | 2 +- crates/interpreter/src/instructions/host.rs | 2 +- crates/interpreter/src/instructions/macros.rs | 2 +- crates/interpreter/src/interpreter.rs | 19 ++++++------ crates/interpreter/src/interpreter/stack.rs | 29 +++++++++++++++++-- 7 files changed, 55 insertions(+), 23 deletions(-) diff --git a/crates/interpreter/src/instructions/arithmetic.rs b/crates/interpreter/src/instructions/arithmetic.rs index 66aa1cbb38..1593ca8751 100644 --- a/crates/interpreter/src/instructions/arithmetic.rs +++ b/crates/interpreter/src/instructions/arithmetic.rs @@ -4,8 +4,10 @@ use super::i256::{i256_div, i256_mod}; use crate::{ gas, instructions::utility::{garbled_uint_to_ruint, ruint_to_garbled_uint}, - Host, Interpreter, + interpreter::StackValueData, + Host, Interpreter, Stack, }; +use compute::prelude::CircuitExecutor; use primitives::U256; use specification::hardfork::Spec; @@ -13,11 +15,18 @@ pub fn add(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); pop_top!(interpreter, op1, op2); - let garbled_op1 = ruint_to_garbled_uint(&op1.into()); - let garbled_op2 = ruint_to_garbled_uint(&op2.to_u256()); - let result = garbled_op1.add(garbled_op2); + // verificar se op1 é StackDataValue::Public ou StackDataValue::Private + // caso seja public, converter para input com o interpreter.circuit_builder.input - *op2 = garbled_uint_to_ruint(&result).into(); + let garbled_op1 = op1.to_garbled_value(&mut interpreter.circuit_builder); + let garbled_op2 = op2.to_garbled_value(&mut interpreter.circuit_builder); + + // cria o circuito de soma usando o circuit builder + + let result = interpreter.circuit_builder.add(&garbled_op1, &garbled_op2); + + // Salvar sempre como StackDataValue::Private + *op2 = StackValueData::Private(result); } pub fn mul(interpreter: &mut Interpreter, _host: &mut H) { diff --git a/crates/interpreter/src/instructions/control.rs b/crates/interpreter/src/instructions/control.rs index a3a91de30e..592558e6b1 100644 --- a/crates/interpreter/src/instructions/control.rs +++ b/crates/interpreter/src/instructions/control.rs @@ -148,10 +148,7 @@ pub fn jumpf(interpreter: &mut Interpreter, _host: &mut H) { pub fn pc(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::BASE); // - 1 because we have already advanced the instruction pointer in `Interpreter::step` - push!( - interpreter, - U256::from(interpreter.program_counter() - 1).into() - ); + push!(interpreter, U256::from(interpreter.program_counter() - 1)); } #[inline] diff --git a/crates/interpreter/src/instructions/data.rs b/crates/interpreter/src/instructions/data.rs index 2ecc599571..2f7cca4d6a 100644 --- a/crates/interpreter/src/instructions/data.rs +++ b/crates/interpreter/src/instructions/data.rs @@ -52,7 +52,7 @@ pub fn data_size(interpreter: &mut Interpreter, _host: &mut H) gas!(interpreter, BASE); let data_size = interpreter.eof().expect("eof").header.data_size; - push!(interpreter, U256::from(data_size).into()); + push!(interpreter, U256::from(data_size)); } pub fn data_copy(interpreter: &mut Interpreter, _host: &mut H) { diff --git a/crates/interpreter/src/instructions/host.rs b/crates/interpreter/src/instructions/host.rs index 944b062fe9..11be8621ad 100644 --- a/crates/interpreter/src/instructions/host.rs +++ b/crates/interpreter/src/instructions/host.rs @@ -115,7 +115,7 @@ pub fn blockhash(interpreter: &mut Interpreter, ho interpreter.instruction_result = InstructionResult::FatalExternalError; return; }; - *number = U256::from_be_bytes(hash.0); + *number = U256::from_be_bytes(hash.0).into(); } pub fn sload(interpreter: &mut Interpreter, host: &mut H) { diff --git a/crates/interpreter/src/instructions/macros.rs b/crates/interpreter/src/instructions/macros.rs index 75a42a2f6a..5a4d6ca658 100644 --- a/crates/interpreter/src/instructions/macros.rs +++ b/crates/interpreter/src/instructions/macros.rs @@ -282,7 +282,7 @@ macro_rules! push { #[macro_export] macro_rules! as_u64_saturated { ($v:expr) => { - match $v.as_limbs() { + match $v.to_u256().as_limbs() { x => { if (x[1] == 0) & (x[2] == 0) & (x[3] == 0) { x[0] diff --git a/crates/interpreter/src/interpreter.rs b/crates/interpreter/src/interpreter.rs index 32ca56a464..e1b7de850c 100644 --- a/crates/interpreter/src/interpreter.rs +++ b/crates/interpreter/src/interpreter.rs @@ -4,6 +4,7 @@ pub mod serde; mod shared_memory; mod stack; +use compute::prelude::WRK17CircuitBuilder; pub use contract::Contract; pub use shared_memory::{num_words, SharedMemory, EMPTY_SHARED_MEMORY}; pub use stack::{Stack, StackValueData, STACK_LIMIT}; @@ -60,7 +61,7 @@ pub struct Interpreter { /// Set inside CALL or CREATE instructions and RETURN or REVERT instructions. Additionally those instructions will set /// InstructionResult to CallOrCreate/Return/Revert so we know the reason. pub next_action: InterpreterAction, - // pub circuit_builder: WRK17CircuitBuilder, + pub circuit_builder: WRK17CircuitBuilder, } impl Default for Interpreter { @@ -215,14 +216,14 @@ impl Interpreter { self.gas.record_refund(create_outcome.gas().refunded()); } return_revert!() => { - push!(self, StackValueData::Public(U256::ZERO)); + push!(self, U256::ZERO); self.gas.erase_cost(create_outcome.gas().remaining()); } InstructionResult::FatalExternalError => { panic!("Fatal external error in insert_eofcreate_outcome"); } _ => { - push!(self, StackValueData::Public(U256::ZERO)) + push!(self, U256::ZERO) } } } @@ -272,9 +273,9 @@ impl Interpreter { push!( self, if self.is_eof { - StackValueData::Public(U256::ZERO) + U256::ZERO } else { - StackValueData::Public(U256::from(1)) + U256::from(1) } ); } @@ -284,9 +285,9 @@ impl Interpreter { push!( self, if self.is_eof { - StackValueData::Public(U256::from(1)) + U256::from(1) } else { - StackValueData::Public(U256::ZERO) + U256::ZERO } ); } @@ -297,9 +298,9 @@ impl Interpreter { push!( self, if self.is_eof { - StackValueData::Public(U256::from(2)) + U256::from(2) } else { - StackValueData::Public(U256::ZERO) + U256::ZERO } ); } diff --git a/crates/interpreter/src/interpreter/stack.rs b/crates/interpreter/src/interpreter/stack.rs index be6226a7ad..7bfc5d5a3d 100644 --- a/crates/interpreter/src/interpreter/stack.rs +++ b/crates/interpreter/src/interpreter/stack.rs @@ -1,5 +1,5 @@ -use crate::InstructionResult; -use compute::prelude::GateIndexVec; +use crate::{instructions::utility::ruint_to_garbled_uint, InstructionResult}; +use compute::prelude::{GateIndexVec, WRK17CircuitBuilder}; use core::{fmt, ptr}; use primitives::{B256, U256}; use std::vec::Vec; @@ -35,13 +35,38 @@ impl Into for StackValueData { } } +impl Into for StackValueData { + fn into(self) -> GateIndexVec { + match self { + StackValueData::Public(_) => panic!("Cannot convert public value to GateIndexVec"), + StackValueData::Private(value) => value, + } + } +} + +impl StackValueData { + pub fn to_garbled_value(&self, circuit_builder: &mut WRK17CircuitBuilder) -> GateIndexVec { + match self { + StackValueData::Private(value) => value.clone(), + StackValueData::Public(value) => circuit_builder.input(&ruint_to_garbled_uint(value)), + } + } +} + // Add From implementation for ergonomics + impl From for StackValueData { fn from(value: U256) -> Self { StackValueData::Public(value) } } +impl From for StackValueData { + fn from(value: GateIndexVec) -> Self { + StackValueData::Private(value) + } +} + impl StackValueData { pub fn to_u256(&self) -> U256 { match self { From 44060bf54fd97db319276ec2048490bcefe5a81c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Conserva?= Date: Thu, 19 Dec 2024 14:36:54 -0300 Subject: [PATCH 09/44] fix: convert StackValueData to appropriate type before pushing to stack --- crates/interpreter/src/interpreter.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/interpreter/src/interpreter.rs b/crates/interpreter/src/interpreter.rs index e1b7de850c..ade4dba913 100644 --- a/crates/interpreter/src/interpreter.rs +++ b/crates/interpreter/src/interpreter.rs @@ -182,14 +182,14 @@ impl Interpreter { self.gas.record_refund(create_outcome.gas().refunded()); } return_revert!() => { - push!(self, StackValueData::Public(U256::ZERO)); + push!(self, StackValueData::Public(U256::ZERO).into()); self.gas.erase_cost(create_outcome.gas().remaining()); } InstructionResult::FatalExternalError => { panic!("Fatal external error in insert_create_outcome"); } _ => { - push!(self, StackValueData::Public(U256::ZERO)) + push!(self, StackValueData::Public(U256::ZERO).into()) } } } From b8779f0b2b31d8f9a682d97d025495fe74db6405 Mon Sep 17 00:00:00 2001 From: "Lucas (Kbooz)" Date: Thu, 19 Dec 2024 16:34:28 -0300 Subject: [PATCH 10/44] feat: added circuit builder --- crates/interpreter/src/interpreter.rs | 1 + crates/interpreter/src/interpreter/serde.rs | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/crates/interpreter/src/interpreter.rs b/crates/interpreter/src/interpreter.rs index ade4dba913..23cb0a5659 100644 --- a/crates/interpreter/src/interpreter.rs +++ b/crates/interpreter/src/interpreter.rs @@ -92,6 +92,7 @@ impl Interpreter { shared_memory: EMPTY_SHARED_MEMORY, stack: Stack::new(), next_action: InterpreterAction::None, + circuit_builder: WRK17CircuitBuilder::default(), } } diff --git a/crates/interpreter/src/interpreter/serde.rs b/crates/interpreter/src/interpreter/serde.rs index 829b1baef3..1cff8ce3f9 100644 --- a/crates/interpreter/src/interpreter/serde.rs +++ b/crates/interpreter/src/interpreter/serde.rs @@ -2,6 +2,7 @@ use super::Interpreter; use crate::{ Contract, FunctionStack, Gas, InstructionResult, InterpreterAction, SharedMemory, Stack, }; +use compute::prelude::WRK17CircuitBuilder; use primitives::Bytes; use serde::{Deserialize, Deserializer, Serialize, Serializer}; @@ -21,6 +22,7 @@ struct InterpreterSerde<'a> { return_data_buffer: &'a Bytes, is_static: bool, next_action: &'a InterpreterAction, + circuit_builder: &'a WRK17CircuitBuilder, } #[derive(Deserialize)] @@ -39,6 +41,7 @@ struct InterpreterDe { return_data_buffer: Bytes, is_static: bool, next_action: InterpreterAction, + circuit_builder: WRK17CircuitBuilder, } impl Serialize for Interpreter { @@ -60,6 +63,7 @@ impl Serialize for Interpreter { return_data_buffer: &self.return_data_buffer, is_static: self.is_static, next_action: &self.next_action, + circuit_builder: &self.circuit_builder, } .serialize(serializer) } @@ -84,6 +88,7 @@ impl<'de> Deserialize<'de> for Interpreter { return_data_buffer, is_static, next_action, + circuit_builder, } = InterpreterDe::deserialize(deserializer)?; // Reconstruct the instruction pointer from usize @@ -108,6 +113,7 @@ impl<'de> Deserialize<'de> for Interpreter { return_data_buffer, is_static, next_action, + circuit_builder, }) } } From d424526d875e23a1d94327a550deafe45adeca0e Mon Sep 17 00:00:00 2001 From: "Lucas (Kbooz)" Date: Thu, 19 Dec 2024 17:57:32 -0300 Subject: [PATCH 11/44] fix: to stack value --- crates/inspector/src/eip3155.rs | 2 +- crates/interpreter/Cargo.toml | 2 +- .../src/instructions/arithmetic.rs | 4 +-- .../src/instructions/block_info.rs | 2 +- .../interpreter/src/instructions/contract.rs | 8 +++--- crates/interpreter/src/instructions/data.rs | 4 +-- crates/interpreter/src/instructions/host.rs | 24 +++++++++++++----- crates/interpreter/src/instructions/macros.rs | 8 +++--- crates/interpreter/src/instructions/memory.rs | 2 +- crates/interpreter/src/instructions/system.rs | 14 +++++------ .../interpreter/src/instructions/tx_info.rs | 14 ++++++----- crates/interpreter/src/interpreter/stack.rs | 25 ++++++++++++++++--- 12 files changed, 69 insertions(+), 40 deletions(-) diff --git a/crates/inspector/src/eip3155.rs b/crates/inspector/src/eip3155.rs index 8fd1c22323..2066820c40 100644 --- a/crates/inspector/src/eip3155.rs +++ b/crates/inspector/src/eip3155.rs @@ -200,7 +200,7 @@ impl Inspector for TracerEip3155 { fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { self.gas_inspector.step(interp, context); - self.stack.clone_from(interp.stack.data()); + self.stack = interp.stack.data().iter().map(|v| v.to_u256()).collect(); self.memory = if self.include_memory { Some(hex::encode_prefixed(interp.shared_memory.context_memory())) } else { diff --git a/crates/interpreter/Cargo.toml b/crates/interpreter/Cargo.toml index 55793b6938..1a4ff0820d 100644 --- a/crates/interpreter/Cargo.toml +++ b/crates/interpreter/Cargo.toml @@ -46,7 +46,7 @@ serde_json = "1.0" bincode = "1.3" [features] -default = ["std"] +default = ["std", "serde"] std = ["serde?/std", "primitives/std", "wiring/std"] hashbrown = ["primitives/hashbrown"] serde = ["dep:serde", "primitives/serde", "bytecode/serde", "wiring/serde"] diff --git a/crates/interpreter/src/instructions/arithmetic.rs b/crates/interpreter/src/instructions/arithmetic.rs index 1593ca8751..8445eead95 100644 --- a/crates/interpreter/src/instructions/arithmetic.rs +++ b/crates/interpreter/src/instructions/arithmetic.rs @@ -1,11 +1,11 @@ -use core::ops::{Add, Div, Mul, Rem, Sub}; +use core::ops::{Div, Mul, Rem, Sub}; use super::i256::{i256_div, i256_mod}; use crate::{ gas, instructions::utility::{garbled_uint_to_ruint, ruint_to_garbled_uint}, interpreter::StackValueData, - Host, Interpreter, Stack, + Host, Interpreter, }; use compute::prelude::CircuitExecutor; use primitives::U256; diff --git a/crates/interpreter/src/instructions/block_info.rs b/crates/interpreter/src/instructions/block_info.rs index 52b916bf64..621168a5cc 100644 --- a/crates/interpreter/src/instructions/block_info.rs +++ b/crates/interpreter/src/instructions/block_info.rs @@ -1,4 +1,4 @@ -use crate::{gas, interpreter::StackValueData, Host, Interpreter}; +use crate::{gas, Host, Interpreter}; use primitives::U256; use specification::hardfork::{Spec, SpecId::*}; use wiring::Block; diff --git a/crates/interpreter/src/instructions/contract.rs b/crates/interpreter/src/instructions/contract.rs index adf4607738..f2ee947175 100644 --- a/crates/interpreter/src/instructions/contract.rs +++ b/crates/interpreter/src/instructions/contract.rs @@ -397,7 +397,7 @@ pub fn call(interpreter: &mut Interpreter, host: & pop!(interpreter, local_gas_limit); pop_address!(interpreter, to); // max gas limit is not possible in real ethereum situation. - let local_gas_limit = u64::try_from(local_gas_limit.into()).unwrap_or(u64::MAX); + let local_gas_limit = u64::try_from(local_gas_limit.to_u256()).unwrap_or(u64::MAX); pop!(interpreter, value); let has_transfer = !value.to_u256().is_zero(); @@ -448,7 +448,7 @@ pub fn call_code(interpreter: &mut Interpreter, ho pop!(interpreter, local_gas_limit); pop_address!(interpreter, to); // max gas limit is not possible in real ethereum situation. - let local_gas_limit = u64::try_from(local_gas_limit.into()).unwrap_or(u64::MAX); + let local_gas_limit = u64::try_from(local_gas_limit.to_u256()).unwrap_or(u64::MAX); pop!(interpreter, value); let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(interpreter) else { @@ -499,7 +499,7 @@ pub fn delegate_call(interpreter: &mut Interpreter pop!(interpreter, local_gas_limit); pop_address!(interpreter, to); // max gas limit is not possible in real ethereum situation. - let local_gas_limit = u64::try_from(local_gas_limit.into()).unwrap_or(u64::MAX); + let local_gas_limit = u64::try_from(local_gas_limit.to_u256()).unwrap_or(u64::MAX); let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(interpreter) else { return; @@ -539,7 +539,7 @@ pub fn static_call(interpreter: &mut Interpreter, pop!(interpreter, local_gas_limit); pop_address!(interpreter, to); // max gas limit is not possible in real ethereum situation. - let local_gas_limit = u64::try_from(local_gas_limit.into()).unwrap_or(u64::MAX); + let local_gas_limit = u64::try_from(local_gas_limit.to_u256()).unwrap_or(u64::MAX); let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(interpreter) else { return; diff --git a/crates/interpreter/src/instructions/data.rs b/crates/interpreter/src/instructions/data.rs index 2f7cca4d6a..71046cd8e9 100644 --- a/crates/interpreter/src/instructions/data.rs +++ b/crates/interpreter/src/instructions/data.rs @@ -134,9 +134,7 @@ mod test { interp.step(&table, &mut host); assert_eq!( interp.stack.data(), - &vec![b256!( - "0500000000000000000000000000000000000000000000000000000000000000" - )] + &vec![b256!("0500000000000000000000000000000000000000000000000000000000000000").into()] ); interp.stack.pop().unwrap(); diff --git a/crates/interpreter/src/instructions/host.rs b/crates/interpreter/src/instructions/host.rs index 11be8621ad..d145f47633 100644 --- a/crates/interpreter/src/instructions/host.rs +++ b/crates/interpreter/src/instructions/host.rs @@ -120,19 +120,23 @@ pub fn blockhash(interpreter: &mut Interpreter, ho pub fn sload(interpreter: &mut Interpreter, host: &mut H) { pop_top!(interpreter, index); - let Some(value) = host.sload(interpreter.contract.target_address, *index) else { + let Some(value) = host.sload(interpreter.contract.target_address, index.to_u256()) else { interpreter.instruction_result = InstructionResult::FatalExternalError; return; }; gas!(interpreter, gas::sload_cost(SPEC::SPEC_ID, value.is_cold)); - *index = value.data; + *index = value.data.into(); } pub fn sstore(interpreter: &mut Interpreter, host: &mut H) { require_non_staticcall!(interpreter); pop!(interpreter, index, value); - let Some(state_load) = host.sstore(interpreter.contract.target_address, index, value) else { + let Some(state_load) = host.sstore( + interpreter.contract.target_address, + index.into(), + value.into(), + ) else { interpreter.instruction_result = InstructionResult::FatalExternalError; return; }; @@ -161,7 +165,11 @@ pub fn tstore(interpreter: &mut Interpreter, host: pop!(interpreter, index, value); - host.tstore(interpreter.contract.target_address, index, value); + host.tstore( + interpreter.contract.target_address, + index.into(), + value.into(), + ); } /// EIP-1153: Transient storage opcodes @@ -172,7 +180,9 @@ pub fn tload(interpreter: &mut Interpreter, host: pop_top!(interpreter, index); - *index = host.tload(interpreter.contract.target_address, *index); + *index = host + .tload(interpreter.contract.target_address, index.clone().into()) + .into(); } pub fn log(interpreter: &mut Interpreter, host: &mut H) { @@ -197,7 +207,9 @@ pub fn log(interpreter: &mut Interpreter, host let mut topics = Vec::with_capacity(N); for _ in 0..N { // SAFETY: stack bounds already checked few lines above - topics.push(B256::from(unsafe { interpreter.stack.pop_unsafe() })); + topics.push(B256::from(unsafe { + interpreter.stack.pop_unsafe().to_u256() + })); } let log = Log { diff --git a/crates/interpreter/src/instructions/macros.rs b/crates/interpreter/src/instructions/macros.rs index 5a4d6ca658..44be6b785d 100644 --- a/crates/interpreter/src/instructions/macros.rs +++ b/crates/interpreter/src/instructions/macros.rs @@ -136,7 +136,7 @@ macro_rules! pop_address_ret { } // SAFETY: Length is checked above. let $x1 = ::primitives::Address::from_word(::primitives::B256::from(unsafe { - $interp.stack.pop_unsafe() + $interp.stack.pop_unsafe().to_u256() })); }; ($interp:expr, $x1:ident, $x2:ident, $ret:expr) => { @@ -146,10 +146,10 @@ macro_rules! pop_address_ret { } // SAFETY: Length is checked above. let $x1 = ::primitives::Address::from_word(::primitives::B256::from(unsafe { - $interp.stack.pop_unsafe() + $interp.stack.pop_unsafe().to_u256() })); let $x2 = ::primitives::Address::from_word(::primitives::B256::from(unsafe { - $interp.stack.pop_unsafe() + $interp.stack.pop_unsafe().to_u256() })); }; } @@ -282,7 +282,7 @@ macro_rules! push { #[macro_export] macro_rules! as_u64_saturated { ($v:expr) => { - match $v.to_u256().as_limbs() { + match $v.as_limbs() { x => { if (x[1] == 0) & (x[2] == 0) & (x[3] == 0) { x[0] diff --git a/crates/interpreter/src/instructions/memory.rs b/crates/interpreter/src/instructions/memory.rs index a3f2e14800..252f4aca60 100644 --- a/crates/interpreter/src/instructions/memory.rs +++ b/crates/interpreter/src/instructions/memory.rs @@ -26,7 +26,7 @@ pub fn mstore8(interpreter: &mut Interpreter, _host: &mut H) { resize_memory!(interpreter, offset, 1); interpreter .shared_memory - .set_byte(offset, value.into().byte(0)) + .set_byte(offset, value.to_u256().byte(0)) } pub fn msize(interpreter: &mut Interpreter, _host: &mut H) { diff --git a/crates/interpreter/src/instructions/system.rs b/crates/interpreter/src/instructions/system.rs index 607ee55f40..a503a2aa6f 100644 --- a/crates/interpreter/src/instructions/system.rs +++ b/crates/interpreter/src/instructions/system.rs @@ -1,4 +1,4 @@ -use crate::{gas, Host, InstructionResult, Interpreter}; +use crate::{gas, interpreter::StackValueData, Host, InstructionResult, Interpreter}; use core::ptr; use primitives::{B256, KECCAK_EMPTY, U256}; use specification::hardfork::Spec; @@ -172,7 +172,7 @@ pub fn gas(interpreter: &mut Interpreter, _host: &mut H) { // common logic for copying data from a source buffer to the EVM's memory pub fn memory_resize( interpreter: &mut Interpreter, - memory_offset: U256, + memory_offset: StackValueData, len: usize, ) -> Option { // safe to cast usize to u64 @@ -180,7 +180,7 @@ pub fn memory_resize( if len == 0 { return None; } - let memory_offset = as_usize_or_fail_ret!(interpreter, memory_offset, None); + let memory_offset = as_usize_or_fail_ret!(interpreter, memory_offset.to_u256(), None); resize_memory!(interpreter, memory_offset, len, None); Some(memory_offset) @@ -219,7 +219,7 @@ mod test { interp.step(&table, &mut host); assert_eq!( interp.stack.data(), - &vec![U256::from_limbs([0x01, 0x02, 0x03, 0x04])] + &vec![U256::from_limbs([0x01, 0x02, 0x03, 0x04]).into()] ); let _ = interp.stack.pop(); @@ -229,7 +229,7 @@ mod test { assert_eq!(interp.instruction_result, InstructionResult::Continue); assert_eq!( interp.stack.data(), - &vec![U256::from_limbs([0x0100, 0x0200, 0x0300, 0x0400])] + &vec![U256::from_limbs([0x0100, 0x0200, 0x0300, 0x0400]).into()] ); let _ = interp.stack.pop(); @@ -238,7 +238,7 @@ mod test { assert_eq!(interp.instruction_result, InstructionResult::Continue); assert_eq!( interp.stack.data(), - &vec![U256::from_limbs([0x00, 0x00, 0x00, 0x00])] + &vec![U256::from_limbs([0x00, 0x00, 0x00, 0x00]).into()] ); // Offset right at the boundary of the return data buffer size @@ -250,7 +250,7 @@ mod test { assert_eq!(interp.instruction_result, InstructionResult::Continue); assert_eq!( interp.stack.data(), - &vec![U256::from_limbs([0x00, 0x00, 0x00, 0x00])] + &vec![U256::from_limbs([0x00, 0x00, 0x00, 0x00]).into()] ); } diff --git a/crates/interpreter/src/instructions/tx_info.rs b/crates/interpreter/src/instructions/tx_info.rs index 755084ba51..180c3084b4 100644 --- a/crates/interpreter/src/instructions/tx_info.rs +++ b/crates/interpreter/src/instructions/tx_info.rs @@ -27,12 +27,14 @@ pub fn blob_hash(interpreter: &mut Interpreter, ho let i = as_usize_saturated!(index); let tx = &host.env().tx; *index = if tx.tx_type().into() == TransactionType::Eip4844 { - tx.eip4844() - .blob_versioned_hashes() - .get(i) - .cloned() - .map(|b| U256::from_be_bytes(*b)) - .unwrap_or(U256::ZERO) + StackValueData::Public( + tx.eip4844() + .blob_versioned_hashes() + .get(i) + .cloned() + .map(|b| U256::from_be_bytes(*b)) + .unwrap_or(U256::ZERO), + ) } else { StackValueData::Public(U256::ZERO) }; diff --git a/crates/interpreter/src/interpreter/stack.rs b/crates/interpreter/src/interpreter/stack.rs index 7bfc5d5a3d..57c77a4177 100644 --- a/crates/interpreter/src/interpreter/stack.rs +++ b/crates/interpreter/src/interpreter/stack.rs @@ -1,7 +1,8 @@ use crate::{instructions::utility::ruint_to_garbled_uint, InstructionResult}; use compute::prelude::{GateIndexVec, WRK17CircuitBuilder}; use core::{fmt, ptr}; -use primitives::{B256, U256}; +use primitives::{FixedBytes, B256, U256}; +use serde::{Deserialize, Serialize}; use std::vec::Vec; /// EVM interpreter stack limit. @@ -10,7 +11,7 @@ pub const STACK_LIMIT: usize = 1024; // Stack value data. Supports both public and private values. // - Private values are represented as a vector of gate input indices created via circuit builder // - Public values are represented as U256 -#[derive(Debug, PartialEq, Eq, Hash, Clone, serde::Serialize, serde::Deserialize)] +#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)] pub enum StackValueData { Private(GateIndexVec), Public(U256), @@ -67,6 +68,12 @@ impl From for StackValueData { } } +impl From> for StackValueData { + fn from(value: FixedBytes<32>) -> Self { + StackValueData::Public(value.into()) + } +} + impl StackValueData { pub fn to_u256(&self) -> U256 { match self { @@ -76,9 +83,18 @@ impl StackValueData { } } +impl StackValueData { + pub const fn as_limbs(&self) -> &[u64; U256::LIMBS] { + match self { + StackValueData::Public(value) => value.as_limbs(), + StackValueData::Private(_) => panic!("Cannot convert private value to U256"), + } + } +} + /// EVM stack with [STACK_LIMIT] capacity of words. #[derive(Debug, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "serde", derive(serde::Serialize))] +#[cfg_attr(feature = "serde", derive(Serialize))] pub struct Stack { /// The underlying data of the stack. data: Vec, @@ -140,6 +156,7 @@ impl Stack { /// Returns a reference to the underlying data buffer. #[inline] pub fn data(&self) -> &Vec { + // export the data buffer for debugging purposes &self.data } @@ -527,7 +544,7 @@ mod tests { }); let n = 0x1111_2222_3333_4444_5555_6666_7777_8888_u128; - run(|stack| { + run(|stack: &mut Stack| { stack.push_slice(&n.to_be_bytes()).unwrap(); assert_eq!(stack.data, [U256::from(n).into()]); }); From 8a9caa145ecb46698084fdfc269c2371bec9c04a Mon Sep 17 00:00:00 2001 From: "Lucas (Kbooz)" Date: Fri, 20 Dec 2024 15:46:04 -0300 Subject: [PATCH 12/44] fix: started fixing push slices to stack value data --- crates/interpreter/src/interpreter/stack.rs | 28 +++++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/crates/interpreter/src/interpreter/stack.rs b/crates/interpreter/src/interpreter/stack.rs index 57c77a4177..989ccc3278 100644 --- a/crates/interpreter/src/interpreter/stack.rs +++ b/crates/interpreter/src/interpreter/stack.rs @@ -420,18 +420,19 @@ impl Stack { return Err(InstructionResult::StackOverflow); } - // SAFETY: length checked above. + // SAFETY: Length checked above. unsafe { - let mut tmp: Vec = vec![0u8; 32]; let dst = self.data.as_mut_ptr().add(self.data.len()).cast::(); self.data.set_len(new_len); let mut i = 0; - // write full words + // Write full words let words = slice.chunks_exact(32); let partial_last_word = words.remainder(); for word in words { + // Note: We unroll `U256::from_be_bytes` here to write directly into the buffer, + // instead of creating a 32 byte array on the stack and then copying it over. for l in word.rchunks_exact(8) { dst.add(i).write(u64::from_be_bytes(l.try_into().unwrap())); i += 1; @@ -442,7 +443,7 @@ impl Stack { return Ok(()); } - // write limbs of partial last word + // Write limbs of partial last word let limbs = partial_last_word.rchunks_exact(8); let partial_last_limb = limbs.remainder(); for l in limbs { @@ -450,7 +451,7 @@ impl Stack { i += 1; } - // write partial last limb by padding with zeros + // Write partial last limb by padding with zeros if !partial_last_limb.is_empty() { let mut tmp = [0u8; 8]; tmp[8 - partial_last_limb.len()..].copy_from_slice(partial_last_limb); @@ -460,17 +461,22 @@ impl Stack { debug_assert_eq!((i + 3) / 4, n_words, "wrote too much"); - // zero out upper bytes of last word + // Zero out upper bytes of last word let m = i % 4; // 32 / 8 if m != 0 { dst.add(i).write_bytes(0, 4 - m); } - // Convert the last 32 bytes to U256 and push as StackValueData::Public - let copy_len = std::cmp::min(32, slice.len()); - tmp[32 - copy_len..].copy_from_slice(&slice[..copy_len]); - let value = U256::from_be_slice(&tmp); - self.data.push(StackValueData::Public(value)); + // Convert the slice to U256 values and replace the corresponding elements with StackValueData::Public + let mut offset = 0; + while offset < slice.len() { + let copy_len = std::cmp::min(32, slice.len() - offset); + let mut tmp = [0u8; 32]; + tmp[32 - copy_len..].copy_from_slice(&slice[offset..offset + copy_len]); + let value = U256::from_be_slice(&tmp); + *self.data.get_mut(offset / 32).unwrap() = StackValueData::Public(value); + offset += 32; + } } Ok(()) From 3f8450ee7752582b1a1696fb4bcfdc37b698f88a Mon Sep 17 00:00:00 2001 From: "Lucas (Kbooz)" Date: Mon, 23 Dec 2024 15:02:49 -0300 Subject: [PATCH 13/44] feat: push slice using new array instead of pointers --- Cargo.lock | 2 +- crates/interpreter/src/interpreter/stack.rs | 68 +++++++++------------ 2 files changed, 31 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dca2ac9a24..d8d15609e5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4341,7 +4341,7 @@ dependencies = [ [[package]] name = "tandem" version = "0.3.0" -source = "git+https://github.com/GatewayLabs/tandem.git?branch=feat/serde-serialization#bfa2e1030bc284e06c2945258d5e194150e78e8d" +source = "git+https://github.com/GatewayLabs/tandem.git#1340a8c360cfa411086f7a1a99a7a39f72420c27" dependencies = [ "bincode", "blake3", diff --git a/crates/interpreter/src/interpreter/stack.rs b/crates/interpreter/src/interpreter/stack.rs index 989ccc3278..7669e8b213 100644 --- a/crates/interpreter/src/interpreter/stack.rs +++ b/crates/interpreter/src/interpreter/stack.rs @@ -420,34 +420,26 @@ impl Stack { return Err(InstructionResult::StackOverflow); } - // SAFETY: Length checked above. - unsafe { - let dst = self.data.as_mut_ptr().add(self.data.len()).cast::(); - self.data.set_len(new_len); - - let mut i = 0; - - // Write full words - let words = slice.chunks_exact(32); - let partial_last_word = words.remainder(); - for word in words { - // Note: We unroll `U256::from_be_bytes` here to write directly into the buffer, - // instead of creating a 32 byte array on the stack and then copying it over. - for l in word.rchunks_exact(8) { - dst.add(i).write(u64::from_be_bytes(l.try_into().unwrap())); - i += 1; - } - } + let mut temp_data = Vec::with_capacity(n_words * 4); // 4 u64 per U256 + + let mut i = 0; - if partial_last_word.is_empty() { - return Ok(()); + // Write full words + let words = slice.chunks_exact(32); + let partial_last_word = words.remainder(); + for word in words { + for l in word.rchunks_exact(8) { + temp_data.push(u64::from_be_bytes(l.try_into().unwrap())); + i += 1; } + } + if !partial_last_word.is_empty() { // Write limbs of partial last word let limbs = partial_last_word.rchunks_exact(8); let partial_last_limb = limbs.remainder(); for l in limbs { - dst.add(i).write(u64::from_be_bytes(l.try_into().unwrap())); + temp_data.push(u64::from_be_bytes(l.try_into().unwrap())); i += 1; } @@ -455,30 +447,30 @@ impl Stack { if !partial_last_limb.is_empty() { let mut tmp = [0u8; 8]; tmp[8 - partial_last_limb.len()..].copy_from_slice(partial_last_limb); - dst.add(i).write(u64::from_be_bytes(tmp)); + temp_data.push(u64::from_be_bytes(tmp)); i += 1; } + } - debug_assert_eq!((i + 3) / 4, n_words, "wrote too much"); - - // Zero out upper bytes of last word - let m = i % 4; // 32 / 8 - if m != 0 { - dst.add(i).write_bytes(0, 4 - m); - } + debug_assert_eq!((i + 3) / 4, n_words, "wrote too much"); - // Convert the slice to U256 values and replace the corresponding elements with StackValueData::Public - let mut offset = 0; - while offset < slice.len() { - let copy_len = std::cmp::min(32, slice.len() - offset); - let mut tmp = [0u8; 32]; - tmp[32 - copy_len..].copy_from_slice(&slice[offset..offset + copy_len]); - let value = U256::from_be_slice(&tmp); - *self.data.get_mut(offset / 32).unwrap() = StackValueData::Public(value); - offset += 32; + // Zero out upper bytes of last word + let m = i % 4; // 32 / 8 + if m != 0 { + for _ in 0..(4 - m) { + temp_data.push(0); } } + let u256_data: Vec = temp_data + .chunks_exact(4) + .map(|chunk| { + StackValueData::Public(U256::from_limbs([chunk[0], chunk[1], chunk[2], chunk[3]])) + }) + .collect(); + + self.data.extend_from_slice(&u256_data); + Ok(()) } From 2959759badc11a70cc997be6299dcc773f7a9211 Mon Sep 17 00:00:00 2001 From: "Lucas (Kbooz)" Date: Mon, 23 Dec 2024 15:28:24 -0300 Subject: [PATCH 14/44] feat: add using circuit builder --- .../src/instructions/arithmetic.rs | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/crates/interpreter/src/instructions/arithmetic.rs b/crates/interpreter/src/instructions/arithmetic.rs index 8445eead95..698d2b41b0 100644 --- a/crates/interpreter/src/instructions/arithmetic.rs +++ b/crates/interpreter/src/instructions/arithmetic.rs @@ -15,17 +15,13 @@ pub fn add(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); pop_top!(interpreter, op1, op2); - // verificar se op1 é StackDataValue::Public ou StackDataValue::Private - // caso seja public, converter para input com o interpreter.circuit_builder.input - let garbled_op1 = op1.to_garbled_value(&mut interpreter.circuit_builder); let garbled_op2 = op2.to_garbled_value(&mut interpreter.circuit_builder); - // cria o circuito de soma usando o circuit builder - + // creates the sum circuit using the circuit builder let result = interpreter.circuit_builder.add(&garbled_op1, &garbled_op2); - // Salvar sempre como StackDataValue::Private + // Always save as StackDataValue::Private *op2 = StackValueData::Private(result); } @@ -149,7 +145,8 @@ pub fn signextend(interpreter: &mut Interpreter, _host: &mut H #[cfg(test)] mod tests { use super::*; - use crate::{Contract, DummyHost}; + use crate::{instructions::utility::garbled_int_to_ruint, Contract, DummyHost}; + use compute::uint::GarbledUint256; use primitives::ruint::Uint; fn generate_interpreter() -> Interpreter { @@ -185,10 +182,15 @@ mod tests { add(&mut interpreter, &mut host); - let result = interpreter.stack.pop().unwrap(); + let output_indices = interpreter.stack.pop().unwrap(); + + let result: GarbledUint256 = interpreter + .circuit_builder + .compile_and_execute(&output_indices.into()) + .unwrap(); let expected_result = Uint::<256, 4>::from(18u64); - assert_eq!(result, expected_result.into()); + assert_eq!(garbled_uint_to_ruint(&result), expected_result); } #[test] From 3cecf044e5aee35fca4e73934d8419d67ed4658f Mon Sep 17 00:00:00 2001 From: "Lucas (Kbooz)" Date: Mon, 23 Dec 2024 15:31:00 -0300 Subject: [PATCH 15/44] chore: todo --- crates/interpreter/src/interpreter/stack.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/interpreter/src/interpreter/stack.rs b/crates/interpreter/src/interpreter/stack.rs index 7669e8b213..66dd145522 100644 --- a/crates/interpreter/src/interpreter/stack.rs +++ b/crates/interpreter/src/interpreter/stack.rs @@ -420,6 +420,8 @@ impl Stack { return Err(InstructionResult::StackOverflow); } + // TODO: Optimize this by directly writing to the stack buffer + // Currently we're writing to a temporary buffer and then copying to the stack buffer let mut temp_data = Vec::with_capacity(n_words * 4); // 4 u64 per U256 let mut i = 0; From c469f550335a01b8c07078172095e6504fec53da Mon Sep 17 00:00:00 2001 From: "Lucas (Kbooz)" Date: Mon, 23 Dec 2024 16:16:35 -0300 Subject: [PATCH 16/44] feat: created pop_top_gates macro --- crates/interpreter/src/instructions/macros.rs | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/crates/interpreter/src/instructions/macros.rs b/crates/interpreter/src/instructions/macros.rs index 44be6b785d..f5b177cd2b 100644 --- a/crates/interpreter/src/instructions/macros.rs +++ b/crates/interpreter/src/instructions/macros.rs @@ -250,6 +250,51 @@ macro_rules! pop_top { }; } +// Pops 'StackValueData' and 'GateIndexVec' values from the stack. Fails the instruction if the stack is too small. +#[macro_export] +macro_rules! pop_top_gates { + ($interp:expr, $x1:ident, $garbled_x1:ident) => { + if $interp.stack.len() < 1 { + $interp.instruction_result = $crate::InstructionResult::StackUnderflow; + return; + } + // SAFETY: Length is checked above. + let mut $x1 = unsafe { $interp.stack.top_unsafe() }; + let $garbled_x1 = $x1.to_garbled_value(&mut $interp.circuit_builder); + }; + ($interp:expr, $x1:ident, $x2:ident, $garbled_x1:ident, $garbled_x2:ident) => { + if $interp.stack.len() < 2 { + $interp.instruction_result = $crate::InstructionResult::StackUnderflow; + return; + } + // SAFETY: Length is checked above. + let ($x1, $x2, $garbled_x1, $garbled_x2) = unsafe { + let (val1, val2) = $interp.stack.pop_top_unsafe(); + let (garbled_val1, garbled_val2) = ( + val1.to_garbled_value(&mut $interp.circuit_builder), + val2.to_garbled_value(&mut $interp.circuit_builder), + ); + (val1, val2, garbled_val1, garbled_val2) + }; + }; + ($interp:expr, $x1:ident, $x2:ident, $x3:ident) => { + if ($interp.stack.len() < 3) { + $interp.instruction_result = $crate::InstructionResult::StackUnderflow; + return; + } + // SAFETY: Length is checked above. + let ($x1, $x2, $x3, $garbled_x1, $garbled_x2, $garbled_x3) = unsafe { + let (val1, val2, val3) = $interp.stack.pop_top_unsafe(); + let (garbled_val1, garbled_val2, garbled_val3) = ( + val1.to_garbled_value(&mut $interp.circuit_builder), + val2.to_garbled_value(&mut $interp.circuit_builder), + val3.to_garbled_value(&mut $interp.circuit_builder), + ); + (val1, val2, garbled_val1, garbled_val2, garbled_val3) + }; + }; +} + /// Pushes `B256` values onto the stack. Fails the instruction if the stack is full. #[macro_export] macro_rules! push_b256 { From 688dcf7252cab7f97c6edd3e87265d50c3c72d00 Mon Sep 17 00:00:00 2001 From: "Lucas (Kbooz)" Date: Mon, 23 Dec 2024 16:19:16 -0300 Subject: [PATCH 17/44] feat: use macro and implemented mult --- .../src/instructions/arithmetic.rs | 36 +++++++++++-------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/crates/interpreter/src/instructions/arithmetic.rs b/crates/interpreter/src/instructions/arithmetic.rs index 698d2b41b0..b63d0f1ae6 100644 --- a/crates/interpreter/src/instructions/arithmetic.rs +++ b/crates/interpreter/src/instructions/arithmetic.rs @@ -13,10 +13,8 @@ use specification::hardfork::Spec; pub fn add(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); - pop_top!(interpreter, op1, op2); - - let garbled_op1 = op1.to_garbled_value(&mut interpreter.circuit_builder); - let garbled_op2 = op2.to_garbled_value(&mut interpreter.circuit_builder); + // pop_top!(interpreter, op1, op2); + pop_top_gates!(interpreter, op1, op2, garbled_op1, garbled_op2); // creates the sum circuit using the circuit builder let result = interpreter.circuit_builder.add(&garbled_op1, &garbled_op2); @@ -27,13 +25,11 @@ pub fn add(interpreter: &mut Interpreter, _host: &mut H) { pub fn mul(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::LOW); - pop_top!(interpreter, op1, op2); + pop_top_gates!(interpreter, op1, op2, garbled_op1, garbled_op2); - let garbled_op1 = ruint_to_garbled_uint(&op1.into()); - let garbled_op2 = ruint_to_garbled_uint(&op2.to_u256()); - let result = garbled_op1.mul(garbled_op2); + let result = interpreter.circuit_builder.mul(&garbled_op1, &garbled_op2); - *op2 = garbled_uint_to_ruint(&result).into(); + *op2 = StackValueData::Private(result); } pub fn sub(interpreter: &mut Interpreter, _host: &mut H) { @@ -145,7 +141,7 @@ pub fn signextend(interpreter: &mut Interpreter, _host: &mut H #[cfg(test)] mod tests { use super::*; - use crate::{instructions::utility::garbled_int_to_ruint, Contract, DummyHost}; + use crate::{Contract, DummyHost}; use compute::uint::GarbledUint256; use primitives::ruint::Uint; @@ -212,10 +208,16 @@ mod tests { sub(&mut interpreter, &mut host); - let result = interpreter.stack.pop().unwrap(); let expected_result = Uint::<256, 4>::from(70u64); - assert_eq!(result, expected_result.into()); + let output_indices = interpreter.stack.pop().unwrap(); + + let result: GarbledUint256 = interpreter + .circuit_builder + .compile_and_execute(&output_indices.into()) + .unwrap(); + + assert_eq!(garbled_uint_to_ruint(&result), expected_result); } #[test] @@ -241,10 +243,16 @@ mod tests { mul(&mut interpreter, &mut host); // Check the result - let result = interpreter.stack.pop().unwrap(); let expected_result = Uint::<256, 4>::from(2000u64); - assert_eq!(result, expected_result.into()); + let output_indices = interpreter.stack.pop().unwrap(); + + let result: GarbledUint256 = interpreter + .circuit_builder + .compile_and_execute(&output_indices.into()) + .unwrap(); + + assert_eq!(garbled_uint_to_ruint(&result), expected_result); } #[test] From 9206680e7f06b8f224efb73e43d55b39c34e5a17 Mon Sep 17 00:00:00 2001 From: "Lucas (Kbooz)" Date: Mon, 23 Dec 2024 16:23:33 -0300 Subject: [PATCH 18/44] feat: mult is working --- crates/interpreter/src/instructions/arithmetic.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/crates/interpreter/src/instructions/arithmetic.rs b/crates/interpreter/src/instructions/arithmetic.rs index b63d0f1ae6..ca47fc1d2d 100644 --- a/crates/interpreter/src/instructions/arithmetic.rs +++ b/crates/interpreter/src/instructions/arithmetic.rs @@ -14,7 +14,7 @@ use specification::hardfork::Spec; pub fn add(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); // pop_top!(interpreter, op1, op2); - pop_top_gates!(interpreter, op1, op2, garbled_op1, garbled_op2); + pop_top_gates!(interpreter, _op1, op2, garbled_op1, garbled_op2); // creates the sum circuit using the circuit builder let result = interpreter.circuit_builder.add(&garbled_op1, &garbled_op2); @@ -25,7 +25,7 @@ pub fn add(interpreter: &mut Interpreter, _host: &mut H) { pub fn mul(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::LOW); - pop_top_gates!(interpreter, op1, op2, garbled_op1, garbled_op2); + pop_top_gates!(interpreter, _op1, op2, garbled_op1, garbled_op2); let result = interpreter.circuit_builder.mul(&garbled_op1, &garbled_op2); @@ -34,13 +34,11 @@ pub fn mul(interpreter: &mut Interpreter, _host: &mut H) { pub fn sub(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); - pop_top!(interpreter, op1, op2); + pop_top_gates!(interpreter, _op1, op2, garbled_op1, garbled_op2); - let garbled_op1 = ruint_to_garbled_uint(&op1.into()); - let garbled_op2 = ruint_to_garbled_uint(&op2.to_u256()); - let result = garbled_op2.sub(garbled_op1); + let result = interpreter.circuit_builder.sub(&garbled_op1, &garbled_op2); - *op2 = garbled_uint_to_ruint(&result).into(); + *op2 = StackValueData::Private(result); } pub fn div(interpreter: &mut Interpreter, _host: &mut H) { From 5c905e08a26dee2ad7cba260e3e13e851d53fc20 Mon Sep 17 00:00:00 2001 From: "Lucas (Kbooz)" Date: Mon, 23 Dec 2024 16:26:07 -0300 Subject: [PATCH 19/44] feat: sub --- crates/interpreter/src/instructions/arithmetic.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/interpreter/src/instructions/arithmetic.rs b/crates/interpreter/src/instructions/arithmetic.rs index ca47fc1d2d..feaf8a68d4 100644 --- a/crates/interpreter/src/instructions/arithmetic.rs +++ b/crates/interpreter/src/instructions/arithmetic.rs @@ -195,14 +195,14 @@ mod tests { let op1 = Uint::<256, 4>::from(90u64); let op2 = Uint::<256, 4>::from(20u64); - interpreter - .stack - .push(op1.clone()) - .expect("Failed to push op1 to stack"); interpreter .stack .push(op2.clone()) .expect("Failed to push op2 to stack"); + interpreter + .stack + .push(op1.clone()) + .expect("Failed to push op1 to stack"); sub(&mut interpreter, &mut host); From 3caa241dddb645332744399af3b581e8a59f7f17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Conserva?= Date: Mon, 23 Dec 2024 17:39:04 -0300 Subject: [PATCH 20/44] feat: implement division operation with circuit builder --- .../src/instructions/arithmetic.rs | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/crates/interpreter/src/instructions/arithmetic.rs b/crates/interpreter/src/instructions/arithmetic.rs index feaf8a68d4..bba401b91b 100644 --- a/crates/interpreter/src/instructions/arithmetic.rs +++ b/crates/interpreter/src/instructions/arithmetic.rs @@ -43,14 +43,11 @@ pub fn sub(interpreter: &mut Interpreter, _host: &mut H) { pub fn div(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::LOW); - pop_top!(interpreter, op1, op2); - if !op2.to_u256().is_zero() { - let garbled_op1 = ruint_to_garbled_uint(&op1.into()); - let garbled_op2 = ruint_to_garbled_uint(&op2.to_u256()); - let result = garbled_op1.div(garbled_op2); + pop_top_gates!(interpreter, _op1, op2, garbled_op1, garbled_op2); - *op2 = garbled_uint_to_ruint(&result).into(); - } + let result = interpreter.circuit_builder.div(&garbled_op1, &garbled_op2); + + *op2 = StackValueData::Private(result); } //TODO: Implement circuit for signed division @@ -276,10 +273,16 @@ mod tests { div(&mut interpreter, &mut host); // Check the result - let result = interpreter.stack.pop().unwrap(); let expected_result = Uint::<256, 4>::from(5u64); - assert_eq!(result, expected_result.into()); + let output_indices = interpreter.stack.pop().unwrap(); + + let result: GarbledUint256 = interpreter + .circuit_builder + .compile_and_execute(&output_indices.into()) + .unwrap(); + + assert_eq!(garbled_uint_to_ruint(&result), expected_result); } #[test] From 798da7856c0c2157fdc77afd341e571cf2aae9cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Conserva?= Date: Mon, 23 Dec 2024 17:51:41 -0300 Subject: [PATCH 21/44] feat: refactor rem function to use circuit builder for modulo operation --- .../src/instructions/arithmetic.rs | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/crates/interpreter/src/instructions/arithmetic.rs b/crates/interpreter/src/instructions/arithmetic.rs index bba401b91b..fced06bc10 100644 --- a/crates/interpreter/src/instructions/arithmetic.rs +++ b/crates/interpreter/src/instructions/arithmetic.rs @@ -60,14 +60,11 @@ pub fn sdiv(interpreter: &mut Interpreter, _host: &mut H) { pub fn rem(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::LOW); - pop_top!(interpreter, op1, op2); - if !op2.to_u256().is_zero() { - let garbled_op1 = ruint_to_garbled_uint(&op1.into()); - let garbled_op2 = ruint_to_garbled_uint(&op2.to_u256()); - let result = garbled_op1.rem(garbled_op2); + pop_top_gates!(interpreter, _op1, op2, garbled_op1, garbled_op2); + + let result = interpreter.circuit_builder.rem(&garbled_op1, &garbled_op2); - *op2 = garbled_uint_to_ruint(&result).into(); - } + *op2 = StackValueData::Private(result); } //TODO: Implement circuit for signed modulo @@ -308,9 +305,15 @@ mod tests { rem(&mut interpreter, &mut host); // Check the result - let result = interpreter.stack.pop().unwrap(); let expected_result = Uint::<256, 4>::from(0u64); - assert_eq!(result, expected_result.into()); + let output_indices = interpreter.stack.pop().unwrap(); + + let result: GarbledUint256 = interpreter + .circuit_builder + .compile_and_execute(&output_indices.into()) + .unwrap(); + + assert_eq!(garbled_uint_to_ruint(&result), expected_result); } } From 9ae8afeff7b739a41705ec4b61037f6270f7608b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Conserva?= Date: Thu, 26 Dec 2024 11:33:28 -0300 Subject: [PATCH 22/44] feat: implement less than comparison using circuit builder and add utility function for garbled uint to bool conversion --- .../interpreter/src/instructions/bitwise.rs | 88 +++++++++++++++++-- .../interpreter/src/instructions/utility.rs | 4 + 2 files changed, 86 insertions(+), 6 deletions(-) diff --git a/crates/interpreter/src/instructions/bitwise.rs b/crates/interpreter/src/instructions/bitwise.rs index 142f1f1d93..785f6d7863 100644 --- a/crates/interpreter/src/instructions/bitwise.rs +++ b/crates/interpreter/src/instructions/bitwise.rs @@ -1,21 +1,25 @@ -use super::i256::i256_cmp; +use core::ops::{Div, Mul, Rem, Sub}; +use super::i256::{i256_cmp}; use crate::{ gas, instructions::utility::{garbled_uint_to_ruint, ruint_to_garbled_uint}, + interpreter::StackValueData, Host, Interpreter, }; -use core::cmp::Ordering; +use compute::prelude::{CircuitExecutor, GateIndexVec}; use primitives::U256; +use core::cmp::Ordering; use specification::hardfork::Spec; pub fn lt(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); - pop_top!(interpreter, op1, op2); + pop_top_gates!(interpreter, _op1, op2, garbled_op1, garbled_op2); - let garbled_op1 = ruint_to_garbled_uint(&op1.into()); - let garbled_op2 = ruint_to_garbled_uint(&op2.to_u256()); + // creates the sum circuit using the circuit builder + let result = interpreter.circuit_builder.lt(&garbled_op1, &garbled_op2); - *op2 = U256::from(garbled_op1.lt(&garbled_op2)).into(); + // Always save as StackDataValue::Private + *op2 = StackValueData::Private(GateIndexVec::from(result)); } pub fn gt(interpreter: &mut Interpreter, _host: &mut H) { @@ -166,12 +170,84 @@ pub fn sar(interpreter: &mut Interpreter, _host: & #[cfg(test)] mod tests { + use super::*; use crate::instructions::bitwise::{byte, sar, shl, shr}; + use crate::instructions::utility::garbled_uint_to_bool; use crate::{Contract, DummyHost, Interpreter}; + use compute::uint::GarbledUint256; use primitives::{uint, U256}; use specification::hardfork::LatestSpec; use wiring::{default::Env, DefaultEthereumWiring}; + fn generate_interpreter() -> Interpreter { + let contract = Contract::default(); + let gas_limit = 10_000_000; + let is_static = false; + Interpreter::new(contract, gas_limit, is_static) + } + + fn generate_host() -> DummyHost< + wiring::EthereumWiring, ()>, + > { + DummyHost::default() + } + + #[test] + fn test_lt() { + let mut interpreter = generate_interpreter(); + let mut host = generate_host(); + struct TestCase { + op1: U256, + op2: U256, + expected: bool, + } + + let test_cases = vec![ + TestCase { + op1: U256::from(1u64), + op2: U256::from(2u64), + expected: true, + }, + TestCase { + op1: U256::from(2u64), + op2: U256::from(1u64), + expected: false, + }, + TestCase { + op1: U256::from(1u64), + op2: U256::from(1u64), + expected: false, + }, + TestCase { + op1: U256::from(1u64), + op2: U256::from(0u64), + expected: false, + }, + ]; + + for test in test_cases.iter() { + interpreter + .stack + .push(test.op2) + .expect("Failed to push op2 to stack"); + interpreter + .stack + .push(test.op1) + .expect("Failed to push op1 to stack"); + + lt(&mut interpreter, &mut host); + + let output_indices = interpreter.stack.pop().unwrap(); + + let result: GarbledUint256 = interpreter + .circuit_builder + .compile_and_execute(&output_indices.into()) + .unwrap(); + + assert_eq!(garbled_uint_to_bool(&result), test.expected, "Failed for op1: {:?}, op2: {:?}", test.op1, test.op2); + } + } + #[test] fn test_shift_left() { let mut host = DummyHost::new(Env::default()); diff --git a/crates/interpreter/src/instructions/utility.rs b/crates/interpreter/src/instructions/utility.rs index 0873120ba8..aa4cbfac28 100644 --- a/crates/interpreter/src/instructions/utility.rs +++ b/crates/interpreter/src/instructions/utility.rs @@ -44,6 +44,10 @@ pub fn garbled_uint_to_ruint(value: &GarbledUint<256>) -> Uint<256, 4> { Uint::from_le_bytes(array) } +pub fn garbled_uint_to_bool(value: &GarbledUint<256>) -> bool { + value.bits[0] +} + pub fn ruint_to_garbled_int(value: &Uint<256, 4>) -> GarbledInt<256> { let bytes: [u8; 32] = value.to_le_bytes(); From a40eb22ac499f2221eb800b4afdadeddf756014b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Conserva?= Date: Thu, 26 Dec 2024 11:36:24 -0300 Subject: [PATCH 23/44] feat: implement greater than comparison using circuit builder and add unit tests --- .../interpreter/src/instructions/bitwise.rs | 65 +++++++++++++++++-- 1 file changed, 59 insertions(+), 6 deletions(-) diff --git a/crates/interpreter/src/instructions/bitwise.rs b/crates/interpreter/src/instructions/bitwise.rs index 785f6d7863..6a07c18582 100644 --- a/crates/interpreter/src/instructions/bitwise.rs +++ b/crates/interpreter/src/instructions/bitwise.rs @@ -15,21 +15,18 @@ pub fn lt(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); pop_top_gates!(interpreter, _op1, op2, garbled_op1, garbled_op2); - // creates the sum circuit using the circuit builder let result = interpreter.circuit_builder.lt(&garbled_op1, &garbled_op2); - // Always save as StackDataValue::Private *op2 = StackValueData::Private(GateIndexVec::from(result)); } pub fn gt(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); - pop_top!(interpreter, op1, op2); + pop_top_gates!(interpreter, _op1, op2, garbled_op1, garbled_op2); - let garbled_op1 = ruint_to_garbled_uint(&op1.into()); - let garbled_op2 = ruint_to_garbled_uint(&op2.to_u256()); + let result = interpreter.circuit_builder.gt(&garbled_op1, &garbled_op2); - *op2 = U256::from(garbled_op1.gt(&garbled_op2)).into(); + *op2 = StackValueData::Private(GateIndexVec::from(result)); } // TODO: Implement in garbled circuits @@ -248,6 +245,62 @@ mod tests { } } + #[test] + fn test_gt() { + let mut interpreter = generate_interpreter(); + let mut host = generate_host(); + struct TestCase { + op1: U256, + op2: U256, + expected: bool, + } + + let test_cases = vec![ + TestCase { + op1: U256::from(1u64), + op2: U256::from(2u64), + expected: false, + }, + TestCase { + op1: U256::from(2u64), + op2: U256::from(1u64), + expected: true, + }, + TestCase { + op1: U256::from(1u64), + op2: U256::from(1u64), + expected: false, + }, + TestCase { + op1: U256::from(1u64), + op2: U256::from(0u64), + expected: true, + }, + ]; + + for test in test_cases.iter() { + interpreter + .stack + .push(test.op2) + .expect("Failed to push op2 to stack"); + interpreter + .stack + .push(test.op1) + .expect("Failed to push op1 to stack"); + + gt(&mut interpreter, &mut host); + + let output_indices = interpreter.stack.pop().unwrap(); + + let result: GarbledUint256 = interpreter + .circuit_builder + .compile_and_execute(&output_indices.into()) + .unwrap(); + + assert_eq!(garbled_uint_to_bool(&result), test.expected, "Failed for op1: {:?}, op2: {:?}", test.op1, test.op2); + } + } + #[test] fn test_shift_left() { let mut host = DummyHost::new(Env::default()); From e3fbb52d3cadec6ee15d5166b41ac764f01d5c1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Conserva?= Date: Thu, 26 Dec 2024 11:38:12 -0300 Subject: [PATCH 24/44] feat: implement equality comparison using circuit builder and add unit tests --- .../interpreter/src/instructions/bitwise.rs | 63 +++++++++++++++++-- 1 file changed, 59 insertions(+), 4 deletions(-) diff --git a/crates/interpreter/src/instructions/bitwise.rs b/crates/interpreter/src/instructions/bitwise.rs index 6a07c18582..f35b93cb65 100644 --- a/crates/interpreter/src/instructions/bitwise.rs +++ b/crates/interpreter/src/instructions/bitwise.rs @@ -45,12 +45,11 @@ pub fn sgt(interpreter: &mut Interpreter, _host: &mut H) { pub fn eq(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); - pop_top!(interpreter, op1, op2); + pop_top_gates!(interpreter, _op1, op2, garbled_op1, garbled_op2); - let garbled_op1 = ruint_to_garbled_uint(&op1.into()); - let garbled_op2 = ruint_to_garbled_uint(&op2.to_u256()); + let result = interpreter.circuit_builder.eq(&garbled_op1, &garbled_op2); - *op2 = U256::from(garbled_op1.eq(&garbled_op2)).into(); + *op2 = StackValueData::Private(GateIndexVec::from(result)); } pub fn iszero(interpreter: &mut Interpreter, _host: &mut H) { @@ -301,6 +300,62 @@ mod tests { } } + #[test] + fn test_eq() { + let mut interpreter = generate_interpreter(); + let mut host = generate_host(); + struct TestCase { + op1: U256, + op2: U256, + expected: bool, + } + + let test_cases = vec![ + TestCase { + op1: U256::from(1u64), + op2: U256::from(2u64), + expected: false, + }, + TestCase { + op1: U256::from(2u64), + op2: U256::from(1u64), + expected: false, + }, + TestCase { + op1: U256::from(1u64), + op2: U256::from(1u64), + expected: true, + }, + TestCase { + op1: U256::from(1u64), + op2: U256::from(0u64), + expected: false, + }, + ]; + + for test in test_cases.iter() { + interpreter + .stack + .push(test.op2) + .expect("Failed to push op2 to stack"); + interpreter + .stack + .push(test.op1) + .expect("Failed to push op1 to stack"); + + eq(&mut interpreter, &mut host); + + let output_indices = interpreter.stack.pop().unwrap(); + + let result: GarbledUint256 = interpreter + .circuit_builder + .compile_and_execute(&output_indices.into()) + .unwrap(); + + assert_eq!(garbled_uint_to_bool(&result), test.expected, "Failed for op1: {:?}, op2: {:?}", test.op1, test.op2); + } + } + #[test] fn test_shift_left() { let mut host = DummyHost::new(Env::default()); From 3027901eb2e3565b0361a11cc6aec7a332b2c5d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Conserva?= Date: Thu, 26 Dec 2024 15:23:58 -0300 Subject: [PATCH 25/44] feat: implement bitwise NOT operation using circuit builder and add unit tests --- .../interpreter/src/instructions/bitwise.rs | 54 +++++++++++++++++-- 1 file changed, 50 insertions(+), 4 deletions(-) diff --git a/crates/interpreter/src/instructions/bitwise.rs b/crates/interpreter/src/instructions/bitwise.rs index f35b93cb65..3bdab523cc 100644 --- a/crates/interpreter/src/instructions/bitwise.rs +++ b/crates/interpreter/src/instructions/bitwise.rs @@ -52,12 +52,14 @@ pub fn eq(interpreter: &mut Interpreter, _host: &mut H) { *op2 = StackValueData::Private(GateIndexVec::from(result)); } +//TODO: Remove this function and use eq zero instead pub fn iszero(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); pop_top!(interpreter, op1); *op1 = U256::from(op1.to_u256().is_zero()).into(); } +//TODO: Implement circuit pub fn bitand(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); pop_top!(interpreter, op1, op2); @@ -69,6 +71,7 @@ pub fn bitand(interpreter: &mut Interpreter, _host: &mut H) { *op2 = garbled_uint_to_ruint(&result).into(); } +//TODO: Implement circuit pub fn bitor(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); pop_top!(interpreter, op1, op2); @@ -80,6 +83,7 @@ pub fn bitor(interpreter: &mut Interpreter, _host: &mut H) { *op2 = garbled_uint_to_ruint(&result).into(); } +//TODO: Implement circuit pub fn bitxor(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); pop_top!(interpreter, op1, op2); @@ -93,12 +97,11 @@ pub fn bitxor(interpreter: &mut Interpreter, _host: &mut H) { pub fn not(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); - pop_top!(interpreter, op1); + pop_top_gates!(interpreter, _op1, op2, _garbled_op1, garbled_op2); - let garbled_op1 = ruint_to_garbled_uint(&op1.to_u256()); - let result = !garbled_op1; + let result = interpreter.circuit_builder.not(&garbled_op2); - *op1 = garbled_uint_to_ruint(&result).into(); + *op2 = StackValueData::Private(GateIndexVec::from(result)); } // TODO: Implement in garbled circuits @@ -356,6 +359,49 @@ mod tests { } } + #[test] + fn test_not() { + let mut interpreter = generate_interpreter(); + let mut host = generate_host(); + struct TestCase { + op1: U256, + expected: U256, + } + + let test_cases = vec![ + TestCase { + op1: U256::from(1u64), + expected: U256::from(0u64), + }, + TestCase { + op1: U256::from(0u64), + expected: U256::from(1u64), + }, + TestCase { + op1: U256::from(0x1234567890abcdefu128), + expected: U256::from(0xedcba9876f543210u128), + }, + ]; + + for test in test_cases.iter() { + interpreter + .stack + .push(test.op1) + .expect("Failed to push op1 to stack"); + + not(&mut interpreter, &mut host); + + let output_indices = interpreter.stack.pop().unwrap(); + + let result: GarbledUint256 = interpreter + .circuit_builder + .compile_and_execute(&output_indices.into()) + .unwrap(); + + assert_eq!(garbled_uint_to_ruint(&result), test.expected, "Failed for op1: {:?}", test.op1); + } + } + #[test] fn test_shift_left() { let mut host = DummyHost::new(Env::default()); From 93a64939aa448f809170dd91a58938a48555e7e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Conserva?= Date: Thu, 26 Dec 2024 15:59:24 -0300 Subject: [PATCH 26/44] feat: implement bitwise AND operation using circuit builder and add unit tests --- .../interpreter/src/instructions/bitwise.rs | 66 ++++++++++++++++--- 1 file changed, 57 insertions(+), 9 deletions(-) diff --git a/crates/interpreter/src/instructions/bitwise.rs b/crates/interpreter/src/instructions/bitwise.rs index 3bdab523cc..da07443c7b 100644 --- a/crates/interpreter/src/instructions/bitwise.rs +++ b/crates/interpreter/src/instructions/bitwise.rs @@ -59,16 +59,13 @@ pub fn iszero(interpreter: &mut Interpreter, _host: &mut H) { *op1 = U256::from(op1.to_u256().is_zero()).into(); } -//TODO: Implement circuit pub fn bitand(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); - pop_top!(interpreter, op1, op2); + pop_top_gates!(interpreter, _op1, op2, garbled_op1, garbled_op2); - let garbled_op1 = ruint_to_garbled_uint(&op1.into()); - let garbled_op2 = ruint_to_garbled_uint(&op2.to_u256()); - let result = garbled_op1 & garbled_op2; + let result = interpreter.circuit_builder.and(&garbled_op1, &garbled_op2); - *op2 = garbled_uint_to_ruint(&result).into(); + *op2 = StackValueData::Private(GateIndexVec::from(result)); } //TODO: Implement circuit @@ -97,11 +94,11 @@ pub fn bitxor(interpreter: &mut Interpreter, _host: &mut H) { pub fn not(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); - pop_top_gates!(interpreter, _op1, op2, _garbled_op1, garbled_op2); + pop_top_gates!(interpreter, op1, garbled_op1); - let result = interpreter.circuit_builder.not(&garbled_op2); + let result = interpreter.circuit_builder.not(&garbled_op1); - *op2 = StackValueData::Private(GateIndexVec::from(result)); + *op1 = StackValueData::Private(GateIndexVec::from(result)); } // TODO: Implement in garbled circuits @@ -402,6 +399,57 @@ mod tests { } } + #[test] + fn test_bitand () { + let mut interpreter = generate_interpreter(); + let mut host = generate_host(); + struct TestCase { + op1: U256, + op2: U256, + expected: U256, + } + + let test_cases = vec![ + TestCase { + op1: U256::from(0x1234567890abcdefu128), + op2: U256::from(0xfedcba0987654321u128), + expected: U256::from(0x0214440010044001u128), + }, + TestCase { + op1: U256::from(0xffffffffffffffffu128), + op2: U256::from(0x0000000000000000u128), + expected: U256::from(0x0000000000000000u128), + }, + TestCase { + op1: U256::from(0x0f0f0f0f0f0f0f0fu128), + op2: U256::from(0xf0f0f0f0f0f0f0f0u128), + expected: U256::from(0x0000000000000000u128), + }, + ]; + + for test in test_cases.iter() { + interpreter + .stack + .push(test.op2) + .expect("Failed to push op2 to stack"); + interpreter + .stack + .push(test.op1) + .expect("Failed to push op1 to stack"); + + bitand(&mut interpreter, &mut host); + + let output_indices = interpreter.stack.pop().unwrap(); + + let result: GarbledUint256 = interpreter + .circuit_builder + .compile_and_execute(&output_indices.into()) + .unwrap(); + + assert_eq!(garbled_uint_to_ruint(&result), test.expected, "Failed for op1: {:?}, op2: {:?}", test.op1, test.op2); + } + } + #[test] fn test_shift_left() { let mut host = DummyHost::new(Env::default()); From c6cb128eebf4826f1beff380136f366deb48835b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Conserva?= Date: Thu, 26 Dec 2024 16:59:13 -0300 Subject: [PATCH 27/44] feat: implement bitwise OR operation using circuit builder and add unit tests --- .../interpreter/src/instructions/bitwise.rs | 85 +++++++++++++++++-- 1 file changed, 79 insertions(+), 6 deletions(-) diff --git a/crates/interpreter/src/instructions/bitwise.rs b/crates/interpreter/src/instructions/bitwise.rs index da07443c7b..82bc11c825 100644 --- a/crates/interpreter/src/instructions/bitwise.rs +++ b/crates/interpreter/src/instructions/bitwise.rs @@ -68,16 +68,13 @@ pub fn bitand(interpreter: &mut Interpreter, _host: &mut H) { *op2 = StackValueData::Private(GateIndexVec::from(result)); } -//TODO: Implement circuit pub fn bitor(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); - pop_top!(interpreter, op1, op2); + pop_top_gates!(interpreter, _op1, op2, garbled_op1, garbled_op2); - let garbled_op1 = ruint_to_garbled_uint(&op1.into()); - let garbled_op2 = ruint_to_garbled_uint(&op2.to_u256()); - let result = garbled_op1 | garbled_op2; + let result = interpreter.circuit_builder.or(&garbled_op1, &garbled_op2); - *op2 = garbled_uint_to_ruint(&result).into(); + *op2 = StackValueData::Private(GateIndexVec::from(result)); } //TODO: Implement circuit @@ -450,6 +447,82 @@ mod tests { } } + #[test] + fn test_bitor() { + let mut interpreter = generate_interpreter(); + let mut host = generate_host(); + struct TestCase { + op1: U256, + op2: U256, + expected: U256, + } + + let test_cases = vec![ + TestCase { + op1: U256::from(0xf000000000000000u64), + op2: U256::from(0x1000000000000000u64), + expected: U256::from(0xf000000000000000u64), + }, + TestCase { + op1: U256::from(0xffffffffffffffffu128), + op2: U256::from(0x0000000000000000u128), + expected: U256::from(0xffffffffffffffffu128), + }, + TestCase { + op1: U256::from(0x0f0f0f0f0f0f0f0fu128), + op2: U256::from(0xf0f0f0f0f0f0f0f0u128), + expected: U256::from(0xffffffffffffffffu128), + }, + TestCase { + op1: U256::from(0xf000000000000000u64), + op2: U256::from(0x1000000000000000u64), + expected: U256::from(0xf000000000000000u64), + }, + TestCase { + op1: U256::from(0x1200), + op2: U256::from(0xfe00), + expected: U256::from(0xfe00), + }, + TestCase { + op1: U256::from(0x3400), + op2: U256::from(0xdc00), + expected: U256::from(0xdc00), + } + ]; + + for test in test_cases.iter() { + interpreter + .stack + .push(test.op2) + .expect("Failed to push op2 to stack"); + interpreter + .stack + .push(test.op1) + .expect("Failed to push op1 to stack"); + + bitor(&mut interpreter, &mut host); + + let output_indices = interpreter.stack.pop().unwrap(); + + let result: GarbledUint256 = interpreter + .circuit_builder + .compile_and_execute(&output_indices.into()) + .unwrap(); + + let actual = garbled_uint_to_ruint(&result); + println!("Test case:"); + println!("op1: 0x{:x}", test.op1); + println!("op2: 0x{:x}", test.op2); + println!("result: 0x{:x}", actual); + println!("expected: 0x{:x}", test.expected); + println!("---"); + + assert_eq!(garbled_uint_to_ruint(&result), test.expected, + "Failed for op1: 0x{:x}, op2: 0x{:x}\nGot: 0x{:x}\nExpected: 0x{:x}", + test.op1, test.op2, actual, test.expected); + } + } + #[test] fn test_shift_left() { let mut host = DummyHost::new(Env::default()); From 38addc5053ec9eed10cf968c90586bb2046fc9f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Conserva?= Date: Thu, 26 Dec 2024 17:11:10 -0300 Subject: [PATCH 28/44] feat: implement bitwise XOR operation using circuit builder and add unit tests --- .../interpreter/src/instructions/bitwise.rs | 89 +++++++++++++++++-- 1 file changed, 82 insertions(+), 7 deletions(-) diff --git a/crates/interpreter/src/instructions/bitwise.rs b/crates/interpreter/src/instructions/bitwise.rs index 82bc11c825..d31757eef5 100644 --- a/crates/interpreter/src/instructions/bitwise.rs +++ b/crates/interpreter/src/instructions/bitwise.rs @@ -77,16 +77,13 @@ pub fn bitor(interpreter: &mut Interpreter, _host: &mut H) { *op2 = StackValueData::Private(GateIndexVec::from(result)); } -//TODO: Implement circuit pub fn bitxor(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); - pop_top!(interpreter, op1, op2); + pop_top_gates!(interpreter, _op1, op2, garbled_op1, garbled_op2); - let garbled_op1 = ruint_to_garbled_uint(&op1.into()); - let garbled_op2 = ruint_to_garbled_uint(&op2.to_u256()); - let result = garbled_op1 ^ garbled_op2; + let result = interpreter.circuit_builder.xor(&garbled_op1, &garbled_op2); - *op2 = garbled_uint_to_ruint(&result).into(); + *op2 = StackValueData::Private(GateIndexVec::from(result)); } pub fn not(interpreter: &mut Interpreter, _host: &mut H) { @@ -483,7 +480,7 @@ mod tests { op2: U256::from(0xfe00), expected: U256::from(0xfe00), }, - TestCase { + TestCase { // TODO: For this case it is not passing op1: U256::from(0x3400), op2: U256::from(0xdc00), expected: U256::from(0xdc00), @@ -523,6 +520,84 @@ mod tests { } } + #[test] + fn test_bitxor () { + let mut interpreter = generate_interpreter(); + let mut host = generate_host(); + struct TestCase { + op1: U256, + op2: U256, + expected: U256, + } + + let test_cases = vec![ + TestCase { + op1: U256::from(0xf000000000000000u64), + op2: U256::from(0x1000000000000000u64), + expected: U256::from(0xe000000000000000u64), + }, + TestCase { + op1: U256::from(0xffffffffffffffffu128), + op2: U256::from(0x0000000000000000u128), + expected: U256::from(0xffffffffffffffffu128), + }, + TestCase { + op1: U256::from(0x0f0f0f0f0f0f0f0fu128), + op2: U256::from(0xf0f0f0f0f0f0f0f0u128), + expected: U256::from(0xffffffffffffffffu128), + }, + TestCase { + op1: U256::from(0xf000000000000000u64), + op2: U256::from(0x1000000000000000u64), + expected: U256::from(0xe000000000000000u64), + }, + TestCase { + op1: U256::from(0x1200), + op2: U256::from(0xfe00), + expected: U256::from(0xec00), + }, + TestCase { // TODO: For this case it is not passing + op1: U256::from(0x3400), + op2: U256::from(0xdc00), + expected: U256::from(0xe000), + } + ]; + + for test in test_cases.iter() { + interpreter + .stack + .push(test.op2) + .expect("Failed to push op2 to stack"); + interpreter + .stack + .push(test.op1) + .expect("Failed to push op1 to stack"); + + bitxor(&mut interpreter, &mut host); + + let output_indices = interpreter.stack.pop().unwrap(); + + let result: GarbledUint256 = interpreter + .circuit_builder + .compile_and_execute(&output_indices.into()) + .unwrap(); + + + let actual = garbled_uint_to_ruint(&result); + println!("Test case:"); + println!("op1: 0x{:x}", test.op1); + println!("op2: 0x{:x}", test.op2); + println!("result: 0x{:x}", actual); + println!("expected: 0x{:x}", test.expected); + println!("---"); + + assert_eq!(garbled_uint_to_ruint(&result), test.expected, + "Failed for op1: 0x{:x}, op2: 0x{:x}\nGot: 0x{:x}\nExpected: 0x{:x}", + test.op1, test.op2, actual, test.expected); + + } + } + #[test] fn test_shift_left() { let mut host = DummyHost::new(Env::default()); From b2b8b0615fe63308295b60f6d0109166a23e442e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Conserva?= Date: Fri, 27 Dec 2024 11:46:51 -0300 Subject: [PATCH 29/44] fix: update test cases for bitwise operations to correct expected values --- .../interpreter/src/instructions/bitwise.rs | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/crates/interpreter/src/instructions/bitwise.rs b/crates/interpreter/src/instructions/bitwise.rs index d31757eef5..bc9ade4dcf 100644 --- a/crates/interpreter/src/instructions/bitwise.rs +++ b/crates/interpreter/src/instructions/bitwise.rs @@ -476,14 +476,9 @@ mod tests { expected: U256::from(0xf000000000000000u64), }, TestCase { - op1: U256::from(0x1200), - op2: U256::from(0xfe00), - expected: U256::from(0xfe00), - }, - TestCase { // TODO: For this case it is not passing - op1: U256::from(0x3400), - op2: U256::from(0xdc00), - expected: U256::from(0xdc00), + op1: U256::from(0x3400u64), + op2: U256::from(0xdc00u64), + expected: U256::from(0xfc00u64), } ]; @@ -552,14 +547,14 @@ mod tests { expected: U256::from(0xe000000000000000u64), }, TestCase { - op1: U256::from(0x1200), - op2: U256::from(0xfe00), - expected: U256::from(0xec00), + op1: U256::from(0x1200u64), + op2: U256::from(0xfe00u64), + expected: U256::from(0xec00u64), }, - TestCase { // TODO: For this case it is not passing - op1: U256::from(0x3400), - op2: U256::from(0xdc00), - expected: U256::from(0xe000), + TestCase { + op1: U256::from(0x3400u64), + op2: U256::from(0xdc00u64), + expected: U256::from(0xe800u64), } ]; From ed52894272b192a4543d3c969f9533686154af88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Conserva?= Date: Fri, 27 Dec 2024 11:48:46 -0300 Subject: [PATCH 30/44] refactor: remove debug print statements from bitwise operation tests --- crates/interpreter/src/instructions/bitwise.rs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/crates/interpreter/src/instructions/bitwise.rs b/crates/interpreter/src/instructions/bitwise.rs index bc9ade4dcf..64c76a4fea 100644 --- a/crates/interpreter/src/instructions/bitwise.rs +++ b/crates/interpreter/src/instructions/bitwise.rs @@ -502,12 +502,6 @@ mod tests { .unwrap(); let actual = garbled_uint_to_ruint(&result); - println!("Test case:"); - println!("op1: 0x{:x}", test.op1); - println!("op2: 0x{:x}", test.op2); - println!("result: 0x{:x}", actual); - println!("expected: 0x{:x}", test.expected); - println!("---"); assert_eq!(garbled_uint_to_ruint(&result), test.expected, "Failed for op1: 0x{:x}, op2: 0x{:x}\nGot: 0x{:x}\nExpected: 0x{:x}", @@ -579,12 +573,6 @@ mod tests { let actual = garbled_uint_to_ruint(&result); - println!("Test case:"); - println!("op1: 0x{:x}", test.op1); - println!("op2: 0x{:x}", test.op2); - println!("result: 0x{:x}", actual); - println!("expected: 0x{:x}", test.expected); - println!("---"); assert_eq!(garbled_uint_to_ruint(&result), test.expected, "Failed for op1: 0x{:x}, op2: 0x{:x}\nGot: 0x{:x}\nExpected: 0x{:x}", From 8d198fe622cfbfd80bc5b91e41ca55dba562b0c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Conserva?= Date: Mon, 6 Jan 2025 15:07:58 -0300 Subject: [PATCH 31/44] refactor: clean up unused imports and improve code organization in interpreter modules --- Cargo.lock | 150 +++++++++--------- .../src/instructions/arithmetic.rs | 4 +- .../interpreter/src/instructions/bitwise.rs | 3 +- crates/interpreter/src/instructions/macros.rs | 2 +- crates/interpreter/src/interpreter/stack.rs | 2 +- examples/abi_contract_deployment/Cargo.toml | 4 +- 6 files changed, 81 insertions(+), 84 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d8d15609e5..7bb5f21919 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -57,9 +57,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "alloy-chains" -version = "0.1.48" +version = "0.1.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0161082e0edd9013d23083465cc04b20e44b7a15646d36ba7b0cdb7cd6fe18f" +checksum = "d4e0f0136c085132939da6b753452ebed4efaa73fe523bb855b10c199c2ebfaf" dependencies = [ "alloy-primitives", "num_enum", @@ -268,7 +268,7 @@ checksum = "5a833d97bf8a5f0f878daf2c8451fff7de7f9de38baa5a45d936ec718d81255a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] @@ -348,7 +348,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] @@ -364,7 +364,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", "syn-solidity", "tiny-keccak", ] @@ -380,7 +380,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", "syn-solidity", ] @@ -498,9 +498,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7" +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" [[package]] name = "arbitrary" @@ -663,7 +663,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] @@ -674,7 +674,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] @@ -701,7 +701,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] @@ -874,9 +874,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.4" +version = "1.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9157bbaa6b165880c27a4293a474c91cdcf265cc68cc829bf10be0964a391caf" +checksum = "c31a0499c1dc64f458ad13872de75c0eb7e3fdb0e67964610c914b034fc5956e" dependencies = [ "shlex", ] @@ -941,7 +941,7 @@ version = "0.1.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", "tracing", "tracing-subscriber", ] @@ -977,7 +977,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] @@ -1201,7 +1201,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] @@ -1269,7 +1269,7 @@ checksum = "62d671cc41a825ebabc75757b62d3d168c577f9149b2d49ece1dad1f72119d25" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] @@ -1280,7 +1280,7 @@ checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] @@ -1300,7 +1300,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", "unicode-xid", ] @@ -1333,7 +1333,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] @@ -1410,7 +1410,7 @@ checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] @@ -1691,9 +1691,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foldhash" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" [[package]] name = "foreign-types" @@ -1781,7 +1781,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] @@ -2043,9 +2043,9 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.3" +version = "0.27.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" +checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" dependencies = [ "futures-util", "http", @@ -2208,7 +2208,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] @@ -2267,7 +2267,7 @@ checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] @@ -2435,9 +2435,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.168" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "libm" @@ -2666,7 +2666,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] @@ -2677,9 +2677,9 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" [[package]] name = "object" -version = "0.36.5" +version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "memchr", ] @@ -2744,7 +2744,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] @@ -2870,7 +2870,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc" dependencies = [ "memchr", - "thiserror 2.0.7", + "thiserror 2.0.9", "ucd-trie", ] @@ -2904,7 +2904,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] @@ -2933,7 +2933,7 @@ checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] @@ -3073,7 +3073,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] @@ -3113,7 +3113,7 @@ checksum = "4ee1c9ac207483d5e7db4940700de86a9aae46ef90c48b57f99fe7edb8345e49" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] @@ -3135,7 +3135,7 @@ dependencies = [ "rustc-hash", "rustls", "socket2", - "thiserror 2.0.7", + "thiserror 2.0.9", "tokio", "tracing", ] @@ -3155,7 +3155,7 @@ dependencies = [ "rustls-pki-types", "rustls-platform-verifier", "slab", - "thiserror 2.0.7", + "thiserror 2.0.9", "tinyvec", "tracing", "web-time", @@ -3163,9 +3163,9 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52cd4b1eff68bf27940dd39811292c49e007f4d0b4c357358dc9b0197be6b527" +checksum = "1c40286217b4ba3a71d644d752e6a0b71f13f1b6a2c5311acfcbe0c2418ed904" dependencies = [ "cfg_aliases", "libc", @@ -3252,9 +3252,9 @@ dependencies = [ [[package]] name = "rcgen" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54077e1872c46788540de1ea3d7f4ccb1983d12f9aa909b234468676c1a36779" +checksum = "75e669e5202259b5314d1ea5397316ad400819437857b90861765f24c4cf80a2" dependencies = [ "pem", "ring", @@ -3677,7 +3677,7 @@ dependencies = [ "regex", "relative-path", "rustc_version 0.4.1", - "syn 2.0.90", + "syn 2.0.91", "unicode-ident", ] @@ -3903,7 +3903,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] @@ -3970,9 +3970,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.12.1" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa39c7303dc58b5543c94d22c1766b0d31f2ee58306363ea622b10bbc075eaa2" +checksum = "1863fd3768cd83c56a7f60faa4dc0d403f1b6df0a38c3c25f44b7894e45370d5" dependencies = [ "core-foundation-sys", "libc", @@ -4019,14 +4019,14 @@ checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] name = "serde_json" -version = "1.0.133" +version = "1.0.134" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" +checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d" dependencies = [ "indexmap", "itoa", @@ -4235,7 +4235,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] @@ -4276,9 +4276,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.90" +version = "2.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" +checksum = "d53cbcb5a243bd33b7858b1d7f4aca2153490815872d86d955d6ea29f743c035" dependencies = [ "proc-macro2", "quote", @@ -4294,7 +4294,7 @@ dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] @@ -4314,7 +4314,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] @@ -4382,11 +4382,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.7" +version = "2.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93605438cbd668185516ab499d589afb7ee1859ea3d5fc8f6b0755e1c7443767" +checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc" dependencies = [ - "thiserror-impl 2.0.7", + "thiserror-impl 2.0.9", ] [[package]] @@ -4397,18 +4397,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] name = "thiserror-impl" -version = "2.0.7" +version = "2.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d8749b4531af2117677a5fcd12b1348a3fe2b81e36e61ffeac5c4aa3273e36" +checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] @@ -4480,9 +4480,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8" dependencies = [ "tinyvec_macros", ] @@ -4519,7 +4519,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] @@ -4630,7 +4630,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] @@ -4840,7 +4840,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", "wasm-bindgen-shared", ] @@ -4875,7 +4875,7 @@ checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -5117,7 +5117,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", "synstructure", ] @@ -5139,7 +5139,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] @@ -5159,7 +5159,7 @@ checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", "synstructure", ] @@ -5180,7 +5180,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] [[package]] @@ -5202,5 +5202,5 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.91", ] diff --git a/crates/interpreter/src/instructions/arithmetic.rs b/crates/interpreter/src/instructions/arithmetic.rs index fced06bc10..a8fd594129 100644 --- a/crates/interpreter/src/instructions/arithmetic.rs +++ b/crates/interpreter/src/instructions/arithmetic.rs @@ -1,9 +1,7 @@ -use core::ops::{Div, Mul, Rem, Sub}; use super::i256::{i256_div, i256_mod}; use crate::{ gas, - instructions::utility::{garbled_uint_to_ruint, ruint_to_garbled_uint}, interpreter::StackValueData, Host, Interpreter, }; @@ -133,7 +131,7 @@ pub fn signextend(interpreter: &mut Interpreter, _host: &mut H #[cfg(test)] mod tests { use super::*; - use crate::{Contract, DummyHost}; + use crate::{instructions::utility::garbled_uint_to_ruint, Contract, DummyHost}; use compute::uint::GarbledUint256; use primitives::ruint::Uint; diff --git a/crates/interpreter/src/instructions/bitwise.rs b/crates/interpreter/src/instructions/bitwise.rs index 64c76a4fea..9468e16a0c 100644 --- a/crates/interpreter/src/instructions/bitwise.rs +++ b/crates/interpreter/src/instructions/bitwise.rs @@ -1,5 +1,4 @@ -use core::ops::{Div, Mul, Rem, Sub}; -use super::i256::{i256_cmp}; +use super::i256::i256_cmp; use crate::{ gas, instructions::utility::{garbled_uint_to_ruint, ruint_to_garbled_uint}, diff --git a/crates/interpreter/src/instructions/macros.rs b/crates/interpreter/src/instructions/macros.rs index f5b177cd2b..5b62f74c7f 100644 --- a/crates/interpreter/src/instructions/macros.rs +++ b/crates/interpreter/src/instructions/macros.rs @@ -259,7 +259,7 @@ macro_rules! pop_top_gates { return; } // SAFETY: Length is checked above. - let mut $x1 = unsafe { $interp.stack.top_unsafe() }; + let $x1 = unsafe { $interp.stack.top_unsafe() }; let $garbled_x1 = $x1.to_garbled_value(&mut $interp.circuit_builder); }; ($interp:expr, $x1:ident, $x2:ident, $garbled_x1:ident, $garbled_x2:ident) => { diff --git a/crates/interpreter/src/interpreter/stack.rs b/crates/interpreter/src/interpreter/stack.rs index 66dd145522..0ce987eeb0 100644 --- a/crates/interpreter/src/interpreter/stack.rs +++ b/crates/interpreter/src/interpreter/stack.rs @@ -1,8 +1,8 @@ use crate::{instructions::utility::ruint_to_garbled_uint, InstructionResult}; use compute::prelude::{GateIndexVec, WRK17CircuitBuilder}; +use serde::{Deserialize, Serialize}; use core::{fmt, ptr}; use primitives::{FixedBytes, B256, U256}; -use serde::{Deserialize, Serialize}; use std::vec::Vec; /// EVM interpreter stack limit. diff --git a/examples/abi_contract_deployment/Cargo.toml b/examples/abi_contract_deployment/Cargo.toml index 2ac0706fea..8e148c27e1 100644 --- a/examples/abi_contract_deployment/Cargo.toml +++ b/examples/abi_contract_deployment/Cargo.toml @@ -22,9 +22,9 @@ rust_2018_idioms = "deny" all = "warn" [dependencies] -revm = { workspace = true, features = ["std"] } +revm = { workspace = true, features = ["std", "serde"] } database.workspace = true # mics anyhow = "1.0.89" -serde_json = "1.0.133" +serde_json = "1.0.133" \ No newline at end of file From 530aa78731cdd2625e4dfd5c35ed0557f460b505 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Conserva?= Date: Wed, 8 Jan 2025 11:56:26 -0300 Subject: [PATCH 32/44] feat: add example for contract deployment demonstration with bytecode details --- Cargo.lock | 13 +++ Cargo.toml | 3 +- examples/add_example/Cargo.toml | 33 ++++++ examples/add_example/src/main.rs | 167 +++++++++++++++++++++++++++++++ 4 files changed, 215 insertions(+), 1 deletion(-) create mode 100644 examples/add_example/Cargo.toml create mode 100644 examples/add_example/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 7bb5f21919..fa9ea993f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,19 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "add-example" +version = "0.0.0" +dependencies = [ + "anyhow", + "compute", + "revm", + "revm-database", + "revm-interpreter", + "revm-primitives", + "serde_json", +] + [[package]] name = "addchain" version = "0.2.0" diff --git a/Cargo.toml b/Cargo.toml index 2d80f1df12..61d527cb93 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,7 @@ members = [ "examples/database_ref", "examples/uniswap_get_reserves", "examples/uniswap_v2_usdc_swap", + "examples/add_example", #"examples/custom_opcodes", ] @@ -72,4 +73,4 @@ debug = true [profile.ethtests] inherits = "test" -opt-level = 3 +opt-level = 3 \ No newline at end of file diff --git a/examples/add_example/Cargo.toml b/examples/add_example/Cargo.toml new file mode 100644 index 0000000000..019fa2e17a --- /dev/null +++ b/examples/add_example/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "add-example" +version = "0.0.0" +publish = false +authors.workspace = true +edition.workspace = true +keywords.workspace = true +license.workspace = true +repository.workspace = true +readme.workspace = true + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + +[lints.rust] +unreachable_pub = "warn" +unused_must_use = "deny" +rust_2018_idioms = "deny" + +[lints.rustdoc] +all = "warn" + +[dependencies] +revm = { workspace = true, features = ["std", "serde"] } +database.workspace = true +interpreter = { workspace = true } +compute = { workspace = true } +primitives = { workspace = true } + +# mics +anyhow = "1.0.89" +serde_json = "1.0.133" \ No newline at end of file diff --git a/examples/add_example/src/main.rs b/examples/add_example/src/main.rs new file mode 100644 index 0000000000..32711f3e5c --- /dev/null +++ b/examples/add_example/src/main.rs @@ -0,0 +1,167 @@ +//! Contract deployment demonstration + +use database::InMemoryDB; +use revm::{ + primitives::{hex, Bytes, TxKind, U256, Address, B256}, + state::AccountInfo, + wiring::{ + result::{ExecutionResult, Output}, + EthereumWiring, + }, + Evm, +}; + +fn print_bytecode_details(bytecode: &Bytes) { + println!("Bytecode Details:"); + println!(" Total Length: {}", bytecode.len()); + println!(" Hex Representation: {}", hex::encode(bytecode)); + + println!(" Bytecode Breakdown:"); + for (i, &byte) in bytecode.iter().enumerate() { + println!(" Byte {}: 0x{:02x} (Decimal: {})", i, byte, byte); + } +} + +fn main() -> anyhow::Result<()> { + let bytecode = hex::decode("6080604052348015600e575f80fd5b506101a58061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610029575f3560e01c8063771602f71461002d575b5f80fd5b610047600480360381019061004291906100a9565b61005d565b60405161005491906100f6565b60405180910390f35b5f818361006a919061013c565b905092915050565b5f80fd5b5f819050919050565b61008881610076565b8114610092575f80fd5b50565b5f813590506100a38161007f565b92915050565b5f80604083850312156100bf576100be610072565b5b5f6100cc85828601610095565b92505060206100dd85828601610095565b9150509250929050565b6100f081610076565b82525050565b5f6020820190506101095f8301846100e7565b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f61014682610076565b915061015183610076565b92508282019050808211156101695761016861010f565b5b9291505056fea2646970667358221220650b77675b6648c0fe06e068764320e465568458bf8e54f349acb7837a95b54d64736f6c634300081a0033")?; + // Concatenate init code and runtime code + let bytecode: Bytes = Bytes::from(bytecode.clone()); + + print_bytecode_details(&bytecode); + + // Sender configuration + let sender = Address::from_slice(&[0x20; 20]); + + // Transaction parameters + let gas_limit = 100_000u64; + let gas_price = U256::from(100u64); + let value = U256::ZERO; + let initial_balance = U256::from(1_000_000_000_000u64); + + // Create in-memory database + let mut db = InMemoryDB::default(); + + // Insert sender account + db.insert_account_info( + sender, + AccountInfo { + balance: initial_balance, + code_hash: B256::default(), + code: None, + nonce: 0, + }, + ); + + // Create EVM instance for deployment + let mut evm: Evm<'_, EthereumWiring> = + Evm::>::builder() + .with_db(db) + .with_default_ext_ctx() + .modify_tx_env(|tx| { + tx.transact_to = TxKind::Create; + tx.data = bytecode.clone(); + tx.gas_limit = gas_limit; + tx.gas_price = gas_price; + tx.value = value; + tx.caller = sender; + tx.nonce = 0; + }) + .modify_env(|env| { + env.block.basefee = U256::ZERO; + env.block.gas_limit = U256::from(gas_limit); + env.block.number = U256::ZERO; + env.block.timestamp = U256::ZERO; + }) + .build(); + + println!("\n--- Contract Deployment Attempt ---"); + let deploy_tx = evm.transact_commit()?; + println!("Deployment Transaction Result:"); + println!("{:#?}", deploy_tx); + + match deploy_tx { + ExecutionResult::Success { + reason, + gas_used, + gas_refunded, + logs, + output + } => { + println!("Deployment Successful:"); + println!(" Reason: {:?}", reason); + println!(" Gas Used: {}", gas_used); + println!(" Gas Refunded: {}", gas_refunded); + println!(" Logs: {:?}", logs); + + match output { + Output::Create(runtime_bytecode, address) => { + println!(" Runtime Bytecode: {}", hex::encode(&runtime_bytecode)); + println!(" Deployed Address: {:?}", address); + + let mut evm_call = Evm::>::builder() + .with_db(evm.db().clone()) + .with_default_ext_ctx() + .modify_tx_env(|tx| { + tx.transact_to = TxKind::Call(address.unwrap()); + tx.data = Bytes::default(); + tx.gas_limit = gas_limit; + tx.gas_price = gas_price; + tx.value = U256::ZERO; + tx.caller = sender; + tx.nonce = 1; + }) + .modify_env(|env| { + env.block.basefee = U256::ZERO; + env.block.gas_limit = U256::from(gas_limit); + env.block.number = U256::ZERO; + env.block.timestamp = U256::ZERO; + }) + .build(); + + match evm_call.transact_commit() { + Ok(call_tx) => { + println!("\n--- Contract Call Result ---"); + println!("Full Call Transaction Result: {:#?}", call_tx); + + match call_tx { + ExecutionResult::Success { + output: Output::Call(value), + .. + } => { + println!("Call Successful:"); + println!(" Private computation completed."); + println!(" This value represents a private result (14 + 20 = 34)"); + println!(" Result in garbled form: {}", hex::encode(&value)); + }, + ExecutionResult::Halt { reason, gas_used } => { + println!("Call Halted:"); + println!(" Reason: {:?}", reason); + println!(" Gas Used: {}", gas_used); + }, + ExecutionResult::Revert { gas_used, output } => { + println!("Call Reverted:"); + println!(" Gas Used: {}", gas_used); + println!(" Output: {}", hex::encode(&output)); + }, + _ => println!("Unexpected call result type"), + } + }, + Err(e) => { + println!("Contract Call Error:"); + println!("{:?}", e); + } + } + } + _ => println!(" Unexpected output type"), + } + }, + ExecutionResult::Revert { gas_used, output } => { + println!("Deployment Reverted:"); + println!(" Gas Used: {}", gas_used); + println!(" Output: {}", hex::encode(&output)); + }, + _ => println!("Unexpected deployment result"), + } + + Ok(()) +} \ No newline at end of file From 302751626aac1e11de750479569b91b8d2b3916a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Conserva?= Date: Wed, 8 Jan 2025 11:56:34 -0300 Subject: [PATCH 33/44] feat: implement iszero function for bitwise operations with handling for public and private values --- crates/interpreter/src/instructions/bitwise.rs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/crates/interpreter/src/instructions/bitwise.rs b/crates/interpreter/src/instructions/bitwise.rs index 9468e16a0c..d1b14ccaa5 100644 --- a/crates/interpreter/src/instructions/bitwise.rs +++ b/crates/interpreter/src/instructions/bitwise.rs @@ -51,13 +51,25 @@ pub fn eq(interpreter: &mut Interpreter, _host: &mut H) { *op2 = StackValueData::Private(GateIndexVec::from(result)); } -//TODO: Remove this function and use eq zero instead +// TODO: Is missing tests pub fn iszero(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); pop_top!(interpreter, op1); - *op1 = U256::from(op1.to_u256().is_zero()).into(); + + let result = match op1 { + StackValueData::Public(value) => StackValueData::Public(U256::from(value.is_zero())), + StackValueData::Private(garbled) => { + let zero_bits = vec![false; 256]; + let zero = compute::uint::GarbledUint::<256>::new(zero_bits); + let zero_gates = interpreter.circuit_builder.input(&zero); + + let result = interpreter.circuit_builder.eq(&garbled, &zero_gates); + StackValueData::Private(GateIndexVec::from(result)) + } + }; + + *op1 = result; } - pub fn bitand(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); pop_top_gates!(interpreter, _op1, op2, garbled_op1, garbled_op2); From 94fdf07a32487063219cdb05c703be90c24944d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Conserva?= Date: Wed, 8 Jan 2025 11:56:41 -0300 Subject: [PATCH 34/44] feat: enhance jumpi function to execute circuit for conditional jumps --- crates/interpreter/src/instructions/control.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/crates/interpreter/src/instructions/control.rs b/crates/interpreter/src/instructions/control.rs index 592558e6b1..3b082e44bc 100644 --- a/crates/interpreter/src/instructions/control.rs +++ b/crates/interpreter/src/instructions/control.rs @@ -60,9 +60,14 @@ pub fn jump(interpreter: &mut Interpreter, _host: &mut H) { pub fn jumpi(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::HIGH); pop!(interpreter, target, cond); - if !cond.to_u256().is_zero() { - jump_inner(interpreter, target.into()); - } + + let cond_garbled = cond.to_garbled_value(&mut interpreter.circuit_builder); + let output = interpreter.circuit_builder.compile_and_execute::<256>(&cond_garbled) + .expect("Failed to execute circuit"); + + if !output.bits[0] { + jump_inner(interpreter, target.into()); + } } #[inline] From 0e6179b20a76b2c22d14ca2a8aade5744a504eb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Conserva?= Date: Wed, 8 Jan 2025 15:36:31 -0300 Subject: [PATCH 35/44] feat: enhance iszero function to support garbled values and add comprehensive unit tests --- .../interpreter/src/instructions/bitwise.rs | 83 ++++++++++++++++--- 1 file changed, 73 insertions(+), 10 deletions(-) diff --git a/crates/interpreter/src/instructions/bitwise.rs b/crates/interpreter/src/instructions/bitwise.rs index d1b14ccaa5..36b0351a85 100644 --- a/crates/interpreter/src/instructions/bitwise.rs +++ b/crates/interpreter/src/instructions/bitwise.rs @@ -51,25 +51,29 @@ pub fn eq(interpreter: &mut Interpreter, _host: &mut H) { *op2 = StackValueData::Private(GateIndexVec::from(result)); } -// TODO: Is missing tests pub fn iszero(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); pop_top!(interpreter, op1); - + + let zero_bits = vec![false; 256]; + let zero = compute::uint::GarbledUint::<256>::new(zero_bits); + let zero_gates = interpreter.circuit_builder.input(&zero); + let result = match op1 { - StackValueData::Public(value) => StackValueData::Public(U256::from(value.is_zero())), + StackValueData::Public(value) => { + let garbled_gates = StackValueData::Public(*value).to_garbled_value(&mut interpreter.circuit_builder); + let eq_result = interpreter.circuit_builder.eq(&garbled_gates, &zero_gates); + StackValueData::Private(GateIndexVec::from(eq_result)) + } StackValueData::Private(garbled) => { - let zero_bits = vec![false; 256]; - let zero = compute::uint::GarbledUint::<256>::new(zero_bits); - let zero_gates = interpreter.circuit_builder.input(&zero); - - let result = interpreter.circuit_builder.eq(&garbled, &zero_gates); - StackValueData::Private(GateIndexVec::from(result)) + let eq_result = interpreter.circuit_builder.eq(&garbled, &zero_gates); + StackValueData::Private(GateIndexVec::from(eq_result)) } }; - + *op1 = result; } + pub fn bitand(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); pop_top_gates!(interpreter, _op1, op2, garbled_op1, garbled_op2); @@ -361,6 +365,65 @@ mod tests { } } + #[test] + fn test_iszero() { + let mut interpreter = generate_interpreter(); + let mut host = generate_host(); + + struct TestCase { + value: U256, + expected: bool + } + + let test_cases = vec![ + TestCase { + value: U256::ZERO, + expected: true + }, + TestCase { + value: U256::from(1u64), + expected: false + }, + TestCase { + value: U256::from(0xffffffffffffffffu64), + expected: false + }, + TestCase { + value: U256::from(0x8000000000000000u64), + expected: false + } + ]; + + for test in test_cases.iter() { + interpreter + .stack + .push(test.value) + .expect("Failed to push value to stack"); + + println!("Value: {:?}", test.value); + + iszero(&mut interpreter, &mut host); + + let output_indices = interpreter.stack.pop().unwrap(); + + println!("Output indices: {:?}", output_indices); + + let result: GarbledUint256 = interpreter + .circuit_builder + .compile_and_execute(&output_indices.into()) + .unwrap(); + + println!("Result: {:?}", garbled_uint_to_bool(&result)); + + assert_eq!( + garbled_uint_to_bool(&result), + test.expected, + "Failed for value: {:?}", + test.value + ); + } + } + #[test] fn test_not() { let mut interpreter = generate_interpreter(); From f4dfa042a63d70163e8441494772674c245bf500 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Conserva?= Date: Wed, 8 Jan 2025 16:16:42 -0300 Subject: [PATCH 36/44] feat: add direct bytecode example for addition and simplify execution flow in contract deployment --- examples/add_example/src/main.rs | 106 ++++--------------------------- 1 file changed, 13 insertions(+), 93 deletions(-) diff --git a/examples/add_example/src/main.rs b/examples/add_example/src/main.rs index 32711f3e5c..59c42527bb 100644 --- a/examples/add_example/src/main.rs +++ b/examples/add_example/src/main.rs @@ -11,6 +11,13 @@ use revm::{ Evm, }; +// Direct bytecode that adds 14 + 20 +const BYTECODE: &[u8] = &[ + 0x60, 0x14, // PUSH1 0x14 (20 decimal) + 0x60, 0x0E, // PUSH1 0x0E (14 decimal) + 0x01, // ADD (add the two values on top of the stack) +]; + fn print_bytecode_details(bytecode: &Bytes) { println!("Bytecode Details:"); println!(" Total Length: {}", bytecode.len()); @@ -23,10 +30,7 @@ fn print_bytecode_details(bytecode: &Bytes) { } fn main() -> anyhow::Result<()> { - let bytecode = hex::decode("6080604052348015600e575f80fd5b506101a58061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610029575f3560e01c8063771602f71461002d575b5f80fd5b610047600480360381019061004291906100a9565b61005d565b60405161005491906100f6565b60405180910390f35b5f818361006a919061013c565b905092915050565b5f80fd5b5f819050919050565b61008881610076565b8114610092575f80fd5b50565b5f813590506100a38161007f565b92915050565b5f80604083850312156100bf576100be610072565b5b5f6100cc85828601610095565b92505060206100dd85828601610095565b9150509250929050565b6100f081610076565b82525050565b5f6020820190506101095f8301846100e7565b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f61014682610076565b915061015183610076565b92508282019050808211156101695761016861010f565b5b9291505056fea2646970667358221220650b77675b6648c0fe06e068764320e465568458bf8e54f349acb7837a95b54d64736f6c634300081a0033")?; - // Concatenate init code and runtime code - let bytecode: Bytes = Bytes::from(bytecode.clone()); - + let bytecode: Bytes = BYTECODE.to_vec().into(); print_bytecode_details(&bytecode); // Sender configuration @@ -52,7 +56,7 @@ fn main() -> anyhow::Result<()> { }, ); - // Create EVM instance for deployment + // Create EVM instance let mut evm: Evm<'_, EthereumWiring> = Evm::>::builder() .with_db(db) @@ -74,94 +78,10 @@ fn main() -> anyhow::Result<()> { }) .build(); - println!("\n--- Contract Deployment Attempt ---"); - let deploy_tx = evm.transact_commit()?; - println!("Deployment Transaction Result:"); - println!("{:#?}", deploy_tx); - - match deploy_tx { - ExecutionResult::Success { - reason, - gas_used, - gas_refunded, - logs, - output - } => { - println!("Deployment Successful:"); - println!(" Reason: {:?}", reason); - println!(" Gas Used: {}", gas_used); - println!(" Gas Refunded: {}", gas_refunded); - println!(" Logs: {:?}", logs); - - match output { - Output::Create(runtime_bytecode, address) => { - println!(" Runtime Bytecode: {}", hex::encode(&runtime_bytecode)); - println!(" Deployed Address: {:?}", address); - - let mut evm_call = Evm::>::builder() - .with_db(evm.db().clone()) - .with_default_ext_ctx() - .modify_tx_env(|tx| { - tx.transact_to = TxKind::Call(address.unwrap()); - tx.data = Bytes::default(); - tx.gas_limit = gas_limit; - tx.gas_price = gas_price; - tx.value = U256::ZERO; - tx.caller = sender; - tx.nonce = 1; - }) - .modify_env(|env| { - env.block.basefee = U256::ZERO; - env.block.gas_limit = U256::from(gas_limit); - env.block.number = U256::ZERO; - env.block.timestamp = U256::ZERO; - }) - .build(); - - match evm_call.transact_commit() { - Ok(call_tx) => { - println!("\n--- Contract Call Result ---"); - println!("Full Call Transaction Result: {:#?}", call_tx); - - match call_tx { - ExecutionResult::Success { - output: Output::Call(value), - .. - } => { - println!("Call Successful:"); - println!(" Private computation completed."); - println!(" This value represents a private result (14 + 20 = 34)"); - println!(" Result in garbled form: {}", hex::encode(&value)); - }, - ExecutionResult::Halt { reason, gas_used } => { - println!("Call Halted:"); - println!(" Reason: {:?}", reason); - println!(" Gas Used: {}", gas_used); - }, - ExecutionResult::Revert { gas_used, output } => { - println!("Call Reverted:"); - println!(" Gas Used: {}", gas_used); - println!(" Output: {}", hex::encode(&output)); - }, - _ => println!("Unexpected call result type"), - } - }, - Err(e) => { - println!("Contract Call Error:"); - println!("{:?}", e); - } - } - } - _ => println!(" Unexpected output type"), - } - }, - ExecutionResult::Revert { gas_used, output } => { - println!("Deployment Reverted:"); - println!(" Gas Used: {}", gas_used); - println!(" Output: {}", hex::encode(&output)); - }, - _ => println!("Unexpected deployment result"), - } + println!("\n--- Execution Attempt ---"); + let result = evm.transact_commit()?; + println!("Execution Result:"); + println!("{:#?}", result); Ok(()) } \ No newline at end of file From 96f2f0e012129edbfc58bba6c24c165acbb80670 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Conserva?= Date: Wed, 8 Jan 2025 16:35:48 -0300 Subject: [PATCH 37/44] feat: enhance contract execution demonstration with comprehensive logging and private computation verification --- examples/add_example/src/main.rs | 148 ++++++++++++++++++++++++++++--- 1 file changed, 135 insertions(+), 13 deletions(-) diff --git a/examples/add_example/src/main.rs b/examples/add_example/src/main.rs index 59c42527bb..eaf8e24fdb 100644 --- a/examples/add_example/src/main.rs +++ b/examples/add_example/src/main.rs @@ -1,18 +1,30 @@ -//! Contract deployment demonstration +//! Contract execution and private computation demonstration with comprehensive logging use database::InMemoryDB; use revm::{ - primitives::{hex, Bytes, TxKind, U256, Address, B256}, + primitives::{hex, Bytes, TxKind, U256, Address, B256, keccak256}, state::AccountInfo, wiring::{ result::{ExecutionResult, Output}, EthereumWiring, }, Evm, + bytecode::Bytecode, }; +use compute::prelude::GarbledUint256; +use interpreter::{ + instructions::utility::garbled_uint_to_ruint, + interpreter::{Interpreter, StackValueData}, + table::make_instruction_table, + Contract, + DummyHost, + SharedMemory +}; +use revm::specification::hardfork::CancunSpec; +use revm::wiring::DefaultEthereumWiring; -// Direct bytecode that adds 14 + 20 -const BYTECODE: &[u8] = &[ +// Runtime bytecode that adds 14 + 20 +const RUNTIME_CODE: &[u8] = &[ 0x60, 0x14, // PUSH1 0x14 (20 decimal) 0x60, 0x0E, // PUSH1 0x0E (14 decimal) 0x01, // ADD (add the two values on top of the stack) @@ -30,11 +42,12 @@ fn print_bytecode_details(bytecode: &Bytes) { } fn main() -> anyhow::Result<()> { - let bytecode: Bytes = BYTECODE.to_vec().into(); - print_bytecode_details(&bytecode); + let bytecode = Bytecode::new_raw(Bytes::from(RUNTIME_CODE.to_vec())); + print_bytecode_details(&bytecode.bytes()); - // Sender configuration + // Sender and contract configuration let sender = Address::from_slice(&[0x20; 20]); + let contract_address = Address::from_slice(&[0x42; 20]); // Fixed contract address // Transaction parameters let gas_limit = 100_000u64; @@ -56,14 +69,25 @@ fn main() -> anyhow::Result<()> { }, ); + // Insert contract with runtime code + db.insert_account_info( + contract_address, + AccountInfo { + balance: U256::ZERO, + code_hash: B256::from(keccak256(bytecode.bytes())), + code: Some(bytecode.clone()), + nonce: 1, + }, + ); + // Create EVM instance let mut evm: Evm<'_, EthereumWiring> = Evm::>::builder() .with_db(db) .with_default_ext_ctx() .modify_tx_env(|tx| { - tx.transact_to = TxKind::Create; - tx.data = bytecode.clone(); + tx.transact_to = TxKind::Call(contract_address); // Call the contract + tx.data = bytecode.bytes().clone(); // Bytecode as call data tx.gas_limit = gas_limit; tx.gas_price = gas_price; tx.value = value; @@ -78,10 +102,108 @@ fn main() -> anyhow::Result<()> { }) .build(); - println!("\n--- Execution Attempt ---"); + println!("\n--- EVM Execution Attempt ---"); let result = evm.transact_commit()?; - println!("Execution Result:"); - println!("{:#?}", result); + + // Comprehensive EVM Execution Logging + println!("EVM Execution Result:"); + println!(" Status: {:#?}", result); + + // Check EVM Execution Success + match result { + ExecutionResult::Success { reason, gas_used, output, .. } => { + println!(" Execution Reason: {:?}", reason); + println!(" Gas Used: {}", gas_used); + + // Verify output or additional checks if needed + match output { + Output::Call(data) => { + println!(" Call Output: {:?}", data); + }, + Output::Create(address, _) => { + println!(" Created Contract Address: {:?}", address); + } + } + }, + ExecutionResult::Revert { gas_used, output, .. } => { + println!(" Execution Reverted"); + println!(" Gas Used: {}", gas_used); + println!(" Revert Output: {:?}", output); + return Err(anyhow::anyhow!("EVM Execution Reverted")); + }, + ExecutionResult::Halt { reason, gas_used } => { + println!(" Execution Halted"); + println!(" Reason: {:?}", reason); + println!(" Gas Used: {}", gas_used); + return Err(anyhow::anyhow!("EVM Execution Halted")); + } + } + + // Private Computation Circuit Verification + let contract = Contract::new( + Bytes::new(), + bytecode.clone(), + None, + Address::default(), + None, + Address::default(), + U256::ZERO, + ); + + // Create interpreter + let mut interpreter = Interpreter::new(contract, u64::MAX, false); + + // Create host and instruction table + let mut host = DummyHost::::default(); + let table = &make_instruction_table::, CancunSpec>(); + + // Execute bytecode + let _action = interpreter.run( + SharedMemory::new(), + table, + &mut host, + ); + + // Verify and convert private result to public + println!("\n--- Private Computation Verification ---"); + match interpreter.stack().peek(0) { + Ok(value) => { + println!(" Top of Stack Value: {:?}", value); + + if let StackValueData::Private(gate_indices) = value { + println!(" Detected Private Value"); + println!(" Gate Indices: {:?}", gate_indices); + + let result: GarbledUint256 = interpreter.circuit_builder.compile_and_execute(&gate_indices) + .map_err(|e| { + println!(" Circuit Compilation Error: {:?}", e); + e + })?; + + let public_result = garbled_uint_to_ruint(&result); + + println!(" Private Computation Result: {:?}", public_result); + + // Verification against expected result + let expected_result = 20 + 14; + println!(" Expected Result: {}", expected_result); + + assert_eq!( + public_result.to_string(), + expected_result.to_string(), + "Private computation result does not match expected value" + ); + + println!(" ✅ Private Computation Verification Successful"); + } else { + println!(" Value is already public: {:?}", value); + } + }, + Err(e) => { + println!(" Error accessing stack: {:?}", e); + return Err(anyhow::anyhow!("Failed to access interpreter stack")); + } + } Ok(()) -} \ No newline at end of file +} From 50468b48c8f99f1085070fc64d5fe9e0ac274507 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Conserva?= Date: Thu, 9 Jan 2025 09:11:21 -0300 Subject: [PATCH 38/44] feat: enable serde support in interpreter package --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 61d527cb93..9ce169a7f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,7 +46,7 @@ specification = { path = "crates/specification", package = "revm-specification", state = { path = "crates/state", package = "revm-state", version = "1.0.0", default-features = false } wiring = { path = "crates/wiring", package = "revm-wiring", version = "1.0.0", default-features = false } transaction = { path = "crates/wiring/transaction", package = "revm-transaction", version = "1.0.0", default-features = false } -interpreter = { path = "crates/interpreter", package = "revm-interpreter", version = "10.0.1", default-features = false } +interpreter = { path = "crates/interpreter", package = "revm-interpreter", version = "10.0.1", features = ["serde"] } inspector = { path = "crates/inspector", package = "revm-inspector", version = "1.0.0", default-features = false } precompile = { path = "crates/precompile", package = "revm-precompile", version = "11.0.1", default-features = false } statetest-types = { path = "crates/statetest-types", package = "revm-statetest-types", version = "1.0.0", default-features = false } From 6469e13865c4ae55da281b2b93049e3677ed9100 Mon Sep 17 00:00:00 2001 From: rafagomes Date: Thu, 9 Jan 2025 10:12:25 -0300 Subject: [PATCH 39/44] feat: add README documentation for revm project with setup instructions and usage examples --- README.md | 130 +++++++-------------------------- documentation/src/README.md | 140 ++++++++++++++++++++++++++++++++++++ 2 files changed, 165 insertions(+), 105 deletions(-) create mode 100644 documentation/src/README.md diff --git a/README.md b/README.md index 87ed61e419..3333e01601 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,4 @@ -# revm - -[![CI](https://github.com/bluealloy/revm/actions/workflows/ci.yml/badge.svg)][gh-ci] -[![License](https://img.shields.io/badge/License-MIT-orange.svg)][mit-license] -[![Chat][tg-badge]][tg-url] - -[mit-license]: https://opensource.org/license/mit/ -[gh-ci]: https://github.com/bluealloy/revm/actions/workflows/ci.yml -[tg-url]: https://t.me/+Ig4WDWOzikA3MzA0 -[tg-badge]: https://img.shields.io/badge/chat-telegram-blue +# REVM Gateway **Rust Ethereum Virtual Machine** @@ -15,126 +6,55 @@ Revm is an EVM written in Rust that is focused on **speed** and **simplicity**. It has a fast and flexible implementation with a simple interface and embedded Host. -It passes all `ethereum/tests` test suites. Here is a list of guiding principles that Revm follows. -* **EVM compatibility and stability** - this goes without saying but it is nice to put it here. In the blockchain industry, stability is the most desired attribute of any system. -* **Speed** - is one of the most important things and most decisions are made to complement this. -* **Simplicity** - simplification of internals so that it can be easily understood and extended, and interface that can be easily used or integrated into other projects. -* **interfacing** - `[no_std]` so that it can be used as wasm lib and integrate with JavaScript and cpp binding if needed. +- **EVM compatibility and stability** - this goes without saying but it is nice to put it here. In the blockchain industry, stability is the most desired attribute of any system. +- **Speed** - is one of the most important things and most decisions are made to complement this. +- **Simplicity** - simplification of internals so that it can be easily understood and extended, and interface that can be easily used or integrated into other projects. +- **interfacing** - `[no_std]` so that it can be used as wasm lib and integrate with JavaScript and cpp binding if needed. -# Project +## Project Structure: -* crates - * revm -> main EVM library. - * revm-primitives -> Primitive data types. - * revm-interpreter -> Execution loop with instructions - * revm-precompile -> EVM precompiles -* bins: - * revme: cli binary, used for running state test jsons +- crates + - revm -> main EVM library. + - revm-primitives -> Primitive data types. + - revm-interpreter -> Execution loop with instructions + - revm-precompile -> EVM precompiles +- bins: + - revme: cli binary, used for running state test jsons This project tends to use the newest rust version, so if you're encountering a build error try running `rustup update` first. -There were some big efforts on optimization of revm: - -* Optimizing interpreter loop: https://github.com/bluealloy/revm/issues/7 -* Introducing Bytecode format (and better bytecode analysis): https://github.com/bluealloy/revm/issues/121 -* Unification of instruction signatures: https://github.com/bluealloy/revm/pull/283 - -# Building from source - -```shell -git clone https://github.com/bluealloy/revm.git -cd revm -cargo build --release -``` - -**_Note:_** `clang` is required for building revm with `c-kzg` or `secp256k1` feature flags as they depend on `C` libraries. If you don't have it installed, you can install it with `apt install clang`. - -# Running eth tests - -go to `cd bins/revme/` - -Download eth tests from (this will take some time): `git clone https://github.com/ethereum/tests` - -run tests with command: `cargo run --release -- statetest tests/GeneralStateTests/ tests/LegacyTests/Constantinople/GeneralStateTests` - -`GeneralStateTests` contains all tests related to EVM. - -## Running benchmarks - -Benches can be found in [`crates/revm/benches`](./crates/revm/benches). +## Running the project -Currently, available benches include the following. -- *analysis* -- *snailtracer* -- *transfer* +### Dependencies -To run the `snailtracer` bench, execute the `cargo bench` subcommand below. +-[Gateway Circuit SDK](https://github.com/GatewayLabs/circuit-sdk) +In in the same work directory as you cloned this repository, you have to clone the `Gateway Circuit SDK`. ```shell -cargo bench --package revm --profile release -- snailtracer +git clone git@github.com:GatewayLabs/circuit-sdk.git ``` -Using [flamegraph][flamegraph], you can create a visualization breaking down the runtime of various -sections of the bench execution - a flame graph. Executing the `cargo flamegraph` subcommand requires -installing [flamegraph][flamegraph] by running `cargo install flamegraph`. +### Building the project ```shell -cargo flamegraph --root --freq 4000 --min-width 0.001 --package revm --bench bench -- snailtracer +cargo build --release ``` -This command will produce a flamegraph image output to `flamegraph.svg`. -Flamegraph also requires sudo mode to run (hence the `--root` cli arg) and will prompt you for your password if not in sudo mode already. - -[flamegraph]: https://docs.rs/crate/flamegraph/0.1.6 - -## Running examples +### Running an example ```shell -cargo run -p revm --features ethersdb --example fork_ref_transact +cd examples/add_example ``` -Generate block traces and write them to json files in a new `traces/` directory. -Each file corresponds to a transaction in the block and is named as such: `.json`. - ```shell -cargo run -p revm --features std,serde-json,ethersdb --example generate_block_traces +cargo run ``` -# Used by: - -* [Foundry](https://github.com/foundry-rs/foundry) is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust. -* [Helios](https://github.com/a16z/helios) is a fully trustless, efficient, and portable Ethereum light client written in Rust. -* [Reth](https://github.com/paradigmxyz/reth) Modular, contributor-friendly and blazing-fast implementation of the Ethereum protocol -* [Arbiter](https://github.com/primitivefinance/arbiter) is a framework for stateful Ethereum smart-contract simulation -* [Zeth](https://github.com/risc0/zeth) is an open-source ZK block prover for Ethereum built on the RISC Zero zkVM. -* [VERBS](https://github.com/simtopia/verbs) an open-source Ethereum agent-based modelling and simulation library with a Python API. -* [Hardhat](https://github.com/NomicFoundation/hardhat) is a development environment to compile, deploy, test, and debug your Ethereum software. -* [Trin](https://github.com/ethereum/trin) is Portal Network client. An execution and consensus layer Ethereum light client written in Rust. Portal Network client's provide complete, provable, and distributed execution archival access. -* [Simular](https://github.com/simular-fi/simular/) is a Python smart-contract API with a fast, embedded, Ethereum Virtual Machine. -* [rbuilder](https://github.com/flashbots/rbuilder) is a state of the art Ethereum MEV-Boost block builder written in Rust and designed to work with Reth. -* ... - -(If you want to add project to the list, ping me or open the PR) - -# Documentation - -The book can be found at github page here: https://bluealloy.github.io/revm/ - -The documentation (alas needs some love) can be found here: https://bluealloy.github.io/revm/docs/ - -To serve the mdbook documentation in a local environment, ensure you have mdbook installed (if not install it with cargo) and then run: - -```shell -mdbook serve documentation -``` - -# Contact - -There is public telegram group: https://t.me/+Ig4WDWOzikA3MzA0 +## Important considerations -Or if you want to contact me directly, here is my email: dragan0rakita@gmail.com and telegram: https://t.me/draganrakita +This is a very volatile project, and it is under heavy development. Expect constant changes in the code and the documentation. diff --git a/documentation/src/README.md b/documentation/src/README.md new file mode 100644 index 0000000000..87ed61e419 --- /dev/null +++ b/documentation/src/README.md @@ -0,0 +1,140 @@ +# revm + +[![CI](https://github.com/bluealloy/revm/actions/workflows/ci.yml/badge.svg)][gh-ci] +[![License](https://img.shields.io/badge/License-MIT-orange.svg)][mit-license] +[![Chat][tg-badge]][tg-url] + +[mit-license]: https://opensource.org/license/mit/ +[gh-ci]: https://github.com/bluealloy/revm/actions/workflows/ci.yml +[tg-url]: https://t.me/+Ig4WDWOzikA3MzA0 +[tg-badge]: https://img.shields.io/badge/chat-telegram-blue + +**Rust Ethereum Virtual Machine** + +![](./assets/logo/revm-banner.png) + +Revm is an EVM written in Rust that is focused on **speed** and **simplicity**. +It has a fast and flexible implementation with a simple interface and embedded Host. +It passes all `ethereum/tests` test suites. + +Here is a list of guiding principles that Revm follows. + +* **EVM compatibility and stability** - this goes without saying but it is nice to put it here. In the blockchain industry, stability is the most desired attribute of any system. +* **Speed** - is one of the most important things and most decisions are made to complement this. +* **Simplicity** - simplification of internals so that it can be easily understood and extended, and interface that can be easily used or integrated into other projects. +* **interfacing** - `[no_std]` so that it can be used as wasm lib and integrate with JavaScript and cpp binding if needed. + +# Project + +Structure: + +* crates + * revm -> main EVM library. + * revm-primitives -> Primitive data types. + * revm-interpreter -> Execution loop with instructions + * revm-precompile -> EVM precompiles +* bins: + * revme: cli binary, used for running state test jsons + +This project tends to use the newest rust version, so if you're encountering a build error try running `rustup update` first. + +There were some big efforts on optimization of revm: + +* Optimizing interpreter loop: https://github.com/bluealloy/revm/issues/7 +* Introducing Bytecode format (and better bytecode analysis): https://github.com/bluealloy/revm/issues/121 +* Unification of instruction signatures: https://github.com/bluealloy/revm/pull/283 + +# Building from source + +```shell +git clone https://github.com/bluealloy/revm.git +cd revm +cargo build --release +``` + +**_Note:_** `clang` is required for building revm with `c-kzg` or `secp256k1` feature flags as they depend on `C` libraries. If you don't have it installed, you can install it with `apt install clang`. + +# Running eth tests + +go to `cd bins/revme/` + +Download eth tests from (this will take some time): `git clone https://github.com/ethereum/tests` + +run tests with command: `cargo run --release -- statetest tests/GeneralStateTests/ tests/LegacyTests/Constantinople/GeneralStateTests` + +`GeneralStateTests` contains all tests related to EVM. + +## Running benchmarks + +Benches can be found in [`crates/revm/benches`](./crates/revm/benches). + +Currently, available benches include the following. +- *analysis* +- *snailtracer* +- *transfer* + +To run the `snailtracer` bench, execute the `cargo bench` subcommand below. + +```shell +cargo bench --package revm --profile release -- snailtracer +``` + +Using [flamegraph][flamegraph], you can create a visualization breaking down the runtime of various +sections of the bench execution - a flame graph. Executing the `cargo flamegraph` subcommand requires +installing [flamegraph][flamegraph] by running `cargo install flamegraph`. + +```shell +cargo flamegraph --root --freq 4000 --min-width 0.001 --package revm --bench bench -- snailtracer +``` + +This command will produce a flamegraph image output to `flamegraph.svg`. +Flamegraph also requires sudo mode to run (hence the `--root` cli arg) and will prompt you for your password if not in sudo mode already. + +[flamegraph]: https://docs.rs/crate/flamegraph/0.1.6 + +## Running examples + +```shell +cargo run -p revm --features ethersdb --example fork_ref_transact +``` + +Generate block traces and write them to json files in a new `traces/` directory. +Each file corresponds to a transaction in the block and is named as such: `.json`. + +```shell +cargo run -p revm --features std,serde-json,ethersdb --example generate_block_traces +``` + +# Used by: + +* [Foundry](https://github.com/foundry-rs/foundry) is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust. +* [Helios](https://github.com/a16z/helios) is a fully trustless, efficient, and portable Ethereum light client written in Rust. +* [Reth](https://github.com/paradigmxyz/reth) Modular, contributor-friendly and blazing-fast implementation of the Ethereum protocol +* [Arbiter](https://github.com/primitivefinance/arbiter) is a framework for stateful Ethereum smart-contract simulation +* [Zeth](https://github.com/risc0/zeth) is an open-source ZK block prover for Ethereum built on the RISC Zero zkVM. +* [VERBS](https://github.com/simtopia/verbs) an open-source Ethereum agent-based modelling and simulation library with a Python API. +* [Hardhat](https://github.com/NomicFoundation/hardhat) is a development environment to compile, deploy, test, and debug your Ethereum software. +* [Trin](https://github.com/ethereum/trin) is Portal Network client. An execution and consensus layer Ethereum light client written in Rust. Portal Network client's provide complete, provable, and distributed execution archival access. +* [Simular](https://github.com/simular-fi/simular/) is a Python smart-contract API with a fast, embedded, Ethereum Virtual Machine. +* [rbuilder](https://github.com/flashbots/rbuilder) is a state of the art Ethereum MEV-Boost block builder written in Rust and designed to work with Reth. +* ... + +(If you want to add project to the list, ping me or open the PR) + +# Documentation + +The book can be found at github page here: https://bluealloy.github.io/revm/ + +The documentation (alas needs some love) can be found here: https://bluealloy.github.io/revm/docs/ + +To serve the mdbook documentation in a local environment, ensure you have mdbook installed (if not install it with cargo) and then run: + +```shell +mdbook serve documentation +``` + +# Contact + +There is public telegram group: https://t.me/+Ig4WDWOzikA3MzA0 + +Or if you want to contact me directly, here is my email: dragan0rakita@gmail.com and telegram: https://t.me/draganrakita From 61d8deea03ab131c09c772fc8ccc301e2f76f299 Mon Sep 17 00:00:00 2001 From: rafagomes Date: Thu, 9 Jan 2025 10:13:29 -0300 Subject: [PATCH 40/44] fix: correct formatting and grammar in README for Gateway Circuit SDK instructions --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3333e01601..5319ec6195 100644 --- a/README.md +++ b/README.md @@ -32,8 +32,8 @@ This project tends to use the newest rust version, so if you're encountering a b ### Dependencies --[Gateway Circuit SDK](https://github.com/GatewayLabs/circuit-sdk) -In in the same work directory as you cloned this repository, you have to clone the `Gateway Circuit SDK`. +- [Gateway Circuit SDK](https://github.com/GatewayLabs/circuit-sdk) + In in the same work directory as you cloned this repository, you have to clone the `Gateway Circuit SDK`. ```shell git clone git@github.com:GatewayLabs/circuit-sdk.git From e7b4308cec32861d2f68cfd3a6c171aaf7ec0957 Mon Sep 17 00:00:00 2001 From: rafagomes Date: Thu, 9 Jan 2025 10:18:51 -0300 Subject: [PATCH 41/44] fix: update example command in README for clarity --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5319ec6195..df08060900 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ cargo build --release ### Running an example ```shell -cd examples/add_example +cargo run -p add-example (for instance) ``` ```shell From ebc4613b459278422dc53424f22552bd0c4590f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Conserva?= Date: Thu, 9 Jan 2025 10:49:21 -0300 Subject: [PATCH 42/44] refactor: simplify jumpi instruction by removing unnecessary circuit execution --- crates/interpreter/src/instructions/control.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/crates/interpreter/src/instructions/control.rs b/crates/interpreter/src/instructions/control.rs index 3b082e44bc..d7459d8bc7 100644 --- a/crates/interpreter/src/instructions/control.rs +++ b/crates/interpreter/src/instructions/control.rs @@ -61,13 +61,9 @@ pub fn jumpi(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::HIGH); pop!(interpreter, target, cond); - let cond_garbled = cond.to_garbled_value(&mut interpreter.circuit_builder); - let output = interpreter.circuit_builder.compile_and_execute::<256>(&cond_garbled) - .expect("Failed to execute circuit"); - - if !output.bits[0] { - jump_inner(interpreter, target.into()); - } + if !cond.to_u256().is_zero() { + jump_inner(interpreter, target.into()); + } } #[inline] From 94ec41cc6a8a9d3e8ebb6945354a7dccf8875d1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Conserva?= Date: Thu, 9 Jan 2025 15:47:49 -0300 Subject: [PATCH 43/44] fix: update expected value in bitwise instruction test case --- crates/interpreter/src/instructions/bitwise.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/interpreter/src/instructions/bitwise.rs b/crates/interpreter/src/instructions/bitwise.rs index 36b0351a85..0aafaf6c7b 100644 --- a/crates/interpreter/src/instructions/bitwise.rs +++ b/crates/interpreter/src/instructions/bitwise.rs @@ -481,7 +481,7 @@ mod tests { TestCase { op1: U256::from(0x1234567890abcdefu128), op2: U256::from(0xfedcba0987654321u128), - expected: U256::from(0x0214440010044001u128), + expected: U256::from(1302686019935617313u64), }, TestCase { op1: U256::from(0xffffffffffffffffu128), From 58da1d654376fb95ce2780a3c58606e044281abf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Conserva?= Date: Thu, 9 Jan 2025 15:47:54 -0300 Subject: [PATCH 44/44] fix: update expected values in bitwise instruction test cases --- crates/interpreter/src/instructions/bitwise.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/interpreter/src/instructions/bitwise.rs b/crates/interpreter/src/instructions/bitwise.rs index 0aafaf6c7b..77848d9db6 100644 --- a/crates/interpreter/src/instructions/bitwise.rs +++ b/crates/interpreter/src/instructions/bitwise.rs @@ -436,15 +436,15 @@ mod tests { let test_cases = vec![ TestCase { op1: U256::from(1u64), - expected: U256::from(0u64), + expected: !U256::from(1u64), }, TestCase { op1: U256::from(0u64), - expected: U256::from(1u64), + expected: !U256::from(0u64), }, TestCase { op1: U256::from(0x1234567890abcdefu128), - expected: U256::from(0xedcba9876f543210u128), + expected: !U256::from(0x1234567890abcdefu128), }, ];