diff --git a/evm/src/cpu/bootstrap_kernel.rs b/evm/src/cpu/bootstrap_kernel.rs index 0a894553..5372d075 100644 --- a/evm/src/cpu/bootstrap_kernel.rs +++ b/evm/src/cpu/bootstrap_kernel.rs @@ -14,17 +14,22 @@ use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer use crate::cpu::columns::{CpuColumnsView, NUM_CPU_COLUMNS}; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::keccak_util::keccakf_u32s; -use crate::generation::state::GenerationState; use crate::keccak_sponge::columns::KECCAK_RATE_U32S; use crate::memory::segments::Segment; use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; +use crate::witness::memory::{MemoryAddress, MemoryState}; +use crate::witness::traces::Traces; +use crate::witness::util::mem_write_gp_log_and_fill; /// We can't process more than `NUM_CHANNELS` bytes per row, since that's all the memory bandwidth /// we have. We also can't process more than 4 bytes (or the number of bytes in a `u32`), since we /// want them to fit in a single limb of Keccak input. const BYTES_PER_ROW: usize = 4; -pub(crate) fn generate_bootstrap_kernel(state: &mut GenerationState) { +pub(crate) fn generate_bootstrap_kernel( + memory: &mut MemoryState, + traces: &mut Traces, +) { let mut sponge_state = [0u32; 50]; let mut sponge_input_pos: usize = 0; @@ -35,30 +40,41 @@ pub(crate) fn generate_bootstrap_kernel(state: &mut GenerationState .enumerate() .chunks(BYTES_PER_ROW) { - state.current_cpu_row.is_bootstrap_kernel = F::ONE; + let mut current_cpu_row = CpuColumnsView::default(); + current_cpu_row.is_bootstrap_kernel = F::ONE; // Write this chunk to memory, while simultaneously packing its bytes into a u32 word. let mut packed_bytes: u32 = 0; for (channel, (addr, &byte)) in chunk.enumerate() { - state.set_mem_cpu_current(channel, Segment::Code, addr, byte.into()); + let address = MemoryAddress::new(0, Segment::Code as usize, addr); + let write = mem_write_gp_log_and_fill( + channel, + address, + traces, + &mut current_cpu_row, + byte.into(), + ); + memory.set(address, byte.into()); + traces.push_memory(write); packed_bytes = (packed_bytes << 8) | byte as u32; } sponge_state[sponge_input_pos] = packed_bytes; - let keccak = state.current_cpu_row.general.keccak_mut(); + let keccak = current_cpu_row.general.keccak_mut(); keccak.input_limbs = sponge_state.map(F::from_canonical_u32); - state.commit_cpu_row(); sponge_input_pos = (sponge_input_pos + 1) % KECCAK_RATE_U32S; // If we just crossed a multiple of KECCAK_RATE_LIMBS, then we've filled the Keccak input // buffer, so it's time to absorb. if sponge_input_pos == 0 { - state.current_cpu_row.is_keccak = F::ONE; + current_cpu_row.is_keccak = F::ONE; keccakf_u32s(&mut sponge_state); - let keccak = state.current_cpu_row.general.keccak_mut(); + let keccak = current_cpu_row.general.keccak_mut(); keccak.output_limbs = sponge_state.map(F::from_canonical_u32); } + + traces.push_cpu(current_cpu_row); } } diff --git a/evm/src/cpu/columns/mod.rs b/evm/src/cpu/columns/mod.rs index c485eca9..cee1f2c5 100644 --- a/evm/src/cpu/columns/mod.rs +++ b/evm/src/cpu/columns/mod.rs @@ -6,6 +6,8 @@ use std::fmt::Debug; use std::mem::{size_of, transmute}; use std::ops::{Index, IndexMut}; +use plonky2::field::types::Field; + use crate::cpu::columns::general::CpuGeneralColumnsView; use crate::cpu::columns::ops::OpsColumnsView; use crate::cpu::membus::NUM_GP_CHANNELS; @@ -82,6 +84,12 @@ pub struct CpuColumnsView { // `u8` is guaranteed to have a `size_of` of 1. pub const NUM_CPU_COLUMNS: usize = size_of::>(); +impl Default for CpuColumnsView { + fn default() -> Self { + Self::from([F::ZERO; NUM_CPU_COLUMNS]) + } +} + impl From<[T; NUM_CPU_COLUMNS]> for CpuColumnsView { fn from(value: [T; NUM_CPU_COLUMNS]) -> Self { unsafe { transmute_no_compile_time_size_checks(value) } diff --git a/evm/src/generation/memory.rs b/evm/src/generation/memory.rs index 944b42a6..8873192e 100644 --- a/evm/src/generation/memory.rs +++ b/evm/src/generation/memory.rs @@ -1,50 +1,51 @@ -use ethereum_types::U256; - -use crate::memory::memory_stark::MemoryOp; -use crate::memory::segments::Segment; - -#[allow(unused)] // TODO: Should be used soon. -#[derive(Debug)] -pub(crate) struct MemoryState { - /// A log of each memory operation, in the order that it occurred. - pub log: Vec, - - pub contexts: Vec, -} - -impl Default for MemoryState { - fn default() -> Self { - Self { - log: vec![], - // We start with an initial context for the kernel. - contexts: vec![MemoryContextState::default()], - } - } -} - -#[derive(Clone, Default, Debug)] -pub(crate) struct MemoryContextState { - /// The content of each memory segment. - pub segments: [MemorySegmentState; Segment::COUNT], -} - -#[derive(Clone, Default, Debug)] -pub(crate) struct MemorySegmentState { - pub content: Vec, -} - -impl MemorySegmentState { - pub(crate) fn get(&self, virtual_addr: usize) -> U256 { - self.content - .get(virtual_addr) - .copied() - .unwrap_or(U256::zero()) - } - - pub(crate) fn set(&mut self, virtual_addr: usize, value: U256) { - if virtual_addr >= self.content.len() { - self.content.resize(virtual_addr + 1, U256::zero()); - } - self.content[virtual_addr] = value; - } -} +// TODO: Remove? +// use ethereum_types::U256; +// +// use crate::memory::memory_stark::MemoryOp; +// use crate::memory::segments::Segment; +// +// #[allow(unused)] // TODO: Should be used soon. +// #[derive(Debug)] +// pub(crate) struct MemoryState { +// /// A log of each memory operation, in the order that it occurred. +// pub log: Vec, +// +// pub contexts: Vec, +// } +// +// impl Default for MemoryState { +// fn default() -> Self { +// Self { +// log: vec![], +// // We start with an initial context for the kernel. +// contexts: vec![MemoryContextState::default()], +// } +// } +// } +// +// #[derive(Clone, Default, Debug)] +// pub(crate) struct MemoryContextState { +// /// The content of each memory segment. +// pub segments: [MemorySegmentState; Segment::COUNT], +// } +// +// #[derive(Clone, Default, Debug)] +// pub(crate) struct MemorySegmentState { +// pub content: Vec, +// } +// +// impl MemorySegmentState { +// pub(crate) fn get(&self, virtual_addr: usize) -> U256 { +// self.content +// .get(virtual_addr) +// .copied() +// .unwrap_or(U256::zero()) +// } +// +// pub(crate) fn set(&mut self, virtual_addr: usize, value: U256) { +// if virtual_addr >= self.content.len() { +// self.content.resize(virtual_addr + 1, U256::zero()); +// } +// self.content[virtual_addr] = value; +// } +// } diff --git a/evm/src/generation/mod.rs b/evm/src/generation/mod.rs index 75f434d7..f805a101 100644 --- a/evm/src/generation/mod.rs +++ b/evm/src/generation/mod.rs @@ -4,7 +4,6 @@ use eth_trie_utils::partial_trie::PartialTrie; use ethereum_types::{Address, BigEndianHash, H256}; use plonky2::field::extension::Extendable; use plonky2::field::polynomial::PolynomialValues; -use plonky2::field::types::Field; use plonky2::hash::hash_types::RichField; use plonky2::util::timing::TimingTree; use serde::{Deserialize, Serialize}; @@ -12,13 +11,15 @@ use serde::{Deserialize, Serialize}; use crate::all_stark::{AllStark, NUM_TABLES}; use crate::config::StarkConfig; use crate::cpu::bootstrap_kernel::generate_bootstrap_kernel; -use crate::cpu::columns::NUM_CPU_COLUMNS; +use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; -use crate::generation::state::GenerationState; use crate::memory::segments::Segment; -use crate::memory::NUM_CHANNELS; use crate::proof::{BlockMetadata, PublicValues, TrieRoots}; use crate::util::trace_rows_to_poly_values; +use crate::witness::memory::{MemoryAddress, MemoryState}; +use crate::witness::state::RegistersState; +use crate::witness::traces::Traces; +use crate::witness::transition::transition; pub(crate) mod memory; pub(crate) mod mpt; @@ -65,25 +66,33 @@ pub(crate) fn generate_traces, const D: usize>( config: &StarkConfig, timing: &mut TimingTree, ) -> ([Vec>; NUM_TABLES], PublicValues) { - let mut state = GenerationState::::new(inputs.clone()); + // let mut state = GenerationState::::new(inputs.clone()); - generate_bootstrap_kernel::(&mut state); + let mut memory_state = MemoryState::default(); + let mut traces = Traces::::default(); + generate_bootstrap_kernel::(&mut memory_state, &mut traces); - for txn in &inputs.signed_txns { - generate_txn(&mut state, txn); + let mut registers_state = RegistersState::default(); + let halt_pc0 = KERNEL.global_labels["halt_pc0"]; + let halt_pc1 = KERNEL.global_labels["halt_pc1"]; + + loop { + // If we've reached the kernel's halt routine, and our trace length is a power of 2, stop. + let pc = registers_state.program_counter as usize; + let in_halt_loop = pc == halt_pc0 || pc == halt_pc1; + if in_halt_loop && traces.cpu.len().is_power_of_two() { + break; + } + + registers_state = transition(registers_state, &memory_state, &mut traces); } - // TODO: Pad to a power of two, ending in the `halt` kernel function. - - let cpu_rows = state.cpu_rows.len(); - let mem_end_timestamp = cpu_rows * NUM_CHANNELS; - let mut read_metadata = |field| { - state.get_mem( + let read_metadata = |field| { + memory_state.get(MemoryAddress::new( 0, - Segment::GlobalMetadata, + Segment::GlobalMetadata as usize, field as usize, - mem_end_timestamp, - ) + )) }; let trie_roots_before = TrieRoots { @@ -101,43 +110,11 @@ pub(crate) fn generate_traces, const D: usize>( receipts_root: H256::from_uint(&read_metadata(GlobalMetadata::ReceiptTrieRootDigestAfter)), }; - let GenerationState { - cpu_rows, - current_cpu_row, - memory, - keccak_inputs, - keccak_memory_inputs, - logic_ops, - .. - } = state; - assert_eq!(current_cpu_row, [F::ZERO; NUM_CPU_COLUMNS].into()); - - let cpu_trace = trace_rows_to_poly_values(cpu_rows); - let keccak_trace = all_stark.keccak_stark.generate_trace(keccak_inputs, timing); - let keccak_memory_trace = all_stark.keccak_memory_stark.generate_trace( - keccak_memory_inputs, - config.fri_config.num_cap_elements(), - timing, - ); - let logic_trace = all_stark.logic_stark.generate_trace(logic_ops, timing); - let memory_trace = all_stark.memory_stark.generate_trace(memory.log, timing); - let traces = [ - cpu_trace, - keccak_trace, - keccak_memory_trace, - logic_trace, - memory_trace, - ]; - let public_values = PublicValues { trie_roots_before, trie_roots_after, block_metadata: inputs.block_metadata, }; - (traces, public_values) -} - -fn generate_txn(_state: &mut GenerationState, _signed_txn: &[u8]) { - // TODO + (traces.to_tables(all_stark, config, timing), public_values) } diff --git a/evm/src/generation/state.rs b/evm/src/generation/state.rs index 17d63018..06f93fc0 100644 --- a/evm/src/generation/state.rs +++ b/evm/src/generation/state.rs @@ -1,35 +1,21 @@ -use std::mem; - use ethereum_types::U256; use plonky2::field::types::Field; -use tiny_keccak::keccakf; -use crate::cpu::columns::{CpuColumnsView, NUM_CPU_COLUMNS}; -use crate::generation::memory::MemoryState; use crate::generation::mpt::all_mpt_prover_inputs_reversed; use crate::generation::rlp::all_rlp_prover_inputs_reversed; use crate::generation::GenerationInputs; -use crate::keccak_memory::keccak_memory_stark::KeccakMemoryOp; -use crate::memory::memory_stark::MemoryOp; -use crate::memory::segments::Segment; -use crate::memory::NUM_CHANNELS; -use crate::util::u256_limbs; -use crate::{keccak, logic}; +use crate::witness::memory::MemoryState; +use crate::witness::state::RegistersState; +use crate::witness::traces::Traces; #[derive(Debug)] pub(crate) struct GenerationState { - #[allow(unused)] // TODO: Should be used soon. pub(crate) inputs: GenerationInputs, - pub(crate) next_txn_index: usize, - pub(crate) cpu_rows: Vec<[F; NUM_CPU_COLUMNS]>, - pub(crate) current_cpu_row: CpuColumnsView, - - pub(crate) current_context: usize, + pub(crate) registers: RegistersState, pub(crate) memory: MemoryState, + pub(crate) traces: Traces, - pub(crate) keccak_inputs: Vec<[u64; keccak::keccak_stark::NUM_INPUTS]>, - pub(crate) keccak_memory_inputs: Vec, - pub(crate) logic_ops: Vec, + pub(crate) next_txn_index: usize, /// Prover inputs containing MPT data, in reverse order so that the next input can be obtained /// via `pop()`. @@ -47,206 +33,204 @@ impl GenerationState { Self { inputs, - next_txn_index: 0, - cpu_rows: vec![], - current_cpu_row: [F::ZERO; NUM_CPU_COLUMNS].into(), - current_context: 0, + registers: Default::default(), memory: MemoryState::default(), - keccak_inputs: vec![], - keccak_memory_inputs: vec![], - logic_ops: vec![], + traces: Traces::default(), + next_txn_index: 0, mpt_prover_inputs, rlp_prover_inputs, } } - /// Compute logical AND, and record the operation to be added in the logic table later. - #[allow(unused)] // TODO: Should be used soon. - pub(crate) fn and(&mut self, input0: U256, input1: U256) -> U256 { - self.logic_op(logic::Op::And, input0, input1) - } + // TODO: Remove dead code below. - /// Compute logical OR, and record the operation to be added in the logic table later. - #[allow(unused)] // TODO: Should be used soon. - pub(crate) fn or(&mut self, input0: U256, input1: U256) -> U256 { - self.logic_op(logic::Op::Or, input0, input1) - } + // /// Compute logical AND, and record the operation to be added in the logic table later. + // #[allow(unused)] // TODO: Should be used soon. + // pub(crate) fn and(&mut self, input0: U256, input1: U256) -> U256 { + // self.logic_op(logic::Op::And, input0, input1) + // } - /// Compute logical XOR, and record the operation to be added in the logic table later. - #[allow(unused)] // TODO: Should be used soon. - pub(crate) fn xor(&mut self, input0: U256, input1: U256) -> U256 { - self.logic_op(logic::Op::Xor, input0, input1) - } + // /// Compute logical OR, and record the operation to be added in the logic table later. + // #[allow(unused)] // TODO: Should be used soon. + // pub(crate) fn or(&mut self, input0: U256, input1: U256) -> U256 { + // self.logic_op(logic::Op::Or, input0, input1) + // } - /// Compute logical AND, and record the operation to be added in the logic table later. - pub(crate) fn logic_op(&mut self, op: logic::Op, input0: U256, input1: U256) -> U256 { - let operation = logic::Operation::new(op, input0, input1); - let result = operation.result; - self.logic_ops.push(operation); - result - } + // /// Compute logical XOR, and record the operation to be added in the logic table later. + // #[allow(unused)] // TODO: Should be used soon. + // pub(crate) fn xor(&mut self, input0: U256, input1: U256) -> U256 { + // self.logic_op(logic::Op::Xor, input0, input1) + // } - /// Like `get_mem_cpu`, but reads from the current context specifically. - #[allow(unused)] // TODO: Should be used soon. - pub(crate) fn get_mem_cpu_current( - &mut self, - channel_index: usize, - segment: Segment, - virt: usize, - ) -> U256 { - let context = self.current_context; - self.get_mem_cpu(channel_index, context, segment, virt) - } + // /// Compute logical AND, and record the operation to be added in the logic table later. + // pub(crate) fn logic_op(&mut self, op: logic::Op, input0: U256, input1: U256) -> U256 { + // let operation = logic::Operation::new(op, input0, input1); + // let result = operation.result; + // self.logic_ops.push(operation); + // result + // } - /// Simulates the CPU reading some memory through the given channel. Besides logging the memory - /// operation, this also generates the associated registers in the current CPU row. - pub(crate) fn get_mem_cpu( - &mut self, - channel_index: usize, - context: usize, - segment: Segment, - virt: usize, - ) -> U256 { - let timestamp = self.cpu_rows.len() * NUM_CHANNELS + channel_index; - let value = self.get_mem(context, segment, virt, timestamp); + // /// Like `get_mem_cpu`, but reads from the current context specifically. + // #[allow(unused)] // TODO: Should be used soon. + // pub(crate) fn get_mem_cpu_current( + // &mut self, + // channel_index: usize, + // segment: Segment, + // virt: usize, + // ) -> U256 { + // let context = self.current_context; + // self.get_mem_cpu(channel_index, context, segment, virt) + // } - let channel = &mut self.current_cpu_row.mem_channels[channel_index]; - channel.used = F::ONE; - channel.is_read = F::ONE; - channel.addr_context = F::from_canonical_usize(context); - channel.addr_segment = F::from_canonical_usize(segment as usize); - channel.addr_virtual = F::from_canonical_usize(virt); - channel.value = u256_limbs(value); + // /// Simulates the CPU reading some memory through the given channel. Besides logging the memory + // /// operation, this also generates the associated registers in the current CPU row. + // pub(crate) fn get_mem_cpu( + // &mut self, + // channel_index: usize, + // context: usize, + // segment: Segment, + // virt: usize, + // ) -> U256 { + // let timestamp = self.cpu_rows.len() * NUM_CHANNELS + channel_index; + // let value = self.get_mem(context, segment, virt, timestamp); + // + // let channel = &mut self.current_cpu_row.mem_channels[channel_index]; + // channel.used = F::ONE; + // channel.is_read = F::ONE; + // channel.addr_context = F::from_canonical_usize(context); + // channel.addr_segment = F::from_canonical_usize(segment as usize); + // channel.addr_virtual = F::from_canonical_usize(virt); + // channel.value = u256_limbs(value); + // + // value + // } - value - } + // /// Read some memory, and log the operation. + // pub(crate) fn get_mem( + // &mut self, + // context: usize, + // segment: Segment, + // virt: usize, + // timestamp: usize, + // ) -> U256 { + // let value = self.memory.contexts[context].segments[segment as usize].get(virt); + // self.memory.log.push(MemoryOp { + // filter: true, + // timestamp, + // is_read: true, + // context, + // segment, + // virt, + // value, + // }); + // value + // } - /// Read some memory, and log the operation. - pub(crate) fn get_mem( - &mut self, - context: usize, - segment: Segment, - virt: usize, - timestamp: usize, - ) -> U256 { - let value = self.memory.contexts[context].segments[segment as usize].get(virt); - self.memory.log.push(MemoryOp { - filter: true, - timestamp, - is_read: true, - context, - segment, - virt, - value, - }); - value - } + // /// Write some memory within the current execution context, and log the operation. + // pub(crate) fn set_mem_cpu_current( + // &mut self, + // channel_index: usize, + // segment: Segment, + // virt: usize, + // value: U256, + // ) { + // let context = self.current_context; + // self.set_mem_cpu(channel_index, context, segment, virt, value); + // } - /// Write some memory within the current execution context, and log the operation. - pub(crate) fn set_mem_cpu_current( - &mut self, - channel_index: usize, - segment: Segment, - virt: usize, - value: U256, - ) { - let context = self.current_context; - self.set_mem_cpu(channel_index, context, segment, virt, value); - } + // /// Write some memory, and log the operation. + // pub(crate) fn set_mem_cpu( + // &mut self, + // channel_index: usize, + // context: usize, + // segment: Segment, + // virt: usize, + // value: U256, + // ) { + // let timestamp = self.cpu_rows.len() * NUM_CHANNELS + channel_index; + // self.set_mem(context, segment, virt, value, timestamp); + // + // let channel = &mut self.current_cpu_row.mem_channels[channel_index]; + // channel.used = F::ONE; + // channel.is_read = F::ZERO; // For clarity; should already be 0. + // channel.addr_context = F::from_canonical_usize(context); + // channel.addr_segment = F::from_canonical_usize(segment as usize); + // channel.addr_virtual = F::from_canonical_usize(virt); + // channel.value = u256_limbs(value); + // } - /// Write some memory, and log the operation. - pub(crate) fn set_mem_cpu( - &mut self, - channel_index: usize, - context: usize, - segment: Segment, - virt: usize, - value: U256, - ) { - let timestamp = self.cpu_rows.len() * NUM_CHANNELS + channel_index; - self.set_mem(context, segment, virt, value, timestamp); + // /// Write some memory, and log the operation. + // pub(crate) fn set_mem( + // &mut self, + // context: usize, + // segment: Segment, + // virt: usize, + // value: U256, + // timestamp: usize, + // ) { + // self.memory.log.push(MemoryOp { + // filter: true, + // timestamp, + // is_read: false, + // context, + // segment, + // virt, + // value, + // }); + // self.memory.contexts[context].segments[segment as usize].set(virt, value) + // } - let channel = &mut self.current_cpu_row.mem_channels[channel_index]; - channel.used = F::ONE; - channel.is_read = F::ZERO; // For clarity; should already be 0. - channel.addr_context = F::from_canonical_usize(context); - channel.addr_segment = F::from_canonical_usize(segment as usize); - channel.addr_virtual = F::from_canonical_usize(virt); - channel.value = u256_limbs(value); - } + // /// Evaluate the Keccak-f permutation in-place on some data in memory, and record the operations + // /// for the purpose of witness generation. + // #[allow(unused)] // TODO: Should be used soon. + // pub(crate) fn keccak_memory( + // &mut self, + // context: usize, + // segment: Segment, + // virt: usize, + // ) -> [u64; keccak::keccak_stark::NUM_INPUTS] { + // let read_timestamp = self.cpu_rows.len() * NUM_CHANNELS; + // let _write_timestamp = read_timestamp + 1; + // let input = (0..25) + // .map(|i| { + // let bytes = [0, 1, 2, 3, 4, 5, 6, 7].map(|j| { + // let virt = virt + i * 8 + j; + // let byte = self.get_mem(context, segment, virt, read_timestamp); + // debug_assert!(byte.bits() <= 8); + // byte.as_u32() as u8 + // }); + // u64::from_le_bytes(bytes) + // }) + // .collect::>() + // .try_into() + // .unwrap(); + // let output = self.keccak(input); + // self.keccak_memory_inputs.push(KeccakMemoryOp { + // context, + // segment, + // virt, + // read_timestamp, + // input, + // output, + // }); + // // TODO: Write output to memory. + // output + // } - /// Write some memory, and log the operation. - pub(crate) fn set_mem( - &mut self, - context: usize, - segment: Segment, - virt: usize, - value: U256, - timestamp: usize, - ) { - self.memory.log.push(MemoryOp { - filter: true, - timestamp, - is_read: false, - context, - segment, - virt, - value, - }); - self.memory.contexts[context].segments[segment as usize].set(virt, value) - } + // /// Evaluate the Keccak-f permutation, and record the operation for the purpose of witness + // /// generation. + // pub(crate) fn keccak( + // &mut self, + // mut input: [u64; keccak::keccak_stark::NUM_INPUTS], + // ) -> [u64; keccak::keccak_stark::NUM_INPUTS] { + // self.keccak_inputs.push(input); + // keccakf(&mut input); + // input + // } - /// Evaluate the Keccak-f permutation in-place on some data in memory, and record the operations - /// for the purpose of witness generation. - #[allow(unused)] // TODO: Should be used soon. - pub(crate) fn keccak_memory( - &mut self, - context: usize, - segment: Segment, - virt: usize, - ) -> [u64; keccak::keccak_stark::NUM_INPUTS] { - let read_timestamp = self.cpu_rows.len() * NUM_CHANNELS; - let _write_timestamp = read_timestamp + 1; - let input = (0..25) - .map(|i| { - let bytes = [0, 1, 2, 3, 4, 5, 6, 7].map(|j| { - let virt = virt + i * 8 + j; - let byte = self.get_mem(context, segment, virt, read_timestamp); - debug_assert!(byte.bits() <= 8); - byte.as_u32() as u8 - }); - u64::from_le_bytes(bytes) - }) - .collect::>() - .try_into() - .unwrap(); - let output = self.keccak(input); - self.keccak_memory_inputs.push(KeccakMemoryOp { - context, - segment, - virt, - read_timestamp, - input, - output, - }); - // TODO: Write output to memory. - output - } - - /// Evaluate the Keccak-f permutation, and record the operation for the purpose of witness - /// generation. - pub(crate) fn keccak( - &mut self, - mut input: [u64; keccak::keccak_stark::NUM_INPUTS], - ) -> [u64; keccak::keccak_stark::NUM_INPUTS] { - self.keccak_inputs.push(input); - keccakf(&mut input); - input - } - - pub(crate) fn commit_cpu_row(&mut self) { - let mut swapped_row = [F::ZERO; NUM_CPU_COLUMNS].into(); - mem::swap(&mut self.current_cpu_row, &mut swapped_row); - self.cpu_rows.push(swapped_row.into()); - } + // pub(crate) fn commit_cpu_row(&mut self) { + // let mut swapped_row = [F::ZERO; NUM_CPU_COLUMNS].into(); + // mem::swap(&mut self.current_cpu_row, &mut swapped_row); + // self.cpu_rows.push(swapped_row.into()); + // } } diff --git a/evm/src/logic.rs b/evm/src/logic.rs index dc6fc777..1a6c3daa 100644 --- a/evm/src/logic.rs +++ b/evm/src/logic.rs @@ -72,13 +72,23 @@ pub struct LogicStark { pub f: PhantomData, } -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] pub(crate) enum Op { And, Or, Xor, } +impl Op { + pub(crate) fn result(&self, a: U256, b: U256) -> U256 { + match self { + Op::And => a & b, + Op::Or => a | b, + Op::Xor => a ^ b, + } + } +} + #[derive(Debug)] pub(crate) struct Operation { operator: Op, @@ -89,11 +99,7 @@ pub(crate) struct Operation { impl Operation { pub(crate) fn new(operator: Op, input0: U256, input1: U256) -> Self { - let result = match operator { - Op::And => input0 & input1, - Op::Or => input0 | input1, - Op::Xor => input0 ^ input1, - }; + let result = operator.result(input0, input1); Operation { operator, input0, @@ -101,6 +107,31 @@ impl Operation { result, } } + + fn to_row(&self) -> [F; NUM_COLUMNS] { + let Operation { + operator, + input0, + input1, + result, + } = self; + let mut row = [F::ZERO; NUM_COLUMNS]; + row[match operator { + Op::And => columns::IS_AND, + Op::Or => columns::IS_OR, + Op::Xor => columns::IS_XOR, + }] = F::ONE; + for i in 0..256 { + row[columns::INPUT0.start + i] = F::from_bool(input0.bit(i)); + row[columns::INPUT1.start + i] = F::from_bool(input1.bit(i)); + } + let result_limbs: &[u64] = result.as_ref(); + for (i, &limb) in result_limbs.iter().enumerate() { + row[columns::RESULT.start + 2 * i] = F::from_canonical_u32(limb as u32); + row[columns::RESULT.start + 2 * i + 1] = F::from_canonical_u32((limb >> 32) as u32); + } + row + } } impl LogicStark { @@ -128,40 +159,20 @@ impl LogicStark { let mut rows = Vec::with_capacity(padded_len); for op in operations { - rows.push(Self::generate_row(op)); + rows.push(op.to_row()); } // Pad to a power of two. for _ in len..padded_len { - rows.push([F::ZERO; columns::NUM_COLUMNS]); + rows.push([F::ZERO; NUM_COLUMNS]); } rows } - - fn generate_row(operation: Operation) -> [F; columns::NUM_COLUMNS] { - let mut row = [F::ZERO; columns::NUM_COLUMNS]; - match operation.operator { - Op::And => row[columns::IS_AND] = F::ONE, - Op::Or => row[columns::IS_OR] = F::ONE, - Op::Xor => row[columns::IS_XOR] = F::ONE, - } - for (i, col) in columns::INPUT0.enumerate() { - row[col] = F::from_bool(operation.input0.bit(i)); - } - for (i, col) in columns::INPUT1.enumerate() { - row[col] = F::from_bool(operation.input1.bit(i)); - } - for (i, col) in columns::RESULT.enumerate() { - let bit_range = i * PACKED_LIMB_BITS..(i + 1) * PACKED_LIMB_BITS; - row[col] = limb_from_bits_le(bit_range.map(|j| F::from_bool(operation.result.bit(j)))); - } - row - } } impl, const D: usize> Stark for LogicStark { - const COLUMNS: usize = columns::NUM_COLUMNS; + const COLUMNS: usize = NUM_COLUMNS; fn eval_packed_generic( &self, diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index f5455a53..c69ad49e 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -1,6 +1,5 @@ use std::marker::PhantomData; -use ethereum_types::U256; use itertools::Itertools; use maybe_rayon::*; use plonky2::field::extension::{Extendable, FieldExtension}; @@ -20,11 +19,12 @@ use crate::memory::columns::{ COUNTER_PERMUTED, FILTER, IS_READ, NUM_COLUMNS, RANGE_CHECK, RANGE_CHECK_PERMUTED, SEGMENT_FIRST_CHANGE, TIMESTAMP, VIRTUAL_FIRST_CHANGE, }; -use crate::memory::segments::Segment; use crate::memory::VALUE_LIMBS; use crate::permutation::PermutationPair; use crate::stark::Stark; use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; +use crate::witness::memory::MemoryOpKind::Read; +use crate::witness::memory::{MemoryAddress, MemoryOp}; pub fn ctl_data() -> Vec> { let mut res = @@ -43,17 +43,18 @@ pub struct MemoryStark { pub(crate) f: PhantomData, } -#[derive(Clone, Debug)] -pub(crate) struct MemoryOp { - /// true if this is an actual memory operation, or false if it's a padding row. - pub filter: bool, - pub timestamp: usize, - pub is_read: bool, - pub context: usize, - pub segment: Segment, - pub virt: usize, - pub value: U256, -} +// TODO: Remove +// #[derive(Clone, Debug)] +// pub(crate) struct MemoryOp { +// /// true if this is an actual memory operation, or false if it's a padding row. +// pub filter: bool, +// pub timestamp: usize, +// pub is_read: bool, +// pub context: usize, +// pub segment: Segment, +// pub virt: usize, +// pub value: U256, +// } impl MemoryOp { /// Generate a row for a given memory operation. Note that this does not generate columns which @@ -64,10 +65,15 @@ impl MemoryOp { let mut row = [F::ZERO; NUM_COLUMNS]; row[FILTER] = F::from_bool(self.filter); row[TIMESTAMP] = F::from_canonical_usize(self.timestamp); - row[IS_READ] = F::from_bool(self.is_read); - row[ADDR_CONTEXT] = F::from_canonical_usize(self.context); - row[ADDR_SEGMENT] = F::from_canonical_usize(self.segment as usize); - row[ADDR_VIRTUAL] = F::from_canonical_usize(self.virt); + row[IS_READ] = F::from_bool(self.op == Read); + let MemoryAddress { + context, + segment, + virt, + } = self.address; + row[ADDR_CONTEXT] = F::from_canonical_usize(context); + row[ADDR_SEGMENT] = F::from_canonical_usize(segment); + row[ADDR_VIRTUAL] = F::from_canonical_usize(virt); for j in 0..VALUE_LIMBS { row[value_limb(j)] = F::from_canonical_u32((self.value >> (j * 32)).low_u32()); } @@ -80,12 +86,12 @@ fn get_max_range_check(memory_ops: &[MemoryOp]) -> usize { .iter() .tuple_windows() .map(|(curr, next)| { - if curr.context != next.context { - next.context - curr.context - 1 - } else if curr.segment != next.segment { - next.segment as usize - curr.segment as usize - 1 - } else if curr.virt != next.virt { - next.virt - curr.virt - 1 + if curr.address.context != next.address.context { + next.address.context - curr.address.context - 1 + } else if curr.address.segment != next.address.segment { + next.address.segment as usize - curr.address.segment as usize - 1 + } else if curr.address.virt != next.address.virt { + next.address.virt - curr.address.virt - 1 } else { next.timestamp - curr.timestamp - 1 } @@ -140,7 +146,14 @@ impl, const D: usize> MemoryStark { /// Generate most of the trace rows. Excludes a few columns like `COUNTER`, which are generated /// later, after transposing to column-major form. fn generate_trace_row_major(&self, mut memory_ops: Vec) -> Vec<[F; NUM_COLUMNS]> { - memory_ops.sort_by_key(|op| (op.context, op.segment, op.virt, op.timestamp)); + memory_ops.sort_by_key(|op| { + ( + op.address.context, + op.address.segment, + op.address.virt, + op.timestamp, + ) + }); Self::pad_memory_ops(&mut memory_ops); @@ -181,7 +194,7 @@ impl, const D: usize> MemoryStark { memory_ops.push(MemoryOp { filter: false, timestamp: last_op.timestamp + i + 1, - is_read: true, + op: Read, ..last_op }); } diff --git a/evm/src/witness/memory.rs b/evm/src/witness/memory.rs index e23ee62a..2baea733 100644 --- a/evm/src/witness/memory.rs +++ b/evm/src/witness/memory.rs @@ -24,19 +24,37 @@ impl MemoryChannel { } } -pub type MemoryAddress = (u32, u32, u32); +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] +pub struct MemoryAddress { + pub(crate) context: usize, + pub(crate) segment: usize, + pub(crate) virt: usize, +} -#[derive(Clone, Copy, Debug)] +impl MemoryAddress { + pub(crate) fn new(context: usize, segment: usize, virt: usize) -> Self { + Self { + context, + segment, + virt, + } + } +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum MemoryOpKind { Read, - Write(U256), + Write, } #[derive(Clone, Copy, Debug)] pub struct MemoryOp { - pub timestamp: u64, + /// true if this is an actual memory operation, or false if it's a padding row. + pub filter: bool, + pub timestamp: usize, pub address: MemoryAddress, pub op: MemoryOpKind, + pub value: U256, } impl MemoryOp { @@ -45,17 +63,20 @@ impl MemoryOp { clock: usize, address: MemoryAddress, op: MemoryOpKind, + value: U256, ) -> Self { - let timestamp = (clock * NUM_CHANNELS + channel.index()) as u64; + let timestamp = clock * NUM_CHANNELS + channel.index(); MemoryOp { + filter: true, timestamp, address, op, + value, } } } -#[derive(Clone)] +#[derive(Clone, Default, Debug)] pub struct MemoryState { contents: HashMap, } @@ -66,7 +87,7 @@ impl MemoryState { for (i, &byte) in kernel_code.iter().enumerate() { if byte != 0 { - let address = (0, 0, i as u32); + let address = MemoryAddress::new(0, 0, i); let val = byte.into(); contents.insert(address, val); } diff --git a/evm/src/witness/mod.rs b/evm/src/witness/mod.rs index 1eb6d36a..b9da345e 100644 --- a/evm/src/witness/mod.rs +++ b/evm/src/witness/mod.rs @@ -1,7 +1,7 @@ mod errors; -mod memory; +pub(crate) mod memory; mod operation; -mod state; -mod traces; +pub(crate) mod state; +pub(crate) mod traces; pub mod transition; -mod util; +pub(crate) mod util; diff --git a/evm/src/witness/operation.rs b/evm/src/witness/operation.rs index d52136da..46587e38 100644 --- a/evm/src/witness/operation.rs +++ b/evm/src/witness/operation.rs @@ -8,7 +8,7 @@ use crate::cpu::simple_logic::eq_iszero::generate_pinv_diff; use crate::logic; use crate::memory::segments::Segment; use crate::witness::errors::ProgramError; -use crate::witness::memory::MemoryState; +use crate::witness::memory::{MemoryAddress, MemoryState}; use crate::witness::state::RegistersState; use crate::witness::traces::Traces; use crate::witness::util::{ @@ -17,7 +17,7 @@ use crate::witness::util::{ }; #[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum Operation { +pub(crate) enum Operation { Dup(u8), Swap(u8), Iszero, @@ -25,53 +25,12 @@ pub enum Operation { Syscall(u8), Eq, ExitKernel, - BinaryLogic(BinaryLogicOp), + BinaryLogic(logic::Op), NotImplemented, } -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum BinaryLogicOp { - And, - Or, - Xor, -} - -impl BinaryLogicOp { - pub(self) fn result(&self, a: U256, b: U256) -> U256 { - match self { - BinaryLogicOp::And => a & b, - BinaryLogicOp::Or => a | b, - BinaryLogicOp::Xor => a ^ b, - } - } -} - -fn make_logic_row( - op: BinaryLogicOp, - in0: U256, - in1: U256, - result: U256, -) -> [F; logic::columns::NUM_COLUMNS] { - let mut row = [F::ZERO; logic::columns::NUM_COLUMNS]; - row[match op { - BinaryLogicOp::And => logic::columns::IS_AND, - BinaryLogicOp::Or => logic::columns::IS_OR, - BinaryLogicOp::Xor => logic::columns::IS_XOR, - }] = F::ONE; - for i in 0..256 { - row[logic::columns::INPUT0.start + i] = F::from_bool(in0.bit(i)); - row[logic::columns::INPUT1.start + i] = F::from_bool(in1.bit(i)); - } - let result_limbs: &[u64] = result.as_ref(); - for (i, &limb) in result_limbs.iter().enumerate() { - row[logic::columns::RESULT.start + 2 * i] = F::from_canonical_u32(limb as u32); - row[logic::columns::RESULT.start + 2 * i + 1] = F::from_canonical_u32((limb >> 32) as u32); - } - row -} - -pub fn generate_binary_logic_op( - op: BinaryLogicOp, +pub(crate) fn generate_binary_logic_op( + op: logic::Op, mut registers_state: RegistersState, memory_state: &MemoryState, traces: &mut Traces, @@ -82,7 +41,7 @@ pub fn generate_binary_logic_op( let result = op.result(in0, in1); let log_out = stack_push_log_and_fill(&mut registers_state, traces, &mut row, result)?; - traces.push_logic(make_logic_row(op, in0, in1, result)); + traces.push_logic(logic::Operation::new(op, in0, in1)); traces.push_memory(log_in0); traces.push_memory(log_in1); traces.push_memory(log_out); @@ -90,7 +49,7 @@ pub fn generate_binary_logic_op( Ok(registers_state) } -pub fn generate_dup( +pub(crate) fn generate_dup( n: u8, mut registers_state: RegistersState, memory_state: &MemoryState, @@ -99,11 +58,11 @@ pub fn generate_dup( ) -> Result { let other_addr_lo = registers_state .stack_len - .checked_sub(1 + (n as u32)) + .checked_sub(1 + (n as usize)) .ok_or(ProgramError::StackUnderflow)?; - let other_addr = ( + let other_addr = MemoryAddress::new( registers_state.context, - Segment::Stack as u32, + Segment::Stack as usize, other_addr_lo, ); @@ -117,7 +76,7 @@ pub fn generate_dup( Ok(registers_state) } -pub fn generate_swap( +pub(crate) fn generate_swap( n: u8, mut registers_state: RegistersState, memory_state: &MemoryState, @@ -126,11 +85,11 @@ pub fn generate_swap( ) -> Result { let other_addr_lo = registers_state .stack_len - .checked_sub(2 + (n as u32)) + .checked_sub(2 + (n as usize)) .ok_or(ProgramError::StackUnderflow)?; - let other_addr = ( + let other_addr = MemoryAddress::new( registers_state.context, - Segment::Stack as u32, + Segment::Stack as usize, other_addr_lo, ); @@ -150,7 +109,7 @@ pub fn generate_swap( Ok(registers_state) } -pub fn generate_not( +pub(crate) fn generate_not( mut registers_state: RegistersState, memory_state: &MemoryState, traces: &mut Traces, @@ -167,7 +126,7 @@ pub fn generate_not( Ok(registers_state) } -pub fn generate_iszero( +pub(crate) fn generate_iszero( mut registers_state: RegistersState, memory_state: &MemoryState, traces: &mut Traces, @@ -190,39 +149,39 @@ pub fn generate_iszero( Ok(registers_state) } -pub fn generate_syscall( +pub(crate) fn generate_syscall( opcode: u8, mut registers_state: RegistersState, memory_state: &MemoryState, traces: &mut Traces, mut row: CpuColumnsView, ) -> Result { - let handler_jumptable_addr = KERNEL.global_labels["syscall_jumptable"] as u32; - let handler_addr_addr = handler_jumptable_addr + (opcode as u32); + let handler_jumptable_addr = KERNEL.global_labels["syscall_jumptable"] as usize; + let handler_addr_addr = handler_jumptable_addr + (opcode as usize); let (handler_addr0, log_in0) = mem_read_gp_with_log_and_fill( 0, - (0, Segment::Code as u32, handler_addr_addr), + MemoryAddress::new(0, Segment::Code as usize, handler_addr_addr), memory_state, traces, &mut row, ); let (handler_addr1, log_in1) = mem_read_gp_with_log_and_fill( 1, - (0, Segment::Code as u32, handler_addr_addr + 1), + MemoryAddress::new(0, Segment::Code as usize, handler_addr_addr + 1), memory_state, traces, &mut row, ); let (handler_addr2, log_in2) = mem_read_gp_with_log_and_fill( 2, - (0, Segment::Code as u32, handler_addr_addr + 2), + MemoryAddress::new(0, Segment::Code as usize, handler_addr_addr + 2), memory_state, traces, &mut row, ); let handler_addr = (handler_addr0 << 16) + (handler_addr1 << 8) + handler_addr2; - let new_program_counter = handler_addr.as_u32(); + let new_program_counter = handler_addr.as_usize(); let syscall_info = U256::from(registers_state.program_counter) + (U256::from(u64::from(registers_state.is_kernel)) << 32); @@ -240,7 +199,7 @@ pub fn generate_syscall( Ok(registers_state) } -pub fn generate_eq( +pub(crate) fn generate_eq( mut registers_state: RegistersState, memory_state: &MemoryState, traces: &mut Traces, @@ -261,7 +220,7 @@ pub fn generate_eq( Ok(registers_state) } -pub fn generate_exit_kernel( +pub(crate) fn generate_exit_kernel( mut registers_state: RegistersState, memory_state: &MemoryState, traces: &mut Traces, @@ -270,7 +229,7 @@ pub fn generate_exit_kernel( let [(kexit_info, log_in)] = stack_pop_with_log_and_fill::<1, _>(&mut registers_state, memory_state, traces, &mut row)?; let kexit_info_u64: [u64; 4] = kexit_info.0; - let program_counter = kexit_info_u64[0] as u32; + let program_counter = kexit_info_u64[0] as usize; let is_kernel_mode_val = (kexit_info_u64[1] >> 32) as u32; assert!(is_kernel_mode_val == 0 || is_kernel_mode_val == 1); let is_kernel_mode = is_kernel_mode_val != 0; diff --git a/evm/src/witness/state.rs b/evm/src/witness/state.rs index 230d1287..6f6843ce 100644 --- a/evm/src/witness/state.rs +++ b/evm/src/witness/state.rs @@ -1,7 +1,20 @@ +use crate::cpu::kernel::aggregator::KERNEL; + #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct RegistersState { - pub program_counter: u32, + pub program_counter: usize, pub is_kernel: bool, - pub stack_len: u32, - pub context: u32, + pub stack_len: usize, + pub context: usize, +} + +impl Default for RegistersState { + fn default() -> Self { + Self { + program_counter: KERNEL.global_labels["main"], + is_kernel: true, + stack_len: 0, + context: 0, + } + } } diff --git a/evm/src/witness/traces.rs b/evm/src/witness/traces.rs index 7049572f..c884cabd 100644 --- a/evm/src/witness/traces.rs +++ b/evm/src/witness/traces.rs @@ -1,9 +1,19 @@ -use crate::arithmetic::columns::NUM_ARITH_COLUMNS; -use crate::cpu::columns::CpuColumnsView; -use crate::logic; -use crate::witness::memory::MemoryOp; +use plonky2::field::extension::Extendable; +use plonky2::field::polynomial::PolynomialValues; +use plonky2::field::types::Field; +use plonky2::hash::hash_types::RichField; +use plonky2::util::timing::TimingTree; + +use crate::all_stark::{AllStark, NUM_TABLES}; +use crate::arithmetic::columns::NUM_ARITH_COLUMNS; +use crate::config::StarkConfig; +use crate::cpu::columns::CpuColumnsView; +use crate::keccak_memory::keccak_memory_stark::KeccakMemoryOp; +use crate::keccak_sponge::keccak_sponge_stark::KeccakSpongeOp; +use crate::util::trace_rows_to_poly_values; +use crate::witness::memory::MemoryOp; +use crate::{keccak, logic}; -type LogicRow = [T; logic::columns::NUM_COLUMNS]; type ArithmeticRow = [T; NUM_ARITH_COLUMNS]; #[derive(Clone, Copy, Debug)] @@ -14,46 +24,54 @@ pub struct TraceCheckpoint { pub(self) memory_len: usize, } -#[derive(Clone, Debug)] -pub struct Traces { - cpu: Vec>, - logic: Vec>, - arithmetic: Vec>, - memory: Vec, +#[derive(Debug)] +pub(crate) struct Traces { + pub(crate) cpu: Vec>, + pub(crate) logic_ops: Vec, + pub(crate) arithmetic: Vec>, + pub(crate) memory_ops: Vec, + pub(crate) keccak_inputs: Vec<[u64; keccak::keccak_stark::NUM_INPUTS]>, + pub(crate) keccak_memory_inputs: Vec, + pub(crate) keccak_sponge_ops: Vec, } impl Traces { pub fn new() -> Self { Traces { cpu: vec![], - logic: vec![], + logic_ops: vec![], arithmetic: vec![], - memory: vec![], + memory_ops: vec![], + keccak_inputs: vec![], + keccak_memory_inputs: vec![], + keccak_sponge_ops: vec![], } } pub fn checkpoint(&self) -> TraceCheckpoint { TraceCheckpoint { cpu_len: self.cpu.len(), - logic_len: self.logic.len(), + logic_len: self.logic_ops.len(), arithmetic_len: self.arithmetic.len(), - memory_len: self.memory.len(), + memory_len: self.memory_ops.len(), + // TODO others } } pub fn rollback(&mut self, checkpoint: TraceCheckpoint) { self.cpu.truncate(checkpoint.cpu_len); - self.logic.truncate(checkpoint.logic_len); + self.logic_ops.truncate(checkpoint.logic_len); self.arithmetic.truncate(checkpoint.arithmetic_len); - self.memory.truncate(checkpoint.memory_len); + self.memory_ops.truncate(checkpoint.memory_len); + // TODO others } pub fn push_cpu(&mut self, val: CpuColumnsView) { self.cpu.push(val); } - pub fn push_logic(&mut self, val: LogicRow) { - self.logic.push(val); + pub fn push_logic(&mut self, val: logic::Operation) { + self.logic_ops.push(val); } pub fn push_arithmetic(&mut self, val: ArithmeticRow) { @@ -61,12 +79,51 @@ impl Traces { } pub fn push_memory(&mut self, val: MemoryOp) { - self.memory.push(val); + self.memory_ops.push(val); } pub fn clock(&self) -> usize { self.cpu.len() } + + pub fn to_tables( + self, + all_stark: &AllStark, + config: &StarkConfig, + timing: &mut TimingTree, + ) -> [Vec>; NUM_TABLES] + where + T: RichField + Extendable, + { + let Traces { + cpu, + logic_ops, + arithmetic, + memory_ops, + keccak_inputs, + keccak_memory_inputs, + keccak_sponge_ops, + } = self; + + let cpu_rows = cpu.into_iter().map(|x| x.into()).collect(); + let cpu_trace = trace_rows_to_poly_values(cpu_rows); + let keccak_trace = all_stark.keccak_stark.generate_trace(keccak_inputs, timing); + let keccak_memory_trace = all_stark.keccak_memory_stark.generate_trace( + keccak_memory_inputs, + config.fri_config.num_cap_elements(), + timing, + ); + let logic_trace = all_stark.logic_stark.generate_trace(logic_ops, timing); + let memory_trace = all_stark.memory_stark.generate_trace(memory_ops, timing); + + [ + cpu_trace, + keccak_trace, + keccak_memory_trace, + logic_trace, + memory_trace, + ] + } } impl Default for Traces { diff --git a/evm/src/witness/transition.rs b/evm/src/witness/transition.rs index 0d9af5ce..b5f6658e 100644 --- a/evm/src/witness/transition.rs +++ b/evm/src/witness/transition.rs @@ -1,18 +1,19 @@ use plonky2::field::types::Field; use crate::cpu::columns::{CpuColumnsView, NUM_CPU_COLUMNS}; +use crate::logic; use crate::memory::segments::Segment; use crate::witness::errors::ProgramError; -use crate::witness::memory::MemoryState; +use crate::witness::memory::{MemoryAddress, MemoryState}; use crate::witness::operation::{ generate_binary_logic_op, generate_dup, generate_eq, generate_exit_kernel, generate_iszero, - generate_not, generate_swap, generate_syscall, BinaryLogicOp, Operation, + generate_not, generate_swap, generate_syscall, Operation, }; use crate::witness::state::RegistersState; use crate::witness::traces::Traces; use crate::witness::util::mem_read_code_with_log_and_fill; -const KERNEL_CONTEXT: u32 = 0; +const KERNEL_CONTEXT: usize = 0; fn read_code_memory( registers_state: RegistersState, @@ -25,11 +26,11 @@ fn read_code_memory( } else { registers_state.context }; - row.code_context = F::from_canonical_u32(code_context); + row.code_context = F::from_canonical_usize(code_context); - let address = ( + let address = MemoryAddress::new( code_context, - Segment::Code as u32, + Segment::Code as usize, registers_state.program_counter, ); let (opcode, mem_log) = mem_read_code_with_log_and_fill(address, memory_state, traces, row); @@ -62,9 +63,9 @@ fn decode(registers_state: RegistersState, opcode: u8) -> Result Ok(Operation::Syscall(opcode)), (0x14, _) => Ok(Operation::Eq), (0x15, _) => Ok(Operation::Iszero), - (0x16, _) => Ok(Operation::BinaryLogic(BinaryLogicOp::And)), - (0x17, _) => Ok(Operation::BinaryLogic(BinaryLogicOp::Or)), - (0x18, _) => Ok(Operation::BinaryLogic(BinaryLogicOp::Xor)), + (0x16, _) => Ok(Operation::BinaryLogic(logic::Op::And)), + (0x17, _) => Ok(Operation::BinaryLogic(logic::Op::Or)), + (0x18, _) => Ok(Operation::BinaryLogic(logic::Op::Xor)), (0x19, _) => Ok(Operation::Not), (0x1a, _) => Ok(Operation::NotImplemented), (0x1b, _) => Ok(Operation::NotImplemented), @@ -151,9 +152,9 @@ fn fill_op_flag(op: Operation, row: &mut CpuColumnsView) { Operation::Syscall(_) => &mut flags.syscall, Operation::Eq => &mut flags.eq, Operation::ExitKernel => &mut flags.exit_kernel, - Operation::BinaryLogic(BinaryLogicOp::And) => &mut flags.and, - Operation::BinaryLogic(BinaryLogicOp::Or) => &mut flags.or, - Operation::BinaryLogic(BinaryLogicOp::Xor) => &mut flags.xor, + Operation::BinaryLogic(logic::Op::And) => &mut flags.and, + Operation::BinaryLogic(logic::Op::Or) => &mut flags.or, + Operation::BinaryLogic(logic::Op::Xor) => &mut flags.xor, Operation::NotImplemented => panic!("operation not implemented"), } = F::ONE; } @@ -199,6 +200,14 @@ fn try_perform_instruction( let opcode = read_code_memory(registers_state, memory_state, traces, &mut row); let op = decode(registers_state, opcode)?; + log::trace!( + "Executing {}={:?} at {}", + opcode, + op, + registers_state.program_counter + ); + // TODO: Temporarily slowing down so we can view logs easily. + std::thread::sleep(std::time::Duration::from_millis(200)); fill_op_flag(op, &mut row); perform_op(op, registers_state, memory_state, traces, row) @@ -212,7 +221,7 @@ fn handle_error( todo!("constraints for exception handling are not implemented"); } -pub fn transition( +pub(crate) fn transition( registers_state: RegistersState, memory_state: &MemoryState, traces: &mut Traces, diff --git a/evm/src/witness/util.rs b/evm/src/witness/util.rs index 94a4259f..c7f8777e 100644 --- a/evm/src/witness/util.rs +++ b/evm/src/witness/util.rs @@ -23,27 +23,27 @@ fn to_bits_le(n: u8) -> [F; 8] { res } -pub fn mem_read_with_log( +pub(crate) fn mem_read_with_log( channel: MemoryChannel, address: MemoryAddress, memory_state: &MemoryState, traces: &Traces, ) -> (U256, MemoryOp) { let val = memory_state.get(address); - let op = MemoryOp::new(channel, traces.clock(), address, MemoryOpKind::Read); + let op = MemoryOp::new(channel, traces.clock(), address, MemoryOpKind::Read, val); (val, op) } -pub fn mem_write_log( +pub(crate) fn mem_write_log( channel: MemoryChannel, address: MemoryAddress, traces: &Traces, val: U256, ) -> MemoryOp { - MemoryOp::new(channel, traces.clock(), address, MemoryOpKind::Write(val)) + MemoryOp::new(channel, traces.clock(), address, MemoryOpKind::Write, val) } -pub fn mem_read_code_with_log_and_fill( +pub(crate) fn mem_read_code_with_log_and_fill( address: MemoryAddress, memory_state: &MemoryState, traces: &Traces, @@ -57,7 +57,7 @@ pub fn mem_read_code_with_log_and_fill( (val_u8, op) } -pub fn mem_read_gp_with_log_and_fill( +pub(crate) fn mem_read_gp_with_log_and_fill( n: usize, address: MemoryAddress, memory_state: &MemoryState, @@ -75,9 +75,9 @@ pub fn mem_read_gp_with_log_and_fill( let channel = &mut row.mem_channels[n]; channel.used = F::ONE; channel.is_read = F::ONE; - channel.addr_context = F::from_canonical_u32(address.0); - channel.addr_segment = F::from_canonical_u32(address.1); - channel.addr_virtual = F::from_canonical_u32(address.2); + channel.addr_context = F::from_canonical_usize(address.context); + channel.addr_segment = F::from_canonical_usize(address.segment); + channel.addr_virtual = F::from_canonical_usize(address.virt); for (i, limb) in val_limbs.into_iter().enumerate() { channel.value[2 * i] = F::from_canonical_u32(limb as u32); channel.value[2 * i + 1] = F::from_canonical_u32((limb >> 32) as u32); @@ -86,7 +86,7 @@ pub fn mem_read_gp_with_log_and_fill( (val, op) } -pub fn mem_write_gp_log_and_fill( +pub(crate) fn mem_write_gp_log_and_fill( n: usize, address: MemoryAddress, traces: &Traces, @@ -99,9 +99,9 @@ pub fn mem_write_gp_log_and_fill( let channel = &mut row.mem_channels[n]; channel.used = F::ONE; channel.is_read = F::ZERO; - channel.addr_context = F::from_canonical_u32(address.0); - channel.addr_segment = F::from_canonical_u32(address.1); - channel.addr_virtual = F::from_canonical_u32(address.2); + channel.addr_context = F::from_canonical_usize(address.context); + channel.addr_segment = F::from_canonical_usize(address.segment); + channel.addr_virtual = F::from_canonical_usize(address.virt); for (i, limb) in val_limbs.into_iter().enumerate() { channel.value[2 * i] = F::from_canonical_u32(limb as u32); channel.value[2 * i + 1] = F::from_canonical_u32((limb >> 32) as u32); @@ -110,7 +110,7 @@ pub fn mem_write_gp_log_and_fill( op } -pub fn stack_pop_with_log_and_fill( +pub(crate) fn stack_pop_with_log_and_fill( registers_state: &mut RegistersState, memory_state: &MemoryState, traces: &Traces, @@ -123,10 +123,10 @@ pub fn stack_pop_with_log_and_fill( let result = { let mut i = 0usize; [(); N].map(|_| { - let address = ( + let address = MemoryAddress::new( registers_state.context, - Segment::Stack as u32, - registers_state.stack_len - 1 - (i as u32), + Segment::Stack as usize, + registers_state.stack_len - 1 - i, ); let res = mem_read_gp_with_log_and_fill(i, address, memory_state, traces, row); i += 1; @@ -134,12 +134,12 @@ pub fn stack_pop_with_log_and_fill( }) }; - registers_state.stack_len -= N as u32; + registers_state.stack_len -= N; Ok(result) } -pub fn stack_push_log_and_fill( +pub(crate) fn stack_push_log_and_fill( registers_state: &mut RegistersState, traces: &Traces, row: &mut CpuColumnsView, @@ -149,9 +149,9 @@ pub fn stack_push_log_and_fill( return Err(ProgramError::StackOverflow); } - let address = ( + let address = MemoryAddress::new( registers_state.context, - Segment::Stack as u32, + Segment::Stack as usize, registers_state.stack_len, ); let res = mem_write_gp_log_and_fill(NUM_GP_CHANNELS - 1, address, traces, row, val); diff --git a/evm/tests/empty_txn_list.rs b/evm/tests/empty_txn_list.rs index 4d8f8e75..2e4de820 100644 --- a/evm/tests/empty_txn_list.rs +++ b/evm/tests/empty_txn_list.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; -use eth_trie_utils::partial_trie::{Nibbles, PartialTrie}; +use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; +use eth_trie_utils::partial_trie::PartialTrie; use plonky2::field::goldilocks_field::GoldilocksField; use plonky2::plonk::config::PoseidonGoldilocksConfig; use plonky2::util::timing::TimingTree; @@ -18,6 +19,8 @@ type C = PoseidonGoldilocksConfig; /// Execute the empty list of transactions, i.e. a no-op. #[test] fn test_empty_txn_list() -> anyhow::Result<()> { + init_logger(); + let all_stark = AllStark::::default(); let config = StarkConfig::standard_fast_config(); @@ -80,3 +83,7 @@ fn test_empty_txn_list() -> anyhow::Result<()> { verify_proof(all_stark, proof, &config) } + +fn init_logger() { + let _ = try_init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "trace")); +}