use std::borrow::{Borrow, BorrowMut}; use std::iter::repeat; use std::marker::PhantomData; use itertools::Itertools; use plonky2::field::extension::{Extendable, FieldExtension}; use plonky2::field::packed::PackedField; use plonky2::field::types::Field; use plonky2::hash::hash_types::RichField; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cpu::columns::{CpuColumnsView, COL_MAP, NUM_CPU_COLUMNS}; use crate::cpu::{ bootstrap_kernel, contextops, control_flow, decode, dup_swap, jumps, membus, memio, modfp254, pc, shift, simple_logic, stack, stack_bounds, syscalls, }; use crate::cross_table_lookup::Column; use crate::memory::segments::Segment; use crate::memory::{NUM_CHANNELS, VALUE_LIMBS}; use crate::stark::Stark; use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; pub fn ctl_data_keccak_sponge() -> Vec> { // When executing KECCAK_GENERAL, the GP memory channels are used as follows: // GP channel 0: stack[-1] = context // GP channel 1: stack[-2] = segment // GP channel 2: stack[-3] = virt // GP channel 3: stack[-4] = len // GP channel 4: pushed = outputs let context = Column::single(COL_MAP.mem_channels[0].value[0]); let segment = Column::single(COL_MAP.mem_channels[1].value[0]); let virt = Column::single(COL_MAP.mem_channels[2].value[0]); let len = Column::single(COL_MAP.mem_channels[3].value[0]); let num_channels = F::from_canonical_usize(NUM_CHANNELS); let timestamp = Column::linear_combination([(COL_MAP.clock, num_channels)]); let mut cols = vec![context, segment, virt, len, timestamp]; cols.extend(COL_MAP.mem_channels[3].value.map(Column::single)); cols } pub fn ctl_filter_keccak_sponge() -> Column { Column::single(COL_MAP.is_keccak_sponge) } pub fn ctl_data_logic() -> Vec> { let mut res = Column::singles([COL_MAP.op.and, COL_MAP.op.or, COL_MAP.op.xor]).collect_vec(); res.extend(Column::singles(COL_MAP.mem_channels[0].value)); res.extend(Column::singles(COL_MAP.mem_channels[1].value)); res.extend(Column::singles(COL_MAP.mem_channels[2].value)); res } pub fn ctl_filter_logic() -> Column { Column::sum([COL_MAP.op.and, COL_MAP.op.or, COL_MAP.op.xor]) } pub const MEM_CODE_CHANNEL_IDX: usize = 0; pub const MEM_GP_CHANNELS_IDX_START: usize = MEM_CODE_CHANNEL_IDX + 1; /// Make the time/channel column for memory lookups. fn mem_time_and_channel(channel: usize) -> Column { let scalar = F::from_canonical_usize(NUM_CHANNELS); let addend = F::from_canonical_usize(channel); Column::linear_combination_with_constant([(COL_MAP.clock, scalar)], addend) } pub fn ctl_data_code_memory() -> Vec> { let mut cols = vec![ Column::constant(F::ONE), // is_read Column::single(COL_MAP.code_context), // addr_context Column::constant(F::from_canonical_u64(Segment::Code as u64)), // addr_segment Column::single(COL_MAP.program_counter), // addr_virtual ]; // Low limb of the value matches the opcode bits cols.push(Column::le_bits(COL_MAP.opcode_bits)); // High limbs of the value are all zero. cols.extend(repeat(Column::constant(F::ZERO)).take(VALUE_LIMBS - 1)); cols.push(mem_time_and_channel(MEM_CODE_CHANNEL_IDX)); cols } pub fn ctl_data_gp_memory(channel: usize) -> Vec> { let channel_map = COL_MAP.mem_channels[channel]; let mut cols: Vec<_> = Column::singles([ channel_map.is_read, channel_map.addr_context, channel_map.addr_segment, channel_map.addr_virtual, ]) .collect(); cols.extend(Column::singles(channel_map.value)); cols.push(mem_time_and_channel(MEM_GP_CHANNELS_IDX_START + channel)); cols } pub fn ctl_filter_code_memory() -> Column { Column::single(COL_MAP.is_cpu_cycle) } pub fn ctl_filter_gp_memory(channel: usize) -> Column { Column::single(COL_MAP.mem_channels[channel].used) } #[derive(Copy, Clone, Default)] pub struct CpuStark { pub f: PhantomData, } impl CpuStark { // TODO: Remove? pub fn generate(&self, local_values: &mut [F; NUM_CPU_COLUMNS]) { let local_values: &mut CpuColumnsView<_> = local_values.borrow_mut(); decode::generate(local_values); membus::generate(local_values); stack_bounds::generate(local_values); // Must come after `decode`. } } impl, const D: usize> Stark for CpuStark { const COLUMNS: usize = NUM_CPU_COLUMNS; fn eval_packed_generic( &self, vars: StarkEvaluationVars, yield_constr: &mut ConstraintConsumer

, ) where FE: FieldExtension, P: PackedField, { let local_values = vars.local_values.borrow(); let next_values = vars.next_values.borrow(); // TODO: Some failing constraints temporarily disabled by using this dummy consumer. let mut dummy_yield_constr = ConstraintConsumer::new(vec![], P::ZEROS, P::ZEROS, P::ZEROS); bootstrap_kernel::eval_bootstrap_kernel(vars, yield_constr); contextops::eval_packed(local_values, next_values, yield_constr); control_flow::eval_packed_generic(local_values, next_values, yield_constr); decode::eval_packed_generic(local_values, &mut dummy_yield_constr); dup_swap::eval_packed(local_values, yield_constr); jumps::eval_packed(local_values, next_values, &mut dummy_yield_constr); membus::eval_packed(local_values, yield_constr); memio::eval_packed(local_values, yield_constr); modfp254::eval_packed(local_values, yield_constr); pc::eval_packed(local_values, yield_constr); shift::eval_packed(local_values, yield_constr); simple_logic::eval_packed(local_values, yield_constr); stack::eval_packed(local_values, &mut dummy_yield_constr); stack_bounds::eval_packed(local_values, &mut dummy_yield_constr); syscalls::eval_packed(local_values, next_values, yield_constr); } fn eval_ext_circuit( &self, builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, vars: StarkEvaluationTargets, yield_constr: &mut RecursiveConstraintConsumer, ) { let local_values = vars.local_values.borrow(); let next_values = vars.next_values.borrow(); // TODO: Some failing constraints temporarily disabled by using this dummy consumer. let zero = builder.zero_extension(); let mut dummy_yield_constr = RecursiveConstraintConsumer::new(zero, vec![], zero, zero, zero); bootstrap_kernel::eval_bootstrap_kernel_circuit(builder, vars, yield_constr); contextops::eval_ext_circuit(builder, local_values, next_values, yield_constr); control_flow::eval_ext_circuit(builder, local_values, next_values, yield_constr); decode::eval_ext_circuit(builder, local_values, &mut dummy_yield_constr); dup_swap::eval_ext_circuit(builder, local_values, yield_constr); jumps::eval_ext_circuit(builder, local_values, next_values, &mut dummy_yield_constr); membus::eval_ext_circuit(builder, local_values, yield_constr); memio::eval_ext_circuit(builder, local_values, yield_constr); modfp254::eval_ext_circuit(builder, local_values, yield_constr); pc::eval_ext_circuit(builder, local_values, yield_constr); shift::eval_ext_circuit(builder, local_values, yield_constr); simple_logic::eval_ext_circuit(builder, local_values, yield_constr); stack::eval_ext_circuit(builder, local_values, &mut dummy_yield_constr); stack_bounds::eval_ext_circuit(builder, local_values, &mut dummy_yield_constr); syscalls::eval_ext_circuit(builder, local_values, next_values, yield_constr); } fn constraint_degree(&self) -> usize { 3 } } #[cfg(test)] mod tests { use anyhow::Result; use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; use crate::cpu::cpu_stark::CpuStark; use crate::stark_testing::{test_stark_circuit_constraints, test_stark_low_degree}; #[test] fn test_stark_degree() -> Result<()> { const D: usize = 2; type C = PoseidonGoldilocksConfig; type F = >::F; type S = CpuStark; let stark = S { f: Default::default(), }; test_stark_low_degree(stark) } #[test] fn test_stark_circuit() -> Result<()> { const D: usize = 2; type C = PoseidonGoldilocksConfig; type F = >::F; type S = CpuStark; let stark = S { f: Default::default(), }; test_stark_circuit_constraints::(stark) } }