diff --git a/bus-mapping/src/evm/opcodes.rs b/bus-mapping/src/evm/opcodes.rs index 52ddcb1785..eaf9723ee9 100644 --- a/bus-mapping/src/evm/opcodes.rs +++ b/bus-mapping/src/evm/opcodes.rs @@ -1,4 +1,11 @@ //! Definition of each opcode of the EVM. +use crate::circuit_input_builder::CircuitInputStateRef; +use crate::Error; +use core::fmt::Debug; +use eth_types::GethExecStep; + +mod caller; +mod callvalue; mod coinbase; mod dup; mod gas; @@ -16,14 +23,12 @@ mod stackonlyop; mod stop; mod swap; mod timestamp; -use crate::circuit_input_builder::CircuitInputStateRef; use crate::evm::OpcodeId; -use crate::Error; -use core::fmt::Debug; -use eth_types::GethExecStep; use log::warn; use self::push::Push; +use caller::Caller; +use callvalue::Callvalue; use dup::Dup; use gas::Gas; use jump::Jump; @@ -96,8 +101,8 @@ fn fn_gen_associated_ops(opcode_id: &OpcodeId) -> FnGenAssociatedOps { // OpcodeId::ADDRESS => {}, // OpcodeId::BALANCE => {}, // OpcodeId::ORIGIN => {}, - // OpcodeId::CALLER => {}, - // OpcodeId::CALLVALUE => {}, + OpcodeId::CALLER => Caller::gen_associated_ops, + OpcodeId::CALLVALUE => Callvalue::gen_associated_ops, // OpcodeId::CALLDATALOAD => {}, // OpcodeId::CALLDATASIZE => {}, // OpcodeId::CALLDATACOPY => {}, diff --git a/bus-mapping/src/evm/opcodes/caller.rs b/bus-mapping/src/evm/opcodes/caller.rs new file mode 100644 index 0000000000..ada767bbf0 --- /dev/null +++ b/bus-mapping/src/evm/opcodes/caller.rs @@ -0,0 +1,102 @@ +use super::Opcode; +use crate::circuit_input_builder::CircuitInputStateRef; +use crate::operation::{CallContextField, CallContextOp, RW}; +use crate::Error; +use eth_types::GethExecStep; + +/// Placeholder structure used to implement [`Opcode`] trait over it +/// corresponding to the [`OpcodeId::PC`](crate::evm::OpcodeId::PC) `OpcodeId`. +#[derive(Debug, Copy, Clone)] +pub(crate) struct Caller; + +impl Opcode for Caller { + fn gen_associated_ops( + state: &mut CircuitInputStateRef, + steps: &[GethExecStep], + ) -> Result<(), Error> { + let step = &steps[0]; + // Get caller_address result from next step + let value = steps[1].stack.last()?; + // CallContext read of the caller_address + state.push_op( + RW::READ, + CallContextOp { + call_id: state.call().call_id, + field: CallContextField::CallerAddress, + value, + }, + ); + // Stack write of the caller_address + state.push_stack_op(RW::WRITE, step.stack.last_filled().map(|a| a - 1), value); + + Ok(()) + } +} + +#[cfg(test)] +mod caller_tests { + use super::*; + use crate::circuit_input_builder::{ExecStep, TransactionContext}; + use eth_types::{bytecode, evm_types::StackAddress, ToWord}; + use pretty_assertions::assert_eq; + + #[test] + fn caller_opcode_impl() -> Result<(), Error> { + let code = bytecode! { + #[start] + CALLER + STOP + }; + + // Get the execution steps from the external tracer + let block = crate::mock::BlockData::new_from_geth_data( + mock::new_single_tx_trace_code_at_start(&code).unwrap(), + ); + + let mut builder = block.new_circuit_input_builder(); + builder.handle_tx(&block.eth_tx, &block.geth_trace).unwrap(); + + let mut test_builder = block.new_circuit_input_builder(); + let mut tx = test_builder + .new_tx(&block.eth_tx, !block.geth_trace.failed) + .unwrap(); + let mut tx_ctx = TransactionContext::new(&block.eth_tx, &block.geth_trace).unwrap(); + + // Generate step corresponding to CALLER + let mut step = ExecStep::new( + &block.geth_trace.struct_logs[0], + 0, + test_builder.block_ctx.rwc, + 0, + ); + let mut state_ref = test_builder.state_ref(&mut tx, &mut tx_ctx, &mut step); + + let caller_address = block.eth_tx.from.to_word(); + + // Add the CallContext read + state_ref.push_op( + RW::READ, + CallContextOp { + call_id: state_ref.call().call_id, + field: CallContextField::CallerAddress, + value: caller_address, + }, + ); + // Add the Stack write + state_ref.push_stack_op(RW::WRITE, StackAddress::from(1024 - 1), caller_address); + + tx.steps_mut().push(step); + test_builder.block.txs_mut().push(tx); + + // Compare first step bus mapping instance + assert_eq!( + builder.block.txs()[0].steps()[0].bus_mapping_instance, + test_builder.block.txs()[0].steps()[0].bus_mapping_instance, + ); + + // Compare containers + assert_eq!(builder.block.container, test_builder.block.container); + + Ok(()) + } +} diff --git a/bus-mapping/src/evm/opcodes/callvalue.rs b/bus-mapping/src/evm/opcodes/callvalue.rs new file mode 100644 index 0000000000..0708766870 --- /dev/null +++ b/bus-mapping/src/evm/opcodes/callvalue.rs @@ -0,0 +1,102 @@ +use super::Opcode; +use crate::circuit_input_builder::CircuitInputStateRef; +use crate::operation::{CallContextField, CallContextOp, RW}; +use crate::Error; +use eth_types::GethExecStep; + +/// Placeholder structure used to implement [`Opcode`] trait over it +/// corresponding to the [`OpcodeId::PC`](crate::evm::OpcodeId::PC) `OpcodeId`. +#[derive(Debug, Copy, Clone)] +pub(crate) struct Callvalue; + +impl Opcode for Callvalue { + fn gen_associated_ops( + state: &mut CircuitInputStateRef, + steps: &[GethExecStep], + ) -> Result<(), Error> { + let step = &steps[0]; + // Get call_value result from next step + let value = steps[1].stack.last()?; + // CallContext read of the call_value + state.push_op( + RW::READ, + CallContextOp { + call_id: state.call().call_id, + field: CallContextField::Value, + value, + }, + ); + // Stack write of the call_value + state.push_stack_op(RW::WRITE, step.stack.last_filled().map(|a| a - 1), value); + + Ok(()) + } +} + +#[cfg(test)] +mod callvalue_tests { + use super::*; + use crate::circuit_input_builder::{ExecStep, TransactionContext}; + use eth_types::{bytecode, evm_types::StackAddress}; + use pretty_assertions::assert_eq; + + #[test] + fn callvalue_opcode_impl() -> Result<(), Error> { + let code = bytecode! { + #[start] + CALLVALUE + STOP + }; + + // Get the execution steps from the external tracer + let block = crate::mock::BlockData::new_from_geth_data( + mock::new_single_tx_trace_code_at_start(&code).unwrap(), + ); + + let mut builder = block.new_circuit_input_builder(); + builder.handle_tx(&block.eth_tx, &block.geth_trace).unwrap(); + + let mut test_builder = block.new_circuit_input_builder(); + let mut tx = test_builder + .new_tx(&block.eth_tx, !block.geth_trace.failed) + .unwrap(); + let mut tx_ctx = TransactionContext::new(&block.eth_tx, &block.geth_trace).unwrap(); + + // Generate step corresponding to CALLVALUE + let mut step = ExecStep::new( + &block.geth_trace.struct_logs[0], + 0, + test_builder.block_ctx.rwc, + 0, + ); + let mut state_ref = test_builder.state_ref(&mut tx, &mut tx_ctx, &mut step); + + let call_value = block.eth_tx.value; + + // Add the CallContext read + state_ref.push_op( + RW::READ, + CallContextOp { + call_id: state_ref.call().call_id, + field: CallContextField::Value, + value: call_value, + }, + ); + // Add the Stack write + state_ref.push_stack_op(RW::WRITE, StackAddress::from(1024 - 1), call_value); + + tx.steps_mut().push(step); + test_builder.block.txs_mut().push(tx); + + // Compare first step bus mapping instance + assert_eq!( + builder.block.txs()[0].steps()[0].bus_mapping_instance, + test_builder.block.txs()[0].steps()[0].bus_mapping_instance, + ); + + // Compare containers + assert_eq!(builder.block.container, test_builder.block.container); + + Ok(()) + } +} diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index b4a9e6a93b..0b00661d3e 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -20,6 +20,8 @@ mod add; mod begin_tx; mod bitwise; mod byte; +mod caller; +mod callvalue; mod coinbase; mod comparator; mod dup; @@ -44,6 +46,8 @@ use add::AddGadget; use begin_tx::BeginTxGadget; use bitwise::BitwiseGadget; use byte::ByteGadget; +use caller::CallerGadget; +use callvalue::CallValueGadget; use coinbase::CoinbaseGadget; use comparator::ComparatorGadget; use dup::DupGadget; @@ -93,6 +97,8 @@ pub(crate) struct ExecutionConfig { bitwise_gadget: BitwiseGadget, begin_tx_gadget: BeginTxGadget, byte_gadget: ByteGadget, + caller_gadget: CallerGadget, + call_value_gadget: CallValueGadget, comparator_gadget: ComparatorGadget, dup_gadget: DupGadget, error_oog_pure_memory_gadget: ErrorOOGPureMemoryGadget, @@ -221,6 +227,8 @@ impl ExecutionConfig { bitwise_gadget: configure_gadget!(), begin_tx_gadget: configure_gadget!(), byte_gadget: configure_gadget!(), + caller_gadget: configure_gadget!(), + call_value_gadget: configure_gadget!(), comparator_gadget: configure_gadget!(), dup_gadget: configure_gadget!(), error_oog_pure_memory_gadget: configure_gadget!(), @@ -485,6 +493,10 @@ impl ExecutionConfig { ExecutionState::PUSH => assign_exec_step!(self.push_gadget), ExecutionState::DUP => assign_exec_step!(self.dup_gadget), ExecutionState::SWAP => assign_exec_step!(self.swap_gadget), + ExecutionState::CALLER => assign_exec_step!(self.caller_gadget), + ExecutionState::CALLVALUE => { + assign_exec_step!(self.call_value_gadget) + } ExecutionState::COINBASE => assign_exec_step!(self.coinbase_gadget), ExecutionState::TIMESTAMP => { assign_exec_step!(self.timestamp_gadget) diff --git a/zkevm-circuits/src/evm_circuit/execution/caller.rs b/zkevm-circuits/src/evm_circuit/execution/caller.rs new file mode 100644 index 0000000000..d518c762ff --- /dev/null +++ b/zkevm-circuits/src/evm_circuit/execution/caller.rs @@ -0,0 +1,106 @@ +use crate::{ + evm_circuit::{ + execution::ExecutionGadget, + param::N_BYTES_ACCOUNT_ADDRESS, + step::ExecutionState, + table::CallContextFieldTag, + util::{ + common_gadget::SameContextGadget, + constraint_builder::{ConstraintBuilder, StepStateTransition, Transition::Delta}, + from_bytes, RandomLinearCombination, + }, + witness::{Block, Call, ExecStep, Transaction}, + }, + util::Expr, +}; +use eth_types::ToLittleEndian; +use halo2::{arithmetic::FieldExt, circuit::Region, plonk::Error}; +use std::convert::TryInto; + +#[derive(Clone, Debug)] +pub(crate) struct CallerGadget { + same_context: SameContextGadget, + // Using RLC to match against rw_table->stack_op value + caller_address: RandomLinearCombination, +} + +impl ExecutionGadget for CallerGadget { + const NAME: &'static str = "CALLER"; + + const EXECUTION_STATE: ExecutionState = ExecutionState::CALLER; + + fn configure(cb: &mut ConstraintBuilder) -> Self { + let caller_address = cb.query_rlc::(); + + // Lookup rw_table -> call_context with caller address + cb.call_context_lookup( + false.expr(), + None, // cb.curr.state.call_id + CallContextFieldTag::CallerAddress, + from_bytes::expr(&caller_address.cells), + ); + + // Push the value to the stack + cb.stack_push(caller_address.expr()); + + // State transition + let opcode = cb.query_cell(); + let step_state_transition = StepStateTransition { + rw_counter: Delta(2.expr()), + program_counter: Delta(1.expr()), + stack_pointer: Delta((-1).expr()), + ..Default::default() + }; + let same_context = SameContextGadget::construct(cb, opcode, step_state_transition, None); + + Self { + same_context, + caller_address, + } + } + + fn assign_exec_step( + &self, + region: &mut Region<'_, F>, + offset: usize, + block: &Block, + _: &Transaction, + _: &Call, + step: &ExecStep, + ) -> Result<(), Error> { + self.same_context.assign_exec_step(region, offset, step)?; + + let caller = block.rws[step.rw_indices[1]].stack_value(); + + self.caller_address.assign( + region, + offset, + Some( + caller.to_le_bytes()[..N_BYTES_ACCOUNT_ADDRESS] + .try_into() + .unwrap(), + ), + )?; + + Ok(()) + } +} + +#[cfg(test)] +mod test { + use crate::test_util::run_test_circuits; + use eth_types::bytecode; + + fn test_ok() { + let bytecode = bytecode! { + #[start] + CALLER + STOP + }; + assert_eq!(run_test_circuits(bytecode), Ok(())); + } + #[test] + fn caller_gadget_test() { + test_ok(); + } +} diff --git a/zkevm-circuits/src/evm_circuit/execution/callvalue.rs b/zkevm-circuits/src/evm_circuit/execution/callvalue.rs new file mode 100644 index 0000000000..9615be8f9d --- /dev/null +++ b/zkevm-circuits/src/evm_circuit/execution/callvalue.rs @@ -0,0 +1,104 @@ +use crate::{ + evm_circuit::{ + execution::ExecutionGadget, + step::ExecutionState, + table::CallContextFieldTag, + util::{ + common_gadget::SameContextGadget, + constraint_builder::{ConstraintBuilder, StepStateTransition, Transition::Delta}, + Cell, Word, + }, + witness::{Block, Call, ExecStep, Transaction}, + }, + util::Expr, +}; +use eth_types::ToLittleEndian; +use halo2::{arithmetic::FieldExt, circuit::Region, plonk::Error}; + +#[derive(Clone, Debug)] +pub(crate) struct CallValueGadget { + same_context: SameContextGadget, + // Value in rw_table->stack_op and call_context->call_value are both RLC + // encoded, so no need to decode. + call_value: Cell, +} + +impl ExecutionGadget for CallValueGadget { + const NAME: &'static str = "CALLVALUE"; + + const EXECUTION_STATE: ExecutionState = ExecutionState::CALLVALUE; + + fn configure(cb: &mut ConstraintBuilder) -> Self { + let call_value = cb.query_cell(); + + // Lookup rw_table -> call_context with call value + cb.call_context_lookup( + false.expr(), + None, // cb.curr.state.call_id + CallContextFieldTag::Value, + call_value.expr(), + ); + + // Push the value to the stack + cb.stack_push(call_value.expr()); + + // State transition + let opcode = cb.query_cell(); + let step_state_transition = StepStateTransition { + rw_counter: Delta(2.expr()), + program_counter: Delta(1.expr()), + stack_pointer: Delta((-1).expr()), + ..Default::default() + }; + let same_context = SameContextGadget::construct(cb, opcode, step_state_transition, None); + + Self { + same_context, + call_value, + } + } + + fn assign_exec_step( + &self, + region: &mut Region<'_, F>, + offset: usize, + block: &Block, + _: &Transaction, + _: &Call, + step: &ExecStep, + ) -> Result<(), Error> { + self.same_context.assign_exec_step(region, offset, step)?; + + let call_value = block.rws[step.rw_indices[1]].stack_value(); + + self.call_value.assign( + region, + offset, + Some(Word::random_linear_combine( + call_value.to_le_bytes(), + block.randomness, + )), + )?; + + Ok(()) + } +} + +#[cfg(test)] +mod test { + use crate::test_util::run_test_circuits; + use eth_types::bytecode; + + fn test_ok() { + let bytecode = bytecode! { + #[start] + CALLVALUE + STOP + }; + assert_eq!(run_test_circuits(bytecode), Ok(())); + } + #[test] + fn callvalue_gadget_test() { + test_ok(); + } +} diff --git a/zkevm-circuits/src/evm_circuit/witness.rs b/zkevm-circuits/src/evm_circuit/witness.rs index 9640511acf..f0fbb9a138 100644 --- a/zkevm-circuits/src/evm_circuit/witness.rs +++ b/zkevm-circuits/src/evm_circuit/witness.rs @@ -862,6 +862,8 @@ impl From<&circuit_input_builder::ExecStep> for ExecutionState { OpcodeId::JUMPI => ExecutionState::JUMPI, OpcodeId::PC => ExecutionState::PC, OpcodeId::MSIZE => ExecutionState::MSIZE, + OpcodeId::CALLER => ExecutionState::CALLER, + OpcodeId::CALLVALUE => ExecutionState::CALLVALUE, OpcodeId::COINBASE => ExecutionState::COINBASE, OpcodeId::TIMESTAMP => ExecutionState::TIMESTAMP, OpcodeId::GAS => ExecutionState::GAS,