Kothapalli, Abhiram, Srinath Setty, and Ioanna Tzialla. “Nova: Recursive zero-knowledge arguments from folding schemes.” Annual International Cryptology Conference. Cham: Springer Nature Switzerland, 2022.
- Understanding Relaxed R1CS
- R1CS
1 | /// A type that holds a witness for a given R1CS instance |
- Relaxed R1CS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145/// A type that holds a witness for a given Relaxed R1CS instance
pub struct RelaxedR1CSWitness<E: Engine> {
pub(crate) W: Vec<E::Scalar>,
pub(crate) E: Vec<E::Scalar>,
}
/// A type that holds a Relaxed R1CS instance
pub struct RelaxedR1CSInstance<E: Engine> {
pub(crate) comm_W: Commitment<E>,
pub(crate) comm_E: Commitment<E>,
pub(crate) X: Vec<E::Scalar>,
pub(crate) u: E::Scalar,
}
impl<E: Engine> RelaxedR1CSWitness<E> {
/// Produces a default `RelaxedR1CSWitness` given an `R1CSShape`
pub fn default(S: &R1CSShape<E>) -> RelaxedR1CSWitness<E> {
RelaxedR1CSWitness {
W: vec![E::Scalar::ZERO; S.num_vars],
E: vec![E::Scalar::ZERO; S.num_cons],
}
}
/// Initializes a new `RelaxedR1CSWitness` from an `R1CSWitness`
pub fn from_r1cs_witness(S: &R1CSShape<E>, witness: &R1CSWitness<E>) -> RelaxedR1CSWitness<E> {
RelaxedR1CSWitness {
W: witness.W.clone(),
E: vec![E::Scalar::ZERO; S.num_cons],
}
}
/// Commits to the witness using the supplied generators
pub fn commit(&self, ck: &CommitmentKey<E>) -> (Commitment<E>, Commitment<E>) {
(CE::<E>::commit(ck, &self.W), CE::<E>::commit(ck, &self.E))
}
/// Folds an incoming `R1CSWitness` into the current one
pub fn fold(
&self,
W2: &R1CSWitness<E>,
T: &[E::Scalar],
r: &E::Scalar,
) -> Result<RelaxedR1CSWitness<E>, NovaError> {
let (W1, E1) = (&self.W, &self.E);
let W2 = &W2.W;
if W1.len() != W2.len() {
return Err(NovaError::InvalidWitnessLength);
}
let W = W1
.par_iter()
.zip(W2)
.map(|(a, b)| *a + *r * *b)
.collect::<Vec<E::Scalar>>();
let E = E1
.par_iter()
.zip(T)
.map(|(a, b)| *a + *r * *b)
.collect::<Vec<E::Scalar>>();
Ok(RelaxedR1CSWitness { W, E })
}
/// Pads the provided witness to the correct length
pub fn pad(&self, S: &R1CSShape<E>) -> RelaxedR1CSWitness<E> {
let mut W = self.W.clone();
W.extend(vec![E::Scalar::ZERO; S.num_vars - W.len()]);
let mut E = self.E.clone();
E.extend(vec![E::Scalar::ZERO; S.num_cons - E.len()]);
Self { W, E }
}
}
impl<E: Engine> RelaxedR1CSInstance<E> {
/// Produces a default `RelaxedR1CSInstance` given `R1CSGens` and `R1CSShape`
pub fn default(_ck: &CommitmentKey<E>, S: &R1CSShape<E>) -> RelaxedR1CSInstance<E> {
let (comm_W, comm_E) = (Commitment::<E>::default(), Commitment::<E>::default());
RelaxedR1CSInstance {
comm_W,
comm_E,
u: E::Scalar::ZERO,
X: vec![E::Scalar::ZERO; S.num_io],
}
}
/// Initializes a new `RelaxedR1CSInstance` from an `R1CSInstance`
pub fn from_r1cs_instance(
ck: &CommitmentKey<E>,
S: &R1CSShape<E>,
instance: &R1CSInstance<E>,
) -> RelaxedR1CSInstance<E> {
let mut r_instance = RelaxedR1CSInstance::default(ck, S);
r_instance.comm_W = instance.comm_W;
r_instance.u = E::Scalar::ONE;
r_instance.X = instance.X.clone();
r_instance
}
/// Initializes a new `RelaxedR1CSInstance` from an `R1CSInstance`
pub fn from_r1cs_instance_unchecked(
comm_W: &Commitment<E>,
X: &[E::Scalar],
) -> RelaxedR1CSInstance<E> {
RelaxedR1CSInstance {
comm_W: *comm_W,
comm_E: Commitment::<E>::default(),
u: E::Scalar::ONE,
X: X.to_vec(),
}
}
/// Folds an incoming `RelaxedR1CSInstance` into the current one
pub fn fold(
&self,
U2: &R1CSInstance<E>,
comm_T: &Commitment<E>,
r: &E::Scalar,
) -> RelaxedR1CSInstance<E> {
let (X1, u1, comm_W_1, comm_E_1) =
(&self.X, &self.u, &self.comm_W.clone(), &self.comm_E.clone());
let (X2, comm_W_2) = (&U2.X, &U2.comm_W);
// weighted sum of X, comm_W, comm_E, and u
let X = X1
.par_iter()
.zip(X2)
.map(|(a, b)| *a + *r * *b)
.collect::<Vec<E::Scalar>>();
let comm_W = *comm_W_1 + *comm_W_2 * *r;
let comm_E = *comm_E_1 + *comm_T * *r;
let u = *u1 + *r;
RelaxedR1CSInstance {
comm_W,
comm_E,
X,
u,
}
}
}