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