diff --git a/.gitignore b/.gitignore index b76f169f15..02a0fa2839 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target *.png +.DS_Store diff --git a/bus-mapping/src/circuit_input_builder.rs b/bus-mapping/src/circuit_input_builder.rs index 45e9ae5867..1999483a98 100644 --- a/bus-mapping/src/circuit_input_builder.rs +++ b/bus-mapping/src/circuit_input_builder.rs @@ -9,6 +9,7 @@ use crate::evm::{ StackAddress, }; use crate::exec_trace::OperationRef; +use crate::external_tracer::BlockConstants; use crate::geth_errors::*; use crate::operation::container::OperationContainer; use crate::operation::{MemoryOp, Op, Operation, StackOp, RW}; @@ -175,8 +176,10 @@ impl BlockContext { /// Circuit Input related to a block. #[derive(Debug)] pub struct Block { - /// Constants associated to this block and the chain. - pub constants: ChainConstants, + /// Constants associated to the chain. + pub chain_const: ChainConstants, + /// Constants associated to the block. + pub block_const: BlockConstants, /// Container of operations done in this block. pub container: OperationContainer, txs: Vec, @@ -187,10 +190,12 @@ impl Block { /// Create a new block. pub fn new( _eth_block: ð_types::Block, - constants: ChainConstants, + chain_const: ChainConstants, + block_const: BlockConstants, ) -> Self { Self { - constants, + chain_const, + block_const, container: OperationContainer::new(), txs: Vec::new(), code: HashMap::new(), @@ -821,12 +826,13 @@ impl<'a> CircuitInputBuilder { sdb: StateDB, code_db: CodeDB, eth_block: ð_types::Block, - constants: ChainConstants, + chain_const: ChainConstants, + block_const: BlockConstants, ) -> Self { Self { sdb, code_db, - block: Block::new(eth_block, constants), + block: Block::new(eth_block, chain_const, block_const), block_ctx: BlockContext::new(), } } @@ -1210,7 +1216,6 @@ impl BuilderClient

{ /// Create a new BuilderClient pub async fn new(client: GethClient

) -> Result { let constants = ChainConstants { - coinbase: client.get_coinbase().await?, chain_id: client.get_chain_id().await?, }; Ok(Self { @@ -1327,6 +1332,10 @@ impl BuilderClient

{ code_db, eth_block, self.constants.clone(), + BlockConstants::from_eth_block( + eth_block, + &Word::from(self.constants.chain_id), + ), ); for (tx_index, tx) in eth_block.transactions.iter().enumerate() { let geth_trace = &geth_traces[tx_index]; diff --git a/bus-mapping/src/eth_types.rs b/bus-mapping/src/eth_types.rs index 3d375e5097..6b93865119 100644 --- a/bus-mapping/src/eth_types.rs +++ b/bus-mapping/src/eth_types.rs @@ -146,8 +146,6 @@ impl ToScalar for Address { /// Chain specific constants #[derive(Debug, Clone)] pub struct ChainConstants { - /// Coinbase - pub coinbase: Address, /// Chain ID pub chain_id: u64, } diff --git a/bus-mapping/src/evm/opcodes.rs b/bus-mapping/src/evm/opcodes.rs index 53ce24d0c3..077ad5099f 100644 --- a/bus-mapping/src/evm/opcodes.rs +++ b/bus-mapping/src/evm/opcodes.rs @@ -1,4 +1,5 @@ //! Definition of each opcode of the EVM. +mod coinbase; mod dup; pub mod ids; mod jump; @@ -22,6 +23,7 @@ use ids::OpcodeId; use log::warn; use self::push::Push; +use coinbase::Coinbase; use dup::Dup; use jump::Jump; use jumpdest::Jumpdest; @@ -110,7 +112,7 @@ impl OpcodeId { // OpcodeId::RETURNDATACOPY => {}, // OpcodeId::EXTCODEHASH => {}, // OpcodeId::BLOCKHASH => {}, - // OpcodeId::COINBASE => {}, + OpcodeId::COINBASE => Coinbase::gen_associated_ops, // OpcodeId::TIMESTAMP => {}, // OpcodeId::NUMBER => {}, // OpcodeId::DIFFICULTY => {}, diff --git a/bus-mapping/src/evm/opcodes/coinbase.rs b/bus-mapping/src/evm/opcodes/coinbase.rs new file mode 100644 index 0000000000..0d8a89a531 --- /dev/null +++ b/bus-mapping/src/evm/opcodes/coinbase.rs @@ -0,0 +1,91 @@ +use super::Opcode; +use crate::circuit_input_builder::CircuitInputStateRef; +use crate::eth_types::GethExecStep; +use crate::{operation::RW, Error}; + +/// Placeholder structure used to implement [`Opcode`] trait over it +/// corresponding to the [`OpcodeId::PC`](crate::evm::OpcodeId::PC) `OpcodeId`. +#[derive(Debug, Copy, Clone)] +pub(crate) struct Coinbase; + +impl Opcode for Coinbase { + fn gen_associated_ops( + state: &mut CircuitInputStateRef, + steps: &[GethExecStep], + ) -> Result<(), Error> { + let step = &steps[0]; + // Get value result from next step and do stack write + let value = steps[1].stack.last()?; + state.push_stack_op( + RW::WRITE, + step.stack.last_filled().map(|a| a - 1), + value, + ); + + Ok(()) + } +} + +#[cfg(test)] +mod coinbase_tests { + use super::*; + use crate::{ + bytecode, + circuit_input_builder::{ExecStep, TransactionContext}, + eth_types::ToWord, + evm::StackAddress, + mock, + }; + use pretty_assertions::assert_eq; + + #[test] + fn coinbase_opcode_impl() -> Result<(), Error> { + let code = bytecode! { + #[start] + COINBASE + STOP + }; + + // Get the execution steps from the external tracer + let block = + mock::BlockData::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(()) + } +} diff --git a/bus-mapping/src/external_tracer.rs b/bus-mapping/src/external_tracer.rs index 4c9068e1af..dbb7b0aadb 100644 --- a/bus-mapping/src/external_tracer.rs +++ b/bus-mapping/src/external_tracer.rs @@ -11,26 +11,30 @@ use serde::Serialize; /// chain to be used as setup for the external tracer. #[derive(Debug, Clone, PartialEq, Eq, Serialize)] pub struct BlockConstants { - hash: Hash, - coinbase: Address, - timestamp: Word, - number: U64, - difficulty: Word, - gas_limit: Word, - chain_id: Word, - base_fee: Word, + /// hash + pub hash: Hash, + /// coinbase + pub coinbase: Address, + /// time + pub timestamp: Word, + /// number + pub number: U64, + /// difficulty + pub difficulty: Word, + /// gas limit + pub gas_limit: Word, + /// chain id + pub chain_id: Word, + /// base fee + pub base_fee: Word, } impl BlockConstants { /// Generate a BlockConstants from an ethereum block, useful for testing. - pub fn from_eth_block( - block: &Block, - chain_id: &Word, - &coinbase: &Address, - ) -> Self { + pub fn from_eth_block(block: &Block, chain_id: &Word) -> Self { Self { hash: block.hash.unwrap(), - coinbase, + coinbase: block.author, timestamp: block.timestamp, number: block.number.unwrap(), difficulty: block.difficulty, diff --git a/bus-mapping/src/lib.rs b/bus-mapping/src/lib.rs index f5c012005a..03911c2d1b 100644 --- a/bus-mapping/src/lib.rs +++ b/bus-mapping/src/lib.rs @@ -56,6 +56,7 @@ //! self, Address, Word, Hash, U64, GethExecTrace, GethExecStep, ChainConstants //! }; //! use bus_mapping::circuit_input_builder::CircuitInputBuilder; +//! use bus_mapping::external_tracer::BlockConstants; //! use pairing::arithmetic::FieldExt; //! //! let input_trace = r#" @@ -107,7 +108,6 @@ //! "#; //! //! let ctants = ChainConstants{ -//! coinbase: Address::zero(), //! chain_id: 0, //! }; //! @@ -117,8 +117,16 @@ //! let mut sdb = StateDB::new(); //! sdb.set_account(&Address::zero(), state_db::Account::zero()); //! -//! let mut builder = -//! CircuitInputBuilder::new(sdb, CodeDB::new(), ð_block, ctants); +//! let mut builder = CircuitInputBuilder::new( +//! sdb, +//! CodeDB::new(), +//! ð_block, +//! ctants.clone(), +//! BlockConstants::from_eth_block( +//! ð_block, +//! &Word::from(ctants.chain_id), +//! ), +//! ); //! //! let geth_steps: Vec = serde_json::from_str(input_trace).unwrap(); //! let geth_trace = GethExecTrace { diff --git a/bus-mapping/src/mock.rs b/bus-mapping/src/mock.rs index 90d7fee7df..5e4d465d92 100644 --- a/bus-mapping/src/mock.rs +++ b/bus-mapping/src/mock.rs @@ -22,10 +22,7 @@ lazy_static! { /// Generate a new mock chain constants, useful for tests. pub fn new_chain_constants() -> eth_types::ChainConstants { - ChainConstants { - chain_id: CHAIN_ID, - coinbase: *COINBASE, - } + ChainConstants { chain_id: CHAIN_ID } } /// Generate a new mock block with preloaded data, useful for tests. @@ -94,7 +91,9 @@ pub struct BlockData { /// Transaction from geth pub eth_tx: eth_types::Transaction, /// Constants - pub ctants: ChainConstants, + pub c_constant: ChainConstants, + /// Constants + pub b_constant: BlockConstants, /// Execution Trace from geth pub geth_trace: eth_types::GethExecTrace, } @@ -112,18 +111,17 @@ impl BlockData { let eth_block = new_block(); let mut eth_tx = new_tx(ð_block); eth_tx.gas = Word::from(gas.0); - let ctants = new_chain_constants(); - let block_ctants = BlockConstants::from_eth_block( + let c_constant = new_chain_constants(); + let b_constant = BlockConstants::from_eth_block( ð_block, - &Word::from(ctants.chain_id), - &ctants.coinbase, + &Word::from(c_constant.chain_id), ); let tracer_tx = external_tracer::Transaction::from_eth_tx(ð_tx); let geth_trace = eth_types::GethExecTrace { gas: Gas(eth_tx.gas.as_u64()), failed: false, struct_logs: external_tracer::trace( - &block_ctants, + &b_constant, &tracer_tx, accounts, )? @@ -148,7 +146,8 @@ impl BlockData { code_db, eth_block, eth_tx, - ctants, + c_constant, + b_constant, geth_trace, }) } @@ -220,7 +219,11 @@ impl BlockData { ) -> Self { let eth_block = new_block(); let eth_tx = new_tx(ð_block); - let ctants = new_chain_constants(); + let c_constant = new_chain_constants(); + let b_constant = BlockConstants::from_eth_block( + ð_block, + &Word::from(c_constant.chain_id), + ); let geth_trace = eth_types::GethExecTrace { gas: Gas(eth_tx.gas.as_u64()), failed: false, @@ -233,7 +236,8 @@ impl BlockData { code_db, eth_block, eth_tx, - ctants, + c_constant, + b_constant, geth_trace, } } @@ -245,7 +249,8 @@ impl BlockData { self.sdb.clone(), self.code_db.clone(), &self.eth_block, - self.ctants.clone(), + self.c_constant.clone(), + self.b_constant.clone(), ) } } diff --git a/circuit-benchmarks/src/evm_circuit.rs b/circuit-benchmarks/src/evm_circuit.rs index 5994c913fc..e974cb0616 100644 --- a/circuit-benchmarks/src/evm_circuit.rs +++ b/circuit-benchmarks/src/evm_circuit.rs @@ -24,6 +24,7 @@ impl Circuit for TestCircuit { let tx_table = [(); 4].map(|_| meta.advice_column()); let rw_table = [(); 8].map(|_| meta.advice_column()); let bytecode_table = [(); 4].map(|_| meta.advice_column()); + let block_table = [(); 3].map(|_| meta.advice_column()); let randomness = meta.instance_column(); EvmCircuit::configure( @@ -32,6 +33,7 @@ impl Circuit for TestCircuit { tx_table, rw_table, bytecode_table, + block_table, ) } diff --git a/integration-tests/src/lib.rs b/integration-tests/src/lib.rs index 6fae4635e1..e6c9c53ebb 100644 --- a/integration-tests/src/lib.rs +++ b/integration-tests/src/lib.rs @@ -64,7 +64,6 @@ pub fn get_provider() -> Provider { pub async fn get_chain_constants() -> ChainConstants { let client = get_client(); ChainConstants { - coinbase: client.get_coinbase().await.unwrap(), chain_id: client.get_chain_id().await.unwrap(), } } diff --git a/integration-tests/tests/circuits.rs b/integration-tests/tests/circuits.rs index bb75e7298a..8d4c30b6ee 100644 --- a/integration-tests/tests/circuits.rs +++ b/integration-tests/tests/circuits.rs @@ -186,6 +186,7 @@ mod test_evm_circuit { let tx_table = [(); 4].map(|_| meta.advice_column()); let rw_table = [(); 8].map(|_| meta.advice_column()); let bytecode_table = [(); 4].map(|_| meta.advice_column()); + let block_table = [(); 3].map(|_| meta.advice_column()); let randomness = meta.instance_column(); Self::Config { @@ -198,6 +199,7 @@ mod test_evm_circuit { tx_table, rw_table, bytecode_table, + block_table, ), } } diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index 816a60c28e..2f5dc0aa2b 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -24,17 +24,19 @@ pub struct EvmCircuit { impl EvmCircuit { /// Configure EvmCircuit - pub fn configure( + pub fn configure( meta: &mut ConstraintSystem, randomness: Column, tx_table: TxTable, rw_table: RwTable, bytecode_table: BytecodeTable, + block_table: BlockTable, ) -> Self where TxTable: LookupTable, RwTable: LookupTable, BytecodeTable: LookupTable, + BlockTable: LookupTable, { let fixed_table = [(); 4].map(|_| meta.fixed_column()); @@ -45,6 +47,7 @@ impl EvmCircuit { tx_table, rw_table, bytecode_table, + block_table, ); Self { @@ -110,7 +113,7 @@ mod test { use crate::evm_circuit::{ param::STEP_HEIGHT, table::FixedTableTag, - witness::{Block, Bytecode, Rw, Transaction}, + witness::{Block, BlockContext, Bytecode, Rw, Transaction}, EvmCircuit, }; use bus_mapping::eth_types::Word; @@ -155,6 +158,7 @@ mod test { tx_table: [Column; 4], rw_table: [Column; 8], bytecode_table: [Column; 4], + block_table: [Column; 3], evm_circuit: EvmCircuit, } @@ -169,7 +173,7 @@ mod test { || "tx table", |mut region| { let mut offset = 0; - for column in self.rw_table { + for column in self.tx_table { region.assign_advice( || "tx table all-zero row", column, @@ -277,6 +281,44 @@ mod test { }, ) } + + fn load_blocks( + &self, + layouter: &mut impl Layouter, + block: &BlockContext, + randomness: F, + ) -> Result<(), Error> { + layouter.assign_region( + || "block table", + |mut region| { + let mut offset = 0; + for column in self.block_table { + region.assign_advice( + || "block table all-zero row", + column, + offset, + || Ok(F::zero()), + )?; + } + offset += 1; + + for row in block.table_assignments(randomness) { + for (column, value) in self.block_table.iter().zip(row) + { + region.assign_advice( + || format!("block table row {}", offset), + *column, + offset, + || Ok(value), + )?; + } + offset += 1; + } + + Ok(()) + }, + ) + } } #[derive(Default)] @@ -309,18 +351,21 @@ mod test { let tx_table = [(); 4].map(|_| meta.advice_column()); let rw_table = [(); 8].map(|_| meta.advice_column()); let bytecode_table = [(); 4].map(|_| meta.advice_column()); + let block_table = [(); 3].map(|_| meta.advice_column()); let randomness = meta.instance_column(); Self::Config { tx_table, rw_table, bytecode_table, + block_table, evm_circuit: EvmCircuit::configure( meta, randomness, tx_table, rw_table, bytecode_table, + block_table, ), } } @@ -349,6 +394,11 @@ mod test { &self.block.bytecodes, self.block.randomness, )?; + config.load_blocks( + &mut layouter, + &self.block.context, + self.block.randomness, + )?; config .evm_circuit .assign_block_exact(&mut layouter, &self.block) diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index d86dfbf371..545b0a87bc 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -22,6 +22,7 @@ mod add; mod begin_tx; mod bitwise; mod byte; +mod coinbase; mod comparator; mod dup; mod error_oog_pure_memory; @@ -37,10 +38,12 @@ mod push; mod signextend; mod stop; mod swap; + use add::AddGadget; use begin_tx::BeginTxGadget; use bitwise::BitwiseGadget; use byte::ByteGadget; +use coinbase::CoinbaseGadget; use comparator::ComparatorGadget; use dup::DupGadget; use error_oog_pure_memory::ErrorOOGPureMemoryGadget; @@ -100,21 +103,24 @@ pub(crate) struct ExecutionConfig { stop_gadget: StopGadget, swap_gadget: SwapGadget, msize_gadget: MsizeGadget, + coinbase_gadget: CoinbaseGadget, } impl ExecutionConfig { - pub(crate) fn configure( + pub(crate) fn configure( meta: &mut ConstraintSystem, randomness: Column, fixed_table: [Column; 4], tx_table: TxTable, rw_table: RwTable, bytecode_table: BytecodeTable, + block_table: BlockTable, ) -> Self where TxTable: LookupTable, RwTable: LookupTable, BytecodeTable: LookupTable, + BlockTable: LookupTable, { let q_step = meta.complex_selector(); let q_step_first = meta.complex_selector(); @@ -234,6 +240,7 @@ impl ExecutionConfig { stop_gadget: configure_gadget!(), swap_gadget: configure_gadget!(), msize_gadget: configure_gadget!(), + coinbase_gadget: configure_gadget!(), step: step_curr, presets_map, }; @@ -245,6 +252,7 @@ impl ExecutionConfig { tx_table, rw_table, bytecode_table, + block_table, independent_lookups, ); @@ -300,18 +308,21 @@ impl ExecutionConfig { gadget } - fn configure_lookup( + #[allow(clippy::too_many_arguments)] + fn configure_lookup( meta: &mut ConstraintSystem, q_step: Selector, fixed_table: [Column; 4], tx_table: TxTable, rw_table: RwTable, bytecode_table: BytecodeTable, + block_table: BlockTable, independent_lookups: Vec>>, ) where TxTable: LookupTable, RwTable: LookupTable, BytecodeTable: LookupTable, + BlockTable: LookupTable, { // Because one and only one ExecutionState is enabled at a step, we then // know only one of independent_lookups will be enabled at a step, so we @@ -372,6 +383,7 @@ impl ExecutionConfig { lookup!(Table::Tx, tx_table); lookup!(Table::Rw, rw_table); lookup!(Table::Bytecode, bytecode_table); + lookup!(Table::Block, block_table); } pub fn assign_block( @@ -500,6 +512,7 @@ impl ExecutionConfig { ExecutionState::PUSH => assign_exec_step!(self.push_gadget), ExecutionState::DUP => assign_exec_step!(self.dup_gadget), ExecutionState::SWAP => assign_exec_step!(self.swap_gadget), + ExecutionState::COINBASE => assign_exec_step!(self.coinbase_gadget), ExecutionState::ErrorOutOfGasPureMemory => { assign_exec_step!(self.error_oog_pure_memory_gadget) } diff --git a/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs b/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs index e2a31497be..eb5b0ba752 100644 --- a/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs +++ b/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs @@ -548,6 +548,7 @@ mod test { ] .concat(), bytecodes: vec![bytecode], + ..Default::default() }; assert_eq!(run_test_circuit_incomplete_fixed_table(block), Ok(())); } diff --git a/zkevm-circuits/src/evm_circuit/execution/coinbase.rs b/zkevm-circuits/src/evm_circuit/execution/coinbase.rs new file mode 100644 index 0000000000..b9bacb3c27 --- /dev/null +++ b/zkevm-circuits/src/evm_circuit/execution/coinbase.rs @@ -0,0 +1,111 @@ +use crate::{ + evm_circuit::{ + execution::ExecutionGadget, + step::ExecutionState, + table::BlockContextFieldTag, + util::{ + common_gadget::SameContextGadget, + constraint_builder::{ + ConstraintBuilder, StepStateTransition, Transition::Delta, + }, + Cell, Word, + }, + witness::{Block, Call, ExecStep, Transaction}, + }, + util::Expr, +}; +use bus_mapping::eth_types::ToLittleEndian; +use halo2::{arithmetic::FieldExt, circuit::Region, plonk::Error}; + +#[derive(Clone, Debug)] +pub(crate) struct CoinbaseGadget { + same_context: SameContextGadget, + coinbase_address: Cell, +} + +impl ExecutionGadget for CoinbaseGadget { + const NAME: &'static str = "COINBASE"; + + const EXECUTION_STATE: ExecutionState = ExecutionState::COINBASE; + + fn configure(cb: &mut ConstraintBuilder) -> Self { + let coinbase_address = cb.query_cell(); + + // Push the value to the stack + cb.stack_push(coinbase_address.expr()); + + // Lookup block table with coinbase address + cb.block_lookup( + BlockContextFieldTag::Coinbase.expr(), + None, + coinbase_address.expr(), + ); + + // State transition + let opcode = cb.query_cell(); + let step_state_transition = StepStateTransition { + rw_counter: Delta(1.expr()), + program_counter: Delta(1.expr()), + stack_pointer: Delta((-1).expr()), + ..Default::default() + }; + let same_context = SameContextGadget::construct( + cb, + opcode, + step_state_transition, + None, + ); + + Self { + same_context, + coinbase_address, + } + } + + fn assign_exec_step( + &self, + region: &mut Region<'_, F>, + offset: usize, + block: &Block, + _: &Transaction, + _: &Call, + step: &ExecStep, + ) -> Result<(), Error> { + self.same_context.assign_exec_step(region, offset, step)?; + + let coinbase = block.rws[step.rw_indices[0]].stack_value(); + + self.coinbase_address.assign( + region, + offset, + Some(Word::random_linear_combine( + coinbase.to_le_bytes(), + block.randomness, + )), + )?; + + Ok(()) + } +} + +#[cfg(test)] +mod test { + use crate::evm_circuit::{ + test::run_test_circuit_incomplete_fixed_table, witness, + }; + use bus_mapping::bytecode; + + fn test_ok() { + let bytecode = bytecode! { + #[start] + COINBASE + STOP + }; + let block = witness::build_block_from_trace_code_at_start(&bytecode); + assert_eq!(run_test_circuit_incomplete_fixed_table(block), Ok(())); + } + #[test] + fn coinbase_gadget_test() { + test_ok(); + } +} diff --git a/zkevm-circuits/src/evm_circuit/table.rs b/zkevm-circuits/src/evm_circuit/table.rs index 139168a914..825155fd88 100644 --- a/zkevm-circuits/src/evm_circuit/table.rs +++ b/zkevm-circuits/src/evm_circuit/table.rs @@ -129,6 +129,17 @@ pub enum TxContextFieldTag { CallData, } +#[derive(Clone, Copy, Debug)] +pub enum BlockContextFieldTag { + Coinbase = 1, + GasLimit, + BlockNumber, + Time, + Difficulty, + BaseFee, + BlockHash, +} + #[derive(Clone, Copy, Debug)] pub enum RwTableTag { TxAccessListAccount = 1, @@ -181,6 +192,7 @@ impl_expr!(TxContextFieldTag); impl_expr!(RwTableTag); impl_expr!(AccountFieldTag); impl_expr!(CallContextFieldTag); +impl_expr!(BlockContextFieldTag); #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub(crate) enum Table { @@ -188,6 +200,7 @@ pub(crate) enum Table { Tx, Rw, Bytecode, + Block, } #[derive(Clone, Debug)] @@ -239,6 +252,16 @@ pub(crate) enum Lookup { /// data portion of PUSH* operations. is_code: Expression, }, + /// Lookup to block table, which contains constants of this block. + Block { + /// Tag to specify which field to read. + field_tag: Expression, + /// Stores the block number only when field_tag is BlockHash, otherwise + /// should be set to 0. + number: Expression, + /// Value of the field. + value: Expression, + }, /// Conditional lookup enabled by the first element. Conditional(Expression, Box>), } @@ -254,6 +277,7 @@ impl Lookup { Self::Tx { .. } => Table::Tx, Self::Rw { .. } => Table::Rw, Self::Bytecode { .. } => Table::Bytecode, + Self::Block { .. } => Table::Block, Self::Conditional(_, lookup) => lookup.table(), } } @@ -297,6 +321,13 @@ impl Lookup { is_code.clone(), ] } + Self::Block { + field_tag, + number, + value, + } => { + vec![field_tag.clone(), number.clone(), value.clone()] + } Self::Conditional(condition, lookup) => lookup .input_exprs() .into_iter() diff --git a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs index 42721ee07f..97fe8b6bb6 100644 --- a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs +++ b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs @@ -437,6 +437,20 @@ impl<'a, F: FieldExt> ConstraintBuilder<'a, F> { }); } + // block + pub(crate) fn block_lookup( + &mut self, + tag: Expression, + number: Option>, + val: Expression, + ) { + self.add_lookup(Lookup::Block { + field_tag: tag, + number: number.unwrap_or_else(|| 0.expr()), + value: val, + }); + } + // Rw /// Add a Lookup::Rw without increasing the rw_counter_offset, which is diff --git a/zkevm-circuits/src/evm_circuit/witness.rs b/zkevm-circuits/src/evm_circuit/witness.rs index 5849e737fb..d4024a7fe8 100644 --- a/zkevm-circuits/src/evm_circuit/witness.rs +++ b/zkevm-circuits/src/evm_circuit/witness.rs @@ -3,12 +3,13 @@ use crate::evm_circuit::{ param::STACK_CAPACITY, step::ExecutionState, table::{ - AccountFieldTag, CallContextFieldTag, RwTableTag, TxContextFieldTag, + AccountFieldTag, BlockContextFieldTag, CallContextFieldTag, RwTableTag, + TxContextFieldTag, }, util::RandomLinearCombination, }; use bus_mapping::{ - eth_types::{Address, ToLittleEndian, ToScalar, Word}, + eth_types::{Address, ToLittleEndian, ToScalar, ToWord, Word}, evm::OpcodeId, }; use halo2::arithmetic::{BaseExt, FieldExt}; @@ -23,6 +24,81 @@ pub struct Block { pub txs: Vec>, pub rws: Vec, pub bytecodes: Vec, + pub context: BlockContext, +} + +#[derive(Debug, Default)] +pub struct BlockContext { + pub coinbase: Address, // u160 + pub gas_limit: u64, + pub block_number: F, + pub time: u64, + pub difficulty: Word, + pub base_fee: Word, + pub previous_block_hashes: Vec, +} + +impl BlockContext { + pub fn table_assignments(&self, randomness: F) -> Vec<[F; 3]> { + [ + vec![ + [ + F::from(BlockContextFieldTag::Coinbase as u64), + F::zero(), + RandomLinearCombination::random_linear_combine( + self.coinbase.to_word().to_le_bytes(), + randomness, + ), + ], + [ + F::from(BlockContextFieldTag::GasLimit as u64), + F::zero(), + F::from(self.gas_limit), + ], + [ + F::from(BlockContextFieldTag::BlockNumber as u64), + F::zero(), + self.block_number, + ], + [ + F::from(BlockContextFieldTag::Time as u64), + F::zero(), + F::from(self.time), + ], + [ + F::from(BlockContextFieldTag::Difficulty as u64), + F::zero(), + RandomLinearCombination::random_linear_combine( + self.difficulty.to_le_bytes(), + randomness, + ), + ], + [ + F::from(BlockContextFieldTag::BaseFee as u64), + F::zero(), + RandomLinearCombination::random_linear_combine( + self.base_fee.to_le_bytes(), + randomness, + ), + ], + ], + self.previous_block_hashes + .iter() + .enumerate() + .map(|(idx, hash)| { + [ + F::from(BlockContextFieldTag::BlockHash as u64), + self.block_number - F::from((idx + 1) as u64), + RandomLinearCombination::random_linear_combine( + hash.to_le_bytes(), + randomness, + ), + ] + }) + .collect(), + ] + .concat() + } } #[derive(Debug, Default)] @@ -454,6 +530,7 @@ impl From<&bus_mapping::circuit_input_builder::ExecStep> for ExecutionState { OpcodeId::JUMPI => ExecutionState::JUMPI, OpcodeId::PC => ExecutionState::PC, OpcodeId::MSIZE => ExecutionState::MSIZE, + OpcodeId::COINBASE => ExecutionState::COINBASE, _ => unimplemented!("unimplemented opcode {:?}", step.op), } } @@ -543,8 +620,15 @@ pub fn block_convert( let mut storage_ops = b.container.sorted_storage(); storage_ops.sort_by_key(|s| usize::from(s.rwc())); + // converting to block context + let context = BlockContext { + coinbase: b.block_const.coinbase, + ..Default::default() + }; + let mut block = Block { randomness, + context, txs: b .txs() .iter()