Skip to content

Commit

Permalink
opt(assignment): speedup EndBlock padding (privacy-scaling-exploratio…
Browse files Browse the repository at this point in the history
…ns#1018)

* opt(assignment): speedup EndBlock padding

* fix

* update comments
  • Loading branch information
lispc authored Jan 6, 2023
1 parent afb6f27 commit 69c0a4a
Show file tree
Hide file tree
Showing 4 changed files with 212 additions and 85 deletions.
28 changes: 24 additions & 4 deletions zkevm-circuits/src/evm_circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -504,19 +504,39 @@ pub mod test {
mod evm_circuit_stats {
use super::test::*;
use super::*;
use crate::evm_circuit::step::ExecutionState;
use crate::{evm_circuit::step::ExecutionState, witness::block_convert};
use bus_mapping::{circuit_input_builder::CircuitsParams, mock::BlockData};
use eth_types::{bytecode, evm_types::OpcodeId, geth_types::GethData};
use halo2_proofs::halo2curves::bn256::Fr;
use halo2_proofs::plonk::ConstraintSystem;
use mock::test_ctx::{helpers::*, TestContext};
use strum::IntoEnumIterator;

#[test]
pub fn empty_evm_circuit() {
fn get_empty_witness_block() -> Block<Fr> {
let block: GethData = TestContext::<0, 0>::new(None, |_| {}, |_, _| {}, |b, _| b)
.unwrap()
.into();
run_test_circuit_geth_data_default::<Fr>(block).unwrap();
let mut builder =
BlockData::new_from_geth_data_with_params(block.clone(), CircuitsParams::default())
.new_circuit_input_builder();
builder
.handle_block(&block.eth_block, &block.geth_traces)
.unwrap();

block_convert(&builder.block, &builder.code_db).unwrap()
}

#[test]
pub fn empty_evm_circuit_no_padding() {
let block = get_empty_witness_block();
run_test_circuit(block).unwrap();
}

#[test]
pub fn empty_evm_circuit_with_padding() {
let mut block = get_empty_witness_block();
block.evm_circuit_pad_to = (1 << 18) - 100;
run_test_circuit(block).unwrap();
}

/// This function prints to stdout a table with all the implemented states
Expand Down
223 changes: 148 additions & 75 deletions zkevm-circuits/src/evm_circuit/execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -741,6 +741,43 @@ impl<F: Field> ExecutionConfig<F> {
}
}

/// Assign columns related to step counter
fn assign_q_step(
&self,
region: &mut Region<'_, F>,
offset: usize,
height: usize,
) -> Result<(), Error> {
for idx in 0..height {
let offset = offset + idx;
self.q_usable.enable(region, offset)?;
region.assign_advice(
|| "step selector",
self.q_step,
offset,
|| Value::known(if idx == 0 { F::one() } else { F::zero() }),
)?;
let value = if idx == 0 {
F::zero()
} else {
F::from((height - idx) as u64)
};
region.assign_advice(
|| "step height",
self.num_rows_until_next_step,
offset,
|| Value::known(value),
)?;
region.assign_advice(
|| "step height inv",
self.num_rows_inv,
offset,
|| Value::known(value.invert().unwrap_or(F::zero())),
)?;
}
Ok(())
}

/// Assign block
/// When exact is enabled, assign exact steps in block without padding for
/// unit test purpose
Expand All @@ -758,6 +795,7 @@ impl<F: Field> ExecutionConfig<F> {
layouter.assign_region(
|| "Execution step",
|mut region| {
log::info!("start execution step assignment");
let mut offset = 0;

self.q_step_first.enable(&mut region, offset)?;
Expand All @@ -774,46 +812,24 @@ impl<F: Field> ExecutionConfig<F> {
let mut steps = block
.txs
.iter()
.flat_map(|tx| tx.steps.iter().map(move |step| (tx, step)))
.flat_map(|tx| {
tx.steps
.iter()
.map(move |step| (tx, &tx.calls[step.call_index], step))
})
.chain(std::iter::once((&dummy_tx, &last_call, end_block_not_last)))
.peekable();

let evm_rows = block.evm_circuit_pad_to;
let exact = evm_rows == 0;
let no_padding = evm_rows == 0;

let mut no_next_step = false;
let mut get_next = |cur_state: ExecutionState, offset: &usize| match steps.next() {
Some((transaction, step)) => Ok(Some((
transaction,
&transaction.calls[step.call_index],
step,
))),
None => {
if no_next_step {
return Ok(None);
}

let mut block_step = end_block_not_last;
let cur_state_height = self.get_step_height(cur_state);
if !exact && offset + cur_state_height >= evm_rows {
log::error!(
"evm circuit larger than evm_rows: {} >= {}",
offset + cur_state_height,
evm_rows
);
return Err(Error::Synthesis);
}
if exact || evm_rows - (offset + cur_state_height) == 1 {
block_step = end_block_last;
no_next_step = true;
}

Ok(Some((&dummy_tx, &last_call, block_step)))
// part1: assign real steps
loop {
let (transaction, call, step) = steps.next().expect("should not be empty");
let next = steps.peek();
if next.is_none() {
break;
}
};

let mut next = get_next(ExecutionState::BeginTx, &offset)?;
while let Some((transaction, call, step)) = next {
next = get_next(step.execution_state, &offset)?;
let height = self.get_step_height(step.execution_state);

// Assign the step witness
Expand All @@ -825,55 +841,73 @@ impl<F: Field> ExecutionConfig<F> {
call,
step,
height,
next,
next.copied(),
power_of_randomness,
)?;

// q_step logic
for idx in 0..height {
let offset = offset + idx;
self.q_usable.enable(&mut region, offset)?;
region.assign_advice(
|| "step selector",
self.q_step,
offset,
|| Value::known(if idx == 0 { F::one() } else { F::zero() }),
)?;
let value = if idx == 0 {
F::zero()
} else {
F::from((height - idx) as u64)
};
region.assign_advice(
|| "step height",
self.num_rows_until_next_step,
offset,
|| Value::known(value),
)?;
region.assign_advice(
|| "step height inv",
self.num_rows_inv,
offset,
|| Value::known(value.invert().unwrap_or(F::zero())),
)?;
}
self.assign_q_step(&mut region, offset, height)?;

offset += height;
}

if !exact && offset > evm_rows {
// part2: assign non-last EndBlock steps when padding needed
if !no_padding {
if offset >= evm_rows {
log::error!(
"evm circuit offset larger than padding: {} > {}",
offset,
evm_rows
);
return Err(Error::Synthesis);
}
let height = self.get_step_height(ExecutionState::EndBlock);
debug_assert_eq!(height, 1);
let last_row = evm_rows - 1;
log::trace!(
"assign non-last EndBlock in range [{},{})",
offset,
last_row
);
self.assign_same_exec_step_in_range(
&mut region,
offset,
last_row,
block,
&dummy_tx,
&last_call,
end_block_not_last,
height,
power_of_randomness,
)?;

if next.is_none() {
// Assert that EndBlock height is 1
debug_assert_eq!(height, 1);
for row_idx in offset..last_row {
self.assign_q_step(&mut region, row_idx, height)?;
}
offset = last_row;
}

// part3: assign the last EndBlock at offset `evm_rows - 1`
let height = self.get_step_height(ExecutionState::EndBlock);
debug_assert_eq!(height, 1);
log::trace!("assign last EndBlock at offset {}", offset);
self.assign_exec_step(
&mut region,
offset,
block,
&dummy_tx,
&last_call,
end_block_last,
height,
None,
power_of_randomness,
)?;
self.assign_q_step(&mut region, offset, height)?;
// enable q_step_last
self.q_step_last.enable(&mut region, offset)?;
offset += height;

// part4:
// These are still referenced (but not used) in next rows
region.assign_advice(
|| "step height",
Expand All @@ -888,15 +922,51 @@ impl<F: Field> ExecutionConfig<F> {
|| Value::known(F::zero()),
)?;

const END_BLOCK_HEIGHT: usize = 1;
self.q_step_last
.enable(&mut region, offset - END_BLOCK_HEIGHT)?;

log::info!("finish execution step assignment");
Ok(())
},
)
}

#[allow(clippy::too_many_arguments)]
fn assign_same_exec_step_in_range(
&self,
region: &mut Region<'_, F>,
offset_begin: usize,
offset_end: usize,
block: &Block<F>,
transaction: &Transaction,
call: &Call,
step: &ExecStep,
height: usize,
power_of_randomness: [F; 31],
) -> Result<(), Error> {
if offset_end <= offset_begin {
return Ok(());
}
assert_eq!(height, 1);
assert!(step.rw_indices.is_empty());
assert!(matches!(step.execution_state, ExecutionState::EndBlock));

// Disable access to next step deliberately for "repeatable" step
let region = &mut CachedRegion::<'_, '_, F>::new(
region,
power_of_randomness,
self.advices.to_vec(),
1,
offset_begin,
);
self.assign_exec_step_int(region, offset_begin, block, transaction, call, step)?;

region.replicate_assignment_for_range(
|| format!("repeat {:?} rows", step.execution_state),
offset_begin + 1,
offset_end,
)?;

Ok(())
}

#[allow(clippy::too_many_arguments)]
fn assign_exec_step(
&self,
Expand Down Expand Up @@ -925,9 +995,8 @@ impl<F: Field> ExecutionConfig<F> {
let region = &mut CachedRegion::<'_, '_, F>::new(
region,
power_of_randomness,
STEP_WIDTH,
self.advices.to_vec(),
MAX_STEP_HEIGHT * 3,
self.advices[0].index(),
offset,
);

Expand Down Expand Up @@ -1127,8 +1196,12 @@ impl<F: Field> ExecutionConfig<F> {

// enable with `RUST_LOG=debug`
if log::log_enabled!(log::Level::Debug) {
// expensive function call
Self::check_rw_lookup(&assigned_stored_expressions, step, block);
let is_padding_step = matches!(step.execution_state, ExecutionState::EndBlock)
&& step.rw_indices.is_empty();
if !is_padding_step {
// expensive function call
Self::check_rw_lookup(&assigned_stored_expressions, step, block);
}
}
Ok(())
}
Expand Down
Loading

0 comments on commit 69c0a4a

Please sign in to comment.