Skip to main content

ark_circom/circom/
r1cs_reader.rs

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 ark_ff::PrimeField;
5use byteorder::{LittleEndian, ReadBytesExt};
6use std::io::{Error, ErrorKind};
7
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> {
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        if prime_size
181            != hex::decode("010000f093f5e1439170b97948e833285d588181b64550b829a031e1724e6430")
182                .unwrap()
183        {
184            return Err(IoError(Error::new(
185                ErrorKind::InvalidData,
186                "This parser only supports bn256",
187            )));
188        }
189
190        Ok(Header {
191            field_size,
192            prime_size,
193            n_wires: reader.read_u32::<LittleEndian>()?,
194            n_pub_out: reader.read_u32::<LittleEndian>()?,
195            n_pub_in: reader.read_u32::<LittleEndian>()?,
196            n_prv_in: reader.read_u32::<LittleEndian>()?,
197            n_labels: reader.read_u64::<LittleEndian>()?,
198            n_constraints: reader.read_u32::<LittleEndian>()?,
199        })
200    }
201}
202
203fn read_constraint_vec<R: Read, F: PrimeField>(mut reader: R) -> IoResult<ConstraintVec<F>> {
204    let n_vec = reader.read_u32::<LittleEndian>()? as usize;
205    let mut vec = Vec::with_capacity(n_vec);
206    for _ in 0..n_vec {
207        vec.push((
208            reader.read_u32::<LittleEndian>()? as usize,
209            F::deserialize_uncompressed(&mut reader)?,
210        ));
211    }
212    Ok(vec)
213}
214
215fn read_constraints<R: Read, F: PrimeField>(
216    mut reader: R,
217    header: &Header,
218) -> IoResult<Vec<Constraints<F>>> {
219    // todo check section size
220    let mut vec = Vec::with_capacity(header.n_constraints as usize);
221    for _ in 0..header.n_constraints {
222        vec.push((
223            read_constraint_vec::<&mut R, F>(&mut reader)?,
224            read_constraint_vec::<&mut R, F>(&mut reader)?,
225            read_constraint_vec::<&mut R, F>(&mut reader)?,
226        ));
227    }
228    Ok(vec)
229}
230
231fn read_map<R: Read>(mut reader: R, size: u64, header: &Header) -> IoResult<Vec<u64>> {
232    if size != header.n_wires as u64 * 8 {
233        return Err(IoError(Error::new(
234            ErrorKind::InvalidData,
235            "Invalid map section size",
236        )));
237    }
238    let mut vec = Vec::with_capacity(header.n_wires as usize);
239    for _ in 0..header.n_wires {
240        vec.push(reader.read_u64::<LittleEndian>()?);
241    }
242    if vec[0] != 0 {
243        return Err(IoError(Error::new(
244            ErrorKind::InvalidData,
245            "Wire 0 should always be mapped to 0",
246        )));
247    }
248    Ok(vec)
249}
250
251#[cfg(test)]
252mod tests {
253    use super::*;
254    use ark_bn254::Fr;
255    use ark_std::io::{BufReader, Cursor};
256
257    #[test]
258    fn sample() {
259        let data = hex_literal::hex!(
260            "
261        72316373
262        01000000
263        03000000
264        01000000 40000000 00000000
265        20000000
266        010000f0 93f5e143 9170b979 48e83328 5d588181 b64550b8 29a031e1 724e6430
267        07000000
268        01000000
269        02000000
270        03000000
271        e8030000 00000000
272        03000000
273        02000000 88020000 00000000
274        02000000
275        05000000 03000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
276        06000000 08000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
277        03000000
278        00000000 02000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
279        02000000 14000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
280        03000000 0C000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
281        02000000
282        00000000 05000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
283        02000000 07000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
284        03000000
285        01000000 04000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
286        04000000 08000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
287        05000000 03000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
288        02000000
289        03000000 2C000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
290        06000000 06000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
291        00000000
292        01000000
293        06000000 04000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
294        03000000
295        00000000 06000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
296        02000000 0B000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
297        03000000 05000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
298        01000000
299        06000000 58020000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
300        03000000 38000000 00000000
301        00000000 00000000
302        03000000 00000000
303        0a000000 00000000
304        0b000000 00000000
305        0c000000 00000000
306        0f000000 00000000
307        44010000 00000000
308    "
309        );
310
311        let reader = BufReader::new(Cursor::new(&data[..]));
312        let file = R1CSFile::<Fr>::new(reader).unwrap();
313        assert_eq!(file.version, 1);
314
315        assert_eq!(file.header.field_size, 32);
316        assert_eq!(
317            file.header.prime_size,
318            hex::decode("010000f093f5e1439170b97948e833285d588181b64550b829a031e1724e6430")
319                .unwrap(),
320        );
321        assert_eq!(file.header.n_wires, 7);
322        assert_eq!(file.header.n_pub_out, 1);
323        assert_eq!(file.header.n_pub_in, 2);
324        assert_eq!(file.header.n_prv_in, 3);
325        assert_eq!(file.header.n_labels, 0x03e8);
326        assert_eq!(file.header.n_constraints, 3);
327
328        assert_eq!(file.constraints.len(), 3);
329        assert_eq!(file.constraints[0].0.len(), 2);
330        assert_eq!(file.constraints[0].0[0].0, 5);
331        assert_eq!(file.constraints[0].0[0].1, Fr::from(3));
332        assert_eq!(file.constraints[2].1[0].0, 0);
333        assert_eq!(file.constraints[2].1[0].1, Fr::from(6));
334        assert_eq!(file.constraints[1].2.len(), 0);
335
336        assert_eq!(file.wire_mapping.len(), 7);
337        assert_eq!(file.wire_mapping[1], 3);
338    }
339}