Skip to content

Commit 224f195

Browse files
committed
rlc improvements
Signed-off-by: lovesh <lovesh.bond@gmail.com>
1 parent 7d1b001 commit 224f195

2 files changed

Lines changed: 44 additions & 50 deletions

File tree

utils/src/msm.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ impl<G: CurveGroup> Mul<&G::ScalarField> for &WindowTable<G> {
4848
type Output = G;
4949

5050
fn mul(self, rhs: &G::ScalarField) -> Self::Output {
51-
FixedBase::windowed_mul(self.num_windows, self.window_size, &self.table, rhs)
51+
self.multiply(rhs)
5252
}
5353
}
5454

utils/src/randomized_mult_checker.rs

Lines changed: 43 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use ark_std::{
55
rand::Rng,
66
vec::Vec,
77
UniformRand,
8+
collections::BTreeMap,
89
};
910

1011
/// Represents a scalar multiplication check of the form `G1 * a1 + G2 * a2 + G3 * a3 + ... = T`.
@@ -18,10 +19,10 @@ use ark_std::{
1819
/// The single check above is simplified by combining terms of `G1`, `H1`, etc to reduce the size of the multi-scalar multiplication
1920
#[derive(Debug, Clone)]
2021
pub struct RandomizedMultChecker<G: AffineRepr> {
21-
// map is more expensive than a vector (checked with a test)
22-
// args: BTreeMap<SortableAffine<G>, G::ScalarField>,
23-
/// Verification will expect the multi-scalar multiplication of first and second vector to be one.
24-
args: (Vec<G>, Vec<G::ScalarField>),
22+
/// Verification will expect the multi-scalar multiplication of key-value pairs to be one.
23+
/// x-coordinate -> (scalar, point)
24+
// This trick is taken from halo2 code (MSM) but keeping the point rather than y coordinate in value since there is no way to convert back from x, y coordinates for AffineRepr
25+
args: BTreeMap<G::BaseField, (G::ScalarField, G)>,
2526
/// The random value chosen during creation
2627
random: G::ScalarField,
2728
/// The random value to be used for current check. After each check, set `current_random = current_random * random`
@@ -31,8 +32,7 @@ pub struct RandomizedMultChecker<G: AffineRepr> {
3132
impl<G: AffineRepr> RandomizedMultChecker<G> {
3233
pub fn new(random: G::ScalarField) -> Self {
3334
Self {
34-
// args: BTreeMap::new(),
35-
args: (Vec::new(), Vec::new()),
35+
args: BTreeMap::new(),
3636
random,
3737
current_random: G::ScalarField::one(),
3838
}
@@ -91,60 +91,40 @@ impl<G: AffineRepr> RandomizedMultChecker<G> {
9191

9292
/// Combine all the checks into a multi-scalar multiplication and return true if the result is 0.
9393
pub fn verify(&self) -> bool {
94-
debug_assert_eq!(self.args.0.len(), self.args.1.len());
95-
G::Group::msm_unchecked(&self.args.0, &self.args.1).is_zero()
94+
let mut points = Vec::with_capacity(self.len());
95+
let mut scalars = Vec::with_capacity(self.len());
96+
for (_, (s, point)) in self.args.iter() {
97+
points.push(*point);
98+
scalars.push(*s);
99+
}
100+
G::Group::msm_unchecked(&points, &scalars).is_zero()
96101
}
97102

98103
pub fn len(&self) -> usize {
99-
self.args.0.len()
104+
self.args.len()
100105
}
101106

102107
fn add(&mut self, p: G, s: G::ScalarField) {
103-
// If the point already exists, update the scalar corresponding to the point
104-
if let Some(i) = self.args.0.iter().position(|&p_i| p_i == p) {
105-
self.args.1[i] = self.args.1[i] + s;
108+
if let Some(x) = p.x() {
109+
self.args
110+
.entry(*x)
111+
.and_modify(|(old_scalar, point)| {
112+
// If the point or its negative already exists, update the scalar accordingly
113+
if *point == p {
114+
*old_scalar += s;
115+
} else {
116+
*old_scalar -= s;
117+
debug_assert_eq!(point.into_group(), -p.into_group());
118+
}
119+
})
120+
.or_insert((s, p));
106121
} else {
107-
self.args.0.push(p);
108-
self.args.1.push(s);
122+
// If p is a point at infinity, then it doesn't impact the result
123+
debug_assert!(p.is_zero());
109124
}
110125
}
111-
112-
// fn add(&mut self, p: G, s: G::ScalarField) {
113-
// let sortable_p = SortableAffine(p);
114-
// let val = self.args.remove(&sortable_p);
115-
// if let Some(v) = val {
116-
// self.args.insert(sortable_p, v + s);
117-
// } else {
118-
// self.args.insert(sortable_p, s);
119-
// }
120-
// }
121-
//
122-
// pub fn verify(self) -> bool {
123-
// let mut b = vec![];
124-
// let mut s = vec![];
125-
// for (k, v) in self.args.into_iter() {
126-
// b.push(k.0);
127-
// s.push(v);
128-
// }
129-
// G::Group::msm_unchecked(&b, &s).is_zero()
130-
// }
131126
}
132127

133-
// #[derive(Debug, Clone, PartialEq, Eq)]
134-
// pub struct SortableAffine<G: AffineRepr>(G);
135-
//
136-
// impl<G: AffineRepr> Ord for SortableAffine<G> {
137-
// fn cmp(&self, other: &Self) -> Ordering {
138-
// self.0.x().cmp(&other.0.x())
139-
// }
140-
// }
141-
//
142-
// impl<G: AffineRepr> PartialOrd for SortableAffine<G> {
143-
// fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
144-
// Some(self.cmp(other))
145-
// }
146-
// }
147-
148128
#[cfg(test)]
149129
mod test {
150130
use super::*;
@@ -278,6 +258,20 @@ mod test {
278258
checker.add_many([g3, h3], [&a3, &a6], c8);
279259
checker.add_many([g1, g2, g3], [&a1, &a2, &a3], c9);
280260
assert!(checker.verify());
261+
262+
let minus_g1 = -g1;
263+
let minus_g2 = -g2;
264+
let c1 = (g1 * a1).into_affine();
265+
let c2 = (minus_g1 * a2).into_affine();
266+
let c3 = (g2 * a3).into_affine();
267+
let c4 = (minus_g2 * a4).into_affine();
268+
269+
let mut checker = RandomizedMultChecker::new_using_rng(&mut rng);
270+
checker.add_1(g1, &a1, c1);
271+
checker.add_1(minus_g1, &a2, c2);
272+
checker.add_1(g2, &a3, c3);
273+
checker.add_1(minus_g2, &a4, c4);
274+
assert!(checker.verify());
281275
}
282276

283277
#[test]

0 commit comments

Comments
 (0)