Skip to content

Commit 0e16bdf

Browse files
rubdosoleganza
authored andcommitted
Implements serialization of R1CS (#243)
* Implements serialization of R1CS * Add serialization roundtrip test for a R1CS proof. This commit splits up the gadget helper in a prover and a verifier, such that it can be re-used in a serialization test. Fixes #214
1 parent be13f15 commit 0e16bdf

File tree

3 files changed

+222
-32
lines changed

3 files changed

+222
-32
lines changed

src/errors.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ pub enum R1CSError {
9797
/// Occurs when there are insufficient generators for the proof.
9898
#[fail(display = "Invalid generators size, too few generators for proof")]
9999
InvalidGeneratorsLength,
100+
/// This error occurs when the proof encoding is malformed.
101+
#[fail(display = "Proof data could not be parsed.")]
102+
FormatError,
100103
/// Occurs when verification of an
101104
/// [`R1CSProof`](::r1cs::R1CSProof) fails.
102105
#[fail(display = "R1CSProof did not verify correctly.")]

src/r1cs/proof.rs

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
1+
#![allow(non_snake_case)]
12
//! Definition of the proof struct.
23
34
use curve25519_dalek::ristretto::CompressedRistretto;
45
use curve25519_dalek::scalar::Scalar;
56

7+
use errors::R1CSError;
68
use inner_product_proof::InnerProductProof;
9+
use util;
10+
11+
use serde::de::Visitor;
12+
use serde::{self, Deserialize, Deserializer, Serialize, Serializer};
713

814
/// A proof of some statement specified by a
915
/// [`ConstraintSystem`](::r1cs::ConstraintSystem).
@@ -56,3 +62,135 @@ pub struct R1CSProof {
5662
/// Proof data for the inner-product argument.
5763
pub(super) ipp_proof: InnerProductProof,
5864
}
65+
66+
impl R1CSProof {
67+
/// Serializes the proof into a byte array of \\(16 + 2k\\) 32-byte elements,
68+
/// where \\(k=\lceil \log_2(n) \rceil\\) and \\(n\\) is the number of multiplication gates.
69+
///
70+
/// # Layout
71+
///
72+
/// The layout of the r1cs proof encoding is:
73+
///
74+
/// * eleven compressed Ristretto points \\(A_{I1},A_{O1},S_1,A_{I2},A_{O2},S_2,T_1,...,T_6\\),
75+
/// * three scalars \\(t_x, \tilde{t}_x, \tilde{e}\\),
76+
/// * \\(k\\) pairs of compressed Ristretto points \\(L_0,R_0\dots,L_{k-1},R_{k-1}\\),
77+
/// * two scalars \\(a, b\\).
78+
pub fn to_bytes(&self) -> Vec<u8> {
79+
let mut buf = Vec::with_capacity(self.serialized_size());
80+
buf.extend_from_slice(self.A_I1.as_bytes());
81+
buf.extend_from_slice(self.A_O1.as_bytes());
82+
buf.extend_from_slice(self.S1.as_bytes());
83+
buf.extend_from_slice(self.A_I2.as_bytes());
84+
buf.extend_from_slice(self.A_O2.as_bytes());
85+
buf.extend_from_slice(self.S2.as_bytes());
86+
buf.extend_from_slice(self.T_1.as_bytes());
87+
buf.extend_from_slice(self.T_3.as_bytes());
88+
buf.extend_from_slice(self.T_4.as_bytes());
89+
buf.extend_from_slice(self.T_5.as_bytes());
90+
buf.extend_from_slice(self.T_6.as_bytes());
91+
buf.extend_from_slice(self.t_x.as_bytes());
92+
buf.extend_from_slice(self.t_x_blinding.as_bytes());
93+
buf.extend_from_slice(self.e_blinding.as_bytes());
94+
// XXX this costs an extra alloc
95+
buf.extend_from_slice(self.ipp_proof.to_bytes().as_slice());
96+
buf
97+
}
98+
99+
/// Returns the size in bytes required to serialize the `R1CSProof`.
100+
pub fn serialized_size(&self) -> usize {
101+
// 14 elements + the ipp
102+
14 * 32 + self.ipp_proof.serialized_size()
103+
}
104+
105+
/// Deserializes the proof from a byte slice.
106+
///
107+
/// Returns an error if the byte slice cannot be parsed into a `R1CSProof`.
108+
pub fn from_bytes(mut slice: &[u8]) -> Result<R1CSProof, R1CSError> {
109+
if slice.len() % 32 != 0 {
110+
return Err(R1CSError::FormatError);
111+
}
112+
if slice.len() < 14 * 32 {
113+
return Err(R1CSError::FormatError);
114+
}
115+
116+
// This macro takes care of counting bytes in the slice
117+
macro_rules! read32 {
118+
() => {{
119+
let tmp = util::read32(slice);
120+
slice = &slice[32..];
121+
tmp
122+
}};
123+
}
124+
125+
let A_I1 = CompressedRistretto(read32!());
126+
let A_O1 = CompressedRistretto(read32!());
127+
let S1 = CompressedRistretto(read32!());
128+
let A_I2 = CompressedRistretto(read32!());
129+
let A_O2 = CompressedRistretto(read32!());
130+
let S2 = CompressedRistretto(read32!());
131+
let T_1 = CompressedRistretto(read32!());
132+
let T_3 = CompressedRistretto(read32!());
133+
let T_4 = CompressedRistretto(read32!());
134+
let T_5 = CompressedRistretto(read32!());
135+
let T_6 = CompressedRistretto(read32!());
136+
let t_x = Scalar::from_canonical_bytes(read32!()).ok_or(R1CSError::FormatError)?;
137+
let t_x_blinding = Scalar::from_canonical_bytes(read32!()).ok_or(R1CSError::FormatError)?;
138+
let e_blinding = Scalar::from_canonical_bytes(read32!()).ok_or(R1CSError::FormatError)?;
139+
140+
// XXX: IPPProof from_bytes gives ProofError.
141+
let ipp_proof = InnerProductProof::from_bytes(slice).map_err(|_| R1CSError::FormatError)?;
142+
143+
Ok(R1CSProof {
144+
A_I1,
145+
A_O1,
146+
S1,
147+
A_I2,
148+
A_O2,
149+
S2,
150+
T_1,
151+
T_3,
152+
T_4,
153+
T_5,
154+
T_6,
155+
t_x,
156+
t_x_blinding,
157+
e_blinding,
158+
ipp_proof,
159+
})
160+
}
161+
}
162+
163+
impl Serialize for R1CSProof {
164+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
165+
where
166+
S: Serializer,
167+
{
168+
serializer.serialize_bytes(&self.to_bytes()[..])
169+
}
170+
}
171+
172+
impl<'de> Deserialize<'de> for R1CSProof {
173+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
174+
where
175+
D: Deserializer<'de>,
176+
{
177+
struct R1CSProofVisitor;
178+
179+
impl<'de> Visitor<'de> for R1CSProofVisitor {
180+
type Value = R1CSProof;
181+
182+
fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
183+
formatter.write_str("a valid R1CSProof")
184+
}
185+
186+
fn visit_bytes<E>(self, v: &[u8]) -> Result<R1CSProof, E>
187+
where
188+
E: serde::de::Error,
189+
{
190+
R1CSProof::from_bytes(v).map_err(serde::de::Error::custom)
191+
}
192+
}
193+
194+
deserializer.deserialize_bytes(R1CSProofVisitor)
195+
}
196+
}

tests/r1cs.rs

Lines changed: 81 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -233,50 +233,53 @@ fn example_gadget<CS: ConstraintSystem>(
233233
cs.constrain(c1 + c2 - c_var);
234234
}
235235

236-
fn example_gadget_roundtrip_helper(
236+
// Prover's scope
237+
fn example_gadget_proof(
238+
pc_gens: &PedersenGens,
239+
bp_gens: &BulletproofGens,
237240
a1: u64,
238241
a2: u64,
239242
b1: u64,
240243
b2: u64,
241244
c1: u64,
242245
c2: u64,
243-
) -> Result<(), R1CSError> {
244-
// Common
245-
let pc_gens = PedersenGens::default();
246-
let bp_gens = BulletproofGens::new(128, 1);
247-
248-
// Prover's scope
249-
let (proof, commitments) = {
250-
let mut transcript = Transcript::new(b"R1CSExampleGadget");
246+
) -> Result<(R1CSProof, Vec<CompressedRistretto>), R1CSError> {
247+
let mut transcript = Transcript::new(b"R1CSExampleGadget");
251248

252-
// 1. Create a prover
253-
let mut prover = Prover::new(&bp_gens, &pc_gens, &mut transcript);
249+
// 1. Create a prover
250+
let mut prover = Prover::new(bp_gens, pc_gens, &mut transcript);
254251

255-
// 2. Commit high-level variables
256-
let (commitments, vars): (Vec<_>, Vec<_>) = [a1, a2, b1, b2, c1]
257-
.into_iter()
258-
.map(|x| prover.commit(Scalar::from(*x), Scalar::random(&mut thread_rng())))
259-
.unzip();
252+
// 2. Commit high-level variables
253+
let (commitments, vars): (Vec<_>, Vec<_>) = [a1, a2, b1, b2, c1]
254+
.into_iter()
255+
.map(|x| prover.commit(Scalar::from(*x), Scalar::random(&mut thread_rng())))
256+
.unzip();
260257

261-
// 3. Build a CS
262-
example_gadget(
263-
&mut prover,
264-
vars[0].into(),
265-
vars[1].into(),
266-
vars[2].into(),
267-
vars[3].into(),
268-
vars[4].into(),
269-
Scalar::from(c2).into(),
270-
);
271-
272-
// 4. Make a proof
273-
let proof = prover.prove()?;
258+
// 3. Build a CS
259+
example_gadget(
260+
&mut prover,
261+
vars[0].into(),
262+
vars[1].into(),
263+
vars[2].into(),
264+
vars[3].into(),
265+
vars[4].into(),
266+
Scalar::from(c2).into(),
267+
);
274268

275-
(proof, commitments)
276-
};
269+
// 4. Make a proof
270+
let proof = prover.prove()?;
277271

278-
// Verifier logic
272+
Ok((proof, commitments))
273+
}
279274

275+
// Verifier logic
276+
fn example_gadget_verify(
277+
pc_gens: &PedersenGens,
278+
bp_gens: &BulletproofGens,
279+
c2: u64,
280+
proof: R1CSProof,
281+
commitments: Vec<CompressedRistretto>,
282+
) -> Result<(), R1CSError> {
280283
let mut transcript = Transcript::new(b"R1CSExampleGadget");
281284

282285
// 1. Create a verifier
@@ -302,10 +305,56 @@ fn example_gadget_roundtrip_helper(
302305
.map_err(|_| R1CSError::VerificationError)
303306
}
304307

308+
fn example_gadget_roundtrip_helper(
309+
a1: u64,
310+
a2: u64,
311+
b1: u64,
312+
b2: u64,
313+
c1: u64,
314+
c2: u64,
315+
) -> Result<(), R1CSError> {
316+
// Common
317+
let pc_gens = PedersenGens::default();
318+
let bp_gens = BulletproofGens::new(128, 1);
319+
320+
let (proof, commitments) = example_gadget_proof(&pc_gens, &bp_gens, a1, a2, b1, b2, c1, c2)?;
321+
322+
example_gadget_verify(&pc_gens, &bp_gens, c2, proof, commitments)
323+
}
324+
325+
fn example_gadget_roundtrip_serialization_helper(
326+
a1: u64,
327+
a2: u64,
328+
b1: u64,
329+
b2: u64,
330+
c1: u64,
331+
c2: u64,
332+
) -> Result<(), R1CSError> {
333+
// Common
334+
let pc_gens = PedersenGens::default();
335+
let bp_gens = BulletproofGens::new(128, 1);
336+
337+
let (proof, commitments) = example_gadget_proof(&pc_gens, &bp_gens, a1, a2, b1, b2, c1, c2)?;
338+
339+
let proof = proof.to_bytes();
340+
341+
let proof = R1CSProof::from_bytes(&proof)?;
342+
343+
example_gadget_verify(&pc_gens, &bp_gens, c2, proof, commitments)
344+
}
345+
305346
#[test]
306347
fn example_gadget_test() {
307348
// (3 + 4) * (6 + 1) = (40 + 9)
308349
assert!(example_gadget_roundtrip_helper(3, 4, 6, 1, 40, 9).is_ok());
309350
// (3 + 4) * (6 + 1) != (40 + 10)
310351
assert!(example_gadget_roundtrip_helper(3, 4, 6, 1, 40, 10).is_err());
311352
}
353+
354+
#[test]
355+
fn example_gadget_serialization_test() {
356+
// (3 + 4) * (6 + 1) = (40 + 9)
357+
assert!(example_gadget_roundtrip_serialization_helper(3, 4, 6, 1, 40, 9).is_ok());
358+
// (3 + 4) * (6 + 1) != (40 + 10)
359+
assert!(example_gadget_roundtrip_serialization_helper(3, 4, 6, 1, 40, 10).is_err());
360+
}

0 commit comments

Comments
 (0)