forked from privacy-scaling-explorations/zkevm-circuits
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement circuit for CALLER and CALLVALUE opcode (privacy-scaling-ex…
- Loading branch information
Showing
7 changed files
with
439 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<F> { | ||
same_context: SameContextGadget<F>, | ||
// Using RLC to match against rw_table->stack_op value | ||
caller_address: RandomLinearCombination<F, 20>, | ||
} | ||
|
||
impl<F: FieldExt> ExecutionGadget<F> for CallerGadget<F> { | ||
const NAME: &'static str = "CALLER"; | ||
|
||
const EXECUTION_STATE: ExecutionState = ExecutionState::CALLER; | ||
|
||
fn configure(cb: &mut ConstraintBuilder<F>) -> Self { | ||
let caller_address = cb.query_rlc::<N_BYTES_ACCOUNT_ADDRESS>(); | ||
|
||
// 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<F>, | ||
_: &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(); | ||
} | ||
} |
Oops, something went wrong.