diff --git a/bus-mapping/src/circuit_input_builder.rs b/bus-mapping/src/circuit_input_builder.rs index 2bc6318a52..6f83e6d13e 100644 --- a/bus-mapping/src/circuit_input_builder.rs +++ b/bus-mapping/src/circuit_input_builder.rs @@ -367,7 +367,7 @@ impl CircuitInputBuilder { // Compute subcircuits parameters let c_params = { let max_txs = eth_block.transactions.len(); - let max_bytecode = self.code_db.0.values().fold(0, |acc, a| acc + a.len() + 1); + let max_bytecode = self.code_db.num_rows_required_for_bytecode_table(); let max_calldata = eth_block .transactions @@ -437,8 +437,8 @@ pub fn keccak_inputs(block: &Block, code_db: &CodeDB) -> Result>, Er let txs: Vec = block.txs.iter().map(|tx| tx.deref().clone()).collect(); keccak_inputs.extend_from_slice(&keccak_inputs_tx_circuit(&txs, block.chain_id.as_u64())?); // Bytecode Circuit - for bytecode in code_db.0.values() { - keccak_inputs.push(bytecode.clone()); + for bytecode in code_db.clone().into_iter() { + keccak_inputs.push(bytecode.code()); } // EVM Circuit keccak_inputs.extend_from_slice(&block.sha3_inputs); @@ -575,7 +575,7 @@ pub fn build_state_code_db( ) } - let mut code_db = CodeDB::new(); + let mut code_db = CodeDB::default(); for (_address, code) in codes { code_db.insert(code.clone()); } diff --git a/bus-mapping/src/circuit_input_builder/input_state_ref.rs b/bus-mapping/src/circuit_input_builder/input_state_ref.rs index 5b110b9d1a..6ddc4d0f3b 100644 --- a/bus-mapping/src/circuit_input_builder/input_state_ref.rs +++ b/bus-mapping/src/circuit_input_builder/input_state_ref.rs @@ -614,9 +614,8 @@ impl<'a> CircuitInputStateRef<'a> { /// Fetch and return code for the given code hash from the code DB. pub fn code(&self, code_hash: H256) -> Result, Error> { self.code_db - .0 - .get(&code_hash) - .cloned() + .get_from_h256(&code_hash) + .map(|bytecode| bytecode.code()) .ok_or(Error::CodeNotFound(code_hash)) } @@ -1448,12 +1447,7 @@ impl<'a> CircuitInputStateRef<'a> { let mut copy_steps = Vec::with_capacity(bytes_left as usize); for idx in 0..bytes_left { let addr = src_addr.checked_add(idx).unwrap_or(src_addr_end); - let step = if addr < src_addr_end { - let code = bytecode.code.get(addr as usize).unwrap(); - (code.value, code.is_code) - } else { - (0, false) - }; + let step = bytecode.get(addr as usize).unwrap_or_default(); copy_steps.push(step); self.memory_write(exec_step, (dst_addr + idx).into(), step.0)?; } diff --git a/bus-mapping/src/circuit_input_builder/tracer_tests.rs b/bus-mapping/src/circuit_input_builder/tracer_tests.rs index 69c0aaf006..6012b0e3c4 100644 --- a/bus-mapping/src/circuit_input_builder/tracer_tests.rs +++ b/bus-mapping/src/circuit_input_builder/tracer_tests.rs @@ -364,17 +364,8 @@ fn tracer_err_address_collision() { }; let mut code_b = Bytecode::default(); - // pad code_creator to multiple of 32 bytes - let len = code_creator.to_vec().len(); - let code_creator: Vec = code_creator - .to_vec() - .iter() - .cloned() - .chain(0u8..((32 - len % 32) as u8)) - .collect(); - for (index, word) in code_creator.chunks(32).enumerate() { - code_b.op_mstore(index * 32, Word::from_big_endian(word)); - } + code_b.store_code_to_mem(&code_creator); + let len = code_creator.codesize(); let code_b_end = bytecode! { PUSH3(0x123456) // salt PUSH1(len) // length @@ -486,17 +477,8 @@ fn tracer_create_collision_free() { }; let mut code_b = Bytecode::default(); - // pad code_creator to multiple of 32 bytes - let len = code_creator.to_vec().len(); - let code_creator: Vec = code_creator - .to_vec() - .iter() - .cloned() - .chain(0u8..((32 - len % 32) as u8)) - .collect(); - for (index, word) in code_creator.chunks(32).enumerate() { - code_b.op_mstore(index * 32, Word::from_big_endian(word)); - } + code_b.store_code_to_mem(&code_creator); + let len = code_creator.codesize(); let code_b_end = bytecode! { PUSH1(len) // length PUSH1(0x00) // offset @@ -621,19 +603,9 @@ fn tracer_err_code_store_out_of_gas() { }; let mut code_b = Bytecode::default(); - // pad code_creator to multiple of 32 bytes - let len = code_creator.to_vec().len(); - let code_creator: Vec = code_creator - .to_vec() - .iter() - .cloned() - .chain(0..(32 - len % 32) as u8) - .collect(); - for (index, word) in code_creator.chunks(32).enumerate() { - code_b.op_mstore(index * 32, Word::from_big_endian(word)); - } + code_b.store_code_to_mem(&code_creator); let code_b_end = bytecode! { - PUSH32(len) // length + PUSH32(code_creator.codesize()) // length PUSH1(0x00) // offset PUSH1(0x00) // value CREATE @@ -769,17 +741,8 @@ fn tracer_err_invalid_code_for_create_opcode() { }; let mut code_b = Bytecode::default(); - // pad code_creator to multiple of 32 bytes - let len = code_creator.to_vec().len(); - let code_creator: Vec = code_creator - .to_vec() - .iter() - .cloned() - .chain(0u8..((32 - len % 32) as u8)) - .collect(); - for (index, word) in code_creator.chunks(32).enumerate() { - code_b.op_mstore(index * 32, Word::from_big_endian(word)); - } + code_b.store_code_to_mem(&code_creator); + let len = code_creator.codesize(); let code_b_end = bytecode! { PUSH1(len) // length PUSH1(0x00) // offset @@ -921,19 +884,9 @@ fn tracer_err_max_code_size_exceeded() { }; let mut code_b = Bytecode::default(); - // pad code_creator to multiple of 32 bytes - let len = code_creator.to_vec().len(); - let code_creator: Vec = code_creator - .to_vec() - .iter() - .cloned() - .chain(0u8..((32 - len % 32) as u8)) - .collect(); - for (index, word) in code_creator.chunks(32).enumerate() { - code_b.op_mstore(index * 32, Word::from_big_endian(word)); - } + code_b.store_code_to_mem(&code_creator); let code_b_end = bytecode! { - PUSH32(len) // length + PUSH32(code_creator.codesize()) // length PUSH1(0x00) // offset PUSH1(0x00) // value CREATE @@ -1059,19 +1012,9 @@ fn tracer_create_stop() { }; let mut code_b = Bytecode::default(); - // pad code_creator to multiple of 32 bytes - let len = code_creator.to_vec().len(); - let code_creator: Vec = code_creator - .to_vec() - .iter() - .cloned() - .chain(0u8..((32 - len % 32) as u8)) - .collect(); - for (index, word) in code_creator.chunks(32).enumerate() { - code_b.op_mstore(index * 32, Word::from_big_endian(word)); - } + code_b.store_code_to_mem(&code_creator); let code_b_end = bytecode! { - PUSH1(len) // length + PUSH1(code_creator.codesize()) // length PUSH1(0x00) // offset PUSH1(0x00) // value CREATE @@ -1760,20 +1703,10 @@ fn create2_address() { }; let mut code_b = Bytecode::default(); - // pad code_creator to multiple of 32 bytes - let len = code_creator.to_vec().len(); - let code_creator: Vec = code_creator - .to_vec() - .iter() - .cloned() - .chain(0u8..((32 - len % 32) as u8)) - .collect(); - for (index, word) in code_creator.chunks(32).enumerate() { - code_b.op_mstore(index * 32, Word::from_big_endian(word)); - } + code_b.store_code_to_mem(&code_creator); let code_b_end = bytecode! { PUSH3(0x123456) // salt - PUSH1(len) // length + PUSH1(code_creator.codesize()) // length PUSH1(0x00) // offset PUSH1(0x00) // value CREATE2 @@ -1853,17 +1786,8 @@ fn create_address() { }; let mut code_b = Bytecode::default(); - // pad code_creator to multiple of 32 bytes - let len = code_creator.to_vec().len(); - let code_creator: Vec = code_creator - .to_vec() - .iter() - .cloned() - .chain(0u8..((32 - len % 32) as u8)) - .collect(); - for (index, word) in code_creator.chunks(32).enumerate() { - code_b.op_mstore(index * 32, Word::from_big_endian(word)); - } + code_b.store_code_to_mem(&code_creator); + let len = code_creator.codesize(); // We do CREATE 2 times to use a nonce != 0 in the second one. let code_b_end = bytecode! { PUSH1(len) // length @@ -2168,19 +2092,10 @@ fn test_gen_access_trace_create_push_call_stack() { }; let mut code_b = Bytecode::default(); - // pad code_creator to multiple of 32 bytes - let len = code_creator.to_vec().len(); - let code_creator: Vec = code_creator - .to_vec() - .iter() - .cloned() - .chain(0u8..((32 - len % 32) as u8)) - .collect(); - for (index, word) in code_creator.chunks(32).enumerate() { - code_b.op_mstore(index * 32, Word::from_big_endian(word)); - } + code_b.store_code_to_mem(&code_creator); + let code_b_end = bytecode! { - PUSH1(len) // length + PUSH1(code_creator.codesize()) // length PUSH1(0x00) // offset PUSH1(0x00) // value CREATE diff --git a/bus-mapping/src/evm/opcodes/codecopy.rs b/bus-mapping/src/evm/opcodes/codecopy.rs index 937f06c04f..5a6e844f6e 100644 --- a/bus-mapping/src/evm/opcodes/codecopy.rs +++ b/bus-mapping/src/evm/opcodes/codecopy.rs @@ -77,7 +77,7 @@ fn gen_copy_event( let code_hash = state.call()?.code_hash; let bytecode: Bytecode = state.code(code_hash)?.into(); - let code_size = bytecode.code.len() as u64; + let code_size = bytecode.codesize() as u64; // Get low Uint64 of offset to generate copy steps. Since offset could be // Uint64 overflow if length is zero. @@ -129,7 +129,6 @@ mod codecopy_tests { circuit_input_builder::{CopyDataType, ExecState, NumberOrHash}, mock::BlockData, operation::{MemoryOp, StackOp, RW}, - state_db::CodeDB, }; #[test] @@ -202,11 +201,7 @@ mod codecopy_tests { MemoryOp::new( 1, MemoryAddress::from(dst_offset + idx), - if code_offset + idx < code.to_vec().len() { - code.to_vec()[code_offset + idx] - } else { - 0 - }, + code.get_byte(code_offset + idx).unwrap_or(0), ), ) }) @@ -216,12 +211,9 @@ mod codecopy_tests { let copy_events = builder.block.copy_events.clone(); assert_eq!(copy_events.len(), 1); assert_eq!(copy_events[0].bytes.len(), size); - assert_eq!( - copy_events[0].src_id, - NumberOrHash::Hash(CodeDB::hash(&code.to_vec())) - ); + assert_eq!(copy_events[0].src_id, NumberOrHash::Hash(code.hash_h256())); assert_eq!(copy_events[0].src_addr as usize, code_offset); - assert_eq!(copy_events[0].src_addr_end as usize, code.to_vec().len()); + assert_eq!(copy_events[0].src_addr_end as usize, code.codesize()); assert_eq!(copy_events[0].src_type, CopyDataType::Bytecode); assert_eq!( copy_events[0].dst_id, @@ -231,10 +223,10 @@ mod codecopy_tests { assert_eq!(copy_events[0].dst_type, CopyDataType::Memory); assert!(copy_events[0].log_id.is_none()); - for (idx, (value, is_code)) in copy_events[0].bytes.iter().enumerate() { - let bytecode_element = code.get(code_offset + idx).unwrap_or_default(); - assert_eq!(*value, bytecode_element.value); - assert_eq!(*is_code, bytecode_element.is_code); + for (idx, &(value, is_code)) in copy_events[0].bytes.iter().enumerate() { + let (true_value, true_is_code) = code.get(code_offset + idx).unwrap_or_default(); + assert_eq!(value, true_value); + assert_eq!(is_code, true_is_code); } } } diff --git a/bus-mapping/src/evm/opcodes/codesize.rs b/bus-mapping/src/evm/opcodes/codesize.rs index 2c5dd5e8d0..a190254623 100644 --- a/bus-mapping/src/evm/opcodes/codesize.rs +++ b/bus-mapping/src/evm/opcodes/codesize.rs @@ -67,7 +67,7 @@ mod codesize_tests { STOP }; code.append(&tail); - let codesize = code.to_vec().len(); + let codesize = code.codesize(); let block: GethData = TestContext::<2, 1>::new( None, diff --git a/bus-mapping/src/evm/opcodes/create.rs b/bus-mapping/src/evm/opcodes/create.rs index cdfa4fe554..3c085cfddc 100644 --- a/bus-mapping/src/evm/opcodes/create.rs +++ b/bus-mapping/src/evm/opcodes/create.rs @@ -291,12 +291,10 @@ fn handle_copy( length: usize, ) -> Result<(Vec, H256), Error> { let initialization_bytes = state.caller_ctx()?.memory.0[offset..(offset + length)].to_vec(); - let code_hash = CodeDB::hash(&initialization_bytes); - let bytes: Vec<_> = Bytecode::from(initialization_bytes.clone()) - .code - .iter() - .map(|element| (element.value, element.is_code)) - .collect(); + + let initialization = Bytecode::from(initialization_bytes.clone()); + let code_hash = initialization.hash_h256(); + let bytes = initialization.code_vec(); let rw_counter_start = state.block_ctx.rwc; for (i, (byte, _)) in bytes.iter().enumerate() { diff --git a/bus-mapping/src/evm/opcodes/extcodecopy.rs b/bus-mapping/src/evm/opcodes/extcodecopy.rs index ed1b109127..3e70cad6c3 100644 --- a/bus-mapping/src/evm/opcodes/extcodecopy.rs +++ b/bus-mapping/src/evm/opcodes/extcodecopy.rs @@ -133,7 +133,7 @@ fn gen_copy_event( } else { Bytecode::default() }; - let code_size = bytecode.code.len() as u64; + let code_size = bytecode.codesize() as u64; // Get low Uint64 of offset to generate copy steps. Since offset could be // Uint64 overflow if length is zero. @@ -410,11 +410,7 @@ mod extcodecopy_tests { MemoryOp::new( expected_call_id, MemoryAddress::from(memory_offset + idx), - if data_offset + idx < bytecode_ext.to_vec().len() { - bytecode_ext.to_vec()[data_offset + idx] - } else { - 0 - }, + bytecode_ext.get_byte(data_offset + idx).unwrap_or(0), ), ) }) @@ -436,10 +432,9 @@ mod extcodecopy_tests { assert_eq!(copy_events[0].dst_type, CopyDataType::Memory); assert!(copy_events[0].log_id.is_none()); - for (idx, (value, is_code)) in copy_events[0].bytes.iter().enumerate() { + for (idx, &(value, is_code)) in copy_events[0].bytes.iter().enumerate() { let bytecode_element = bytecode_ext.get(idx).unwrap_or_default(); - assert_eq!(*value, bytecode_element.value); - assert_eq!(*is_code, bytecode_element.is_code); + assert_eq!((value, is_code), bytecode_element); } } diff --git a/bus-mapping/src/evm/opcodes/return_revert.rs b/bus-mapping/src/evm/opcodes/return_revert.rs index 69c7fc5656..30b9636146 100644 --- a/bus-mapping/src/evm/opcodes/return_revert.rs +++ b/bus-mapping/src/evm/opcodes/return_revert.rs @@ -207,14 +207,10 @@ fn handle_create( source: Source, ) -> Result { let values = state.call_ctx()?.memory.0[source.offset..source.offset + source.length].to_vec(); - let code_hash = CodeDB::hash(&values); + let bytecode = Bytecode::from(values); + let code_hash = bytecode.hash_h256(); + let bytes = bytecode.code_vec(); let dst_id = NumberOrHash::Hash(code_hash); - let bytes: Vec<_> = Bytecode::from(values) - .code - .iter() - .map(|element| (element.value, element.is_code)) - .collect(); - let rw_counter_start = state.block_ctx.rwc; for (i, (byte, _)) in bytes.iter().enumerate() { state.push_op( diff --git a/bus-mapping/src/mock.rs b/bus-mapping/src/mock.rs index d6621ebe33..4e06367dbe 100644 --- a/bus-mapping/src/mock.rs +++ b/bus-mapping/src/mock.rs @@ -50,7 +50,7 @@ impl BlockData { fn init_dbs(geth_data: &GethData) -> (StateDB, CodeDB) { let mut sdb = StateDB::new(); - let mut code_db = CodeDB::new(); + let mut code_db = CodeDB::default(); let access_set = get_state_accesses(&geth_data.eth_block, &geth_data.geth_traces) .expect("state accesses"); diff --git a/bus-mapping/src/state_db.rs b/bus-mapping/src/state_db.rs index b306127281..eb47ba787d 100644 --- a/bus-mapping/src/state_db.rs +++ b/bus-mapping/src/state_db.rs @@ -1,8 +1,9 @@ //! Implementation of an in-memory key-value database to represent the //! Ethereum State Trie. -use eth_types::{geth_types, Address, Hash, Word, H256, U256}; +use eth_types::{geth_types, Address, BigEndianHash, Bytecode, Hash, Word, H256, U256}; use ethers_core::utils::keccak256; +use itertools::Itertools; use lazy_static::lazy_static; use std::collections::{HashMap, HashSet}; @@ -21,20 +22,10 @@ lazy_static! { const VALUE_ZERO: Word = Word::zero(); /// Memory storage for contract code by code hash. -#[derive(Debug, Clone)] -pub struct CodeDB(pub HashMap>); - -impl Default for CodeDB { - fn default() -> Self { - Self::new() - } -} +#[derive(Debug, Clone, Default)] +pub struct CodeDB(HashMap>); impl CodeDB { - /// Create a new empty Self. - pub fn new() -> Self { - Self(HashMap::new()) - } /// Insert code indexed by code hash, and return the code hash. pub fn insert(&mut self, code: Vec) -> Hash { let hash = Self::hash(&code); @@ -51,6 +42,46 @@ impl CodeDB { pub fn empty_code_hash() -> Hash { *EMPTY_CODE_HASH } + + /// Compute number of rows required for bytecode table. + pub fn num_rows_required_for_bytecode_table(&self) -> usize { + self.0.values().map(|bytecode| bytecode.len() + 1).sum() + } + + /// Query Bytecode by H256 + pub fn get_from_h256(&self, codehash: &H256) -> Option { + self.0.get(codehash).cloned().map(|code| code.into()) + } + + /// Query Bytecode by U256 + pub fn get_from_u256(&self, codehash: &Word) -> Option { + self.get_from_h256(&H256::from_uint(codehash)) + } +} + +impl From>> for CodeDB { + fn from(bytecodes: Vec>) -> Self { + Self(HashMap::from_iter( + bytecodes + .iter() + .cloned() + .map(|bytecode| (Self::hash(&bytecode), bytecode)), + )) + } +} + +impl IntoIterator for CodeDB { + type Item = Bytecode; + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0 + .values() + .cloned() + .map(Bytecode::from) + .collect_vec() + .into_iter() + } } /// Account of the Ethereum State Trie, which contains an in-memory key-value diff --git a/circuit-benchmarks/src/bytecode_circuit.rs b/circuit-benchmarks/src/bytecode_circuit.rs index 42ca583f35..b145bbb834 100644 --- a/circuit-benchmarks/src/bytecode_circuit.rs +++ b/circuit-benchmarks/src/bytecode_circuit.rs @@ -3,8 +3,7 @@ #[cfg(test)] mod tests { use ark_std::{end_timer, start_timer}; - use bus_mapping::evm::OpcodeId; - use eth_types::Field; + use bus_mapping::{evm::OpcodeId, state_db::CodeDB}; use halo2_proofs::{ halo2curves::bn256::{Bn256, Fr, G1Affine}, plonk::{create_proof, keygen_pk, keygen_vk, verify_proof}, @@ -22,14 +21,8 @@ mod tests { }; use rand::SeedableRng; use rand_xorshift::XorShiftRng; - use std::env::var; - use zkevm_circuits::{ - bytecode_circuit::{ - bytecode_unroller::{unroll, UnrolledBytecode}, - TestBytecodeCircuit, - }, - util::SubCircuit, - }; + use std::{env::var, iter}; + use zkevm_circuits::{bytecode_circuit::TestBytecodeCircuit, util::SubCircuit}; #[cfg_attr(not(feature = "benches"), ignore)] #[test] @@ -127,10 +120,7 @@ mod tests { } /// fill bytecodes_num * bytecode_len bytes to the witness table - fn fillup_codebytes( - bytecodes_num: usize, - bytecode_len: usize, - ) -> Vec> { + fn fillup_codebytes(bytecodes_num: usize, bytecode_len: usize) -> CodeDB { fn valid_or(base: OpcodeId, or: OpcodeId) -> OpcodeId { match base { OpcodeId::INVALID(_) => or, @@ -138,14 +128,13 @@ mod tests { } } - let mut codebytes = vec![]; - (0..bytecodes_num).for_each(|_| { - let bytecodes = (0..bytecode_len) + let codebytes = iter::repeat( + (0..bytecode_len) .map(|v| valid_or(OpcodeId::from(v as u8), OpcodeId::STOP).as_u8()) - .collect::>(); - let unrolled_bytes = unroll::(bytecodes); - codebytes.push(unrolled_bytes); - }); - codebytes + .collect::>(), + ) + .take(bytecodes_num) + .collect::>>(); + CodeDB::from(codebytes) } } diff --git a/eth-types/src/bytecode.rs b/eth-types/src/bytecode.rs index a72b30c682..d822a22230 100644 --- a/eth-types/src/bytecode.rs +++ b/eth-types/src/bytecode.rs @@ -1,7 +1,6 @@ //! EVM byte code generator - -use crate::{evm_types::OpcodeId, Bytes, ToWord, Word}; -use std::{collections::HashMap, str::FromStr}; +use crate::{evm_types::OpcodeId, keccak256, Bytes, Hash, ToBigEndian, ToWord, Word}; +use std::{collections::HashMap, iter, str::FromStr}; /// Error type for Bytecode related failures #[derive(Debug)] @@ -12,29 +11,25 @@ pub enum Error { /// Helper struct that represents a single element in a bytecode. #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] -pub struct BytecodeElement { +struct BytecodeElement { /// The byte value of the element. - pub value: u8, + value: u8, /// Whether the element is an opcode or push data byte. - pub is_code: bool, + is_code: bool, } /// EVM Bytecode #[derive(Debug, Default, Clone, PartialEq, Eq)] pub struct Bytecode { /// Vector for bytecode elements. - pub code: Vec, + code: Vec, num_opcodes: usize, markers: HashMap, } impl From for Bytes { fn from(code: Bytecode) -> Self { - code.code - .iter() - .map(|e| e.value) - .collect::>() - .into() + code.code().into() } } @@ -59,14 +54,34 @@ impl Bytecode { self.code.iter().map(|b| b.value).collect() } + /// Get the code and is_code + pub fn code_vec(&self) -> Vec<(u8, bool)> { + self.code.iter().map(|b| (b.value, b.is_code)).collect() + } + + /// Geth the code size + pub fn codesize(&self) -> usize { + self.code.len() + } + + /// Get the code hash + pub fn hash(&self) -> Word { + Word::from_big_endian(&keccak256(&self.code())) + } + + /// Get the code hash + pub fn hash_h256(&self) -> Hash { + Hash::from_slice(&self.hash().to_be_bytes()) + } + /// Get the bytecode element at an index. - pub fn get(&self, index: usize) -> Option { - self.code.get(index).cloned() + pub fn get(&self, index: usize) -> Option<(u8, bool)> { + self.code.get(index).map(|elem| (elem.value, elem.is_code)) } - /// Get the generated code - pub fn to_vec(&self) -> Vec { - self.code.iter().map(|e| e.value).collect() + /// Get the bytecode element at an index. + pub fn get_byte(&self, index: usize) -> Option { + self.code.get(index).map(|elem| elem.value) } /// Append @@ -149,25 +164,6 @@ impl Bytecode { self } - /// Generate the diassembly - pub fn disasm(&self) -> String { - let mut asm = String::new(); - for op in self.iter() { - asm.push_str(&op.to_string()); - asm.push('\n'); - } - asm - } - - /// Append asm - pub fn append_asm(&mut self, op: &str) -> Result<(), Error> { - match OpcodeWithData::from_str(op)? { - OpcodeWithData::Opcode(op) => self.write_op(op), - OpcodeWithData::Push(n, value) => self.push(n, value), - }; - Ok(()) - } - /// Append an opcode pub fn append_op(&mut self, op: OpcodeWithData) -> &mut Self { match op { @@ -191,6 +187,22 @@ impl Bytecode { self.write_op(OpcodeId::JUMPDEST); self.code.len() } + + /// Append the instructions to store another code to memory + pub fn store_code_to_mem(&mut self, code: &Self) { + let len = code.codesize(); + // pad to multiple of 32 bytes + let code: Vec = code + .code() + .iter() + .cloned() + .chain(iter::repeat(0).take(32 - len % 32)) + .collect(); + + for (index, word) in code.chunks(32).enumerate() { + self.op_mstore(index * 32, Word::from_big_endian(word)); + } + } } /// An ASM entry @@ -531,9 +543,7 @@ impl_other_opcodes! { #[cfg(test)] mod tests { - use super::*; use crate::Bytecode; - use std::str::FromStr; #[test] fn test_bytecode_roundtrip() { @@ -550,25 +560,6 @@ mod tests { POP STOP }; - assert_eq!(Bytecode::try_from(code.to_vec()).unwrap(), code); - } - - #[test] - fn test_asm_disasm() { - let code = bytecode! { - PUSH1(5) - PUSH2(0xa) - MUL - STOP - }; - let mut code2 = Bytecode::default(); - code.iter() - .map(|op| op.to_string()) - .map(|op| OpcodeWithData::from_str(&op).unwrap()) - .for_each(|op| { - code2.append_op(op); - }); - - assert_eq!(code.code, code2.code); + assert_eq!(Bytecode::try_from(code.code()).unwrap(), code); } } diff --git a/eth-types/src/geth_types.rs b/eth-types/src/geth_types.rs index 5b1a893a90..66323c04d7 100644 --- a/eth-types/src/geth_types.rs +++ b/eth-types/src/geth_types.rs @@ -3,8 +3,8 @@ use crate::{ keccak256, sign_types::{biguint_to_32bytes_le, ct_option_ok_or, recover_pk, SignData, SECP256K1_Q}, - AccessList, Address, Block, Bytes, Error, GethExecTrace, Hash, ToBigEndian, ToLittleEndian, - ToWord, Word, U64, + AccessList, Address, Block, Bytecode, Bytes, Error, GethExecTrace, Hash, ToBigEndian, + ToLittleEndian, ToWord, Word, U64, }; use ethers_core::{ types::{transaction::response, NameOrAddress, TransactionRequest}, @@ -44,6 +44,28 @@ impl Account { && self.code.is_empty() && self.storage.is_empty() } + + /// Generate an account that is either empty or has code, balance, and non-zero nonce + pub fn mock_code_balance(code: Bytecode) -> Self { + let is_empty = code.codesize() == 0; + Self { + address: Address::repeat_byte(0xff), + code: code.into(), + nonce: U64::from(!is_empty as u64), + balance: if is_empty { 0 } else { 0xdeadbeefu64 }.into(), + ..Default::default() + } + } + + /// Generate an account that has 100 ETH + pub fn mock_100_ether(code: Bytecode) -> Self { + Self { + address: Address::repeat_byte(0xfe), + balance: Word::from(10).pow(20.into()), + code: code.into(), + ..Default::default() + } + } } fn serde_account_storage( diff --git a/testool/src/statetest/executor.rs b/testool/src/statetest/executor.rs index 7dae91d8a0..e46dbdcfeb 100644 --- a/testool/src/statetest/executor.rs +++ b/testool/src/statetest/executor.rs @@ -77,15 +77,19 @@ fn check_post( } if let Some(expected_code) = &expected.code { - let actual_code = if actual.code_hash.is_zero() { - std::borrow::Cow::Owned(Vec::new()) - } else { - std::borrow::Cow::Borrowed(&builder.code_db.0[&actual.code_hash]) - }; - if &actual_code as &[u8] != expected_code.0 { + let actual_code = (!actual.code_hash.is_zero()) + .then(|| { + builder + .code_db + .get_from_h256(&actual.code_hash) + .map(|bytecode| bytecode.code()) + .expect("code exists") + }) + .unwrap_or_default(); + if actual_code != expected_code.0 { return Err(StateTestError::CodeMismatch { expected: expected_code.clone(), - found: Bytes::from(actual_code.to_vec()), + found: Bytes::from(actual_code), }); } } diff --git a/zkevm-circuits/src/bin/stats/helpers.rs b/zkevm-circuits/src/bin/stats/helpers.rs index 6f91a8c129..12432c9661 100644 --- a/zkevm-circuits/src/bin/stats/helpers.rs +++ b/zkevm-circuits/src/bin/stats/helpers.rs @@ -167,7 +167,7 @@ pub(crate) fn print_circuit_stats_by_states( let bytecode_prefix_op = fn_bytecode_prefix_op(opcode); code.append(&bytecode_prefix_op); code.write_op(opcode); - let opcode_pc = code.code.len() - 1; + let opcode_pc = code.codesize() - 1; // let opcode_step_index = (proxy_code.num_opcodes - 1 + code.num_opcodes) - 1; code.op_stop(); let block: GethData = TestContext::<10, 1>::new( diff --git a/zkevm-circuits/src/bytecode_circuit.rs b/zkevm-circuits/src/bytecode_circuit.rs index 97ce348718..715e9f0add 100644 --- a/zkevm-circuits/src/bytecode_circuit.rs +++ b/zkevm-circuits/src/bytecode_circuit.rs @@ -1,7 +1,5 @@ //! The bytecode circuit implementation. -/// Bytecode unroller -pub mod bytecode_unroller; /// Bytecode circuit pub mod circuit; diff --git a/zkevm-circuits/src/bytecode_circuit/bytecode_unroller.rs b/zkevm-circuits/src/bytecode_circuit/bytecode_unroller.rs deleted file mode 100644 index 6c4fb174fc..0000000000 --- a/zkevm-circuits/src/bytecode_circuit/bytecode_unroller.rs +++ /dev/null @@ -1,55 +0,0 @@ -use crate::{ - table::BytecodeFieldTag, - util::{get_push_size, keccak}, -}; -use eth_types::{Field, Word}; -use std::vec; - -/// Public data for the bytecode -#[derive(Clone, Debug, PartialEq)] -pub(crate) struct BytecodeRow { - pub(crate) code_hash: Word, - pub(crate) tag: F, - pub(crate) index: F, - pub(crate) is_code: F, - pub(crate) value: F, -} - -/// Unrolled bytecode -#[derive(Clone, Debug, PartialEq, Default)] -pub struct UnrolledBytecode { - pub(crate) bytes: Vec, - pub(crate) rows: Vec>, -} - -/// Get unrolled bytecode from raw bytes -pub fn unroll(bytes: Vec) -> UnrolledBytecode { - let code_hash = keccak(&bytes[..]); - let mut rows = vec![BytecodeRow:: { - code_hash, - tag: F::from(BytecodeFieldTag::Header as u64), - index: F::ZERO, - is_code: F::ZERO, - value: F::from(bytes.len() as u64), - }]; - // Run over all the bytes - let mut push_rindex = 0; - for (index, byte) in bytes.iter().enumerate() { - // Track which byte is an opcode and which is push data - let is_code = push_rindex == 0; - push_rindex = if is_code { - get_push_size(*byte) - } else { - push_rindex - 1 - }; - - rows.push(BytecodeRow:: { - code_hash, - tag: F::from(BytecodeFieldTag::Byte as u64), - index: F::from(index as u64), - is_code: F::from(is_code as u64), - value: F::from(*byte as u64), - }); - } - UnrolledBytecode { bytes, rows } -} diff --git a/zkevm-circuits/src/bytecode_circuit/circuit.rs b/zkevm-circuits/src/bytecode_circuit/circuit.rs index cae9ec27c4..5a645c282f 100644 --- a/zkevm-circuits/src/bytecode_circuit/circuit.rs +++ b/zkevm-circuits/src/bytecode_circuit/circuit.rs @@ -6,14 +6,14 @@ use crate::{ }, table::{BytecodeFieldTag, BytecodeTable, KeccakTable, LookupTable}, util::{ - get_push_size, + self, get_push_size, word::{empty_code_hash_word_value, Word, Word32, WordExpr}, Challenges, Expr, SubCircuit, SubCircuitConfig, }, - witness, + witness::{self}, }; -use bus_mapping::state_db::EMPTY_CODE_HASH_LE; -use eth_types::Field; +use bus_mapping::state_db::{CodeDB, EMPTY_CODE_HASH_LE}; +use eth_types::{Bytecode, Field}; use gadgets::is_zero::{IsZeroChip, IsZeroInstruction}; use halo2_proofs::{ circuit::{Layouter, Region, Value}, @@ -22,50 +22,133 @@ use halo2_proofs::{ }, poly::Rotation, }; +use itertools::Itertools; use log::trace; -use std::vec; - -use super::bytecode_unroller::{unroll, UnrolledBytecode}; +use std::{iter, ops::Deref, vec}; const PUSH_TABLE_WIDTH: usize = 2; #[derive(Debug, Clone, Default)] /// Row for assignment -pub struct BytecodeCircuitRow { - offset: usize, - last_row_offset: usize, - code_hash: Word>, +pub(crate) struct BytecodeCircuitRow { + pub(crate) code_hash: Word>, tag: F, - index: F, - is_code: F, - value: F, + pub(crate) index: F, + pub(crate) is_code: F, + pub(crate) value: F, push_data_left: u64, value_rlc: Value, length: F, push_data_size: F, } impl BytecodeCircuitRow { - /// enable selector if we are within the range of table size. - pub fn enable(&self) -> bool { - self.offset <= self.last_row_offset - } - - /// Determine if we are at last row of the bytecode table. - pub fn last(&self) -> bool { - self.offset == self.last_row_offset + #[cfg(test)] + pub(crate) fn new(code_hash: Word>, tag: F, index: F, is_code: F, value: F) -> Self { + Self { + code_hash, + tag, + index, + is_code, + value, + push_data_left: 0, + value_rlc: Value::known(F::ZERO), + length: F::ZERO, + push_data_size: F::ZERO, + } } - /// Get offset - pub fn offset(&self) -> usize { - self.offset + /// Padding must be a header, acording to the q_last constraints + fn pad() -> Self { + Self { + code_hash: empty_code_hash_word_value(), + tag: F::from(BytecodeFieldTag::Header as u64), + value_rlc: Value::known(F::ZERO), + ..Default::default() + } } /// Witness to IsZero chip to determine if we are at the last row of a bytecode instance - pub fn diff(&self) -> F { + fn diff(&self) -> F { self.index + F::ONE - self.length } } +#[derive(Clone, Default, Debug)] +pub(crate) struct BytecodeCircuitAssignment(pub(crate) Vec>); + +impl From> for BytecodeCircuitAssignment { + fn from(codes: Vec) -> Self { + let mut rows = vec![]; + for bytecode in codes.iter() { + let code_hash = util::word::Word::from(bytecode.hash()).into_value(); + let code_size = bytecode.codesize(); + let head = BytecodeCircuitRow { + code_hash, + tag: F::from(BytecodeFieldTag::Header as u64), + index: F::ZERO, + is_code: F::ZERO, + value: F::from(code_size as u64), + push_data_left: 0, + value_rlc: Value::known(F::ZERO), + length: F::from(code_size as u64), + push_data_size: F::ZERO, + }; + rows.push(head); + let mut push_data_left = 0; + + for (index, &(value, is_code)) in bytecode.code_vec().iter().enumerate() { + let push_data_size = get_push_size(value); + let value = F::from(value.into()); + + let body = BytecodeCircuitRow { + code_hash, + tag: F::from(BytecodeFieldTag::Byte as u64), + index: F::from(index as u64), + is_code: F::from(is_code.into()), + value, + push_data_left, + value_rlc: Value::unknown(), + length: F::from(code_size as u64), + push_data_size: F::from(push_data_size), + }; + rows.push(body); + push_data_left = if is_code { + push_data_size + } else { + push_data_left - 1 + }; + } + } + Self(rows) + } +} + +impl From for BytecodeCircuitAssignment { + fn from(code_db: CodeDB) -> Self { + // CodeDB use hash maps, so the bytecodes will be reordered. + code_db.into_iter().collect_vec().into() + } +} + +impl From>> for BytecodeCircuitAssignment { + fn from(codes: Vec>) -> Self { + // We don't go through BytecodeCollection struct to preserve bytecode order. + codes + .iter() + .map(|bytes| Bytecode::from(bytes.clone())) + .collect_vec() + .into() + } +} + +impl Deref for BytecodeCircuitAssignment { + type Target = Vec>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + #[derive(Clone, Debug)] /// Bytecode circuit configuration pub struct BytecodeCircuitConfig { @@ -484,13 +567,12 @@ impl BytecodeCircuitConfig { &self, layouter: &mut impl Layouter, size: usize, - witness: &[UnrolledBytecode], - overwrite: &UnrolledBytecode, + witness: &BytecodeCircuitAssignment, challenges: &Challenges>, - fail_fast: bool, ) -> Result<(), Error> { // Subtract the unusable rows from the size assert!(size > self.minimum_rows); + let last_row_offset = size - self.minimum_rows + 1; trace!( @@ -499,176 +581,73 @@ impl BytecodeCircuitConfig { self.minimum_rows, last_row_offset ); - + if witness.len() > last_row_offset { + // The last_row_offset-th row must be reserved for padding. + // so we have "last_row_offset rows" usable + log::error!( + "the witness has size {}, but only {} rows usable. Some witness will not be assigned", + witness.len(), + last_row_offset + ); + } layouter.assign_region( || "assign bytecode", |mut region| { // annotate columns self.annotate_circuit(&mut region); - let mut offset = 0; - for bytecode in witness.iter() { - self.assign_bytecode( - &mut region, - bytecode, - challenges, - &mut offset, - last_row_offset, - fail_fast, - )?; - } - - // Padding - for idx in offset..=last_row_offset { - self.set_padding_row(&mut region, idx, last_row_offset)?; - } - - // Overwrite the witness assignment by using the values in the `overwrite` - // parameter. This is used to explicitly set intermediate witness values for - // negative tests. - let mut value_rlc = challenges.keccak_input().map(|_| F::ZERO); - for (offset, row) in overwrite.rows.iter().enumerate() { - for (name, column, value) in [ - ("tag", self.bytecode_table.tag, row.tag), - ("index", self.bytecode_table.index, row.index), - ("is_code", self.bytecode_table.is_code, row.is_code), - ("value", self.bytecode_table.value, row.value), - ("length", self.length, F::from(overwrite.bytes.len() as u64)), - ] { - region.assign_advice( - || format!("assign {} {}", name, offset), - column, - offset, - || Value::known(value), - )?; - } - - if row.tag == F::ONE { + let mut value_rlc = Value::known(F::ZERO); + // Chain the witness rows with as many padding rows as we like. + // We take only the first "last_row_offset" rows. + for (offset, row) in witness + .iter() + .chain(iter::repeat(&BytecodeCircuitRow::pad())) + .take(last_row_offset) + .enumerate() + { + let mut row = row.clone(); + // unfortunately this is the only place we can set the RLC. + // The RLC of the padding rows are unaffected. As they are always with the + // Header tag, the RLC value for them are always zero. + if row.tag == F::from(BytecodeFieldTag::Byte as u64) { value_rlc.as_mut().zip(challenges.keccak_input()).map( |(value_rlc, challenge)| { *value_rlc = *value_rlc * challenge + row.value }, ); } else { - value_rlc = challenges.keccak_input().map(|_| F::ZERO); + value_rlc = Value::known(F::ZERO); } - - region.assign_advice( - || format!("assign value_rlc {}", offset), - self.value_rlc, - offset, - || value_rlc, - )?; + row.value_rlc = value_rlc; + self.set_row(&mut region, offset, last_row_offset, &row)?; } - Ok(()) - }, - ) - } - - fn assign_bytecode( - &self, - region: &mut Region<'_, F>, - bytecode: &UnrolledBytecode, - challenges: &Challenges>, - offset: &mut usize, - last_row_offset: usize, - fail_fast: bool, - ) -> Result<(), Error> { - // Run over all the bytes - let mut push_data_left = 0; - let mut next_push_data_left = 0; - let mut push_data_size = 0; - let mut value_rlc = challenges.keccak_input().map(|_| F::ZERO); - let length = F::from(bytecode.bytes.len() as u64); - - let code_hash = Word::from(bytecode.rows[0].code_hash).into_value(); - - for (idx, row) in bytecode.rows.iter().enumerate() { - if fail_fast && *offset > last_row_offset { - log::error!( - "Bytecode Circuit: offset={} > last_row_offset={}", - offset, - last_row_offset - ); - return Err(Error::Synthesis); - } - - // Track which byte is an opcode and which is push - // data - if idx > 0 { - let is_code = push_data_left == 0; - push_data_size = get_push_size(row.value.get_lower_128() as u8); - - next_push_data_left = if is_code { - push_data_size - } else { - push_data_left - 1 - }; - - value_rlc - .as_mut() - .zip(challenges.keccak_input()) - .map(|(value_rlc, challenge)| *value_rlc = *value_rlc * challenge + row.value); - } - - // Set the data for this row - if *offset < last_row_offset { - let row = BytecodeCircuitRow { - offset: *offset, + // Last row must be a padding row + self.set_row( + &mut region, last_row_offset, - code_hash, - tag: row.tag, - index: row.index, - is_code: row.is_code, - value: row.value, - push_data_left, - value_rlc, - length, - push_data_size: F::from(push_data_size), - }; - self.set_row(region, row.clone())?; - - trace!("bytecode.set_row({:?})", row); - - *offset += 1; - push_data_left = next_push_data_left - } - if *offset == last_row_offset { - self.set_padding_row(region, *offset, last_row_offset)?; - } - } + last_row_offset, + &BytecodeCircuitRow::pad(), + )?; - Ok(()) + Ok(()) + }, + ) } - fn set_padding_row( + fn set_row( &self, region: &mut Region<'_, F>, offset: usize, last_row_offset: usize, + row: &BytecodeCircuitRow, ) -> Result<(), Error> { - self.set_row( - region, - BytecodeCircuitRow { - offset, - last_row_offset, - code_hash: empty_code_hash_word_value(), - tag: F::from(BytecodeFieldTag::Header as u64), - value_rlc: Value::known(F::ZERO), - ..Default::default() - }, - ) - } - - fn set_row(&self, region: &mut Region<'_, F>, row: BytecodeCircuitRow) -> Result<(), Error> { - let offset = row.offset; // q_enable region.assign_fixed( || format!("assign q_enable {}", offset), self.q_enable, offset, - || Value::known(F::from(row.enable().into())), + || Value::known(F::from((offset <= last_row_offset).into())), )?; // q_first @@ -684,7 +663,7 @@ impl BytecodeCircuitConfig { || format!("assign q_last {}", offset), self.q_last, offset, - || Value::known(F::from(row.last().into())), + || Value::known(F::from((offset == last_row_offset).into())), )?; // Advices @@ -786,33 +765,23 @@ impl BytecodeCircuitConfig { /// BytecodeCircuit #[derive(Clone, Default, Debug)] pub struct BytecodeCircuit { + pub(crate) bytecodes: CodeDB, /// Unrolled bytecodes - pub bytecodes: Vec>, + pub(crate) rows: BytecodeCircuitAssignment, /// Circuit size pub size: usize, - /// Overwrite - pub overwrite: UnrolledBytecode, } impl BytecodeCircuit { /// new BytecodeCircuitTester - pub fn new(bytecodes: Vec>, size: usize) -> Self { - BytecodeCircuit { + pub fn new(bytecodes: CodeDB, size: usize) -> Self { + let rows: BytecodeCircuitAssignment = bytecodes.clone().into(); + Self { bytecodes, + rows, size, - overwrite: Default::default(), } } - - /// Creates bytecode circuit from block and bytecode_size. - pub fn new_from_block_sized(block: &witness::Block, bytecode_size: usize) -> Self { - let bytecodes: Vec> = block - .bytecodes - .values() - .map(|b| unroll(b.bytes.clone())) - .collect(); - Self::new(bytecodes, bytecode_size) - } } impl SubCircuit for BytecodeCircuit { @@ -825,18 +794,13 @@ impl SubCircuit for BytecodeCircuit { } fn new_from_block(block: &witness::Block) -> Self { - let bytecode_size = block.circuits_params.max_bytecode; - Self::new_from_block_sized(block, bytecode_size) + Self::new(block.bytecodes.clone(), block.circuits_params.max_bytecode) } /// Return the minimum number of rows required to prove the block fn min_num_rows_block(block: &witness::Block) -> (usize, usize) { ( - block - .bytecodes - .values() - .map(|bytecode| bytecode.bytes.len() + 1) - .sum(), + block.bytecodes.num_rows_required_for_bytecode_table(), block.circuits_params.max_bytecode, ) } @@ -849,13 +813,6 @@ impl SubCircuit for BytecodeCircuit { layouter: &mut impl Layouter, ) -> Result<(), Error> { config.load_aux_tables(layouter)?; - config.assign_internal( - layouter, - self.size, - &self.bytecodes, - &self.overwrite, - challenges, - false, - ) + config.assign_internal(layouter, self.size, &self.rows, challenges) } } diff --git a/zkevm-circuits/src/bytecode_circuit/dev.rs b/zkevm-circuits/src/bytecode_circuit/dev.rs index 178aecc90d..71ccb42c00 100644 --- a/zkevm-circuits/src/bytecode_circuit/dev.rs +++ b/zkevm-circuits/src/bytecode_circuit/dev.rs @@ -10,6 +10,7 @@ use halo2_proofs::{ circuit::{Layouter, SimpleFloorPlanner}, plonk::{Circuit, ConstraintSystem, Error}, }; +use itertools::Itertools; impl Circuit for BytecodeCircuit { type Config = (BytecodeCircuitConfig, Challenges); @@ -49,7 +50,12 @@ impl Circuit for BytecodeCircuit { config.keccak_table.dev_load( &mut layouter, - self.bytecodes.iter().map(|b| &b.bytes), + &self + .bytecodes + .clone() + .into_iter() + .map(|b| b.code()) + .collect_vec(), &challenges, )?; self.synthesize_sub(&config, &challenges, &mut layouter)?; diff --git a/zkevm-circuits/src/bytecode_circuit/test.rs b/zkevm-circuits/src/bytecode_circuit/test.rs index 349b1544c1..68fc65a212 100644 --- a/zkevm-circuits/src/bytecode_circuit/test.rs +++ b/zkevm-circuits/src/bytecode_circuit/test.rs @@ -1,13 +1,14 @@ use crate::{ - bytecode_circuit::{bytecode_unroller::*, circuit::BytecodeCircuit}, - table::BytecodeFieldTag, - util::{is_push, keccak, unusable_rows, SubCircuit}, + bytecode_circuit::circuit::BytecodeCircuit, + util::{log2_ceil, unusable_rows, SubCircuit}, }; -use bus_mapping::evm::OpcodeId; -use eth_types::{Bytecode, Field, Word}; +use bus_mapping::{evm::OpcodeId, state_db::CodeDB}; +use eth_types::Field; use halo2_proofs::{arithmetic::Field as Halo2Field, dev::MockProver, halo2curves::bn256::Fr}; use log::error; +use super::circuit::BytecodeCircuitRow; + #[test] fn bytecode_circuit_unusable_rows() { assert_eq!( @@ -17,186 +18,102 @@ fn bytecode_circuit_unusable_rows() { } impl BytecodeCircuit { - /// Verify that the selected bytecode fulfills the circuit - pub fn verify_raw(k: u32, bytecodes: Vec>) { - let unrolled: Vec<_> = bytecodes.iter().map(|b| unroll(b.clone())).collect(); - Self::verify(k, unrolled, true); + fn mut_rows(&mut self, mut mut_func: impl FnMut(&mut Vec>)) -> Self { + mut_func(&mut self.rows.0); + self.clone() } - pub(crate) fn verify(k: u32, bytecodes: Vec>, success: bool) { - let circuit = BytecodeCircuit::::new(bytecodes, 2usize.pow(k)); - - let prover = MockProver::::run(k, &circuit, Vec::new()).unwrap(); - let result = prover.verify(); - if let Err(failures) = &result { - for failure in failures.iter() { - error!("{}", failure); - } - } - assert_eq!(result.is_ok(), success); + fn from_bytes(bytecodes: impl Into, k: u32) -> Self { + Self::new(bytecodes.into(), 2usize.pow(k)) } -} - -/// Test bytecode circuit with unrolled bytecode -pub fn test_bytecode_circuit_unrolled( - k: u32, - bytecodes: Vec>, - success: bool, -) { - let circuit = BytecodeCircuit::::new(bytecodes, 2usize.pow(k)); - let prover = MockProver::::run(k, &circuit, Vec::new()).unwrap(); - let result = prover.verify_par(); - if let Err(failures) = &result { - for failure in failures.iter() { - error!("{}", failure); - } - } - let error_msg = if success { "valid" } else { "invalid" }; - assert_eq!(result.is_ok(), success, "proof must be {}", error_msg); -} - -/// Verify unrolling code -#[test] -fn bytecode_unrolling() { - let k = 10; - let mut rows = vec![]; - let mut bytecode = Bytecode::default(); - // First add all non-push bytes, which should all be seen as code - for byte in 0u8..=255u8 { - if !is_push(byte) { - bytecode.write(byte, true); - rows.push(BytecodeRow { - code_hash: Word::zero(), - tag: Fr::from(BytecodeFieldTag::Byte as u64), - index: Fr::from(rows.len() as u64), - is_code: Fr::from(true as u64), - value: Fr::from(byte as u64), - }); - } - } - // Now add the different push ops - for n in 1..=32 { - let data_byte = OpcodeId::PUSH32.as_u8(); - bytecode.push( - n, - Word::from_little_endian(&vec![data_byte; n as usize][..]), - ); - rows.push(BytecodeRow { - code_hash: Word::zero(), - tag: Fr::from(BytecodeFieldTag::Byte as u64), - index: Fr::from(rows.len() as u64), - is_code: Fr::from(true as u64), - value: Fr::from(OpcodeId::PUSH1.as_u64() + ((n - 1) as u64)), - }); - for _ in 0..n { - rows.push(BytecodeRow { - code_hash: Word::zero(), - tag: Fr::from(BytecodeFieldTag::Byte as u64), - index: Fr::from(rows.len() as u64), - is_code: Fr::from(false as u64), - value: Fr::from(data_byte as u64), - }); + fn verify(&self, success: bool) { + let prover = MockProver::::run(log2_ceil(self.size), self, Vec::new()).unwrap(); + let result = prover.verify_par(); + if success { + if let Err(failures) = &result { + for failure in failures.iter() { + error!("{}", failure); + } + } } + let error_msg = if success { "valid" } else { "invalid" }; + assert_eq!(result.is_ok(), success, "proof must be {}", error_msg); } - // Set the code_hash of the complete bytecode in the rows - let code_hash = keccak(&bytecode.to_vec()[..]); - for row in rows.iter_mut() { - row.code_hash = code_hash; - } - rows.insert( - 0, - BytecodeRow { - code_hash, - tag: Fr::from(BytecodeFieldTag::Header as u64), - index: Fr::ZERO, - is_code: Fr::ZERO, - value: Fr::from(bytecode.to_vec().len() as u64), - }, - ); - // Unroll the bytecode - let unrolled = unroll(bytecode.to_vec()); - // Check if the bytecode was unrolled correctly - assert_eq!( - UnrolledBytecode { - bytes: bytecode.to_vec(), - rows, - }, - unrolled, - ); - // Verify the unrolling in the circuit - test_bytecode_circuit_unrolled::(k, vec![unrolled], true); } /// Tests a fully empty circuit #[test] fn bytecode_empty() { let k = 9; - test_bytecode_circuit_unrolled::(k, vec![unroll(vec![])], true); + BytecodeCircuit::::from_bytes(vec![vec![]], k).verify(true); } #[test] fn bytecode_simple() { let k = 9; - let bytecodes = vec![unroll(vec![7u8]), unroll(vec![6u8]), unroll(vec![5u8])]; - test_bytecode_circuit_unrolled::(k, bytecodes, true); + let bytecodes = vec![vec![7u8], vec![6u8], vec![5u8]]; + BytecodeCircuit::::from_bytes(bytecodes, k).verify(true); } /// Tests a fully full circuit #[test] fn bytecode_full() { let k = 9; - test_bytecode_circuit_unrolled::(k, vec![unroll(vec![7u8; 2usize.pow(k) - 8])], true); + BytecodeCircuit::::from_bytes(vec![vec![7u8; 2usize.pow(k) - 8]], k).verify(true); } #[test] fn bytecode_last_row_with_byte() { let k = 9; // Last row must be a padding row, so we have one row less for actual bytecode - test_bytecode_circuit_unrolled::(k, vec![unroll(vec![7u8; 2usize.pow(k) - 7])], false); + BytecodeCircuit::::from_bytes(vec![vec![7u8; 2usize.pow(k) - 7]], k).verify(false); } /// Tests a circuit with incomplete bytecode #[test] fn bytecode_incomplete() { let k = 9; - test_bytecode_circuit_unrolled::(k, vec![unroll(vec![7u8; 2usize.pow(k) + 1])], false); + BytecodeCircuit::::from_bytes(vec![vec![7u8; 2usize.pow(k) + 1]], k).verify(false); } /// Tests multiple bytecodes in a single circuit #[test] fn bytecode_push() { let k = 9; - test_bytecode_circuit_unrolled::( - k, + BytecodeCircuit::::from_bytes( vec![ - unroll(vec![]), - unroll(vec![OpcodeId::PUSH32.as_u8()]), - unroll(vec![OpcodeId::PUSH32.as_u8(), OpcodeId::ADD.as_u8()]), - unroll(vec![OpcodeId::ADD.as_u8(), OpcodeId::PUSH32.as_u8()]), - unroll(vec![ + vec![], + vec![OpcodeId::PUSH32.as_u8()], + vec![OpcodeId::PUSH32.as_u8(), OpcodeId::ADD.as_u8()], + vec![OpcodeId::ADD.as_u8(), OpcodeId::PUSH32.as_u8()], + vec![ OpcodeId::ADD.as_u8(), OpcodeId::PUSH32.as_u8(), OpcodeId::ADD.as_u8(), - ]), + ], ], - true, - ); + k, + ) + .verify(true); } /// Test invalid code_hash data #[test] fn bytecode_invalid_hash_data() { let k = 9; - let bytecode = vec![8u8, 2, 3, 8, 9, 7, 128]; - let unrolled = unroll(bytecode); - test_bytecode_circuit_unrolled::(k, vec![unrolled.clone()], true); + let bytecodes = vec![vec![8u8, 2, 3, 8, 9, 7, 128]]; // Change the code_hash on the first position (header row) { - let mut invalid = unrolled; - invalid.rows[0].code_hash += Word::one(); - log::trace!("bytecode_invalid_hash_data: Change the code_hash on the first position"); - test_bytecode_circuit_unrolled::(k, vec![invalid], false); + BytecodeCircuit::::from_bytes(bytecodes, k) + .mut_rows(|rows| { + let code_hash = rows[0].code_hash; + rows[0].code_hash = code_hash.map(|limb| limb.map(|limb| limb + Fr::one())); + log::trace!( + "bytecode_invalid_hash_data: Change the code_hash on the first position" + ); + }) + .verify(false); } // TODO: other rows code_hash are ignored by the witness generation, to // test other rows invalid code_hash, we would need to inject an evil @@ -205,25 +122,26 @@ fn bytecode_invalid_hash_data() { /// Test invalid index #[test] -#[ignore] fn bytecode_invalid_index() { let k = 9; - let bytecode = vec![8u8, 2, 3, 8, 9, 7, 128]; - let unrolled = unroll(bytecode); - test_bytecode_circuit_unrolled::(k, vec![unrolled.clone()], true); + let bytecodes = vec![vec![8u8, 2, 3, 8, 9, 7, 128]]; // Start the index at 1 { - let mut invalid = unrolled.clone(); - for row in invalid.rows.iter_mut() { - row.index += Fr::ONE; - } - test_bytecode_circuit_unrolled::(k, vec![invalid], false); + BytecodeCircuit::::from_bytes(bytecodes.clone(), k) + .mut_rows(|rows| { + for row in rows.iter_mut() { + row.index += Fr::ONE; + } + }) + .verify(false); } // Don't increment an index once { - let mut invalid = unrolled; - invalid.rows.last_mut().unwrap().index -= Fr::ONE; - test_bytecode_circuit_unrolled::(k, vec![invalid], false); + BytecodeCircuit::::from_bytes(bytecodes, k) + .mut_rows(|rows| { + rows.last_mut().unwrap().index -= Fr::ONE; + }) + .verify(false); } } @@ -231,26 +149,30 @@ fn bytecode_invalid_index() { #[test] fn bytecode_invalid_byte_data() { let k = 9; - let bytecode = vec![8u8, 2, 3, 8, 9, 7, 128]; - let unrolled = unroll(bytecode); - test_bytecode_circuit_unrolled::(k, vec![unrolled.clone()], true); + let bytecodes = vec![vec![8u8, 2, 3, 8, 9, 7, 128]]; // Change the first byte { - let mut invalid = unrolled.clone(); - invalid.rows[1].value = Fr::from(9u64); - test_bytecode_circuit_unrolled::(k, vec![invalid], false); + BytecodeCircuit::::from_bytes(bytecodes.clone(), k) + .mut_rows(|rows| { + rows[1].value = Fr::from(9u64); + }) + .verify(false); } // Change a byte on another position { - let mut invalid = unrolled.clone(); - invalid.rows[5].value = Fr::from(6u64); - test_bytecode_circuit_unrolled::(k, vec![invalid], false); + BytecodeCircuit::::from_bytes(bytecodes.clone(), k) + .mut_rows(|rows| { + rows[5].value = Fr::from(6u64); + }) + .verify(false); } // Set a byte value out of range { - let mut invalid = unrolled; - invalid.rows[3].value = Fr::from(256u64); - test_bytecode_circuit_unrolled::(k, vec![invalid], false); + BytecodeCircuit::::from_bytes(bytecodes, k) + .mut_rows(|rows| { + rows[3].value = Fr::from(256u64); + }) + .verify(false); } } @@ -258,7 +180,7 @@ fn bytecode_invalid_byte_data() { #[test] fn bytecode_invalid_is_code() { let k = 9; - let bytecode = vec![ + let bytecodes = vec![vec![ OpcodeId::ADD.as_u8(), OpcodeId::PUSH1.as_u8(), OpcodeId::PUSH1.as_u8(), @@ -266,59 +188,57 @@ fn bytecode_invalid_is_code() { OpcodeId::PUSH7.as_u8(), OpcodeId::ADD.as_u8(), OpcodeId::PUSH6.as_u8(), - ]; - let unrolled = unroll(bytecode); - test_bytecode_circuit_unrolled::(k, vec![unrolled.clone()], true); + ]]; + BytecodeCircuit::::from_bytes(bytecodes.clone(), k).verify(true); // Mark the 3rd byte as code (is push data from the first PUSH1) { - let mut invalid = unrolled.clone(); - invalid.rows[3].is_code = Fr::ONE; - test_bytecode_circuit_unrolled::(k, vec![invalid], false); + BytecodeCircuit::::from_bytes(bytecodes.clone(), k) + .mut_rows(|rows| { + rows[3].is_code = Fr::ONE; + }) + .verify(false); } // Mark the 4rd byte as data (is code) { - let mut invalid = unrolled.clone(); - invalid.rows[4].is_code = Fr::ZERO; - test_bytecode_circuit_unrolled::(k, vec![invalid], false); + BytecodeCircuit::::from_bytes(bytecodes.clone(), k) + .mut_rows(|rows| { + rows[4].is_code = Fr::ZERO; + }) + .verify(false); } // Mark the 7th byte as code (is data for the PUSH7) { - let mut invalid = unrolled; - invalid.rows[7].is_code = Fr::ONE; - test_bytecode_circuit_unrolled::(k, vec![invalid], false); + BytecodeCircuit::::from_bytes(bytecodes, k) + .mut_rows(|rows| { + rows[7].is_code = Fr::ONE; + }) + .verify(false); } } #[test] -#[should_panic] -#[allow(clippy::clone_on_copy)] fn bytecode_soundness_bug_1() { let k = 9; - let bytecode = vec![1, 2, 3, 4]; - let bytecode_len = bytecode.len(); - let unrolled = unroll(bytecode); - let unrolled_len = unrolled.rows.len(); - let code_hash = unrolled.rows[0].code_hash.clone(); - let mut index = bytecode_len as u64; - let size = 100; - let minimum_rows = 8; - - let mut overwrite = unrolled.clone(); - for i in 0..size - minimum_rows + 3 { - if i >= unrolled_len { - overwrite.rows.push(BytecodeRow { - code_hash: code_hash.clone(), - tag: Fr::ONE, - index: Fr::from(index), - is_code: Fr::ONE, - value: Fr::from((i % 10 + 1) as u64), - }); - index += 1; - } - } - let mut circuit = BytecodeCircuit::::new(vec![unrolled], size); - circuit.overwrite = overwrite; - - let prover = MockProver::::run(k, &circuit, Vec::new()).unwrap(); - prover.assert_satisfied_par(); + let bytecodes = vec![vec![1, 2, 3, 4]]; + + let bytecode_len = bytecodes[0].len(); + BytecodeCircuit::::from_bytes(bytecodes, k) + .mut_rows(|rows| { + let code_hash = rows[0].code_hash; + let mut index = bytecode_len as u64; + let size = 100; + let minimum_rows = 8; + let len = rows.len(); + for i in len..size - minimum_rows { + rows.push(BytecodeCircuitRow::new( + code_hash, + Fr::ONE, + Fr::from(index), + Fr::ONE, + Fr::from((i % 10 + 1) as u64), + )); + index += 1; + } + }) + .verify(false); } diff --git a/zkevm-circuits/src/copy_circuit.rs b/zkevm-circuits/src/copy_circuit.rs index 08b4dd7b02..ebea1507b7 100644 --- a/zkevm-circuits/src/copy_circuit.rs +++ b/zkevm-circuits/src/copy_circuit.rs @@ -10,12 +10,22 @@ mod test; #[cfg(feature = "test-circuits")] pub use dev::CopyCircuit as TestCopyCircuit; +use crate::{ + evm_circuit::util::constraint_builder::{BaseConstraintBuilder, ConstrainBuilderCommon}, + table::{ + BytecodeFieldTag, BytecodeTable, CopyTable, LookupTable, RwTable, TxContextFieldTag, + TxTable, + }, + util::{Challenges, SubCircuit, SubCircuitConfig}, + witness, + witness::{RwMap, Transaction}, +}; use bus_mapping::{ circuit_input_builder::{CopyDataType, CopyEvent}, operation::Target, + state_db::CodeDB, }; -use eth_types::{Field, Word}; - +use eth_types::Field; use gadgets::{ binary_number::BinaryNumberChip, less_than::{LtChip, LtConfig, LtInstruction}, @@ -27,18 +37,7 @@ use halo2_proofs::{ poly::Rotation, }; use itertools::Itertools; -use std::{collections::HashMap, marker::PhantomData}; - -use crate::{ - evm_circuit::util::constraint_builder::{BaseConstraintBuilder, ConstrainBuilderCommon}, - table::{ - BytecodeFieldTag, BytecodeTable, CopyTable, LookupTable, RwTable, TxContextFieldTag, - TxTable, - }, - util::{Challenges, SubCircuit, SubCircuitConfig}, - witness, - witness::{Bytecode, RwMap, Transaction}, -}; +use std::marker::PhantomData; /// The rw table shared between evm circuit and state circuit #[derive(Clone, Debug)] @@ -754,7 +753,7 @@ pub struct ExternalData { /// StateCircuit -> rws pub rws: RwMap, /// BytecodeCircuit -> bytecodes - pub bytecodes: HashMap, + pub bytecodes: CodeDB, } /// Copy Circuit diff --git a/zkevm-circuits/src/copy_circuit/dev.rs b/zkevm-circuits/src/copy_circuit/dev.rs index 85f0f71b04..cd5b4c2a5c 100644 --- a/zkevm-circuits/src/copy_circuit/dev.rs +++ b/zkevm-circuits/src/copy_circuit/dev.rs @@ -68,7 +68,7 @@ impl Circuit for CopyCircuit { config .0 .bytecode_table - .load(&mut layouter, self.external_data.bytecodes.values())?; + .load(&mut layouter, self.external_data.bytecodes.clone())?; self.synthesize_sub(&config.0, &challenge_values, &mut layouter) } } diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index f9f9de0333..e330550eb7 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -183,7 +183,7 @@ impl EvmCircuit { fixed_table_tags: FixedTableTag::iter().collect(), } } - #[cfg(feature = "test-circuits")] + #[cfg(any(test, feature = "test-circuits"))] /// Construct the EvmCircuit with only subset of Fixed table tags required by tests to save /// testing time pub(crate) fn get_test_circuit_from_block(block: Block) -> Self { @@ -193,7 +193,7 @@ impl EvmCircuit { fixed_table_tags, } } - #[cfg(feature = "test-circuits")] + #[cfg(any(test, feature = "test-circuits"))] /// Calculate which rows are "actually" used in the circuit pub(crate) fn get_active_rows(block: &Block) -> (Vec, Vec) { let max_offset = Self::get_num_rows_required(block); @@ -420,7 +420,7 @@ impl Circuit for EvmCircuit { )?; config .bytecode_table - .load(&mut layouter, block.bytecodes.values())?; + .load(&mut layouter, block.bytecodes.clone())?; config.block_table.load(&mut layouter, &block.context)?; config.copy_table.load(&mut layouter, block, &challenges)?; config diff --git a/zkevm-circuits/src/evm_circuit/execution/callop.rs b/zkevm-circuits/src/evm_circuit/execution/callop.rs index fc556ad0d9..3258fe88fe 100644 --- a/zkevm-circuits/src/evm_circuit/execution/callop.rs +++ b/zkevm-circuits/src/evm_circuit/execution/callop.rs @@ -640,7 +640,6 @@ mod test { use bus_mapping::circuit_input_builder::FixedCParams; use eth_types::{ address, bytecode, evm_types::OpcodeId, geth_types::Account, word, Address, ToWord, Word, - U64, }; use itertools::Itertools; @@ -780,15 +779,7 @@ mod test { } fn callee(code: bytecode::Bytecode) -> Account { - let code = code.to_vec(); - let is_empty = code.is_empty(); - Account { - address: Address::repeat_byte(0xff), - code: code.into(), - nonce: U64::from(!is_empty as u64), - balance: if is_empty { 0 } else { 0xdeadbeefu64 }.into(), - ..Default::default() - } + Account::mock_code_balance(code) } fn caller(opcode: &OpcodeId, stack: Stack, caller_is_success: bool) -> Account { @@ -830,12 +821,7 @@ mod test { .write_op(terminator) }); - Account { - address: Address::repeat_byte(0xfe), - balance: Word::from(10).pow(20.into()), - code: bytecode.to_vec().into(), - ..Default::default() - } + Account::mock_100_ether(bytecode) } fn caller_for_insufficient_balance(opcode: &OpcodeId, stack: Stack) -> Account { @@ -860,8 +846,8 @@ mod test { Account { address: Address::repeat_byte(0xfe), - balance: Word::from(10).pow(18.into()), - code: bytecode.to_vec().into(), + balance: Word::from(10).pow(18.into()), // 1 Ether + code: bytecode.into(), ..Default::default() } } @@ -995,12 +981,7 @@ mod test { STOP }); test_ok( - Account { - address: Address::repeat_byte(0xfe), - balance: Word::from(10).pow(20.into()), - code: caller_bytecode.into(), - ..Default::default() - }, + Account::mock_100_ether(caller_bytecode), callee(callee_bytecode), ); } diff --git a/zkevm-circuits/src/evm_circuit/execution/codecopy.rs b/zkevm-circuits/src/evm_circuit/execution/codecopy.rs index c95902b885..f0c192852d 100644 --- a/zkevm-circuits/src/evm_circuit/execution/codecopy.rs +++ b/zkevm-circuits/src/evm_circuit/execution/codecopy.rs @@ -1,5 +1,5 @@ use bus_mapping::{circuit_input_builder::CopyDataType, evm::OpcodeId}; -use eth_types::{evm_types::GasCost, Field, ToScalar, ToWord}; +use eth_types::{evm_types::GasCost, Field, ToScalar}; use halo2_proofs::{circuit::Value, plonk::Error}; use crate::{ @@ -159,10 +159,10 @@ impl ExecutionGadget for CodeCopyGadget { let bytecode = block .bytecodes - .get(&call.code_hash.to_word()) + .get_from_h256(&call.code_hash) .expect("could not find current environment's bytecode"); - let code_size = bytecode.bytes.len() as u64; + let code_size = bytecode.codesize() as u64; self.code_size .assign(region, offset, Value::known(F::from(code_size)))?; diff --git a/zkevm-circuits/src/evm_circuit/execution/error_invalid_jump.rs b/zkevm-circuits/src/evm_circuit/execution/error_invalid_jump.rs index 8edb48b549..0d4eb1e9fd 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_invalid_jump.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_invalid_jump.rs @@ -16,7 +16,7 @@ use crate::{ Expr, }, }; -use eth_types::{evm_types::OpcodeId, Field, ToWord, U256}; +use eth_types::{evm_types::OpcodeId, Field, U256}; use halo2_proofs::{circuit::Value, plonk::Error}; @@ -128,9 +128,9 @@ impl ExecutionGadget for ErrorInvalidJumpGadget { let code = block .bytecodes - .get(&call.code_hash.to_word()) + .get_from_h256(&call.code_hash) .expect("could not find current environment's bytecode"); - let code_len = code.bytes.len() as u64; + let code_len = code.codesize() as u64; self.code_len .assign(region, offset, Value::known(F::from(code_len)))?; @@ -138,21 +138,17 @@ impl ExecutionGadget for ErrorInvalidJumpGadget { self.dest.assign(region, offset, dest, F::from(code_len))?; // set default value in case can not find value, is_code from bytecode table - let dest = u64::try_from(dest).unwrap_or(code_len); - let mut code_pair = [0u8, 0u8]; - if dest < code_len { - // get real value from bytecode table - code_pair = code.get(dest as usize); - } + let dest = usize::try_from(dest).unwrap_or(code.codesize()); + let (value, is_code) = code.get(dest).unwrap_or((0, false)); self.value - .assign(region, offset, Value::known(F::from(code_pair[0] as u64)))?; + .assign(region, offset, Value::known(F::from(value.into())))?; self.is_code - .assign(region, offset, Value::known(F::from(code_pair[1] as u64)))?; + .assign(region, offset, Value::known(F::from(is_code.into())))?; self.is_jump_dest.assign( region, offset, - F::from(code_pair[0] as u64), + F::from(value.into()), F::from(OpcodeId::JUMPDEST.as_u64()), )?; @@ -186,7 +182,7 @@ mod test { use crate::test_util::CircuitTestBuilder; use eth_types::{ address, bytecode, bytecode::Bytecode, evm_types::OpcodeId, geth_types::Account, Address, - ToWord, Word, U64, + ToWord, Word, }; use mock::TestContext; @@ -244,15 +240,7 @@ mod test { } fn callee(code: Bytecode) -> Account { - let code = code.to_vec(); - let is_empty = code.is_empty(); - Account { - address: Address::repeat_byte(0xff), - code: code.into(), - nonce: U64::from(!is_empty as u64), - balance: if is_empty { 0 } else { 0xdeadbeefu64 }.into(), - ..Default::default() - } + Account::mock_code_balance(code) } // jump or jumpi error happen in internal call @@ -309,12 +297,7 @@ mod test { STOP }); test_ok( - Account { - address: Address::repeat_byte(0xfe), - balance: Word::from(10).pow(20.into()), - code: caller_bytecode.into(), - ..Default::default() - }, + Account::mock_100_ether(caller_bytecode), callee(callee_bytecode), ); } diff --git a/zkevm-circuits/src/evm_circuit/execution/error_oog_call.rs b/zkevm-circuits/src/evm_circuit/execution/error_oog_call.rs index 41dad3a55f..f2f884e293 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_oog_call.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_oog_call.rs @@ -223,7 +223,7 @@ mod test { use crate::test_util::CircuitTestBuilder; use eth_types::{ address, bytecode, bytecode::Bytecode, evm_types::OpcodeId, geth_types::Account, Address, - ToWord, Word, U64, + ToWord, Word, }; use mock::TestContext; use std::default::Default; @@ -269,25 +269,11 @@ mod test { fn caller(opcode: OpcodeId, stack: Stack) -> Account { let bytecode = call_bytecode(opcode, Address::repeat_byte(0xff), stack); - - Account { - address: Address::repeat_byte(0xfe), - balance: Word::from(10).pow(20.into()), - code: bytecode.to_vec().into(), - ..Default::default() - } + Account::mock_100_ether(bytecode) } fn callee(code: Bytecode) -> Account { - let code = code.to_vec(); - let is_empty = code.is_empty(); - Account { - address: Address::repeat_byte(0xff), - code: code.into(), - nonce: U64::from(!is_empty as u64), - balance: if is_empty { 0 } else { 0xdeadbeefu64 }.into(), - ..Default::default() - } + Account::mock_code_balance(code) } fn test_oog(caller: &Account, callee: &Account, is_root: bool) { diff --git a/zkevm-circuits/src/evm_circuit/execution/error_oog_constant.rs b/zkevm-circuits/src/evm_circuit/execution/error_oog_constant.rs index 90bfd06163..01e12c3a00 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_oog_constant.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_oog_constant.rs @@ -94,7 +94,7 @@ mod test { use bus_mapping::evm::OpcodeId; use eth_types::{ self, address, bytecode, bytecode::Bytecode, evm_types::GasCost, geth_types::Account, - Address, ToWord, Word, U64, + Address, ToWord, Word, }; use mock::{ @@ -211,12 +211,7 @@ mod test { .write_op(terminator) }; - Account { - address: Address::repeat_byte(0xfe), - balance: Word::from(10).pow(20.into()), - code: bytecode.to_vec().into(), - ..Default::default() - } + Account::mock_100_ether(bytecode) } fn oog_constant_internal_call(caller: Account, callee: Account) { @@ -243,15 +238,7 @@ mod test { } fn callee(code: Bytecode) -> Account { - let code = code.to_vec(); - let is_empty = code.is_empty(); - Account { - address: Address::repeat_byte(0xff), - code: code.into(), - nonce: U64::from(!is_empty as u64), - balance: if is_empty { 0 } else { 0xdeadbeefu64 }.into(), - ..Default::default() - } + Account::mock_code_balance(code) } #[test] diff --git a/zkevm-circuits/src/evm_circuit/execution/error_oog_log.rs b/zkevm-circuits/src/evm_circuit/execution/error_oog_log.rs index 8d3db42960..dd4ef88ad0 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_oog_log.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_oog_log.rs @@ -145,7 +145,7 @@ mod test { use bus_mapping::evm::OpcodeId; use eth_types::{ self, address, bytecode, bytecode::Bytecode, evm_types::GasCost, geth_types::Account, - Address, ToWord, Word, U64, + Address, ToWord, Word, }; use mock::{ @@ -252,12 +252,7 @@ mod test { .write_op(terminator) }; - Account { - address: Address::repeat_byte(0xfe), - balance: Word::from(10).pow(20.into()), - code: bytecode.to_vec().into(), - ..Default::default() - } + Account::mock_100_ether(bytecode) } fn oog_log_internal_call(caller: Account, callee: Account) { @@ -284,15 +279,7 @@ mod test { } fn callee(code: Bytecode) -> Account { - let code = code.to_vec(); - let is_empty = code.is_empty(); - Account { - address: Address::repeat_byte(0xff), - code: code.into(), - nonce: U64::from(!is_empty as u64), - balance: if is_empty { 0 } else { 0xdeadbeefu64 }.into(), - ..Default::default() - } + Account::mock_code_balance(code) } #[test] diff --git a/zkevm-circuits/src/evm_circuit/execution/error_stack.rs b/zkevm-circuits/src/evm_circuit/execution/error_stack.rs index e74cb392f7..60235e8fce 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_stack.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_stack.rs @@ -75,7 +75,6 @@ mod test { use bus_mapping::{circuit_input_builder::FixedCParams, evm::OpcodeId}; use eth_types::{ self, address, bytecode, bytecode::Bytecode, geth_types::Account, Address, ToWord, Word, - U64, }; use mock::TestContext; @@ -185,12 +184,7 @@ mod test { .write_op(terminator) }; - Account { - address: Address::repeat_byte(0xfe), - balance: Word::from(10).pow(20.into()), - code: bytecode.to_vec().into(), - ..Default::default() - } + Account::mock_100_ether(bytecode) } fn stack_error_internal_call(caller: Account, callee: Account) { @@ -217,15 +211,7 @@ mod test { } fn callee(code: Bytecode) -> Account { - let code = code.to_vec(); - let is_empty = code.is_empty(); - Account { - address: Address::repeat_byte(0xff), - code: code.into(), - nonce: U64::from(!is_empty as u64), - balance: if is_empty { 0 } else { 0xdeadbeefu64 }.into(), - ..Default::default() - } + Account::mock_code_balance(code) } // internal call error test diff --git a/zkevm-circuits/src/evm_circuit/execution/error_write_protection.rs b/zkevm-circuits/src/evm_circuit/execution/error_write_protection.rs index 91a1181878..6182e413d5 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_write_protection.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_write_protection.rs @@ -144,21 +144,12 @@ impl ExecutionGadget for ErrorWriteProtectionGadget { mod test { use crate::test_util::CircuitTestBuilder; use eth_types::{ - address, bytecode, bytecode::Bytecode, geth_types::Account, Address, ToWord, Word, U64, + address, bytecode, bytecode::Bytecode, geth_types::Account, Address, ToWord, Word, }; use mock::TestContext; fn callee(code: Bytecode) -> Account { - let code = code.to_vec(); - let is_empty = code.is_empty(); - - Account { - address: Address::repeat_byte(0xff), - code: code.into(), - nonce: U64::from(!is_empty as u64), - balance: if is_empty { 0 } else { 0xdeadbeefu64 }.into(), - ..Default::default() - } + Account::mock_code_balance(code) } #[test] @@ -213,12 +204,7 @@ mod test { } test_ok( - Account { - address: Address::repeat_byte(0xfe), - balance: Word::from(10).pow(20.into()), - code: caller_bytecode.into(), - ..Default::default() - }, + Account::mock_100_ether(caller_bytecode), callee(callee_bytecode), ); } diff --git a/zkevm-circuits/src/evm_circuit/execution/extcodecopy.rs b/zkevm-circuits/src/evm_circuit/execution/extcodecopy.rs index 25543d8419..803a9fc050 100644 --- a/zkevm-circuits/src/evm_circuit/execution/extcodecopy.rs +++ b/zkevm-circuits/src/evm_circuit/execution/extcodecopy.rs @@ -209,10 +209,9 @@ impl ExecutionGadget for ExtcodecopyGadget { } else { block .bytecodes - .get(&code_hash) + .get_from_u256(&code_hash) .expect("could not find external bytecode") - .bytes - .len() as u64 + .codesize() as u64 }; self.code_size .assign(region, offset, Value::known(F::from(code_size)))?; diff --git a/zkevm-circuits/src/evm_circuit/execution/stop.rs b/zkevm-circuits/src/evm_circuit/execution/stop.rs index 464b936f74..d29a9c23c5 100644 --- a/zkevm-circuits/src/evm_circuit/execution/stop.rs +++ b/zkevm-circuits/src/evm_circuit/execution/stop.rs @@ -20,7 +20,7 @@ use crate::{ }, }; use bus_mapping::evm::OpcodeId; -use eth_types::{Field, ToWord}; +use eth_types::Field; use halo2_proofs::{circuit::Value, plonk::Error}; #[derive(Clone, Debug)] @@ -108,18 +108,18 @@ impl ExecutionGadget for StopGadget { ) -> Result<(), Error> { let code = block .bytecodes - .get(&call.code_hash.to_word()) + .get_from_h256(&call.code_hash) .expect("could not find current environment's bytecode"); self.code_length.assign( region, offset, - Value::known(F::from(code.bytes.len() as u64)), + Value::known(F::from(code.codesize() as u64)), )?; self.is_out_of_range.assign( region, offset, - F::from(code.bytes.len() as u64) - F::from(step.pc), + F::from(code.codesize() as u64) - F::from(step.pc), )?; let opcode = step.opcode().unwrap(); diff --git a/zkevm-circuits/src/table.rs b/zkevm-circuits/src/table.rs index 2d64b58750..38ae3ab5f3 100644 --- a/zkevm-circuits/src/table.rs +++ b/zkevm-circuits/src/table.rs @@ -9,9 +9,7 @@ use crate::{ word::{self, Word}, Challenges, }, - witness::{ - Block, BlockContext, Bytecode, MptUpdateRow, MptUpdates, Rw, RwMap, RwRow, Transaction, - }, + witness::{Block, BlockContext, MptUpdateRow, MptUpdates, Rw, RwMap, RwRow, Transaction}, }; use bus_mapping::circuit_input_builder::{CopyDataType, CopyEvent, CopyStep}; use core::iter::once; diff --git a/zkevm-circuits/src/table/bytecode_table.rs b/zkevm-circuits/src/table/bytecode_table.rs index d96443deb7..3599180297 100644 --- a/zkevm-circuits/src/table/bytecode_table.rs +++ b/zkevm-circuits/src/table/bytecode_table.rs @@ -1,4 +1,6 @@ use super::*; +use crate::util; +use bus_mapping::state_db::CodeDB; /// Tag to identify the field in a Bytecode Table row #[derive(Clone, Copy, Debug)] @@ -41,10 +43,10 @@ impl BytecodeTable { /// Assign the `BytecodeTable` from a list of bytecodes, followig the same /// table layout that the Bytecode Circuit uses. - pub fn load<'a, F: Field>( + pub fn load( &self, layouter: &mut impl Layouter, - bytecodes: impl IntoIterator + Clone, + bytecodes: CodeDB, ) -> Result<(), Error> { layouter.assign_region( || "bytecode table", @@ -62,14 +64,38 @@ impl BytecodeTable { let bytecode_table_columns = >::advice_columns(self); - for bytecode in bytecodes.clone() { - for row in bytecode.table_assignments::() { - for (&column, value) in bytecode_table_columns.iter().zip_eq(row) { + for bytecode in bytecodes.clone().into_iter() { + let rows = { + let code_hash = util::word::Word::from(bytecode.hash()); + std::iter::once([ + code_hash.lo(), + code_hash.hi(), + F::from(BytecodeFieldTag::Header as u64), + F::ZERO, + F::ZERO, + F::from(bytecode.codesize() as u64), + ]) + .chain(bytecode.code_vec().iter().enumerate().map( + |(index, &(byte, is_code))| { + [ + code_hash.lo(), + code_hash.hi(), + F::from(BytecodeFieldTag::Byte as u64), + F::from(index as u64), + F::from(is_code.into()), + F::from(byte.into()), + ] + }, + )) + .collect_vec() + }; + for row in rows.iter() { + for (&column, value) in bytecode_table_columns.iter().zip_eq(row.to_vec()) { region.assign_advice( || format!("bytecode table row {}", offset), column, offset, - || value, + || Value::known(value), )?; } offset += 1; diff --git a/zkevm-circuits/src/witness.rs b/zkevm-circuits/src/witness.rs index 8a1211e3fc..bcf731dd6a 100644 --- a/zkevm-circuits/src/witness.rs +++ b/zkevm-circuits/src/witness.rs @@ -4,8 +4,6 @@ mod block; pub use block::{block_convert, Block, BlockContext}; -mod bytecode; -pub use bytecode::Bytecode; mod mpt; pub use mpt::{MptUpdate, MptUpdateRow, MptUpdates}; mod rw; diff --git a/zkevm-circuits/src/witness/block.rs b/zkevm-circuits/src/witness/block.rs index f2d93faaf7..540f801900 100644 --- a/zkevm-circuits/src/witness/block.rs +++ b/zkevm-circuits/src/witness/block.rs @@ -1,5 +1,4 @@ -use std::collections::HashMap; - +use super::{ExecStep, Rw, RwMap, Transaction}; use crate::{ evm_circuit::{detect_fixed_table_tags, EvmCircuit}, exp_circuit::param::OFFSET_INCREMENT, @@ -9,13 +8,12 @@ use crate::{ }; use bus_mapping::{ circuit_input_builder::{self, CopyEvent, ExpEvent, FixedCParams}, + state_db::CodeDB, Error, }; use eth_types::{Address, Field, ToScalar, Word}; use halo2_proofs::circuit::Value; -use super::{Bytecode, ExecStep, Rw, RwMap, Transaction}; - // TODO: Remove fields that are duplicated in`eth_block` /// Block is the struct used by all circuits, which contains all the needed /// data for witness generation. @@ -33,7 +31,7 @@ pub struct Block { /// Read write events in the RwTable pub rws: RwMap, /// Bytecode used in the block - pub bytecodes: HashMap, + pub bytecodes: CodeDB, /// The block context pub context: BlockContext, /// Copy events for the copy circuit's table. @@ -86,11 +84,8 @@ impl Block { .iter() .map(|tag| tag.build::().count()) .sum(); - let num_rows_required_for_bytecode_table: usize = self - .bytecodes - .values() - .map(|bytecode| bytecode.bytes.len() + 1) - .sum(); + let num_rows_required_for_bytecode_table = + self.bytecodes.num_rows_required_for_bytecode_table(); let num_rows_required_for_copy_table: usize = self.copy_events.iter().map(|c| c.bytes.len() * 2).sum(); let num_rows_required_for_keccak_table: usize = self.keccak_inputs.len(); @@ -252,14 +247,7 @@ pub fn block_convert( txs: block.txs().to_vec(), end_block_not_last: block.block_steps.end_block_not_last.clone(), end_block_last: block.block_steps.end_block_last.clone(), - bytecodes: code_db - .0 - .values() - .map(|v| { - let bytecode = Bytecode::new(v.clone()); - (bytecode.hash, bytecode) - }) - .collect(), + bytecodes: code_db.clone(), copy_events: block.copy_events.clone(), exp_events: block.exp_events.clone(), sha3_inputs: block.sha3_inputs.clone(), diff --git a/zkevm-circuits/src/witness/bytecode.rs b/zkevm-circuits/src/witness/bytecode.rs deleted file mode 100644 index c3531629d2..0000000000 --- a/zkevm-circuits/src/witness/bytecode.rs +++ /dev/null @@ -1,87 +0,0 @@ -use bus_mapping::evm::OpcodeId; -use eth_types::{Field, Word}; -use halo2_proofs::circuit::Value; -use sha3::{Digest, Keccak256}; - -use crate::{table::BytecodeFieldTag, util::word}; - -/// Bytecode -#[derive(Clone, Debug)] -pub struct Bytecode { - /// Hash of bytecode - pub hash: Word, - /// Raw bytes - pub bytes: Vec, -} - -impl Bytecode { - /// Construct from bytecode bytes - pub fn new(bytes: Vec) -> Self { - let hash = Word::from_big_endian(Keccak256::digest(&bytes).as_slice()); - Self { hash, bytes } - } - - /// Assignments for bytecode table - pub fn table_assignments(&self) -> Vec<[Value; 6]> { - let n = 1 + self.bytes.len(); - let mut rows = Vec::with_capacity(n); - - rows.push([ - Value::known(word::Word::from(self.hash).lo()), - Value::known(word::Word::from(self.hash).hi()), - Value::known(F::from(BytecodeFieldTag::Header as u64)), - Value::known(F::ZERO), - Value::known(F::ZERO), - Value::known(F::from(self.bytes.len() as u64)), - ]); - - let mut push_data_left = 0; - for (idx, byte) in self.bytes.iter().enumerate() { - let is_code = push_data_left == 0; - - push_data_left = if is_code { - // push_data_left will be > 0 only if it is a push opcode - OpcodeId::from(*byte).data_len() - } else { - push_data_left - 1 - }; - - rows.push([ - Value::known(word::Word::from(self.hash).lo()), - Value::known(word::Word::from(self.hash).hi()), - Value::known(F::from(BytecodeFieldTag::Byte as u64)), - Value::known(F::from(idx as u64)), - Value::known(F::from(is_code as u64)), - Value::known(F::from(*byte as u64)), - ]) - } - rows - } - - /// get byte value and is_code pair - pub fn get(&self, dest: usize) -> [u8; 2] { - let mut push_data_left = 0; - for (idx, byte) in self.bytes.iter().enumerate() { - let mut is_code = true; - if push_data_left > 0 { - is_code = false; - push_data_left -= 1; - } else if (OpcodeId::PUSH1.as_u8()..=OpcodeId::PUSH32.as_u8()).contains(byte) { - push_data_left = *byte as usize - (OpcodeId::PUSH1.as_u8() - 1) as usize; - } - - if idx == dest { - return [*byte, is_code as u8]; - } - } - - // here dest > bytecodes len - panic!("can not find byte in the bytecodes list") - } -} - -impl From<ð_types::bytecode::Bytecode> for Bytecode { - fn from(b: ð_types::bytecode::Bytecode) -> Self { - Bytecode::new(b.to_vec()) - } -}