Skip to content
This repository was archived by the owner on Apr 18, 2025. It is now read-only.
Closed
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
3 changes: 2 additions & 1 deletion bus-mapping/src/evm/opcodes.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//! Definition of each opcode of the EVM.
mod calldatasize;
mod coinbase;
mod dup;
mod gas;
Expand Down Expand Up @@ -99,7 +100,7 @@ fn fn_gen_associated_ops(opcode_id: &OpcodeId) -> FnGenAssociatedOps {
// OpcodeId::CALLER => {},
// OpcodeId::CALLVALUE => {},
// OpcodeId::CALLDATALOAD => {},
// OpcodeId::CALLDATASIZE => {},
OpcodeId::CALLDATASIZE => StackOnlyOpcode::<0>::gen_associated_ops,
// OpcodeId::CALLDATACOPY => {},
// OpcodeId::CODESIZE => {},
// OpcodeId::CODECOPY => {},
Expand Down
63 changes: 63 additions & 0 deletions bus-mapping/src/evm/opcodes/calldatasize.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#[cfg(test)]
mod calldatasize_tests {
use crate::{
circuit_input_builder::{ExecStep, TransactionContext},
mock::BlockData,
operation::RW,
Error,
};
use eth_types::evm_types::StackAddress;
use eth_types::{bytecode, ToWord};
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).unwrap();
let mut tx_ctx = TransactionContext::new(&block.eth_tx);

// Generate step corresponding to COINBASE
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);

// Add the last Stack write
state_ref.push_stack_op(
RW::WRITE,
StackAddress::from(1024 - 1),
block.b_constant.coinbase.to_word(),
);

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 @@ -20,6 +20,7 @@ mod add;
mod begin_tx;
mod bitwise;
mod byte;
mod calldatasize;
mod coinbase;
mod comparator;
mod dup;
Expand All @@ -44,6 +45,7 @@ use add::AddGadget;
use begin_tx::BeginTxGadget;
use bitwise::BitwiseGadget;
use byte::ByteGadget;
use calldatasize::CallDataSizeGadget;
use coinbase::CoinbaseGadget;
use comparator::ComparatorGadget;
use dup::DupGadget;
Expand Down Expand Up @@ -93,6 +95,7 @@ pub(crate) struct ExecutionConfig<F> {
bitwise_gadget: BitwiseGadget<F>,
begin_tx_gadget: BeginTxGadget<F>,
byte_gadget: ByteGadget<F>,
calldatasize_gadget: CallDataSizeGadget<F>,
comparator_gadget: ComparatorGadget<F>,
dup_gadget: DupGadget<F>,
error_oog_pure_memory_gadget: ErrorOOGPureMemoryGadget<F>,
Expand Down Expand Up @@ -221,6 +224,7 @@ impl<F: FieldExt> ExecutionConfig<F> {
bitwise_gadget: configure_gadget!(),
begin_tx_gadget: configure_gadget!(),
byte_gadget: configure_gadget!(),
calldatasize_gadget: configure_gadget!(),
comparator_gadget: configure_gadget!(),
dup_gadget: configure_gadget!(),
error_oog_pure_memory_gadget: configure_gadget!(),
Expand Down Expand Up @@ -492,6 +496,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
179 changes: 179 additions & 0 deletions zkevm-circuits/src/evm_circuit/execution/calldatasize.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
use halo2::{arithmetic::FieldExt, circuit::Region, plonk::Error};

use crate::{
evm_circuit::{
step::ExecutionState,
table::CallContextFieldTag,
util::{
common_gadget::SameContextGadget,
constraint_builder::{ConstraintBuilder, StepStateTransition, Transition},
Cell,
},
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: Cell<F>,
}

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();

// Calldatasize can be looked up in the above tx_id's context.
let call_data_size = cb.query_cell();

// Add lookup constraint in the call context for the calldatasize field.
cb.call_context_lookup(
None,
CallContextFieldTag::CallDataLength,
call_data_size.expr(),
);

// The calldatasize should be pushed to the top of the stack.
cb.stack_push(call_data_size.expr());

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<F>,
call: &Call<F>,
step: &ExecStep,
) -> Result<(), Error> {
self.same_context.assign_exec_step(region, offset, step)?;

self.call_data_size
.assign(region, offset, Some(F::from(call.call_data_length as u64)))?;

Ok(())
}
}

#[cfg(test)]
mod test {
use bus_mapping::evm::OpcodeId;
use eth_types::{bytecode, ToLittleEndian, Word};
use halo2::arithmetic::BaseExt;
use pairing::bn256::Fr;

use crate::evm_circuit::{
step::ExecutionState,
table::CallContextFieldTag,
test::{rand_bytes, run_test_circuit_incomplete_fixed_table},
util::RandomLinearCombination,
witness::{Block, Bytecode, Call, ExecStep, Rw, 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 rws = vec![
Rw::CallContext {
rw_counter: 9,
is_write: false,
call_id,
field_tag: CallContextFieldTag::CallDataLength,
value: Word::from(call_data_size),
},
Rw::Stack {
rw_counter: 10,
is_write: true,
call_id,
stack_pointer: 1023,
value: Word::from(call_data_size),
},
];

let steps = vec![
ExecStep {
execution_state: ExecutionState::CALLDATASIZE,
rw_indices: vec![0, 1],
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,
opcode_source: RandomLinearCombination::random_linear_combine(
bytecode.hash.to_le_bytes(),
randomness,
),
..Default::default()
}],
..Default::default()
}],
rws,
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);
}
}