Skip to content

Commit b822f19

Browse files
Fix/disprove script exceeds 4 m with constraints (BitVM#297)
* feat: add tmul for fp2 with 4 window stack len * feat: add fp2_w4 in ss_mul to reduce script size * feat: add w4 version of lc4_keep_elements * chore: specify vec capactiy * doc: add docs to new functions
1 parent 67e7c27 commit b822f19

6 files changed

Lines changed: 167 additions & 3 deletions

File tree

bitvm/src/bn254/fp254impl.rs

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -686,6 +686,49 @@ pub trait Fp254Impl {
686686
(script, hints)
687687
}
688688

689+
// Assumes tmul hint (1 BigInteger) at the top of stack
690+
// and operands (a, b, c, d) at stack depths (a_depth, b_depth, c_depth, d_depth)
691+
// Computes r = a * c + b * d (mod p)
692+
#[allow(clippy::too_many_arguments)]
693+
fn hinted_mul_lc2_w4(
694+
a_depth: u32,
695+
a: ark_bn254::Fq,
696+
b_depth: u32,
697+
b: ark_bn254::Fq,
698+
c_depth: u32,
699+
c: ark_bn254::Fq,
700+
d_depth: u32,
701+
d: ark_bn254::Fq,
702+
) -> (Script, Vec<Hint>) {
703+
assert!(a_depth > b_depth && b_depth > c_depth && c_depth > d_depth);
704+
705+
let mut hints = Vec::with_capacity(1);
706+
707+
let modulus = &Fq::modulus_as_bigint();
708+
709+
let x = BigInt::from_str(&a.to_string()).unwrap();
710+
let y = BigInt::from_str(&b.to_string()).unwrap();
711+
let z = BigInt::from_str(&c.to_string()).unwrap();
712+
let w = BigInt::from_str(&d.to_string()).unwrap();
713+
714+
let q = (x * z + y * w) / modulus;
715+
716+
let script = script! {
717+
for _ in 0..Self::N_LIMBS {
718+
OP_DEPTH OP_1SUB OP_ROLL // hints
719+
}
720+
// { Fq::push(ark_bn254::Fq::from_str(&q.to_string()).unwrap()) }
721+
{ Fq::roll(a_depth + 1) }
722+
{ Fq::roll(b_depth + 2) }
723+
{ Fq::roll(c_depth + 3) }
724+
{ Fq::roll(d_depth + 4) }
725+
{ Fq::tmul_lc2_w4() }
726+
};
727+
hints.push(Hint::BigIntegerTmulLC2(q));
728+
729+
(script, hints)
730+
}
731+
689732
#[allow(clippy::too_many_arguments)]
690733
fn hinted_mul_lc4(
691734
a_depth: u32,
@@ -792,6 +835,47 @@ pub trait Fp254Impl {
792835
(script, hints)
793836
}
794837

838+
// Same as hinted_mul_lc2_keep_elements(), except retains operands (a, b, c, d) on stack
839+
#[allow(clippy::too_many_arguments)]
840+
fn hinted_mul_lc2_keep_elements_w4(
841+
a_depth: u32,
842+
a: ark_bn254::Fq,
843+
b_depth: u32,
844+
b: ark_bn254::Fq,
845+
c_depth: u32,
846+
c: ark_bn254::Fq,
847+
d_depth: u32,
848+
d: ark_bn254::Fq,
849+
) -> (Script, Vec<Hint>) {
850+
assert!(a_depth > b_depth && b_depth > c_depth && c_depth > d_depth);
851+
852+
let mut hints = Vec::with_capacity(1);
853+
854+
let modulus = &Fq::modulus_as_bigint();
855+
856+
let x = BigInt::from_str(&a.to_string()).unwrap();
857+
let y = BigInt::from_str(&b.to_string()).unwrap();
858+
let z = BigInt::from_str(&c.to_string()).unwrap();
859+
let w = BigInt::from_str(&d.to_string()).unwrap();
860+
861+
let q = (x * z + y * w) / modulus;
862+
863+
let script = script! {
864+
for _ in 0..Self::N_LIMBS {
865+
OP_DEPTH OP_1SUB OP_ROLL // hints
866+
}
867+
// { Fq::push(ark_bn254::Fq::from_str(&q.to_string()).unwrap()) }
868+
{ Fq::copy(a_depth + 1) }
869+
{ Fq::copy(b_depth + 2) }
870+
{ Fq::copy(c_depth + 3) }
871+
{ Fq::copy(d_depth + 4) }
872+
{ Fq::tmul_lc2_w4() }
873+
};
874+
hints.push(Hint::BigIntegerTmulLC2(q));
875+
876+
(script, hints)
877+
}
878+
795879
// TODO: Optimize using the sqaure feature
796880
fn hinted_square(a: ark_bn254::Fq) -> (Script, Vec<Hint>) {
797881
let mut hints = Vec::new();

bitvm/src/bn254/fq.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,16 @@ impl Fq {
4747
}
4848
}
4949

50+
// Wrapper over 4-bit windowed tmul that computes a linear combination of two terms
51+
// Computes r = a * c + b * d (mod p)
52+
// Input Stack: [hint, a, b, c, d]
53+
// Output Stack: [r]
54+
pub fn tmul_lc2_w4() -> Script {
55+
script! {
56+
{ <Fq as Fp254Mul2LCW4>::tmul() }
57+
}
58+
}
59+
5060
pub fn tmul_lc4() -> Script {
5161
script! {
5262
{ <Fq as Fp254Mul4LC>::tmul() }
@@ -408,6 +418,7 @@ macro_rules! fp_lc_mul {
408418

409419
fp_lc_mul!(Mul, 4, 4, [true]);
410420
fp_lc_mul!(Mul2LC, 3, 3, [true, true]);
421+
fp_lc_mul!(Mul2LCW4, 4, 4, [true, true]);
411422
fp_lc_mul!(Mul4LC, 3, 3, [true, true, true, true]);
412423

413424
#[cfg(test)]

bitvm/src/bn254/fq2.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,46 @@ impl Fq2 {
192192
(script, hints)
193193
}
194194

195+
// Given Fq2 elements a and b, compute their product
196+
// A = a0 + a1 u, B = b0 + b1 u, where $u^2$ is quadratic non-residue, for bn-254 $u^2$ = -1
197+
// A.B = (a0.b0 + a1.b1 $u^2$) + u (a0.b1 + a1b0) = (a0.b0 - a1.b1) + u (a0.b1 + a1.b0)
198+
// This specific version uses tmul of 4-bit window to compute each of the two terms above.
199+
pub fn hinted_mul_w4(
200+
mut a_depth: u32,
201+
mut a: ark_bn254::Fq2,
202+
mut b_depth: u32,
203+
mut b: ark_bn254::Fq2,
204+
) -> (Script, Vec<Hint>) {
205+
if a_depth < b_depth {
206+
(a_depth, b_depth) = (b_depth, a_depth);
207+
(a, b) = (b, a);
208+
}
209+
assert_ne!(a_depth, b_depth);
210+
211+
let mut hints = Vec::with_capacity(2);
212+
213+
let (hinted_script1, hint1) =
214+
Fq::hinted_mul_lc2_keep_elements_w4(3, a.c0, 2, a.c1, 1, b.c1, 0, b.c0);
215+
let (hinted_script2, hint2) = Fq::hinted_mul_lc2_w4(3, a.c0, 2, a.c1, 1, b.c0, 0, -b.c1);
216+
217+
let script = script! {
218+
{ Fq2::roll(a_depth) }
219+
{ Fq2::roll(b_depth + 2) } // a.c0 a.c1 b.c0 b.c1
220+
{ Fq::roll(1) } // a.c0 a.c1 b.c1 b.c0
221+
{ hinted_script1 } // a.c0 a.c1 b.c1 b.c0 a.c0*b.c1+a.c1*b.c0
222+
{ Fq::toaltstack() } // a.c0 a.c1 b.c1 b.c0 | a.c0*b.c1+a.c1*b.c0
223+
{ Fq::roll(1) } // a.c0 a.c1 b.c0 b.c1 | a.c0*b.c1+a.c1*b.c0
224+
{ Fq::neg(0) } // a.c0 a.c1 b.c0 -b.c1 | a.c0*b.c1+a.c1*b.c0
225+
{ hinted_script2 } // a.c0*b.c0-a.c1*b.c1 | a.c0*b.c1+a.c1*b.c0
226+
{ Fq::fromaltstack() } // a.c0*b.c0-a.c1*b.c1 a.c0*b.c1+a.c1*b.c0
227+
};
228+
229+
hints.extend(hint1);
230+
hints.extend(hint2);
231+
232+
(script, hints)
233+
}
234+
195235
pub fn hinted_mul_by_constant(
196236
a: ark_bn254::Fq2,
197237
constant: &ark_bn254::Fq2,
@@ -496,6 +536,33 @@ mod test {
496536
}
497537
}
498538

539+
#[test]
540+
fn test_bn254_fq2_hinted_mul_w4() {
541+
let mut prng: ChaCha20Rng = ChaCha20Rng::seed_from_u64(0);
542+
543+
for _ in 0..100 {
544+
let a = ark_bn254::Fq2::rand(&mut prng);
545+
let b = ark_bn254::Fq2::rand(&mut prng);
546+
let c = a.mul(&b);
547+
548+
let (hinted_mul, hints) = Fq2::hinted_mul_w4(2, a, 0, b);
549+
println!("Fq2::hinted_mul_w4: {} bytes", hinted_mul.len());
550+
551+
let script = script! {
552+
for hint in hints {
553+
{ hint.push() }
554+
}
555+
{ Fq2::push(a) }
556+
{ Fq2::push(b) }
557+
{ hinted_mul.clone() }
558+
{ Fq2::push(c) }
559+
{ Fq2::equalverify() }
560+
OP_TRUE
561+
};
562+
run(script);
563+
}
564+
}
565+
499566
#[test]
500567
fn test_bn254_fq2_hinted_mul_by_constant() {
501568
let mut prng: ChaCha20Rng = ChaCha20Rng::seed_from_u64(0);

bitvm/src/bn254/g2.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -472,7 +472,7 @@ pub fn hinted_affine_add_line(
472472
) -> (Script, Vec<Hint>) {
473473
let mut hints = Vec::new();
474474
let (hinted_script0, hint0) = Fq2::hinted_square(c3);
475-
let (hinted_script1, hint1) = Fq2::hinted_mul(4, c3, 0, c3.square() - tx - qx);
475+
let (hinted_script1, hint1) = Fq2::hinted_mul_w4(4, c3, 0, c3.square() - tx - qx);
476476

477477
let script = script! {
478478
// [c3, c4, T.x, Q.x]

bitvm/src/chunk/api.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1302,6 +1302,7 @@ mod test {
13021302
}
13031303

13041304
let _total = NUM_PUBS + NUM_U256 + NUM_HASH;
1305+
const RESERVED_SPACE: usize = 16000; // blockreservedweight=8000 + extra (8000)
13051306
for i in 0.._total {
13061307
println!("ITERATION {:?}", i);
13071308
let mut proof_asserts = read_asserts_from_file("bridge_data/chunker_data/assert.json");
@@ -1317,6 +1318,7 @@ mod test {
13171318
let scr = hint_script
13181319
.clone()
13191320
.push_script(verifier_scripts[index].clone());
1321+
assert!(scr.len() < 4_000_000 - RESERVED_SPACE);
13201322
let res = execute_script(scr);
13211323
for i in 0..res.final_stack.len() {
13221324
println!("{i:} {:?}", res.final_stack.get(i));

bitvm/src/chunk/taps_mul.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@ pub(crate) fn utils_fq6_ss_mul(
2727
let i = b * e;
2828
let result = ark_bn254::Fq6::new(g, h, i);
2929

30-
let (g_scr, g_hints) = Fq2::hinted_mul(2, d, 0, a);
30+
let (g_scr, g_hints) = Fq2::hinted_mul_w4(2, d, 0, a);
3131
// We use lc4 to compute h as it requires lesser number of tmul hints compared to doing the same thing with two LC2s
3232
let (h_scr, h_hints) = Fq2::hinted_mul_lc4_keep_elements(b, d, e, a);
33-
let (i_scr, i_hints) = Fq2::hinted_mul(2, e, 0, b);
33+
let (i_scr, i_hints) = Fq2::hinted_mul_w4(2, e, 0, b);
3434

3535
let mut hints = vec![];
3636
for hint in [i_hints, g_hints, h_hints] {

0 commit comments

Comments
 (0)