Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
* Implement EndBlock

- Implement EndBlock following updated spec at privacy-scaling-explorations/zkevm-specs#286
- Update BeginTx so that it forbids Transaction.CallerAccount != 0 (in
  order to support Padding Txs as described in
  privacy-scaling-explorations/zkevm-specs#273 (comment))
- Update state transition constraints to support empty block.
- Extend ConstraintBuilder with:
    - Constraint location (step_first, step_last, step_any)
    - Add copy cells and fixed column to store constants to be accessed
      via copy constraints at fixed offsets.

Update from spec update

Constraint rw_counter to be 1 in the first step unconditionally when
q_step_first is enabled, instead of via BeginTx, so that the constrain
also applies on an empty block (where there's no BeginTx)

Apply suggestion by @han0110

Apply suggestions by @ChihChengLiang

Fix clippy complaints

Apply suggestions by @han0110

Do some cleaning

* Rename functions to reduce conflicts

Co-authored-by: Rohit Narurkar <[email protected]>
  • Loading branch information
ed255 and roynalnaruto authored Oct 28, 2022
1 parent 5549e71 commit f0c1cf8
Show file tree
Hide file tree
Showing 44 changed files with 1,279 additions and 502 deletions.
88 changes: 85 additions & 3 deletions bus-mapping/src/circuit_input_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ mod transaction;
use self::access::gen_state_access_trace;
use crate::error::Error;
use crate::evm::opcodes::{gen_associated_ops, gen_begin_tx_ops, gen_end_tx_ops};
use crate::operation::{CallContextField, RW};
use crate::operation::{CallContextField, Operation, RWCounter, StartOp, RW};
use crate::rpc::GethClient;
use crate::state_db::{self, CodeDB, StateDB};
pub use access::{Access, AccessSet, AccessValue, CodeSource};
Expand All @@ -29,6 +29,29 @@ use itertools::Itertools;
use std::collections::HashMap;
pub use transaction::{Transaction, TransactionContext};

/// Circuit Setup Parameters
#[derive(Debug, Clone)]
pub struct CircuitsParams {
/// Maximum number of rw operations in the state circuit (RwTable length /
/// nummber of rows). This must be at least the number of rw operations
/// + 1, in order to allocate at least a Start row.
pub max_rws: usize,
// TODO: evm_rows: Maximum number of rows in the EVM Circuit
/// Maximum number of txs in the Tx Circuit
pub max_txs: usize,
// TODO: max_calldata: Maximum number of bytes from all txs calldata in the Tx Circuit
}

impl Default for CircuitsParams {
/// Default values for most of the unit tests of the Circuit Parameters
fn default() -> Self {
CircuitsParams {
max_rws: 256,
max_txs: 1,
}
}
}

/// Builder to generate a complete circuit input from data gathered from a geth
/// instance. This structure is the centre of the crate and is intended to be
/// the only entry point to it. The `CircuitInputBuilder` works in several
Expand Down Expand Up @@ -145,9 +168,58 @@ impl<'a> CircuitInputBuilder {
self.handle_tx(tx, geth_trace, tx_index + 1 == eth_block.transactions.len())?;
}
self.set_value_ops_call_context_rwc_eor();
self.set_end_block();
Ok(())
}

fn set_end_block(&mut self) {
let max_rws = self.block.circuits_params.max_rws;
let mut end_block_not_last = self.block.block_steps.end_block_not_last.clone();
let mut end_block_last = self.block.block_steps.end_block_last.clone();
end_block_not_last.rwc = self.block_ctx.rwc;
end_block_last.rwc = self.block_ctx.rwc;

let mut dummy_tx = Transaction::dummy();
let mut dummy_tx_ctx = TransactionContext::default();
let mut state = self.state_ref(&mut dummy_tx, &mut dummy_tx_ctx);

if let Some(call_id) = state.block.txs.last().map(|tx| tx.calls[0].call_id) {
state.call_context_read(
&mut end_block_last,
call_id,
CallContextField::TxId,
Word::from(state.block.txs.len() as u64),
);
}

let mut push_op = |step: &mut ExecStep, rwc: RWCounter, rw: RW, op: StartOp| {
let op_ref = state.block.container.insert(Operation::new(rwc, rw, op));
step.bus_mapping_instance.push(op_ref);
};

let total_rws = state.block_ctx.rwc.0 - 1;
// We need at least 1 extra Start row
#[allow(clippy::int_plus_one)]
{
assert!(
total_rws + 1 <= max_rws,
"total_rws + 1 <= max_rws, total_rws={}, max_rws={}",
total_rws,
max_rws
);
}
push_op(&mut end_block_last, RWCounter(1), RW::READ, StartOp {});
push_op(
&mut end_block_last,
RWCounter(max_rws - total_rws),
RW::READ,
StartOp {},
);

self.block.block_steps.end_block_not_last = end_block_not_last;
self.block.block_steps.end_block_last = end_block_last;
}

/// Handle a transaction with its corresponding execution trace to generate
/// all the associated operations. Each operation is registered in
/// `self.block.container`, and each step stores the
Expand Down Expand Up @@ -277,18 +349,23 @@ pub struct BuilderClient<P: JsonRpcClient> {
cli: GethClient<P>,
chain_id: Word,
history_hashes: Vec<Word>,
circuits_params: CircuitsParams,
}

impl<P: JsonRpcClient> BuilderClient<P> {
/// Create a new BuilderClient
pub async fn new(client: GethClient<P>) -> Result<Self, Error> {
pub async fn new(
client: GethClient<P>,
circuits_params: CircuitsParams,
) -> Result<Self, Error> {
let chain_id = client.get_chain_id().await?;

Ok(Self {
cli: client,
chain_id: chain_id.into(),
// TODO: Get history hashes
history_hashes: Vec::new(),
circuits_params,
})
}

Expand Down Expand Up @@ -402,7 +479,12 @@ impl<P: JsonRpcClient> BuilderClient<P> {
eth_block: &EthBlock,
geth_traces: &[eth_types::GethExecTrace],
) -> Result<CircuitInputBuilder, Error> {
let block = Block::new(self.chain_id, self.history_hashes.clone(), eth_block)?;
let block = Block::new(
self.chain_id,
self.history_hashes.clone(),
eth_block,
self.circuits_params.clone(),
)?;
let mut builder = CircuitInputBuilder::new(sdb, code_db, block);
builder.handle_block(eth_block, geth_traces)?;
Ok(builder)
Expand Down
28 changes: 27 additions & 1 deletion bus-mapping/src/circuit_input_builder/block.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Block-related utility module
use super::{transaction::Transaction, CopyEvent};
use super::{execution::ExecState, transaction::Transaction, CircuitsParams, CopyEvent, ExecStep};
use crate::{
operation::{OperationContainer, RWCounter},
Error,
Expand Down Expand Up @@ -39,6 +39,16 @@ impl BlockContext {
}
}

/// Block-wise execution steps that don't belong to any Transaction.
#[derive(Debug)]
pub struct BlockSteps {
/// EndBlock step that is repeated after the last transaction and before
/// reaching the last EVM row.
pub end_block_not_last: ExecStep,
/// Last EndBlock step that appears in the last EVM row.
pub end_block_last: ExecStep,
}

/// Circuit Input related to a block.
#[derive(Debug)]
pub struct Block {
Expand All @@ -63,11 +73,15 @@ pub struct Block {
pub container: OperationContainer,
/// Transactions contained in the block
pub txs: Vec<Transaction>,
/// Block-wise steps
pub block_steps: BlockSteps,
/// Copy events in this block.
pub copy_events: Vec<CopyEvent>,
/// Inputs to the SHA3 opcode
pub sha3_inputs: Vec<Vec<u8>>,
code: HashMap<Hash, Vec<u8>>,
/// Circuits Setup Paramteres
pub circuits_params: CircuitsParams,
}

impl Block {
Expand All @@ -76,6 +90,7 @@ impl Block {
chain_id: Word,
history_hashes: Vec<Word>,
eth_block: &eth_types::Block<TX>,
circuits_params: CircuitsParams,
) -> Result<Self, Error> {
if eth_block.base_fee_per_gas.is_none() {
// FIXME: resolve this once we have proper EIP-1559 support
Expand All @@ -101,9 +116,20 @@ impl Block {
base_fee: eth_block.base_fee_per_gas.unwrap_or_default(),
container: OperationContainer::new(),
txs: Vec::new(),
block_steps: BlockSteps {
end_block_not_last: ExecStep {
exec_state: ExecState::EndBlock,
..ExecStep::default()
},
end_block_last: ExecStep {
exec_state: ExecState::EndBlock,
..ExecStep::default()
},
},
copy_events: Vec::new(),
code: HashMap::new(),
sha3_inputs: Vec::new(),
circuits_params,
})
}

Expand Down
2 changes: 2 additions & 0 deletions bus-mapping/src/circuit_input_builder/execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ pub enum ExecState {
BeginTx,
/// Virtual step End Tx
EndTx,
/// Virtual step End Block
EndBlock,
}

impl ExecState {
Expand Down
24 changes: 22 additions & 2 deletions bus-mapping/src/circuit_input_builder/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use crate::{

use super::{call::ReversionGroup, Call, CallContext, CallKind, CodeSource, ExecStep};

#[derive(Debug)]
#[derive(Debug, Default)]
/// Context of a [`Transaction`] which can mutate in an [`ExecStep`].
pub struct TransactionContext {
/// Unique identifier of transaction of the block. The value is `index + 1`.
Expand Down Expand Up @@ -198,7 +198,7 @@ pub struct Transaction {
/// Signature
pub signature: Signature,
/// Calls made in the transaction
calls: Vec<Call>,
pub(crate) calls: Vec<Call>,
/// Execution steps
steps: Vec<ExecStep>,
}
Expand All @@ -222,6 +222,26 @@ impl From<&Transaction> for geth_types::Transaction {
}

impl Transaction {
/// Create a dummy Transaction with zero values
pub fn dummy() -> Self {
Self {
nonce: 0,
gas: 0,
gas_price: Word::zero(),
from: Address::zero(),
to: Address::zero(),
value: Word::zero(),
input: Vec::new(),
signature: Signature {
r: Word::zero(),
s: Word::zero(),
v: 0,
},
calls: Vec::new(),
steps: Vec::new(),
}
}

/// Create a new Self.
pub fn new(
call_id: usize,
Expand Down
Loading

0 comments on commit f0c1cf8

Please sign in to comment.