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}