arnaucube
arnaucube/circom-compatPublic Self-hosted repository
circom-compat/src/circom/r1cs_reader.rs
src/circom/r1cs_reader.rsRaw
  1//! R1CS circom file reader
  2//! Copied from <https://github.com/poma/zkutil>
  3//! Spec: <https://github.com/iden3/r1csfile/blob/master/doc/r1cs_bin_format.md>
  4use byteorder::{LittleEndian, ReadBytesExt};
  5use std::io::{Error, ErrorKind};
  6
  7use ark_ff::PrimeField;
  8use ark_serialize::{SerializationError, SerializationError::IoError};
  9use ark_std::io::{Read, Seek, SeekFrom};
 10
 11use std::collections::HashMap;
 12
 13type IoResult<T> = Result<T, SerializationError>;
 14
 15use super::{ConstraintVec, Constraints};
 16
 17#[derive(Clone, Debug)]
 18pub struct R1CS<F: PrimeField> {
 19    pub num_inputs: usize,
 20    pub num_aux: usize,
 21    pub num_variables: usize,
 22    pub constraints: Vec<Constraints<F>>,
 23    pub wire_mapping: Option<Vec<usize>>,
 24}
 25
 26impl<F: PrimeField> From<R1CSFile<F>> for R1CS<F> {
 27    fn from(file: R1CSFile<F>) -> Self {
 28        let num_inputs = (1 + file.header.n_pub_in + file.header.n_pub_out) as usize;
 29        let num_variables = file.header.n_wires as usize;
 30        let num_aux = num_variables - num_inputs;
 31        R1CS {
 32            num_aux,
 33            num_inputs,
 34            num_variables,
 35            constraints: file.constraints,
 36            wire_mapping: Some(file.wire_mapping.iter().map(|e| *e as usize).collect()),
 37        }
 38    }
 39}
 40
 41pub struct R1CSFile<F: PrimeField> {
 42    pub version: u32,
 43    pub header: Header,
 44    pub constraints: Vec<Constraints<F>>,
 45    pub wire_mapping: Vec<u64>,
 46}
 47
 48impl<F: PrimeField> R1CSFile<F> {
 49    /// reader must implement the Seek trait, for example with a Cursor
 50    ///
 51    /// ```rust,ignore
 52    /// let reader = BufReader::new(Cursor::new(&data[..]));
 53    /// ```
 54    pub fn new<R: Read + Seek>(mut reader: R) -> IoResult<R1CSFile<F>> {
 55        let mut magic = [0u8; 4];
 56        reader.read_exact(&mut magic)?;
 57        if magic != [0x72, 0x31, 0x63, 0x73] {
 58            return Err(IoError(Error::new(
 59                ErrorKind::InvalidData,
 60                "Invalid magic number",
 61            )));
 62        }
 63
 64        let version = reader.read_u32::<LittleEndian>()?;
 65        if version != 1 {
 66            return Err(IoError(Error::new(
 67                ErrorKind::InvalidData,
 68                "Unsupported version",
 69            )));
 70        }
 71
 72        let num_sections = reader.read_u32::<LittleEndian>()?;
 73
 74        // todo: handle sec_size correctly
 75        // section type -> file offset
 76        let mut sec_offsets = HashMap::<u32, u64>::new();
 77        let mut sec_sizes = HashMap::<u32, u64>::new();
 78
 79        // get file offset of each section
 80        for _ in 0..num_sections {
 81            let sec_type = reader.read_u32::<LittleEndian>()?;
 82            let sec_size = reader.read_u64::<LittleEndian>()?;
 83            let offset = reader.stream_position()?;
 84            sec_offsets.insert(sec_type, offset);
 85            sec_sizes.insert(sec_type, sec_size);
 86            reader.seek(SeekFrom::Current(sec_size as i64))?;
 87        }
 88
 89        let header_type = 1;
 90        let constraint_type = 2;
 91        let wire2label_type = 3;
 92
 93        let header_offset = sec_offsets.get(&header_type).ok_or_else(|| {
 94            Error::new(
 95                ErrorKind::InvalidData,
 96                "No section offset for header type found",
 97            )
 98        });
 99
100        reader.seek(SeekFrom::Start(*header_offset?))?;
101
102        let header_size = sec_sizes.get(&header_type).ok_or_else(|| {
103            Error::new(
104                ErrorKind::InvalidData,
105                "No section size for header type found",
106            )
107        });
108
109        let header = Header::new(&mut reader, *header_size?)?;
110
111        let constraint_offset = sec_offsets.get(&constraint_type).ok_or_else(|| {
112            Error::new(
113                ErrorKind::InvalidData,
114                "No section offset for constraint type found",
115            )
116        });
117
118        reader.seek(SeekFrom::Start(*constraint_offset?))?;
119
120        let constraints = read_constraints::<&mut R, F>(&mut reader, &header)?;
121
122        let wire2label_offset = sec_offsets.get(&wire2label_type).ok_or_else(|| {
123            Error::new(
124                ErrorKind::InvalidData,
125                "No section offset for wire2label type found",
126            )
127        });
128
129        reader.seek(SeekFrom::Start(*wire2label_offset?))?;
130
131        let wire2label_size = sec_sizes.get(&wire2label_type).ok_or_else(|| {
132            Error::new(
133                ErrorKind::InvalidData,
134                "No section size for wire2label type found",
135            )
136        });
137
138        let wire_mapping = read_map(&mut reader, *wire2label_size?, &header)?;
139
140        Ok(R1CSFile {
141            version,
142            header,
143            constraints,
144            wire_mapping,
145        })
146    }
147}
148
149pub struct Header {
150    pub field_size: u32,
151    pub prime_size: Vec<u8>,
152    pub n_wires: u32,
153    pub n_pub_out: u32,
154    pub n_pub_in: u32,
155    pub n_prv_in: u32,
156    pub n_labels: u64,
157    pub n_constraints: u32,
158}
159
160impl Header {
161    fn new<R: Read>(mut reader: R, size: u64) -> IoResult<Header> {
162        let field_size = reader.read_u32::<LittleEndian>()?;
163        if field_size != 32 {
164            return Err(IoError(Error::new(
165                ErrorKind::InvalidData,
166                "This parser only supports 32-byte fields",
167            )));
168        }
169
170        if size != 32 + field_size as u64 {
171            return Err(IoError(Error::new(
172                ErrorKind::InvalidData,
173                "Invalid header section size",
174            )));
175        }
176
177        let mut prime_size = vec![0u8; field_size as usize];
178        reader.read_exact(&mut prime_size)?;
179
180        // TODO WIP
181        // if prime_size
182        //     != hex::decode("010000f093f5e1439170b97948e833285d588181b64550b829a031e1724e6430")
183        //         .unwrap()
184        // {
185        //     return Err(IoError(Error::new(
186        //         ErrorKind::InvalidData,
187        //         "This parser only supports bn256",
188        //     )));
189        // }
190
191        Ok(Header {
192            field_size,
193            prime_size,
194            n_wires: reader.read_u32::<LittleEndian>()?,
195            n_pub_out: reader.read_u32::<LittleEndian>()?,
196            n_pub_in: reader.read_u32::<LittleEndian>()?,
197            n_prv_in: reader.read_u32::<LittleEndian>()?,
198            n_labels: reader.read_u64::<LittleEndian>()?,
199            n_constraints: reader.read_u32::<LittleEndian>()?,
200        })
201    }
202}
203
204fn read_constraint_vec<R: Read, F: PrimeField>(mut reader: R) -> IoResult<ConstraintVec<F>> {
205    let n_vec = reader.read_u32::<LittleEndian>()? as usize;
206    let mut vec = Vec::with_capacity(n_vec);
207    for _ in 0..n_vec {
208        vec.push((
209            reader.read_u32::<LittleEndian>()? as usize,
210            F::deserialize_uncompressed(&mut reader)?,
211        ));
212    }
213    Ok(vec)
214}
215
216fn read_constraints<R: Read, F: PrimeField>(
217    mut reader: R,
218    header: &Header,
219) -> IoResult<Vec<Constraints<F>>> {
220    // todo check section size
221    let mut vec = Vec::with_capacity(header.n_constraints as usize);
222    for _ in 0..header.n_constraints {
223        vec.push((
224            read_constraint_vec::<&mut R, F>(&mut reader)?,
225            read_constraint_vec::<&mut R, F>(&mut reader)?,
226            read_constraint_vec::<&mut R, F>(&mut reader)?,
227        ));
228    }
229    Ok(vec)
230}
231
232fn read_map<R: Read>(mut reader: R, size: u64, header: &Header) -> IoResult<Vec<u64>> {
233    if size != header.n_wires as u64 * 8 {
234        return Err(IoError(Error::new(
235            ErrorKind::InvalidData,
236            "Invalid map section size",
237        )));
238    }
239    let mut vec = Vec::with_capacity(header.n_wires as usize);
240    for _ in 0..header.n_wires {
241        vec.push(reader.read_u64::<LittleEndian>()?);
242    }
243    if vec[0] != 0 {
244        return Err(IoError(Error::new(
245            ErrorKind::InvalidData,
246            "Wire 0 should always be mapped to 0",
247        )));
248    }
249    Ok(vec)
250}
251
252#[cfg(test)]
253mod tests {
254    use super::*;
255    use ark_bn254::Fr;
256    use ark_std::io::{BufReader, Cursor};
257
258    #[test]
259    fn sample() {
260        let data = hex_literal::hex!(
261            "
262        72316373
263        01000000
264        03000000
265        01000000 40000000 00000000
266        20000000
267        010000f0 93f5e143 9170b979 48e83328 5d588181 b64550b8 29a031e1 724e6430
268        07000000
269        01000000
270        02000000
271        03000000
272        e8030000 00000000
273        03000000
274        02000000 88020000 00000000
275        02000000
276        05000000 03000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
277        06000000 08000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
278        03000000
279        00000000 02000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
280        02000000 14000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
281        03000000 0C000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
282        02000000
283        00000000 05000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
284        02000000 07000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
285        03000000
286        01000000 04000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
287        04000000 08000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
288        05000000 03000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
289        02000000
290        03000000 2C000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
291        06000000 06000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
292        00000000
293        01000000
294        06000000 04000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
295        03000000
296        00000000 06000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
297        02000000 0B000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
298        03000000 05000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
299        01000000
300        06000000 58020000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
301        03000000 38000000 00000000
302        00000000 00000000
303        03000000 00000000
304        0a000000 00000000
305        0b000000 00000000
306        0c000000 00000000
307        0f000000 00000000
308        44010000 00000000
309    "
310        );
311
312        let reader = BufReader::new(Cursor::new(&data[..]));
313        let file = R1CSFile::<Fr>::new(reader).unwrap();
314        assert_eq!(file.version, 1);
315
316        assert_eq!(file.header.field_size, 32);
317        assert_eq!(
318            file.header.prime_size,
319            hex::decode("010000f093f5e1439170b97948e833285d588181b64550b829a031e1724e6430")
320                .unwrap(),
321        );
322        assert_eq!(file.header.n_wires, 7);
323        assert_eq!(file.header.n_pub_out, 1);
324        assert_eq!(file.header.n_pub_in, 2);
325        assert_eq!(file.header.n_prv_in, 3);
326        assert_eq!(file.header.n_labels, 0x03e8);
327        assert_eq!(file.header.n_constraints, 3);
328
329        assert_eq!(file.constraints.len(), 3);
330        assert_eq!(file.constraints[0].0.len(), 2);
331        assert_eq!(file.constraints[0].0[0].0, 5);
332        assert_eq!(file.constraints[0].0[0].1, Fr::from(3));
333        assert_eq!(file.constraints[2].1[0].0, 0);
334        assert_eq!(file.constraints[2].1[0].1, Fr::from(6));
335        assert_eq!(file.constraints[1].2.len(), 0);
336
337        assert_eq!(file.wire_mapping.len(), 7);
338        assert_eq!(file.wire_mapping[1], 3);
339    }
340}