nova_scotia/circom/
file.rs

1// some codes borrowed from https://github.com/poma/zkutil/blob/master/src/r1cs_reader.rs
2#![allow(unused_variables, dead_code)]
3
4use crate::circom::circuit::Constraint;
5use byteorder::{LittleEndian, ReadBytesExt};
6use ff::PrimeField;
7use hex_literal::hex;
8use nova_snark::traits::Group;
9use std::{
10    collections::HashMap,
11    io::{Error, ErrorKind, Read, Result, Seek, SeekFrom},
12};
13
14// R1CSFile's header
15#[derive(Debug, Default)]
16pub struct Header {
17    pub field_size: u32,
18    pub prime_size: Vec<u8>,
19    pub n_wires: u32,
20    pub n_pub_out: u32,
21    pub n_pub_in: u32,
22    pub n_prv_in: u32,
23    pub n_labels: u64,
24    pub n_constraints: u32,
25}
26
27// R1CSFile parse result
28#[derive(Debug, Default)]
29pub struct R1CSFile<Fr: PrimeField> {
30    pub version: u32,
31    pub header: Header,
32    pub constraints: Vec<Constraint<Fr>>,
33    pub wire_mapping: Vec<u64>,
34}
35
36pub(crate) fn read_field<R: Read, Fr: PrimeField>(mut reader: R) -> Result<Fr> {
37    let mut repr = Fr::ZERO.to_repr();
38    for digit in repr.as_mut().iter_mut() {
39        // TODO: may need to reverse order?
40        *digit = reader.read_u8()?;
41    }
42    let fr = Fr::from_repr(repr).unwrap();
43    Ok(fr)
44}
45
46fn read_header<R: Read>(mut reader: R, size: u64) -> Result<Header> {
47    let field_size = reader.read_u32::<LittleEndian>()?;
48    let mut prime_size = vec![0u8; field_size as usize];
49    reader.read_exact(&mut prime_size)?;
50    if size != 32 + field_size as u64 {
51        return Err(Error::new(
52            ErrorKind::InvalidData,
53            "Invalid header section size",
54        ));
55    }
56
57    Ok(Header {
58        field_size,
59        prime_size,
60        n_wires: reader.read_u32::<LittleEndian>()?,
61        n_pub_out: reader.read_u32::<LittleEndian>()?,
62        n_pub_in: reader.read_u32::<LittleEndian>()?,
63        n_prv_in: reader.read_u32::<LittleEndian>()?,
64        n_labels: reader.read_u64::<LittleEndian>()?,
65        n_constraints: reader.read_u32::<LittleEndian>()?,
66    })
67}
68
69fn read_constraint_vec<R: Read, Fr: PrimeField>(
70    mut reader: R,
71    header: &Header,
72) -> Result<Vec<(usize, Fr)>> {
73    let n_vec = reader.read_u32::<LittleEndian>()? as usize;
74    let mut vec = Vec::with_capacity(n_vec);
75    for _ in 0..n_vec {
76        vec.push((
77            reader.read_u32::<LittleEndian>()? as usize,
78            read_field::<&mut R, Fr>(&mut reader)?,
79        ));
80    }
81    Ok(vec)
82}
83
84fn read_constraints<R: Read, Fr: PrimeField>(
85    mut reader: R,
86    size: u64,
87    header: &Header,
88) -> Result<Vec<Constraint<Fr>>> {
89    // todo check section size
90    let mut vec = Vec::with_capacity(header.n_constraints as usize);
91    for _ in 0..header.n_constraints {
92        vec.push((
93            read_constraint_vec::<&mut R, Fr>(&mut reader, header)?,
94            read_constraint_vec::<&mut R, Fr>(&mut reader, header)?,
95            read_constraint_vec::<&mut R, Fr>(&mut reader, header)?,
96        ));
97    }
98    Ok(vec)
99}
100
101fn read_map<R: Read>(mut reader: R, size: u64, header: &Header) -> Result<Vec<u64>> {
102    if size != header.n_wires as u64 * 8 {
103        return Err(Error::new(
104            ErrorKind::InvalidData,
105            "Invalid map section size",
106        ));
107    }
108    let mut vec = Vec::with_capacity(header.n_wires as usize);
109    for _ in 0..header.n_wires {
110        vec.push(reader.read_u64::<LittleEndian>()?);
111    }
112    if vec[0] != 0 {
113        return Err(Error::new(
114            ErrorKind::InvalidData,
115            "Wire 0 should always be mapped to 0",
116        ));
117    }
118    Ok(vec)
119}
120
121pub fn from_reader<R: Read + Seek, G1, G2>(mut reader: R) -> Result<R1CSFile<<G1 as Group>::Scalar>>
122where
123    G1: Group<Base = <G2 as Group>::Scalar>,
124    G2: Group<Base = <G1 as Group>::Scalar>,
125{
126    let mut magic = [0u8; 4];
127    reader.read_exact(&mut magic)?;
128    if magic != [0x72, 0x31, 0x63, 0x73] {
129        // magic = "r1cs"
130        return Err(Error::new(ErrorKind::InvalidData, "Invalid magic number"));
131    }
132
133    let version = reader.read_u32::<LittleEndian>()?;
134    if version != 1 {
135        return Err(Error::new(ErrorKind::InvalidData, "Unsupported version"));
136    }
137
138    let num_sections = reader.read_u32::<LittleEndian>()?;
139
140    // section type -> file offset
141    let mut section_offsets = HashMap::<u32, u64>::new();
142    let mut section_sizes = HashMap::<u32, u64>::new();
143
144    // get file offset of each section
145    for _ in 0..num_sections {
146        let section_type = reader.read_u32::<LittleEndian>()?;
147        let section_size = reader.read_u64::<LittleEndian>()?;
148        let offset = reader.seek(SeekFrom::Current(0))?;
149        section_offsets.insert(section_type, offset);
150        section_sizes.insert(section_type, section_size);
151        reader.seek(SeekFrom::Current(section_size as i64))?;
152    }
153
154    let header_type = 1;
155    let constraint_type = 2;
156    let wire2label_type = 3;
157
158    reader.seek(SeekFrom::Start(*section_offsets.get(&header_type).unwrap()))?;
159    let header = read_header(&mut reader, *section_sizes.get(&header_type).unwrap())?;
160    if header.field_size != 32 {
161        return Err(Error::new(
162            ErrorKind::InvalidData,
163            "This parser only supports 32-byte fields",
164        ));
165    }
166
167    // println!("header: {:?}", header);
168    // if header.prime_size != hex!("010000f093f5e1439170b97948e833285d588181b64550b829a031e1724e6430")
169    // {
170    //     return Err(Error::new(
171    //         ErrorKind::InvalidData,
172    //         "This parser only supports bn256",
173    //     ));
174    // }
175
176    reader.seek(SeekFrom::Start(
177        *section_offsets.get(&constraint_type).unwrap(),
178    ))?;
179    let constraints = read_constraints::<&mut R, <G1 as Group>::Scalar>(
180        &mut reader,
181        *section_sizes.get(&constraint_type).unwrap(),
182        &header,
183    )?;
184
185    reader.seek(SeekFrom::Start(
186        *section_offsets.get(&wire2label_type).unwrap(),
187    ))?;
188    let wire_mapping = read_map(
189        &mut reader,
190        *section_sizes.get(&wire2label_type).unwrap(),
191        &header,
192    )?;
193
194    Ok(R1CSFile {
195        version,
196        header,
197        constraints,
198        wire_mapping,
199    })
200}
201
202mod tests {
203    #[test]
204    fn sample() {
205        use super::*;
206        use hex_literal::hex;
207        use std::io::{BufReader, Cursor};
208
209        let data = hex!(
210            "
211        72316373
212        01000000
213        03000000
214        01000000 40000000 00000000
215        20000000
216        010000f0 93f5e143 9170b979 48e83328 5d588181 b64550b8 29a031e1 724e6430
217        07000000
218        01000000
219        02000000
220        03000000
221        e8030000 00000000
222        03000000
223        02000000 88020000 00000000
224        02000000
225        05000000 03000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
226        06000000 08000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
227        03000000
228        00000000 02000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
229        02000000 14000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
230        03000000 0C000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
231        02000000
232        00000000 05000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
233        02000000 07000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
234        03000000
235        01000000 04000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
236        04000000 08000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
237        05000000 03000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
238        02000000
239        03000000 2C000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
240        06000000 06000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
241        00000000
242        01000000
243        06000000 04000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
244        03000000
245        00000000 06000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
246        02000000 0B000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
247        03000000 05000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
248        01000000
249        06000000 58020000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
250        03000000 38000000 00000000
251        00000000 00000000
252        03000000 00000000
253        0a000000 00000000
254        0b000000 00000000
255        0c000000 00000000
256        0f000000 00000000
257        44010000 00000000
258    "
259        );
260
261        type G1 = pasta_curves::pallas::Point;
262        type G2 = pasta_curves::vesta::Point;
263
264        let reader = BufReader::new(Cursor::new(&data[..]));
265        let file = from_reader::<_, G1, G2>(reader).unwrap();
266        assert_eq!(file.version, 1);
267
268        assert_eq!(file.header.field_size, 32);
269        assert_eq!(
270            file.header.prime_size,
271            &hex!("010000f093f5e1439170b97948e833285d588181b64550b829a031e1724e6430")
272        );
273        assert_eq!(file.header.n_wires, 7);
274        assert_eq!(file.header.n_pub_out, 1);
275        assert_eq!(file.header.n_pub_in, 2);
276        assert_eq!(file.header.n_prv_in, 3);
277        assert_eq!(file.header.n_labels, 0x03e8);
278        assert_eq!(file.header.n_constraints, 3);
279
280        assert_eq!(file.constraints.len(), 3);
281        assert_eq!(file.constraints[0].0.len(), 2);
282        assert_eq!(file.constraints[0].0[0].0, 5);
283        // assert_eq!(file.constraints[0].0[0].1, ff::from_hex("0x03").unwrap());
284        assert_eq!(file.constraints[2].1[0].0, 0);
285        // assert_eq!(file.constraints[2].1[0].1, ff::from_hex("0x06").unwrap());
286        assert_eq!(file.constraints[1].2.len(), 0);
287
288        assert_eq!(file.wire_mapping.len(), 7);
289        assert_eq!(file.wire_mapping[1], 3);
290    }
291
292    #[test]
293    fn test_reader_size_fail() {
294        use super::*;
295
296        // fn read_header<R: Read>(mut reader: R, size: u64) -> Result<Header>
297        let mut buf: Vec<u8> = 32_u32.to_le_bytes().to_vec();
298        buf.resize(4 + 32, 0);
299        let err = read_header(&mut buf.as_slice(), 32).err().unwrap();
300        assert_eq!(err.kind(), ErrorKind::InvalidData)
301    }
302}