Skip to content
This repository was archived by the owner on Jul 5, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion bus-mapping/src/evm/opcodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::Error;
use core::fmt::Debug;
use eth_types::GethExecStep;

mod calldatasize;
mod caller;
mod callvalue;
mod coinbase;
Expand All @@ -27,6 +28,7 @@ use crate::evm::OpcodeId;
use log::warn;

use self::push::Push;
use calldatasize::Calldatasize;
use caller::Caller;
use callvalue::Callvalue;
use dup::Dup;
Expand Down Expand Up @@ -104,7 +106,7 @@ fn fn_gen_associated_ops(opcode_id: &OpcodeId) -> FnGenAssociatedOps {
OpcodeId::CALLER => Caller::gen_associated_ops,
OpcodeId::CALLVALUE => Callvalue::gen_associated_ops,
// OpcodeId::CALLDATALOAD => {},
// OpcodeId::CALLDATASIZE => {},
OpcodeId::CALLDATASIZE => Calldatasize::gen_associated_ops,
// OpcodeId::CALLDATACOPY => {},
// OpcodeId::CODESIZE => {},
// OpcodeId::CODECOPY => {},
Expand Down
111 changes: 111 additions & 0 deletions bus-mapping/src/evm/opcodes/calldatasize.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
use crate::{
circuit_input_builder::CircuitInputStateRef,
operation::{CallContextField, CallContextOp, RW},
Error,
};

use eth_types::GethExecStep;

use super::Opcode;

#[derive(Clone, Copy, Debug)]
pub(crate) struct Calldatasize;

impl Opcode for Calldatasize {
fn gen_associated_ops(
state: &mut CircuitInputStateRef,
steps: &[GethExecStep],
) -> Result<(), Error> {
let step = &steps[0];
let value = steps[1].stack.last()?;
state.push_op(
RW::READ,
CallContextOp {
call_id: state.call().call_id,
field: CallContextField::CallDataLength,
value,
},
);
state.push_stack_op(RW::WRITE, step.stack.last_filled().map(|a| a - 1), value);
Ok(())
}
}

#[cfg(test)]
mod calldatasize_tests {
use crate::{
circuit_input_builder::{ExecStep, TransactionContext},
mock::BlockData,
operation::{CallContextField, CallContextOp, RW},
Error,
};
use eth_types::bytecode;
use eth_types::evm_types::StackAddress;
use mock::new_single_tx_trace_code_at_start;
use pretty_assertions::assert_eq;

#[test]
fn calldatasize_opcode_impl() -> Result<(), Error> {
let code = bytecode! {
#[start]
CALLDATASIZE
STOP
};

// Get the execution steps from the external tracer
let block =
BlockData::new_from_geth_data(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 CALLDATASIZE
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);

// Get calldatasize from eth tx.
let call_data_size = block.eth_tx.input.to_vec().len();

// Add the read operation.
state_ref.push_op(
RW::READ,
CallContextOp {
call_id: state_ref.call().call_id,
field: CallContextField::CallDataLength,
value: eth_types::U256::from(call_data_size),
},
);

// Add the stack write.
state_ref.push_stack_op(
RW::WRITE,
StackAddress::from(1024 - 1),
eth_types::U256::from(call_data_size),
);

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(())
}
}
7 changes: 7 additions & 0 deletions zkevm-circuits/src/evm_circuit/execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ mod begin_tx;
mod bitwise;
mod byte;
mod calldatacopy;
mod calldatasize;
mod caller;
mod callvalue;
mod coinbase;
Expand Down Expand Up @@ -49,6 +50,7 @@ use begin_tx::BeginTxGadget;
use bitwise::BitwiseGadget;
use byte::ByteGadget;
use calldatacopy::CallDataCopyGadget;
use calldatasize::CallDataSizeGadget;
use caller::CallerGadget;
use callvalue::CallValueGadget;
use coinbase::CoinbaseGadget;
Expand Down Expand Up @@ -102,6 +104,7 @@ pub(crate) struct ExecutionConfig<F> {
begin_tx_gadget: BeginTxGadget<F>,
byte_gadget: ByteGadget<F>,
calldatacopy_gadget: CallDataCopyGadget<F>,
calldatasize_gadget: CallDataSizeGadget<F>,
caller_gadget: CallerGadget<F>,
call_value_gadget: CallValueGadget<F>,
comparator_gadget: ComparatorGadget<F>,
Expand Down Expand Up @@ -234,6 +237,7 @@ impl<F: FieldExt> ExecutionConfig<F> {
begin_tx_gadget: configure_gadget!(),
byte_gadget: configure_gadget!(),
calldatacopy_gadget: configure_gadget!(),
calldatasize_gadget: configure_gadget!(),
caller_gadget: configure_gadget!(),
call_value_gadget: configure_gadget!(),
comparator_gadget: configure_gadget!(),
Expand Down Expand Up @@ -518,6 +522,9 @@ impl<F: FieldExt> ExecutionConfig<F> {
ExecutionState::ErrorOutOfGasPureMemory => {
assign_exec_step!(self.error_oog_pure_memory_gadget)
}
ExecutionState::CALLDATASIZE => {
assign_exec_step!(self.calldatasize_gadget)
}
_ => unimplemented!(),
}

Expand Down
198 changes: 198 additions & 0 deletions zkevm-circuits/src/evm_circuit/execution/calldatasize.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
use std::convert::TryInto;

use eth_types::ToLittleEndian;
use halo2::{arithmetic::FieldExt, circuit::Region, plonk::Error};

use crate::{
evm_circuit::{
param::N_BYTES_CALLDATASIZE,
step::ExecutionState,
table::CallContextFieldTag,
util::{
common_gadget::SameContextGadget,
constraint_builder::{ConstraintBuilder, StepStateTransition, Transition},
from_bytes, RandomLinearCombination,
},
witness::{Block, Call, ExecStep, Transaction},
},
util::Expr,
};

use super::ExecutionGadget;

#[derive(Clone, Debug)]
pub(crate) struct CallDataSizeGadget<F> {
same_context: SameContextGadget<F>,
call_data_size: RandomLinearCombination<F, N_BYTES_CALLDATASIZE>,
}

impl<F: FieldExt> ExecutionGadget<F> for CallDataSizeGadget<F> {
const NAME: &'static str = "CALLDATASIZE";

const EXECUTION_STATE: ExecutionState = ExecutionState::CALLDATASIZE;

fn configure(cb: &mut ConstraintBuilder<F>) -> Self {
let opcode = cb.query_cell();

// Add lookup constraint in the call context for the calldatasize field.
let call_data_size = cb.query_rlc();
cb.call_context_lookup(
false.expr(),
None,
CallContextFieldTag::CallDataLength,
from_bytes::expr(&call_data_size.cells),
);

// The calldatasize should be pushed to the top of the stack.
cb.stack_push(call_data_size.expr());
Comment thread
ed255 marked this conversation as resolved.

let step_state_transition = StepStateTransition {
rw_counter: Transition::Delta(2.expr()),
program_counter: Transition::Delta(1.expr()),
stack_pointer: Transition::Delta((-1).expr()),
..Default::default()
};

let same_context = SameContextGadget::construct(cb, opcode, step_state_transition, None);

Self {
same_context,
call_data_size,
}
}

fn assign_exec_step(
&self,
region: &mut Region<'_, F>,
offset: usize,
block: &Block<F>,
_tx: &Transaction,
_call: &Call,
step: &ExecStep,
) -> Result<(), Error> {
self.same_context.assign_exec_step(region, offset, step)?;

let call_data_size = block.rws[step.rw_indices[1]].stack_value();

self.call_data_size.assign(
region,
offset,
Some(
call_data_size.to_le_bytes()[..N_BYTES_CALLDATASIZE]
.try_into()
.unwrap(),
),
)?;

Ok(())
}
}

#[cfg(test)]
mod test {
use std::collections::HashMap;

use bus_mapping::evm::OpcodeId;
use eth_types::{bytecode, Word};
use halo2::arithmetic::BaseExt;
use pairing::bn256::Fr;

use crate::evm_circuit::{
step::ExecutionState,
table::{CallContextFieldTag, RwTableTag},
test::{rand_bytes, run_test_circuit_incomplete_fixed_table},
witness::{Block, Bytecode, Call, CodeSource, ExecStep, Rw, RwMap, Transaction},
};

fn test_ok(call_data_size: usize, is_root: bool) {
let randomness = Fr::rand();
let bytecode = bytecode! {
#[start]
CALLDATASIZE
STOP
};
let bytecode = Bytecode::new(bytecode.to_vec());
let call_id = 1;
let call_data = rand_bytes(call_data_size);

let mut rw_map = HashMap::new();
rw_map.insert(
RwTableTag::CallContext,
vec![Rw::CallContext {
rw_counter: 9,
is_write: false,
call_id,
field_tag: CallContextFieldTag::CallDataLength,
value: Word::from(call_data_size),
}],
);
rw_map.insert(
RwTableTag::Stack,
vec![Rw::Stack {
rw_counter: 10,
is_write: true,
call_id,
stack_pointer: 1023,
value: Word::from(call_data_size),
Comment thread
ed255 marked this conversation as resolved.
}],
);

let steps = vec![
ExecStep {
execution_state: ExecutionState::CALLDATASIZE,
rw_indices: vec![(RwTableTag::CallContext, 0), (RwTableTag::Stack, 0)],
rw_counter: 9,
program_counter: 0,
stack_pointer: 1024,
gas_left: OpcodeId::CALLDATASIZE.constant_gas_cost().as_u64(),
gas_cost: OpcodeId::CALLDATASIZE.constant_gas_cost().as_u64(),
opcode: Some(OpcodeId::CALLDATASIZE),
..Default::default()
},
ExecStep {
execution_state: ExecutionState::STOP,
rw_counter: 11,
program_counter: 1,
stack_pointer: 1023,
gas_left: 0,
opcode: Some(OpcodeId::STOP),
..Default::default()
},
];

let block = Block {
randomness,
txs: vec![Transaction {
id: 1,
call_data,
call_data_length: call_data_size,
steps,
calls: vec![Call {
id: call_id,
is_root,
is_create: false,
call_data_length: call_data_size as u64,
code_source: CodeSource::Account(bytecode.hash),
..Default::default()
}],
..Default::default()
}],
rws: RwMap(rw_map),
bytecodes: vec![bytecode],
..Default::default()
};

assert_eq!(run_test_circuit_incomplete_fixed_table(block), Ok(()));
}

#[test]
fn calldatasize_gadget_root() {
test_ok(32, true);
test_ok(64, true);
test_ok(96, true);
test_ok(128, true);
test_ok(256, true);
test_ok(512, true);
test_ok(1024, true);
}
}
3 changes: 3 additions & 0 deletions zkevm-circuits/src/evm_circuit/param.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,6 @@ pub(crate) const N_BYTES_PROGRAM_COUNTER: usize = N_BYTES_U64;

// Number of bytes that will be used for a tx's gas field.
pub(crate) const N_BYTES_GAS: usize = N_BYTES_U64;

// Number of bytes that will be used for call data's size.
pub(crate) const N_BYTES_CALLDATASIZE: usize = N_BYTES_U64;