diff --git a/snark-verifier-sdk/src/multi_batch.rs b/snark-verifier-sdk/src/multi_batch.rs index c37c812e..2d428096 100644 --- a/snark-verifier-sdk/src/multi_batch.rs +++ b/snark-verifier-sdk/src/multi_batch.rs @@ -7,6 +7,11 @@ mod evm; mod halo2; +mod hash; + +#[cfg(test)] +mod tests; pub use evm::*; pub use halo2::*; +pub(crate) use hash::*; diff --git a/snark-verifier-sdk/src/multi_batch/hash.rs b/snark-verifier-sdk/src/multi_batch/hash.rs new file mode 100644 index 00000000..62a6a213 --- /dev/null +++ b/snark-verifier-sdk/src/multi_batch/hash.rs @@ -0,0 +1,73 @@ +//! This module implements the sha2 circuit that glues together all the instances +//! +//! Each instance is a Vec> which will be serialized via the following: +//! - Prefix: +//! - num_ins: number of instance vectors; 8 bytes +//! - for each j in num_ins, the number of Fr elements in the instance vector: 8 bytes each +//! - actual data: +//! - serialized Fr Elements +//! + +use halo2_base::halo2_proofs::halo2curves::bn256::Fr; + +/// Serialize an instance from the snark +pub(crate) fn serialize_instances(instance: &[Vec]) -> Vec { + let mut buf: Vec = vec![]; + + // write the prefix + let num_ins = instance.len(); + buf.extend_from_slice(num_ins.to_le_bytes().as_ref()); + for instance_column in instance.iter() { + buf.extend_from_slice(instance_column.len().to_le_bytes().as_ref()); + } + + // write the actual data + for instance_column in instance.iter() { + for element in instance_column.iter() { + buf.extend_from_slice(element.to_bytes().as_slice()) + } + } + + buf +} + +/// Deserialize an instance for the snark +pub(crate) fn deserialize_instances(data: &[u8]) -> Vec> { + // the input data is at least 8 bytes + assert!(data.len() >= 8); + + let mut res = vec![]; + let mut res_len = vec![]; + let num_ins = + usize::from_le_bytes(data[0..8].try_into().expect("input data has incorrect length")); + + for i in 0..num_ins { + res_len.push(usize::from_le_bytes( + data[8 * (i + 1)..8 * (i + 2)].try_into().expect("input data has incorrect length"), + )); + } + let total_fr_elements: usize = res_len.iter().sum(); + let pre_fix_len = num_ins * 8 + 8; + assert_eq!( + data.len(), + total_fr_elements * 32 + num_ins * 8 + 8, + "input data has incorrect length" + ); + let mut ctr = 0; + for i in 0..num_ins { + let mut cur_column = vec![]; + for _ in 0..res_len[i] { + cur_column.push( + Fr::from_bytes( + data[pre_fix_len + 32 * ctr..pre_fix_len + 32 * ctr + 32] + .try_into() + .expect("input data has incorrect length"), + ) + .unwrap(), + ); + ctr += 1; + } + res.push(cur_column) + } + res +} diff --git a/snark-verifier-sdk/src/multi_batch/tests.rs b/snark-verifier-sdk/src/multi_batch/tests.rs new file mode 100644 index 00000000..18a04f1a --- /dev/null +++ b/snark-verifier-sdk/src/multi_batch/tests.rs @@ -0,0 +1,34 @@ +use ark_std::test_rng; +use halo2_base::halo2_proofs::halo2curves::bn256::Fr; +use itertools::Itertools; +use rand::Rng; +use zkevm_circuits::tx_circuit::GroupField; + +use super::hash::{deserialize_instances, serialize_instances}; + +#[test] +fn test_instance_serialization() { + let max_row = 100; + let max_col = 100; + let mut rng = test_rng(); + + for _ in 0..100 { + let instance = random_instance(&mut rng, max_row, max_col); + let serialized = serialize_instances(&instance); + let instance_rec = deserialize_instances(&serialized); + assert_eq!(instance, instance_rec) + } +} + +fn random_instance(rng: &mut R, max_row: usize, max_col: usize) -> Vec> { + let num_cols = rng.next_u32() as usize % max_row + 1; + let mut res = vec![]; + + for _ in 0..num_cols { + let col_size = rng.next_u32() as usize % max_col + 1; + let cur_col = (0..col_size).map(|_| Fr::random(&mut *rng)).collect_vec(); + res.push(cur_col); + } + + res +}