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