diff --git a/circuits/src/builtins/poseidon/poseidon_stark.rs b/circuits/src/builtins/poseidon/poseidon_stark.rs index e475f09f..230b7155 100644 --- a/circuits/src/builtins/poseidon/poseidon_stark.rs +++ b/circuits/src/builtins/poseidon/poseidon_stark.rs @@ -1,15 +1,12 @@ -use core::util::poseidon_utils::{ - constant_layer_field, mds_layer_field, mds_partial_layer_fast_field, mds_partial_layer_init, - partial_first_constant_layer, sbox_layer_field, sbox_monomial, POSEIDON_STATE_WIDTH, -}; -use core::vm::opcodes::OlaOpcode; -use std::marker::PhantomData; - use crate::builtins::poseidon::columns::*; use crate::stark::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::stark::cross_table_lookup::Column; use crate::stark::stark::Stark; use crate::stark::vars::{StarkEvaluationTargets, StarkEvaluationVars}; +use core::util::poseidon_utils::{ + constant_layer_field, mds_layer_field, mds_partial_layer_fast_field, mds_partial_layer_init, + partial_first_constant_layer, sbox_layer_field, sbox_monomial, POSEIDON_STATE_WIDTH, +}; use itertools::Itertools; use plonky2::field::extension::{Extendable, FieldExtension}; use plonky2::field::goldilocks_field::GoldilocksField; @@ -18,6 +15,7 @@ use plonky2::field::types::Field; use plonky2::hash::poseidon::Poseidon; use plonky2::hash::{hash_types::RichField, poseidon}; use plonky2::plonk::circuit_builder::CircuitBuilder; +use std::marker::PhantomData; #[derive(Copy, Clone, Default)] pub struct PoseidonStark { diff --git a/circuits/src/builtins/storage/columns.rs b/circuits/src/builtins/storage/columns.rs index 2980a5e3..90afcfd4 100644 --- a/circuits/src/builtins/storage/columns.rs +++ b/circuits/src/builtins/storage/columns.rs @@ -28,7 +28,8 @@ pub(crate) const COL_ST_IS_LAYER_256: usize = COL_ST_IS_LAYER_192 + 1; pub(crate) const COL_ST_ACC_LAYER_MARKER: usize = COL_ST_IS_LAYER_256 + 1; pub(crate) const COL_ST_FILTER_IS_HASH_BIT_0: usize = COL_ST_ACC_LAYER_MARKER + 1; pub(crate) const COL_ST_FILTER_IS_HASH_BIT_1: usize = COL_ST_FILTER_IS_HASH_BIT_0 + 1; -pub(crate) const COL_ST_IS_PADDING: usize = COL_ST_FILTER_IS_HASH_BIT_1 + 1; +pub(crate) const COL_ST_FILTER_IS_FOR_PROG: usize = COL_ST_FILTER_IS_HASH_BIT_1 + 1; +pub(crate) const COL_ST_IS_PADDING: usize = COL_ST_FILTER_IS_FOR_PROG + 1; pub(crate) const NUM_COL_ST: usize = COL_ST_IS_PADDING + 1; pub(crate) fn get_storage_access_col_name_map() -> BTreeMap { @@ -85,6 +86,10 @@ pub(crate) fn get_storage_access_col_name_map() -> BTreeMap { COL_ST_FILTER_IS_HASH_BIT_1, String::from("FILTER_IS_HASH_BIT_1"), ); + m.insert( + COL_ST_FILTER_IS_FOR_PROG, + String::from("FILTER_IS_FOR_PROG"), + ); m.insert(COL_ST_IS_PADDING, String::from("IS_PADDING")); m } diff --git a/circuits/src/builtins/storage/storage_access_stark.rs b/circuits/src/builtins/storage/storage_access_stark.rs index 2bd5433a..61180628 100644 --- a/circuits/src/builtins/storage/storage_access_stark.rs +++ b/circuits/src/builtins/storage/storage_access_stark.rs @@ -20,6 +20,16 @@ use crate::stark::{ use super::columns::*; +pub fn ctl_data_for_prog_chunk() -> Vec> { + let mut res = Column::singles([COL_ST_IS_WRITE]).collect_vec(); + res.extend(Column::singles(COL_ST_ADDR_RANGE.chain(COL_ST_PATH_RANGE))); + res +} + +pub fn ctl_filter_for_prog_chunk() -> Column { + Column::single(COL_ST_FILTER_IS_FOR_PROG) +} + pub fn ctl_data_with_cpu() -> Vec> { let mut res = Column::singles([COL_ST_ACCESS_IDX, COL_ST_IS_WRITE]).collect_vec(); res.extend(Column::singles(COL_ST_ADDR_RANGE.chain(COL_ST_PATH_RANGE))); @@ -27,7 +37,13 @@ pub fn ctl_data_with_cpu() -> Vec> { } pub fn ctl_filter_with_cpu_sstore() -> Column { - Column::single(COL_ST_IS_LAYER_256) + Column::linear_combination_with_constant( + [ + (COL_ST_IS_LAYER_256, F::ONE), + (COL_ST_FILTER_IS_FOR_PROG, F::NEG_ONE), + ], + F::ZERO, + ) } pub fn ctl_data_with_poseidon_bit0() -> Vec> { @@ -109,6 +125,7 @@ impl, const D: usize> Stark for StorageAccess let nv_st_access_idx = nv[COL_ST_ACCESS_IDX]; let lv_layer = lv[COL_ST_LAYER]; let nv_layer = nv[COL_ST_LAYER]; + // is_padding binary and change from 0 to 1 once. yield_constr.constraint((P::ONES - lv_is_padding) * lv_is_padding); yield_constr.constraint_transition( @@ -299,6 +316,9 @@ impl, const D: usize> Stark for StorageAccess ); yield_constr.constraint(lv_is_padding * lv[COL_ST_FILTER_IS_HASH_BIT_0]); yield_constr.constraint(lv_is_padding * lv[COL_ST_FILTER_IS_HASH_BIT_1]); + yield_constr.constraint(lv[COL_ST_FILTER_IS_FOR_PROG] * lv[COL_ST_IS_WRITE]); + yield_constr + .constraint(lv[COL_ST_FILTER_IS_FOR_PROG] * (P::ONES - lv[COL_ST_IS_LAYER_256])); } fn eval_ext_circuit( @@ -323,19 +343,17 @@ mod tests { }, generation::storage::generate_storage_access_trace, stark::stark::Stark, + test_utils::simple_test_stark, }; use core::{ - trace::trace::{StorageHashRow, Trace}, + trace::trace::Trace, types::{Field, GoldilocksField}, }; use std::path::PathBuf; use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; - use crate::{ - stark::{constraint_consumer::ConstraintConsumer, vars::StarkEvaluationVars}, - test_utils::test_stark_with_asm_path, - }; + use crate::stark::{constraint_consumer::ConstraintConsumer, vars::StarkEvaluationVars}; #[test] fn test_storage_with_program() { @@ -360,8 +378,9 @@ mod tests { type S = StorageAccessStark; let stark = S::default(); - let get_trace_rows = |trace: Trace| trace.builtin_storage_hash; - let generate_trace = |rows: &Vec| generate_storage_access_trace(rows); + let generate_trace = |trace: Trace| { + generate_storage_access_trace(&trace.builtin_storage_hash, &trace.builtin_program_hash) + }; let eval_packed_generic = |vars: StarkEvaluationVars, constraint_consumer: &mut ConstraintConsumer| { @@ -379,9 +398,8 @@ mod tests { println!("{:>32}\t{:>22}\t{:>22}", name, lv, nv); } }; - test_stark_with_asm_path( + simple_test_stark( program_path.to_string(), - get_trace_rows, generate_trace, eval_packed_generic, Some(error_hook), diff --git a/circuits/src/cpu/columns.rs b/circuits/src/cpu/columns.rs index d6817425..c9af6005 100644 --- a/circuits/src/cpu/columns.rs +++ b/circuits/src/cpu/columns.rs @@ -127,7 +127,8 @@ pub(crate) const COL_FILTER_TAPE_LOOKING: usize = COL_IS_NEXT_LINE_SAME_TX + 1; pub(crate) const IS_SCCALL_EXT_LINE: usize = COL_FILTER_TAPE_LOOKING + 1; pub(crate) const COL_IS_STORAGE_EXT_LINE: usize = IS_SCCALL_EXT_LINE + 1; pub(crate) const COL_FILTER_SCCALL_END: usize = COL_IS_STORAGE_EXT_LINE + 1; -pub(crate) const COL_IS_PADDING: usize = COL_FILTER_SCCALL_END + 1; +pub(crate) const COL_FILTER_LOOKING_PROG_IMM: usize = COL_FILTER_SCCALL_END + 1; +pub(crate) const COL_IS_PADDING: usize = COL_FILTER_LOOKING_PROG_IMM + 1; pub(crate) const NUM_CPU_COLS: usize = COL_IS_PADDING + 1; @@ -205,6 +206,10 @@ pub(crate) fn get_cpu_col_name_map() -> BTreeMap { m.insert(IS_SCCALL_EXT_LINE, "is_sccall_ext_line".to_string()); m.insert(COL_IS_STORAGE_EXT_LINE, "is_storage_ext_line".to_string()); m.insert(COL_FILTER_SCCALL_END, "filter_sccall_end".to_string()); + m.insert( + COL_FILTER_LOOKING_PROG_IMM, + "filter_looking_prog_imm".to_string(), + ); m.insert(COL_IS_PADDING, "is_padding".to_string()); m } diff --git a/circuits/src/cpu/cpu_stark.rs b/circuits/src/cpu/cpu_stark.rs index c8f90945..e0cea5d1 100644 --- a/circuits/src/cpu/cpu_stark.rs +++ b/circuits/src/cpu/cpu_stark.rs @@ -269,12 +269,29 @@ pub fn ctl_filter_cpu_sccall_end() -> Column { } // get the data source for Rangecheck in Cpu table -pub fn ctl_data_with_program() -> Vec> { - Column::singles([COL_PC, COL_INST, COL_IMM_VAL]).collect_vec() +pub fn ctl_data_inst_to_program() -> Vec> { + Column::singles(COL_ADDR_CODE_RANGE.chain([COL_PC, COL_INST])).collect_vec() } -pub fn ctl_filter_with_program() -> Column { - Column::single(COL_INST) +pub fn ctl_data_imm_to_program() -> Vec> { + let mut res = Column::singles(COL_ADDR_CODE_RANGE).collect_vec(); + res.push(Column::linear_combination_with_constant( + [(COL_PC, F::ONE)], + F::ONE, + )); + res.push(Column::single(COL_IMM_VAL)); + res +} + +pub fn ctl_filter_with_program_inst() -> Column { + Column::linear_combination_with_constant( + [(COL_IS_EXT_LINE, F::NEG_ONE), (COL_IS_PADDING, F::NEG_ONE)], + F::ONE, + ) +} + +pub fn ctl_filter_with_program_imm() -> Column { + Column::single(COL_FILTER_LOOKING_PROG_IMM) } pub(crate) fn ctl_data_cpu_tape_sccall_caller(i: usize) -> Vec> { @@ -828,6 +845,7 @@ impl< + lv[COL_S_SSTORE] + lv[COL_S_TLOAD] * (lv[COL_OP0] * lv[COL_OP1] + (P::ONES - lv[COL_OP0])) + lv[COL_S_TSTORE] * lv[COL_OP1] + + lv[COL_S_TSTORE] * lv[COL_OP1] + lv[COL_S_CALL_SC] + lv[COL_S_END] * (P::ONES - lv_is_entry_sc); let is_crossing_inst = lv[COL_IS_NEXT_LINE_DIFF_INST]; @@ -896,6 +914,17 @@ impl, const D: usize> Stark for CpuStark(steps: &Vec) -> [Vec; cpu::NUM_ } else { F::ZERO }; + trace[cpu::COL_FILTER_LOOKING_PROG_IMM][i] = if s.is_ext_line.0 == 1 { + F::ZERO + } else if s.opcode.0 == OlaOpcode::MLOAD.binary_bit_mask() + || s.opcode.0 == OlaOpcode::MSTORE.binary_bit_mask() + { + F::ONE + } else if s.op1_imm.0 == 1 { + F::ONE + } else { + F::ZERO + }; } // fill in padding. let inst_end = if trace_len == 0 { diff --git a/circuits/src/generation/ctl_test/chunk_poseidon.rs b/circuits/src/generation/ctl_test/chunk_poseidon.rs new file mode 100644 index 00000000..93d4f263 --- /dev/null +++ b/circuits/src/generation/ctl_test/chunk_poseidon.rs @@ -0,0 +1,76 @@ +use core::types::{merkle_tree::decode_addr, Field, GoldilocksField}; + +use crate::{ + builtins::poseidon::columns::*, + generation::{ + poseidon::generate_poseidon_trace, poseidon_chunk::generate_poseidon_chunk_trace, + prog::generate_prog_chunk_trace, + }, + program::columns::*, +}; + +use super::debug_trace_print::{get_exec_trace, get_rows_vec_from_trace, print_title_data}; + +#[test] +fn print_chunk_poseidon_ctl_info() { + let program_file_name: String = "storage_u32.json".to_string(); + let call_data = vec![ + GoldilocksField::from_canonical_u64(0), + GoldilocksField::from_canonical_u64(2364819430), + ]; + + let trace = get_exec_trace(program_file_name, Some(call_data), None); + let poseidon_chunk_cols = generate_poseidon_chunk_trace(&trace.builtin_poseidon_chunk); + let poseidon_chunk_rows = get_rows_vec_from_trace(poseidon_chunk_cols); + + let progs = trace + .addr_program_hash + .into_iter() + .map(|(addr, hash)| (decode_addr(addr), hash)) + .collect::>(); + let prog_chunk_cols = generate_prog_chunk_trace(progs); + let prog_chunk_rows = get_rows_vec_from_trace(prog_chunk_cols); + + let poseidon_cols = generate_poseidon_trace::(&trace.builtin_poseidon); + let poseidon_rows = get_rows_vec_from_trace(poseidon_cols); + + let psdn_chunk_looking_cols: Vec = COL_POSEIDON_CHUNK_VALUE_RANGE + .chain(COL_POSEIDON_CHUNK_CAP_RANGE) + .chain(COL_POSEIDON_CHUNK_HASH_RANGE) + .collect(); + let prog_chunk_looking_cols: Vec = COL_PROG_CHUNK_INST_RANGE + .chain(COL_PROG_CHUNK_CAP_RANGE) + .chain(COL_PROG_CHUNK_HASH_RANGE) + .collect(); + let poseidon_looked_cols: Vec = COL_POSEIDON_INPUT_RANGE + .chain(COL_POSEIDON_OUTPUT_RANGE) + .collect(); + + print_title_data( + "psdn_chunk", + get_poseidon_chunk_col_name_map(), + &poseidon_chunk_rows, + psdn_chunk_looking_cols, + |row: &[GoldilocksField], _| row[COL_POSEIDON_CHUNK_FILTER_LOOKING_POSEIDON].is_one(), + 0, + None, + ); + print_title_data( + "prog_chunk", + get_prog_chunk_col_name_map(), + &prog_chunk_rows, + prog_chunk_looking_cols, + |row: &[GoldilocksField], _| row[COL_PROG_CHUNK_IS_PADDING_LINE].is_zero(), + 0, + None, + ); + print_title_data( + "poseidon", + get_poseidon_col_name_map(), + &poseidon_rows, + poseidon_looked_cols, + |row: &[GoldilocksField], _| row[FILTER_LOOKED_NORMAL].is_one(), + 0, + None, + ); +} diff --git a/circuits/src/generation/ctl_test/cpu_program.rs b/circuits/src/generation/ctl_test/cpu_program.rs new file mode 100644 index 00000000..15c84933 --- /dev/null +++ b/circuits/src/generation/ctl_test/cpu_program.rs @@ -0,0 +1,77 @@ +use core::types::{merkle_tree::decode_addr, Field, GoldilocksField}; + +use crate::{ + cpu::columns::*, + generation::{cpu::generate_cpu_trace, prog::generate_prog_trace}, + program::columns::*, +}; + +use super::debug_trace_print::{get_exec_trace, get_rows_vec_from_trace, print_title_data}; + +#[test] +fn print_cpu_program_ctl_info() { + let program_file_name: String = "storage_u32.json".to_string(); + let call_data = vec![ + GoldilocksField::from_canonical_u64(0), + GoldilocksField::from_canonical_u64(2364819430), + ]; + + let trace = get_exec_trace(program_file_name, Some(call_data), None); + let cpu_cols = generate_cpu_trace::(&trace.exec); + let cpu_rows = get_rows_vec_from_trace(cpu_cols); + + let progs = trace + .addr_program_hash + .into_iter() + .map(|(addr, hash)| (decode_addr(addr), hash)) + .collect::>(); + let progs_for_program = progs.clone(); + let (program_cols, _) = generate_prog_trace::( + &trace.exec, + progs_for_program, + trace.start_end_roots, + ); + let program_rows = get_rows_vec_from_trace(program_cols); + + let insts_looking_cols: Vec = COL_ADDR_CODE_RANGE.chain([COL_PC, COL_INST]).collect(); + let imm_looking_cols: Vec = COL_ADDR_CODE_RANGE.chain([COL_PC, COL_IMM_VAL]).collect(); + let prog_looked_cols: Vec = COL_PROG_EXEC_CODE_ADDR_RANGE + .chain([COL_PROG_EXEC_PC, COL_PROG_EXEC_INST]) + .collect(); + + print_title_data( + "cpu inst", + get_cpu_col_name_map(), + &cpu_rows, + insts_looking_cols, + |row: &[GoldilocksField], _| { + (GoldilocksField::ONE - row[COL_IS_EXT_LINE] - row[COL_IS_PADDING]).is_one() + }, + 0, + None, + ); + print_title_data( + "cpu imm", + get_cpu_col_name_map(), + &cpu_rows, + imm_looking_cols, + |row: &[GoldilocksField], _| row[COL_FILTER_LOOKING_PROG_IMM].is_one(), + 0, + Some(|col, value, _| { + if col == COL_PC { + GoldilocksField::from_canonical_u64(value.0 + 1) + } else { + value + } + }), + ); + print_title_data( + "program", + get_prog_col_name_map(), + &program_rows, + prog_looked_cols, + |row: &[GoldilocksField], _| row[COL_PROG_FILTER_EXEC].is_one(), + 0, + None, + ); +} diff --git a/circuits/src/generation/ctl_test/debug_trace_print.rs b/circuits/src/generation/ctl_test/debug_trace_print.rs index c7f422b8..4eb1d974 100644 --- a/circuits/src/generation/ctl_test/debug_trace_print.rs +++ b/circuits/src/generation/ctl_test/debug_trace_print.rs @@ -11,8 +11,17 @@ use std::{ }; use assembler::encoder::encode_asm_from_json_file; -use core::vm::transaction::init_tx_context; -use executor::{load_tx::init_tape, Process}; +use core::{ + crypto::{hash::Hasher, ZkHasher}, + merkle_tree::log::{StorageLog, WitnessStorageLog}, + types::merkle_tree::{encode_addr, tree_key_default}, + vm::transaction::init_tx_context, +}; +use executor::{ + load_tx::init_tape, + trace::{gen_storage_hash_table, gen_storage_table}, + Process, +}; use plonky2::hash::hash_types::RichField; #[allow(unused)] @@ -161,7 +170,13 @@ pub fn get_exec_trace( }; let program = encode_asm_from_json_file(program_path).unwrap(); + let hash = ZkHasher::default(); let instructions = program.bytecode.split("\n"); + let code: Vec<_> = instructions + .clone() + .map(|e| GoldilocksField::from_canonical_u64(u64::from_str_radix(&e[2..], 16).unwrap())) + .collect(); + let code_hash = hash.hash_bytes(&code); let mut prophets = HashMap::new(); for item in program.prophets { prophets.insert(item.host as u64, item); @@ -175,17 +190,61 @@ pub fn get_exec_trace( let mut process = Process::new(); process.addr_storage = Address::default(); + + let tp_start = 0; + + let callee: Address = [ + GoldilocksField::from_canonical_u64(9), + GoldilocksField::from_canonical_u64(10), + GoldilocksField::from_canonical_u64(11), + GoldilocksField::from_canonical_u64(12), + ]; + let caller_addr = [ + GoldilocksField::from_canonical_u64(17), + GoldilocksField::from_canonical_u64(18), + GoldilocksField::from_canonical_u64(19), + GoldilocksField::from_canonical_u64(20), + ]; + let callee_exe_addr = [ + GoldilocksField::from_canonical_u64(13), + GoldilocksField::from_canonical_u64(14), + GoldilocksField::from_canonical_u64(15), + GoldilocksField::from_canonical_u64(16), + ]; + if let Some(calldata) = call_data { - process.tp = GoldilocksField::ZERO; + process.tp = GoldilocksField::from_canonical_u64(tp_start as u64); + init_tape( &mut process, calldata, - Address::default(), - Address::default(), - Address::default(), + caller_addr, + callee, + callee_exe_addr, &init_tx_context(), ); } + + process.addr_code = callee_exe_addr; + process.addr_storage = callee; + program + .trace + .addr_program_hash + .insert(encode_addr(&callee_exe_addr), code); + + db.process_block(vec![WitnessStorageLog { + storage_log: StorageLog::new_write_log(callee_exe_addr, code_hash), + previous_value: tree_key_default(), + }]); + let _ = db.save(); + + let start = db.root_hash(); + + process.program_log.push(WitnessStorageLog { + storage_log: StorageLog::new_read_log(callee_exe_addr, code_hash), + previous_value: tree_key_default(), + }); + let res = process.execute(&mut program, &mut Some(prophets), &mut db); match res { Ok(_) => {} @@ -193,6 +252,9 @@ pub fn get_exec_trace( println!("execute err:{:?}", e); } } + let hash_roots = gen_storage_hash_table(&mut process, &mut program, &mut db); + gen_storage_table(&mut process, &mut program, hash_roots).unwrap(); + program.trace.start_end_roots = (start, db.root_hash()); return program.trace; } diff --git a/circuits/src/generation/ctl_test/mod.rs b/circuits/src/generation/ctl_test/mod.rs index dea9ec89..9dba1056 100644 --- a/circuits/src/generation/ctl_test/mod.rs +++ b/circuits/src/generation/ctl_test/mod.rs @@ -3,3 +3,6 @@ mod cpu_tape; mod debug_trace_print; mod poseidon_chunk_mem; mod storage_access_poseidon; +mod chunk_poseidon; +mod cpu_program; +mod prog_chunk_program; diff --git a/circuits/src/generation/ctl_test/prog_chunk_program.rs b/circuits/src/generation/ctl_test/prog_chunk_program.rs new file mode 100644 index 00000000..1219c6ae --- /dev/null +++ b/circuits/src/generation/ctl_test/prog_chunk_program.rs @@ -0,0 +1,66 @@ +use core::types::{merkle_tree::decode_addr, Field, GoldilocksField}; + +use crate::{ + generation::prog::{generate_prog_chunk_trace, generate_prog_trace}, + program::columns::*, +}; + +use super::debug_trace_print::{get_exec_trace, get_rows_vec_from_trace, print_title_data}; + +#[test] +fn print_prog_chunk_program_ctl_info() { + let program_file_name: String = "storage_u32.json".to_string(); + let call_data = vec![ + GoldilocksField::from_canonical_u64(0), + GoldilocksField::from_canonical_u64(2364819430), + ]; + + let trace = get_exec_trace(program_file_name, Some(call_data), None); + + let progs = trace + .addr_program_hash + .into_iter() + .map(|(addr, hash)| (decode_addr(addr), hash)) + .collect::>(); + let prog_chunk_cols = generate_prog_chunk_trace(progs.clone()); + let prog_chunk_rows = get_rows_vec_from_trace(prog_chunk_cols); + + let (program_cols, _) = + generate_prog_trace::(&trace.exec, progs, trace.start_end_roots); + let program_rows = get_rows_vec_from_trace(program_cols); + + (0..8).for_each(|i| { + let cols_to_ctl = COL_PROG_CHUNK_CODE_ADDR_RANGE + .chain([COL_PROG_CHUNK_START_PC, COL_PROG_CHUNK_INST_RANGE.start + i]) + .collect(); + print_title_data( + format!("looker src {}", i).as_str(), + get_prog_chunk_col_name_map(), + &prog_chunk_rows, + cols_to_ctl, + |row: &[GoldilocksField], filter_offset| { + row[COL_PROG_CHUNK_FILTER_LOOKING_PROG_RANGE.start + filter_offset].is_one() + }, + i, + Some(|col, value, offset| { + if col == COL_PROG_CHUNK_START_PC { + GoldilocksField::from_canonical_u64(value.0 + offset as u64) + } else { + value + } + }), + ); + }); + + print_title_data( + "program", + get_prog_col_name_map(), + &program_rows, + COL_PROG_CODE_ADDR_RANGE + .chain([COL_PROG_PC, COL_PROG_INST]) + .collect(), + |row: &[GoldilocksField], _| row[COL_PROG_FILTER_PROG_CHUNK].is_one(), + 0, + None, + ); +} diff --git a/circuits/src/generation/ctl_test/storage_access_poseidon.rs b/circuits/src/generation/ctl_test/storage_access_poseidon.rs index 9a423472..083289ae 100644 --- a/circuits/src/generation/ctl_test/storage_access_poseidon.rs +++ b/circuits/src/generation/ctl_test/storage_access_poseidon.rs @@ -17,7 +17,10 @@ fn print_storage_access_poseidon_ctl_info() { ]; let trace = get_exec_trace(program_file_name, Some(call_data), None); - let st_cols = generate_storage_access_trace::(&trace.builtin_storage_hash); + let st_cols = generate_storage_access_trace::( + &trace.builtin_storage_hash, + &trace.builtin_program_hash, + ); let st_rows = get_rows_vec_from_trace(st_cols); let poseidon_cols = generate_poseidon_trace::(&trace.builtin_poseidon); let poseidon_rows = get_rows_vec_from_trace(poseidon_cols); diff --git a/circuits/src/generation/mod.rs b/circuits/src/generation/mod.rs index ae11b0a9..f53fd0cc 100644 --- a/circuits/src/generation/mod.rs +++ b/circuits/src/generation/mod.rs @@ -1,15 +1,15 @@ //use std::collections::HashMap; use core::program::Program; +use core::types::merkle_tree::decode_addr; use std::collections::HashMap; -use std::ops::{Deref, DerefMut}; + use std::sync::mpsc::channel; use std::thread; use eth_trie_utils::partial_trie::HashedPartialTrie; use ethereum_types::{Address, H256}; -use itertools::Itertools; -use num::complex::ComplexFloat; + //use eth_trie_utils::partial_trie::PartialTrie; use plonky2::field::extension::Extendable; use plonky2::field::polynomial::PolynomialValues; @@ -80,9 +80,10 @@ pub fn generate_traces, const D: usize>( inputs: GenerationInputs, ) -> ([Vec>; NUM_TABLES], PublicValues) { let (cpu_tx, cpu_rx) = channel(); - let exec = std::mem::replace(&mut program.trace.exec, Vec::new()); + let exec = std::mem::replace(&mut program.trace.exec, Vec::new()); + let exec_for_cpu = exec.clone(); thread::spawn(move || { - let cpu_rows = generate_cpu_trace::(&exec); + let cpu_rows = generate_cpu_trace::(&exec_for_cpu); cpu_tx.send(trace_to_poly_values(cpu_rows)); }); @@ -94,15 +95,15 @@ pub fn generate_traces, const D: usize>( }); let (bitwise_tx, bitwise_rx) = channel(); - let builtin_bitwise_combined = std::mem::replace(&mut program.trace.builtin_bitwise_combined, Vec::new()); + let builtin_bitwise_combined = + std::mem::replace(&mut program.trace.builtin_bitwise_combined, Vec::new()); thread::spawn(move || { - let (bitwise_rows, bitwise_beta) = - generate_bitwise_trace::(&builtin_bitwise_combined); + let (bitwise_rows, bitwise_beta) = generate_bitwise_trace::(&builtin_bitwise_combined); bitwise_tx.send((trace_to_poly_values(bitwise_rows), bitwise_beta)); }); let (cmp_tx, cmp_rx) = channel(); - let builtin_cmp = std::mem::replace(&mut program.trace.builtin_cmp, Vec::new()); + let builtin_cmp = std::mem::replace(&mut program.trace.builtin_cmp, Vec::new()); thread::spawn(move || { let cmp_rows = generate_cmp_trace(&builtin_cmp); cmp_tx.send(trace_to_poly_values(cmp_rows)); @@ -123,7 +124,8 @@ pub fn generate_traces, const D: usize>( }); let (poseidon_chunk_tx, poseidon_chunk_rx) = channel(); - let builtin_poseidon_chunk = std::mem::replace(&mut program.trace.builtin_poseidon_chunk, Vec::new()); + let builtin_poseidon_chunk = + std::mem::replace(&mut program.trace.builtin_poseidon_chunk, Vec::new()); thread::spawn(move || { let poseidon_chunk_rows: [Vec; 53] = generate_poseidon_chunk_trace(&builtin_poseidon_chunk); @@ -131,9 +133,13 @@ pub fn generate_traces, const D: usize>( }); let (storage_tx, storage_rx) = channel(); - let builtin_storage_hash = std::mem::replace(&mut program.trace.builtin_storage_hash, Vec::new()); + let builtin_storage_hash = + std::mem::replace(&mut program.trace.builtin_storage_hash, Vec::new()); + let builtin_program_hash = + std::mem::replace(&mut program.trace.builtin_program_hash, Vec::new()); thread::spawn(move || { - let storage_access_rows = generate_storage_access_trace(&builtin_storage_hash); + let storage_access_rows = + generate_storage_access_trace(&builtin_storage_hash, &builtin_program_hash); storage_tx.send(trace_to_poly_values(storage_access_rows)); }); @@ -151,11 +157,36 @@ pub fn generate_traces, const D: usize>( sccall_tx.send(trace_to_poly_values(sccall_rows)); }); + let (program_tx, program_rx) = channel(); + let progs = program + .trace + .addr_program_hash + .into_iter() + .map(|(addr, hash)| (decode_addr(addr), hash)) + .collect::>(); + let progs_for_program = progs.clone(); + thread::spawn(move || { + let (program_rows, program_beta) = + prog::generate_prog_trace::(&exec, progs_for_program, program.trace.start_end_roots); + program_tx.send((trace_to_poly_values(program_rows), program_beta)); + }); + + let (prog_chunk_tx, prog_chunk_rx) = channel(); + thread::spawn(move || { + let prog_chunk_rows = prog::generate_prog_chunk_trace::(progs); + prog_chunk_tx.send(trace_to_poly_values(prog_chunk_rows)); + }); + let (bitwise_trace, bitwise_beta) = bitwise_rx.recv().unwrap(); ola_stark .bitwise_stark .set_compress_challenge(bitwise_beta) .unwrap(); + let (program_trace, program_beta) = program_rx.recv().unwrap(); + ola_stark + .program_stark + .set_compress_challenge(program_beta) + .unwrap(); let traces = [ cpu_rx.recv().unwrap(), @@ -168,6 +199,8 @@ pub fn generate_traces, const D: usize>( storage_rx.recv().unwrap(), tape_rx.recv().unwrap(), sccall_rx.recv().unwrap(), + program_trace, + prog_chunk_rx.recv().unwrap(), ]; // TODO: update trie_roots_before & trie_roots_after diff --git a/circuits/src/generation/prog.rs b/circuits/src/generation/prog.rs index 04233849..4b7df39f 100644 --- a/circuits/src/generation/prog.rs +++ b/circuits/src/generation/prog.rs @@ -57,13 +57,16 @@ pub fn generate_prog_trace( let mut trace: Vec> = vec![vec![F::ZERO; num_padded_rows]; NUM_PROG_COLS]; let mut exec_index = 0; for e in execs { + if e.is_ext_line.0 == 1 { + continue; + } for j in 0..4 { trace[COL_PROG_EXEC_CODE_ADDR_RANGE.start + j][exec_index] = F::from_canonical_u64(e.addr_code[j].0); } trace[COL_PROG_EXEC_PC][exec_index] = F::from_canonical_u64(e.pc); trace[COL_PROG_EXEC_INST][exec_index] = F::from_canonical_u64(e.instruction.0); - trace[COL_PROG_FILTER_EXEC_OPERATION][exec_index] = F::ONE; + trace[COL_PROG_FILTER_EXEC][exec_index] = F::ONE; trace[COL_PROG_EXEC_COMP_PROG][exec_index] = compress( [ trace[COL_PROG_EXEC_CODE_ADDR_RANGE.start][exec_index], @@ -88,7 +91,7 @@ pub fn generate_prog_trace( } trace[COL_PROG_EXEC_PC][exec_index] = F::from_canonical_u64(e.pc + 1); trace[COL_PROG_EXEC_INST][exec_index] = F::from_canonical_u64(e.immediate_data.0); - trace[COL_PROG_FILTER_EXEC_IMM_VALUE][exec_index] = F::ONE; + trace[COL_PROG_FILTER_EXEC][exec_index] = F::ONE; trace[COL_PROG_EXEC_COMP_PROG][exec_index] = compress( [ trace[COL_PROG_EXEC_CODE_ADDR_RANGE.start][exec_index], @@ -113,7 +116,7 @@ pub fn generate_prog_trace( } trace[COL_PROG_PC][prog_index] = F::from_canonical_u64(pc as u64); trace[COL_PROG_INST][prog_index] = F::from_canonical_u64(inst.0); - trace[COL_PROG_FILTER_PROG_CHUNK][exec_index] = F::ONE; + trace[COL_PROG_FILTER_PROG_CHUNK][prog_index] = F::ONE; trace[COL_PROG_COMP_PROG][prog_index] = compress( [ trace[COL_PROG_CODE_ADDR_RANGE.start][prog_index], diff --git a/circuits/src/generation/storage.rs b/circuits/src/generation/storage.rs index 8efd6884..87949248 100644 --- a/circuits/src/generation/storage.rs +++ b/circuits/src/generation/storage.rs @@ -5,9 +5,10 @@ use plonky2::hash::hash_types::RichField; use crate::builtins::storage::columns::*; pub fn generate_storage_access_trace( - cells: &[StorageHashRow], + accesses: &[StorageHashRow], + prog_hash_read: &[StorageHashRow], ) -> [Vec; NUM_COL_ST] { - let num_filled_row_len: usize = cells.len(); + let num_filled_row_len: usize = accesses.len() + prog_hash_read.len(); let num_padded_rows = if !num_filled_row_len.is_power_of_two() || num_filled_row_len < 2 { if num_filled_row_len < 2 { 2 @@ -19,7 +20,7 @@ pub fn generate_storage_access_trace( }; let mut trace: Vec> = vec![vec![F::ZERO; num_padded_rows]; NUM_COL_ST]; - for (i, c) in cells.iter().enumerate() { + for (i, c) in accesses.iter().chain(prog_hash_read).enumerate() { trace[COL_ST_ACCESS_IDX][i] = F::from_canonical_u64(c.storage_access_idx); for j in 0..4 { trace[COL_ST_PRE_ROOT_RANGE.start + j][i] = F::from_canonical_u64(c.pre_root[j].0); @@ -70,6 +71,13 @@ pub fn generate_storage_access_trace( }; trace[COL_ST_FILTER_IS_HASH_BIT_0][i] = if c.layer_bit == 0 { F::ONE } else { F::ZERO }; trace[COL_ST_FILTER_IS_HASH_BIT_1][i] = if c.layer_bit == 1 { F::ONE } else { F::ZERO }; + trace[COL_ST_FILTER_IS_FOR_PROG][i] = if i < accesses.len() { + F::ZERO + } else if c.layer == 256 { + F::ONE + } else { + F::ZERO + }; trace[COL_ST_IS_PADDING][i] = F::ZERO; } diff --git a/circuits/src/generation/tape.rs b/circuits/src/generation/tape.rs index 7c22a28f..e04f1f3b 100644 --- a/circuits/src/generation/tape.rs +++ b/circuits/src/generation/tape.rs @@ -21,7 +21,7 @@ pub fn generate_tape_trace(cells: &[TapeRow]) -> [Vec; NUM_COL_ let mut trace: Vec> = vec![vec![F::ZERO; num_padded_rows]; NUM_COL_TAPE]; for (i, c) in cells.iter().enumerate() { - // trace[COL_TAPE_TX_IDX][i] = F::from_canonical_u64(c.tx_idx); + trace[COL_TAPE_TX_IDX][i] = F::from_canonical_u64(c.tx_idx.to_canonical_u64()); trace[COL_TAPE_IS_INIT_SEG][i] = if c.is_init { F::ONE } else { F::ZERO }; trace[COL_TAPE_OPCODE][i] = F::from_canonical_u64(c.opcode.to_canonical_u64()); trace[COL_TAPE_ADDR][i] = F::from_canonical_u64(c.addr.to_canonical_u64()); diff --git a/circuits/src/program/columns.rs b/circuits/src/program/columns.rs index 5abde5db..4eea25ac 100644 --- a/circuits/src/program/columns.rs +++ b/circuits/src/program/columns.rs @@ -11,9 +11,8 @@ pub(crate) const COL_PROG_EXEC_PC: usize = COL_PROG_EXEC_CODE_ADDR_RANGE.end; pub(crate) const COL_PROG_EXEC_INST: usize = COL_PROG_EXEC_PC + 1; pub(crate) const COL_PROG_EXEC_COMP_PROG: usize = COL_PROG_EXEC_INST + 1; pub(crate) const COL_PROG_EXEC_COMP_PROG_PERM: usize = COL_PROG_EXEC_COMP_PROG + 1; -pub(crate) const COL_PROG_FILTER_EXEC_OPERATION: usize = COL_PROG_EXEC_COMP_PROG_PERM + 1; -pub(crate) const COL_PROG_FILTER_EXEC_IMM_VALUE: usize = COL_PROG_FILTER_EXEC_OPERATION + 1; -pub(crate) const COL_PROG_FILTER_PROG_CHUNK: usize = COL_PROG_FILTER_EXEC_IMM_VALUE + 1; +pub(crate) const COL_PROG_FILTER_EXEC: usize = COL_PROG_EXEC_COMP_PROG_PERM + 1; +pub(crate) const COL_PROG_FILTER_PROG_CHUNK: usize = COL_PROG_FILTER_EXEC + 1; pub(crate) const NUM_PROG_COLS: usize = COL_PROG_FILTER_PROG_CHUNK + 1; pub(crate) fn get_prog_col_name_map() -> BTreeMap { @@ -38,12 +37,8 @@ pub(crate) fn get_prog_col_name_map() -> BTreeMap { String::from("EXEC_COMP_PROG_PERM"), ); m.insert( - COL_PROG_FILTER_EXEC_OPERATION, - String::from("FILTER_EXEC_OPERATION"), - ); - m.insert( - COL_PROG_FILTER_EXEC_IMM_VALUE, - String::from("FILTER_EXEC_IMM_VALUE"), + COL_PROG_FILTER_EXEC, + String::from("FILTER_EXEC"), ); m.insert( COL_PROG_FILTER_PROG_CHUNK, diff --git a/circuits/src/program/prog_chunk_stark.rs b/circuits/src/program/prog_chunk_stark.rs index a2444277..64249dbe 100644 --- a/circuits/src/program/prog_chunk_stark.rs +++ b/circuits/src/program/prog_chunk_stark.rs @@ -1,5 +1,7 @@ +use core::types::Field; use std::marker::PhantomData; +use itertools::Itertools; use plonky2::{ field::{ extension::{Extendable, FieldExtension}, @@ -11,11 +13,52 @@ use plonky2::{ use crate::stark::{ constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}, + cross_table_lookup::Column, stark::Stark, vars::{StarkEvaluationTargets, StarkEvaluationVars}, }; use super::columns::*; + +pub fn ctl_data_to_program(i: usize) -> Vec> { + let mut res = Column::singles(COL_PROG_CHUNK_CODE_ADDR_RANGE).collect_vec(); + res.push(Column::linear_combination_with_constant( + [(COL_PROG_CHUNK_START_PC, F::ONE)], + F::from_canonical_usize(i), + )); + res.push(Column::single(COL_PROG_CHUNK_INST_RANGE.start + i)); + res +} + +pub fn ctl_filter_to_program(i: usize) -> Column { + Column::single(COL_PROG_CHUNK_FILTER_LOOKING_PROG_RANGE.start + i) +} + +pub fn ctl_data_to_poseidon() -> Vec> { + Column::singles( + COL_PROG_CHUNK_INST_RANGE + .chain(COL_PROG_CHUNK_CAP_RANGE) + .chain(COL_PROG_CHUNK_HASH_RANGE), + ) + .collect_vec() +} + +pub fn ctl_filter_to_poseidon() -> Column { + Column::linear_combination_with_constant([(COL_PROG_CHUNK_IS_PADDING_LINE, F::NEG_ONE)], F::ONE) +} + +pub fn ctl_data_to_storage_access() -> Vec> { + let mut res: Vec> = vec![Column::zero()]; + res.extend( + Column::singles(COL_PROG_CHUNK_CODE_ADDR_RANGE.chain(COL_PROG_CHUNK_HASH_RANGE.take(4))) + .collect_vec(), + ); + res +} +pub fn ctl_filter_to_storage_access() -> Column { + Column::single(COL_PROG_CHUNK_IS_RESULT_LINE) +} + #[derive(Copy, Clone, Default)] pub struct ProgChunkStark { pub _phantom: PhantomData, diff --git a/circuits/src/program/program_stark.rs b/circuits/src/program/program_stark.rs index 8a501d57..3a42926f 100644 --- a/circuits/src/program/program_stark.rs +++ b/circuits/src/program/program_stark.rs @@ -1,5 +1,7 @@ +use core::types::Field; use std::{marker::PhantomData, vec}; +use itertools::Itertools; use plonky2::{ field::{ extension::{Extendable, FieldExtension}, @@ -12,6 +14,7 @@ use plonky2::{ use super::columns::*; use crate::stark::{ constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}, + cross_table_lookup::Column, lookup::eval_lookups, permutation::PermutationPair, stark::Stark, @@ -19,6 +22,23 @@ use crate::stark::{ }; use anyhow::Result; +pub fn ctl_data_by_cpu() -> Vec> { + Column::singles(COL_PROG_EXEC_CODE_ADDR_RANGE.chain([COL_PROG_EXEC_PC, COL_PROG_EXEC_INST])) + .collect_vec() +} + +pub fn ctl_filter_by_cpu() -> Column { + Column::single(COL_PROG_FILTER_EXEC) +} + +pub fn ctl_data_by_program_chunk() -> Vec> { + Column::singles(COL_PROG_CODE_ADDR_RANGE.chain([COL_PROG_PC, COL_PROG_INST])).collect_vec() +} + +pub fn ctl_filter_by_program_chunk() -> Column { + Column::single(COL_PROG_FILTER_PROG_CHUNK) +} + #[derive(Copy, Clone, Default)] pub struct ProgramStark { compress_challenge: Option, @@ -84,7 +104,7 @@ impl, const D: usize> Stark for ProgramStark< } fn constraint_degree(&self) -> usize { - 2 + 3 } fn permutation_pairs(&self) -> Vec { diff --git a/circuits/src/stark/ola_stark.rs b/circuits/src/stark/ola_stark.rs index c6e3f417..ba292605 100644 --- a/circuits/src/stark/ola_stark.rs +++ b/circuits/src/stark/ola_stark.rs @@ -17,6 +17,8 @@ use crate::memory::memory_stark::{ self, ctl_data as mem_ctl_data, ctl_data_mem_rc_diff_cond, ctl_data_mem_sort_rc, ctl_filter as mem_ctl_filter, ctl_filter_mem_rc_diff_cond, ctl_filter_mem_sort_rc, MemoryStark, }; +use crate::program::prog_chunk_stark::{self, ProgChunkStark}; +use crate::program::program_stark::{self, ProgramStark}; use plonky2::field::extension::Extendable; use plonky2::field::types::Field; use plonky2::hash::hash_types::RichField; @@ -34,6 +36,8 @@ pub struct OlaStark, const D: usize> { pub storage_access_stark: StorageAccessStark, pub tape_stark: TapeStark, pub sccall_stark: SCCallStark, + pub program_stark: ProgramStark, + pub prog_chunk_stark: ProgChunkStark, pub cross_table_lookups: Vec>, } @@ -51,6 +55,8 @@ impl, const D: usize> Default for OlaStark { storage_access_stark: StorageAccessStark::default(), tape_stark: TapeStark::default(), sccall_stark: SCCallStark::default(), + program_stark: ProgramStark::default(), + prog_chunk_stark: ProgChunkStark::default(), cross_table_lookups: all_cross_table_lookups(), } } @@ -69,6 +75,8 @@ impl, const D: usize> OlaStark { self.storage_access_stark.num_permutation_batches(config), self.tape_stark.num_permutation_batches(config), self.sccall_stark.num_permutation_batches(config), + self.program_stark.num_permutation_batches(config), + self.prog_chunk_stark.num_permutation_batches(config), ] } @@ -84,6 +92,8 @@ impl, const D: usize> OlaStark { self.storage_access_stark.permutation_batch_size(), self.tape_stark.permutation_batch_size(), self.sccall_stark.permutation_batch_size(), + self.program_stark.permutation_batch_size(), + self.prog_chunk_stark.permutation_batch_size(), ] } } @@ -101,11 +111,11 @@ pub enum Table { StorageAccess = 7, Tape = 8, SCCall = 9, - // program table - // Program = 8, + Program = 10, + ProgChunk = 11, } -pub(crate) const NUM_TABLES: usize = 10; +pub(crate) const NUM_TABLES: usize = 12; pub(crate) fn all_cross_table_lookups() -> Vec> { vec![ @@ -118,13 +128,16 @@ pub(crate) fn all_cross_table_lookups() -> Vec> { ctl_rangecheck_cpu(), ctl_cpu_poseidon_chunk(), ctl_poseidon_chunk_mem(), - ctl_poseidon_chunk_poseidon(), + ctl_chunk_poseidon(), ctl_cpu_poseidon_tree_key(), ctl_cpu_storage_access(), ctl_storage_access_poseidon(), ctl_cpu_tape(), ctl_cpu_sccall(), ctl_cpu_sccall_end(), + ctl_cpu_program(), + ctl_prog_chunk_prog(), + ctl_prog_chunk_storage(), ] } @@ -235,13 +248,11 @@ fn ctl_memory_rc_region() -> CrossTableLookup { // Cross_Lookup_Table(looking_table, looked_table) fn ctl_bitwise_cpu() -> CrossTableLookup { CrossTableLookup::new( - vec![ - TableWithColumns::new( - Table::Cpu, - cpu_stark::ctl_data_with_bitwise(), - Some(cpu_stark::ctl_filter_with_bitwise()), - ), - ], + vec![TableWithColumns::new( + Table::Cpu, + cpu_stark::ctl_data_with_bitwise(), + Some(cpu_stark::ctl_filter_with_bitwise()), + )], TableWithColumns::new( Table::Bitwise, bitwise_stark::ctl_data_with_cpu(), @@ -336,13 +347,20 @@ fn ctl_poseidon_chunk_mem() -> CrossTableLookup { CrossTableLookup::new(all_lookers, mem_looked) } -fn ctl_poseidon_chunk_poseidon() -> CrossTableLookup { +fn ctl_chunk_poseidon() -> CrossTableLookup { CrossTableLookup::new( - vec![TableWithColumns::new( - Table::PoseidonChunk, - poseidon_chunk_stark::ctl_data_with_poseidon(), - Some(poseidon_chunk_stark::ctl_filter_with_poseidon()), - )], + vec![ + TableWithColumns::new( + Table::PoseidonChunk, + poseidon_chunk_stark::ctl_data_with_poseidon(), + Some(poseidon_chunk_stark::ctl_filter_with_poseidon()), + ), + TableWithColumns::new( + Table::ProgChunk, + prog_chunk_stark::ctl_data_to_poseidon(), + Some(prog_chunk_stark::ctl_filter_to_poseidon()), + ), + ], TableWithColumns::new( Table::Poseidon, poseidon_stark::ctl_data_with_poseidon_chunk(), @@ -483,6 +501,62 @@ fn ctl_cpu_sccall_end() -> CrossTableLookup { ) } +fn ctl_cpu_program() -> CrossTableLookup { + CrossTableLookup::new( + vec![ + TableWithColumns::new( + Table::Cpu, + cpu_stark::ctl_data_inst_to_program(), + Some(cpu_stark::ctl_filter_with_program_inst()), + ), + TableWithColumns::new( + Table::Cpu, + cpu_stark::ctl_data_imm_to_program(), + Some(cpu_stark::ctl_filter_with_program_imm()), + ), + ], + TableWithColumns::new( + Table::Program, + program_stark::ctl_data_by_cpu(), + Some(program_stark::ctl_filter_by_cpu()), + ), + ) +} + +fn ctl_prog_chunk_prog() -> CrossTableLookup { + CrossTableLookup::new( + (0..8) + .map(|i: usize| { + TableWithColumns::new( + Table::ProgChunk, + prog_chunk_stark::ctl_data_to_program(i), + Some(prog_chunk_stark::ctl_filter_to_program(i)), + ) + }) + .collect(), + TableWithColumns::new( + Table::Program, + program_stark::ctl_data_by_program_chunk(), + Some(program_stark::ctl_filter_by_program_chunk()), + ), + ) +} + +fn ctl_prog_chunk_storage() -> CrossTableLookup { + CrossTableLookup::new( + vec![TableWithColumns::new( + Table::ProgChunk, + prog_chunk_stark::ctl_data_to_storage_access(), + Some(prog_chunk_stark::ctl_filter_to_storage_access()), + )], + TableWithColumns::new( + Table::StorageAccess, + storage_access_stark::ctl_data_for_prog_chunk(), + Some(storage_access_stark::ctl_filter_for_prog_chunk()), + ), + ) +} + // Cross_Lookup_Table(looking_table, looked_table) /*fn ctl_bitwise_bitwise_fixed_table() -> CrossTableLookup { CrossTableLookup::new( @@ -577,13 +651,18 @@ mod tests { use crate::stark::verifier::verify_proof; use anyhow::Result; use assembler::encoder::encode_asm_from_json_file; + use core::crypto::hash::Hasher; + use core::crypto::ZkHasher; + use core::merkle_tree::log::{StorageLog, WitnessStorageLog}; use core::merkle_tree::tree::AccountTree; use core::program::binary_program::BinaryProgram; use core::program::Program; use core::types::account::Address; + use core::types::merkle_tree::{encode_addr, tree_key_default}; use core::types::{Field, GoldilocksField}; use core::vm::transaction::init_tx_context; use executor::load_tx::init_tape; + use executor::trace::{gen_storage_hash_table, gen_storage_table}; use executor::Process; use itertools::Itertools; use log::{debug, LevelFilter}; @@ -707,11 +786,7 @@ mod tests { .iter() .map(|v| GoldilocksField::from_canonical_u64(*v)) .collect_vec(); - test_by_asm_json( - "vote.json".to_string(), - Some(init_calldata), - Some(db_name), - ); + test_by_asm_json("vote.json".to_string(), Some(init_calldata), Some(db_name)); } #[test] @@ -744,6 +819,7 @@ mod tests { Some(name) => { let mut db_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); db_path.push("../executor/db_test/"); + db_path.push("../executor/db_test/"); db_path.push(name); AccountTree::new_db_test(db_path.display().to_string()) } @@ -751,7 +827,13 @@ mod tests { }; let program = encode_asm_from_json_file(program_path).unwrap(); + let hash = ZkHasher::default(); let instructions = program.bytecode.split("\n"); + let code: Vec<_> = instructions + .clone() + .map(|e| GoldilocksField::from_canonical_u64(u64::from_str_radix(&e[2..], 16).unwrap())) + .collect(); + let code_hash = hash.hash_bytes(&code); let mut prophets = HashMap::new(); for item in program.prophets { prophets.insert(item.host as u64, item); @@ -765,18 +847,61 @@ mod tests { let mut process = Process::new(); process.addr_storage = Address::default(); + + let tp_start = 0; + + let callee: Address = [ + GoldilocksField::from_canonical_u64(9), + GoldilocksField::from_canonical_u64(10), + GoldilocksField::from_canonical_u64(11), + GoldilocksField::from_canonical_u64(12), + ]; + let caller_addr = [ + GoldilocksField::from_canonical_u64(17), + GoldilocksField::from_canonical_u64(18), + GoldilocksField::from_canonical_u64(19), + GoldilocksField::from_canonical_u64(20), + ]; + let callee_exe_addr = [ + GoldilocksField::from_canonical_u64(13), + GoldilocksField::from_canonical_u64(14), + GoldilocksField::from_canonical_u64(15), + GoldilocksField::from_canonical_u64(16), + ]; + if let Some(calldata) = call_data { - process.tp = GoldilocksField::ZERO; + process.tp = GoldilocksField::from_canonical_u64(tp_start as u64); init_tape( &mut process, calldata, - Address::default(), - Address::default(), - Address::default(), + caller_addr, + callee, + callee_exe_addr, &init_tx_context(), ); } + + process.addr_code = callee_exe_addr; + process.addr_storage = callee; + program + .trace + .addr_program_hash + .insert(encode_addr(&callee_exe_addr), code); + + db.process_block(vec![WitnessStorageLog { + storage_log: StorageLog::new_write_log(callee_exe_addr, code_hash), + previous_value: tree_key_default(), + }]); + let _ = db.save(); + + let start = db.root_hash(); + + process.program_log.push(WitnessStorageLog { + storage_log: StorageLog::new_read_log(callee_exe_addr, code_hash), + previous_value: tree_key_default(), + }); + let res = process.execute(&mut program, &mut Some(prophets), &mut db); match res { Ok(_) => {} @@ -785,6 +910,9 @@ mod tests { return; } } + let hash_roots = gen_storage_hash_table(&mut process, &mut program, &mut db); + gen_storage_table(&mut process, &mut program, hash_roots).unwrap(); + program.trace.start_end_roots = (start, db.root_hash()); let inputs = GenerationInputs::default(); diff --git a/circuits/src/stark/prover.rs b/circuits/src/stark/prover.rs index 151a3d8b..76bea7aa 100644 --- a/circuits/src/stark/prover.rs +++ b/circuits/src/stark/prover.rs @@ -30,6 +30,8 @@ use crate::builtins::poseidon::poseidon_stark::PoseidonStark; use crate::builtins::rangecheck::rangecheck_stark::RangeCheckStark; use crate::builtins::sccall::sccall_stark::SCCallStark; use crate::builtins::storage::storage_access_stark::StorageAccessStark; +use crate::program::prog_chunk_stark::ProgChunkStark; +use crate::program::program_stark::ProgramStark; // use crate::builtins::tape::tape_stark::TapeStark; //use crate::columns::NUM_CPU_COLS; use super::config::StarkConfig; @@ -63,12 +65,14 @@ where [(); MemoryStark::::COLUMNS]:, [(); BitwiseStark::::COLUMNS]:, [(); CmpStark::::COLUMNS]:, - [(); RangeCheckStark::::COLUMNS]:, + // [(); RangeCheckStark::::COLUMNS]:, [(); PoseidonStark::::COLUMNS]:, [(); PoseidonChunkStark::::COLUMNS]:, [(); StorageAccessStark::::COLUMNS]:, // [(); TapeStark::::COLUMNS]:, [(); SCCallStark::::COLUMNS]:, + [(); ProgramStark::::COLUMNS]:, + [(); ProgChunkStark::::COLUMNS]:, { let (traces, public_values) = generate_traces(program, ola_stark, inputs); prove_with_traces(ola_stark, config, traces, public_values, timing) @@ -90,12 +94,14 @@ where [(); MemoryStark::::COLUMNS]:, [(); BitwiseStark::::COLUMNS]:, [(); CmpStark::::COLUMNS]:, - [(); RangeCheckStark::::COLUMNS]:, + // [(); RangeCheckStark::::COLUMNS]:, [(); PoseidonStark::::COLUMNS]:, [(); PoseidonChunkStark::::COLUMNS]:, [(); StorageAccessStark::::COLUMNS]:, // [(); TapeStark::::COLUMNS]:, [(); SCCallStark::::COLUMNS]:, + [(); ProgramStark::::COLUMNS]:, + [(); ProgChunkStark::::COLUMNS]:, { let rate_bits = config.fri_config.rate_bits; let cap_height = config.fri_config.cap_height; @@ -262,6 +268,26 @@ where timing, &mut twiddle_map, )?; + let program_proof = prove_single_table( + &ola_stark.program_stark, + config, + &trace_poly_values[Table::Program as usize], + &trace_commitments[Table::Program as usize], + &ctl_data_per_table[Table::Program as usize], + &mut challenger, + timing, + &mut twiddle_map, + )?; + let prog_chunk_proof = prove_single_table( + &ola_stark.prog_chunk_stark, + config, + &trace_poly_values[Table::ProgChunk as usize], + &trace_commitments[Table::ProgChunk as usize], + &ctl_data_per_table[Table::ProgChunk as usize], + &mut challenger, + timing, + &mut twiddle_map, + )?; #[cfg(feature = "benchmark")] info!("prove_other_table total time: {:?}", start.elapsed()); @@ -277,6 +303,8 @@ where storage_access_proof, tape_proof, sccall_proof, + program_proof, + prog_chunk_proof, ]; let compress_challenges = [ @@ -290,6 +318,8 @@ where F::ZERO, F::ZERO, F::ZERO, + ola_stark.program_stark.get_compress_challenge().unwrap(), + F::ZERO, ]; Ok(AllProof { @@ -351,7 +381,10 @@ where #[cfg(feature = "benchmark")] if S::COLUMNS == 76 { - info!("compute_permutation_z_polys total time: {:?}", start.elapsed()); + info!( + "compute_permutation_z_polys total time: {:?}", + start.elapsed() + ); } let num_permutation_zs = permutation_zs.as_ref().map(|v| v.len()).unwrap_or(0); @@ -383,7 +416,10 @@ where #[cfg(feature = "benchmark")] if S::COLUMNS == 76 { - info!("permutation_ctl_zs_commitment total time: {:?}", start.elapsed()); + info!( + "permutation_ctl_zs_commitment total time: {:?}", + start.elapsed() + ); } let permutation_ctl_zs_cap = permutation_ctl_zs_commitment.merkle_tree.cap.clone(); @@ -457,7 +493,10 @@ where ); #[cfg(feature = "benchmark")] if S::COLUMNS == 76 { - info!("compute quotient commitment total time: {:?}", start.elapsed()); + info!( + "compute quotient commitment total time: {:?}", + start.elapsed() + ); } let quotient_polys_cap = quotient_commitment.merkle_tree.cap.clone(); diff --git a/circuits/src/stark/verifier.rs b/circuits/src/stark/verifier.rs index f0466cb3..c63afa5f 100644 --- a/circuits/src/stark/verifier.rs +++ b/circuits/src/stark/verifier.rs @@ -29,6 +29,8 @@ use crate::builtins::storage::storage_access_stark::StorageAccessStark; // use crate::builtins::tape::tape_stark::TapeStark; use crate::cpu::cpu_stark::CpuStark; use crate::memory::memory_stark::MemoryStark; +use crate::program::prog_chunk_stark::ProgChunkStark; +use crate::program::program_stark::ProgramStark; pub fn verify_proof, C: GenericConfig, const D: usize>( ola_stark: OlaStark, @@ -47,6 +49,8 @@ where [(); StorageAccessStark::::COLUMNS]:, // [(); TapeStark::::COLUMNS]:, [(); SCCallStark::::COLUMNS]:, + [(); ProgramStark::::COLUMNS]:, + [(); ProgChunkStark::::COLUMNS]:, { let AllProofChallenges { stark_challenges, @@ -66,6 +70,8 @@ where storage_access_stark, tape_stark, sccall_stark, + mut program_stark, + prog_chunk_stark, cross_table_lookups, } = ola_stark; @@ -74,6 +80,11 @@ where .set_compress_challenge(all_proof.compress_challenges[Table::Bitwise as usize]) .unwrap(); } + if program_stark.get_compress_challenge().is_none() { + program_stark + .set_compress_challenge(all_proof.compress_challenges[Table::Program as usize]) + .unwrap(); + } let ctl_vars_per_table = CtlCheckVars::from_proofs( &all_proof.stark_proofs, @@ -160,6 +171,22 @@ where config, )?; + verify_stark_proof_with_challenges( + program_stark, + &all_proof.stark_proofs[Table::Program as usize], + &stark_challenges[Table::Program as usize], + &ctl_vars_per_table[Table::Program as usize], + config, + )?; + + verify_stark_proof_with_challenges( + prog_chunk_stark, + &all_proof.stark_proofs[Table::ProgChunk as usize], + &stark_challenges[Table::ProgChunk as usize], + &ctl_vars_per_table[Table::ProgChunk as usize], + config, + )?; + // TODO: // let public_values = all_proof.public_values; let extra_looking_products = vec![vec![F::ONE; config.num_challenges]; NUM_TABLES]; diff --git a/circuits/src/test_utils.rs b/circuits/src/test_utils.rs index 507a05e1..66ef1075 100644 --- a/circuits/src/test_utils.rs +++ b/circuits/src/test_utils.rs @@ -1,8 +1,13 @@ +use core::crypto::hash::Hasher; +use core::crypto::ZkHasher; +use core::merkle_tree::log::{StorageLog, WitnessStorageLog}; +use core::types::merkle_tree::{encode_addr, tree_key_default}; use core::{program::Program, trace::trace::Trace, types::account::Address}; use std::collections::HashMap; use std::path::PathBuf; use assembler::encoder::encode_asm_from_json_file; +use executor::trace::{gen_storage_hash_table, gen_storage_table}; use executor::{load_tx::init_tape, Process}; use plonky2::field::{goldilocks_field::GoldilocksField, types::Field}; use plonky2_util::log2_strict; @@ -37,7 +42,13 @@ pub fn test_stark_with_asm_path( }; let program = encode_asm_from_json_file(path).unwrap(); + let hash = ZkHasher::default(); let instructions = program.bytecode.split("\n"); + let code: Vec<_> = instructions + .clone() + .map(|e| GoldilocksField::from_canonical_u64(u64::from_str_radix(&e[2..], 16).unwrap())) + .collect(); + let code_hash = hash.hash_bytes(&code); let mut prophets = HashMap::new(); for item in program.prophets { prophets.insert(item.host as u64, item); @@ -52,18 +63,60 @@ pub fn test_stark_with_asm_path( let mut process = Process::new(); process.addr_storage = Address::default(); + let tp_start = 0; + + let callee: Address = [ + GoldilocksField::from_canonical_u64(9), + GoldilocksField::from_canonical_u64(10), + GoldilocksField::from_canonical_u64(11), + GoldilocksField::from_canonical_u64(12), + ]; + let caller_addr = [ + GoldilocksField::from_canonical_u64(17), + GoldilocksField::from_canonical_u64(18), + GoldilocksField::from_canonical_u64(19), + GoldilocksField::from_canonical_u64(20), + ]; + let callee_exe_addr = [ + GoldilocksField::from_canonical_u64(13), + GoldilocksField::from_canonical_u64(14), + GoldilocksField::from_canonical_u64(15), + GoldilocksField::from_canonical_u64(16), + ]; + if let Some(calldata) = call_data { - process.tp = GoldilocksField::ZERO; + process.tp = GoldilocksField::from_canonical_u64(tp_start as u64); + init_tape( &mut process, calldata, - Address::default(), - Address::default(), - Address::default(), + caller_addr, + callee, + callee_exe_addr, &init_tx_context(), ); } + process.addr_code = callee_exe_addr; + process.addr_storage = callee; + program + .trace + .addr_program_hash + .insert(encode_addr(&callee_exe_addr), code); + + db.process_block(vec![WitnessStorageLog { + storage_log: StorageLog::new_write_log(callee_exe_addr, code_hash), + previous_value: tree_key_default(), + }]); + let _ = db.save(); + + let start = db.root_hash(); + + process.program_log.push(WitnessStorageLog { + storage_log: StorageLog::new_read_log(callee_exe_addr, code_hash), + previous_value: tree_key_default(), + }); + let res = process.execute(&mut program, &mut Some(prophets), &mut db); match res { Ok(_) => {} @@ -72,6 +125,9 @@ pub fn test_stark_with_asm_path( return; } } + let hash_roots = gen_storage_hash_table(&mut process, &mut program, &mut db); + gen_storage_table(&mut process, &mut program, hash_roots).unwrap(); + program.trace.start_end_roots = (start, db.root_hash()); let raw_trace_rows = get_trace_rows(program.trace); let rows = generate_trace(&raw_trace_rows); @@ -132,3 +188,175 @@ pub fn test_stark_with_asm_path( } } } + +pub fn simple_test_stark( + path: String, + generate_trace: fn(Trace) -> [Vec; COL_NUM], + eval_packed_generic: E, + error_hook: Option, + call_data: Option>, + db_name: Option, +) where + E: Fn( + StarkEvaluationVars, + &mut ConstraintConsumer, + ) -> (), + H: Fn(usize, StarkEvaluationVars) -> (), +{ + let mut db = match db_name { + Some(name) => { + let mut db_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + db_path.push("../executor/db_test/"); + db_path.push(name); + AccountTree::new_db_test(db_path.display().to_string()) + } + _ => AccountTree::new_test(), + }; + + let program = encode_asm_from_json_file(path).unwrap(); + let hash = ZkHasher::default(); + let instructions = program.bytecode.split("\n"); + let code: Vec<_> = instructions + .clone() + .map(|e| GoldilocksField::from_canonical_u64(u64::from_str_radix(&e[2..], 16).unwrap())) + .collect(); + let code_hash = hash.hash_bytes(&code); + let mut prophets = HashMap::new(); + for item in program.prophets { + prophets.insert(item.host as u64, item); + } + + let mut program: Program = Program { + instructions: Vec::new(), + trace: Default::default(), + debug_info: program.debug_info, + pre_exe_flag: false, + }; + + for inst in instructions { + program.instructions.push(inst.to_string()); + } + + let mut process = Process::new(); + process.addr_storage = Address::default(); + + let tp_start = 0; + + let callee: Address = [ + GoldilocksField::from_canonical_u64(9), + GoldilocksField::from_canonical_u64(10), + GoldilocksField::from_canonical_u64(11), + GoldilocksField::from_canonical_u64(12), + ]; + let caller_addr = [ + GoldilocksField::from_canonical_u64(17), + GoldilocksField::from_canonical_u64(18), + GoldilocksField::from_canonical_u64(19), + GoldilocksField::from_canonical_u64(20), + ]; + let callee_exe_addr = [ + GoldilocksField::from_canonical_u64(13), + GoldilocksField::from_canonical_u64(14), + GoldilocksField::from_canonical_u64(15), + GoldilocksField::from_canonical_u64(16), + ]; + + if let Some(calldata) = call_data { + process.tp = GoldilocksField::from_canonical_u64(tp_start as u64); + + init_tape( + &mut process, + calldata, + caller_addr, + callee, + callee_exe_addr, + &init_tx_context(), + ); + } + + process.addr_code = callee_exe_addr; + process.addr_storage = callee; + program + .trace + .addr_program_hash + .insert(encode_addr(&callee_exe_addr), code); + + db.process_block(vec![WitnessStorageLog { + storage_log: StorageLog::new_write_log(callee_exe_addr, code_hash), + previous_value: tree_key_default(), + }]); + let _ = db.save(); + + let start = db.root_hash(); + + process.program_log.push(WitnessStorageLog { + storage_log: StorageLog::new_read_log(callee_exe_addr, code_hash), + previous_value: tree_key_default(), + }); + + let res = process.execute(&mut program, &mut Some(prophets), &mut db); + match res { + Ok(_) => {} + Err(e) => { + println!("execute err:{:?}", e); + return; + } + } + let hash_roots = gen_storage_hash_table(&mut process, &mut program, &mut db); + gen_storage_table(&mut process, &mut program, hash_roots).unwrap(); + program.trace.start_end_roots = (start, db.root_hash()); + + let rows = generate_trace(program.trace); + let len = rows[0].len(); + + let last = GoldilocksField::primitive_root_of_unity(log2_strict(len)).inverse(); + let subgroup = GoldilocksField::cyclic_subgroup_known_order( + GoldilocksField::primitive_root_of_unity(log2_strict(len)), + len, + ); + + for i in 0..len - 1 { + let local_values: [GoldilocksField; COL_NUM] = rows + .iter() + .map(|row| row[i % len]) + .collect::>() + .try_into() + .unwrap(); + let next_values: [GoldilocksField; COL_NUM] = rows + .iter() + .map(|row| row[(i + 1) % len]) + .collect::>() + .try_into() + .unwrap(); + let vars = StarkEvaluationVars { + local_values: &local_values, + next_values: &next_values, + }; + + let mut constraint_consumer = ConstraintConsumer::new( + vec![GoldilocksField::rand()], + subgroup[i] - last, + if i == 0 { + GoldilocksField::ONE + } else { + GoldilocksField::ZERO + }, + if i == len - 1 { + GoldilocksField::ONE + } else { + GoldilocksField::ZERO + }, + ); + eval_packed_generic(vars, &mut constraint_consumer); + + for &acc in &constraint_consumer.constraint_accs { + if !acc.eq(&GoldilocksField::ZERO) { + match error_hook { + Some(ref hook) => hook(i, vars), + None => {} + } + } + assert_eq!(acc, GoldilocksField::ZERO); + } + } +} diff --git a/core/src/vm/vm_state.rs b/core/src/vm/vm_state.rs index 4161c411..3bc2b0e0 100644 --- a/core/src/vm/vm_state.rs +++ b/core/src/vm/vm_state.rs @@ -1,3 +1,4 @@ + use crate::trace::trace::Step; pub use crate::types::account::Address; pub use plonky2::field::goldilocks_field::GoldilocksField; diff --git a/executor/src/lib.rs b/executor/src/lib.rs index 49630898..c17c3626 100644 --- a/executor/src/lib.rs +++ b/executor/src/lib.rs @@ -17,6 +17,7 @@ use core::trace::trace::{FilterLockForMain, MemoryOperation, MemoryType}; use core::types::account::AccountTreeId; use core::crypto::poseidon_trace::{ + calculate_arbitrary_poseidon_and_generate_intermediate_trace, calculate_poseidon_and_generate_intermediate_trace, POSEIDON_INPUT_VALUE_LEN, POSEIDON_OUTPUT_VALUE_LEN, }; @@ -1858,6 +1859,24 @@ impl Process { // todo : why need clear? //self.storage_log.clear(); let mut end_step = None; + let mut prog_hash_rows = calculate_arbitrary_poseidon_and_generate_intermediate_trace( + program + .instructions + .iter() + .map(|insts_str| { + GoldilocksField::from_canonical_u64( + u64::from_str_radix(insts_str.trim_start_matches("0x"), 16).unwrap(), + ) + }) + .collect::>() + .as_slice(), + ) + .1; + for row in &mut prog_hash_rows { + row.filter_looked_normal = true; + } + program.trace.builtin_poseidon.extend(prog_hash_rows); + loop { self.register_selector = RegisterSelector::default(); let registers_status = self.registers; diff --git a/executor/src/tests.rs b/executor/src/tests.rs index f43de254..9162d933 100644 --- a/executor/src/tests.rs +++ b/executor/src/tests.rs @@ -345,7 +345,7 @@ fn storage_u32_test() { GoldilocksField::from_canonical_u64(2364819430), ]; executor_run_test_program( - "../assembler/test_data/bin/storage_u32.json", + "/Users/Softcloud/develop/zk/sin7y/olavm/assembler/test_data/bin/storage_u32.json", "storage_u32_trace.txt", false, Some(calldata),