From 892eba45abe8fe14208028df6a8d6c5166110f52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hakan=20Karaku=C5=9F?= Date: Thu, 5 Sep 2024 12:25:15 +0300 Subject: [PATCH 001/195] fix is_positive() --- src/bigint/std.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/bigint/std.rs b/src/bigint/std.rs index a6cbe8b04..27b616804 100644 --- a/src/bigint/std.rs +++ b/src/bigint/std.rs @@ -249,9 +249,10 @@ impl BigIntImpl { pub fn is_positive(depth: u32) -> Script { script! { - { (1 + depth) * Self::N_LIMBS - 1 } OP_PICK + { Self::is_zero_keep_element(depth) } OP_NOT + { (1 + depth) * Self::N_LIMBS } OP_PICK { Self::HEAD_OFFSET >> 1 } - OP_LESSTHAN + OP_LESSTHAN OP_BOOLAND } } From 6b5170d90caa5d9338fd2584da7abe25018a7345 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hakan=20Karaku=C5=9F?= Date: Thu, 5 Sep 2024 12:27:38 +0300 Subject: [PATCH 002/195] fix N_WINDOW --- src/bn254/fq.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/bn254/fq.rs b/src/bn254/fq.rs index ba19e0890..1e2ac0512 100644 --- a/src/bn254/fq.rs +++ b/src/bn254/fq.rs @@ -96,7 +96,8 @@ macro_rules! fp_lc_mul { } }; - const N_WINDOW: u32 = MAIN_LOOP_END / VAR_WIDTH; + const N_VAR_WINDOW: u32 = MAIN_LOOP_END / VAR_WIDTH; + const N_MOD_WINDOW: u32 = MAIN_LOOP_END / MOD_WIDTH; // Pre-computed lookup table's size fn size_table(window: u32) -> u32 { (1 << window) - 1 } @@ -134,7 +135,7 @@ macro_rules! fp_lc_mul { // Get modulus window at given index fn mod_window(index: u32) -> u32 { - let shift_by = MOD_WIDTH * (N_WINDOW - index - 1); + let shift_by = MOD_WIDTH * (N_MOD_WINDOW - index - 1); let bit_mask = BigInt::from_i32((1 << MOD_WIDTH) - 1).unwrap() << shift_by; ((Fq::modulus_as_bigint() & bit_mask) >> shift_by).to_u32().unwrap() } @@ -142,7 +143,7 @@ macro_rules! fp_lc_mul { // Get var windows at given index fn var_windows_script(index: u32) -> Script { let stack_top = T::N_LIMBS; - let iter = N_WINDOW - index; + let iter = N_VAR_WINDOW - index; let s_bit = iter * VAR_WIDTH - 1; // start bit let e_bit = (iter - 1) * VAR_WIDTH; // end bit @@ -153,7 +154,7 @@ macro_rules! fp_lc_mul { script! { for j in 0..N_LC { { 0 } - if iter == N_WINDOW { // initialize accumulator to track reduced limb + if iter == N_VAR_WINDOW { // initialize accumulator to track reduced limb { stack_top + T::N_LIMBS * j + s_limb + 1 } OP_PICK @@ -187,7 +188,7 @@ macro_rules! fp_lc_mul { } if j+1 < N_LC { - if iter == N_WINDOW { + if iter == N_VAR_WINDOW { OP_TOALTSTACK OP_TOALTSTACK } else { From 22929e87bcd2dd7c26bce4d45b5b10a6b5b6b54e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hakan=20Karaku=C5=9F?= Date: Fri, 6 Sep 2024 15:39:07 +0300 Subject: [PATCH 003/195] better limb_add_with_carry_prevent_overflow and limb_double_with_carry_prevent_overflow --- src/bigint/add.rs | 47 ++++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/src/bigint/add.rs b/src/bigint/add.rs index 151524543..373385886 100644 --- a/src/bigint/add.rs +++ b/src/bigint/add.rs @@ -300,23 +300,26 @@ fn limb_add_with_carry_prevent_overflow(head_offset: u32) -> Script { // {a} {b} {c:carry} OP_3DUP // {a} {b} {c} {a} {b} {c} OP_ADD OP_ADD OP_NIP // {a} {b} {a+b+c} - OP_ROT // {b} {a+b+c} {a} - { head_offset >> 1 } OP_LESSTHAN // {b} {a+b+c} {sign_a} - OP_ROT // {a+b+c} {sign_a} {b} - { head_offset >> 1 } OP_LESSTHAN // {a+b+c} {sign_a} {sign_b} - OP_ADD // {a+b+c} {sign_a+b} -> both neg: 0, both diff: 1, both pos: 2 - OP_SWAP // {sign_a+b} {a+b+c} - OP_DUP { head_offset } OP_GREATERTHANOREQUAL // {sign_a+b} {a+b+c} {L:0/1} // limb overflow - OP_TUCK // {sign_a+b} {L:0/1} {a+b+c} {L:0/1} - OP_IF { head_offset } OP_SUB OP_ENDIF // {sign_a+b} {L:0/1} {a+b+c_nlo} - OP_DUP { head_offset >> 1 } OP_GREATERTHANOREQUAL // {sign_a+b} {L:0/1} {a+b+c_nlo} {I:0/1} // integer overflow - OP_2SWAP // {a+b+c_nlo} {I:0/1} {sign_a+b} {L:0/1} + { head_offset >> 1 } // {a} {b} {a+b+c} {x} + OP_TUCK OP_DUP OP_ADD // {a} {b} {x} {a+b+c} {2x} + OP_2DUP OP_GREATERTHANOREQUAL // {a} {b} {x} {a+b+c} {2x} {L:0/1} + OP_DUP OP_TOALTSTACK // {a} {b} {x} {a+b+c} {2x} {L:0/1} | {L:0/1} + OP_IF OP_SUB OP_ELSE OP_DROP OP_ENDIF // {a} {b} {x} {a+b+c_nlo} | {L:0/1} + OP_SWAP OP_2DUP OP_GREATERTHANOREQUAL // {a} {b} {a+b+c_nlo} {x} {I:0/1} | {L:0/1} + OP_FROMALTSTACK OP_ROT // {a} {b} {a+b+c_nlo} {I:0/1} {L:0/1} {x} + OP_2ROT // {a+b+c_nlo} {I:0/1} {L:0/1} {x} {a} {b} + OP_ROT OP_TUCK // {a+b+c_nlo} {I:0/1} {L:0/1} {a} {x} {b} {x} + OP_LESSTHAN // {a+b+c_nlo} {I:0/1} {L:0/1} {a} {x} {sign_b} + OP_ROT OP_ROT // {a+b+c_nlo} {I:0/1} {L:0/1} {sign_b} {a} {x} + OP_LESSTHAN // {a+b+c_nlo} {I:0/1} {L:0/1} {sign_b} {sign_a} + OP_ADD // {a+b+c_nlo} {I:0/1} {L:0/1} {sign_a+b} + OP_SWAP // {a+b+c_nlo} {I:0/1} {sign_a+b} {L:0/1} OP_IF // {a+b+c_nlo} {I:0/1} {sign_a+b} OP_NOTIF OP_VERIFY 0 OP_ENDIF // {a+b+c_nlo} 0 OP_ELSE - OP_1SUB OP_IF OP_NOT OP_VERIFY 0 OP_ENDIF // {a+b+c_nlo} 0 + OP_1SUB OP_IF OP_NOT OP_VERIFY 0 OP_ENDIF // {a+b+c_nlo} 0 OP_ENDIF - OP_DROP // {a+b+c_nlo} + OP_DROP // {a+b+c_nlo} } } @@ -369,16 +372,14 @@ fn limb_double_with_carry_prevent_overflow(head_offset: u32) -> Script { // {a} {c:carry} OP_OVER // {a} {c} {a} OP_DUP OP_ADD OP_ADD // {a} {2a+c} - OP_SWAP // {2a+c} {a} - { head_offset >> 1 } OP_LESSTHAN // {2a+c} {sign_a} // neg: 0, pos: 1 - OP_SWAP // {sign_a} {2a+c} - OP_DUP { head_offset } OP_GREATERTHANOREQUAL // {sign_a} {2a+c} {L:0/1} // limb overflow - - OP_TUCK // {sign_a} {L:0/1} {2a+c} {L:0/1} - OP_IF { head_offset } OP_SUB OP_ENDIF // {sign_a} {L:0/1} {2a+c_nlo} - OP_DUP {head_offset >> 1 } OP_GREATERTHANOREQUAL // {sign_a} {L:0/1} {2a+c_nlo} {I:0/1} - OP_2SWAP // {2a+c_nlo} {I:0/1} {sign_a} {L:0/1} - + { head_offset >> 1 } // {a} {2a+c} {x} + OP_TUCK OP_DUP OP_ADD // {a} {x} {2a+c} {2x} + OP_2DUP OP_GREATERTHANOREQUAL // {a} {x} {2a+c} {2x} {L:0/1} + OP_DUP OP_TOALTSTACK // {a} {x} {2a+c} {2x} {L:0/1} | {L:0/1} + OP_IF OP_SUB OP_ELSE OP_DROP OP_ENDIF // {a} {x} {2a+c_nlo} | {L:0/1} + OP_2DUP OP_LESSTHAN // {a} {x} {2a+c_nlo} {I:0/1} | {L:0/1} + OP_2SWAP // {2a+c_nlo} {I:0/1} {a} {x} | {L:0/1} + OP_LESSTHAN OP_FROMALTSTACK // {2a+c_nlo} {I:0/1} {sign_a} {L:0/1} OP_IF // {2a+c_nlo} {I:0/1} {sign_a} OP_NOTIF OP_VERIFY 0 OP_ENDIF // {2a+c_nlo} 0 OP_ELSE // {2a+c_nlo} {I:0/1} {sign_a} From 2bf2a781e399a8668b4efec2de75d0a8d2aa36af Mon Sep 17 00:00:00 2001 From: fatih Date: Mon, 9 Sep 2024 11:29:23 +0300 Subject: [PATCH 004/195] feat: hinted mul integration --- src/bn254/fp254impl.rs | 71 +++++++-- src/bn254/fq.rs | 79 +++++++--- src/bn254/fq12.rs | 171 +++++++++++++++++++++- src/bn254/fq2.rs | 75 +++++++++- src/bn254/fq6.rs | 318 +++++++++++++++++++++++++++++++++++++++++ src/bn254/utils.rs | 237 +++++++++++++++++++++++++++--- 6 files changed, 894 insertions(+), 57 deletions(-) diff --git a/src/bn254/fp254impl.rs b/src/bn254/fp254impl.rs index af17744cd..06965c8db 100644 --- a/src/bn254/fp254impl.rs +++ b/src/bn254/fp254impl.rs @@ -6,12 +6,17 @@ use crate::bigint::u29x9::{u29x9_mul_karazuba, u29x9_mul_karazuba_imm, u29x9_mul use crate::pseudo::OP_256MUL; use crate::treepp::*; use ark_ff::{BigInteger, PrimeField}; +use bitcoin::opcodes::all::{OP_1SUB, OP_DEPTH, OP_PICK}; use bitcoin_script::script; -use num_bigint::BigUint; +use num_bigint::{BigInt, BigUint}; use num_traits::{Num, One}; use std::ops::{Add, Div, Mul, Rem, Shl}; +use std::str::FromStr; use std::sync::OnceLock; +use super::fq::Fq; +use super::utils::Hint; + pub trait Fp254Impl { const MODULUS: &'static str; const MONTGOMERY_ONE: &'static str; @@ -46,10 +51,8 @@ pub trait Fp254Impl { #[inline] fn push_u32_le(v: &[u32]) -> Script { - let r = BigUint::from_str_radix(Self::MONTGOMERY_ONE, 16).unwrap(); - let p = BigUint::from_str_radix(Self::MODULUS, 16).unwrap(); script! { - { U254::push_u32_le(&BigUint::from_slice(v).mul(r).rem(p).to_u32_digits()) } + { U254::push_u32_le(&BigUint::from_slice(v).to_u32_digits()) } } } @@ -62,20 +65,16 @@ pub trait Fp254Impl { #[inline] fn push_dec(dec_string: &str) -> Script { let v = BigUint::from_str_radix(dec_string, 10).unwrap(); - let r = BigUint::from_str_radix(Self::MONTGOMERY_ONE, 16).unwrap(); - let p = BigUint::from_str_radix(Self::MODULUS, 16).unwrap(); script! { - { U254::push_u32_le(&v.mul(r).rem(p).to_u32_digits()) } + { U254::push_u32_le(&v.to_u32_digits()) } } } #[inline] fn push_hex(hex_string: &str) -> Script { let v = BigUint::from_str_radix(hex_string, 16).unwrap(); - let r = BigUint::from_str_radix(Self::MONTGOMERY_ONE, 16).unwrap(); - let p = BigUint::from_str_radix(Self::MODULUS, 16).unwrap(); script! { - { U254::push_u32_le(&v.mul(r).rem(p).to_u32_digits()) } + { U254::push_u32_le(&v.to_u32_digits()) } } } @@ -98,7 +97,7 @@ pub trait Fp254Impl { fn push_zero() -> Script { U254::push_zero() } #[inline] - fn push_one() -> Script { U254::push_hex(Self::MONTGOMERY_ONE) } + fn push_one() -> Script { U254::push_one() } fn decode_montgomery() -> Script { script! { @@ -484,6 +483,56 @@ pub trait Fp254Impl { } } + fn hinted_mul(mut a_depth: u32, a: ark_bn254::Fq, mut b_depth: u32, b: ark_bn254::Fq) -> (Script, Vec) { + assert_ne!(a_depth, b_depth); + if a_depth > b_depth { + (a_depth, b_depth) = (b_depth, a_depth); + } + + let mut hints = Vec::new(); + let x = BigInt::from_str(&a.to_string()).unwrap(); + let y = BigInt::from_str(&b.to_string()).unwrap(); + let modulus = &Fq::modulus_as_bigint(); + let q = (x * y) / modulus; + + let script = script!{ + for _ in 0..Self::N_LIMBS { + OP_DEPTH OP_1SUB OP_ROLL // hints + } + { Fq::roll(a_depth + 1) } + { Fq::roll(b_depth + 1) } + { Fq::tmul() } + }; + hints.push(Hint::Fq(ark_bn254::Fq::from_str(&q.to_string()).unwrap())); + + (script, hints) + } + + fn hinted_mul_keep_element(mut a_depth: u32, a: ark_bn254::Fq, mut b_depth: u32, b: ark_bn254::Fq) -> (Script, Vec) { + assert_ne!(a_depth, b_depth); + if a_depth > b_depth { + (a_depth, b_depth) = (b_depth, a_depth); + } + + let mut hints = Vec::new(); + let x = BigInt::from_str(&a.to_string()).unwrap(); + let y = BigInt::from_str(&b.to_string()).unwrap(); + let modulus = &Fq::modulus_as_bigint(); + let q = (x * y) / modulus; + + let script = script!{ + for _ in 0..Self::N_LIMBS { + OP_DEPTH OP_1SUB OP_ROLL // hints + } + { Fq::copy(a_depth + 1) } + { Fq::copy(b_depth + 2) } + { Fq::tmul() } + }; + hints.push(Hint::Fq(ark_bn254::Fq::from_str(&q.to_string()).unwrap())); + + (script, hints) + } + fn mul() -> Script { Self::MUL_ONCELOCK.get_or_init(|| { script! { diff --git a/src/bn254/fq.rs b/src/bn254/fq.rs index 1e2ac0512..083944745 100644 --- a/src/bn254/fq.rs +++ b/src/bn254/fq.rs @@ -5,6 +5,7 @@ use crate::bigint::BigIntImpl; use crate::bn254::fp254impl::Fp254Impl; use crate::pseudo::NMUL; use crate::treepp::*; +use crate::bigint::U254; pub struct Fq; @@ -40,16 +41,40 @@ impl Fp254Impl for Fq { impl Fq { - fn modulus_as_bigint() -> BigInt { + pub fn modulus_as_bigint() -> BigInt { BigInt::from_str_radix(Self::MODULUS, 16).unwrap() } + + pub fn tmul() -> Script { + script!{ + { ::tmul() } + } + } + +} + +pub fn bigint_to_u32_limbs(n: BigInt, n_bits: u32) -> Vec { + const limb_size: u64 = 32; + let mut limbs = vec![]; + let mut limb: u32 = 0; + for i in 0..n_bits as u64 { + if i > 0 && i % limb_size == 0 { + limbs.push(limb); + limb = 0; + } + if n.bit(i) { + limb += 1 << (i % limb_size); + } + } + limbs.push(limb); + limbs } macro_rules! fp_lc_mul { ($NAME:ident, $MOD_WIDTH:literal, $VAR_WIDTH:literal, $LCS:expr) => { paste::paste! { trait [] { - const LIMB_SIZE: u32 = 30; + const LIMB_SIZE: u32 = 29; const LCS: [bool; $LCS.len()] = $LCS; const LC_BITS: u32 = usize::BITS - $LCS.len().leading_zeros() - 1; type U; @@ -58,6 +83,7 @@ macro_rules! fp_lc_mul { } impl [] for Fq { + type U = BigIntImpl<{ Self::N_BITS }, { ]>::LIMB_SIZE }>; type T = BigIntImpl<{ Self::N_BITS + $VAR_WIDTH + ]>::LC_BITS + 1 }, { ]>::LIMB_SIZE }>; @@ -303,17 +329,18 @@ macro_rules! fp_lc_mul { }; } -fp_lc_mul!(Mul, 4, 4, [true]); +fp_lc_mul!(Mul, 3, 3, [true]); fp_lc_mul!(Mul2LC, 3, 3, [true, true]); #[cfg(test)] mod test { - use crate::bn254::fq::Fq; + use crate::bn254::{fq::Fq, utils::fq_push}; use crate::bn254::fp254impl::Fp254Impl; use crate::bigint::U254; use crate::treepp::*; use ark_ff::{BigInteger, Field, PrimeField}; use ark_std::UniformRand; + use std::str::FromStr; use core::ops::{Add, Mul, Rem, Sub}; use num_bigint::{BigInt, BigUint, RandBigInt, RandomBits}; @@ -759,23 +786,39 @@ mod test { bools } - fn bigint_to_u32_limbs(n: BigInt, n_bits: u32) -> Vec { - const limb_size: u64 = 32; - let mut limbs = vec![]; - let mut limb: u32 = 0; - for i in 0..n_bits as u64 { - if i > 0 && i % limb_size == 0 { - limbs.push(limb); - limb = 0; - } - if n.bit(i) { - limb += 1 << (i % limb_size); - } + #[test] + fn test_hinted_mul() { + let mut prng: ChaCha20Rng = ChaCha20Rng::seed_from_u64(0); + + let mut max_stack = 0; + + for _ in 0..100 { + let a = ark_bn254::Fq::rand(&mut prng); + let b = ark_bn254::Fq::rand(&mut prng); + let c = a.mul(&b); + + let (hinted_mul, hints) = Fq::hinted_mul(1, a, 0, b); + + let script = script! { + for hint in hints { + { hint.push() } + } + { fq_push(a) } + { fq_push(b) } + { hinted_mul.clone() } + { fq_push(c) } + { Fq::equal(0, 1) } + }; + let res = execute_script(script); + assert!(res.success); + + max_stack = max_stack.max(res.stats.max_nb_stack_items); + println!("Fq::window_mul: {} @ {} stack", hinted_mul.len(), max_stack); } - limbs.push(limb); - limbs + } + #[test] fn test_windowed_mul() { type U = ::U; diff --git a/src/bn254/fq12.rs b/src/bn254/fq12.rs index b8a06cf00..f881d0e81 100644 --- a/src/bn254/fq12.rs +++ b/src/bn254/fq12.rs @@ -4,11 +4,14 @@ use crate::bn254::fq2::Fq2; use crate::bn254::fq6::Fq6; use crate::bn254::fr::Fr; use crate::treepp::{script, Script}; -use ark_ff::Fp12Config; +use ark_bn254::fq2; +use ark_ff::{Field, Fp12Config}; use num_bigint::BigUint; use num_traits::{Num, Zero}; use std::ops::{ShrAssign, Sub}; +use super::utils::Hint; + pub struct Fq12; impl Fq12 { @@ -103,6 +106,41 @@ impl Fq12 { } } + pub fn hinted_mul(mut a_depth: u32, a: ark_bn254::Fq12, mut b_depth: u32, b: ark_bn254::Fq12) -> (Script, Vec) { + if a_depth < b_depth { + (a_depth, b_depth) = (b_depth, a_depth); + } + assert_ne!(a_depth, b_depth); + let mut hints = Vec::new(); + + let (hinted_script1, hint1) = Fq6::hinted_mul(6, a.c0, 0, b.c0); + let (hinted_script2, hint2) = Fq6::hinted_mul(6, a.c1, 0, b.c1); + let (hinted_script3, hint3) = Fq6::hinted_mul(6, a.c0+a.c1, 0, b.c0+b.c1); + + let script = script! { + { Fq6::copy(a_depth + 6) } + { Fq6::copy(b_depth + 12) } + { hinted_script1 } + { Fq6::copy(a_depth + 6) } + { Fq6::copy(b_depth + 12) } + { hinted_script2 } + { Fq6::add(a_depth + 12, a_depth + 18) } + { Fq6::add(b_depth + 18, b_depth + 24) } + { hinted_script3 } + { Fq6::copy(12) } + { Fq6::copy(12) } + { Fq12::mul_fq6_by_nonresidue() } + { Fq6::add(6, 0) } + { Fq6::add(18, 12)} + { Fq6::sub(12, 0) } + }; + hints.extend(hint1); + hints.extend(hint2); + hints.extend(hint3); + + (script, hints) + } + pub fn mul_cpt(mut a: u32, mut b: u32) -> Script { if a < b { (a, b) = (b, a); @@ -308,6 +346,64 @@ impl Fq12 { } } + pub fn hinted_mul_by_34(p: ark_bn254::Fq12, c3: ark_bn254::Fq2, c4: ark_bn254::Fq2) -> (Script, Vec) { + let mut hints = Vec::new(); + + let (hinted_script1, hint1) = Fq6::hinted_mul_by_01(p.c1, c3, c4); + let (hinted_script2, hint2) = Fq6::hinted_mul_by_01(p.c0+p.c1, c3+ark_bn254::Fq2::ONE, c4); + let script = script! { + // copy p.c1, c3, c4 + { Fq6::copy(4) } + { Fq2::copy(8) } + { Fq2::copy(8) } + // [p, c3, c4, p.c1, c3, c4] + + // compute b = p.c1 * (c3, c4) + { hinted_script1 } + // [p, c3, c4, b] + + // a = p.c0 * c0, where c0 = 1 + { Fq6::copy(16) } + // [p, c3, c4, b, a] + + // compute beta * b + { Fq6::copy(6) } + { Fq12::mul_fq6_by_nonresidue() } + // [p, c3, c4, b, a, beta * b] + + // compute final c0 = a + beta * b + { Fq6::copy(6) } + { Fq6::add(6, 0) } + // [p, c3, c4, b, a, c0] + + // compute e = p.c0 + p.c1 + { Fq6::add(28, 22) } + // [c3, c4, b, a, c0, e] + + // compute c0 + c3, where c0 = 1 + { Fq2::roll(26) } + { Fq2::push_one() } + { Fq2::add(2, 0) } + // [c4, b, a, c0, e, 1 + c3] + + // update e = e * (c0 + c3, c4), where c0 = 1 + { Fq2::roll(26) } + {hinted_script2 } + // [b, a, c0, e] + + // sum a and b + { Fq6::add(18, 12) } + // [c0, e, a + b] + + // compute final c1 = e - (a + b) + { Fq6::sub(6, 0) } + }; + hints.extend(hint1); + hints.extend(hint2); + + (script, hints) + } + pub fn copy(a: u32) -> Script { script! { { Fq6::copy(a + 6) } @@ -615,7 +711,7 @@ mod test { use crate::bn254::fp254impl::Fp254Impl; use crate::bn254::fq::Fq; use crate::bn254::fq12::Fq12; - use crate::treepp::*; + use crate::{execute_script_without_stack_limit, treepp::*}; use ark_ff::AdditiveGroup; use ark_ff::{CyclotomicMultSubgroup, Field}; use ark_std::UniformRand; @@ -703,11 +799,80 @@ mod test { { Fq12::equalverify() } OP_TRUE }; - let exec_result = execute_script(script); + let exec_result = execute_script_without_stack_limit(script); assert!(exec_result.success); } } + #[test] + fn test_bn254_fq12_hinted_mul() { + let mut prng: ChaCha20Rng = ChaCha20Rng::seed_from_u64(0); + + let mut max_stack = 0; + + for _ in 0..100 { + let a = ark_bn254::Fq12::rand(&mut prng); + let b = ark_bn254::Fq12::rand(&mut prng); + let c = a.mul(&b); + + let (hinted_mul, hints) = Fq12::hinted_mul(12, a, 0, b); + + let script = script! { + for hint in hints { + { hint.push() } + } + { fq12_push(a) } + { fq12_push(b) } + { hinted_mul.clone() } + { fq12_push(c) } + { Fq12::equalverify() } + OP_TRUE + }; + let res = execute_script(script); + assert!(res.success); + + max_stack = max_stack.max(res.stats.max_nb_stack_items); + println!("Fq6::window_mul: {} @ {} stack", hinted_mul.len(), max_stack); + } + + } + + #[test] + fn test_bn254_fq12_hinted_mul_by_34() { + let mut prng: ChaCha20Rng = ChaCha20Rng::seed_from_u64(0); + + let mut max_stack = 0; + + for _ in 0..100 { + let a = ark_bn254::Fq12::rand(&mut prng); + let c0 = ark_bn254::Fq2::ONE; + let c3 = ark_bn254::Fq2::rand(&mut prng); + let c4 = ark_bn254::Fq2::rand(&mut prng); + let mut b = a; + b.mul_by_034(&c0, &c3, &c4); + let (hinted_mul, hints) = Fq12::hinted_mul_by_34(a, c3, c4); + + let script = script! { + for hint in hints { + { hint.push() } + } + { fq12_push(a) } + { fq2_push(c3) } + { fq2_push(c4) } + { hinted_mul.clone() } + { fq12_push(b) } + { Fq12::equalverify() } + OP_TRUE + }; + let res = execute_script(script); + assert!(res.success); + + max_stack = max_stack.max(res.stats.max_nb_stack_items); + println!("Fq6::window_mul: {} @ {} stack", hinted_mul.len(), max_stack); + } + + } + #[test] fn test_bn254_fq12_mul_cpt() { println!("Fq12.mul_cpt: {} bytes", Fq12::mul_cpt(12, 0).len()); diff --git a/src/bn254/fq2.rs b/src/bn254/fq2.rs index d1296840f..6c2a23089 100644 --- a/src/bn254/fq2.rs +++ b/src/bn254/fq2.rs @@ -2,7 +2,13 @@ use crate::bn254::fp254impl::Fp254Impl; use crate::bn254::fq::Fq; use crate::treepp::{script, Script}; use ark_ff::Fp2Config; +use num_bigint::BigInt; use std::ops::Add; +use std::str::FromStr; + +use utils::Hint; + +use super::utils; pub struct Fq2; @@ -102,6 +108,36 @@ impl Fq2 { } } + pub fn hinted_mul(mut a_depth: u32, a: ark_bn254::Fq2, mut b_depth: u32, b: ark_bn254::Fq2) -> (Script, Vec) { + if a_depth > b_depth { + (a_depth, b_depth) = (b_depth, a_depth); + } + assert_ne!(a_depth, b_depth); + let mut hints = Vec::new(); + + let (hinted_script1, hint1) = Fq::hinted_mul_keep_element(a_depth + 1, a.c0, b_depth + 1, b.c0); + let (hinted_script2, hint2) = Fq::hinted_mul_keep_element(a_depth + 1, a.c1, b_depth + 1, b.c1); + let (hinted_script3, hint3) = Fq::hinted_mul(1, a.c0+a.c1, 0, b.c0+b.c1); + + let script = script! { + { hinted_script1 } + { hinted_script2 } + { Fq::add(a_depth + 2, a_depth + 3) } + { Fq::add(b_depth + 1, b_depth + 2) } + { hinted_script3 } + { Fq::copy(2) } + { Fq::copy(2) } + { Fq::sub(1, 0) } + { Fq::add(3, 2) } + { Fq::sub(2, 0) } + }; + hints.extend(hint1); + hints.extend(hint2); + hints.extend(hint3); + + (script, hints) + } + pub fn mul_by_fq(mut a: u32, b: u32) -> Script { if a < b { a += 1; @@ -246,17 +282,19 @@ impl Fq2 { #[cfg(test)] mod test { - use crate::bn254::fq::Fq; + use crate::bn254::fq::{bigint_to_u32_limbs, Fq}; use crate::bn254::fq2::Fq2; use crate::bn254::{fp254impl::Fp254Impl, utils::fq2_push}; use crate::treepp::*; use ark_ff::Field; use ark_std::UniformRand; + use bitcoin::opcodes::OP_TRUE; use core::ops::{Add, Mul}; - use num_bigint::BigUint; + use num_bigint::{BigInt, BigUint, RandBigInt}; use rand::SeedableRng; use rand_chacha::ChaCha20Rng; use ark_ff::AdditiveGroup; + use std::str::FromStr; #[test] fn test_bn254_fq2_add() { @@ -347,6 +385,39 @@ mod test { } } + #[test] + fn test_bn254_fq2_hinted_mul() { + let mut prng: ChaCha20Rng = ChaCha20Rng::seed_from_u64(0); + + let mut max_stack = 0; + + for _ in 0..100 { + let a = ark_bn254::Fq2::rand(&mut prng); + let b = ark_bn254::Fq2::rand(&mut prng); + let c = a.mul(&b); + + let (hinted_mul, hints) = Fq2::hinted_mul(2, a, 0, b); + + let script = script! { + for hint in hints { + { hint.push() } + } + { fq2_push(a) } + { fq2_push(b) } + { hinted_mul.clone() } + { fq2_push(c) } + { Fq2::equalverify() } + OP_TRUE + }; + let res = execute_script(script); + assert!(res.success); + + max_stack = max_stack.max(res.stats.max_nb_stack_items); + println!("Fq2::window_mul: {} @ {} stack", hinted_mul.len(), max_stack); + } + + } + #[test] fn test_bn254_fq2_mul() { println!("Fq2.mul: {} bytes", Fq2::mul(1, 0).len()); diff --git a/src/bn254/fq6.rs b/src/bn254/fq6.rs index 6612d1d53..d58c4037e 100644 --- a/src/bn254/fq6.rs +++ b/src/bn254/fq6.rs @@ -5,6 +5,8 @@ use crate::treepp::{script, Script}; use ark_ff::Fp6Config; use num_bigint::BigUint; +use super::utils::Hint; + pub struct Fq6; impl Fq6 { @@ -271,6 +273,171 @@ impl Fq6 { } } + pub fn hinted_mul(mut a_depth: u32, a: ark_bn254::Fq6, mut b_depth: u32, b: ark_bn254::Fq6) -> (Script, Vec) { + if a_depth < b_depth { + (a_depth, b_depth) = (b_depth, a_depth); + } + assert_ne!(a_depth, b_depth); + let mut hints = Vec::new(); + + let (hinted_script1, hint1) = Fq2::hinted_mul(2, a.c0, 0, b.c0); + let (hinted_script2, hint2) = Fq2::hinted_mul(6, a.c0+a.c1+a.c2, 2, b.c0+b.c1+b.c2); + let (hinted_script3, hint3) = Fq2::hinted_mul(4, a.c0-a.c1+a.c2, 2, b.c0-b.c1+b.c2); + let (hinted_script4, hint4) = Fq2::hinted_mul(2, a.c0+a.c1+a.c1+a.c2+a.c2+a.c2+a.c2, + 0, b.c0+b.c1+b.c1+b.c2+b.c2+b.c2+b.c2); + let (hinted_script5, hint5) = Fq2::hinted_mul(2, a.c2, 0, b.c2); + + let script = script! { + // compute ad = P(0) + { Fq2::copy(b_depth + 4) } + { Fq2::copy(a_depth + 6) } + { hinted_script1 } + + // compute a+c + { Fq2::copy(a_depth + 6) } + { Fq2::copy(a_depth + 4) } + { Fq2::add(2, 0) } + + // compute a+b+c, a-b+c + { Fq2::copy(0) } + { Fq2::copy(a_depth + 8) } + { Fq2::copy(0) } + { Fq2::add(4, 0) } + { Fq2::sub(4, 2) } + + // compute d+f + { Fq2::copy(b_depth + 10) } + { Fq2::copy(b_depth + 8) } + { Fq2::add(2, 0) } + + // compute d+e+f, d-e+f + { Fq2::copy(0) } + { Fq2::copy(b_depth + 12) } + { Fq2::copy(0) } + { Fq2::add(4, 0) } + { Fq2::sub(4, 2) } + + // compute (a+b+c)(d+e+f) = P(1) + { hinted_script2 } + + // compute (a-b+c)(d-e+f) = P(-1) + { hinted_script3 } + + // compute 2b + { Fq2::roll(a_depth + 8) } + { Fq2::double(0) } + + // compute 4c + { Fq2::copy(a_depth + 8) } + { Fq2::double(0) } + { Fq2::double(0) } + + // compute a+2b+4c + { Fq2::add(2, 0) } + { Fq2::roll(a_depth + 10) } + { Fq2::add(2, 0) } + + // compute 2e + { Fq2::roll(b_depth + 10) } + { Fq2::double(0) } + + // compute 4f + { Fq2::copy(b_depth + 10) } + { Fq2::double(0) } + { Fq2::double(0) } + + // compute d+2e+4f + { Fq2::add(2, 0) } + { Fq2::roll(b_depth + 12) } + { Fq2::add(2, 0) } + + // compute (a+2b+4c)(d+2e+4f) = P(2) + { hinted_script4 } + + // compute cf = P(inf) + { Fq2::roll(b_depth + 8) } + { Fq2::roll(a_depth + 4) } + { hinted_script5 } + + // // at this point, we have v_0, v_1, v_2, v_3, v_4 + + // compute 3v_0 + { Fq2::triple(8) } + + // compute 3v_1 + { Fq2::triple(8) } + + // compute 6v_4 + { Fq2::triple(4) } + { Fq2::double(0) } + + // compute x = 3v_0 - 3v_1 - v_2 + v_3 - 12v_4 + { Fq2::copy(4) } + { Fq2::copy(4) } + { Fq2::sub(2, 0) } + { Fq2::copy(10) } + { Fq2::sub(2, 0) } + { Fq2::copy(8) } + { Fq2::add(2, 0) } + { Fq2::copy(2) } + { Fq2::double(0) } + { Fq2::sub(2, 0) } + + // compute c_0 = 6v_0 + \beta x + { Fq6::mul_fq2_by_nonresidue() } + { Fq2::copy(6) } + { Fq2::double(0) } + { Fq2::add(2, 0) } + + // compute y = -3v_0 + 6v_1 - 2v_2 - v_3 + 12v_4 + { Fq2::copy(4) } + { Fq2::double(0) } + { Fq2::copy(8) } + { Fq2::sub(2, 0) } + { Fq2::copy(12) } + { Fq2::double(0) } + { Fq2::sub(2, 0) } + { Fq2::roll(10) } + { Fq2::sub(2, 0) } + { Fq2::copy(4) } + { Fq2::double(0) } + { Fq2::add(2, 0) } + + // compute c_1 = y + \beta 6v_4 + { Fq2::copy(4) } + { Fq6::mul_fq2_by_nonresidue() } + { Fq2::add(2, 0) } + + // compute c_2 = 3v_1 - 6v_0 + 3v_2 - 6v_4 + { Fq2::roll(6) } + { Fq2::roll(8) } + { Fq2::double(0) } + { Fq2::sub(2, 0) } + { Fq2::roll(8) } + { Fq2::triple(0) } + { Fq2::add(2, 0) } + { Fq2::sub(0, 6) } + + // divide by 6 + { Fq2::roll(4) } + { Fq2::div2() } + { Fq2::div3() } + { Fq2::roll(4) } + { Fq2::div2() } + { Fq2::div3() } + { Fq2::roll(4) } + { Fq2::div2() } + { Fq2::div3() } + }; + hints.extend(hint1); + hints.extend(hint2); + hints.extend(hint3); + hints.extend(hint4); + hints.extend(hint5); + + (script, hints) + } + // input: // p.c0 (2 elements) // p.c1 (2 elements) @@ -344,6 +511,88 @@ impl Fq6 { } } + pub fn hinted_mul_by_01(p: ark_bn254::Fq6, c0: ark_bn254::Fq2, c1: ark_bn254::Fq2) -> (Script, Vec) { + let mut hints = Vec::new(); + + let (hinted_script1, hint1) = Fq2::hinted_mul(2, p.c0, 0, c0); + let (hinted_script2, hint2) = Fq2::hinted_mul(2, p.c1, 0, c1); + let (hinted_script3, hint3) = Fq2::hinted_mul(2, c1, 0, p.c1+p.c2); + let (hinted_script4, hint4) = Fq2::hinted_mul(2, c0+c1, 0, p.c0+p.c1); + let (hinted_script5, hint5) = Fq2::hinted_mul(10, c0, 0, p.c0+p.c2); + let script = script! { + // compute a_a = p.c0 * c0 + { Fq2::copy(8) } + { Fq2::copy(4) } + { hinted_script1 } + + // compute b_b = p.c1 * c1 + { Fq2::copy(8) } + { Fq2::copy(4) } + { hinted_script2 } + + // compute tmp = p.c1 + p.c2 + { Fq2::copy(10) } + { Fq2::copy(10) } + { Fq2::add(2, 0) } + + // t1 = c1 * tmp + { Fq2::copy(6) } + { hinted_script3 } + + // t1 = t1 - b_b + { Fq2::copy(2) } + { Fq2::sub(2, 0) } + + // t1 = t1 * nonresidue + { Fq6::mul_fq2_by_nonresidue() } + + // t1 = t1 + a_a + { Fq2::copy(4) } + { Fq2::add(2, 0) } + + // compute tmp = p.c0 + p.c1 + { Fq2::copy(14) } + { Fq2::roll(14) } + { Fq2::add(2, 0) } + + // t2 = c0 + c1 + { Fq2::copy(10) } + { Fq2::roll(10) } + { Fq2::add(2, 0) } + + // t2 = t2 * tmp + { hinted_script4 } + + // t2 = t2 - a_a + { Fq2::copy(6) } + { Fq2::sub(2, 0) } + + // t2 = t2 - b_b + { Fq2::copy(4) } + { Fq2::sub(2, 0) } + + // compute tmp = p.c0 + p.c2 + { Fq2::add(12, 10) } + + // t3 = c0 * tmp + { hinted_script5 } + + // t3 = t3 - a_a + { Fq2::sub(0, 8) } + + // t3 = t3 + b_b + { Fq2::add(0, 6) } + }; + hints.extend(hint1); + hints.extend(hint2); + hints.extend(hint3); + hints.extend(hint4); + hints.extend(hint5); + + (script, hints) + + } + // input: // p.c0 (2 elements) // p.c1 (2 elements) @@ -699,6 +948,75 @@ mod test { } } + #[test] + fn test_bn254_fq6_hinted_mul() { + let mut prng: ChaCha20Rng = ChaCha20Rng::seed_from_u64(0); + + let mut max_stack = 0; + + for _ in 0..100 { + let a = ark_bn254::Fq6::rand(&mut prng); + let b = ark_bn254::Fq6::rand(&mut prng); + let c = a.mul(&b); + + let (hinted_mul, hints) = Fq6::hinted_mul(6, a, 0, b); + + let script = script! { + for hint in hints { + { hint.push() } + } + { fq6_push(a) } + { fq6_push(b) } + { hinted_mul.clone() } + { fq6_push(c) } + { Fq6::equalverify() } + OP_TRUE + }; + let res = execute_script(script); + assert!(res.success); + + max_stack = max_stack.max(res.stats.max_nb_stack_items); + println!("Fq6::window_mul: {} @ {} stack", hinted_mul.len(), max_stack); + } + + } + + #[test] + fn test_bn254_fq6_hinted_mul_by_01() { + let mut prng: ChaCha20Rng = ChaCha20Rng::seed_from_u64(0); + + let mut max_stack = 0; + + for _ in 0..100 { + let a = ark_bn254::Fq6::rand(&mut prng); + let c0 = ark_bn254::Fq2::rand(&mut prng); + let c1 = ark_bn254::Fq2::rand(&mut prng); + let mut b = a; + b.mul_by_01(&c0, &c1); + + let (hinted_mul, hints) = Fq6::hinted_mul_by_01(a, c0, c1); + + let script = script! { + for hint in hints { + { hint.push() } + } + { fq6_push(a) } + { fq2_push(c0) } + { fq2_push(c1) } + { hinted_mul.clone() } + { fq6_push(b) } + { Fq6::equalverify() } + OP_TRUE + }; + let res = execute_script(script); + assert!(res.success); + + max_stack = max_stack.max(res.stats.max_nb_stack_items); + println!("Fq6::window_mul: {} @ {} stack", hinted_mul.len(), max_stack); + } + + } + #[test] fn test_bn254_fq6_mul() { println!("Fq6.mul: {} bytes", Fq6::mul(6, 0).len()); diff --git a/src/bn254/utils.rs b/src/bn254/utils.rs index 4958a333d..157e08549 100644 --- a/src/bn254/utils.rs +++ b/src/bn254/utils.rs @@ -11,6 +11,49 @@ use crate::{ treepp::*, }; +pub fn fq_push(element: ark_bn254::Fq) -> Script { + script! { + { Fq::push_u32_le(&BigUint::from(element).to_u32_digits()) } + } +} + +pub fn fq2_push(element: ark_bn254::Fq2) -> Script { + script! { + { Fq::push_u32_le(&BigUint::from(element.c0).to_u32_digits()) } + { Fq::push_u32_le(&BigUint::from(element.c1).to_u32_digits()) } + } +} + +pub fn fq6_push(element: ark_bn254::Fq6) -> Script { + script! { + for elem in element.to_base_prime_field_elements() { + { Fq::push_u32_le(&BigUint::from(elem).to_u32_digits()) } + } + } +} + +pub fn fq12_push(element: ark_bn254::Fq12) -> Script { + script! { + for elem in element.to_base_prime_field_elements() { + { Fq::push_u32_le(&BigUint::from(elem).to_u32_digits()) } + } + } +} + +pub enum Hint { + Fq(ark_bn254::Fq), +} + +impl Hint { + pub fn push(&self) -> Script { + match self { + Hint::Fq(fq) => script! { + { fq_push(*fq) } + }, + } + } +} + // input: // f 12 elements // coeffs.c0 2 elements @@ -111,6 +154,62 @@ pub fn ell_by_constant_affine(constant: &EllCoeff) -> Script { } } + +//TODO:: Implement mul_by_constant +pub fn hinted_ell_by_constant_affine(f: ark_bn254::Fq12, x: ark_bn254::Fq, y: ark_bn254::Fq, constant: &EllCoeff) -> (Script, Vec) { + assert_eq!(constant.0, ark_bn254::Fq2::ONE); + let mut hints = Vec::new(); + + let (hinted_script1, hint1) = Fq::hinted_mul(1, x, 0, constant.1.c0); + let (hinted_script2, hint2) = Fq::hinted_mul(1, x, 0, constant.1.c1); + let (hinted_script3, hint3) = Fq::hinted_mul(1, y, 0, constant.2.c0); + let (hinted_script4, hint4) = Fq::hinted_mul(1, y, 0, constant.2.c1); + let mut c1 = constant.1; + c1.mul_assign_by_fp(&x); + let mut c2 = constant.2; + c2.mul_assign_by_fp(&y); + let (hinted_script5, hint5) = Fq12::hinted_mul_by_34(f, c1, c2); + + let script = script! { + // [f, x', y'] + // update c1, c1' = x' * c1 + { Fq::copy(1) } + { fq_push(constant.1.c0) } + { hinted_script1 } // { Fq::mul_by_constant(&constant.1.c0) } + + // [f, x', y', x' * c1.0] + { Fq::roll(2) } + { fq_push(constant.1.c1) } + { hinted_script2 } // { Fq::mul_by_constant(&constant.1.c1) } + // [f, y', x' * c1.0, x' * c1.1] + // [f, y', x' * c1] + + // update c2, c2' = -y' * c2 + { Fq::copy(2) } + { fq_push(constant.2.c0) } + { hinted_script3 } // { Fq::mul_by_constant(&constant.2.c0) } + // [f, y', x' * c1, y' * c2.0] + { Fq::roll(3) } + { fq_push(constant.2.c1) } + { hinted_script4 } // { Fq::mul_by_constant(&constant.2.c1) } + // [f, x' * c1, y' * c2.0, y' * c2.1] + // [f, x' * c1, y' * c2] + // [f, c1', c2'] + + // compute the new f with c1'(c3) and c2'(c4), where c1 is trival value 1 + { hinted_script5 } + // [f] + }; + hints.extend(hint1); + hints.extend(hint2); + hints.extend(hint3); + hints.extend(hint4); + hints.extend(hint5); + + (script, hints) + +} + pub fn collect_line_coeffs( constants: Vec, ) -> Vec>> { @@ -200,6 +299,51 @@ pub fn from_eval_point(p: ark_bn254::G1Affine) -> Script { } } +/// input of func (params): +/// p.x, p.y +/// output on stack: +/// x' = -p.x / p.y +/// y' = 1 / p.y +pub fn hinted_from_eval_point(p: ark_bn254::G1Affine) -> (Script, Vec) { + let mut hints = Vec::new(); + + let py_inv = p.y().unwrap().inverse().unwrap(); + + let (hinted_script1, hint1) = Fq::hinted_mul(2, py_inv, 0, p.y); + let (hinted_script2, hint2) = Fq::hinted_mul(2, py_inv, 0, -p.x); + let script = script! { + { Fq::push_u32_le(&BigUint::from(py_inv).to_u32_digits()) } + // [1/y] + // check p.y.inv() is valid + { Fq::copy(0) } + // [1/y, 1/y] + { Fq::push_u32_le(&BigUint::from(p.y).to_u32_digits()) } + // [1/y, 1/y, y] + { hinted_script1 } + // [1/y, 1] + { Fq::push_one() } + // [1/y, 1, 1] + { Fq::equalverify(1, 0) } + // [1/y] + + // -p.x / p.y + { Fq::copy(0) } + // [1/y, 1/y] + { Fq::push_u32_le(&BigUint::from(p.x).to_u32_digits()) } + // [1/y, 1/y, x] + { Fq::neg(0) } + // [1/y, 1/y, -x] + { hinted_script2 } + // [1/y, -x/y] + { Fq::roll(1) } + // [-x/y, 1/y] + }; + hints.extend(hint1); + hints.extend(hint2); + + (script, hints) +} + /// input of stack: /// p.x, p.y (affine space) /// output on stack: @@ -238,29 +382,6 @@ pub fn from_eval_point_in_stack() -> Script { } } -pub fn fq2_push(element: ark_bn254::Fq2) -> Script { - script! { - { Fq::push_u32_le(&BigUint::from(element.c0).to_u32_digits()) } - { Fq::push_u32_le(&BigUint::from(element.c1).to_u32_digits()) } - } -} - -pub fn fq6_push(element: ark_bn254::Fq6) -> Script { - script! { - for elem in element.to_base_prime_field_elements() { - { Fq::push_u32_le(&BigUint::from(elem).to_u32_digits()) } - } - } -} - -pub fn fq12_push(element: ark_bn254::Fq12) -> Script { - script! { - for elem in element.to_base_prime_field_elements() { - { Fq::push_u32_le(&BigUint::from(elem).to_u32_digits()) } - } - } -} - /// add two points T and Q /// x' = alpha^2 - T.x - Q.x /// y' = -bias - alpha * x' @@ -934,6 +1055,56 @@ mod test { assert!(exec_result.success); } + #[test] + fn test_hinted_ell_by_constant_affine() { + let mut prng = ChaCha20Rng::seed_from_u64(0); + + let f = ark_bn254::Fq12::rand(&mut prng); + let b = ark_bn254::g2::G2Affine::rand(&mut prng); + let p = ark_bn254::g1::G1Affine::rand(&mut prng); + + // affine mode + let coeffs = G2Prepared::from_affine(b); + let (from_eval_point_script, hints_eval) = hinted_from_eval_point(p); + let (ell_by_constant_affine_script, hints) = hinted_ell_by_constant_affine(f, -p.x / p.y, p.y.inverse().unwrap(),&coeffs.ell_coeffs[0]); + println!( + "Pairing.ell_by_constant_affine: {} bytes", + ell_by_constant_affine_script.len() + ); + + // affine mode as well + let hint = { + assert_eq!(coeffs.ell_coeffs[0].0, ark_bn254::fq2::Fq2::ONE); + + let mut f1 = f; + let mut c1new = coeffs.ell_coeffs[0].1; + c1new.mul_assign_by_fp(&(-p.x / p.y)); + + let mut c2new = coeffs.ell_coeffs[0].2; + c2new.mul_assign_by_fp(&(p.y.inverse().unwrap())); + + f1.mul_by_034(&coeffs.ell_coeffs[0].0, &c1new, &c2new); + f1 + }; + + let script = script! { + for tmp in hints_eval { + { tmp.push() } + } + for tmp in hints { + { tmp.push() } + } + { fq12_push(f) } + { from_eval_point_script } + { ell_by_constant_affine_script.clone() } + { fq12_push(hint) } + { Fq12::equalverify() } + OP_TRUE + }; + let exec_result = execute_script(script); + assert!(exec_result.success); + } + #[test] fn test_from_eval_point() { let mut prng = ChaCha20Rng::seed_from_u64(0); @@ -950,6 +1121,26 @@ mod test { assert!(exec_result.success); } + #[test] + fn test_hinted_from_eval_point() { + let mut prng = ChaCha20Rng::seed_from_u64(0); + let p = ark_bn254::G1Affine::rand(&mut prng); + let (ell_by_constant_affine_script, hints) = hinted_from_eval_point(p); + let script = script! { + for tmp in hints { + { tmp.push() } + } + { ell_by_constant_affine_script.clone() } + { Fq::push_u32_le(&BigUint::from(-p.x / p.y).to_u32_digits()) } + { Fq::push_u32_le(&BigUint::from(p.y.inverse().unwrap()).to_u32_digits()) } + { Fq::equalverify(2, 0) } + { Fq::equalverify(1, 0) } + OP_TRUE + }; + let exec_result = execute_script(script); + assert!(exec_result.success); + } + #[test] fn test_affine_add_line() { // alpha = (t.y - q.y) / (t.x - q.x) From 61badf8085610b880275a1a00786e4258bf61d04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hakan=20Karaku=C5=9F?= Date: Mon, 9 Sep 2024 14:40:11 +0300 Subject: [PATCH 005/195] Fq.hinted_square --- src/bn254/fp254impl.rs | 17 +++++++++++++++++ src/bn254/fq.rs | 30 ++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/src/bn254/fp254impl.rs b/src/bn254/fp254impl.rs index 06965c8db..e7a05273e 100644 --- a/src/bn254/fp254impl.rs +++ b/src/bn254/fp254impl.rs @@ -643,6 +643,23 @@ pub trait Fp254Impl { } } + fn hinted_square(a: ark_bn254::Fq) -> (Script, Vec) { + let mut hints = Vec::new(); + let x = &BigInt::from_str(&a.to_string()).unwrap(); + let modulus = &Fq::modulus_as_bigint(); + let q = (x * x) / modulus; + let script = script! { + for _ in 0..Self::N_LIMBS { + OP_DEPTH OP_1SUB OP_ROLL // hints + } + { Fq::roll(1) } + { Fq::copy(0) } + { Fq::tmul() } + }; + hints.push(Hint::Fq(ark_bn254::Fq::from_str(&q.to_string()).unwrap())); + (script, hints) + } + fn inv() -> Script { let r = BigUint::from_str_radix(Self::MONTGOMERY_ONE, 16).unwrap(); let p = BigUint::from_str_radix(Self::MODULUS, 16).unwrap(); diff --git a/src/bn254/fq.rs b/src/bn254/fq.rs index 083944745..cd5549040 100644 --- a/src/bn254/fq.rs +++ b/src/bn254/fq.rs @@ -818,6 +818,36 @@ mod test { } + #[test] + fn test_hinted_square() { + let mut prng: ChaCha20Rng = ChaCha20Rng::seed_from_u64(0); + + let mut max_stack = 0; + + for _ in 0..100 { + let a = ark_bn254::Fq::rand(&mut prng); + let c = a.mul(&a); + + let (hinted_square, hints) = Fq::hinted_square(a); + + let script = script! { + for hint in hints { + { hint.push() } + } + { fq_push(a) } + { hinted_square.clone() } + { fq_push(c) } + { Fq::equal(0, 1) } + }; + let res = execute_script(script); + assert!(res.success); + + max_stack = max_stack.max(res.stats.max_nb_stack_items); + println!("Fq::hinted_square: {} @ {} stack", hinted_square.len(), max_stack); + } + + } + #[test] fn test_windowed_mul() { From a817f1b2c519c2015890552c9604f33c808bc605 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hakan=20Karaku=C5=9F?= Date: Mon, 9 Sep 2024 14:58:38 +0300 Subject: [PATCH 006/195] Fq2.hinted_square --- src/bn254/fq2.rs | 57 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/src/bn254/fq2.rs b/src/bn254/fq2.rs index 6c2a23089..41a5a2f1e 100644 --- a/src/bn254/fq2.rs +++ b/src/bn254/fq2.rs @@ -63,6 +63,32 @@ impl Fq2 { } } + pub fn hinted_square(a: ark_bn254::Fq2) -> (Script, Vec) { + let mut hints = Vec::new(); + let (hinted_script1, hint1) = Fq::hinted_mul_keep_element(1, a.c0, 0, a.c1); + let (hinted_script2, hint2) = Fq::hinted_mul(1, a.c0 - a.c1, 0, a.c0 + a.c1); + let script = script! { + // a0, a1 + { Fq::copy(1) } + { Fq::copy(1) } + // a0, a1, a0, a1 + { hinted_script1 } + // a0, a1, a0, a1, a0*a1 + { Fq::double(0) } + // a0, a1, a0, a1, 2*a0*a1 + { Fq::sub(2, 1) } + { Fq::add(3, 2) } + // 2*a0*a1, a0-a1, a0+a1 + { hinted_script2 } + // 2*a0*a1, a0^2-a1^2 + { Fq::roll(1) } + // a0^2-a1^2, 2*a0*a1 + }; + hints.extend(hint1); + hints.extend(hint2); + (script, hints) + } + pub fn copy(a: u32) -> Script { script! { { Fq::copy(a + 1) } @@ -507,6 +533,37 @@ mod test { } } + #[test] + fn test_bn254_fq2_hinted_square() { + let mut prng: ChaCha20Rng = ChaCha20Rng::seed_from_u64(0); + + let mut max_stack = 0; + + for _ in 0..100 { + let a = ark_bn254::Fq2::rand(&mut prng); + let c = a.mul(&a); + + let (hinted_square, hints) = Fq2::hinted_square(a); + + let script = script! { + for hint in hints { + { hint.push() } + } + { fq2_push(a) } + { hinted_square.clone() } + { fq2_push(c) } + { Fq2::equalverify() } + OP_TRUE + }; + let res = execute_script(script); + assert!(res.success); + + max_stack = max_stack.max(res.stats.max_nb_stack_items); + println!("Fq2::hinted_square: {} @ {} stack", hinted_square.len(), max_stack); + } + + } + #[test] fn test_bn254_fq2_div2() { println!("Fq2.div2: {} bytes", Fq2::div2().len()); From 6d2b4de257b7329c652ea3e934a174c7f3e30d58 Mon Sep 17 00:00:00 2001 From: fatih Date: Mon, 9 Sep 2024 14:59:16 +0300 Subject: [PATCH 007/195] feat: hinted mul by const --- src/bn254/fp254impl.rs | 28 ++++++++++++++++-- src/bn254/fq.rs | 66 +++++++++++++++++++++++++++++++++++++++++- src/bn254/fq12.rs | 3 +- src/bn254/fq2.rs | 3 +- src/bn254/fq6.rs | 3 +- src/bn254/utils.rs | 20 +++++-------- 6 files changed, 104 insertions(+), 19 deletions(-) diff --git a/src/bn254/fp254impl.rs b/src/bn254/fp254impl.rs index 06965c8db..348d723b4 100644 --- a/src/bn254/fp254impl.rs +++ b/src/bn254/fp254impl.rs @@ -3,6 +3,7 @@ use crate::bigint::bits::limb_to_be_bits; use crate::bigint::sub::limb_sub_borrow; use crate::bigint::U254; use crate::bigint::u29x9::{u29x9_mul_karazuba, u29x9_mul_karazuba_imm, u29x9_mulhi_karazuba_imm, u29x9_mullo_karazuba_imm, u29x9_square}; +use crate::bn254::utils::fq_push; use crate::pseudo::OP_256MUL; use crate::treepp::*; use ark_ff::{BigInteger, PrimeField}; @@ -483,10 +484,11 @@ pub trait Fp254Impl { } } - fn hinted_mul(mut a_depth: u32, a: ark_bn254::Fq, mut b_depth: u32, b: ark_bn254::Fq) -> (Script, Vec) { + fn hinted_mul(mut a_depth: u32, mut a: ark_bn254::Fq, mut b_depth: u32, mut b: ark_bn254::Fq) -> (Script, Vec) { assert_ne!(a_depth, b_depth); if a_depth > b_depth { (a_depth, b_depth) = (b_depth, a_depth); + (a, b) = (b, a); } let mut hints = Vec::new(); @@ -508,10 +510,32 @@ pub trait Fp254Impl { (script, hints) } - fn hinted_mul_keep_element(mut a_depth: u32, a: ark_bn254::Fq, mut b_depth: u32, b: ark_bn254::Fq) -> (Script, Vec) { + // TODO: Optimize by using constant feature + fn hinted_mul_by_constant(a_depth: u32, a: ark_bn254::Fq, constant: ark_bn254::Fq) -> (Script, Vec) { + let mut hints = Vec::new(); + let x = BigInt::from_str(&a.to_string()).unwrap(); + let y = BigInt::from_str(&constant.to_string()).unwrap(); + let modulus = &Fq::modulus_as_bigint(); + let q = (x * y) / modulus; + + let script = script!{ + for _ in 0..Self::N_LIMBS { + OP_DEPTH OP_1SUB OP_ROLL // hints + } + { Fq::roll(a_depth + 1) } + { fq_push(constant) } + { Fq::tmul() } + }; + hints.push(Hint::Fq(ark_bn254::Fq::from_str(&q.to_string()).unwrap())); + + (script, hints) + } + + fn hinted_mul_keep_element(mut a_depth: u32, mut a: ark_bn254::Fq, mut b_depth: u32, mut b: ark_bn254::Fq) -> (Script, Vec) { assert_ne!(a_depth, b_depth); if a_depth > b_depth { (a_depth, b_depth) = (b_depth, a_depth); + (a, b) = (b, a); } let mut hints = Vec::new(); diff --git a/src/bn254/fq.rs b/src/bn254/fq.rs index 083944745..f07e499be 100644 --- a/src/bn254/fq.rs +++ b/src/bn254/fq.rs @@ -340,6 +340,7 @@ mod test { use crate::treepp::*; use ark_ff::{BigInteger, Field, PrimeField}; use ark_std::UniformRand; + use bitcoin::opcodes::all::{OP_FROMALTSTACK, OP_TOALTSTACK}; use std::str::FromStr; use core::ops::{Add, Mul, Rem, Sub}; @@ -813,11 +814,74 @@ mod test { assert!(res.success); max_stack = max_stack.max(res.stats.max_nb_stack_items); - println!("Fq::window_mul: {} @ {} stack", hinted_mul.len(), max_stack); + println!("Fq::hinted_mul: {} @ {} stack", hinted_mul.len(), max_stack); } + } + + #[test] + fn test_hinted_mul_keep_element() { + let mut prng: ChaCha20Rng = ChaCha20Rng::seed_from_u64(0); + + let mut max_stack = 0; + + for _ in 0..100 { + let a = ark_bn254::Fq::rand(&mut prng); + let b = ark_bn254::Fq::rand(&mut prng); + let c = a.mul(&b); + + let (hinted_mul, hints) = Fq::hinted_mul_keep_element(1, a, 0, b); + + let script = script! { + for hint in hints { + { hint.push() } + } + { fq_push(a) } + { fq_push(b) } + { hinted_mul.clone() } + { fq_push(c) } + { Fq::equal(0, 1) } + OP_TOALTSTACK + { Fq::drop() } + { Fq::drop() } + OP_FROMALTSTACK + }; + let res = execute_script(script); + assert!(res.success); + max_stack = max_stack.max(res.stats.max_nb_stack_items); + println!("Fq::hinted_mul_keep_element: {} @ {} stack", hinted_mul.len(), max_stack); + } } + #[test] + fn test_hinted_mul_by_constant() { + let mut prng: ChaCha20Rng = ChaCha20Rng::seed_from_u64(0); + + let mut max_stack = 0; + + for _ in 0..100 { + let a = ark_bn254::Fq::rand(&mut prng); + let b = ark_bn254::Fq::rand(&mut prng); + let c = a.mul(&b); + + let (hinted_mul, hints) = Fq::hinted_mul_by_constant(0, a, b); + + let script = script! { + for hint in hints { + { hint.push() } + } + { fq_push(a) } + { hinted_mul.clone() } + { fq_push(c) } + { Fq::equal(0, 1) } + }; + let res = execute_script(script); + assert!(res.success); + + max_stack = max_stack.max(res.stats.max_nb_stack_items); + println!("Fq::hinted_mul_by_constant: {} @ {} stack", hinted_mul.len(), max_stack); + } + } #[test] fn test_windowed_mul() { diff --git a/src/bn254/fq12.rs b/src/bn254/fq12.rs index f881d0e81..c1eeb0330 100644 --- a/src/bn254/fq12.rs +++ b/src/bn254/fq12.rs @@ -106,9 +106,10 @@ impl Fq12 { } } - pub fn hinted_mul(mut a_depth: u32, a: ark_bn254::Fq12, mut b_depth: u32, b: ark_bn254::Fq12) -> (Script, Vec) { + pub fn hinted_mul(mut a_depth: u32, mut a: ark_bn254::Fq12, mut b_depth: u32, mut b: ark_bn254::Fq12) -> (Script, Vec) { if a_depth < b_depth { (a_depth, b_depth) = (b_depth, a_depth); + (a, b) = (b, a); } assert_ne!(a_depth, b_depth); let mut hints = Vec::new(); diff --git a/src/bn254/fq2.rs b/src/bn254/fq2.rs index 6c2a23089..23e673282 100644 --- a/src/bn254/fq2.rs +++ b/src/bn254/fq2.rs @@ -108,9 +108,10 @@ impl Fq2 { } } - pub fn hinted_mul(mut a_depth: u32, a: ark_bn254::Fq2, mut b_depth: u32, b: ark_bn254::Fq2) -> (Script, Vec) { + pub fn hinted_mul(mut a_depth: u32, mut a: ark_bn254::Fq2, mut b_depth: u32, mut b: ark_bn254::Fq2) -> (Script, Vec) { if a_depth > b_depth { (a_depth, b_depth) = (b_depth, a_depth); + (a, b) = (b, a); } assert_ne!(a_depth, b_depth); let mut hints = Vec::new(); diff --git a/src/bn254/fq6.rs b/src/bn254/fq6.rs index d58c4037e..0e86ebc74 100644 --- a/src/bn254/fq6.rs +++ b/src/bn254/fq6.rs @@ -273,9 +273,10 @@ impl Fq6 { } } - pub fn hinted_mul(mut a_depth: u32, a: ark_bn254::Fq6, mut b_depth: u32, b: ark_bn254::Fq6) -> (Script, Vec) { + pub fn hinted_mul(mut a_depth: u32, mut a: ark_bn254::Fq6, mut b_depth: u32, mut b: ark_bn254::Fq6) -> (Script, Vec) { if a_depth < b_depth { (a_depth, b_depth) = (b_depth, a_depth); + (a, b) = (b, a); } assert_ne!(a_depth, b_depth); let mut hints = Vec::new(); diff --git a/src/bn254/utils.rs b/src/bn254/utils.rs index 157e08549..3d69a6a71 100644 --- a/src/bn254/utils.rs +++ b/src/bn254/utils.rs @@ -154,16 +154,14 @@ pub fn ell_by_constant_affine(constant: &EllCoeff) -> Script { } } - -//TODO:: Implement mul_by_constant pub fn hinted_ell_by_constant_affine(f: ark_bn254::Fq12, x: ark_bn254::Fq, y: ark_bn254::Fq, constant: &EllCoeff) -> (Script, Vec) { assert_eq!(constant.0, ark_bn254::Fq2::ONE); let mut hints = Vec::new(); - let (hinted_script1, hint1) = Fq::hinted_mul(1, x, 0, constant.1.c0); - let (hinted_script2, hint2) = Fq::hinted_mul(1, x, 0, constant.1.c1); - let (hinted_script3, hint3) = Fq::hinted_mul(1, y, 0, constant.2.c0); - let (hinted_script4, hint4) = Fq::hinted_mul(1, y, 0, constant.2.c1); + let (hinted_script1, hint1) = Fq::hinted_mul_by_constant(0, x, constant.1.c0); + let (hinted_script2, hint2) = Fq::hinted_mul_by_constant(0, x, constant.1.c1); + let (hinted_script3, hint3) = Fq::hinted_mul_by_constant(0, y, constant.2.c0); + let (hinted_script4, hint4) = Fq::hinted_mul_by_constant(0, y, constant.2.c1); let mut c1 = constant.1; c1.mul_assign_by_fp(&x); let mut c2 = constant.2; @@ -174,24 +172,20 @@ pub fn hinted_ell_by_constant_affine(f: ark_bn254::Fq12, x: ark_bn254::Fq, y: ar // [f, x', y'] // update c1, c1' = x' * c1 { Fq::copy(1) } - { fq_push(constant.1.c0) } - { hinted_script1 } // { Fq::mul_by_constant(&constant.1.c0) } + { hinted_script1 } // [f, x', y', x' * c1.0] { Fq::roll(2) } - { fq_push(constant.1.c1) } - { hinted_script2 } // { Fq::mul_by_constant(&constant.1.c1) } + { hinted_script2 } // [f, y', x' * c1.0, x' * c1.1] // [f, y', x' * c1] // update c2, c2' = -y' * c2 { Fq::copy(2) } - { fq_push(constant.2.c0) } { hinted_script3 } // { Fq::mul_by_constant(&constant.2.c0) } // [f, y', x' * c1, y' * c2.0] { Fq::roll(3) } - { fq_push(constant.2.c1) } - { hinted_script4 } // { Fq::mul_by_constant(&constant.2.c1) } + { hinted_script4 } // [f, x' * c1, y' * c2.0, y' * c2.1] // [f, x' * c1, y' * c2] // [f, c1', c2'] From e133ca3cea8b54aeff5934fffd4f969777adc235 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hakan=20Karaku=C5=9F?= Date: Mon, 9 Sep 2024 15:19:20 +0300 Subject: [PATCH 008/195] Fq6.hinted_square --- src/bn254/fq2.rs | 2 +- src/bn254/fq6.rs | 106 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+), 1 deletion(-) diff --git a/src/bn254/fq2.rs b/src/bn254/fq2.rs index 3b0d9c390..e3feacc88 100644 --- a/src/bn254/fq2.rs +++ b/src/bn254/fq2.rs @@ -542,7 +542,7 @@ mod test { for _ in 0..100 { let a = ark_bn254::Fq2::rand(&mut prng); - let c = a.mul(&a); + let c = a.square(); let (hinted_square, hints) = Fq2::hinted_square(a); diff --git a/src/bn254/fq6.rs b/src/bn254/fq6.rs index 0e86ebc74..fd8e4f3cc 100644 --- a/src/bn254/fq6.rs +++ b/src/bn254/fq6.rs @@ -729,6 +729,82 @@ impl Fq6 { } } + pub fn hinted_square(a: ark_bn254::Fq6) -> (Script, Vec) { + let mut hints = Vec::new(); + + let (hinted_script1, hints1) = Fq2::hinted_square(a.c0); + let (hinted_script2, hints2) = Fq2::hinted_square(a.c0 + a.c1 + a.c2); + let (hinted_script3, hints3) = Fq2::hinted_square(a.c0 - a.c1 + a.c2); + let (hinted_script4, hints4) = Fq2::hinted_mul(2, a.c1,0, a.c2); + let (hinted_script5, hints5) = Fq2::hinted_square(a.c2); + + let script = script! { + // compute s_0 = a_0 ^ 2 + { Fq2::copy(4) } + { hinted_script1 } + + // compute a_0 + a_2 + { Fq2::roll(6) } + { Fq2::copy(4) } + { Fq2::add(2, 0) } + + // compute s_1 = (a_0 + a_1 + a_2) ^ 2 + { Fq2::copy(0) } + { Fq2::copy(8) } + { Fq2::add(2, 0) } + { hinted_script2 } + + // compute s_2 = (a_0 - a_1 + a_2) ^ 2 + { Fq2::copy(8) } + { Fq2::sub(4, 0) } + { hinted_script3 } + + // compute s_3 = 2a_1a_2 + { Fq2::roll(8) } + { Fq2::copy(8) } + { hinted_script4 } + { Fq2::double(0) } + + // compute s_4 = a_2 ^ 2 + { Fq2::roll(8) } + { hinted_script5 } + + // compute t_1 = (s_1 + s_2) / 2 + { Fq2::copy(6) } + { Fq2::roll(6) } + { Fq2::add(2, 0) } + { Fq2::div2() } + + // at this point, we have s_0, s_1, s_3, s_4, t_1 + + // compute c_0 = s_0 + \beta s_3 + { Fq2::copy(4) } + { Fq6::mul_fq2_by_nonresidue() } + { Fq2::copy(10) } + { Fq2::add(2, 0) } + + // compute c_1 = s_1 - s_3 - t_1 + \beta s_4 + { Fq2::copy(4) } + { Fq6::mul_fq2_by_nonresidue() } + { Fq2::copy(4) } + { Fq2::add(10, 0) } + { Fq2::sub(10, 0) } + { Fq2::add(2, 0) } + + // compute c_2 = t_1 - s_0 - s_4 + { Fq2::add(8, 6) } + { Fq2::sub(6, 0) } + }; + + hints.extend(hints1); + hints.extend(hints2); + hints.extend(hints3); + hints.extend(hints4); + hints.extend(hints5); + + (script, hints) + } + pub fn copy(a: u32) -> Script { script! { { Fq2::copy(a + 4) } @@ -1133,6 +1209,36 @@ mod test { } } + #[test] + fn test_bn254_fq6_hinted_square() { + let mut prng = ChaCha20Rng::seed_from_u64(0); + + let mut max_stack = 0; + + for _ in 0..1 { + let a = ark_bn254::Fq6::rand(&mut prng); + let b = a.square(); + + let (hinted_square, hints) = Fq6::hinted_square(a); + + let script = script! { + for hint in hints { + { hint.push() } + } + { fq6_push(a) } + { hinted_square.clone() } + { fq6_push(b) } + { Fq6::equalverify() } + OP_TRUE + }; + let exec_result = execute_script(script); + assert!(exec_result.success); + + max_stack = max_stack.max(exec_result.stats.max_nb_stack_items); + println!("Fq6::hinted_square: {} @ {} stack", hinted_square.len(), max_stack); + } + } + #[test] fn test_bn254_fq6_frobenius_map() { let mut prng = ChaCha20Rng::seed_from_u64(0); From 9b318bd0bf0bb7a72f6ad5e8b73a9aed696bca57 Mon Sep 17 00:00:00 2001 From: fatih Date: Mon, 9 Sep 2024 15:26:58 +0300 Subject: [PATCH 009/195] feat: fq2 hinted mul by const --- src/bn254/fp254impl.rs | 9 +++--- src/bn254/fq.rs | 2 +- src/bn254/fq2.rs | 62 +++++++++++++++++++++++++++++++++++++++++- src/bn254/utils.rs | 8 +++--- 4 files changed, 71 insertions(+), 10 deletions(-) diff --git a/src/bn254/fp254impl.rs b/src/bn254/fp254impl.rs index d2cf65d06..3c9cd7e70 100644 --- a/src/bn254/fp254impl.rs +++ b/src/bn254/fp254impl.rs @@ -510,8 +510,8 @@ pub trait Fp254Impl { (script, hints) } - // TODO: Optimize by using constant feature - fn hinted_mul_by_constant(a_depth: u32, a: ark_bn254::Fq, constant: ark_bn254::Fq) -> (Script, Vec) { + // TODO: Optimize by using the constant feature + fn hinted_mul_by_constant(a: ark_bn254::Fq, constant: &ark_bn254::Fq) -> (Script, Vec) { let mut hints = Vec::new(); let x = BigInt::from_str(&a.to_string()).unwrap(); let y = BigInt::from_str(&constant.to_string()).unwrap(); @@ -522,8 +522,8 @@ pub trait Fp254Impl { for _ in 0..Self::N_LIMBS { OP_DEPTH OP_1SUB OP_ROLL // hints } - { Fq::roll(a_depth + 1) } - { fq_push(constant) } + { Fq::roll(1) } + { fq_push(*constant) } { Fq::tmul() } }; hints.push(Hint::Fq(ark_bn254::Fq::from_str(&q.to_string()).unwrap())); @@ -667,6 +667,7 @@ pub trait Fp254Impl { } } + // TODO: Optimize using the sqaure feature fn hinted_square(a: ark_bn254::Fq) -> (Script, Vec) { let mut hints = Vec::new(); let x = &BigInt::from_str(&a.to_string()).unwrap(); diff --git a/src/bn254/fq.rs b/src/bn254/fq.rs index b6a806932..ef052abdb 100644 --- a/src/bn254/fq.rs +++ b/src/bn254/fq.rs @@ -864,7 +864,7 @@ mod test { let b = ark_bn254::Fq::rand(&mut prng); let c = a.mul(&b); - let (hinted_mul, hints) = Fq::hinted_mul_by_constant(0, a, b); + let (hinted_mul, hints) = Fq::hinted_mul_by_constant(a, &b); let script = script! { for hint in hints { diff --git a/src/bn254/fq2.rs b/src/bn254/fq2.rs index e3feacc88..f3428246b 100644 --- a/src/bn254/fq2.rs +++ b/src/bn254/fq2.rs @@ -285,6 +285,34 @@ impl Fq2 { } } + pub fn hinted_mul_by_constant(a: ark_bn254::Fq2, constant: &ark_bn254::Fq2) -> (Script, Vec) { + let mut hints = Vec::new(); + + let (hinted_script1, hint1) = Fq::hinted_mul_by_constant(a.c0, &constant.c0); + let (hinted_script2, hint2) = Fq::hinted_mul_by_constant(a.c1, &constant.c1); + let (hinted_script3, hint3) = Fq::hinted_mul_by_constant(a.c0+a.c1, &(constant.c0+constant.c1)); + + let script = script! { + { Fq::copy(1) } + { hinted_script1 } + { Fq::copy(1) } + { hinted_script2 } + { Fq::add(3, 2) } + { hinted_script3 } + { Fq::copy(2) } + { Fq::copy(2) } + { Fq::add(1, 0) } + { Fq::sub(1, 0) } + { Fq::sub(2, 1) } + { Fq::roll(1) } + }; + hints.extend(hint1); + hints.extend(hint2); + hints.extend(hint3); + + (script, hints) + } + pub fn toaltstack() -> Script { script! { { Fq::toaltstack() } @@ -445,6 +473,38 @@ mod test { } + #[test] + fn test_bn254_fq2_hinted_mul_by_constant() { + let mut prng: ChaCha20Rng = ChaCha20Rng::seed_from_u64(0); + + let mut max_stack = 0; + + for _ in 0..100 { + let a = ark_bn254::Fq2::rand(&mut prng); + let b = ark_bn254::Fq2::rand(&mut prng); + let c = a.mul(&b); + + let (hinted_mul, hints) = Fq2::hinted_mul_by_constant(a, &b); + + let script = script! { + for hint in hints { + { hint.push() } + } + { fq2_push(a) } + { hinted_mul.clone() } + { fq2_push(c) } + { Fq2::equalverify() } + OP_TRUE + }; + let res = execute_script(script); + assert!(res.success); + + max_stack = max_stack.max(res.stats.max_nb_stack_items); + println!("Fq2::window_mul: {} @ {} stack", hinted_mul.len(), max_stack); + } + + } + #[test] fn test_bn254_fq2_mul() { println!("Fq2.mul: {} bytes", Fq2::mul(1, 0).len()); @@ -542,7 +602,7 @@ mod test { for _ in 0..100 { let a = ark_bn254::Fq2::rand(&mut prng); - let c = a.square(); + let c = a.mul(&a); let (hinted_square, hints) = Fq2::hinted_square(a); diff --git a/src/bn254/utils.rs b/src/bn254/utils.rs index 3d69a6a71..931120bf6 100644 --- a/src/bn254/utils.rs +++ b/src/bn254/utils.rs @@ -158,10 +158,10 @@ pub fn hinted_ell_by_constant_affine(f: ark_bn254::Fq12, x: ark_bn254::Fq, y: ar assert_eq!(constant.0, ark_bn254::Fq2::ONE); let mut hints = Vec::new(); - let (hinted_script1, hint1) = Fq::hinted_mul_by_constant(0, x, constant.1.c0); - let (hinted_script2, hint2) = Fq::hinted_mul_by_constant(0, x, constant.1.c1); - let (hinted_script3, hint3) = Fq::hinted_mul_by_constant(0, y, constant.2.c0); - let (hinted_script4, hint4) = Fq::hinted_mul_by_constant(0, y, constant.2.c1); + let (hinted_script1, hint1) = Fq::hinted_mul_by_constant(x, &constant.1.c0); + let (hinted_script2, hint2) = Fq::hinted_mul_by_constant(x, &constant.1.c1); + let (hinted_script3, hint3) = Fq::hinted_mul_by_constant(y, &constant.2.c0); + let (hinted_script4, hint4) = Fq::hinted_mul_by_constant(y, &constant.2.c1); let mut c1 = constant.1; c1.mul_assign_by_fp(&x); let mut c2 = constant.2; From 25e421651394db9600dfaa005b087ab6b969cb5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hakan=20Karaku=C5=9F?= Date: Mon, 9 Sep 2024 15:32:29 +0300 Subject: [PATCH 010/195] Fq12.hinted_square --- src/bn254/fq12.rs | 74 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/src/bn254/fq12.rs b/src/bn254/fq12.rs index c1eeb0330..a656a5712 100644 --- a/src/bn254/fq12.rs +++ b/src/bn254/fq12.rs @@ -556,6 +556,49 @@ impl Fq12 { } } + pub fn hinted_square(a: ark_bn254::Fq12) -> (Script, Vec) { + let mut hints = Vec::new(); + + let (hinted_script1, hints1) = Fq6::hinted_mul(12, a.c1, 18, a.c0); + let mut beta_ac1 = a.c1; + ark_bn254::Fq12Config::mul_fp6_by_nonresidue_in_place(&mut beta_ac1); + let (hinted_script2, hints2) = Fq6::hinted_mul(12, a.c0 + a.c1, 6, a.c0 + beta_ac1); + + let script = script! { + // v0 = c0 + c1 + { Fq6::copy(6) } + { Fq6::copy(6) } + { Fq6::add(6, 0) } + + // v3 = c0 + beta * c1 + { Fq6::copy(6) } + { Fq12::mul_fq6_by_nonresidue() } + { Fq6::copy(18) } + { Fq6::add(0, 6) } + + // v2 = c0 * c1 + { hinted_script1 } + + // v0 = v0 * v3 + { hinted_script2 } + + // final c0 = v0 - (beta + 1) * v2 + { Fq6::copy(6) } + { Fq12::mul_fq6_by_nonresidue() } + { Fq6::copy(12) } + { Fq6::add(6, 0) } + { Fq6::sub(6, 0) } + + // final c1 = 2 * v2 + { Fq6::double(6) } + }; + + hints.extend(hints1); + hints.extend(hints2); + + (script, hints) + } + pub fn cyclotomic_inverse() -> Script { script! { { Fq6::neg(0) } @@ -942,6 +985,37 @@ mod test { } } + #[test] + fn test_bn254_fq6_hinted_square() { + let mut prng = ChaCha20Rng::seed_from_u64(0); + + let mut max_stack = 0; + + for _ in 0..1 { + let a = ark_bn254::Fq12::rand(&mut prng); + let b = a.square(); + + let (hinted_square, hints) = Fq12::hinted_square(a); + + let script = script! { + for hint in hints { + { hint.push() } + } + { fq12_push(a) } + { hinted_square.clone() } + { fq12_push(b) } + { Fq12::equalverify() } + OP_TRUE + }; + let exec_result = execute_script(script); + assert!(exec_result.success); + + max_stack = max_stack.max(exec_result.stats.max_nb_stack_items); + println!("Fq12::hinted_square: {} @ {} stack", hinted_square.len(), max_stack); + } + } + + #[test] fn test_bn254_fq12_mul_by_034() { println!("Fq12.mul_by_034: {} bytes", Fq12::mul_by_034().len()); From 808045428e7e8121eabacd51d10e09a1d16c301b Mon Sep 17 00:00:00 2001 From: fatih Date: Mon, 9 Sep 2024 16:21:44 +0300 Subject: [PATCH 011/195] feat: hinted line utils --- src/bn254/utils.rs | 186 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 186 insertions(+) diff --git a/src/bn254/utils.rs b/src/bn254/utils.rs index 931120bf6..5ce16817f 100644 --- a/src/bn254/utils.rs +++ b/src/bn254/utils.rs @@ -425,6 +425,44 @@ pub fn affine_add_line(c3: ark_bn254::Fq2, c4: ark_bn254::Fq2) -> Script { } } +pub fn hinted_affine_add_line(tx: ark_bn254::Fq2, qx: ark_bn254::Fq2, c3: ark_bn254::Fq2, c4: ark_bn254::Fq2) -> (Script, Vec) { + let mut hints = Vec::new(); + let (hinted_script1, hint1) = Fq2::hinted_mul(4, c3, 0, c3.square()-tx-qx); + + let script = script! { + // [T.x, Q.x] + { Fq2::neg(0) } + // [T.x, -Q.x] + { Fq2::roll(2) } + // [-Q.x, T.x] + { Fq2::neg(0) } + // [-T.x - Q.x] + { Fq2::add(2, 0) } + // [-T.x - Q.x] + { fq2_push(c3) } + // [-T.x - Q.x, alpha] + { fq2_push(c3.square()) } + // [-T.x - Q.x, alpha, alpha^2] + // calculate x' = alpha^2 - T.x - Q.x + { Fq2::add(4, 0) } + // [alpha, x'] + { Fq2::copy(0) } + // [alpha, x', x'] + { hinted_script1 } + // [x', alpha * x'] + { Fq2::neg(0) } + // [x', -alpha * x'] + { fq2_push(c4) } + // [x', -alpha * x', -bias] + // compute y' = -bias - alpha * x' + { Fq2::add(2, 0) } + // [x', y'] + }; + hints.extend(hint1); + + (script, hints) +} + /// double a point T: /// x' = alpha^2 - 2 * T.x /// y' = -bias - alpha* x' @@ -493,6 +531,36 @@ pub fn check_line_through_point(c3: ark_bn254::Fq2, c4: ark_bn254::Fq2) -> Scrip } } +pub fn hinted_check_line_through_point(x: ark_bn254::Fq2, c3: ark_bn254::Fq2, c4: ark_bn254::Fq2) -> (Script, Vec) { + let mut hints: Vec = Vec::new(); + + let (hinted_script1, hint1) = Fq2::hinted_mul_by_constant(x, &c3); + + let script = script! { + // [x, y] + { Fq2::roll(2) } + // [y, x] + { hinted_script1 } + // [y, alpha * x] + { Fq2::neg(0) } + // [y, -alpha * x] + { Fq2::add(2, 0) } + // [y - alpha * x] + + { fq2_push(c4) } + // [y - alpha * x, -bias] + { Fq2::add(2, 0) } + // [y - alpha * x - bias] + + { Fq2::push_zero() } + // [y - alpha * x - bias, 0] + { Fq2::equalverify() } + }; + hints.extend(hint1); + + (script, hints) +} + /// check whether a tuple coefficient (alpha, -bias) of a tangent line is satisfied with expected point T (affine) /// two aspects: /// 1. alpha * (2 * T.y) = 3 * T.x^2, make sure the alpha is the right ONE @@ -560,6 +628,26 @@ pub fn check_chord_line(c3: ark_bn254::Fq2, c4: ark_bn254::Fq2) -> Script { } } +pub fn hinted_check_chord_line(t: ark_bn254::G2Affine, q: ark_bn254::G2Affine, c3: ark_bn254::Fq2, c4: ark_bn254::Fq2) -> (Script, Vec) { + let mut hints = Vec::new(); + + let (hinted_script1, hint1) = hinted_check_line_through_point(q.x, c3, c4); + let (hinted_script2, hint2) = hinted_check_line_through_point(t.x, c3, c4); + + let script = script! { + // check: Q.y - alpha * Q.x - bias = 0 + { hinted_script1 } + // [T.x, T.y] + // check: T.y - alpha * T.x - bias = 0 + { hinted_script2 } + // [] + }; + hints.extend(hint1); + hints.extend(hint2); + + (script, hints) +} + // stack data: beta^{2 * (p - 1) / 6}, beta^{3 * (p - 1) / 6}, beta^{2 * (p^2 - 1) / 6}, 1/2, B, // P1, P2, P3, P4, Q4, c, c', wi, f, Px, Py, Tx, Ty, Tz, Qx, Qy // [..., Fq12, Fq12, Fq12, Fq12, Fq, Fq, (Fq, Fq), (Fq, Fq), (Fq, Fq), (Fq, Fq), (Fq, Fq)] @@ -1171,6 +1259,47 @@ mod test { assert!(exec_result.success); } + #[test] + fn test_hinted_affine_add_line() { + // alpha = (t.y - q.y) / (t.x - q.x) + // bias = t.y - alpha * t.x + // x' = alpha^2 - T.x - Q.x + // y' = -bias - alpha * x' + let mut prng = ChaCha20Rng::seed_from_u64(0); + let t = ark_bn254::G2Affine::rand(&mut prng); + let q = ark_bn254::G2Affine::rand(&mut prng); + let alpha = (t.y - q.y) / (t.x - q.x); + // -bias + let bias_minus = alpha * t.x - t.y; + + let x = alpha.square() - t.x - q.x; + let y = bias_minus - alpha * x; + let (hinted_add_line, hints) = hinted_affine_add_line(t.x, q.x, alpha, bias_minus); + + let script = script! { + for hint in hints { + { hint.push() } + } + { fq2_push(t.x) } + { fq2_push(q.x) } + { hinted_add_line.clone() } + // [x'] + { fq2_push(y) } + // [x', y', y] + { Fq2::equalverify() } + // [x'] + { fq2_push(x) } + // [x', x] + { Fq2::equalverify() } + // [] + OP_TRUE + // [OP_TRUE] + }; + let exec_result = execute_script(script); + assert!(exec_result.success); + println!("hinted_add_line: {} @ {} stack", hinted_add_line.len(), exec_result.stats.max_nb_stack_items); + } + #[test] fn test_affine_double_line() { // slope: alpha = 3 * x^2 / 2 * y @@ -1229,6 +1358,36 @@ mod test { }; let exec_result = execute_script(script); assert!(exec_result.success); + println!("check_line: {} @ {} stack", check_line_through_point(alpha, bias_minus).len(), exec_result.stats.max_nb_stack_items); + } + + #[test] + fn test_hinted_check_tangent_line() { + let mut prng = ChaCha20Rng::seed_from_u64(0); + let t = ark_bn254::G2Affine::rand(&mut prng); + let two_inv = ark_bn254::Fq::one().double().inverse().unwrap(); + let three_div_two = (ark_bn254::Fq::one().double() + ark_bn254::Fq::one()) * two_inv; + let mut alpha = t.x.square(); + alpha /= t.y; + alpha.mul_assign_by_fp(&three_div_two); + // -bias + let bias_minus = alpha * t.x - t.y; + assert_eq!(alpha * t.x - t.y, bias_minus); + + let (hinted_check_line, hints) = hinted_check_line_through_point(t.x, alpha, bias_minus); + + let script = script! { + for hint in hints { + { hint.push() } + } + { fq2_push(t.x) } + { fq2_push(t.y) } + { hinted_check_line.clone() } + OP_TRUE + }; + let exec_result = execute_script(script); + assert!(exec_result.success); + println!("hinted_check_line: {} @ {} stack", hinted_check_line.len(), exec_result.stats.max_nb_stack_items); } #[test] @@ -1252,4 +1411,31 @@ mod test { let exec_result = execute_script(script); assert!(exec_result.success); } + + #[test] + fn test_hinted_check_chord_line() { + let mut prng = ChaCha20Rng::seed_from_u64(0); + let t = ark_bn254::G2Affine::rand(&mut prng); + let q = ark_bn254::G2Affine::rand(&mut prng); + let alpha = (t.y - q.y) / (t.x - q.x); + // -bias + let bias_minus = alpha * t.x - t.y; + assert_eq!(alpha * t.x - t.y, bias_minus); + let (hinted_check_line, hints) = hinted_check_chord_line(t, q, alpha, bias_minus); + + let script = script! { + for hint in hints { + { hint.push() } + } + { fq2_push(t.x) } + { fq2_push(t.y) } + { fq2_push(q.x) } + { fq2_push(q.y) } + { hinted_check_line.clone() } + OP_TRUE + }; + let exec_result = execute_script(script); + assert!(exec_result.success); + println!("hinted_check_line: {} @ {} stack", hinted_check_line.len(), exec_result.stats.max_nb_stack_items); + } } From 3ade7fa8485e59ecc8924a15e59eb904210d4d91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hakan=20Karaku=C5=9F?= Date: Mon, 9 Sep 2024 16:24:22 +0300 Subject: [PATCH 012/195] hinted_frobenius functions --- src/bn254/fq12.rs | 50 +++++++++++++++++++++++++++ src/bn254/fq2.rs | 54 ++++++++++++++++++++++++++--- src/bn254/fq6.rs | 87 ++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 186 insertions(+), 5 deletions(-) diff --git a/src/bn254/fq12.rs b/src/bn254/fq12.rs index a656a5712..6ca1d2bbe 100644 --- a/src/bn254/fq12.rs +++ b/src/bn254/fq12.rs @@ -646,6 +646,28 @@ impl Fq12 { } } + pub fn hinted_frobenius_map(i: usize, a: ark_bn254::Fq12) -> (Script, Vec) { + let mut hints = Vec::new(); + + let (hinted_script1, hint1) = Fq6::hinted_frobenius_map(i, a.c0); + let (hinted_script2, hint2) = Fq6::hinted_frobenius_map(i, a.c1); + let (hinted_script3, hint3) = Fq6::hinted_mul_by_fp2_constant(a.c1.frobenius_map(i), &ark_bn254::Fq12Config::FROBENIUS_COEFF_FP12_C1[i % ark_bn254::Fq12Config::FROBENIUS_COEFF_FP12_C1.len()]); + + let script = script! { + { Fq6::roll(6) } + { hinted_script1 } + { Fq6::roll(6) } + { hinted_script2 } + { hinted_script3 } + }; + + hints.extend(hint1); + hints.extend(hint2); + hints.extend(hint3); + + (script, hints) + } + pub fn toaltstack() -> Script { script! { { Fq6::toaltstack() } @@ -1118,6 +1140,34 @@ mod test { } } + #[test] + fn test_bn254_fq12_hinted_frobenius_map() { + let mut prng = ChaCha20Rng::seed_from_u64(0); + + for _ in 0..1 { + for i in 0..12 { + let a = ark_bn254::Fq12::rand(&mut prng); + let b = a.frobenius_map(i); + + let (hinted_frobenius_map, hints) = Fq12::hinted_frobenius_map(i, a); + println!("Fq12.hinted_frobenius_map({}): {} bytes", i, hinted_frobenius_map.len()); + + let script = script! { + for hint in hints { + { hint.push() } + } + { fq12_push(a) } + { hinted_frobenius_map.clone() } + { fq12_push(b) } + { Fq12::equalverify() } + OP_TRUE + }; + let exec_result = execute_script(script); + assert!(exec_result.success); + } + } + } + #[test] fn test_bn254_fq12_mul_by_r() { let mut prng = ChaCha20Rng::seed_from_u64(0); diff --git a/src/bn254/fq2.rs b/src/bn254/fq2.rs index f3428246b..154cd4d62 100644 --- a/src/bn254/fq2.rs +++ b/src/bn254/fq2.rs @@ -268,6 +268,10 @@ impl Fq2 { } } + pub fn hinted_frobenius_map(i: usize, a: ark_bn254::Fq2) -> (Script, Vec) { + Fq::hinted_mul_by_constant(a.c1, &ark_bn254::Fq2Config::FROBENIUS_COEFF_FP2_C1[i % ark_bn254::Fq2Config::FROBENIUS_COEFF_FP2_C1.len()]) + } + pub fn mul_by_constant(constant: &ark_bn254::Fq2) -> Script { script! { { Fq::copy(1) } @@ -337,19 +341,17 @@ impl Fq2 { #[cfg(test)] mod test { - use crate::bn254::fq::{bigint_to_u32_limbs, Fq}; + use crate::bn254::fq::Fq; use crate::bn254::fq2::Fq2; use crate::bn254::{fp254impl::Fp254Impl, utils::fq2_push}; use crate::treepp::*; use ark_ff::Field; use ark_std::UniformRand; - use bitcoin::opcodes::OP_TRUE; use core::ops::{Add, Mul}; - use num_bigint::{BigInt, BigUint, RandBigInt}; + use num_bigint::BigUint; use rand::SeedableRng; use rand_chacha::ChaCha20Rng; use ark_ff::AdditiveGroup; - use std::str::FromStr; #[test] fn test_bn254_fq2_add() { @@ -730,4 +732,48 @@ mod test { assert!(exec_result.success); } } + + #[test] + fn test_bn254_fq2_hinted_frobenius_map() { + let mut prng = ChaCha20Rng::seed_from_u64(0); + + for _ in 0..3 { + let a = ark_bn254::Fq2::rand(&mut prng); + let b = a.frobenius_map(0); + + let (hinted_frobenius_map_0, hints) = Fq2::hinted_frobenius_map(0, a); + println!("Fq2.hinted_frobenius_map(0): {} bytes", hinted_frobenius_map_0.len()); + + let script = script! { + for hint in hints { + { hint.push() } + } + { fq2_push(a) } + { hinted_frobenius_map_0 } + { fq2_push(b) } + { Fq2::equalverify() } + OP_TRUE + }; + let exec_result = execute_script(script); + assert!(exec_result.success); + + let b = a.frobenius_map(1); + + let (hinted_frobenius_map_1, hints) = Fq2::hinted_frobenius_map(1, a); + println!("Fq2.hinted_frobenius_map(1): {} bytes", hinted_frobenius_map_1.len()); + + let script = script! { + for hint in hints { + { hint.push() } + } + { fq2_push(a) } + { hinted_frobenius_map_1 } + { fq2_push(b) } + { Fq2::equalverify() } + OP_TRUE + }; + let exec_result = execute_script(script); + assert!(exec_result.success); + } + } } diff --git a/src/bn254/fq6.rs b/src/bn254/fq6.rs index fd8e4f3cc..b87ac36c7 100644 --- a/src/bn254/fq6.rs +++ b/src/bn254/fq6.rs @@ -2,7 +2,7 @@ use crate::bn254::fp254impl::Fp254Impl; use crate::bn254::fq::Fq; use crate::bn254::fq2::Fq2; use crate::treepp::{script, Script}; -use ark_ff::Fp6Config; +use ark_ff::{Field, Fp6Config}; use num_bigint::BigUint; use super::utils::Hint; @@ -106,6 +106,34 @@ impl Fq6 { } } + pub fn hinted_mul_by_fp2_constant(a: ark_bn254::Fq6, constant: &ark_bn254::Fq2) -> (Script, Vec) { + let mut hints = Vec::new(); + + let (hinted_script1, hint1) = Fq2::hinted_mul_by_constant(a.c0, constant); + let (hinted_script2, hint2) = Fq2::hinted_mul_by_constant(a.c1, constant); + let (hinted_script3, hint3) = Fq2::hinted_mul_by_constant(a.c2, constant); + + let script = script! { + // compute p.c0 * c0 + { Fq2::roll(4) } + { hinted_script1 } + + // compute p.c1 * c1 + { Fq2::roll(4) } + { hinted_script2 } + + // compute p.c2 * c2 + { Fq2::roll(4) } + { hinted_script3 } + }; + + hints.extend(hint1); + hints.extend(hint2); + hints.extend(hint3); + + (script, hints) + } + pub fn push_one() -> Script { script! { { Fq2::push_one() } @@ -910,6 +938,35 @@ impl Fq6 { } } + pub fn hinted_frobenius_map(i: usize, a: ark_bn254::Fq6) -> (Script, Vec) { + let mut hints = Vec::new(); + + let (hinted_script1, hint1) = Fq2::hinted_frobenius_map(i, a.c0); + let (hinted_script2, hint2) = Fq2::hinted_frobenius_map(i, a.c1); + let (hinted_script3, hint3) = Fq2::hinted_mul_by_constant(a.c1.frobenius_map(i), &ark_bn254::Fq6Config::FROBENIUS_COEFF_FP6_C1[i % ark_bn254::Fq6Config::FROBENIUS_COEFF_FP6_C1.len()]); + let (hinted_script4, hint4) = Fq2::hinted_frobenius_map(i, a.c2); + let (hinted_script5, hint5) = Fq2::hinted_mul_by_constant(a.c2.frobenius_map(i), &ark_bn254::Fq6Config::FROBENIUS_COEFF_FP6_C2[i % ark_bn254::Fq6Config::FROBENIUS_COEFF_FP6_C2.len()]); + + let script = script! { + { Fq2::roll(4) } + { hinted_script1 } + { Fq2::roll(4) } + { hinted_script2 } + { hinted_script3 } + { Fq2::roll(4) } + { hinted_script4 } + { hinted_script5 } + }; + + hints.extend(hint1); + hints.extend(hint2); + hints.extend(hint3); + hints.extend(hint4); + hints.extend(hint5); + + (script, hints) + } + pub fn toaltstack() -> Script { script! { { Fq2::toaltstack() } @@ -1263,4 +1320,32 @@ mod test { } } } + + #[test] + fn test_bn254_fq6_hinted_frobenius_map() { + let mut prng = ChaCha20Rng::seed_from_u64(0); + + for _ in 0..1 { + for i in 0..6 { + let a = ark_bn254::Fq6::rand(&mut prng); + let b = a.frobenius_map(i); + + let (hinted_frobenius_map, hints) = Fq6::hinted_frobenius_map(i, a); + println!("Fq6.hinted_frobenius_map({}): {} bytes", i, hinted_frobenius_map.len()); + + let script = script! { + for hint in hints { + { hint.push() } + } + { fq6_push(a) } + { hinted_frobenius_map.clone() } + { fq6_push(b) } + { Fq6::equalverify() } + OP_TRUE + }; + let exec_result = execute_script(script); + assert!(exec_result.success); + } + } + } } From 31becf7f297a71ef1c41947f81eafb786837cfa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hakan=20Karaku=C5=9F?= Date: Mon, 9 Sep 2024 17:20:26 +0300 Subject: [PATCH 013/195] G1Projective.hinted_double --- src/bn254/curves.rs | 152 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) diff --git a/src/bn254/curves.rs b/src/bn254/curves.rs index 89044ce68..8cdb11220 100644 --- a/src/bn254/curves.rs +++ b/src/bn254/curves.rs @@ -1,3 +1,4 @@ +use ark_ff::{AdditiveGroup, Field}; use num_bigint::BigUint; use crate::bigint::U254; @@ -8,6 +9,8 @@ use crate::treepp::{script, Script}; use std::cmp::min; use std::sync::OnceLock; +use super::utils::Hint; + static G1_DOUBLE_PROJECTIVE: OnceLock