From f7761808133743f04f374d15eb8d6ed657f37806 Mon Sep 17 00:00:00 2001 From: jules Date: Fri, 20 Oct 2023 17:19:42 +0200 Subject: [PATCH] Fix witness generation logic, update circuits with missing gates (#33) --- circuit_definitions/Cargo.toml | 1 + .../base_layer/ecrecover.rs | 39 ++++++++- .../base_layer/keccak256_round_function.rs | 4 + src/external_calls.rs | 2 +- .../individual_circuits/events_sort_dedup.rs | 10 ++- .../keccak256_round_function.rs | 85 ++++++++++--------- .../storage_application.rs | 4 +- src/witness/oracle.rs | 12 +-- 8 files changed, 104 insertions(+), 53 deletions(-) diff --git a/circuit_definitions/Cargo.toml b/circuit_definitions/Cargo.toml index 39327bba..bfca0d74 100644 --- a/circuit_definitions/Cargo.toml +++ b/circuit_definitions/Cargo.toml @@ -24,6 +24,7 @@ zk_evm = {git = "https://github.com/matter-labs/era-zk_evm.git", branch = "v1.4. derivative = "*" serde = {version = "1", features = ["derive"]} crossbeam = "0.8" +seq-macro = "0.3.5" [features] default = [] diff --git a/circuit_definitions/src/circuit_definitions/base_layer/ecrecover.rs b/circuit_definitions/src/circuit_definitions/base_layer/ecrecover.rs index 053d430d..98260f2b 100644 --- a/circuit_definitions/src/circuit_definitions/base_layer/ecrecover.rs +++ b/circuit_definitions/src/circuit_definitions/base_layer/ecrecover.rs @@ -15,8 +15,11 @@ pub struct ECRecoverFunctionInstanceSynthesisFunction< _marker: std::marker::PhantomData<(F, R)>, } -use zkevm_circuits::ecrecover::ecrecover_function_entry_point; use zkevm_circuits::ecrecover::input::*; +use zkevm_circuits::ecrecover::{ + decomp_table::*, ecrecover_function_entry_point, naf_abs_div2_table::*, + secp256k1::fixed_base_mul_table::*, +}; impl< F: SmallField, @@ -69,7 +72,10 @@ where share_constants: false, }, ); - + let builder = U8x4FMAGate::configure_builder( + builder, + GatePlacementStrategy::UseGeneralPurposeColumns, + ); let builder = ZeroCheckGate::configure_builder( builder, GatePlacementStrategy::UseGeneralPurposeColumns, @@ -87,6 +93,10 @@ where builder, GatePlacementStrategy::UseGeneralPurposeColumns, ); + let builder = UIntXAddGate::<8>::configure_builder( + builder, + GatePlacementStrategy::UseGeneralPurposeColumns, + ); let builder = DotProductGate::<4>::configure_builder( builder, GatePlacementStrategy::UseGeneralPurposeColumns, @@ -147,6 +157,31 @@ where let table = create_and8_table(); cs.add_lookup_table::(table); + let table = create_naf_abs_div2_table(); + cs.add_lookup_table::(table); + + let table = create_wnaf_decomp_table(); + cs.add_lookup_table::(table); + + seq_macro::seq!(C in 0..32 { + let table = create_fixed_base_mul_table::(); + cs.add_lookup_table::, 3>(table); + let table = create_fixed_base_mul_table::(); + cs.add_lookup_table::, 3>(table); + let table = create_fixed_base_mul_table::(); + cs.add_lookup_table::, 3>(table); + let table = create_fixed_base_mul_table::(); + cs.add_lookup_table::, 3>(table); + let table = create_fixed_base_mul_table::(); + cs.add_lookup_table::, 3>(table); + let table = create_fixed_base_mul_table::(); + cs.add_lookup_table::, 3>(table); + let table = create_fixed_base_mul_table::(); + cs.add_lookup_table::, 3>(table); + let table = create_fixed_base_mul_table::(); + cs.add_lookup_table::, 3>(table); + }); + let table = create_byte_split_table::(); cs.add_lookup_table::, 3>(table); let table = create_byte_split_table::(); diff --git a/circuit_definitions/src/circuit_definitions/base_layer/keccak256_round_function.rs b/circuit_definitions/src/circuit_definitions/base_layer/keccak256_round_function.rs index 7e33d0b3..93037ade 100644 --- a/circuit_definitions/src/circuit_definitions/base_layer/keccak256_round_function.rs +++ b/circuit_definitions/src/circuit_definitions/base_layer/keccak256_round_function.rs @@ -87,6 +87,10 @@ where builder, GatePlacementStrategy::UseGeneralPurposeColumns, ); + let builder = UIntXAddGate::<8>::configure_builder( + builder, + GatePlacementStrategy::UseGeneralPurposeColumns, + ); let builder = SelectionGate::configure_builder( builder, GatePlacementStrategy::UseGeneralPurposeColumns, diff --git a/src/external_calls.rs b/src/external_calls.rs index 004db6c1..ea42ee96 100644 --- a/src/external_calls.rs +++ b/src/external_calls.rs @@ -229,7 +229,7 @@ round_function: R, // used for all queues implementation // dbg!(tools.witness_tracer.vm_snapshots.len()); let (instance_oracles, artifacts) = create_artifacts_from_tracer( - out_of_circuit_vm.witness_tracer, + &mut out_of_circuit_vm.witness_tracer, &round_function, &geometry, ( diff --git a/src/witness/individual_circuits/events_sort_dedup.rs b/src/witness/individual_circuits/events_sort_dedup.rs index ccf3ca36..1a04f066 100644 --- a/src/witness/individual_circuits/events_sort_dedup.rs +++ b/src/witness/individual_circuits/events_sort_dedup.rs @@ -32,7 +32,7 @@ pub fn compute_events_dedup_and_sort< // return singe dummy witness use crate::boojum::gadgets::queue::QueueState; - let initial_fsm_state = EventsDeduplicatorFSMInputOutput::::placeholder_witness(); + let mut initial_fsm_state = EventsDeduplicatorFSMInputOutput::::placeholder_witness(); assert_eq!( take_queue_state_from_simulator(&unsorted_simulator), @@ -44,7 +44,13 @@ pub fn compute_events_dedup_and_sort< take_queue_state_from_simulator(&unsorted_simulator); passthrough_input.intermediate_sorted_queue_state = QueueState::placeholder_witness(); - let final_fsm_state = EventsDeduplicatorFSMInputOutput::::placeholder_witness(); + let mut final_fsm_state = EventsDeduplicatorFSMInputOutput::::placeholder_witness(); + // NOTE: little hack to make the trivial case work. we should really however remove the + // requirement to have trivial events sorter circuits in the scheduler. + initial_fsm_state.lhs_accumulator = [F::ONE; 2]; + initial_fsm_state.rhs_accumulator = [F::ONE; 2]; + final_fsm_state.lhs_accumulator = [F::ONE; 2]; + final_fsm_state.rhs_accumulator = [F::ONE; 2]; let mut passthrough_output = EventsDeduplicatorOutputData::placeholder_witness(); passthrough_output.final_queue_state = QueueState::placeholder_witness(); diff --git a/src/witness/individual_circuits/keccak256_round_function.rs b/src/witness/individual_circuits/keccak256_round_function.rs index e688dff6..0c10c5cc 100644 --- a/src/witness/individual_circuits/keccak256_round_function.rs +++ b/src/witness/individual_circuits/keccak256_round_function.rs @@ -47,9 +47,11 @@ pub fn keccak256_decompose_into_per_circuit_witness< } = el; // we read, then write - if let Some(reads) = reads.as_ref() { - artifacts.keccak_256_memory_queries.extend_from_slice(reads); - } + reads.iter().for_each(|read| { + if let Some(read) = read { + artifacts.keccak_256_memory_queries.push(*read); + } + }); if let Some(writes) = writes.as_ref() { artifacts @@ -204,9 +206,14 @@ pub fn keccak256_decompose_into_per_circuit_witness< assert_eq!(request, _req); // those are refreshed every cycle - let mut input_buffer = zk_evm::zk_evm_abstractions::precompiles::keccak256::Buffer::new(); - use crate::zk_evm::zk_evm_abstractions::precompiles::keccak256::NEW_WORDS_PER_CYCLE; - let mut words_buffer = [0u64; NEW_WORDS_PER_CYCLE]; + use crate::zk_evm::zk_evm_abstractions::precompiles::keccak256::KECCAK_PRECOMPILE_BUFFER_SIZE; + let mut input_buffer = zk_evm::zk_evm_abstractions::precompiles::keccak256::ByteBuffer { + bytes: [0u8; KECCAK_PRECOMPILE_BUFFER_SIZE], + filled: 0, + }; + use crate::zk_evm::zk_evm_abstractions::precompiles::keccak256::MEMORY_READS_PER_CYCLE; + use crate::zk_evm::zk_evm_abstractions::precompiles::keccak256::NUM_WORDS_PER_QUERY; + let mut words_buffer = [0u8; NUM_WORDS_PER_QUERY * MEMORY_READS_PER_CYCLE * 8]; use crate::zk_evm::zk_evm_abstractions::precompiles::precompile_abi_in_log; let mut precompile_request = precompile_abi_in_log(request); @@ -219,6 +226,9 @@ pub fn keccak256_decompose_into_per_circuit_witness< precompile_state = Keccak256PrecompileState::RunRoundFunction; + let needs_full_padding_round = + precompile_request.input_memory_length as usize % KECCAK_PRECOMPILE_BUFFER_SIZE == 0; + for (round_idx, round) in round_witness.into_iter().enumerate() { // we proceed the request as long as we can if round_idx == 0 { @@ -226,18 +236,15 @@ pub fn keccak256_decompose_into_per_circuit_witness< } // simulate absorb - if input_buffer.can_read_into() { + if input_buffer.can_fill_bytes(NUM_WORDS_PER_QUERY * MEMORY_READS_PER_CYCLE * 8) { use crate::zk_evm::zk_evm_abstractions::precompiles::keccak256::NUM_WORDS_PER_QUERY; - assert!(round.reads.is_some()); - let reads = round.reads.unwrap(); - for (query_index, read) in reads.into_iter().enumerate() { - let data = read.value; - let mut bytes32_buffer = [0u8; 32]; - data.to_big_endian(&mut bytes32_buffer[..]); - for (i, chunk) in bytes32_buffer.chunks(8).enumerate() { - let as_u64 = u64::from_le_bytes(chunk.try_into().unwrap()); - words_buffer[query_index * NUM_WORDS_PER_QUERY + i] = as_u64; + for (query_index, read) in round.reads.into_iter().enumerate() { + if read.is_none() { + continue; } + let read = read.unwrap(); + let data = read.value; + data.to_big_endian(&mut words_buffer[..]); let read_query = memory_queries_it.next().unwrap(); assert_eq!(read, read_query); @@ -255,10 +262,14 @@ pub fn keccak256_decompose_into_per_circuit_witness< precompile_request.input_memory_offset += 1; } - input_buffer.append(&words_buffer); + input_buffer.fill_with_bytes(&words_buffer, 0, 32); } - let words = input_buffer.consume_rate(); + let words: Vec = input_buffer + .consume::() + .chunks(8) + .map(|chunk| u64::from_le_bytes(chunk.try_into().unwrap())) + .collect(); use crate::zk_evm::zk_evm_abstractions::precompiles::keccak256::KECCAK_RATE_IN_U64_WORDS; let mut block = [0u8; KECCAK_RATE_IN_U64_WORDS * 8]; @@ -327,8 +338,8 @@ pub fn keccak256_decompose_into_per_circuit_witness< for el in u64_words_buffer_markers.iter_mut() { *el = false; } - for el in input_buffer.words.iter_mut() { - *el = 0u64; + for el in input_buffer.bytes.iter_mut() { + *el = 0u8; } // internal state is a bit more tricky, it'll be a round over empty input let mut internal_state_over_empty_buffer = Keccak256::default(); @@ -354,21 +365,29 @@ pub fn keccak256_decompose_into_per_circuit_witness< let read_precompile_call = precompile_state == Keccak256PrecompileState::GetRequestFromQueue; + let padding_round = read_unaligned_words_for_round + && precompile_request.input_memory_length == 0 + && needs_full_padding_round; + let hidden_fsm_output_state = Keccak256RoundFunctionFSMWitness:: { completed, read_unaligned_words_for_round, + padding_round, keccak_internal_state, read_precompile_call, timestamp_to_use_for_read: request.timestamp.0, timestamp_to_use_for_write: request.timestamp.0 + 1, - u8_words_buffer: buffer_to_bytes(&input_buffer), - u64_words_buffer_markers, precompile_call_params: Keccak256PrecompileCallParamsWitness:: { input_page: precompile_request.memory_page_to_read, - input_offset: precompile_request.input_memory_offset, + input_memory_byte_offset: precompile_request.input_memory_offset, + input_memory_byte_length: precompile_request.input_memory_length, output_page: precompile_request.memory_page_to_write, - output_offset: precompile_request.output_memory_offset, - num_rounds: num_rounds_left as u32, + output_word_offset: precompile_request.output_memory_offset, + needs_full_padding_round, + }, + buffer: zkevm_circuits::keccak256_round_function::buffer::ByteBufferWitness { + bytes: input_buffer.bytes, + filled: input_buffer.filled as u8, }, }; @@ -474,22 +493,6 @@ pub(crate) fn encode_kecca256_inner_state(state: [u64; 25]) -> [[[u8; 8]; 5]; 5] result } -fn buffer_to_bytes( - buffer: &zk_evm::zk_evm_abstractions::precompiles::keccak256::Buffer, -) -> [u8; zkevm_circuits::keccak256_round_function::BYTES_BUFFER_SIZE] { - assert_eq!( - zkevm_circuits::keccak256_round_function::BUFFER_SIZE_IN_U64_WORDS, - buffer.words.len() - ); - - let mut result = [0u8; zkevm_circuits::keccak256_round_function::BYTES_BUFFER_SIZE]; - for (dst, src) in result.array_chunks_mut::<8>().zip(buffer.words.iter()) { - *dst = src.to_le_bytes(); - } - - result -} - // #[cfg(test)] // mod test { // use super::*; diff --git a/src/witness/individual_circuits/storage_application.rs b/src/witness/individual_circuits/storage_application.rs index 071eb1b9..49ad6eea 100644 --- a/src/witness/individual_circuits/storage_application.rs +++ b/src/witness/individual_circuits/storage_application.rs @@ -4,7 +4,9 @@ use crate::boojum::sha3::digest::{FixedOutput, Update}; use crate::boojum::sha3::Keccak256; use crate::witness::individual_circuits::keccak256_round_function::encode_kecca256_inner_state; use crate::witness::tree::*; -use crate::zk_evm::zk_evm_abstractions::precompiles::keccak256::{transmute_state, BUFFER_SIZE}; +use crate::zk_evm::zk_evm_abstractions::precompiles::keccak256::{ + transmute_state, KECCAK_PRECOMPILE_BUFFER_SIZE, +}; use crate::zkevm_circuits::base_structures::state_diff_record::NUM_KECCAK256_ROUNDS_PER_RECORD_ACCUMULATION; use crate::zkevm_circuits::storage_application::input::*; use blake2::Blake2s256; diff --git a/src/witness/oracle.rs b/src/witness/oracle.rs index 42ed9a3e..3a9dcb0c 100644 --- a/src/witness/oracle.rs +++ b/src/witness/oracle.rs @@ -173,7 +173,7 @@ pub fn create_artifacts_from_tracer< F: SmallField, R: BuildableCircuitRoundFunction + AlgebraicRoundFunction, >( - tracer: WitnessTracer, + tracer: &mut WitnessTracer, round_function: &R, geometry: &GeometryConfig, entry_point_decommittment_query: (DecommittmentQuery, Vec), @@ -933,11 +933,11 @@ pub fn create_artifacts_from_tracer< // when it potentially came into and out of scope let mut artifacts = FullBlockArtifacts::::default(); - artifacts.vm_memory_queries_accumulated = vm_memory_queries_accumulated; - artifacts.all_decommittment_queries = decommittment_queries; - artifacts.keccak_round_function_witnesses = keccak_round_function_witnesses; - artifacts.sha256_round_function_witnesses = sha256_round_function_witnesses; - artifacts.ecrecover_witnesses = ecrecover_witnesses; + artifacts.vm_memory_queries_accumulated = vm_memory_queries_accumulated.to_vec(); + artifacts.all_decommittment_queries = decommittment_queries.to_vec(); + artifacts.keccak_round_function_witnesses = keccak_round_function_witnesses.to_vec(); + artifacts.sha256_round_function_witnesses = sha256_round_function_witnesses.to_vec(); + artifacts.ecrecover_witnesses = ecrecover_witnesses.to_vec(); artifacts.original_log_queue = original_log_queue; artifacts.original_log_queue_simulator = original_log_queue_simulator.unwrap_or(LogQueueSimulator::empty());