Skip to content

Commit a977a21

Browse files
authored
[Zellic Audit] 3.12 Incomplete handling of G1 point addition (BitVM#379)
* create a version of G1Affine::hinted_check_add that the caller must use with points that are not t==q nor t==-q * optimize G1Affine::hinted_check_add and add TODO comments for later * rearrange wrong if checks in G1Affine::hinted_check_add * add validity checks to hints of new G1Affine::hinted_check_add * fix hint validity check and roll in G1Affine::hinted_check_add
1 parent dda579f commit a977a21

5 files changed

Lines changed: 171 additions & 27 deletions

File tree

bitvm/src/bigint/cmp.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,22 @@ impl<const N_BITS: u32, const LIMB_SIZE: u32> BigIntImpl<N_BITS, LIMB_SIZE> {
2727
}
2828
}
2929

30+
pub fn equal_keep_elements(a: u32, b: u32) -> Script {
31+
script! {
32+
{ Self::copy_zip(a, b) }
33+
for _ in 0..Self::N_LIMBS {
34+
OP_EQUAL
35+
OP_TOALTSTACK
36+
}
37+
for _ in 0..Self::N_LIMBS {
38+
OP_FROMALTSTACK
39+
}
40+
for _ in 0..Self::N_LIMBS - 1 {
41+
OP_BOOLAND
42+
}
43+
}
44+
}
45+
3046
pub fn notequal(a: u32, b: u32) -> Script {
3147
script! {
3248
{ Self::equal(a, b) }

bitvm/src/bn254/fp254impl.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,11 @@ pub trait Fp254Impl {
107107
U254::equal(a, b)
108108
}
109109

110+
#[inline]
111+
fn equal_keep_elements(a: u32, b: u32) -> Script {
112+
U254::equal_keep_elements(a, b)
113+
}
114+
110115
#[inline]
111116
fn equalverify(a: u32, b: u32) -> Script {
112117
U254::equalverify(a, b)

bitvm/src/bn254/g1.rs

Lines changed: 148 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -169,8 +169,13 @@ impl G1Affine {
169169
}
170170
}
171171

172-
pub fn hinted_check_add(t: ark_bn254::G1Affine, q: ark_bn254::G1Affine) -> (Script, Vec<Hint>) {
172+
pub fn hinted_check_add_prevent_degenerate(
173+
t: ark_bn254::G1Affine,
174+
q: ark_bn254::G1Affine,
175+
) -> (Script, Vec<Hint>) {
173176
let mut hints = vec![];
177+
assert_ne!(t, q);
178+
assert_ne!(t, -q);
174179

175180
let (alpha, bias) = if !t.is_zero() && !q.is_zero() {
176181
let alpha = (t.y - q.y) / (t.x - q.x);
@@ -226,6 +231,106 @@ impl G1Affine {
226231
(script, hints)
227232
}
228233

234+
pub fn hinted_check_add(t: ark_bn254::G1Affine, q: ark_bn254::G1Affine) -> (Script, Vec<Hint>) {
235+
let mut hints = vec![];
236+
237+
let (alpha, bias) = if t.is_zero() || q.is_zero() {
238+
// no hint, dummy zero
239+
(ark_bn254::Fq::ZERO, ark_bn254::Fq::ZERO)
240+
} else if t == -q {
241+
// no hint, dummy zero
242+
(ark_bn254::Fq::ZERO, ark_bn254::Fq::ZERO)
243+
} else if t == q {
244+
// doubling hints
245+
let alpha = (t.x.square() + t.x.square() + t.x.square()) / (t.y + t.y);
246+
let bias = t.y - alpha * t.x;
247+
(alpha, bias)
248+
} else {
249+
// adding hints
250+
let alpha = (t.y - q.y) / (t.x - q.x);
251+
let bias = t.y - alpha * t.x;
252+
(alpha, bias)
253+
};
254+
255+
let (hinted_script1, hint1) = Self::hinted_check_chord_line(t, q, alpha);
256+
let (hinted_script2, hint2) = Self::hinted_add(t.x, q.x, alpha);
257+
let (hinted_script3, hint3) = Self::hinted_check_tangent_line(t, alpha);
258+
259+
let script = script! { // tx ty qx qy
260+
{ G1Affine::is_zero_keep_element() }
261+
OP_IF
262+
{ G1Affine::drop() }
263+
OP_ELSE
264+
{ G1Affine::roll(2) }
265+
{ G1Affine::is_zero_keep_element() }
266+
OP_IF
267+
{ G1Affine::drop() }
268+
OP_ELSE // qx qy tx ty
269+
{ Fq::equal_keep_elements(3, 1) } // t == q or t == -q
270+
OP_DUP
271+
OP_TOALTSTACK
272+
OP_TOALTSTACK
273+
{ Fq::equal_keep_elements(2, 0) }
274+
OP_NOT
275+
OP_FROMALTSTACK
276+
OP_BOOLAND
277+
OP_IF // case: t == -q
278+
OP_FROMALTSTACK OP_DROP
279+
{ G1Affine::drop() }
280+
{ G1Affine::drop() }
281+
{ G1Affine::identity() }
282+
OP_ELSE // case: t != -q
283+
for _ in 0..Fq::N_LIMBS {
284+
OP_DEPTH OP_1SUB OP_ROLL
285+
}
286+
{ Fq::check_validity_and_keep_element() }
287+
for _ in 0..Fq::N_LIMBS {
288+
OP_DEPTH OP_1SUB OP_ROLL
289+
} // qx qy tx ty c3 c4
290+
{ Fq::check_validity_and_keep_element() }
291+
{ Fq::copy(1) } // qx qy tx ty c3 c4 c3
292+
{ Fq::copy(1) } // qx qy tx ty c3 c4 c3 c4
293+
{ Fq::copy(5) } // qx qy tx ty c3 c4 c3 c4 tx
294+
{ Fq::roll(5) } // qx qy tx c3 c4 c3 c4 tx ty
295+
OP_FROMALTSTACK
296+
OP_IF // case: t == q
297+
{ hinted_script3 } // qx qy tx c3 c4 is_tangent_line_correct
298+
OP_VERIFY // qx qy tx c3 c4
299+
{ Fq::roll(3) } // qx tx c3 c4 qy
300+
{ Fq::drop() } // qx tx c3 c4
301+
OP_ELSE // case: general case
302+
{ Fq::copy(8) } // qx qy tx c3 c4 c3 c4 tx ty qx
303+
{ Fq::roll(8) } // qx tx c3 c4 c3 c4 tx ty qx qy
304+
{ hinted_script1 } // qx tx c3 c4 0/1
305+
OP_VERIFY // qx tx c3 c4
306+
OP_ENDIF
307+
{ Fq::roll(2) } // qx c3 c4 tx
308+
{ Fq::roll(3) } // c3 c4 tx qx
309+
{ hinted_script2 } // x' y'
310+
OP_ENDIF
311+
OP_ENDIF
312+
OP_ENDIF
313+
};
314+
315+
if t.is_zero() || q.is_zero() {
316+
// no hint
317+
} else if t == -q {
318+
// no hint
319+
} else if t == q {
320+
hints.push(Hint::Fq(alpha));
321+
hints.push(Hint::Fq(-bias));
322+
hints.extend(hint3);
323+
hints.extend(hint2);
324+
} else {
325+
hints.push(Hint::Fq(alpha));
326+
hints.push(Hint::Fq(-bias));
327+
hints.extend(hint1);
328+
hints.extend(hint2);
329+
};
330+
331+
(script, hints)
332+
}
333+
229334
/// add two points T and Q
230335
/// x' = alpha^2 - T.x - Q.x
231336
/// y' = -bias - alpha * x'
@@ -673,50 +778,68 @@ mod test {
673778
}
674779

675780
#[test]
676-
fn test_g1_affine_hinted_check_add() {
677-
//println!("G1.hinted_add: {} bytes", G1Affine::check_add().len());
781+
fn test_g1_affine_hinted_check_add_prevent_degenerate() {
678782
let mut prng = ChaCha20Rng::seed_from_u64(0);
679783
let t = ark_bn254::G1Affine::rand(&mut prng);
680784
let q = ark_bn254::G1Affine::rand(&mut prng);
681-
let alpha = (t.y - q.y) / (t.x - q.x);
682-
// -bias
683-
let bias_minus = alpha * t.x - t.y;
684-
685-
let x = alpha.square() - t.x - q.x;
686-
let y = bias_minus - alpha * x;
785+
let r = (t + q).into_affine();
687786

688-
let (hinted_check_add, hints) = G1Affine::hinted_check_add(t, q);
787+
let (hinted_check_add, hints) = G1Affine::hinted_check_add_prevent_degenerate(t, q);
689788

690789
let script = script! {
691790
for hint in hints {
692791
{ hint.push() }
693792
}
694-
{ Fq::push(t.x) }
695-
{ Fq::push(t.y) }
696-
{ Fq::push(q.x) }
697-
{ Fq::push(q.y) }
793+
{ G1Affine::push(t) }
794+
{ G1Affine::push(q) }
698795
{ hinted_check_add.clone() }
699-
// [x']
700-
{ Fq::push(y) }
701-
// [x', y', y]
702-
{ Fq::equalverify(1,0) }
703-
// [x']
704-
{ Fq::push(x) }
705-
// [x', x]
706-
{ Fq::equalverify(1,0) }
707-
// []
796+
{ G1Affine::push(r) }
797+
{ G1Affine::equalverify() }
708798
OP_TRUE
709-
// [OP_TRUE]
710799
};
711800
let exec_result = execute_script(script);
712801
assert!(exec_result.success);
713802
println!(
714-
"hinted_check_add: {} @ {} stack",
803+
"hinted_check_add_prevent_degenerate: {} @ {} stack",
715804
hinted_check_add.len(),
716805
exec_result.stats.max_nb_stack_items
717806
);
718807
}
719808

809+
#[test]
810+
fn test_g1_affine_hinted_check_add() {
811+
let mut prng = ChaCha20Rng::seed_from_u64(0);
812+
let t = ark_bn254::G1Affine::rand(&mut prng);
813+
let q = ark_bn254::G1Affine::rand(&mut prng);
814+
let r = (t + q).into_affine();
815+
let tt = (t + t).into_affine();
816+
let negt = -t;
817+
let z = ark_bn254::G1Affine::zero();
818+
819+
for (t, q, r) in [(t, q, r), (t, t, tt), (t, negt, z), (t, z, t)] {
820+
let (hinted_check_add, hints) = G1Affine::hinted_check_add(t, q);
821+
822+
let script = script! {
823+
for hint in hints {
824+
{ hint.push() }
825+
}
826+
{ G1Affine::push(t) }
827+
{ G1Affine::push(q) }
828+
{ hinted_check_add.clone() }
829+
{ G1Affine::push(r) }
830+
{ G1Affine::equalverify() }
831+
OP_TRUE
832+
};
833+
let exec_result = execute_script(script);
834+
assert!(exec_result.success);
835+
println!(
836+
"hinted_check_add: {} @ {} stack",
837+
hinted_check_add.len(),
838+
exec_result.stats.max_nb_stack_items
839+
);
840+
}
841+
}
842+
720843
#[test]
721844
fn test_g1_affine_hinted_check_double() {
722845
//println!("G1.hinted_add: {} bytes", G1Affine::check_add().len());

bitvm/src/bn254/msm.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ fn accumulate_addition_chain_for_a_scalar_mul(
230230
);
231231

232232
// accumulate value using hinted_check_add
233-
let (add_scr, add_hints) = G1Affine::hinted_check_add(prev, row_g1);
233+
let (add_scr, add_hints) = G1Affine::hinted_check_add_prevent_degenerate(prev, row_g1);
234234

235235
prev = (prev + row_g1).into_affine(); // output of this chunk: t + q
236236
vec_row_g1_scr.push(row_g1_scr);

bitvm/src/chunk/taps_msm.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ pub(crate) fn chunk_hash_p(
137137
let (tx, qx, ty, qy) = (hint_in_t.x, hint_in_q.x, hint_in_t.y, hint_in_q.y);
138138
let t = ark_bn254::G1Affine::new_unchecked(tx, ty);
139139
let q = ark_bn254::G1Affine::new_unchecked(qx, qy);
140-
let (add_scr, add_hints) = G1Affine::hinted_check_add(t, q);
140+
let (add_scr, add_hints) = G1Affine::hinted_check_add_prevent_degenerate(t, q);
141141
let r = (t + q).into_affine();
142142
let ops_script = script! {
143143
// [t] [hash_r, hash_t]

0 commit comments

Comments
 (0)