From 3d9a8017c3e243cb2e9a7253e15d373e0d0c0006 Mon Sep 17 00:00:00 2001 From: Ho Date: Tue, 30 Jul 2024 08:25:36 +0800 Subject: [PATCH 1/5] Soundness Tests for `SeqInstTable` and `SequenceConfig` (#1323) * more verification on positive tests Signed-off-by: noelwei * prerequisite for negative tests Signed-off-by: noelwei * negative mock framework for inst table Signed-off-by: noelwei * negative sample Signed-off-by: noelwei * more negative samples Signed-off-by: noelwei * extend testing framework Signed-off-by: noelwei * negative test framework Signed-off-by: noelwei * negative tests Signed-off-by: noelwei * more negative tests Signed-off-by: noelwei * more negative tests Signed-off-by: noelwei * more negative tests Signed-off-by: noelwei --------- Signed-off-by: noelwei --- .../src/aggregation/decoder/seq_exec.rs | 730 +++++++++++++++--- .../decoder/tables/seqinst_table.rs | 557 +++++++++++-- .../src/aggregation/decoder/witgen/types.rs | 2 +- 3 files changed, 1141 insertions(+), 148 deletions(-) diff --git a/aggregator/src/aggregation/decoder/seq_exec.rs b/aggregator/src/aggregation/decoder/seq_exec.rs index 6d68719bc5..79d0a4e7b3 100644 --- a/aggregator/src/aggregation/decoder/seq_exec.rs +++ b/aggregator/src/aggregation/decoder/seq_exec.rs @@ -124,6 +124,7 @@ impl LiteralTable { pub fn mock_assign( &self, layouter: &mut impl Layouter, + block_id: u64, literals: &[u64], ) -> Result<(), Error> { layouter.assign_region( @@ -157,7 +158,7 @@ impl LiteralTable { )?; for (col, val) in [ (self.tag, F::from(ZstdTag::ZstdBlockLiteralsRawBytes as u64)), - (self.block_index, F::one()), + (self.block_index, F::from(block_id)), (self.byte_index, F::from(i as u64 + 1)), (self.char, F::from(char)), (self.last_flag, F::zero()), @@ -191,7 +192,7 @@ impl LiteralTable { || "set dummy border", self.block_index, offset, - || Value::known(F::from(2)), + || Value::known(F::from(block_id + 1)), )?; region.assign_advice( || "set dummy border", @@ -988,54 +989,63 @@ impl SeqExecConfig { #[cfg(test)] #[allow(clippy::too_many_arguments)] - pub fn mock_assign( + fn mock_assign( &self, - layouter: &mut impl Layouter, - chng: Value, - n_seq: usize, - seq_exec_infos: &[SequenceExec], - literals: &[u8], - // all of the decompressed bytes, not only current block - decompressed_bytes: &[u8], - enabled_rows: usize, + region: &mut Region, + offset: usize, + index_mock: [Option; 2], //block_ind, seq_ind + decode_mock: [Option; 2], //decode_le, decode_byte, + phase_mock: [Option; 3], //last_phase, cp_phase, backref_phase + pos_mock: [Option; 3], //literal, offset, ref_len ) -> Result<(), Error> { - let literals = literals - .iter() - .copied() - .map(|b| b as u64) - .collect::>(); + for (mock_val, col) in index_mock + .into_iter() + .zip([self.block_index, self.seq_index]) + { + if let Some(val) = mock_val { + region.assign_advice(|| "mock index", col, offset, || Value::known(val))?; + } + } - layouter.assign_region( - || "output region", - |mut region| { - let offset = self.init_top_row(&mut region, None)?; - let (offset, decoded_len, decoded_rlc) = self.assign_block( - &mut region, - chng, - offset, - 0, - Value::known(F::zero()), - &SequenceInfo { - block_idx: 1, - num_sequences: n_seq, - ..Default::default() - }, - seq_exec_infos.iter(), - &literals, - decompressed_bytes, - )?; - self.paddings( - &mut region, - offset, - enabled_rows, - decoded_len, - decoded_rlc, - 2, - )?; + for (mock_val, col) in decode_mock + .into_iter() + .zip([self.decoded_len, self.decoded_byte]) + { + if let Some(val) = mock_val { + region.assign_advice(|| "mock decode", col, offset, || Value::known(val))?; + } + } + if let Some(val) = decode_mock[1] { + region.assign_advice( + || "mock decode rlc", + self.decoded_rlc, + offset, + || Value::known(val), + )?; + } - Ok(()) - }, - ) + for (mock_val, col) in + pos_mock + .into_iter() + .zip([self.literal_pos, self.backref_offset, self.backref_progress]) + { + if let Some(val) = mock_val { + region.assign_advice(|| "mock position", col, offset, || Value::known(val))?; + } + } + + for (mock_val, bool_adv) in phase_mock.into_iter().zip([ + self.s_last_lit_cp_phase, + self.s_lit_cp_phase, + self.s_back_ref_phase, + ]) { + if let Some(val) = mock_val { + let val = Value::known(if val { F::one() } else { F::zero() }); + region.assign_advice(|| "phase mock", bool_adv.column, offset, || val)?; + } + } + + Ok(()) } } @@ -1046,28 +1056,41 @@ mod tests { circuit::SimpleFloorPlanner, dev::MockProver, halo2curves::bn256::Fr, plonk::Circuit, }; use witgen::AddressTableRow; - use zkevm_circuits::util::MockChallenges; - #[derive(Clone, Debug)] + #[allow(dead_code)] + #[derive(Clone, Copy, Debug)] + enum MockEntry { + Index([Option; 2]), + Decode([Option; 2]), + Phase([Option; 3]), + Position([Option; 3]), + } + + #[derive(Clone, Debug, Default)] struct SeqExecMock { - outputs: Vec, literals: Vec, seq_conf: SequenceInfo, insts: Vec, exec_trace: Vec, + mocks: Vec<(usize, MockEntry)>, + literal_mocks: Option>, } impl SeqExecMock { // use the code in witgen to generate exec trace - pub fn mock_generate(literals: Vec, insts: Vec) -> Self { + pub fn mock_generate( + block_idx: usize, + literals: Vec, + insts: Vec, + outputs: &mut Vec, + ) -> Self { let seq_conf = SequenceInfo { - block_idx: 1, + block_idx, num_sequences: insts.len(), ..Default::default() }; let mut exec_trace = Vec::new(); - let mut outputs = Vec::new(); let mut current_literal_pos: usize = 0; for inst in &insts { @@ -1106,11 +1129,11 @@ mod tests { } Self { - outputs, literals, seq_conf, insts, exec_trace, + ..Default::default() } } } @@ -1121,10 +1144,18 @@ mod tests { inst_tbl: SeqInstTable, literal_tbl: LiteralTable, seq_cfg: SequenceConfig, - chng_mock: MockChallenges, + //chng_mock: MockChallenges, } - impl Circuit for SeqExecMock { + #[derive(Clone, Default)] + struct SeqExecMockCircuit { + traces: Vec, + padding_mocks: Vec<(usize, MockEntry)>, + all_padding_mocks: Vec, + output: Vec, + } + + impl Circuit for SeqExecMockCircuit { type Config = SeqExecMockConfig; type FloorPlanner = SimpleFloorPlanner; fn without_witnesses(&self) -> Self { @@ -1154,23 +1185,15 @@ mod tests { let inst_tbl = SeqInstTable::configure(meta); - let chng_mock = MockChallenges::construct_p1(meta); - let chng = chng_mock.exprs(meta); + let chng = 0.expr(); - let config = SeqExecConfig::configure( - meta, - chng.keccak_input(), - &literal_tbl, - &inst_tbl, - &seq_cfg, - ); + let config = SeqExecConfig::configure(meta, chng, &literal_tbl, &inst_tbl, &seq_cfg); Self::Config { config, literal_tbl, inst_tbl, seq_cfg, - chng_mock, } } @@ -1179,32 +1202,132 @@ mod tests { config: Self::Config, mut layouter: impl Layouter, ) -> Result<(), Error> { - config.literal_tbl.mock_assign( - &mut layouter, - self.literals - .iter() - .copied() - .map(|b| b as u64) - .collect::>() - .as_slice(), - )?; + for blk_trace in &self.traces { + config + .seq_cfg + .mock_assign(&mut layouter, &blk_trace.seq_conf)?; + + let literals = blk_trace + .literal_mocks + .as_ref() + .unwrap_or(&blk_trace.literals); + + config.literal_tbl.mock_assign( + &mut layouter, + blk_trace.seq_conf.block_idx as u64, + literals + .iter() + .copied() + .map(|b| b as u64) + .collect::>() + .as_slice(), + )?; + } - config.seq_cfg.mock_assign(&mut layouter, &self.seq_conf)?; + let chng_val = Value::known(Fr::zero()); + let assigned_rows = layouter.assign_region( + || "mock exec output region", + |mut region| { + let config = &config.config; + + let mut offset = config.init_top_row(&mut region, None)?; + + let mut decoded_len = 0usize; + let mut decoded_rlc = Value::known(Fr::zero()); + let mut blk_ind = 0; + + let fill_mock_row = + |region: &mut Region, offset: usize, entry: &MockEntry| match entry { + MockEntry::Decode(mock) => config.mock_assign( + region, + offset, + [None, None], + *mock, + [None, None, None], + [None, None, None], + ), + MockEntry::Index(mock) => config.mock_assign( + region, + offset, + *mock, + [None, None], + [None, None, None], + [None, None, None], + ), + MockEntry::Phase(mock) => config.mock_assign( + region, + offset, + [None, None], + [None, None], + *mock, + [None, None, None], + ), + MockEntry::Position(mock) => config.mock_assign( + region, + offset, + [None, None], + [None, None], + [None, None, None], + *mock, + ), + }; + + for tr in &self.traces { + let literals = tr + .literals + .iter() + .copied() + .map(|b| b as u64) + .collect::>(); + let seq_info = &tr.seq_conf; + let exec_trace = &tr.exec_trace; + blk_ind = seq_info.block_idx; + let begin_offset = offset; + (offset, decoded_len, decoded_rlc) = config.assign_block( + &mut region, + chng_val, + offset, + decoded_len, + decoded_rlc, + seq_info, + exec_trace.iter(), + &literals, + &self.output, + )?; - config - .inst_tbl - .mock_assign(&mut layouter, &self.insts, 15)?; + for (mock_offset, entry) in &tr.mocks { + assert!(mock_offset + begin_offset < offset); + fill_mock_row(&mut region, begin_offset + mock_offset, entry)?; + } + } - let chng_val = config.chng_mock.values(&layouter); + let end_offset = offset + 10; + config.paddings( + &mut region, + offset, + end_offset, + decoded_len, + decoded_rlc, + blk_ind as u64 + 1, + )?; + for (offset, entry) in (offset..end_offset) + .flat_map(|i| self.all_padding_mocks.iter().map(move |entry| (i, entry))) + { + fill_mock_row(&mut region, offset, entry)?; + } - config.config.mock_assign( + for (mock_offset, entry) in &self.padding_mocks { + fill_mock_row(&mut region, offset + mock_offset, entry)?; + } + + Ok(end_offset) + }, + )?; + + config.inst_tbl.assign( &mut layouter, - chng_val.keccak_input(), - self.insts.len(), - &self.exec_trace, - &self.literals, - &self.outputs, - 50, + self.traces.iter().map(|tr| tr.insts.iter()), + assigned_rows, )?; Ok(()) @@ -1214,9 +1337,21 @@ mod tests { #[test] fn seq_exec_literal_only() { // no instructions, we only copy literals to output - let circuit = SeqExecMock::mock_generate(Vec::from("abcd".as_bytes()), Vec::new()); - - assert_eq!(circuit.outputs, Vec::from("abcd".as_bytes())); + let mut output = Vec::new(); + let traces = vec![SeqExecMock::mock_generate( + 1, + Vec::from("abcd".as_bytes()), + Vec::new(), + &mut output, + )]; + + let circuit = SeqExecMockCircuit { + traces, + output, + ..Default::default() + }; + + assert_eq!(circuit.output, Vec::from("abcd".as_bytes())); let k = 12; let mock_prover = @@ -1226,17 +1361,94 @@ mod tests { #[test] fn seq_exec_simple() { - // no instructions, we only copy literals to output - let circuit = SeqExecMock::mock_generate( + let mut output = Vec::new(); + let traces = vec![SeqExecMock::mock_generate( + 1, Vec::from("abcdef".as_bytes()), AddressTableRow::mock_samples_full([ [1, 4, 1, 1, 4, 8], [9, 1, 3, 6, 1, 4], [3, 0, 4, 5, 6, 1], ]), - ); + &mut output, + )]; + let circuit = SeqExecMockCircuit { + traces, + output, + ..Default::default() + }; + + assert_eq!(circuit.output, Vec::from("abcddeabcdeabf".as_bytes())); + + let k = 12; + let mock_prover = + MockProver::::run(k, &circuit, vec![]).expect("failed to run mock prover"); + mock_prover.verify().unwrap(); + } - assert_eq!(circuit.outputs, Vec::from("abcddeabcdeabf".as_bytes())); + // #[test] + // fn seq_exec_no_literal() { + // + // let mut output = Vec::new(); + // let traces = vec![ + // SeqExecMock::mock_generate( + // 1, + // Vec::from("abcdef".as_bytes()), + // AddressTableRow::mock_samples_full([ + // [1, 4, 1, 1, 4, 8], + // [9, 1, 3, 6, 1, 4], + // [3, 0, 4, 5, 6, 1], + // ]), + // &mut output, + // ), + // SeqExecMock::mock_generate( + // 2, + // Vec::new(), + // AddressTableRow::mock_samples_full([ + // [17, 0, 3, 14, 5, 6], + // [7, 0, 2, 4, 14, 5], + // ]), + // &mut output, + // ) + // ]; + // let circuit = SeqExecMockCircuit {traces, output}; + + // assert_eq!(circuit.output, Vec::from("abcddeabcdeabfabcfa".as_bytes())); + + // let k = 12; + // let mock_prover = + // MockProver::::run(k, &circuit, vec![]).expect("failed to run mock prover"); + // mock_prover.verify().unwrap(); + // } + + #[test] + fn seq_exec_common() { + let mut output = Vec::new(); + let traces = vec![ + SeqExecMock::mock_generate( + 1, + Vec::from("abcdef".as_bytes()), + AddressTableRow::mock_samples_full([ + [1, 4, 1, 1, 4, 8], + [9, 1, 3, 6, 1, 4], + [3, 0, 4, 5, 6, 1], + ]), + &mut output, + ), + SeqExecMock::mock_generate( + 2, + Vec::from("g".as_bytes()), + AddressTableRow::mock_samples_full([[17, 0, 3, 14, 5, 6], [8, 1, 2, 5, 14, 5]]), + &mut output, + ), + ]; + let circuit = SeqExecMockCircuit { + traces, + output, + ..Default::default() + }; + + assert_eq!(circuit.output, Vec::from("abcddeabcdeabfabcgfa".as_bytes())); let k = 12; let mock_prover = @@ -1246,17 +1458,24 @@ mod tests { #[test] fn seq_exec_rle_like() { - // no instructions, we only copy literals to output - let circuit = SeqExecMock::mock_generate( + let mut output = Vec::new(); + let traces = vec![SeqExecMock::mock_generate( + 1, Vec::from("abcdef".as_bytes()), AddressTableRow::mock_samples_full([ [1, 4, 1, 1, 4, 8], [9, 1, 3, 6, 1, 4], [5, 0, 6, 2, 6, 1], // an RLE like inst, match len exceed match offset ]), - ); + &mut output, + )]; + let circuit = SeqExecMockCircuit { + traces, + output, + ..Default::default() + }; - assert_eq!(circuit.outputs, Vec::from("abcddeabcbcbcbcf".as_bytes())); + assert_eq!(circuit.output, Vec::from("abcddeabcbcbcbcf".as_bytes())); let k = 12; let mock_prover = @@ -1266,17 +1485,326 @@ mod tests { #[test] fn seq_exec_no_tail_cp() { - // no instructions, we only copy literals to output - let circuit = SeqExecMock::mock_generate( + let mut output = Vec::new(); + let traces = vec![SeqExecMock::mock_generate( + 1, Vec::from("abcde".as_bytes()), AddressTableRow::mock_samples_full([[1, 4, 1, 1, 4, 8], [9, 1, 3, 6, 1, 4]]), - ); + &mut output, + )]; + let circuit = SeqExecMockCircuit { + traces, + output, + ..Default::default() + }; - assert_eq!(circuit.outputs, Vec::from("abcddeabc".as_bytes())); + assert_eq!(circuit.output, Vec::from("abcddeabc".as_bytes())); let k = 12; let mock_prover = MockProver::::run(k, &circuit, vec![]).expect("failed to run mock prover"); mock_prover.verify().unwrap(); } + + #[test] + fn seq_exec_neg_literal_unmatch() { + let mut output = Vec::new(); + let base_trace_blk1 = SeqExecMock::mock_generate( + 1, + Vec::from("abcdef".as_bytes()), + AddressTableRow::mock_samples_full([ + [1, 4, 1, 1, 4, 8], + [9, 1, 3, 6, 1, 4], + [3, 0, 4, 5, 6, 1], + ]), + &mut output, + ); + let base_trace_blk2 = SeqExecMock::mock_generate( + 2, + Vec::from("g".as_bytes()), + AddressTableRow::mock_samples_full([[17, 0, 3, 14, 5, 6], [8, 1, 2, 5, 14, 5]]), + &mut output, + ); + + assert_eq!(output, Vec::from("abcddeabcdeabfabcgfa".as_bytes())); + + let mut literal_unmatch_blk = base_trace_blk1.clone(); + literal_unmatch_blk.literal_mocks = Some(Vec::from("abcdefg".as_bytes())); + let lit_unmatch_circuit_1 = SeqExecMockCircuit { + traces: vec![literal_unmatch_blk, base_trace_blk2.clone()], + output: output.clone(), + ..Default::default() + }; + let mut literal_unmatch_blk = base_trace_blk1.clone(); + literal_unmatch_blk.literal_mocks = Some(Vec::from("abddef".as_bytes())); + let lit_unmatch_circuit_2 = SeqExecMockCircuit { + traces: vec![literal_unmatch_blk, base_trace_blk2.clone()], + output: output.clone(), + ..Default::default() + }; + let mut literal_unmatch_blk1 = base_trace_blk1.clone(); + literal_unmatch_blk1.literal_mocks = Some(Vec::from("abcde".as_bytes())); + let mut literal_unmatch_blk2 = base_trace_blk2.clone(); + literal_unmatch_blk2.literal_mocks = Some(Vec::from("fg".as_bytes())); + let lit_unmatch_circuit_3 = SeqExecMockCircuit { + traces: vec![literal_unmatch_blk1, literal_unmatch_blk2], + output: output.clone(), + ..Default::default() + }; + + let k = 12; + for circuit in [ + lit_unmatch_circuit_1, + lit_unmatch_circuit_2, + lit_unmatch_circuit_3, + ] { + let mock_prover = + MockProver::::run(k, &circuit, vec![]).expect("failed to run mock prover"); + let ret = mock_prover.verify(); + println!("{:?}", ret); + assert!(ret.is_err()); + } + } + + #[test] + fn seq_exec_neg_phases() { + let mut output = Vec::new(); + let base_trace_blk1 = SeqExecMock::mock_generate( + 1, + Vec::from("abcdef".as_bytes()), + AddressTableRow::mock_samples_full([ + [1, 4, 1, 1, 4, 8], + [9, 1, 3, 6, 1, 4], + [3, 0, 4, 5, 6, 1], + [4, 0, 1, 1, 5, 6], + ]), + &mut output, + ); + let base_trace_blk2 = SeqExecMock::mock_generate( + 2, + Vec::from("gg".as_bytes()), + AddressTableRow::mock_samples_full([[4, 2, 2, 1, 1, 5]]), + &mut output, + ); + + assert_eq!(output, Vec::from("abcddeabcdeabbfgggg".as_bytes())); + + // try to put the final phase into previous one + let mut mis_phase_blk1 = base_trace_blk1.clone(); + assert_eq!(mis_phase_blk1.exec_trace.len(), 7); + assert_eq!(mis_phase_blk1.exec_trace[6].0, 4); + assert_eq!(mis_phase_blk1.exec_trace[5].0, 3); + mis_phase_blk1.exec_trace[6].0 = 3; + mis_phase_blk1.mocks = vec![ + (14, MockEntry::Index([None, Some(Fr::from(3u64))])), + (14, MockEntry::Phase([Some(false), None, None])), + ]; + + let circuit_mis_phase_1 = SeqExecMockCircuit { + traces: vec![mis_phase_blk1, base_trace_blk2.clone()], + output: output.clone(), + ..Default::default() + }; + + // try to make last cp phase cross instruction + let mut mis_phase_blk2 = base_trace_blk1.clone(); + mis_phase_blk2.mocks = vec![(13, MockEntry::Phase([Some(true), None, None]))]; + let circuit_mis_phase_2 = SeqExecMockCircuit { + traces: vec![mis_phase_blk2, base_trace_blk2.clone()], + output: output.clone(), + ..Default::default() + }; + + // try to a phase both lit-cp and backref + let mut mis_phase_blk3 = base_trace_blk1.clone(); + mis_phase_blk3.mocks = vec![(13, MockEntry::Phase([Some(false), Some(true), Some(true)]))]; + let circuit_mis_phase_3 = SeqExecMockCircuit { + traces: vec![mis_phase_blk3, base_trace_blk2.clone()], + output: output.clone(), + ..Default::default() + }; + + // detect phase must work in a normal row + let mut mis_phase_blk4 = base_trace_blk2.clone(); + mis_phase_blk4.mocks = vec![ + (3, MockEntry::Phase([Some(false), Some(false), Some(false)])), + (3, MockEntry::Decode([Some(Fr::from(18)), Some(Fr::zero())])), + ]; + let circuit_mis_phase_4 = SeqExecMockCircuit { + traces: vec![base_trace_blk1.clone(), mis_phase_blk4], + output: Vec::from("abcddeabcdeabbfggg".as_bytes()), + all_padding_mocks: vec![MockEntry::Decode([Some(Fr::from(18)), None])], + ..Default::default() + }; + + // detect out of order phases + let mut mis_phase_blk5 = base_trace_blk2.clone(); + mis_phase_blk5.mocks = vec![ + (0, MockEntry::Decode([None, Some(Fr::from(0x66))])), //the decoded byte become 'f' + ( + 0, + MockEntry::Position([Some(Fr::zero()), None, Some(Fr::one())]), + ), + ( + 1, + MockEntry::Position([Some(Fr::one()), None, Some(Fr::one())]), + ), + ( + 2, + MockEntry::Position([Some(Fr::from(2)), None, Some(Fr::one())]), + ), + (0, MockEntry::Phase([None, Some(false), Some(true)])), + (2, MockEntry::Phase([None, Some(true), Some(false)])), + ]; + let circuit_mis_phase_5 = SeqExecMockCircuit { + traces: vec![base_trace_blk1.clone(), mis_phase_blk5], + output: output.clone(), + ..Default::default() + }; + + let k = 12; + for circuit in [ + circuit_mis_phase_1, + circuit_mis_phase_2, + circuit_mis_phase_3, + circuit_mis_phase_4, + circuit_mis_phase_5, + ] { + let mock_prover = + MockProver::::run(k, &circuit, vec![]).expect("failed to run mock prover"); + let ret = mock_prover.verify(); + println!("{:?}", ret); + assert!(ret.is_err()); + } + } + + #[test] + fn seq_exec_neg_insts() { + let mut output = Vec::new(); + let base_trace_blk = SeqExecMock::mock_generate( + 1, + Vec::from("abcdef".as_bytes()), + AddressTableRow::mock_samples_full([ + [1, 4, 1, 1, 4, 8], + [9, 1, 3, 6, 1, 4], + [3, 0, 4, 5, 6, 1], + [4, 0, 1, 1, 5, 6], + ]), + &mut output, + ); + + assert_eq!(output, Vec::from("abcddeabcdeabbf".as_bytes())); + + let mut output_mis = Vec::new(); + SeqExecMock::mock_generate( + 1, + Vec::from("abcdef".as_bytes()), + AddressTableRow::mock_samples_full([ + [1, 4, 1, 1, 4, 8], + [9, 1, 3, 6, 1, 4], + [3, 0, 4, 5, 6, 1], + [4, 0, 2, 1, 5, 6], + ]), + &mut output_mis, + ); + + assert_eq!(output_mis, Vec::from("abcddeabcdeabbbf".as_bytes())); + + let mut mis_inst_blk = base_trace_blk.clone(); + let tr = &mut mis_inst_blk.exec_trace[5].1; + assert_eq!(*tr, SequenceExecInfo::BackRef(12..13)); + // build the mis-match len to 2 + *tr = SequenceExecInfo::BackRef(12..14); + + let circuit_mis_inst_1 = SeqExecMockCircuit { + traces: vec![mis_inst_blk], + output: output_mis, + ..Default::default() + }; + let mut mis_inst_blk = base_trace_blk.clone(); + let tr = &mut mis_inst_blk.exec_trace[5].1; + // build the mis-match offset to 2 + *tr = SequenceExecInfo::BackRef(11..12); + let circuit_mis_inst_2 = SeqExecMockCircuit { + traces: vec![mis_inst_blk], + output: Vec::from("abcddeabcdeabaf".as_bytes()), + ..Default::default() + }; + + let k = 12; + for circuit in [circuit_mis_inst_1, circuit_mis_inst_2] { + let mock_prover = + MockProver::::run(k, &circuit, vec![]).expect("failed to run mock prover"); + let ret = mock_prover.verify(); + println!("{:?}", ret); + assert!(ret.is_err()); + } + } + + #[test] + fn seq_exec_neg_paddings() { + let mut output = Vec::new(); + let base_trace_blk = SeqExecMock::mock_generate( + 1, + Vec::from("abcdef".as_bytes()), + AddressTableRow::mock_samples_full([ + [1, 4, 1, 1, 4, 8], + [9, 1, 3, 6, 1, 4], + [3, 0, 4, 5, 6, 1], + [4, 0, 1, 1, 5, 6], + ]), + &mut output, + ); + + assert_eq!(output, Vec::from("abcddeabcdeabbf".as_bytes())); + + let circuit_ref = SeqExecMockCircuit { + traces: vec![base_trace_blk.clone()], + output: output.clone(), + padding_mocks: vec![ + (0, MockEntry::Decode([Some(Fr::from(15)), None])), + (1, MockEntry::Index([Some(Fr::from(2)), Some(Fr::zero())])), + ], + ..Default::default() + }; + + let k = 12; + let mock_prover = + MockProver::::run(k, &circuit_ref, vec![]).expect("failed to run mock prover"); + assert!(mock_prover.verify().is_ok()); + + let circuit_mal_block_index = SeqExecMockCircuit { + traces: vec![base_trace_blk.clone()], + output: output.clone(), + padding_mocks: vec![(4, MockEntry::Index([Some(Fr::from(3)), None]))], + ..Default::default() + }; + let circuit_mal_decode_len = SeqExecMockCircuit { + traces: vec![base_trace_blk.clone()], + output: output.clone(), + padding_mocks: vec![(4, MockEntry::Decode([Some(Fr::from(16)), None]))], + ..Default::default() + }; + let circuit_mal_inst = SeqExecMockCircuit { + traces: vec![base_trace_blk.clone()], + output: output.clone(), + padding_mocks: vec![ + (0, MockEntry::Index([Some(Fr::from(1)), Some(Fr::from(5))])), + (0, MockEntry::Phase([Some(true), Some(true), None])), + ], + ..Default::default() + }; + + for circuit in [ + circuit_mal_block_index, + circuit_mal_decode_len, + circuit_mal_inst, + ] { + let mock_prover = + MockProver::::run(k, &circuit, vec![]).expect("failed to run mock prover"); + let ret = mock_prover.verify(); + println!("{:?}", ret); + assert!(ret.is_err()); + } + } } diff --git a/aggregator/src/aggregation/decoder/tables/seqinst_table.rs b/aggregator/src/aggregation/decoder/tables/seqinst_table.rs index bf567e9195..ba75aa2d16 100644 --- a/aggregator/src/aggregation/decoder/tables/seqinst_table.rs +++ b/aggregator/src/aggregation/decoder/tables/seqinst_table.rs @@ -944,49 +944,56 @@ impl SeqInstTable { } #[cfg(test)] - pub fn mock_assign( + fn mock_assign( &self, - layouter: &mut impl Layouter, - table_rows: &[AddressTableRow], - enabled_rows: usize, + region: &mut Region, + offset: usize, + block_ind: u64, + n_seq: usize, + chip_ctx: &ChipContext, + mock_table_row: &AddressTableRow, ) -> Result<(), Error> { - let chip_ctx = ChipContext::construct(self); - layouter.assign_region( - || "addr table", - |mut region| { - let mut offset_table: [u64; 3] = [1, 4, 8]; - let offset = self.init_top_row(&mut region, None)?; - let offset = self.assign_heading_row( - &mut region, - offset, - 1, - table_rows.len(), - &chip_ctx, - &offset_table, - )?; - let offset = self.assign_block( - &mut region, - offset, - 1, - table_rows.len(), - table_rows.iter(), - &chip_ctx, - &mut offset_table, - )?; - assert!(offset < enabled_rows); + for (col, val) in [ + (self.s_beginning.column, F::from(mock_table_row.s_padding)), + (self.rep_offset_1, F::from(mock_table_row.repeated_offset1)), + (self.rep_offset_2, F::from(mock_table_row.repeated_offset2)), + (self.rep_offset_3, F::from(mock_table_row.repeated_offset3)), + (self.match_len, F::from(mock_table_row.match_length)), + ( + self.match_offset, + F::from(mock_table_row.cooked_match_offset), + ), + (self.literal_len, F::from(mock_table_row.literal_length)), + ( + self.acc_literal_len, + F::from(mock_table_row.literal_length_acc), + ), + (self.offset, F::from(mock_table_row.actual_offset)), + (self.seq_index, F::from(mock_table_row.instruction_idx + 1)), + (self.block_index, F::from(block_ind)), + (self.n_seq, F::from(n_seq as u64)), + ] { + region.assign_advice(|| "mock rewritten", col, offset, || Value::known(val))?; + } - self.padding_rows( - &mut region, - offset, - enabled_rows, - 2, - &chip_ctx, - &offset_table, - )?; + chip_ctx.literal_is_zero_chip.assign( + region, + offset, + Value::known(F::from(mock_table_row.literal_length)), + )?; + chip_ctx.ref_offset_1_is_zero_chip.assign( + region, + offset, + Value::known(F::from(mock_table_row.repeated_offset1)), + )?; + chip_ctx.seq_index_chip.assign( + region, + offset, + Value::known(F::from(mock_table_row.instruction_idx + 1)), + Value::known(F::from(n_seq as u64)), + )?; - Ok(()) - }, - ) + Ok(()) } } @@ -996,12 +1003,156 @@ mod tests { use halo2_proofs::{ circuit::SimpleFloorPlanner, dev::MockProver, halo2curves::bn256::Fr, plonk::Circuit, }; + use itertools::Itertools; + + #[derive(Clone, Debug)] + struct MockDecoderTable { + q_inst_data: Column, + q_inst_cnt: Column, + block_idx: Column, + sequence_idx: Column, + literal_length_value: Column, + match_offset_value: Column, + match_length_value: Column, + } + + impl MockDecoderTable { + fn configure(meta: &mut ConstraintSystem, inst_table: &SeqInstTable) -> Self { + let q_inst_data = meta.fixed_column(); + let q_inst_cnt = meta.fixed_column(); + let block_idx = meta.advice_column(); + let sequence_idx = meta.advice_column(); + let literal_length_value = meta.advice_column(); + let match_offset_value = meta.advice_column(); + let match_length_value = meta.advice_column(); + + meta.lookup_any("Mock DecoderConfig's tag ZstdBlockSequenceData", |meta| { + let enabled = meta.query_fixed(q_inst_data, Rotation::cur()); + let (block_idx, sequence_idx) = ( + meta.query_advice(block_idx, Rotation::cur()), + meta.query_advice(sequence_idx, Rotation::cur()), + ); + let (literal_length_value, match_offset_value, match_length_value) = ( + meta.query_advice(literal_length_value, Rotation::cur()), + meta.query_advice(match_offset_value, Rotation::cur()), + meta.query_advice(match_length_value, Rotation::cur()), + ); + [ + 1.expr(), // q_enabled + block_idx, + 0.expr(), // s_beginning + sequence_idx, + literal_length_value, + match_offset_value, + match_length_value, + ] + .into_iter() + .zip_eq(inst_table.seq_values_exprs(meta)) + .map(|(arg, table)| (enabled.expr() * arg, table)) + .collect() + }); + + meta.lookup_any("Mock DecoderConfig's tag ZstdBlockSequenceHeader", |meta| { + let enabled = meta.query_fixed(q_inst_cnt, Rotation::cur()); + let (block_idx, sequence_idx) = ( + meta.query_advice(block_idx, Rotation::cur()), + meta.query_advice(sequence_idx, Rotation::cur()), + ); + + [ + 1.expr(), // q_enabled + block_idx, + 1.expr(), // s_beginning + sequence_idx, + ] + .into_iter() + .zip_eq(inst_table.seq_count_exprs(meta)) + .map(|(arg, table)| (enabled.expr() * arg, table)) + .collect() + }); + + Self { + q_inst_data, + q_inst_cnt, + block_idx, + sequence_idx, + literal_length_value, + match_offset_value, + match_length_value, + } + } + + fn assign_blk( + &self, + layouter: &mut impl Layouter, + blk_id: usize, + rows: &[AddressTableRow], + ) -> Result<(), Error> { + layouter.assign_region( + || format!("mock decoder config blk {}", blk_id), + |mut region| { + for (i, row) in rows.iter().enumerate() { + for (col, val) in [ + (self.block_idx, Fr::from(blk_id as u64)), + (self.sequence_idx, Fr::from(row.instruction_idx + 1)), + (self.literal_length_value, Fr::from(row.literal_length)), + (self.match_offset_value, Fr::from(row.cooked_match_offset)), + (self.match_length_value, Fr::from(row.match_length)), + ] { + region.assign_advice( + || "assign mock data", + col, + i, + || Value::known(val), + )?; + } + region.assign_fixed( + || "enable mock data row", + self.q_inst_data, + i, + || Value::known(Fr::one()), + )?; + } + let n_seq = rows.len(); + region.assign_fixed( + || "enable mock cnt row", + self.q_inst_cnt, + n_seq, + || Value::known(Fr::one()), + )?; + for (col, val) in [ + (self.block_idx, Fr::from(blk_id as u64)), + (self.sequence_idx, Fr::from(n_seq as u64)), + ] { + region.assign_advice( + || "assign mock nseq", + col, + n_seq, + || Value::known(val), + )?; + } + + Ok(()) + }, + ) + } + } #[derive(Clone, Debug)] - struct SeqTable(Vec); + struct MockSeqTableConfig { + config: SeqInstTable, + mock_decoder: MockDecoderTable, + } + + #[derive(Clone, Debug, Default)] + struct SeqTable { + base_data: Vec>, + rows_for_table: Option>>, + mock_rows: Vec)>>, + } impl Circuit for SeqTable { - type Config = SeqInstTable; + type Config = MockSeqTableConfig; type FloorPlanner = SimpleFloorPlanner; fn without_witnesses(&self) -> Self { unimplemented!() @@ -1011,7 +1162,13 @@ mod tests { let const_col = meta.fixed_column(); meta.enable_constant(const_col); - Self::Config::configure(meta) + let config = SeqInstTable::configure(meta); + let mock_decoder = MockDecoderTable::configure(meta, &config); + + Self::Config { + config, + mock_decoder, + } } fn synthesize( @@ -1019,16 +1176,142 @@ mod tests { config: Self::Config, mut layouter: impl Layouter, ) -> Result<(), Error> { - config.mock_assign(&mut layouter, &self.0, 15)?; + for (i, blk_rows) in self.base_data.iter().enumerate() { + config + .mock_decoder + .assign_blk(&mut layouter, i + 1, blk_rows)?; + } - Ok(()) + let rows_for_table = self.rows_for_table.as_ref().unwrap_or(&self.base_data); + let config = &config.config; + let chip_ctx = ChipContext::construct(config); + layouter.assign_region( + || "mock addr table", + |mut region| { + let mut offset_table: [u64; 3] = [1, 4, 8]; + let mut blk_id = 0u64; + let mut offset = config.init_top_row(&mut region, None)?; + for (i, rows_in_blk) in rows_for_table.iter().enumerate() { + blk_id = (i + 1) as u64; + let n_seqs = rows_in_blk.len(); + let block_begin_offset = offset; + offset = config.assign_heading_row( + &mut region, + offset, + blk_id, + n_seqs, + &chip_ctx, + &offset_table, + )?; + offset = config.assign_block( + &mut region, + offset, + blk_id, + n_seqs, + rows_in_blk.iter(), + &chip_ctx, + &mut offset_table, + )?; + + for (mock_offset, mock_row, overwriteen_id) in + self.mock_rows.get(i).iter().flat_map(|data| data.iter()) + { + assert!(*mock_offset <= offset); + config.mock_assign( + &mut region, + block_begin_offset + *mock_offset, + overwriteen_id.unwrap_or(blk_id), + n_seqs, + &chip_ctx, + mock_row, + )? + } + } + + config.padding_rows( + &mut region, + offset, + offset + 5, + blk_id + 1, + &chip_ctx, + &offset_table, + )?; + + Ok(()) + }, + ) } } #[test] fn seqinst_table_gates() { // example comes from zstd's spec - let circuit = SeqTable(AddressTableRow::mock_samples(&[ + let base_data = vec![ + AddressTableRow::mock_samples(&[ + [1114, 11, 1111, 1, 4], + [1, 22, 1111, 1, 4], + [2225, 22, 2222, 1111, 1], + [1114, 111, 1111, 2222, 1111], + [3336, 33, 3333, 1111, 2222], + [2, 22, 1111, 3333, 2222], + [3, 33, 2222, 1111, 3333], + [3, 0, 2221, 2222, 1111], + [1, 0, 2222, 2221, 1111], + ]), + AddressTableRow::mock_samples(&[ + [3, 0, 2221, 2222, 2221], + [3, 0, 2220, 2221, 2222], + [3, 0, 2219, 2220, 2221], + ]), + ]; + let mock_rows = vec![ + vec![(7, base_data[0][6].clone(), None)], + vec![(3, base_data[1][2].clone(), None)], + ]; + + let circuit = SeqTable { + base_data, + mock_rows, + ..Default::default() + }; + + let k = 12; + let mock_prover = + MockProver::::run(k, &circuit, vec![]).expect("failed to run mock prover"); + mock_prover.verify().unwrap(); + } + + #[test] + fn seqinst_table_negative_unmatch_seq() { + let mut base_data = vec![AddressTableRow::mock_samples(&[ + [1114, 11, 1111, 1, 4], + [1, 22, 1111, 1, 4], + [2225, 22, 2222, 1111, 1], + [1114, 111, 1111, 2222, 1111], + [3336, 33, 3333, 1111, 2222], + [2, 22, 1111, 3333, 2222], + [3, 33, 2222, 1111, 3333], + [3, 0, 2221, 2222, 1111], + [1, 0, 2222, 2221, 1111], + ])]; + let rows_for_table = Some(base_data.clone()); + // so we build a negative sample which there is an extra row in inst table + base_data[0].pop(); + + let circuit = SeqTable { + base_data, + rows_for_table, + ..Default::default() + }; + + let k = 12; + let mock_prover = + MockProver::::run(k, &circuit, vec![]).expect("failed to run mock prover"); + let ret = mock_prover.verify(); + println!("{:?}", ret); + assert!(ret.is_err()); + + let base_data = vec![AddressTableRow::mock_samples(&[ [1114, 11, 1111, 1, 4], [1, 22, 1111, 1, 4], [2225, 22, 2222, 1111, 1], @@ -1038,11 +1321,193 @@ mod tests { [3, 33, 2222, 1111, 3333], [3, 0, 2221, 2222, 1111], [1, 0, 2222, 2221, 1111], + ])]; + let mut rows_for_table = base_data.clone(); + // so we build a negative sample which there is less row in inst table + rows_for_table[0].pop(); + + let circuit = SeqTable { + base_data, + rows_for_table: Some(rows_for_table), + ..Default::default() + }; + + let mock_prover = + MockProver::::run(k, &circuit, vec![]).expect("failed to run mock prover"); + let ret = mock_prover.verify(); + println!("{:?}", ret); + assert!(ret.is_err()); + + let base_data = vec![AddressTableRow::mock_samples(&[ + [1114, 11, 1111, 1, 4], + [1, 22, 1111, 1, 4], + [2225, 22, 2222, 1111, 1], + [1114, 111, 1111, 2222, 1111], + [3336, 33, 3333, 1111, 2222], + [2, 22, 1111, 3333, 2222], + [3, 33, 2222, 1111, 3333], + [3, 0, 2221, 2222, 1111], + [1, 0, 2222, 2221, 1111], + ])]; + let mut rows_for_table = base_data.clone(); + rows_for_table[0].pop(); + // try to use a similar row in another block + rows_for_table.push(AddressTableRow::mock_samples(&[[1, 0, 2222, 2221, 1111]])); + let circuit = SeqTable { + base_data, + rows_for_table: Some(rows_for_table), + ..Default::default() + }; + + let mock_prover = + MockProver::::run(k, &circuit, vec![]).expect("failed to run mock prover"); + let ret = mock_prover.verify(); + println!("{:?}", ret); + assert!(ret.is_err()); + } + + #[test] + fn seqinst_table_negative_unmatch_block() { + let base_data = vec![AddressTableRow::mock_samples(&[ + [1114, 11, 1111, 1, 4], + [1, 22, 1111, 1, 4], + [2225, 22, 2222, 1111, 1], + [1114, 111, 1111, 2222, 1111], + [3336, 33, 3333, 1111, 2222], + [2, 22, 1111, 3333, 2222], + [11, 0, 8, 1111, 3333], + [7, 0, 4, 8, 1111], + [4, 0, 1, 4, 8], + ])]; + let mut rows_for_table = base_data.clone(); + // so we build a negative sample which there is an extra row in inst table + rows_for_table.push(AddressTableRow::mock_samples(&[ + [1, 5, 1, 4, 8], + [1, 5, 1, 4, 8], + [1, 5, 1, 4, 8], ])); + let circuit = SeqTable { + base_data: base_data.clone(), + rows_for_table: Some(rows_for_table.clone()), + ..Default::default() + }; + let k = 12; let mock_prover = MockProver::::run(k, &circuit, vec![]).expect("failed to run mock prover"); - mock_prover.verify().unwrap(); + let ret = mock_prover.verify(); + // notice more block data can be filled into seq table, but inst exec + // never touch them + assert!(ret.is_ok()); + + let circuit = SeqTable { + base_data: rows_for_table, + rows_for_table: Some(base_data.clone()), + ..Default::default() + }; + + let mock_prover = + MockProver::::run(k, &circuit, vec![]).expect("failed to run mock prover"); + let ret = mock_prover.verify(); + println!("{:?}", ret); + // but inst table can never miss a block + assert!(ret.is_err()); + + let rows_for_table = vec![ + AddressTableRow::mock_samples(&[ + [1, 5, 1, 4, 8], + [1, 5, 1, 4, 8], + [1, 5, 1, 4, 8], + [1, 5, 1, 4, 8], + [1, 5, 1, 4, 8], + [1, 5, 1, 4, 8], + [1, 5, 1, 4, 8], + [1, 5, 1, 4, 8], + [1, 5, 1, 4, 8], + ]), + base_data[0].clone(), + ]; + + let mock_rows = vec![ + Vec::new(), + rows_for_table[1] + .iter() + .enumerate() + .map(|(i, row)| (i + 1, row.clone(), Some(1))) + .collect(), + ]; + + let circuit = SeqTable { + base_data, + rows_for_table: Some(rows_for_table), + mock_rows, + }; + + let mock_prover = + MockProver::::run(k, &circuit, vec![]).expect("failed to run mock prover"); + let ret = mock_prover.verify(); + println!("{:?}", ret); + // try to put the block with unordered index, should fail + assert!(ret.is_err()); + } + + #[test] + fn seqinst_table_negative_unmatch_data() { + let base_data = vec![AddressTableRow::mock_samples(&[ + [1114, 11, 1111, 1, 4], + [1, 22, 1111, 1, 4], + [2225, 22, 2222, 1111, 1], + [1114, 111, 1111, 2222, 1111], + [3336, 33, 3333, 1111, 2222], + [2, 22, 1111, 3333, 2222], + [3, 33, 2222, 1111, 3333], + [3, 0, 2221, 2222, 1111], + [1, 0, 2222, 2221, 1111], + ])]; + + let mut wrong_offset_row = base_data[0][0].clone(); + assert_eq!(wrong_offset_row.actual_offset, 1111); + wrong_offset_row.actual_offset = 1110; + let neg_case_offset_1 = vec![vec![(1, wrong_offset_row.clone(), None)]]; + wrong_offset_row.cooked_match_offset = 1113; + let neg_case_offset_2 = vec![vec![(1, wrong_offset_row, None)]]; + + let mut wrong_lit_row = base_data[0][1].clone(); + assert_eq!(wrong_lit_row.literal_length, 22); + wrong_lit_row.literal_length = 23; + let wrong_lit_1 = vec![vec![(2, wrong_lit_row.clone(), None)]]; + wrong_lit_row.literal_length = 20; + let wrong_lit_2 = vec![vec![(2, wrong_lit_row.clone(), None)]]; + + let mut out_order_row_1 = base_data[0][0].clone(); + let mut out_order_row_2 = base_data[0][1].clone(); + assert_eq!(out_order_row_1.literal_length_acc, 11); + out_order_row_1.literal_length_acc = out_order_row_2.literal_length_acc; + out_order_row_2.literal_length_acc = 11; + let neg_case_outoforder_seq = vec![ + vec![(2, out_order_row_1, None)], + vec![(1, out_order_row_2, None)], + ]; + + let k = 12; + for mock_rows in [ + neg_case_offset_1, + neg_case_offset_2, + wrong_lit_1, + wrong_lit_2, + neg_case_outoforder_seq, + ] { + let circuit = SeqTable { + base_data: base_data.clone(), + mock_rows, + ..Default::default() + }; + let mock_prover = + MockProver::::run(k, &circuit, vec![]).expect("failed to run mock prover"); + let ret = mock_prover.verify(); + println!("{:?}", ret); + assert!(ret.is_err()); + } } } diff --git a/aggregator/src/aggregation/decoder/witgen/types.rs b/aggregator/src/aggregation/decoder/witgen/types.rs index 0bd3af5535..c5423643f5 100644 --- a/aggregator/src/aggregation/decoder/witgen/types.rs +++ b/aggregator/src/aggregation/decoder/witgen/types.rs @@ -51,7 +51,7 @@ pub struct SequenceInfo { } /// The type for indicate each range in output bytes by sequence execution -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum SequenceExecInfo { LiteralCopy(std::ops::Range), BackRef(std::ops::Range), From 0735eba0de73f578f09c110feee3a5452a57ddf9 Mon Sep 17 00:00:00 2001 From: Ray Gao Date: Mon, 29 Jul 2024 20:26:12 -0400 Subject: [PATCH 2/5] Soundness Tests for `BitstringTable` (#1309) * Add skeleton for bitstring soundness tests * Add test skeleton * Add test case * Add test cases * Complete bitstring table test cases * Fix compilation * Adjust feature flag * fmt and clippy * Complete debugging test cases * fmt * Fix failing constraint from non feature flag --------- Co-authored-by: Rohit Narurkar --- aggregator/Cargo.toml | 1 + aggregator/src/aggregation.rs | 2 +- aggregator/src/aggregation/decoder.rs | 2 +- .../aggregation/decoder/tables/bitstring.rs | 144 +++++- aggregator/src/tests.rs | 3 + aggregator/src/tests/bitstring.rs | 479 ++++++++++++++++++ 6 files changed, 605 insertions(+), 26 deletions(-) create mode 100644 aggregator/src/tests/bitstring.rs diff --git a/aggregator/Cargo.toml b/aggregator/Cargo.toml index d34abe90d9..d312464f6a 100644 --- a/aggregator/Cargo.toml +++ b/aggregator/Cargo.toml @@ -48,3 +48,4 @@ default = ["revm-precompile/c-kzg"] print-trace = ["ark-std/print-trace"] # This feature is useful for unit tests where we check the SAT of pi aggregation circuit disable_proof_aggregation = [] +soundness-tests = [] \ No newline at end of file diff --git a/aggregator/src/aggregation.rs b/aggregator/src/aggregation.rs index a27aba3e0a..1403bdea21 100644 --- a/aggregator/src/aggregation.rs +++ b/aggregator/src/aggregation.rs @@ -9,7 +9,7 @@ mod circuit; /// Config for aggregation circuit mod config; /// Config for decoding zstd-encoded data. -mod decoder; +pub mod decoder; /// config for RLC circuit mod rlc; /// Utility module diff --git a/aggregator/src/aggregation/decoder.rs b/aggregator/src/aggregation/decoder.rs index eed09afbe9..2957c7a536 100644 --- a/aggregator/src/aggregation/decoder.rs +++ b/aggregator/src/aggregation/decoder.rs @@ -1,5 +1,5 @@ mod seq_exec; -mod tables; +pub mod tables; pub mod witgen; use gadgets::{ diff --git a/aggregator/src/aggregation/decoder/tables/bitstring.rs b/aggregator/src/aggregation/decoder/tables/bitstring.rs index e46ffb3f4f..1cb6031312 100644 --- a/aggregator/src/aggregation/decoder/tables/bitstring.rs +++ b/aggregator/src/aggregation/decoder/tables/bitstring.rs @@ -1,6 +1,6 @@ use gadgets::util::{and, not, select, Expr}; use halo2_proofs::{ - circuit::{Layouter, Value}, + circuit::{AssignedCell, Layouter, Value}, halo2curves::bn256::Fr, plonk::{Advice, Any, Column, ConstraintSystem, Error, Expression, Fixed}, poly::Rotation, @@ -21,6 +21,30 @@ use crate::{ witgen::{N_BITS_PER_BYTE, N_BLOCK_SIZE_TARGET}, }; +#[derive(Default, Clone, Debug)] +pub struct AssignedBitstringTableRow { + pub q_start: Option>, + pub bit_index: Option>, + pub byte_idx_1: Option>, + pub byte_idx_2: Option>, + pub byte_idx_3: Option>, + pub byte_1: Option>, + pub byte_2: Option>, + pub byte_3: Option>, + pub bit: Option>, + pub bit_f: Option, + pub bitstring_value: Option>, + pub bitstring_value_acc: Option>, + pub bitstring_len: Option>, + pub from_start: Option>, + pub until_end: Option>, + pub is_reverse: Option>, + pub is_reverse_f: Option, + pub is_padding: Option>, +} + +pub(crate) type AssignedBitstringTableRows = Vec; + /// In the process of decoding zstd encoded data, there are several scenarios in which we process /// bits instead of bytes, for instance: /// - decoding FSE table @@ -444,7 +468,10 @@ impl BitstringTable { block_info_arr: &Vec, witness_rows: &[ZstdWitnessRow], n_enabled: usize, - ) -> Result<(), Error> { + ) -> Result { + let mut assigned_bitstring_table_rows: Vec = + Vec::with_capacity(n_enabled); + layouter.assign_region( || "Bitstring Table", |mut region| { @@ -454,20 +481,37 @@ impl BitstringTable { // assign fixed columns. for i in 0..n_enabled { let bit_index = i % (N_BYTES * N_BITS_PER_BYTE); - if bit_index == 0 { + let _assigned_q_start = if bit_index == 0 { region.assign_fixed( || "q_start", self.q_start, i, || Value::known(Fr::one()), - )?; - } - region.assign_fixed( + )? + } else { + region.assign_fixed( + || "q_start", + self.q_start, + i, + || Value::known(Fr::zero()), + )? + }; + let _assigned_bit_index = region.assign_fixed( || "bit_index", self.bit_index, i, || Value::known(Fr::from(bit_index as u64)), )?; + + #[cfg(feature = "soundness-tests")] + assigned_bitstring_table_rows.push(AssignedBitstringTableRow { + q_start: Some(_assigned_q_start), + bit_index: Some(_assigned_bit_index), + ..Default::default() + }); + + #[cfg(not(feature = "soundness-tests"))] + assigned_bitstring_table_rows.push(AssignedBitstringTableRow::default()); } let n_witness_rows = witness_rows.len(); @@ -591,37 +635,37 @@ impl BitstringTable { for (bit_idx, bit) in bits.into_iter().enumerate().take(N_BYTES * N_BITS_PER_BYTE) { - region.assign_advice( + let _assigned_byte_idx_1 = region.assign_advice( || "byte_idx_1", self.byte_idx_1, offset + bit_idx, || Value::known(Fr::from(byte_idx_1)), )?; - region.assign_advice( + let _assigned_byte_idx_2 = region.assign_advice( || "byte_idx_2", self.byte_idx_2, offset + bit_idx, || Value::known(Fr::from(byte_idx_2)), )?; - region.assign_advice( + let _assigned_byte_idx_3 = region.assign_advice( || "byte_idx_3", self.byte_idx_3, offset + bit_idx, || Value::known(Fr::from(byte_idx_3)), )?; - region.assign_advice( + let _assigned_byte_1 = region.assign_advice( || "byte_1", self.byte_1, offset + bit_idx, || Value::known(Fr::from(byte_1)), )?; - region.assign_advice( + let _assigned_byte_2 = region.assign_advice( || "byte_2", self.byte_2, offset + bit_idx, || Value::known(Fr::from(byte_2)), )?; - region.assign_advice( + let _assigned_byte_3 = region.assign_advice( || "byte_3", self.byte_3, offset + bit_idx, @@ -632,73 +676,125 @@ impl BitstringTable { acc = acc * 2 + (bit as u64); bitstring_len += 1; } - region.assign_advice( + let _assigned_bit_f = Fr::from(bit as u64); + let _assigned_bit = region.assign_advice( || "bit", self.bit.column, offset + bit_idx, || Value::known(Fr::from(bit as u64)), )?; - region.assign_advice( + let _assigned_bitstring_value = region.assign_advice( || "bitstring_value", self.bitstring_value, offset + bit_idx, || Value::known(Fr::from(curr_row.4)), )?; - region.assign_advice( + let _assigned_bitstring_value_acc = region.assign_advice( || "bitstring_value_acc", self.bitstring_value_acc, offset + bit_idx, || Value::known(Fr::from(acc)), )?; - region.assign_advice( + let _assigned_bitstring_len = region.assign_advice( || "bitstring_len", self.bitstring_len, offset + bit_idx, || Value::known(Fr::from(bitstring_len)), )?; - region.assign_advice( + let _assigned_from_start = region.assign_advice( || "from_start", self.from_start.column, offset + bit_idx, || Value::known(Fr::from((bit_idx <= (curr_row.3 as usize)) as u64)), )?; - region.assign_advice( + let _assigned_until_end = region.assign_advice( || "until_end", self.until_end.column, offset + bit_idx, || Value::known(Fr::from((bit_idx >= (curr_row.2 as usize)) as u64)), )?; - region.assign_advice( + let _assigned_is_reverse_f = Fr::from(curr_row.5); + let _assigned_is_reverse = region.assign_advice( || "is_reverse", self.is_reverse.column, offset + bit_idx, || Value::known(Fr::from(curr_row.5)), )?; + + #[cfg(feature = "soundness-tests")] + { + assigned_bitstring_table_rows[offset + bit_idx].byte_idx_1 = + Some(_assigned_byte_idx_1); + assigned_bitstring_table_rows[offset + bit_idx].byte_idx_2 = + Some(_assigned_byte_idx_2); + assigned_bitstring_table_rows[offset + bit_idx].byte_idx_3 = + Some(_assigned_byte_idx_3); + assigned_bitstring_table_rows[offset + bit_idx].byte_1 = + Some(_assigned_byte_1); + assigned_bitstring_table_rows[offset + bit_idx].byte_2 = + Some(_assigned_byte_2); + assigned_bitstring_table_rows[offset + bit_idx].byte_3 = + Some(_assigned_byte_3); + assigned_bitstring_table_rows[offset + bit_idx].bit_f = + Some(_assigned_bit_f); + assigned_bitstring_table_rows[offset + bit_idx].bit = + Some(_assigned_bit); + assigned_bitstring_table_rows[offset + bit_idx].bitstring_value = + Some(_assigned_bitstring_value); + assigned_bitstring_table_rows[offset + bit_idx].bitstring_value_acc = + Some(_assigned_bitstring_value_acc); + assigned_bitstring_table_rows[offset + bit_idx].bitstring_len = + Some(_assigned_bitstring_len); + assigned_bitstring_table_rows[offset + bit_idx].from_start = + Some(_assigned_from_start); + assigned_bitstring_table_rows[offset + bit_idx].until_end = + Some(_assigned_until_end); + assigned_bitstring_table_rows[offset + bit_idx].is_reverse = + Some(_assigned_is_reverse); + assigned_bitstring_table_rows[offset + bit_idx].is_reverse_f = + Some(_assigned_is_reverse_f); + } } offset += N_BYTES * N_BITS_PER_BYTE; } - for idx in 0..offset { - region.assign_advice( + for (idx, row) in assigned_bitstring_table_rows + .iter_mut() + .enumerate() + .take(offset) + { + let assigned_is_padding = region.assign_advice( || "is_padding", self.is_padding.column, idx, || Value::known(Fr::zero()), )?; + + row.is_padding = Some(assigned_is_padding); } - for idx in offset..n_enabled { - region.assign_advice( + + for (idx, row) in assigned_bitstring_table_rows + .iter_mut() + .enumerate() + .take(n_enabled) + .skip(offset) + { + let assigned_is_padding = region.assign_advice( || "is_padding", self.is_padding.column, idx, || Value::known(Fr::one()), )?; + + row.is_padding = Some(assigned_is_padding); } Ok(()) }, - ) + )?; + + Ok(assigned_bitstring_table_rows) } } diff --git a/aggregator/src/tests.rs b/aggregator/src/tests.rs index 3ed7a3a6ec..5df5117fb7 100644 --- a/aggregator/src/tests.rs +++ b/aggregator/src/tests.rs @@ -4,6 +4,9 @@ mod compression; mod mock_chunk; mod rlc; +#[cfg(feature = "soundness-tests")] +mod bitstring; + #[macro_export] macro_rules! layer_0 { // generate a snark for layer 0 diff --git a/aggregator/src/tests/bitstring.rs b/aggregator/src/tests/bitstring.rs new file mode 100644 index 0000000000..6cc556aa68 --- /dev/null +++ b/aggregator/src/tests/bitstring.rs @@ -0,0 +1,479 @@ +use crate::{ + aggregation::decoder::tables::BitstringTable, + witgen::{init_zstd_encoder, process, MultiBlockProcessResult, N_BLOCK_SIZE_TARGET}, +}; +use halo2_proofs::{ + circuit::{Layouter, SimpleFloorPlanner, Value}, + dev::{MockProver, VerifyFailure}, + halo2curves::bn256::Fr, + plonk::{Circuit, Column, ConstraintSystem, Error, Fixed}, +}; +use rand::Rng; +use std::io::Write; +use zkevm_circuits::table::RangeTable; + +#[derive(Clone)] +struct TestBitstringConfig { + /// Fixed column to mark all enabled rows. + q_enable: Column, + /// Range table for [0, 128kb). + range_block_len: RangeTable<{ N_BLOCK_SIZE_TARGET as usize }>, + /// Helper table for decoding bitstreams that span over 1 byte. + bitstring_table_1: BitstringTable<1>, + /// Helper table for decoding bitstreams that span over 2 bytes. + bitstring_table_2: BitstringTable<2>, + /// Helper table for decoding bitstreams that span over 3 bytes. + bitstring_table_3: BitstringTable<3>, +} + +impl TestBitstringConfig { + fn unusable_rows() -> usize { + 64 + } +} + +#[derive(Default)] +struct TestBitstringCircuit { + /// Degree for the test circuit, i.e. 1 << k number of rows. + k: u32, + /// Compressed bytes + compressed: Vec, + /// Variant of possible unsound case. + case: UnsoundCase, +} + +impl Circuit for TestBitstringCircuit { + type Config = TestBitstringConfig; + type FloorPlanner = SimpleFloorPlanner; + + fn without_witnesses(&self) -> Self { + Self::default() + } + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + let q_enable = meta.fixed_column(); + let range_block_len = RangeTable::construct(meta); + let bitstring_table_1 = BitstringTable::configure(meta, q_enable, range_block_len); + let bitstring_table_2 = BitstringTable::configure(meta, q_enable, range_block_len); + let bitstring_table_3 = BitstringTable::configure(meta, q_enable, range_block_len); + + Self::Config { + q_enable, + range_block_len, + bitstring_table_1, + bitstring_table_2, + bitstring_table_3, + } + } + + fn synthesize( + &self, + config: Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + let n_enabled = (1 << self.k) - Self::Config::unusable_rows(); + + let MultiBlockProcessResult { + witness_rows, + literal_bytes: _l, + fse_aux_tables: _f, + block_info_arr, + sequence_info_arr: _s, + address_table_rows: _a, + sequence_exec_results: _seq, + } = process(&self.compressed, Value::known(Fr::from(12345))); + + config.range_block_len.load(&mut layouter)?; + + let assigned_bitstring_table_1_rows = config.bitstring_table_1.assign( + &mut layouter, + &block_info_arr, + &witness_rows, + n_enabled, + )?; + let table_1_padding_start_offset = assigned_bitstring_table_1_rows + .iter() + .position(|row| row.bit.is_none()) + .expect("table within capacity"); + + let assigned_bitstring_table_2_rows = config.bitstring_table_2.assign( + &mut layouter, + &block_info_arr, + &witness_rows, + n_enabled, + )?; + let table_2_padding_start_offset = assigned_bitstring_table_2_rows + .iter() + .position(|row| row.bit.is_none()) + .expect("table within capacity"); + + let assigned_bitstring_table_3_rows = config.bitstring_table_3.assign( + &mut layouter, + &block_info_arr, + &witness_rows, + n_enabled, + )?; + let table_3_padding_start_offset = assigned_bitstring_table_3_rows + .iter() + .position(|row| row.bit.is_none()) + .expect("table within capacity"); + + let mut first_pass = halo2_base::SKIP_FIRST_PASS; + layouter.assign_region( + || "TestBitstringCircuit: potentially unsound assignments", + |mut region| { + if first_pass { + first_pass = false; + return Ok(()); + } + + for offset in 0..n_enabled { + region.assign_fixed( + || "q_enable", + config.q_enable, + offset, + || Value::known(Fr::one()), + )?; + } + + let mut rng = rand::thread_rng(); + + match self.case { + // sound case + UnsoundCase::None => {}, + + // bits are not the correct representation of byte_1/byte_2/byte_3 + UnsoundCase::IncorrectBitDecomposition => { + for (assigned_rows, padding_start_offset) in [ + (&assigned_bitstring_table_1_rows, table_1_padding_start_offset), + (&assigned_bitstring_table_2_rows, table_2_padding_start_offset), + (&assigned_bitstring_table_3_rows, table_3_padding_start_offset), + ] { + // Note: For some input data, especially for BitstringTable<3>, + // the assigned rows will be all padding as there were no 3-byte read operations performed during decoding. + if padding_start_offset > 0 { + let row_idx: usize = rng.gen_range(0..padding_start_offset); + let bit_cell = assigned_rows[row_idx].bit.clone().expect("cell is assigned"); + let bit_value = assigned_rows[row_idx].bit_f.expect("bit value exits"); + + region.assign_advice( + || "corrupt bit decomposition at a random location in the assigned witness", + bit_cell.cell().column.try_into().expect("assigned cell col is valid"), + bit_cell.cell().row_offset, + || if bit_value > Fr::zero() { + Value::known(Fr::zero()) + } else { + Value::known(Fr::one()) + }, + )?; + } + } + }, + + // bits are not the correct representation of byte_1/byte_2/byte_3 due to incorrect endianness (wrong is_reverse) + UnsoundCase::IncorrectBitDecompositionEndianness => { + for (assigned_rows, end_idx, padding_start_offset) in [ + (&assigned_bitstring_table_1_rows, 7, table_1_padding_start_offset), + (&assigned_bitstring_table_2_rows, 15, table_2_padding_start_offset), + (&assigned_bitstring_table_3_rows, 23, table_3_padding_start_offset), + ] { + if padding_start_offset > 0 { + // Sample the first index of a multi-byte operation + let row_idx = (rng.gen_range(0..(padding_start_offset/(end_idx + 1))) - 1) * (end_idx + 1); + let is_reverse = assigned_rows[row_idx].is_reverse.clone().expect("cell is assigned"); + let is_reverse_f = assigned_rows[row_idx].is_reverse_f.expect("is_reverse value exists"); + + for bit_idx in 0..end_idx { + region.assign_advice( + || "inject incorrect is_reverse to a read operation", + is_reverse.cell().column.try_into().expect("assigned cell col is valid"), + row_idx + bit_idx, + || if is_reverse_f > Fr::zero() { + Value::known(Fr::zero()) + } else { + Value::known(Fr::one()) + }, + )?; + } + } + } + }, + + // byte_idx_1/2/3 delta value is not boolean + UnsoundCase::IrregularTransitionByteIdx => { + for (assigned_rows, padding_start_offset) in [ + (&assigned_bitstring_table_1_rows, table_1_padding_start_offset), + (&assigned_bitstring_table_2_rows, table_2_padding_start_offset), + (&assigned_bitstring_table_3_rows, table_3_padding_start_offset), + ] { + if padding_start_offset > 0 { + let row_idx: usize = rng.gen_range(0..padding_start_offset); + let byte_idx_1_cell = assigned_rows[row_idx].byte_idx_1.clone().expect("cell is assigned"); + let _byte_idx_2_cell = assigned_rows[row_idx].byte_idx_2.clone().expect("cell is assigned"); + let byte_idx_3_cell = assigned_rows[row_idx].byte_idx_3.clone().expect("cell is assigned"); + + region.assign_advice( + || "corrupt byte_idx at a random location in the assigned witness (+1 for byte_idx_1)", + byte_idx_1_cell.cell().column.try_into().expect("assigned cell col is valid"), + byte_idx_1_cell.cell().row_offset, + || byte_idx_1_cell.value() + Value::known(Fr::one()), + )?; + region.assign_advice( + || "corrupt byte_idx at a random location in the assigned witness", + byte_idx_3_cell.cell().column.try_into().expect("assigned cell col is valid"), + byte_idx_3_cell.cell().row_offset, + || byte_idx_3_cell.value() + Value::known(Fr::one()), + )?; + } + } + }, + + // The boolean from_start does not start at bit_idx = 0 + UnsoundCase::IrregularValueFromStart => { + for (assigned_rows, end_idx, padding_start_offset) in [ + (&assigned_bitstring_table_1_rows, 7, table_1_padding_start_offset), + (&assigned_bitstring_table_2_rows, 15, table_2_padding_start_offset), + (&assigned_bitstring_table_3_rows, 23, table_3_padding_start_offset), + ] { + if padding_start_offset > 0 { + // Sample the first index of a multi-byte operation + let read_idx: usize = (rng.gen_range(0..(padding_start_offset/(end_idx + 1))) - 1) * (end_idx + 1); + let from_start = assigned_rows[read_idx].from_start.clone().expect("cell is assigned"); + + region.assign_advice( + || "Make from_start at bit_index=0 zero", + from_start.cell().column.try_into().expect("assigned cell col is valid"), + read_idx, + || Value::known(Fr::zero()), + )?; + } + } + }, + + // The boolean until_end does not end at bit_idx = 7/15/23 + UnsoundCase::IrregularValueUntilEnd => { + for (assigned_rows, end_idx, padding_start_offset) in [ + (&assigned_bitstring_table_1_rows, 7, table_1_padding_start_offset), + (&assigned_bitstring_table_2_rows, 15, table_2_padding_start_offset), + (&assigned_bitstring_table_3_rows, 23, table_3_padding_start_offset), + ] { + if padding_start_offset > 0 { + // Sample the first index of a multi-byte operation + let read_idx: usize = (rng.gen_range(0..(padding_start_offset/(end_idx + 1))) - 1) * (end_idx + 1); + let until_end = assigned_rows[read_idx + end_idx].until_end.clone().expect("cell is assigned"); + + region.assign_advice( + || "Make until_end at bit_index=end_index zero", + until_end.cell().column.try_into().expect("assigned cell col is valid"), + read_idx + end_idx, + || Value::known(Fr::zero()), + )?; + } + } + }, + + // The boolean from_start flips from 0 -> 1 + UnsoundCase::IrregularTransitionFromStart => { + for (assigned_rows, end_idx, padding_start_offset) in [ + (&assigned_bitstring_table_1_rows, 7, table_1_padding_start_offset), + (&assigned_bitstring_table_2_rows, 15, table_2_padding_start_offset), + (&assigned_bitstring_table_3_rows, 23, table_3_padding_start_offset), + ] { + if padding_start_offset > 0 { + // Sample the first index of a multi-byte operation + let read_idx: usize = (rng.gen_range(0..(padding_start_offset/(end_idx + 1))) - 1) * (end_idx + 1); + let from_start = assigned_rows[read_idx + end_idx].from_start.clone().expect("cell is assigned"); + + region.assign_advice( + || "Make from_start at bit_index=end_index one", + from_start.cell().column.try_into().expect("assigned cell col is valid"), + read_idx + end_idx, + || Value::known(Fr::one()), + )?; + } + } + }, + + // The boolean until_end flips from 1 -> 0 + UnsoundCase::IrregularTransitionUntilEnd => { + for (assigned_rows, end_idx, padding_start_offset) in [ + (&assigned_bitstring_table_1_rows, 7, table_1_padding_start_offset), + (&assigned_bitstring_table_2_rows, 15, table_2_padding_start_offset), + (&assigned_bitstring_table_3_rows, 23, table_3_padding_start_offset), + ] { + if padding_start_offset > 0 { + // Sample the first index of a multi-byte operation + let read_idx: usize = (rng.gen_range(0..(padding_start_offset/(end_idx + 1))) - 1) * (end_idx + 1); + let until_end = assigned_rows[read_idx + end_idx].until_end.clone().expect("cell is assigned"); + + region.assign_advice( + || "Make until_end at bit_index=end_index zero", + until_end.cell().column.try_into().expect("assigned cell col is valid"), + read_idx + end_idx, + || Value::known(Fr::zero()), + )?; + } + } + }, + + // The bitstring_value is not constant for a bitstring + UnsoundCase::InconsistentBitstringValue => { + for (assigned_rows, padding_start_offset) in [ + (&assigned_bitstring_table_1_rows, table_1_padding_start_offset), + (&assigned_bitstring_table_2_rows, table_2_padding_start_offset), + (&assigned_bitstring_table_3_rows, table_3_padding_start_offset), + ] { + if padding_start_offset > 0 { + let row_idx: usize = rng.gen_range(0..padding_start_offset); + let bitstring_value_cell = assigned_rows[row_idx].bitstring_value.clone().expect("cell is assigned"); + + region.assign_advice( + || "corrupt bitstring_value at a random location in the assigned witness", + bitstring_value_cell.cell().column.try_into().expect("assigned cell col is valid"), + bitstring_value_cell.cell().row_offset, + || bitstring_value_cell.value() + Value::known(Fr::one()), + )?; + } + } + }, + + // The bitstring_value and bitstring_value_acc do not agree at the last set bit + UnsoundCase::InconsistentEndBitstringAccValue => { + for (assigned_rows, end_idx, padding_start_offset) in [ + (&assigned_bitstring_table_1_rows, 7, table_1_padding_start_offset), + (&assigned_bitstring_table_2_rows, 15, table_2_padding_start_offset), + (&assigned_bitstring_table_3_rows, 23, table_3_padding_start_offset), + ] { + if padding_start_offset > 0 { + // Sample the first index of a multi-byte operation + let read_idx: usize = (rng.gen_range(0..(padding_start_offset/(end_idx + 1))) - 1) * (end_idx + 1); + let bitstring_value_acc = assigned_rows[read_idx + end_idx].bitstring_value_acc.clone().expect("cell is assigned"); + + region.assign_advice( + || "Make end bitstring_value_acc value different at bit_index=end_index", + bitstring_value_acc.cell().column.try_into().expect("assigned cell col is valid"), + read_idx + end_idx, + || bitstring_value_acc.value() + Value::known(Fr::one()), + )?; + } + } + }, + } + + Ok(()) + }, + ) + } +} + +enum UnsoundCase { + /// sound case. + None, + /// bits are not the correct representation of byte_1/byte_2/byte_3 + IncorrectBitDecomposition, + /// bits are not the correct representation of byte_1/byte_2/byte_3 due to incorrect endianness + /// (wrong is_reverse) + IncorrectBitDecompositionEndianness, + /// byte_idx_1/2/3 delta value is not boolean + IrregularTransitionByteIdx, + /// The boolean from_start does not start at bit_idx = 0 + IrregularValueFromStart, + /// The boolean until_end does not end at bit_idx = 7/15/23 + IrregularValueUntilEnd, + /// The boolean from_start flips from 0 -> 1 + IrregularTransitionFromStart, + /// The boolean until_end flips from 1 -> 0 + IrregularTransitionUntilEnd, + /// The bitstring_value is not constant for a bitstring + InconsistentBitstringValue, + /// The bitstring_value and bitstring_value_acc do not agree at the last set bit + InconsistentEndBitstringAccValue, +} + +impl Default for UnsoundCase { + fn default() -> Self { + Self::None + } +} + +fn run(case: UnsoundCase) -> Result<(), Vec> { + let k = 18; + + // Batch 127 + let raw = hex::decode("00000073f8718302d9848422551000827b0c94f565295eddcc0682bb16376c742e9bc9dbb32512880429d069189e01fd8083104ec3a02b10f9f3bbaa927b805b9b225f04d90a9994da49f309fb1e029312c661ffb68ea065de06a6d34dadf1af4f80d9133a67cf7753c925f5bfd785f56c20c11280ede0000000aef8ac10841c9c38008305d0a594ec53c830f4444a8a56455c6836b5d2aa794289aa80b844f2b9fdb8000000000000000000000000b6966083c7b68175b4bf77511608aee9a80d2ca4000000000000000000000000000000000000000000000000003d83508c36cdb583104ec4a0203dff6f72962bb8aa5a9bc365c705818ad2ae51485a8c831e453668d4b75d1fa03de15a7b705a8ad59f8437b4ca717f1e8094c77c5459ee57b0cae8b6c4ebdf5e000002d7f902d402841c9c38008302c4589480e38291e06339d10aab483c65695d004dbd5c69870334ae29914c90b902642cc4081e000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000001e9dd10000000000000000000000000000000000000000000000000000000065b3f7550000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000334ae29914c9000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000814a23b053fd0f102aeeda0459215c2444799c700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000530000000000000000000000000000000000000400000000000000000000000097af7be0b94399f9dd54a984e8498ce38356f0380000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000083104ec3a039a8970ba5ef7fb1cfe8d5db8e293b9837e298fd55faab853f56b66322c8ef80a0523ceb389a544389f6775b8ff982feac3f05b092869e35fed509e828d5e5759900000170f9016d03841c9c38008302a98f9418b71386418a9fca5ae7165e31c385a5130011b680b9010418cbafe5000000000000000000000000000000000000000000000000000000000091855b000000000000000000000000000000000000000000000000000e9f352b7fc38100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000006fd71e5088bdaaed42efd384fede02a76dca87f00000000000000000000000000000000000000000000000000000000065b3cd55000000000000000000000000000000000000000000000000000000000000000200000000000000000000000006efdbff2a14a7c8e15944d1f4a48f9f95f663a4000000000000000000000000530000000000000000000000000000000000000483104ec4a097411c6aad88135b8918b29d318898808fc04e933379c6d5da9f267315af2300a0265e6b38e475244e2639ea6eb42ae0d7f4f227a9dd9dd5328744bcd9f5f25bd2000000aff8ad82077a841c9c3800826716940a88bc5c32b684d467b43c06d9e0899efeaf59df86d12f0c4c832ab83e646174613a2c7b2270223a226c61796572322d3230222c226f70223a22636c61696d222c227469636b223a22244c32222c22616d74223a2231303030227d83104ec3a0bef600f17b5037519044f2296d1181abf140b986a7d43b7472e93b6be8378848a03c951790ad335a2d1947b3913e2280e506b17463c5f3b61849595a94b37439b3").expect("Decoding hex data should not fail"); + + let compressed = { + // compression level = 0 defaults to using level=3, which is zstd's default. + let mut encoder = init_zstd_encoder(None); + + // set source length, which will be reflected in the frame header. + encoder + .set_pledged_src_size(Some(raw.len() as u64)) + .expect("Encoder src_size: raw.len()"); + // include the content size to know at decode time the expected size of decoded data. + + encoder.write_all(&raw).expect("Encoder wirte_all"); + encoder.finish().expect("Encoder success") + }; + + let test_circuit = TestBitstringCircuit { + k, + compressed, + case, + }; + + let prover = + MockProver::run(k, &test_circuit, vec![]).expect("unexpected failure: MockProver::run"); + + prover.verify_par() +} + +#[test] +fn test_bitstring_ok() { + assert!(run(UnsoundCase::None).is_ok()) +} + +#[test] +fn test_incorrect_bit_decomposition() { + assert!(run(UnsoundCase::IncorrectBitDecomposition).is_err()) +} + +#[test] +fn test_incorrect_bit_decomposition_endianness() { + assert!(run(UnsoundCase::IncorrectBitDecompositionEndianness).is_err()) +} + +#[test] +fn test_irregular_transition_byte_idx() { + assert!(run(UnsoundCase::IrregularTransitionByteIdx).is_err()) +} + +#[test] +fn test_irregular_value_from_start() { + assert!(run(UnsoundCase::IrregularValueFromStart).is_err()) +} + +#[test] +fn test_irregular_value_until_end() { + assert!(run(UnsoundCase::IrregularValueUntilEnd).is_err()) +} + +#[test] +fn test_irregular_transition_from_start() { + assert!(run(UnsoundCase::IrregularTransitionFromStart).is_err()) +} + +#[test] +fn test_irregular_transition_until_end() { + assert!(run(UnsoundCase::IrregularTransitionUntilEnd).is_err()) +} + +#[test] +fn test_inconsistent_bitstring_value() { + assert!(run(UnsoundCase::InconsistentBitstringValue).is_err()) +} + +#[test] +fn test_inconsistent_end_bitstring_acc_value() { + assert!(run(UnsoundCase::InconsistentEndBitstringAccValue).is_err()) +} From 1b96dd7da7fd8a12ed970283fcc15e8a122c01c2 Mon Sep 17 00:00:00 2001 From: Ray Gao Date: Mon, 29 Jul 2024 20:42:14 -0400 Subject: [PATCH 3/5] Soundness Tests for `LiteralsHeaderTable` (#1319) * Add literals header soundness tests * fmt * Compilation Fix * Clippy * fmt * fmt * Use multiblock test vector for literal table soundness tests * Remove debug flags * Add feature flag * Debug tests * Fix feature flag test failure --------- Co-authored-by: Rohit Narurkar --- .../decoder/tables/literals_header.rs | 88 ++++- aggregator/src/tests.rs | 2 + aggregator/src/tests/literals_header.rs | 347 ++++++++++++++++++ 3 files changed, 420 insertions(+), 17 deletions(-) create mode 100644 aggregator/src/tests/literals_header.rs diff --git a/aggregator/src/aggregation/decoder/tables/literals_header.rs b/aggregator/src/aggregation/decoder/tables/literals_header.rs index 142d89c2e2..3ab6e27a2a 100644 --- a/aggregator/src/aggregation/decoder/tables/literals_header.rs +++ b/aggregator/src/aggregation/decoder/tables/literals_header.rs @@ -1,7 +1,7 @@ use eth_types::Field; use gadgets::util::{and, not, select, Expr}; use halo2_proofs::{ - circuit::{Layouter, Value}, + circuit::{AssignedCell, Layouter, Value}, halo2curves::bn256::Fr, plonk::{Advice, Any, Column, ConstraintSystem, Error, Fixed}, poly::Rotation, @@ -19,6 +19,25 @@ use crate::aggregation::{ util::BooleanAdvice, }; +#[derive(Default, Debug, Clone)] +pub struct AssignedLiteralsHeaderTableRow { + pub block_idx: Option>, + pub byte0: Option>, + pub byte1: Option>, + pub byte2: Option>, + pub size_format_bit0: Option>, + pub size_format_bit1: Option>, + pub byte0_rs_3: Option>, + pub byte0_rs_4: Option>, + pub regen_size: Option>, + pub is_padding: Option>, +} + +pub(crate) type AssignedLiteralsHeaderTableRows = ( + Vec>, + Vec>, +); + /// Helper table to decode the regenerated size from the Literals Header. #[derive(Clone, Debug)] pub struct LiteralsHeaderTable { @@ -200,7 +219,11 @@ impl LiteralsHeaderTable { layouter: &mut impl Layouter, literals_headers: Vec<(u64, u64, (u64, u64, u64))>, n_enabled: usize, - ) -> Result<(), Error> { + ) -> Result, Error> { + let mut assigned_literals_header_table_rows: Vec> = + vec![]; + let mut assigned_padding_cells: Vec> = vec![]; + layouter.assign_region( || "LiteralsHeaderTable", |mut region| { @@ -234,7 +257,9 @@ impl LiteralsHeaderTable { let regen_size = le_bits_to_value(&sizing_bits[0..n_bits_regen]); - for (col, value, annotation) in [ + let mut assigned_table_row = AssignedLiteralsHeaderTableRow::default(); + + for (idx, (col, value, annotation)) in [ (self.block_idx, block_idx, "block_idx"), (self.byte0, byte0, "byte0"), (self.byte1, byte1, "byte1"), @@ -252,28 +277,57 @@ impl LiteralsHeaderTable { ), (self.byte0_rs_3, byte0 >> 3, "byte0_rs_3"), (self.byte0_rs_4, byte0 >> 4, "byte0_rs_4"), - ] { - region.assign_advice( - || annotation, - col, - offset, - || Value::known(F::from(value)), - )?; + (self.is_padding.column, 0, "is_padding"), + ] + .into_iter() + .enumerate() + { + let _assigned_cell = region + .assign_advice( + || annotation, + col, + offset, + || Value::known(F::from(value)), + ) + .expect("failed witness assignment"); + + #[cfg(feature = "soundness-tests")] + match idx { + 0 => assigned_table_row.block_idx = Some(_assigned_cell), + 1 => assigned_table_row.byte0 = Some(_assigned_cell), + 2 => assigned_table_row.byte1 = Some(_assigned_cell), + 3 => assigned_table_row.byte2 = Some(_assigned_cell), + 4 => assigned_table_row.regen_size = Some(_assigned_cell), + 5 => assigned_table_row.size_format_bit0 = Some(_assigned_cell), + 6 => assigned_table_row.size_format_bit1 = Some(_assigned_cell), + 7 => assigned_table_row.byte0_rs_3 = Some(_assigned_cell), + 8 => assigned_table_row.byte0_rs_4 = Some(_assigned_cell), + 9 => assigned_table_row.is_padding = Some(_assigned_cell), + _ => {} + } } + + assigned_literals_header_table_rows.push(assigned_table_row); } for offset in literals_headers.len()..n_enabled { - region.assign_advice( - || "is_padding", - self.is_padding.column, - offset, - || Value::known(F::one()), - )?; + assigned_padding_cells.push( + region + .assign_advice( + || "is_padding", + self.is_padding.column, + offset, + || Value::known(F::one()), + ) + .expect("failed witness assignment"), + ); } Ok(()) }, - ) + )?; + + Ok((assigned_literals_header_table_rows, assigned_padding_cells)) } } diff --git a/aggregator/src/tests.rs b/aggregator/src/tests.rs index 5df5117fb7..df097acdc2 100644 --- a/aggregator/src/tests.rs +++ b/aggregator/src/tests.rs @@ -6,6 +6,8 @@ mod rlc; #[cfg(feature = "soundness-tests")] mod bitstring; +#[cfg(feature = "soundness-tests")] +mod literals_header; #[macro_export] macro_rules! layer_0 { diff --git a/aggregator/src/tests/literals_header.rs b/aggregator/src/tests/literals_header.rs new file mode 100644 index 0000000000..b175bc8c8f --- /dev/null +++ b/aggregator/src/tests/literals_header.rs @@ -0,0 +1,347 @@ +use crate::aggregation::{ + decoder::tables::LiteralsHeaderTable, + witgen::{init_zstd_encoder, process, MultiBlockProcessResult, ZstdTag, ZstdWitnessRow}, +}; +use halo2_proofs::{ + circuit::{Layouter, SimpleFloorPlanner, Value}, + dev::{MockProver, VerifyFailure}, + halo2curves::bn256::Fr, + plonk::{Circuit, Column, ConstraintSystem, Error, Fixed}, +}; +use rand::{self, Rng}; +use std::{fs, io::Write}; +use zkevm_circuits::table::RangeTable; + +#[derive(Clone)] +struct TestLiteralsHeaderConfig { + /// Fixed column to mark all enabled rows. + q_enable: Column, + /// Range Table for [0, 8). + range8: RangeTable<8>, + /// Range Table for [0, 16). + range16: RangeTable<16>, + /// Helper table for decoding the regenerated size from LiteralsHeader. + literals_header_table: LiteralsHeaderTable, +} + +impl TestLiteralsHeaderConfig { + fn unusable_rows() -> usize { + 64 + } +} + +#[derive(Default)] +struct TestLiteralsHeaderCircuit { + /// Degree for the test circuit, i.e. 1 << k number of rows. + k: u32, + /// Compressed bytes + compressed: Vec, + /// Variant of possible unsound case. + case: UnsoundCase, +} + +impl Circuit for TestLiteralsHeaderCircuit { + type Config = TestLiteralsHeaderConfig; + type FloorPlanner = SimpleFloorPlanner; + + fn without_witnesses(&self) -> Self { + Self::default() + } + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + let q_enable = meta.fixed_column(); + + // Helper tables + let range8 = RangeTable::construct(meta); + let range16 = RangeTable::construct(meta); + let literals_header_table = LiteralsHeaderTable::configure(meta, q_enable, range8, range16); + + Self::Config { + q_enable, + range8, + range16, + literals_header_table, + } + } + + fn synthesize( + &self, + config: Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + let n_enabled = (1 << self.k) - Self::Config::unusable_rows(); + + let MultiBlockProcessResult { + witness_rows, + literal_bytes: _l, + fse_aux_tables: _f, + block_info_arr: _b, + sequence_info_arr: _s, + address_table_rows: _a, + sequence_exec_results: _seq, + } = process(&self.compressed, Value::known(Fr::from(12345))); + + // Load auxiliary tables + config.range8.load(&mut layouter)?; + config.range16.load(&mut layouter)?; + + ///////////////////////////////////////// + ///// Assign LiteralHeaderTable //////// + ///////////////////////////////////////// + let mut literal_headers: Vec<(u64, u64, (u64, u64, u64))> = vec![]; // (block_idx, byte_offset, (byte0, byte1, byte2)) + let literal_header_rows = witness_rows + .iter() + .filter(|r| r.state.tag == ZstdTag::ZstdBlockLiteralsHeader) + .cloned() + .collect::>>(); + let max_block_idx = witness_rows + .iter() + .last() + .expect("Last row of witness exists.") + .state + .block_idx; + for curr_block_idx in 1..=max_block_idx { + let byte_idx = literal_header_rows + .iter() + .find(|r| r.state.block_idx == curr_block_idx) + .unwrap() + .encoded_data + .byte_idx; + + let literal_bytes = literal_header_rows + .iter() + .filter(|&r| r.state.block_idx == curr_block_idx) + .map(|r| r.encoded_data.value_byte as u64) + .collect::>(); + + literal_headers.push(( + curr_block_idx, + byte_idx, + ( + literal_bytes[0], + if literal_bytes.len() > 1 { + literal_bytes[1] + } else { + 0 + }, + if literal_bytes.len() > 2 { + literal_bytes[2] + } else { + 0 + }, + ), + )); + } + + #[cfg(feature = "soundness-tests")] + let (assigned_literals_header_table_rows, assigned_padding_cells) = config + .literals_header_table + .assign(&mut layouter, literal_headers, n_enabled)?; + + #[cfg(not(feature = "soundness-tests"))] + let _ = config + .literals_header_table + .assign(&mut layouter, literal_headers, n_enabled)?; + + // Modify assigned witness values + let mut first_pass = halo2_base::SKIP_FIRST_PASS; + layouter.assign_region( + || "TestLiteralsHeaderCircuit: potentially unsound assignments", + |mut region| { + if first_pass { + first_pass = false; + return Ok(()); + } + + for offset in 0..n_enabled { + region.assign_fixed( + || "q_enable", + config.q_enable, + offset, + || Value::known(Fr::one()), + )?; + } + + let mut rng = rand::thread_rng(); + + #[cfg(feature = "soundness-tests")] + match self.case { + // sound case + UnsoundCase::None => {} + + // First block index is not 1 + UnsoundCase::IncorrectInitialBlockIdx => { + let block_idx_cell = assigned_literals_header_table_rows[0] + .clone() + .block_idx + .expect("cell is assigned") + .cell(); + let _modified_cell = region.assign_advice( + || "Change the first block index value", + block_idx_cell + .column + .try_into() + .expect("assigned cell col is valid"), + block_idx_cell.row_offset, + || Value::known(Fr::from(2)), + )?; + } + + // Block index should increment by 1 with each valid row + UnsoundCase::IncorrectBlockIdxTransition => { + let row_idx: usize = + rng.gen_range(0..assigned_literals_header_table_rows.len()); + let block_idx_cell = assigned_literals_header_table_rows[row_idx] + .clone() + .block_idx + .expect("cell is assigned"); + let _modified_cell = region.assign_advice( + || "Corrupt the block index value at a random location", + block_idx_cell + .cell() + .column + .try_into() + .expect("assigned cell col is valid"), + block_idx_cell.cell().row_offset, + || block_idx_cell.value() + Value::known(Fr::one()), + )?; + } + + // Padding indicator transitions from 1 -> 0 + UnsoundCase::IrregularPaddingTransition => { + let row_idx: usize = rng.gen_range(0..assigned_padding_cells.len()); + let is_padding_cell = assigned_padding_cells[row_idx].clone(); + + let _modified_cell = region.assign_advice( + || "Flip is_padding value in the padding section", + is_padding_cell + .cell() + .column + .try_into() + .expect("assigned cell col is valid"), + is_padding_cell.cell().row_offset, + || Value::known(Fr::zero()), + )?; + } + + // Regen size is not calculated correctly + UnsoundCase::IncorrectRegenSize => { + let row_idx: usize = + rng.gen_range(0..assigned_literals_header_table_rows.len()); + let regen_size_cell = assigned_literals_header_table_rows[row_idx] + .clone() + .regen_size + .expect("cell is assigned"); + + let _modified_cell = region.assign_advice( + || "Invalidate the regen_size value at a random location", + regen_size_cell + .cell() + .column + .try_into() + .expect("assigned cell col is valid"), + regen_size_cell.cell().row_offset, + || regen_size_cell.value() + Value::known(Fr::one()), + )?; + } + } + + Ok(()) + }, + ) + } +} + +enum UnsoundCase { + /// sound case. + None, + /// First block index is not 1 + IncorrectInitialBlockIdx, + /// Block index should increment by 1 with each valid row + IncorrectBlockIdxTransition, + /// Padding indicator transitions from 1 -> 0 + IrregularPaddingTransition, + /// Regen size is not calculated correctly + IncorrectRegenSize, +} + +impl Default for UnsoundCase { + fn default() -> Self { + Self::None + } +} + +fn run(case: UnsoundCase) -> Result<(), Vec> { + let mut batch_files = fs::read_dir("./data/test_blobs/multi") + .expect("batch data dir exits") + .map(|entry| entry.map(|e| e.path())) + .collect::, std::io::Error>>() + .expect("batch files handle successfully"); + batch_files.sort(); + + let mut multi_batch_data = Vec::with_capacity(500_000); + + for batch_file in batch_files { + let batch_data = fs::read(batch_file).expect("batch file reads successfully"); + multi_batch_data.extend_from_slice(&batch_data); + } + + let encoded_multi_batch_data = { + // compression level = 0 defaults to using level=3, which is zstd's default. + let mut encoder = init_zstd_encoder(None); + + // set source length, which will be reflected in the frame header. + encoder + .set_pledged_src_size(Some(multi_batch_data.len() as u64)) + .expect("Encoder src_size: raw.len()"); + + encoder + .write_all(&multi_batch_data) + .expect("Encoder wirte_all"); + encoder.finish().expect("Encoder success") + }; + + println!("len(multi_batch_data)={:?}", multi_batch_data.len()); + println!( + "len(encoded_multi_batch_data)={:?}", + encoded_multi_batch_data.len() + ); + + let k = 18; + + let test_circuit = TestLiteralsHeaderCircuit { + k, + compressed: encoded_multi_batch_data, + case, + }; + + let prover = + MockProver::run(k, &test_circuit, vec![]).expect("unexpected failure: MockProver::run"); + + prover.verify_par() +} + +#[test] +fn test_literals_header_ok() { + assert!(run(UnsoundCase::None).is_ok()) +} + +#[test] +fn test_incorrect_initial_block_idx() { + assert!(run(UnsoundCase::IncorrectInitialBlockIdx).is_err()) +} + +#[test] +fn test_incorrect_block_idx_transition() { + assert!(run(UnsoundCase::IncorrectBlockIdxTransition).is_err()) +} + +#[test] +fn test_irregular_padding_transition() { + assert!(run(UnsoundCase::IrregularPaddingTransition).is_err()) +} + +#[test] +fn test_incorrect_regen_size() { + assert!(run(UnsoundCase::IncorrectRegenSize).is_err()) +} From f5ace2c87ce73379d9e3cd20eda198cfdb085b6e Mon Sep 17 00:00:00 2001 From: Rohit Narurkar Date: Thu, 29 Aug 2024 16:00:08 +0100 Subject: [PATCH 4/5] chore(fix): typos --- .../src/aggregation/decoder/seq_exec.rs | 77 ++++++++++--------- aggregator/src/tests/bitstring.rs | 2 +- aggregator/src/tests/literals_header.rs | 2 +- 3 files changed, 41 insertions(+), 40 deletions(-) diff --git a/aggregator/src/aggregation/decoder/seq_exec.rs b/aggregator/src/aggregation/decoder/seq_exec.rs index 6cc6f3b147..60549d3b9b 100644 --- a/aggregator/src/aggregation/decoder/seq_exec.rs +++ b/aggregator/src/aggregation/decoder/seq_exec.rs @@ -1590,56 +1590,57 @@ mod tests { assert_eq!(output, Vec::from("abcddeabcdeabbfgggg".as_bytes())); // try to put the final phase into previous one - let mut mis_phase_blk1 = base_trace_blk1.clone(); - assert_eq!(mis_phase_blk1.exec_trace.len(), 7); - assert_eq!(mis_phase_blk1.exec_trace[6].0, 4); - assert_eq!(mis_phase_blk1.exec_trace[5].0, 3); - mis_phase_blk1.exec_trace[6].0 = 3; - mis_phase_blk1.mocks = vec![ + let mut mismatch_phase_blk1 = base_trace_blk1.clone(); + assert_eq!(mismatch_phase_blk1.exec_trace.len(), 7); + assert_eq!(mismatch_phase_blk1.exec_trace[6].0, 4); + assert_eq!(mismatch_phase_blk1.exec_trace[5].0, 3); + mismatch_phase_blk1.exec_trace[6].0 = 3; + mismatch_phase_blk1.mocks = vec![ (14, MockEntry::Index([None, Some(Fr::from(3u64))])), (14, MockEntry::Phase([Some(false), None, None])), ]; - let circuit_mis_phase_1 = SeqExecMockCircuit { - traces: vec![mis_phase_blk1, base_trace_blk2.clone()], + let circuit_mismatch_phase_1 = SeqExecMockCircuit { + traces: vec![mismatch_phase_blk1, base_trace_blk2.clone()], output: output.clone(), ..Default::default() }; // try to make last cp phase cross instruction - let mut mis_phase_blk2 = base_trace_blk1.clone(); - mis_phase_blk2.mocks = vec![(13, MockEntry::Phase([Some(true), None, None]))]; - let circuit_mis_phase_2 = SeqExecMockCircuit { - traces: vec![mis_phase_blk2, base_trace_blk2.clone()], + let mut mismatch_phase_blk2 = base_trace_blk1.clone(); + mismatch_phase_blk2.mocks = vec![(13, MockEntry::Phase([Some(true), None, None]))]; + let circuit_mismatch_phase_2 = SeqExecMockCircuit { + traces: vec![mismatch_phase_blk2, base_trace_blk2.clone()], output: output.clone(), ..Default::default() }; // try to a phase both lit-cp and backref - let mut mis_phase_blk3 = base_trace_blk1.clone(); - mis_phase_blk3.mocks = vec![(13, MockEntry::Phase([Some(false), Some(true), Some(true)]))]; - let circuit_mis_phase_3 = SeqExecMockCircuit { - traces: vec![mis_phase_blk3, base_trace_blk2.clone()], + let mut mismatch_phase_blk3 = base_trace_blk1.clone(); + mismatch_phase_blk3.mocks = + vec![(13, MockEntry::Phase([Some(false), Some(true), Some(true)]))]; + let circuit_mismatch_phase_3 = SeqExecMockCircuit { + traces: vec![mismatch_phase_blk3, base_trace_blk2.clone()], output: output.clone(), ..Default::default() }; // detect phase must work in a normal row - let mut mis_phase_blk4 = base_trace_blk2.clone(); - mis_phase_blk4.mocks = vec![ + let mut mismatch_phase_blk4 = base_trace_blk2.clone(); + mismatch_phase_blk4.mocks = vec![ (3, MockEntry::Phase([Some(false), Some(false), Some(false)])), (3, MockEntry::Decode([Some(Fr::from(18)), Some(Fr::zero())])), ]; - let circuit_mis_phase_4 = SeqExecMockCircuit { - traces: vec![base_trace_blk1.clone(), mis_phase_blk4], + let circuit_mismatch_phase_4 = SeqExecMockCircuit { + traces: vec![base_trace_blk1.clone(), mismatch_phase_blk4], output: Vec::from("abcddeabcdeabbfggg".as_bytes()), all_padding_mocks: vec![MockEntry::Decode([Some(Fr::from(18)), None])], ..Default::default() }; // detect out of order phases - let mut mis_phase_blk5 = base_trace_blk2.clone(); - mis_phase_blk5.mocks = vec![ + let mut mismatch_phase_blk5 = base_trace_blk2.clone(); + mismatch_phase_blk5.mocks = vec![ (0, MockEntry::Decode([None, Some(Fr::from(0x66))])), //the decoded byte become 'f' ( 0, @@ -1656,19 +1657,19 @@ mod tests { (0, MockEntry::Phase([None, Some(false), Some(true)])), (2, MockEntry::Phase([None, Some(true), Some(false)])), ]; - let circuit_mis_phase_5 = SeqExecMockCircuit { - traces: vec![base_trace_blk1.clone(), mis_phase_blk5], + let circuit_mismatch_phase_5 = SeqExecMockCircuit { + traces: vec![base_trace_blk1.clone(), mismatch_phase_blk5], output: output.clone(), ..Default::default() }; let k = 12; for circuit in [ - circuit_mis_phase_1, - circuit_mis_phase_2, - circuit_mis_phase_3, - circuit_mis_phase_4, - circuit_mis_phase_5, + circuit_mismatch_phase_1, + circuit_mismatch_phase_2, + circuit_mismatch_phase_3, + circuit_mismatch_phase_4, + circuit_mismatch_phase_5, ] { let mock_prover = MockProver::::run(k, &circuit, vec![]).expect("failed to run mock prover"); @@ -1710,29 +1711,29 @@ mod tests { assert_eq!(output_mis, Vec::from("abcddeabcdeabbbf".as_bytes())); - let mut mis_inst_blk = base_trace_blk.clone(); - let tr = &mut mis_inst_blk.exec_trace[5].1; + let mut mismatch_inst_blk = base_trace_blk.clone(); + let tr = &mut mismatch_inst_blk.exec_trace[5].1; assert_eq!(*tr, SequenceExecInfo::BackRef(12..13)); // build the mis-match len to 2 *tr = SequenceExecInfo::BackRef(12..14); - let circuit_mis_inst_1 = SeqExecMockCircuit { - traces: vec![mis_inst_blk], + let circuit_mismatch_inst_1 = SeqExecMockCircuit { + traces: vec![mismatch_inst_blk], output: output_mis, ..Default::default() }; - let mut mis_inst_blk = base_trace_blk.clone(); - let tr = &mut mis_inst_blk.exec_trace[5].1; + let mut mismatch_inst_blk = base_trace_blk.clone(); + let tr = &mut mismatch_inst_blk.exec_trace[5].1; // build the mis-match offset to 2 *tr = SequenceExecInfo::BackRef(11..12); - let circuit_mis_inst_2 = SeqExecMockCircuit { - traces: vec![mis_inst_blk], + let circuit_mismatch_inst_2 = SeqExecMockCircuit { + traces: vec![mismatch_inst_blk], output: Vec::from("abcddeabcdeabaf".as_bytes()), ..Default::default() }; let k = 12; - for circuit in [circuit_mis_inst_1, circuit_mis_inst_2] { + for circuit in [circuit_mismatch_inst_1, circuit_mismatch_inst_2] { let mock_prover = MockProver::::run(k, &circuit, vec![]).expect("failed to run mock prover"); let ret = mock_prover.verify(); diff --git a/aggregator/src/tests/bitstring.rs b/aggregator/src/tests/bitstring.rs index 6cc556aa68..37cd88df08 100644 --- a/aggregator/src/tests/bitstring.rs +++ b/aggregator/src/tests/bitstring.rs @@ -412,7 +412,7 @@ fn run(case: UnsoundCase) -> Result<(), Vec> { .expect("Encoder src_size: raw.len()"); // include the content size to know at decode time the expected size of decoded data. - encoder.write_all(&raw).expect("Encoder wirte_all"); + encoder.write_all(&raw).expect("Encoder write_all"); encoder.finish().expect("Encoder success") }; diff --git a/aggregator/src/tests/literals_header.rs b/aggregator/src/tests/literals_header.rs index b175bc8c8f..d7d7e1b3aa 100644 --- a/aggregator/src/tests/literals_header.rs +++ b/aggregator/src/tests/literals_header.rs @@ -297,7 +297,7 @@ fn run(case: UnsoundCase) -> Result<(), Vec> { encoder .write_all(&multi_batch_data) - .expect("Encoder wirte_all"); + .expect("Encoder write_all"); encoder.finish().expect("Encoder success") }; From cba066b64d649cbb3c5f521a0716eea81823ce17 Mon Sep 17 00:00:00 2001 From: Rohit Narurkar Date: Thu, 29 Aug 2024 16:04:45 +0100 Subject: [PATCH 5/5] chore(fix): part 2 typos --- aggregator/src/aggregation/decoder/seq_exec.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/aggregator/src/aggregation/decoder/seq_exec.rs b/aggregator/src/aggregation/decoder/seq_exec.rs index 60549d3b9b..543ec6e92e 100644 --- a/aggregator/src/aggregation/decoder/seq_exec.rs +++ b/aggregator/src/aggregation/decoder/seq_exec.rs @@ -1696,7 +1696,7 @@ mod tests { assert_eq!(output, Vec::from("abcddeabcdeabbf".as_bytes())); - let mut output_mis = Vec::new(); + let mut output_mismatch = Vec::new(); SeqExecMock::mock_generate( 1, Vec::from("abcdef".as_bytes()), @@ -1706,25 +1706,25 @@ mod tests { [3, 0, 4, 5, 6, 1], [4, 0, 2, 1, 5, 6], ]), - &mut output_mis, + &mut output_mismatch, ); - assert_eq!(output_mis, Vec::from("abcddeabcdeabbbf".as_bytes())); + assert_eq!(output_mismatch, Vec::from("abcddeabcdeabbbf".as_bytes())); let mut mismatch_inst_blk = base_trace_blk.clone(); let tr = &mut mismatch_inst_blk.exec_trace[5].1; assert_eq!(*tr, SequenceExecInfo::BackRef(12..13)); - // build the mis-match len to 2 + // build the mismatch len to 2 *tr = SequenceExecInfo::BackRef(12..14); let circuit_mismatch_inst_1 = SeqExecMockCircuit { traces: vec![mismatch_inst_blk], - output: output_mis, + output: output_mismatch, ..Default::default() }; let mut mismatch_inst_blk = base_trace_blk.clone(); let tr = &mut mismatch_inst_blk.exec_trace[5].1; - // build the mis-match offset to 2 + // build the mismatch offset to 2 *tr = SequenceExecInfo::BackRef(11..12); let circuit_mismatch_inst_2 = SeqExecMockCircuit { traces: vec![mismatch_inst_blk],