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
29 changes: 29 additions & 0 deletions bus-mapping/src/circuit_input_builder/input_state_ref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use eth_types::{
Address, GethExecStep, ToAddress, ToBigEndian, Word, H256,
};
use ethers_core::utils::{get_contract_address, get_create2_address};
use std::cmp::max;

/// Reference to the internal state of the CircuitInputBuilder in a particular
/// [`ExecStep`].
Expand Down Expand Up @@ -994,4 +995,32 @@ impl<'a> CircuitInputStateRef<'a> {

Ok(None)
}

/// Expand memory of the call context when entering a new call context in
/// case the call arguments or return arguments go beyond the call
/// context current memory.
pub(crate) fn call_expand_memory(
&mut self,
args_offset: usize,
args_length: usize,
ret_offset: usize,
ret_length: usize,
) -> Result<(), Error> {
let call_ctx = self.call_ctx_mut()?;
let args_minimal = if args_length != 0 {
args_offset + args_length
} else {
0
};
let ret_minimal = if ret_length != 0 {
ret_offset + ret_length
} else {
0
};
if args_minimal != 0 || ret_minimal != 0 {
let minimal_length = max(args_minimal, ret_minimal);
call_ctx.memory.extend_at_least(minimal_length);
}
Ok(())
}
}
18 changes: 18 additions & 0 deletions bus-mapping/src/evm/opcodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,24 @@ fn dummy_gen_call_ops(
let geth_step = &geth_steps[0];
let mut exec_step = state.new_step(geth_step)?;

let (args_offset, args_length, ret_offset, ret_length) = {
// CALLCODE (gas, addr, value, argsOffset, argsLength, retOffset, retLength)
// DELEGATECALL(gas, addr, argsOffset, argsLength, retOffset, retLength)
// STATICCALL (gas, addr, argsOffset, argsLength, retOffset, retLength)
let pos = match geth_step.op {
OpcodeId::CALLCODE => (3, 4, 5, 6),
OpcodeId::DELEGATECALL | OpcodeId::STATICCALL => (2, 3, 4, 5),
_ => unreachable!("opcode is not of call type"),
};
(
geth_step.stack.nth_last(pos.0)?.as_usize(),
geth_step.stack.nth_last(pos.1)?.as_usize(),
geth_step.stack.nth_last(pos.2)?.as_usize(),
geth_step.stack.nth_last(pos.3)?.as_usize(),
)
};
state.call_expand_memory(args_offset, args_length, ret_offset, ret_length)?;

let tx_id = state.tx_ctx.id();
let call = state.parse_call(geth_step)?;

Expand Down
17 changes: 1 addition & 16 deletions bus-mapping/src/evm/opcodes/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ use eth_types::{
};
use keccak256::EMPTY_HASH;
use log::warn;
use std::cmp::max;

/// Placeholder structure used to implement [`Opcode`] trait over it
/// corresponding to the `OpcodeId::CALL` `OpcodeId`.
Expand All @@ -34,21 +33,7 @@ impl Opcode for Call {
let ret_length = geth_step.stack.nth_last(6)?.as_usize();

// we need to keep the memory until parse_call complete
let call_ctx = state.call_ctx_mut()?;
let args_minimal = if args_length != 0 {
args_offset + args_length
} else {
0
};
let ret_minimal = if ret_length != 0 {
ret_offset + ret_length
} else {
0
};
if args_minimal != 0 || ret_minimal != 0 {
let minimal_length = max(args_minimal, ret_minimal);
call_ctx.memory.extend_at_least(minimal_length);
}
state.call_expand_memory(args_offset, args_length, ret_offset, ret_length)?;

let tx_id = state.tx_ctx.id();
let call = state.parse_call(geth_step)?;
Expand Down
4 changes: 2 additions & 2 deletions zkevm-circuits/src/evm_circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use witness::Block;
pub struct EvmCircuit<F> {
fixed_table: [Column<Fixed>; 4],
byte_table: [Column<Fixed>; 1],
execution: Box<ExecutionConfig<F>>,
pub(crate) execution: Box<ExecutionConfig<F>>,
}

impl<F: Field> EvmCircuit<F> {
Expand Down Expand Up @@ -388,7 +388,7 @@ mod evm_circuit_stats {

/// This function prints to stdout a table with all the implemented states
/// and their responsible opcodes with the following stats:
/// - height: number of rows used by the execution state
/// - height: number of rows in the EVM circuit used by the execution state
/// - gas: gas value used for the opcode execution
/// - height/gas: ratio between circuit cost and gas cost
///
Expand Down
131 changes: 131 additions & 0 deletions zkevm-circuits/src/state_circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -370,3 +370,134 @@ fn queries<F: Field>(meta: &mut VirtualCells<'_, F>, c: &StateCircuitConfig<F>)
* meta.query_advice(first_different_limb.bits[3], Rotation::cur()),
}
}

#[cfg(test)]
mod state_circuit_stats {
use crate::evm_circuit::step::ExecutionState;
use crate::evm_circuit::test::TestCircuit;
use bus_mapping::{circuit_input_builder::ExecState, mock::BlockData};
use eth_types::{bytecode, evm_types::OpcodeId, geth_types::GethData, Address};
use halo2_proofs::halo2curves::bn256::Fr;
use halo2_proofs::plonk::{Circuit, ConstraintSystem};
use mock::{eth, test_ctx::TestContext, MOCK_ACCOUNTS};
use strum::IntoEnumIterator;

/// This function prints to stdout a table with all the implemented states
/// and their responsible opcodes with the following stats:
/// - height: number of rows in the State circuit used by the execution
/// state
/// - gas: gas value used for the opcode execution
/// - height/gas: ratio between circuit cost and gas cost
///
/// Run with:
/// `cargo test -p zkevm-circuits --release get_state_states_stats --
/// --nocapture --ignored`
#[ignore]
#[test]
pub fn get_state_states_stats() {
// Get the list of implemented execution states by configuring the EVM Circuit
// and querying the step height for each possible execution state (only those
// implemented will return a Some value).
let mut meta = ConstraintSystem::<Fr>::default();
let circuit = TestCircuit::configure(&mut meta);

let mut implemented_states = Vec::new();
for state in ExecutionState::iter() {
let height = circuit.evm_circuit.execution.get_step_height_option(state);
if height.is_some() {
implemented_states.push(state);
}
}

let mut stats = Vec::new();
for state in implemented_states {
for opcode in state.responsible_opcodes() {
let mut code = bytecode! {
PUSH2(0x100)
MLOAD // Expand memory a bit
PUSH2(0x00)
EXTCODESIZE // Warm up 0x0 address
PUSH2(0x8000)
PUSH2(0x00)
PUSH2(0x10)
PUSH2(0x20)
PUSH2(0x30)
};
// Make sure that opcodes that take an address as argument use addres 0x0, which
// will exist in the test.
match opcode {
OpcodeId::BALANCE
| OpcodeId::EXTCODESIZE
| OpcodeId::EXTCODECOPY
| OpcodeId::SELFDESTRUCT
| OpcodeId::EXTCODEHASH => code.append(&bytecode! {
PUSH2(0x40)
PUSH2(0x00)
}),
OpcodeId::CALL
| OpcodeId::CALLCODE
| OpcodeId::DELEGATECALL
| OpcodeId::STATICCALL => code.append(&bytecode! {
PUSH2(0x00)
PUSH2(0x50)
}),
_ => code.append(&bytecode! {
PUSH2(0x40)
PUSH2(0x50)
}),
};
code.write_op(opcode);
code.write_op(OpcodeId::STOP);
let block: GethData = TestContext::<3, 1>::new(
None,
|accs| {
accs[0]
.address(MOCK_ACCOUNTS[0])
.balance(eth(10))
.code(code.clone());
accs[1].address(MOCK_ACCOUNTS[1]).balance(eth(10));
accs[2].address(Address::zero()).balance(eth(10)).code(code);
},
|mut txs, accs| {
txs[0]
.from(accs[1].address)
.to(accs[0].address)
.input(vec![1, 2, 3, 4, 5, 6, 7].into());
},
|block, _tx| block.number(0xcafeu64),
)
.unwrap()
.into();
let mut builder =
BlockData::new_from_geth_data(block.clone()).new_circuit_input_builder();
builder
.handle_block(&block.eth_block, &block.geth_traces)
.unwrap();
let step_index = 1 + 11; // 1 is for the BeginTx, 11 for the bytecode opcodes.
let step = &builder.block.txs[0].steps()[step_index];
let step_next = &builder.block.txs[0].steps()[step_index + 1];
assert_eq!(ExecState::Op(opcode), step.exec_state);
let h = step_next.rwc.0 - step.rwc.0;

let gas_cost = block.geth_traces[0].struct_logs[11].gas_cost.0;
stats.push((state, opcode, h, gas_cost));
}
}

println!(
"| {: <14} | {: <14} | {: <2} | {: >6} | {: <5} |",
"state", "opcode", "h", "g", "h/g"
);
println!("| --- | --- | ---| --- | --- |");
for (state, opcode, height, gas_cost) in stats {
println!(
"| {: <14?} | {: <14?} | {: >2} | {: >6} | {: >1.3} |",
state,
opcode,
height,
gas_cost,
height as f64 / gas_cost as f64
);
}
}
}