Skip to content
This repository was archived by the owner on Apr 18, 2025. It is now read-only.

Bus-mapping for opcode calldatacopy #102

Closed
wants to merge 28 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
9104842
Support generating multiple exec-steps from one geth-step.
silathdiir Feb 21, 2022
27d17c2
Fix build error.
silathdiir Feb 25, 2022
24c3806
Fix to use bus-mapping to generate bytecode.
silathdiir Feb 25, 2022
8c26e41
Update test cases.
silathdiir Feb 25, 2022
1cdfb99
Add basic of calldatacopy bus-mappinng to just support zero call data…
silathdiir Mar 3, 2022
1c5c570
Update bus-mapping calldatacopy.
silathdiir Mar 4, 2022
d4d9519
Push op of call_data_length and call data offset.
silathdiir Mar 8, 2022
b49fb8c
Add `is_root_call` to BytecodeTestConfig.
silathdiir Mar 8, 2022
b02cf91
Replace `OpcodeId` with `ExecState` in bus-mapping ExcStep.
silathdiir Mar 9, 2022
d851c0a
Generate CopyToMemory exection step.
silathdiir Mar 9, 2022
fc11d4d
Add TransactionConfig to bus-mapping handle_tx.
silathdiir Mar 9, 2022
66bd409
Update test code.
silathdiir Mar 10, 2022
b85ae04
1. Remove TransactionConfig and replace with `call_data`.
silathdiir Mar 11, 2022
4cf808f
Update test cases which call `handle_tx` and `new_tx` of circuit inpu…
silathdiir Mar 11, 2022
db85545
Update constant max address of state circuit.
silathdiir Mar 13, 2022
20fa747
Add unit test for calldatacopy bus-mapping.
silathdiir Mar 14, 2022
3c13cbe
change api
icemelon Mar 15, 2022
3b0a8d3
fix calldatacopy
icemelon Mar 15, 2022
58916b1
fix rebase
icemelon Mar 16, 2022
018866b
Set exec_state for BeginTx and EndTx.
silathdiir Mar 16, 2022
0e8b5e9
Fix to return a new exec step in function `dummy_gen_associated_ops`.
silathdiir Mar 16, 2022
ec2ed33
Update bus-mapping calldatacopy unit-test.
silathdiir Mar 16, 2022
9ec6e77
Update for fmt and clippy.
silathdiir Mar 16, 2022
53d7993
Fix doc test.
silathdiir Mar 16, 2022
3296d52
Update according to code review.
silathdiir Mar 16, 2022
7e8716c
Fix a comment.
silathdiir Mar 16, 2022
08fd368
Fix to directly use StepAuxiliaryData of bus-mapping in zkevm-circuits.
silathdiir Mar 16, 2022
e89b472
Revert a comment and a fix.
silathdiir Mar 17, 2022
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
175 changes: 118 additions & 57 deletions bus-mapping/src/circuit_input_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use crate::rpc::GethClient;
use ethers_providers::JsonRpcClient;

/// Out of Gas errors by opcode
#[derive(Debug, PartialEq)]
#[derive(Clone, Debug, PartialEq)]
pub enum OogError {
/// Out of Gas for opcodes which have non-zero constant gas cost
Constant,
Expand Down Expand Up @@ -65,7 +65,7 @@ pub enum OogError {
}

/// EVM Execution Error
#[derive(Debug, PartialEq)]
#[derive(Clone, Debug, PartialEq)]
pub enum ExecError {
/// Invalid Opcode
InvalidOpcode,
Expand Down Expand Up @@ -101,11 +101,73 @@ pub enum ExecError {
MaxCodeSizeExceeded,
}

/// Execution state
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ExecState {
/// EVM Opcode ID
Op(OpcodeId),
/// Virtual step Begin Tx
BeginTx,
/// Virtual step End Tx
EndTx,
/// Virtual step Copy To Memory
CopyToMemory,
}

impl ExecState {
/// Returns `true` if `ExecState` is an opcode and the opcode is a `PUSHn`.
pub fn is_push(&self) -> bool {
if let ExecState::Op(op) = self {
op.is_push()
} else {
false
}
}

/// Returns `true` if `ExecState` is an opcode and the opcode is a `DUPn`.
pub fn is_dup(&self) -> bool {
if let ExecState::Op(op) = self {
op.is_dup()
} else {
false
}
}

/// Returns `true` if `ExecState` is an opcode and the opcode is a `SWAPn`.
pub fn is_swap(&self) -> bool {
if let ExecState::Op(op) = self {
op.is_swap()
} else {
false
}
}
}

/// Auxiliary data of Execution step
#[derive(Clone, Debug)]
pub enum StepAuxiliaryData {
/// Auxiliary data of Copy To Memory
CopyToMemory {
/// Source start address
src_addr: u64,
/// Destination address
dst_addr: u64,
/// Bytes left
bytes_left: u64,
/// Source end address
src_addr_end: u64,
/// If from transaction
from_tx: bool,
/// Selectors
selectors: Vec<u8>,
},
}

/// An execution step of the EVM.
#[derive(Debug)]
#[derive(Clone, Debug)]
pub struct ExecStep {
/// The opcode ID
pub op: OpcodeId,
/// Execution state
pub exec_state: ExecState,
/// Program Counter
pub pc: ProgramCounter,
/// Stack size
Expand All @@ -129,6 +191,8 @@ pub struct ExecStep {
pub bus_mapping_instance: Vec<OperationRef>,
/// Error generated by this step
pub error: Option<ExecError>,
/// Step auxiliary data
pub aux_data: Option<StepAuxiliaryData>,
}

impl ExecStep {
Expand All @@ -140,7 +204,7 @@ impl ExecStep {
swc: usize, // State Write Counter
) -> Self {
ExecStep {
op: step.op,
exec_state: ExecState::Op(step.op),
pc: step.pc,
stack_size: step.stack.0.len(),
memory_size: step.memory.0.len(),
Expand All @@ -151,14 +215,15 @@ impl ExecStep {
swc,
bus_mapping_instance: Vec::new(),
error: None,
aux_data: None,
}
}
}

impl Default for ExecStep {
fn default() -> Self {
Self {
op: OpcodeId::INVALID(0),
exec_state: ExecState::Op(OpcodeId::INVALID(0)),
pc: ProgramCounter(0),
stack_size: 0,
memory_size: 0,
Expand All @@ -169,6 +234,7 @@ impl Default for ExecStep {
swc: 0,
bus_mapping_instance: Vec::new(),
error: None,
aux_data: None,
}
}
}
Expand Down Expand Up @@ -368,12 +434,12 @@ impl Call {
/// Context of a [`Call`].
#[derive(Debug, Default)]
pub struct CallContext {
// Index of call
index: usize,
/// Index of call
pub index: usize,
/// State Write Counter tracks the count of state write operations in the
/// call. When a subcall in this call succeeds, the `swc` increases by the
/// number of successful state writes in the subcall.
swc: usize,
pub swc: usize,
}

/// A reversion group is the collection of calls and the operations which are
Expand Down Expand Up @@ -466,6 +532,11 @@ impl TransactionContext {
self.is_last_tx
}

/// Return the calls in this transaction.
pub fn calls(&self) -> &[CallContext] {
&self.calls
}

/// Return the index of the current call (the last call in the call stack).
fn call_index(&self) -> Result<usize, Error> {
self.calls
Expand Down Expand Up @@ -541,8 +612,10 @@ pub struct Transaction {
/// Value
pub value: Word,
/// Input / Call Data
pub input: Vec<u8>, // call_data
pub input: Vec<u8>,
/// Calls made in the transaction
calls: Vec<Call>,
/// Execution steps
steps: Vec<ExecStep>,
}

Expand Down Expand Up @@ -654,21 +727,30 @@ pub struct CircuitInputStateRef<'a> {
pub tx: &'a mut Transaction,
/// Transaction Context
pub tx_ctx: &'a mut TransactionContext,
/// Step
pub step: &'a mut ExecStep,
}

impl<'a> CircuitInputStateRef<'a> {
/// Create a new step from a `GethExecStep`
pub fn new_step(&self, geth_step: &GethExecStep) -> Result<ExecStep, Error> {
let call_ctx = self.tx_ctx.call_ctx()?;
Ok(ExecStep::new(
geth_step,
call_ctx.index,
self.block_ctx.rwc,
call_ctx.swc,
))
}

/// Push an [`Operation`] into the [`OperationContainer`] with the next
/// [`RWCounter`] and then adds a reference to the stored operation
/// ([`OperationRef`]) inside the bus-mapping instance of the current
/// [`ExecStep`]. Then increase the block_ctx [`RWCounter`] by one.
pub fn push_op<T: Op>(&mut self, rw: RW, op: T) {
pub fn push_op<T: Op>(&mut self, step: &mut ExecStep, rw: RW, op: T) {
let op_ref =
self.block
.container
.insert(Operation::new(self.block_ctx.rwc.inc_pre(), rw, op));
self.step.bus_mapping_instance.push(op_ref);
step.bus_mapping_instance.push(op_ref);
}

/// Push an [`Operation`] with reversible to be true into the
Expand All @@ -679,13 +761,18 @@ impl<'a> CircuitInputStateRef<'a> {
/// This method should be used in `Opcode::gen_associated_ops` instead of
/// `push_op` when the operation is `RW::WRITE` and it can be reverted (for
/// example, a write `StorageOp`).
pub fn push_op_reversible<T: Op>(&mut self, rw: RW, op: T) -> Result<(), Error> {
pub fn push_op_reversible<T: Op>(
&mut self,
step: &mut ExecStep,
rw: RW,
op: T,
) -> Result<(), Error> {
let op_ref = self.block.container.insert(Operation::new_reversible(
self.block_ctx.rwc.inc_pre(),
rw,
op,
));
self.step.bus_mapping_instance.push(op_ref);
step.bus_mapping_instance.push(op_ref);

// Increase state_write_counter
self.call_ctx_mut()?.swc += 1;
Expand All @@ -710,12 +797,13 @@ impl<'a> CircuitInputStateRef<'a> {
/// [`RWCounter`] by one.
pub fn push_memory_op(
&mut self,
step: &mut ExecStep,
rw: RW,
address: MemoryAddress,
value: u8,
) -> Result<(), Error> {
let call_id = self.call()?.call_id;
self.push_op(rw, MemoryOp::new(call_id, address, value));
self.push_op(step, rw, MemoryOp::new(call_id, address, value));
Ok(())
}

Expand All @@ -726,12 +814,13 @@ impl<'a> CircuitInputStateRef<'a> {
/// [`RWCounter`] by one.
pub fn push_stack_op(
&mut self,
step: &mut ExecStep,
rw: RW,
address: StackAddress,
value: Word,
) -> Result<(), Error> {
let call_id = self.call()?.call_id;
self.push_op(rw, StackOp::new(call_id, address, value));
self.push_op(step, rw, StackOp::new(call_id, address, value));
Ok(())
}

Expand Down Expand Up @@ -1254,7 +1343,6 @@ impl<'a> CircuitInputBuilder {
&'a mut self,
tx: &'a mut Transaction,
tx_ctx: &'a mut TransactionContext,
step: &'a mut ExecStep,
) -> CircuitInputStateRef {
CircuitInputStateRef {
sdb: &mut self.sdb,
Expand All @@ -1263,7 +1351,6 @@ impl<'a> CircuitInputBuilder {
block_ctx: &mut self.block_ctx,
tx,
tx_ctx,
step,
}
}

Expand Down Expand Up @@ -1342,50 +1429,25 @@ impl<'a> CircuitInputBuilder {
// - execution_state: BeginTx
// - op: None
// Generate BeginTx step
let mut step = ExecStep {
gas_left: Gas(tx.gas),
rwc: self.block_ctx.rwc,
..Default::default()
};
gen_begin_tx_ops(&mut self.state_ref(&mut tx, &mut tx_ctx, &mut step))?;
tx.steps.push(step);
let begin_tx_step = gen_begin_tx_ops(&mut self.state_ref(&mut tx, &mut tx_ctx))?;
tx.steps.push(begin_tx_step);

for (index, geth_step) in geth_trace.struct_logs.iter().enumerate() {
let call_ctx = tx_ctx.call_ctx()?;
let mut step =
ExecStep::new(geth_step, call_ctx.index, self.block_ctx.rwc, call_ctx.swc);
let mut state_ref = self.state_ref(&mut tx, &mut tx_ctx, &mut step);

gen_associated_ops(
let mut state_ref = self.state_ref(&mut tx, &mut tx_ctx);
let exec_steps = gen_associated_ops(
&geth_step.op,
&mut state_ref,
&geth_trace.struct_logs[index..],
)?;

tx.steps.push(step);
tx.steps.extend(exec_steps);
}

// TODO: Move into gen_associated_steps with
// - execution_state: EndTx
// - op: None
// Generate EndTx step
let step_prev = tx
.steps
.last()
.expect("steps should have at least one BeginTx step");
let mut step = ExecStep {
gas_left: Gas(step_prev.gas_left.0 - step_prev.gas_cost.0),
rwc: self.block_ctx.rwc,
// For tx without code execution
swc: if let Some(call_ctx) = tx_ctx.calls.last() {
call_ctx.swc
} else {
0
},
..Default::default()
};
gen_end_tx_ops(&mut self.state_ref(&mut tx, &mut tx_ctx, &mut step))?;
tx.steps.push(step);
let end_tx_step = gen_end_tx_ops(&mut self.state_ref(&mut tx, &mut tx_ctx))?;
tx.steps.push(end_tx_step);

self.block.txs.push(tx);
self.sdb.clear_access_list_and_refund();
Expand Down Expand Up @@ -1898,8 +1960,7 @@ mod tracer_tests {
}

fn state_ref(&mut self) -> CircuitInputStateRef {
self.builder
.state_ref(&mut self.tx, &mut self.tx_ctx, &mut self.step)
self.builder.state_ref(&mut self.tx, &mut self.tx_ctx)
}
}

Expand Down Expand Up @@ -1970,7 +2031,7 @@ mod tracer_tests {
STOP
};
let block =
mock::new_single_tx_trace_code_gas(&code, Gas(1_000_000_000_000_000u64)).unwrap();
mock::new_single_tx_trace_code_gas(&code, Gas(1_000_000_000_000_000u64), None).unwrap();
let struct_logs = &block.geth_traces[0].struct_logs;

// get last CALL
Expand Down Expand Up @@ -2830,7 +2891,7 @@ mod tracer_tests {
PUSH1(0x1)
PUSH1(0x2)
};
let block = mock::new_single_tx_trace_code_gas(&code, Gas(21004)).unwrap();
let block = mock::new_single_tx_trace_code_gas(&code, Gas(21004), None).unwrap();
let struct_logs = &block.geth_traces[0].struct_logs;

assert_eq!(struct_logs[1].error, Some(GETH_ERR_OUT_OF_GAS.to_string()));
Expand Down
Loading