diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 6b5a3440..a7a692c4 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -15,7 +15,7 @@ jobs: timeout-minutes: 60 runs-on: self-hosted steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Run rustfmt run: cargo fmt --all -- --check @@ -24,7 +24,7 @@ jobs: timeout-minutes: 60 runs-on: self-hosted steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Run clippy (no guests) run: cargo clippy --workspace --exclude header-chain-circuit --exclude final-spv-circuit @@ -34,7 +34,7 @@ jobs: timeout-minutes: 60 runs-on: self-hosted steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Build guests run: | REPR_GUEST_BUILD=1 BITCOIN_NETWORK=mainnet cargo build -p header-chain-circuit --release @@ -46,7 +46,7 @@ jobs: timeout-minutes: 60 runs-on: self-hosted steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Cache Build Artifacts uses: actions/cache@v4 @@ -79,7 +79,7 @@ jobs: hash::blake3::tests::test_blake3_randominputs hash::blake3::tests::test_blake3_randominputs_multipleof64bytes steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Cache Build Artifacts uses: actions/cache@v4 @@ -110,7 +110,7 @@ jobs: # needs: build # runs-on: self-hosted # steps: -# - uses: actions/checkout@v4 +# - uses: actions/checkout@v5 # # - name: Cache Build Artifacts # uses: actions/cache@v4 diff --git a/bitvm/src/bigint/add.rs b/bitvm/src/bigint/add.rs index 2ed22490..07a88f6d 100644 --- a/bitvm/src/bigint/add.rs +++ b/bitvm/src/bigint/add.rs @@ -63,6 +63,34 @@ impl BigIntImpl { } } + /// add one + pub fn add1() -> Script { + script! { + OP_1ADD // a0 ... an + { 1 << LIMB_SIZE } // a0 ... an x + OP_SWAP // a0 ... x an + for _ in 0..Self::N_LIMBS-1 { // ... x a + OP_2DUP // ... x a x a + OP_EQUAL // ... x a 0/1 + OP_TUCK // ... x 0/1 a 0/1 + OP_IF OP_NOT OP_ENDIF // ... x 0/1 a/0 + OP_TOALTSTACK // ... x 0/1 + OP_ROT // .. x 0/1 a_{i-1} + OP_ADD // .. x a' + } + // x a0 + OP_NIP // a0 + { Self::HEAD_OFFSET } // a0 y + OP_OVER // a0 y a0 + OP_EQUAL // a0 0/1 + OP_IF OP_NOT OP_ENDIF // a/0 + + for _ in 0..Self::N_LIMBS - 1 { + OP_FROMALTSTACK + } + } + } + /// Double the BigInt on top of the stack /// /// # Note @@ -98,15 +126,15 @@ impl BigIntImpl { { 1 << LIMB_SIZE } // Double the limb, take the result to the alt stack, and add initial carry - { n + 1 } OP_PICK limb_double_without_carry OP_TOALTSTACK + { n * Self::N_LIMBS + 1 } OP_PICK limb_double_without_carry OP_TOALTSTACK for i in 0..Self::N_LIMBS - 2 { - { n + i + 3 } OP_PICK limb_double_with_carry OP_TOALTSTACK + { n * Self::N_LIMBS + i + 3 } OP_PICK limb_double_with_carry OP_TOALTSTACK } // When we got {limb} {base} {carry} on the stack, we drop the base OP_NIP // {limb} {carry} - { n + 9 } OP_PICK { limb_double_with_carry_allow_overflow(Self::HEAD_OFFSET) } + { n * Self::N_LIMBS + Self::N_LIMBS } OP_PICK { limb_double_with_carry_allow_overflow(Self::HEAD_OFFSET) } // Take all limbs from the alt stack to the main stack for _ in 0..Self::N_LIMBS - 1 { @@ -144,6 +172,25 @@ impl BigIntImpl { } } + /// Double the referenced BigInt but keep the original element in its position + /// This function prevents overflow of the underlying integer types during + /// doubling operation. + pub fn double_prevent_overflow_keep_element(n: u32) -> Script { + script! { + { 1 << LIMB_SIZE } + { n * Self::N_LIMBS + 1 } OP_PICK limb_double_without_carry OP_TOALTSTACK + for i in 0..Self::N_LIMBS - 2 { + { n * Self::N_LIMBS + i + 3 } OP_PICK limb_double_with_carry OP_TOALTSTACK + } + OP_NIP + { n * Self::N_LIMBS + Self::N_LIMBS } OP_PICK OP_SWAP + { limb_double_with_carry_prevent_overflow(Self::HEAD_OFFSET) } + for _ in 0..Self::N_LIMBS - 1 { + OP_FROMALTSTACK + } + } + } + /// Left shift the BigInt on top of the stack by `bits` /// /// # Note @@ -234,7 +281,7 @@ impl BigIntImpl { OP_NIP { b_depth + 1 } OP_PICK - OP_ROT + OP_SWAP { limb_add_with_carry_prevent_overflow(Self::HEAD_OFFSET) } for _ in 0..Self::N_LIMBS - 1 { @@ -361,7 +408,7 @@ fn limb_add_with_carry_prevent_overflow(head_offset: u32) -> Script { OP_2SWAP // {a+b+c_nlo} {x} {a} {sign_b} {a+b+c_nlo} {x} OP_GREATERTHANOREQUAL // {a+b+c_nlo} {x} {a} {sign_b} {I:0/1} OP_2SWAP // {a+b+c_nlo} {sign_b} {I:0/1} {x} {a} - OP_GREATERTHANOREQUAL // {a+b+c_nlo} {sign_b} {I:0/1} {sign_a} + OP_GREATERTHAN // {a+b+c_nlo} {sign_b} {I:0/1} {sign_a} OP_ADD OP_ADD 1 3 OP_WITHIN OP_VERIFY // verify (sign_a, sign_b, I) is not (0, 0, 0) or (1, 1, 1) which would mean overflow } } @@ -417,7 +464,7 @@ fn limb_double_with_carry_prevent_overflow(head_offset: u32) -> Script { OP_TUCK OP_DUP OP_ADD // {a} {x} {2a+c} {2x} OP_2DUP OP_GREATERTHANOREQUAL // {a} {x} {2a+c} {2x} {L:0/1} OP_NOTIF OP_NOT OP_ENDIF OP_SUB // {a} {x} {2a+c_nlo} - OP_2DUP OP_LESSTHAN // {a} {x} {2a+c_nlo} {I:0/1} + OP_2DUP OP_LESSTHANOREQUAL // {a} {x} {2a+c_nlo} {I:0/1} OP_2SWAP // {2a+c_nlo} {I:0/1} {a} {x} OP_LESSTHAN // {2a+c_nlo} {I:0/1} {sign_a} @@ -537,6 +584,25 @@ mod test { } } + #[test] + fn test_add1() { + println!("U254.add1: {} bytes", U254::add1().len()); + let mut prng = ChaCha20Rng::seed_from_u64(0); + for _ in 0..100 { + let a: BigUint = prng.sample(RandomBits::new(254)); + let c: BigUint = (a.clone() + BigUint::one()).rem(BigUint::one().shl(254)); + + let script = script! { + { U254::push_u32_le(&a.to_u32_digits()) } + { U254::add1() } + { U254::push_u32_le(&c.to_u32_digits()) } + { U254::equalverify(1, 0) } + OP_TRUE + }; + run(script); + } + } + #[test] fn test_double() { println!("U254.double: {} bytes", U254::double(0).len()); diff --git a/bitvm/src/bigint/bits.rs b/bitvm/src/bigint/bits.rs index 998556a2..7f7868fe 100644 --- a/bitvm/src/bigint/bits.rs +++ b/bitvm/src/bigint/bits.rs @@ -45,44 +45,6 @@ impl BigIntImpl { { limb_to_le_bits_toaltstack(N_BITS - LIMB_SIZE * (Self::N_LIMBS - 1)) } } } - - pub fn limb_from_bytes() -> Script { - let bytes_per_limb = LIMB_SIZE.div_ceil(8); - - assert!(LIMB_SIZE > 0, "LIMB_SIZE must not be 0"); - assert!(LIMB_SIZE < 33, "LIMB_SIZE must be less than 33"); - - script! { - // This will be our sum on the stack - OP_0 - for i in 0..bytes_per_limb { - // Check that the number is a u8 - OP_SWAP - OP_DUP - { 256 } - OP_LESSTHAN - OP_VERIFY - // lshift - for _ in 0..8*i { - OP_DUP - OP_ADD - } - OP_ADD - } - } - } - - pub fn from_bytes() -> Script { - script! { - for _ in 0..Self::N_LIMBS { - { Self::limb_from_bytes() } - OP_TOALTSTACK - } - for _ in 0..Self::N_LIMBS { - OP_FROMALTSTACK - } - } - } } fn limb_to_be_bits_common(num_bits: u32) -> Script { @@ -171,16 +133,19 @@ fn limb_to_le_bits_common(num_bits: u32) -> Script { } pub fn limb_to_le_bits(num_bits: u32) -> Script { + assert!(num_bits > 0); if num_bits >= 2 { script! { { limb_to_le_bits_common(num_bits) } } } else { + // if num_bits == 1 script! {} } } pub fn limb_to_le_bits_toaltstack(num_bits: u32) -> Script { + assert!(num_bits > 0); if num_bits >= 2 { script! { { limb_to_le_bits_common(num_bits) } @@ -189,11 +154,15 @@ pub fn limb_to_le_bits_toaltstack(num_bits: u32) -> Script { } } } else { - script! {} + // if num_bits == 1 + script! { + OP_TOALTSTACK + } } } pub fn limb_to_be_bits(num_bits: u32) -> Script { + assert!(num_bits > 0); if num_bits >= 2 { script! { { limb_to_be_bits_common(num_bits) } @@ -202,11 +171,13 @@ pub fn limb_to_be_bits(num_bits: u32) -> Script { } } } else { + // if num_bits == 1 script! {} } } pub fn limb_to_be_bits_toaltstack(num_bits: u32) -> Script { + assert!(num_bits > 0); if num_bits >= 2 { script! { { limb_to_be_bits_common(num_bits) } @@ -214,6 +185,7 @@ pub fn limb_to_be_bits_toaltstack(num_bits: u32) -> Script { OP_TOALTSTACK } } else { + // if num_bits == 1 script! { OP_TOALTSTACK } @@ -304,11 +276,6 @@ mod test { }; run(script); } - - let script = script! { - 0 { limb_to_be_bits(0) } 0 OP_EQUAL - }; - run(script); } #[test] @@ -385,11 +352,6 @@ mod test { }; run(script); } - - let script = script! { - 0 { limb_to_le_bits(0) } 0 OP_EQUAL - }; - run(script); } #[test] @@ -598,93 +560,4 @@ mod test { run(script); } } - - #[test] - fn test_u29_limb_from_bytes() { - assert_eq!(U254::N_LIMBS, 9); - let script = script! { - { 0x01 } - { 0x12 } - { 0x13 } - { 0x14 } - { U254::limb_from_bytes() } - { 0x01121314 } - OP_EQUAL - }; - run(script); - - let script = script! { - { 0x00 } - { 0x01 } - { 0x13 } - { 0x14 } - { U254::limb_from_bytes() } - { 0x00011314 } - OP_EQUAL - }; - run(script); - } - #[test] - fn test_u254_from_bytes() { - assert_eq!(U254::N_LIMBS, 9); - let script = script! { - { 0x00 } - { 0x01 } - { 0x13 } - { 0x14 } - - { 0x01 } - { 0x22 } - { 0x23 } - { 0x24 } - - { 0x01 } - { 0x32 } - { 0x33 } - { 0x34 } - - { 0x01 } - { 0x42 } - { 0x43 } - { 0x44 } - - { 0x01 } - { 0x52 } - { 0x53 } - { 0x54 } - - { 0x01 } - { 0x62 } - { 0x63 } - { 0x64 } - - { 0x01 } - { 0x72 } - { 0x73 } - { 0x74 } - - { 0x01 } - { 0x82 } - { 0x83 } - { 0x84 } - - { 0x01 } - { 0x92 } - { 0x93 } - { 0x94 } - - { U254::from_bytes() } - { 0x00011314 } - { 0x01222324 } - { 0x01323334 } - { 0x01424344 } - { 0x01525354 } - { 0x01626364 } - { 0x01727374 } - { 0x01828384 } - { 0x01929394 } - { U254::equal(0, 1) } - }; - run(script); - } } diff --git a/bitvm/src/bigint/cmp.rs b/bitvm/src/bigint/cmp.rs index 2bf37eee..ec1df178 100644 --- a/bitvm/src/bigint/cmp.rs +++ b/bitvm/src/bigint/cmp.rs @@ -27,6 +27,22 @@ impl BigIntImpl { } } + pub fn equal_keep_elements(a: u32, b: u32) -> Script { + script! { + { Self::copy_zip(a, b) } + for _ in 0..Self::N_LIMBS { + OP_EQUAL + OP_TOALTSTACK + } + for _ in 0..Self::N_LIMBS { + OP_FROMALTSTACK + } + for _ in 0..Self::N_LIMBS - 1 { + OP_BOOLAND + } + } + } + pub fn notequal(a: u32, b: u32) -> Script { script! { { Self::equal(a, b) } diff --git a/bitvm/src/bigint/inv.rs b/bitvm/src/bigint/inv.rs index 8f683adb..abfd1b59 100644 --- a/bitvm/src/bigint/inv.rs +++ b/bitvm/src/bigint/inv.rs @@ -48,7 +48,7 @@ impl BigIntImpl { pub fn limb_shr1_carry(num_bits: u32) -> Script { let powers_of_2_script = if num_bits < 7 { script! { - for i in 0..num_bits - 1 { + for i in 1..num_bits { { 2_u32.pow(i) } } } @@ -158,37 +158,43 @@ mod test { #[test] fn test_limb_shr1_carry() { - println!("limb_shr1_carry: {} bytes", limb_shr1_carry(29).len()); - let mut prng = ChaCha20Rng::seed_from_u64(0); + for shift in 2..30 { + println!( + "limb_shr1_carry({:?}): {} bytes", + shift, + limb_shr1_carry(shift).len() + ); + let mut prng = ChaCha20Rng::seed_from_u64(0); + + for _ in 0..100 { + let mut a: u32 = prng.gen(); + a %= 1 << shift; - for _ in 0..100 { - let mut a: u32 = prng.gen(); - a %= 1 << 29; - - let script = script! { - { a } - { 0 } - { limb_shr1_carry(29) } - { a & 1 } OP_EQUALVERIFY - { a >> 1 } OP_EQUAL - }; + let script = script! { + { a } + { 0 } + { limb_shr1_carry(shift) } + { a & 1 } OP_EQUALVERIFY + { a >> 1 } OP_EQUAL + }; - run(script); - } + run(script); + } - for _ in 0..100 { - let mut a: u32 = prng.gen(); - a %= 1 << 29; + for _ in 0..100 { + let mut a: u32 = prng.gen(); + a %= 1 << shift; - let script = script! { - { a } - { 1 } - { limb_shr1_carry(29) } - { a & 1 } OP_EQUALVERIFY - { (1 << 28) | (a >> 1) } OP_EQUAL - }; + let script = script! { + { a } + { 1 } + { limb_shr1_carry(shift) } + { a & 1 } OP_EQUALVERIFY + { (1 << (shift - 1)) | (a >> 1) } OP_EQUAL + }; - run(script); + run(script); + } } } diff --git a/bitvm/src/bigint/mod.rs b/bitvm/src/bigint/mod.rs index 323d62a3..441296fa 100644 --- a/bitvm/src/bigint/mod.rs +++ b/bitvm/src/bigint/mod.rs @@ -15,6 +15,8 @@ impl BigIntImpl { pub const N_LIMBS: u32 = N_BITS.div_ceil(LIMB_SIZE); pub const HEAD: u32 = N_BITS - (Self::N_LIMBS - 1) * LIMB_SIZE; pub const HEAD_OFFSET: u32 = 1u32 << Self::HEAD; + const _ASSERTION1: () = assert!(Self::N_LIMBS > 1); + const _ASSERTION2: () = assert!(Self::LIMB_SIZE < 31); } pub type U254 = BigIntImpl<254, 29>; diff --git a/bitvm/src/bigint/mul.rs b/bitvm/src/bigint/mul.rs index 6a7a3d54..51f2cde0 100644 --- a/bitvm/src/bigint/mul.rs +++ b/bitvm/src/bigint/mul.rs @@ -7,18 +7,15 @@ impl BigIntImpl { script! { { Self::convert_to_be_bits_toaltstack() } - { push_to_stack(0,Self::N_LIMBS as usize) } - - OP_FROMALTSTACK OP_IF - { Self::copy(1) } - { Self::add(1, 0) } + { Self::copy(0) } + OP_ELSE + { push_to_stack(0, Self::N_LIMBS as usize)} OP_ENDIF for _ in 1..N_BITS - 1 { - { Self::roll(1) } - { Self::double(0) } + { Self::double(1) } { Self::roll(1) } OP_FROMALTSTACK OP_IF @@ -27,8 +24,7 @@ impl BigIntImpl { OP_ENDIF } - { Self::roll(1) } - { Self::double(0) } + { Self::double(1) } OP_FROMALTSTACK OP_IF { Self::add(1, 0) } diff --git a/bitvm/src/bigint/std.rs b/bitvm/src/bigint/std.rs index 106fe205..b2fbef1b 100644 --- a/bitvm/src/bigint/std.rs +++ b/bitvm/src/bigint/std.rs @@ -23,6 +23,27 @@ struct TransformStep { } impl BigIntImpl { + pub fn biguint_to_limbs(x: BigUint) -> Vec { + let mut limbs = vec![]; + let bits: Vec = (0..N_BITS).map(|i| x.bit(i as u64)).collect(); + for chunk in bits.chunks(LIMB_SIZE as usize) { + let mut limb_value = 0u32; + for (i, bit_value) in chunk.into_iter().enumerate() { + limb_value += (*bit_value as u32) << i; + } + limbs.push(limb_value); + } + limbs + } + + pub fn push_biguint(x: BigUint) -> Script { + script! { + for limb in Self::biguint_to_limbs(x).iter().rev() { + { *limb } + } + } + } + pub fn push_u32_le(v: &[u32]) -> Script { let mut bits = vec![]; for elem in v.iter() { @@ -30,6 +51,21 @@ impl BigIntImpl { bits.push((elem & (1 << i)) != 0); } } + // make sure most significant 1 lies inside the limits + let ms_one = if bits.len() > 0 { + let mut ms_one = bits.len() - 1; + while !bits[ms_one] { + if ms_one != 0 { + ms_one -= 1; + } else { + break; + } + } + ms_one + } else { + 0 + }; + assert!(ms_one < Self::N_BITS as usize); bits.resize(N_BITS as usize, false); let mut limbs = vec![]; @@ -159,7 +195,7 @@ impl BigIntImpl { a = (a + 1) * Self::N_LIMBS - 1; script! { - if a < 134 { + if a < 128 { for _ in 0..Self::N_LIMBS { { a } OP_PICK } @@ -307,6 +343,21 @@ impl BigIntImpl { } } + pub fn check_validity() -> Script { + script! { // a0 a1 ... an + { 1 << LIMB_SIZE } // a0 a1 ... an x + for _ in 0..Self::N_LIMBS-2 { // a x + OP_TUCK // x a x + 0 OP_SWAP // x a 0 x + OP_WITHIN OP_VERIFY // x + } // a0 a1 x + 0 OP_SWAP // a0 a1 0 x + OP_WITHIN OP_VERIFY // a0 + 0 { Self::HEAD_OFFSET } // a0 0 y + OP_WITHIN OP_VERIFY + } + } + /// Resize positive numbers /// /// # Note @@ -488,6 +539,81 @@ impl BigIntImpl { ) } } + + /// Validate that the BigInt on stack has valid limb values + /// + /// This function checks that each limb in the BigInt representation + /// does not exceed the maximum value allowed for the given LIMB_SIZE + /// and ensures no limb is negative. + /// + /// ## Stack Effects: + /// - Input: BigInt limbs (MSB first, LSB on top) + /// - Output: Same BigInt limbs + validation result (1 if valid, 0 if invalid) + /// + /// ## Validation Rules: + /// - Each limb must be >= 0 (no negative values) + /// - Each limb must be < (1 << LIMB_SIZE) + /// - The head limb must be < (1 << HEAD_OFFSET) where HEAD_OFFSET is the remaining bits + /// + /// ## Note: + /// This function is expensive in terms of script size and should be used carefully + pub fn is_valid_bigint_with_limb_size(limb_size: u32) -> Script { + let n_limbs = N_BITS.div_ceil(limb_size); + let head = N_BITS - (n_limbs - 1) * limb_size; + script! { + // Start with validation result = 1 (valid) + 1 + + // Validate each regular limb (except the head) + for i in 0..n_limbs - 1 { + // Pick the limb from stack (limbs are ordered MSB first, LSB on top) + { i + 1 } OP_PICK + + // Check if limb >= 0 (not negative) + OP_DUP + 0 OP_GREATERTHANOREQUAL + + // Check if limb < (1 << LIMB_SIZE) + OP_SWAP + { 1 << limb_size } + OP_LESSTHAN + + // AND the boolean results + OP_BOOLAND OP_BOOLAND + } + + // Validate the head limb (MSB) separately as it may have fewer bits + { n_limbs } OP_PICK + + // Check if head limb >= 0 (not negative) + OP_DUP + 0 OP_GREATERTHANOREQUAL + + // Check if head limb < (1 << HEAD) + OP_SWAP + { 1 << head } + OP_LESSTHAN + + // AND the boolean results + OP_BOOLAND OP_BOOLAND + } + } + + /// Validate BigInt and fail script if invalid + pub fn verify_bigint_on_stack_with_limb_size(limb_size: u32) -> Script { + script! { + { Self::is_valid_bigint_with_limb_size(limb_size) } + OP_VERIFY + } + } + + pub fn verify_bigint_on_stack() -> Script { + Self::verify_bigint_on_stack_with_limb_size(Self::LIMB_SIZE) + } + + pub fn is_valid_bigint() -> Script { + Self::is_valid_bigint_with_limb_size(Self::LIMB_SIZE) + } } /// Extracts a window of bits from a u32 limb on top of stack @@ -538,13 +664,50 @@ pub fn extract_digits(start_index: u32, window: u32) -> Script { #[cfg(test)] mod test { use crate::bigint::std::extract_digits; + use crate::bigint::U256; use crate::bigint::{BigIntImpl, U254}; - use crate::run; + use crate::{execute_script, run}; use bitcoin_script::script; + use num_bigint::{BigUint, RandBigInt}; use rand::{Rng, SeedableRng}; use rand_chacha::ChaCha20Rng; + #[test] + fn test_valid_bigint() { + let invalid_bigint = ( + script! { + {0} {0} {0} {0} {0} {0} {0} {0} {-1} + }, + false, + ); + + let invalid_bigint2 = ( + script! { + {1 << U256::HEAD} {0} {0} {0} {0} {0} {0} {0} {0} + }, + false, + ); + + let valid_bigint = ( + script! { + {0x1234} {0x1234} {0x1234} {0x1234} {0x1234} {0x1234} {0x1234} {0x1234} {0x1234} + }, + true, + ); + + for (bigint, expected) in [invalid_bigint, invalid_bigint2, valid_bigint] { + let res = execute_script(script! { + {bigint.clone()} + { U256::is_valid_bigint() } + OP_TOALTSTACK + for _ in 0..9 { OP_DROP } + OP_FROMALTSTACK + }); + assert_eq!(res.success, expected); + } + } + #[test] fn test_zip() { const N_BITS: u32 = 1450; @@ -1079,4 +1242,19 @@ mod test { assert!(res.success); } } + + #[test] + fn test_biguint_to_limbs() { + const LIMB_SIZE: u32 = 29; + type U256 = BigIntImpl<256, LIMB_SIZE>; + let mut prng = ChaCha20Rng::seed_from_u64(37); + for _ in 0..100 { + let x: BigUint = prng.gen_biguint(256); + let mut sum = BigUint::from(0u32); + for limb in U256::biguint_to_limbs(x.clone()).iter().rev() { + sum = (sum * (1u32 << LIMB_SIZE)) + limb; + } + assert_eq!(sum, x); + } + } } diff --git a/bitvm/src/bigint/sub.rs b/bitvm/src/bigint/sub.rs index 537632aa..92cb72c4 100644 --- a/bitvm/src/bigint/sub.rs +++ b/bitvm/src/bigint/sub.rs @@ -31,6 +31,32 @@ impl BigIntImpl { } } } + + pub fn neg() -> Script { + script! { // ... a_n + { (1 << LIMB_SIZE) - 1 } // ... a_n x + + for _ in 0..Self::N_LIMBS-2 { // ... a_{i-1} a_i x + OP_TUCK // ... a_{i-1} x a_i x + OP_SWAP // ... a_{i-1} x x a_i + OP_SUB // ... a_{i-1} x x-a_i + OP_TOALTSTACK // ... a_{i-1} x + } + // a_0 a_1 x + OP_SWAP // a_0 x a_1 + OP_SUB // a_0 x-a_1 + OP_TOALTSTACK // a_0 + + { Self::HEAD_OFFSET-1 } + OP_SWAP + OP_SUB + + for _ in 0..Self::N_LIMBS-1 { + OP_FROMALTSTACK + } + { Self::add1() } + } + } } /// Compute the difference of two limbs, including the carry bit @@ -134,4 +160,23 @@ mod test { run(script); } } + + #[test] + fn test_neg() { + println!("U254.neg: {} bytes", U254::neg().len()); + let mut prng = ChaCha20Rng::seed_from_u64(0); + + let a: BigUint = prng.sample(RandomBits::new(254)); + + let script = script! { + { U254::push_zero() } + { U254::push_u32_le(&a.to_u32_digits()) } + { U254::sub(1, 0) } + { U254::push_u32_le(&a.to_u32_digits()) } + { U254::neg() } + { U254::equalverify(1, 0) } + OP_TRUE + }; + run(script); + } } diff --git a/bitvm/src/bn254/fp254impl.rs b/bitvm/src/bn254/fp254impl.rs index 3662b910..ac9abb34 100644 --- a/bitvm/src/bn254/fp254impl.rs +++ b/bitvm/src/bn254/fp254impl.rs @@ -1,14 +1,11 @@ use crate::bigint::add::limb_add_carry; use crate::bigint::sub::limb_sub_borrow; use crate::bigint::U254; -use crate::bn254::fq::Fq; -use crate::bn254::utils::Hint; use crate::treepp::*; use ark_ff::PrimeField; use bitcoin_script::script; use num_bigint::{BigInt, BigUint}; use num_traits::Num; -use std::str::FromStr; use std::sync::OnceLock; #[allow(clippy::declare_interior_mutable_const)] @@ -28,6 +25,10 @@ pub trait Fp254Impl { type ConstantType: PrimeField; + fn modulus_as_bigint() -> BigInt { + BigInt::from_str_radix(Self::MODULUS, 16).unwrap() + } + #[inline] fn copy(a: u32) -> Script { U254::copy(a) @@ -106,6 +107,11 @@ pub trait Fp254Impl { U254::equal(a, b) } + #[inline] + fn equal_keep_elements(a: u32, b: u32) -> Script { + U254::equal_keep_elements(a, b) + } + #[inline] fn equalverify(a: u32, b: u32) -> Script { U254::equalverify(a, b) @@ -126,31 +132,20 @@ pub trait Fp254Impl { } } - fn is_one_keep_element(a: u32) -> Script { + fn is_one_verify() -> Script { script! { - { Self::copy(a) } - { Self::is_one() } + OP_1 + OP_EQUALVERIFY + for _ in 0..Self::N_LIMBS-1 { + OP_NOT OP_VERIFY + } } } - fn is_field() -> Script { + fn is_one_keep_element(a: u32) -> Script { script! { - // Each limb must not be negative - for i in 0..Self::N_LIMBS - 1 { - { i } OP_PICK - 0 OP_GREATERTHANOREQUAL OP_TOALTSTACK - } - { Self::N_LIMBS - 1 } OP_PICK - 0 OP_GREATERTHANOREQUAL - for _ in 0..Self::N_LIMBS - 1 { - OP_FROMALTSTACK OP_BOOLAND - } - OP_TOALTSTACK - - { Self::push_modulus() } - { U254::lessthan(1, 0) } - - OP_FROMALTSTACK OP_BOOLAND + { Self::copy(a) } + { Self::is_one() } } } @@ -205,9 +200,7 @@ pub trait Fp254Impl { // ⋯ A₈ B₈ A₇ B₇ A₆ B₆ A₅ B₅ A₄ B₄ A₃ B₃ A₂ B₂ C₁⁺ (B₁+C₀)+A₁ 2²⁹ C₀⁻ M₁ OP_ADD // ⋯ A₈ B₈ A₇ B₇ A₆ B₆ A₅ B₅ A₄ B₄ A₃ B₃ A₂ B₂ C₁⁺ (B₁+C₀)+A₁ 2²⁹ C₀⁻+M₁ - OP_ROT OP_SWAP - OP_ROT // ⋯ A₈ B₈ A₇ B₇ A₆ B₆ A₅ B₅ A₄ B₄ A₃ B₃ A₂ B₂ C₁⁺ (B₁+C₀)+A₁ C₀⁻+M₁ 2²⁹ limb_sub_borrow OP_TOALTSTACK @@ -275,51 +268,25 @@ pub trait Fp254Impl { { Self::roll(a) } { Self::is_zero_keep_element(0) } OP_NOTIF - // ⋯ A₈ A₇ A₆ A₅ A₄ A₃ A₂ A₁ A₀ - { Self::MODULUS_LIMBS[0] } OP_SWAP { 0x20000000 } - limb_sub_borrow OP_TOALTSTACK - // ⋯ A₈ A₇ A₆ A₅ A₄ A₃ A₂ A₁ 2²⁹ C₀⁻ | M₀-A₀ ⋯ - OP_ROT OP_ADD - // ⋯ A₈ A₇ A₆ A₅ A₄ A₃ A₂ 2²⁹ C₀⁻+A₁ - { Self::MODULUS_LIMBS[1] } OP_SWAP OP_ROT - limb_sub_borrow OP_TOALTSTACK - // ⋯ A₈ A₇ A₆ A₅ A₄ A₃ A₂ 2²⁹ C₁⁻ | M₁-(C₀⁻+A₁) ⋯ - OP_ROT OP_ADD - // ⋯ A₈ A₇ A₆ A₅ A₄ A₃ 2²⁹ C₁⁻+A₂ - { Self::MODULUS_LIMBS[2] } OP_SWAP OP_ROT - limb_sub_borrow OP_TOALTSTACK - // ⋯ A₈ A₇ A₆ A₅ A₄ A₃ 2²⁹ C₂⁻ | M₂-(C₁⁻+A₂) ⋯ - OP_ROT OP_ADD - // ⋯ A₈ A₇ A₆ A₅ A₄ 2²⁹ C₂⁻+A₃ - { Self::MODULUS_LIMBS[3] } OP_SWAP OP_ROT - limb_sub_borrow OP_TOALTSTACK - // ⋯ A₈ A₇ A₆ A₅ A₄ 2²⁹ C₃⁻ | M₃-(C₂⁻+A₃) ⋯ - OP_ROT OP_ADD - // ⋯ A₈ A₇ A₆ A₅ 2²⁹ C₃⁻+A₄ - { Self::MODULUS_LIMBS[4] } OP_SWAP OP_ROT - limb_sub_borrow OP_TOALTSTACK - // ⋯ A₈ A₇ A₆ A₅ 2²⁹ C₄⁻ | M₄-(C₃⁻+A₄) ⋯ - OP_ROT OP_ADD - // ⋯ A₈ A₇ A₆ 2²⁹ C₄⁻+A₅ - { Self::MODULUS_LIMBS[5] } OP_SWAP OP_ROT - limb_sub_borrow OP_TOALTSTACK - // ⋯ A₈ A₇ A₆ 2²⁹ C₅⁻ | M₅-(C₄⁻+A₅) ⋯ - OP_ROT OP_ADD - // ⋯ A₈ A₇ 2²⁹ C₅⁻+A₆ - { Self::MODULUS_LIMBS[6] } OP_SWAP OP_ROT - limb_sub_borrow OP_TOALTSTACK - // ⋯ A₈ A₇ 2²⁹ C₆⁻ | M₆-(C₅⁻+A₆) ⋯ - OP_ROT OP_ADD - // ⋯ A₈ 2²⁹ C₆⁻+A₇ - { Self::MODULUS_LIMBS[7] } OP_SWAP OP_ROT - limb_sub_borrow OP_TOALTSTACK - // ⋯ A₈ 2²⁹ C₇⁻ | M₇-(C₆⁻+A₇) ⋯ - OP_NIP OP_ADD - // ⋯ C₇⁻+A₈ - { Self::MODULUS_LIMBS[8] } OP_SWAP OP_SUB - // ⋯ M₈-(C₇⁻+A₈) - OP_FROMALTSTACK OP_FROMALTSTACK OP_FROMALTSTACK OP_FROMALTSTACK - OP_FROMALTSTACK OP_FROMALTSTACK OP_FROMALTSTACK OP_FROMALTSTACK + for i in 0..Self::N_LIMBS-1 { + { Self::MODULUS_LIMBS[i as usize] } OP_SWAP + if i == 0 { + { 2_usize.pow(U254::LIMB_SIZE) } + } else { + OP_ROT + } + limb_sub_borrow OP_TOALTSTACK + if i == Self::N_LIMBS-2 { + OP_NIP + } else { + OP_ROT + } + OP_ADD + } + { Self::MODULUS_LIMBS[Self::N_LIMBS as usize - 1] } OP_SWAP OP_SUB + for _ in 0..Self::N_LIMBS-1 { + OP_FROMALTSTACK + } OP_ENDIF } } @@ -560,369 +527,78 @@ pub trait Fp254Impl { } } - 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(); - 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 + // verifies that the element at the top of the stack is less than the modulo and valid (limbs are in range) + // doesn't consume the element, instead sends it to the altstack + fn check_validity() -> Script { + let limbs_of_c = U254::biguint_to_limbs(Self::modulus_as_bigint().to_biguint().unwrap()); + script! { + // (Assuming limbs are numbered big endian) + // Number A is greater than number B <=> there exists a limb i, s.t. (A_i > B_i OR (A_i >= B_i and i is the first limb)) and there's no limb j > i satisfying A_i < B_i + // Script below maintains if such state exists for each i behind the foremost limb, combining the results and negating them if there is such j + for i in 0..(Self::N_LIMBS as usize) { + OP_DUP OP_DUP OP_TOALTSTACK + { 0 } { 1 << U254::LIMB_SIZE } OP_WITHIN OP_VERIFY + if i == 0 { + { limbs_of_c[i] } + OP_GREATERTHANOREQUAL + } else { + { limbs_of_c[i] } OP_2DUP + OP_GREATERTHAN OP_TOALTSTACK + OP_GREATERTHANOREQUAL + OP_BOOLAND + OP_FROMALTSTACK OP_BOOLOR + } + if i == (Self::N_LIMBS as usize) - 1 { + OP_NOT OP_VERIFY //This OP_NOT can be negated, but it probably isn't necessary + } else { + OP_SWAP + } } - // { Fq::push(ark_bn254::Fq::from_str(&q.to_string()).unwrap()) } - { Fq::roll(a_depth + 1) } - { Fq::roll(b_depth + 1) } - { Fq::tmul() } - }; - hints.push(Hint::BigIntegerTmulLC1(q)); - - (script, hints) + } } - // 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(); - let modulus = &Fq::modulus_as_bigint(); - let q = (x * y) / modulus; - - let script = script! { + fn check_validity_and_keep_element() -> Script { + script! { + { Self::check_validity() } for _ in 0..Self::N_LIMBS { - OP_DEPTH OP_1SUB OP_ROLL // hints + OP_FROMALTSTACK } - // { Fq::push(ark_bn254::Fq::from_str(&q.to_string()).unwrap()) } - { Fq::roll(1) } - { Fq::push(*constant) } - { Fq::tmul() } - }; - hints.push(Hint::BigIntegerTmulLC1(q)); - - (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(); - 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::push(ark_bn254::Fq::from_str(&q.to_string()).unwrap()) } - { Fq::copy(a_depth + 1) } - { Fq::copy(b_depth + 2) } - { Fq::tmul() } - }; - hints.push(Hint::BigIntegerTmulLC1(q)); - - (script, hints) } - #[allow(clippy::too_many_arguments)] - fn hinted_mul_lc2( - a_depth: u32, - a: ark_bn254::Fq, - b_depth: u32, - b: ark_bn254::Fq, - c_depth: u32, - c: ark_bn254::Fq, - d_depth: u32, - d: ark_bn254::Fq, - ) -> (Script, Vec) { - assert!(a_depth > b_depth && b_depth > c_depth && c_depth > d_depth); - - let mut hints = Vec::new(); - - let modulus = &Fq::modulus_as_bigint(); - - let x = BigInt::from_str(&a.to_string()).unwrap(); - let y = BigInt::from_str(&b.to_string()).unwrap(); - let z = BigInt::from_str(&c.to_string()).unwrap(); - let w = BigInt::from_str(&d.to_string()).unwrap(); - - let q = (x * z + y * w) / modulus; - - let script = script! { - for _ in 0..Self::N_LIMBS { - OP_DEPTH OP_1SUB OP_ROLL // hints - } - // { Fq::push(ark_bn254::Fq::from_str(&q.to_string()).unwrap()) } - { Fq::roll(a_depth + 1) } - { Fq::roll(b_depth + 2) } - { Fq::roll(c_depth + 3) } - { Fq::roll(d_depth + 4) } - { Fq::tmul_lc2() } - }; - hints.push(Hint::BigIntegerTmulLC2(q)); - - (script, hints) - } - - // Assumes tmul hint (1 BigInteger) at the top of stack - // and operands (a, b, c, d) at stack depths (a_depth, b_depth, c_depth, d_depth) - // Computes r = a * c + b * d (mod p) - #[allow(clippy::too_many_arguments)] - fn hinted_mul_lc2_w4( - a_depth: u32, - a: ark_bn254::Fq, - b_depth: u32, - b: ark_bn254::Fq, - c_depth: u32, - c: ark_bn254::Fq, - d_depth: u32, - d: ark_bn254::Fq, - ) -> (Script, Vec) { - assert!(a_depth > b_depth && b_depth > c_depth && c_depth > d_depth); - - let mut hints = Vec::with_capacity(1); - - let modulus = &Fq::modulus_as_bigint(); - - let x = BigInt::from_str(&a.to_string()).unwrap(); - let y = BigInt::from_str(&b.to_string()).unwrap(); - let z = BigInt::from_str(&c.to_string()).unwrap(); - let w = BigInt::from_str(&d.to_string()).unwrap(); - - let q = (x * z + y * w) / modulus; - - let script = script! { - for _ in 0..Self::N_LIMBS { - OP_DEPTH OP_1SUB OP_ROLL // hints - } - // { Fq::push(ark_bn254::Fq::from_str(&q.to_string()).unwrap()) } - { Fq::roll(a_depth + 1) } - { Fq::roll(b_depth + 2) } - { Fq::roll(c_depth + 3) } - { Fq::roll(d_depth + 4) } - { Fq::tmul_lc2_w4() } - }; - hints.push(Hint::BigIntegerTmulLC2(q)); - - (script, hints) - } - - #[allow(clippy::too_many_arguments)] - fn hinted_mul_lc4( - a_depth: u32, - a: ark_bn254::Fq, - b_depth: u32, - b: ark_bn254::Fq, - c_depth: u32, - c: ark_bn254::Fq, - d_depth: u32, - d: ark_bn254::Fq, - - e_depth: u32, - e: ark_bn254::Fq, - f_depth: u32, - f: ark_bn254::Fq, - g_depth: u32, - g: ark_bn254::Fq, - h_depth: u32, - h: ark_bn254::Fq, - ) -> (Script, Vec) { - assert!( - a_depth > b_depth - && b_depth > c_depth - && c_depth > d_depth - && d_depth > e_depth - && e_depth > f_depth - && f_depth > g_depth - && g_depth > h_depth - ); - - let mut hints = Vec::new(); - - let modulus = &Fq::modulus_as_bigint(); - - let x1 = BigInt::from_str(&a.to_string()).unwrap(); - let y1 = BigInt::from_str(&b.to_string()).unwrap(); - let z1 = BigInt::from_str(&c.to_string()).unwrap(); - let w1 = BigInt::from_str(&d.to_string()).unwrap(); - - let x2 = BigInt::from_str(&e.to_string()).unwrap(); - let y2 = BigInt::from_str(&f.to_string()).unwrap(); - let z2 = BigInt::from_str(&g.to_string()).unwrap(); - let w2 = BigInt::from_str(&h.to_string()).unwrap(); - - let q = (x1 * x2 + y1 * y2 + z1 * z2 + w1 * w2) / modulus; - - let script = script! { - for _ in 0..Self::N_LIMBS { - OP_DEPTH OP_1SUB OP_ROLL // hints - } - // { fq_push(ark_bn254::Fq::from_str(&q.to_string()).unwrap()) } - { Fq::roll(a_depth + 1) } - { Fq::roll(b_depth + 2) } - { Fq::roll(c_depth + 3) } - { Fq::roll(d_depth + 4) } - { Fq::roll(e_depth + 5) } - { Fq::roll(f_depth + 6) } - { Fq::roll(g_depth + 7) } - { Fq::roll(h_depth + 8) } - { Fq::tmul_lc4() } - }; - hints.push(Hint::BigIntegerTmulLC4(q)); - - (script, hints) - } - - #[allow(clippy::too_many_arguments)] - fn hinted_mul_lc2_keep_elements( - a_depth: u32, - a: ark_bn254::Fq, - b_depth: u32, - b: ark_bn254::Fq, - c_depth: u32, - c: ark_bn254::Fq, - d_depth: u32, - d: ark_bn254::Fq, - ) -> (Script, Vec) { - assert!(a_depth > b_depth && b_depth > c_depth && c_depth > d_depth); - - let mut hints = Vec::new(); - - let modulus = &Fq::modulus_as_bigint(); - - let x = BigInt::from_str(&a.to_string()).unwrap(); - let y = BigInt::from_str(&b.to_string()).unwrap(); - let z = BigInt::from_str(&c.to_string()).unwrap(); - let w = BigInt::from_str(&d.to_string()).unwrap(); - - let q = (x * z + y * w) / modulus; - - let script = script! { - for _ in 0..Self::N_LIMBS { - OP_DEPTH OP_1SUB OP_ROLL // hints - } - // { Fq::push(ark_bn254::Fq::from_str(&q.to_string()).unwrap()) } - { Fq::copy(a_depth + 1) } - { Fq::copy(b_depth + 2) } - { Fq::copy(c_depth + 3) } - { Fq::copy(d_depth + 4) } - { Fq::tmul_lc2() } - }; - hints.push(Hint::BigIntegerTmulLC2(q)); - - (script, hints) - } - - // Same as hinted_mul_lc2_keep_elements(), except retains operands (a, b, c, d) on stack - #[allow(clippy::too_many_arguments)] - fn hinted_mul_lc2_keep_elements_w4( - a_depth: u32, - a: ark_bn254::Fq, - b_depth: u32, - b: ark_bn254::Fq, - c_depth: u32, - c: ark_bn254::Fq, - d_depth: u32, - d: ark_bn254::Fq, - ) -> (Script, Vec) { - assert!(a_depth > b_depth && b_depth > c_depth && c_depth > d_depth); - - let mut hints = Vec::with_capacity(1); - - let modulus = &Fq::modulus_as_bigint(); - - let x = BigInt::from_str(&a.to_string()).unwrap(); - let y = BigInt::from_str(&b.to_string()).unwrap(); - let z = BigInt::from_str(&c.to_string()).unwrap(); - let w = BigInt::from_str(&d.to_string()).unwrap(); - - let q = (x * z + y * w) / modulus; - - let script = script! { - for _ in 0..Self::N_LIMBS { - OP_DEPTH OP_1SUB OP_ROLL // hints - } - // { Fq::push(ark_bn254::Fq::from_str(&q.to_string()).unwrap()) } - { Fq::copy(a_depth + 1) } - { Fq::copy(b_depth + 2) } - { Fq::copy(c_depth + 3) } - { Fq::copy(d_depth + 4) } - { Fq::tmul_lc2_w4() } - }; - hints.push(Hint::BigIntegerTmulLC2(q)); - - (script, hints) - } - - // 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(); - 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::push(ark_bn254::Fq::from_str(&q.to_string()).unwrap()) } - { Fq::roll(1) } - { Fq::copy(0) } - { Fq::tmul() } - }; - hints.push(Hint::BigIntegerTmulLC1(q)); - - (script, hints) - } - - fn hinted_inv(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 y = &x.modinv(modulus).unwrap(); - let q = (x * y) / modulus; - let script = script! { - for _ in 0..Self::N_LIMBS { - OP_DEPTH OP_1SUB OP_ROLL // hints - } - for _ in 0..Self::N_LIMBS { - OP_DEPTH OP_1SUB OP_ROLL // hints + // finds if the element at the top of the stack is less than the modulo and valid (limbs are in range) + // consumes the element, leaves the result at the topstack + fn is_valid() -> Script { + let limbs_of_c = U254::biguint_to_limbs(Self::modulus_as_bigint().to_biguint().unwrap()); + script! { + // (Assuming limbs are numbered big endian) + // Number A is greater than number B <=> there exists a limb i, s.t. (A_i > B_i OR (A_i >= B_i and i is the first limb)) and there's no limb j > i satisfying A_i < B_i + // Script below maintains if such state exists for each i behind the foremost limb, combining the results and negating them if there is such j + for i in 0..(Self::N_LIMBS as usize) { + OP_DUP + { 0 } { 1 << U254::LIMB_SIZE } OP_WITHIN + if i == 0 { + OP_TOALTSTACK //u254 validity check + + { limbs_of_c[i] } + OP_GREATERTHANOREQUAL + } else { + OP_FROMALTSTACK OP_BOOLAND OP_TOALTSTACK //u254 validity check + + { limbs_of_c[i] } OP_2DUP + OP_GREATERTHAN OP_TOALTSTACK + OP_GREATERTHANOREQUAL + OP_BOOLAND + OP_FROMALTSTACK OP_BOOLOR + } + if i == (Self::N_LIMBS as usize) - 1 { + OP_NOT //This OP_NOT can be negated, but it probably isn't necessary + OP_FROMALTSTACK + OP_BOOLAND + } else { + OP_SWAP + } } - // { Fq::push(ark_bn254::Fq::from_str(&y.to_string()).unwrap()) } - // { Fq::push(ark_bn254::Fq::from_str(&q.to_string()).unwrap()) } - // x, y, q - { Fq::roll(2) } - { Fq::copy(2) } - // y, q, x, y - { Fq::tmul() } - // y, 1 - { Fq::push_one() } - { Fq::equalverify(1, 0) } - }; - hints.push(Hint::Fq(ark_bn254::Fq::from_str(&y.to_string()).unwrap())); - hints.push(Hint::BigIntegerTmulLC1(q)); - - (script, hints) + } } } diff --git a/bitvm/src/bn254/fq.rs b/bitvm/src/bn254/fq.rs index d4f0052a..a21fa9a6 100644 --- a/bitvm/src/bn254/fq.rs +++ b/bitvm/src/bn254/fq.rs @@ -1,9 +1,12 @@ #![allow(clippy::reversed_empty_ranges)] +use std::str::FromStr; + use num_bigint::{BigInt, BigUint}; -use num_traits::{FromPrimitive, Num, ToPrimitive}; +use num_traits::{FromPrimitive, ToPrimitive}; use crate::bigint::BigIntImpl; use crate::bn254::fp254impl::Fp254Impl; +use crate::bn254::utils::Hint; use crate::pseudo::NMUL; use crate::treepp::*; @@ -31,10 +34,6 @@ impl Fp254Impl for Fq { } impl Fq { - pub fn modulus_as_bigint() -> BigInt { - BigInt::from_str_radix(Self::MODULUS, 16).unwrap() - } - pub fn tmul() -> Script { script! { { ::tmul() } @@ -63,22 +62,32 @@ impl Fq { } } - pub const fn bigint_tmul_lc_1() -> (u32, u32) { + pub const fn bigint_tmul_lc_1() -> (u32, u32, u32) { const X: u32 = ::T::N_BITS; - const Y: u32 = ::LIMB_SIZE; - (X, Y) + const Y: u32 = ::T::LIMB_SIZE; + const Z: u32 = ::T::N_LIMBS; + (X, Y, Z) } - pub const fn bigint_tmul_lc_2() -> (u32, u32) { + pub const fn bigint_tmul_lc_2() -> (u32, u32, u32) { const X: u32 = ::T::N_BITS; - const Y: u32 = ::LIMB_SIZE; - (X, Y) + const Y: u32 = ::T::LIMB_SIZE; + const Z: u32 = ::T::N_LIMBS; + (X, Y, Z) } - pub const fn bigint_tmul_lc_4() -> (u32, u32) { + pub const fn bigint_tmul_lc_2_w4() -> (u32, u32, u32) { + const X: u32 = ::T::N_BITS; + const Y: u32 = ::T::LIMB_SIZE; + const Z: u32 = ::T::N_LIMBS; + (X, Y, Z) + } + + pub const fn bigint_tmul_lc_4() -> (u32, u32, u32) { const X: u32 = ::T::N_BITS; - const Y: u32 = ::LIMB_SIZE; - (X, Y) + const Y: u32 = ::T::LIMB_SIZE; + const Z: u32 = ::T::N_LIMBS; + (X, Y, Z) } #[inline] @@ -87,6 +96,375 @@ impl Fq { { Fq::push_u32_le(&BigUint::from(a).to_u32_digits()) } } } + + pub 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(); + 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::push(ark_bn254::Fq::from_str(&q.to_string()).unwrap()) } + { Fq::roll(a_depth + 1) } + { Fq::roll(b_depth + 1) } + { Fq::tmul() } + }; + hints.push(Hint::BigIntegerTmulLC1(q)); + + (script, hints) + } + + // TODO: Optimize by using the constant feature + pub 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(); + 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::push(ark_bn254::Fq::from_str(&q.to_string()).unwrap()) } + { Fq::roll(1) } + { Fq::push(*constant) } + { Fq::tmul() } + }; + hints.push(Hint::BigIntegerTmulLC1(q)); + + (script, hints) + } + + pub 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(); + 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::push(ark_bn254::Fq::from_str(&q.to_string()).unwrap()) } + { Fq::copy(a_depth + 1) } + { Fq::copy(b_depth + 2) } + { Fq::tmul() } + }; + hints.push(Hint::BigIntegerTmulLC1(q)); + + (script, hints) + } + + #[allow(clippy::too_many_arguments)] + pub fn hinted_mul_lc2( + a_depth: u32, + a: ark_bn254::Fq, + b_depth: u32, + b: ark_bn254::Fq, + c_depth: u32, + c: ark_bn254::Fq, + d_depth: u32, + d: ark_bn254::Fq, + ) -> (Script, Vec) { + assert!(a_depth > b_depth && b_depth > c_depth && c_depth > d_depth); + + let mut hints = Vec::new(); + + let modulus = &Fq::modulus_as_bigint(); + + let x = BigInt::from_str(&a.to_string()).unwrap(); + let y = BigInt::from_str(&b.to_string()).unwrap(); + let z = BigInt::from_str(&c.to_string()).unwrap(); + let w = BigInt::from_str(&d.to_string()).unwrap(); + + let q = (x * z + y * w) / modulus; + + let script = script! { + for _ in 0..Self::N_LIMBS { + OP_DEPTH OP_1SUB OP_ROLL // hints + } + // { Fq::push(ark_bn254::Fq::from_str(&q.to_string()).unwrap()) } + { Fq::roll(a_depth + 1) } + { Fq::roll(b_depth + 2) } + { Fq::roll(c_depth + 3) } + { Fq::roll(d_depth + 4) } + { Fq::tmul_lc2() } + }; + hints.push(Hint::BigIntegerTmulLC2(q)); + + (script, hints) + } + + // Assumes tmul hint (1 BigInteger) at the top of stack + // and operands (a, b, c, d) at stack depths (a_depth, b_depth, c_depth, d_depth) + // Computes r = a * c + b * d (mod p) + #[allow(clippy::too_many_arguments)] + pub fn hinted_mul_lc2_w4( + a_depth: u32, + a: ark_bn254::Fq, + b_depth: u32, + b: ark_bn254::Fq, + c_depth: u32, + c: ark_bn254::Fq, + d_depth: u32, + d: ark_bn254::Fq, + ) -> (Script, Vec) { + assert!(a_depth > b_depth && b_depth > c_depth && c_depth > d_depth); + + let mut hints = Vec::with_capacity(1); + + let modulus = &Fq::modulus_as_bigint(); + + let x = BigInt::from_str(&a.to_string()).unwrap(); + let y = BigInt::from_str(&b.to_string()).unwrap(); + let z = BigInt::from_str(&c.to_string()).unwrap(); + let w = BigInt::from_str(&d.to_string()).unwrap(); + + let q = (x * z + y * w) / modulus; + + let script = script! { + for _ in 0..Self::N_LIMBS { + OP_DEPTH OP_1SUB OP_ROLL // hints + } + // { Fq::push(ark_bn254::Fq::from_str(&q.to_string()).unwrap()) } + { Fq::roll(a_depth + 1) } + { Fq::roll(b_depth + 2) } + { Fq::roll(c_depth + 3) } + { Fq::roll(d_depth + 4) } + { Fq::tmul_lc2_w4() } + }; + hints.push(Hint::BigIntegerTmulLC2(q)); + + (script, hints) + } + + #[allow(clippy::too_many_arguments)] + pub fn hinted_mul_lc4( + a_depth: u32, + a: ark_bn254::Fq, + b_depth: u32, + b: ark_bn254::Fq, + c_depth: u32, + c: ark_bn254::Fq, + d_depth: u32, + d: ark_bn254::Fq, + + e_depth: u32, + e: ark_bn254::Fq, + f_depth: u32, + f: ark_bn254::Fq, + g_depth: u32, + g: ark_bn254::Fq, + h_depth: u32, + h: ark_bn254::Fq, + ) -> (Script, Vec) { + assert!( + a_depth > b_depth + && b_depth > c_depth + && c_depth > d_depth + && d_depth > e_depth + && e_depth > f_depth + && f_depth > g_depth + && g_depth > h_depth + ); + + let mut hints = Vec::new(); + + let modulus = &Fq::modulus_as_bigint(); + + let x1 = BigInt::from_str(&a.to_string()).unwrap(); + let y1 = BigInt::from_str(&b.to_string()).unwrap(); + let z1 = BigInt::from_str(&c.to_string()).unwrap(); + let w1 = BigInt::from_str(&d.to_string()).unwrap(); + + let x2 = BigInt::from_str(&e.to_string()).unwrap(); + let y2 = BigInt::from_str(&f.to_string()).unwrap(); + let z2 = BigInt::from_str(&g.to_string()).unwrap(); + let w2 = BigInt::from_str(&h.to_string()).unwrap(); + + let q = (x1 * x2 + y1 * y2 + z1 * z2 + w1 * w2) / modulus; + + let script = script! { + for _ in 0..Self::N_LIMBS { + OP_DEPTH OP_1SUB OP_ROLL // hints + } + // { fq_push(ark_bn254::Fq::from_str(&q.to_string()).unwrap()) } + { Fq::roll(a_depth + 1) } + { Fq::roll(b_depth + 2) } + { Fq::roll(c_depth + 3) } + { Fq::roll(d_depth + 4) } + { Fq::roll(e_depth + 5) } + { Fq::roll(f_depth + 6) } + { Fq::roll(g_depth + 7) } + { Fq::roll(h_depth + 8) } + { Fq::tmul_lc4() } + }; + hints.push(Hint::BigIntegerTmulLC4(q)); + + (script, hints) + } + + #[allow(clippy::too_many_arguments)] + pub fn hinted_mul_lc2_keep_elements( + a_depth: u32, + a: ark_bn254::Fq, + b_depth: u32, + b: ark_bn254::Fq, + c_depth: u32, + c: ark_bn254::Fq, + d_depth: u32, + d: ark_bn254::Fq, + ) -> (Script, Vec) { + assert!(a_depth > b_depth && b_depth > c_depth && c_depth > d_depth); + + let mut hints = Vec::new(); + + let modulus = &Fq::modulus_as_bigint(); + + let x = BigInt::from_str(&a.to_string()).unwrap(); + let y = BigInt::from_str(&b.to_string()).unwrap(); + let z = BigInt::from_str(&c.to_string()).unwrap(); + let w = BigInt::from_str(&d.to_string()).unwrap(); + + let q = (x * z + y * w) / modulus; + + let script = script! { + for _ in 0..Self::N_LIMBS { + OP_DEPTH OP_1SUB OP_ROLL // hints + } + // { Fq::push(ark_bn254::Fq::from_str(&q.to_string()).unwrap()) } + { Fq::copy(a_depth + 1) } + { Fq::copy(b_depth + 2) } + { Fq::copy(c_depth + 3) } + { Fq::copy(d_depth + 4) } + { Fq::tmul_lc2() } + }; + hints.push(Hint::BigIntegerTmulLC2(q)); + + (script, hints) + } + + // Same as hinted_mul_lc2_keep_elements(), except retains operands (a, b, c, d) on stack + #[allow(clippy::too_many_arguments)] + pub fn hinted_mul_lc2_keep_elements_w4( + a_depth: u32, + a: ark_bn254::Fq, + b_depth: u32, + b: ark_bn254::Fq, + c_depth: u32, + c: ark_bn254::Fq, + d_depth: u32, + d: ark_bn254::Fq, + ) -> (Script, Vec) { + assert!(a_depth > b_depth && b_depth > c_depth && c_depth > d_depth); + + let mut hints = Vec::with_capacity(1); + + let modulus = &Fq::modulus_as_bigint(); + + let x = BigInt::from_str(&a.to_string()).unwrap(); + let y = BigInt::from_str(&b.to_string()).unwrap(); + let z = BigInt::from_str(&c.to_string()).unwrap(); + let w = BigInt::from_str(&d.to_string()).unwrap(); + + let q = (x * z + y * w) / modulus; + + let script = script! { + for _ in 0..Self::N_LIMBS { + OP_DEPTH OP_1SUB OP_ROLL // hints + } + // { Fq::push(ark_bn254::Fq::from_str(&q.to_string()).unwrap()) } + { Fq::copy(a_depth + 1) } + { Fq::copy(b_depth + 2) } + { Fq::copy(c_depth + 3) } + { Fq::copy(d_depth + 4) } + { Fq::tmul_lc2_w4() } + }; + hints.push(Hint::BigIntegerTmulLC2(q)); + + (script, hints) + } + + // TODO: Optimize using the sqaure feature + pub 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::push(ark_bn254::Fq::from_str(&q.to_string()).unwrap()) } + { Fq::roll(1) } + { Fq::copy(0) } + { Fq::tmul() } + }; + hints.push(Hint::BigIntegerTmulLC1(q)); + + (script, hints) + } + + pub fn hinted_inv(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 y = &x.modinv(modulus).unwrap(); + let q = (x * y) / modulus; + let script = script! { + for _ in 0..Self::N_LIMBS { + OP_DEPTH OP_1SUB OP_ROLL // hints + } + for _ in 0..Self::N_LIMBS { + OP_DEPTH OP_1SUB OP_ROLL // hints + } + // { Fq::push(ark_bn254::Fq::from_str(&y.to_string()).unwrap()) } + // { Fq::push(ark_bn254::Fq::from_str(&q.to_string()).unwrap()) } + // x, y, q + { Fq::roll(2) } + { Fq::copy(2) } + // y, q, x, y + { Fq::tmul() } + // y, 1 + { Fq::push_one() } + { Fq::equalverify(1, 0) } + }; + hints.push(Hint::Fq(ark_bn254::Fq::from_str(&y.to_string()).unwrap())); + hints.push(Hint::BigIntegerTmulLC1(q)); + + (script, hints) + } } pub fn bigint_to_u32_limbs(n: BigInt, n_bits: u32) -> Vec { @@ -112,7 +490,7 @@ macro_rules! fp_lc_mul { trait [] { const LIMB_SIZE: u32 = 29; const LCS: [bool; $LCS.len()] = $LCS; - const LC_BITS: u32 = usize::BITS - $LCS.len().leading_zeros() - 1; + const LC_BITS: u32 = usize::BITS - ($LCS.len() - 1).leading_zeros(); type U; type T; fn tmul() -> Script; @@ -169,15 +547,15 @@ macro_rules! fp_lc_mul { // Initialize the lookup table fn init_table(window: u32) -> Script { assert!( - (1..=6).contains(&window), - "expected 1<=window<=6; got window={}", + (2..=6).contains(&window), + "expected 2<=window<=6; got window={}", window ); script! { for i in 2..=window { for j in 1 << (i - 1)..1 << i { if j % 2 == 0 { - { T::double_allow_overflow_keep_element( (j/2 - 1) * T::N_LIMBS ) } + { T::double_prevent_overflow_keep_element(j/2 - 1) } } else { { T::add_ref_with_top(j - 2) } } @@ -323,9 +701,10 @@ macro_rules! fp_lc_mul { { U::lessthan(1, 0) } OP_VERIFY // {q} {x0} {x1} {y0} {y1} { U::toaltstack() } // {q} {x0} {x1} {y0} -> {y1} } // {q} -> {x0} {x1} {y0} {y1} - // Pre-compute lookup tables - { T::push_zero() } // {q} {0} -> {x0} {x1} {y0} {y1} - { T::sub(0, 1) } // {-q} -> {x0} {x1} {y0} {y1} + // ensure q is a valid bigint + { T::copy(0) } { T::check_validity() } + // Pre-compute lookup tables (q can not be 2^T::N_BITS-1 when tmul is correctly used, so neg() gives correct result) + { T::neg() } // {-q} -> {x0} {x1} {y0} {y1} { init_table(MOD_WIDTH) } // {-q_table} -> {x0} {x1} {y0} {y1} for i in 0..N_LC { { U::fromaltstack() } // {-q_table} {x0} -> {x1} {y0} {y1} @@ -358,12 +737,12 @@ macro_rules! fp_lc_mul { OP_SWAP OP_SUB if i + j == MAIN_LOOP_START && j == 0 { - for _ in 0..Self::N_LIMBS { + for _ in 0..T::N_LIMBS { OP_NIP } - { NMUL(Self::N_LIMBS) } + { NMUL(T::N_LIMBS) } OP_DUP OP_PICK - for _ in 0..Self::N_LIMBS-1 { + for _ in 0..T::N_LIMBS-1 { OP_SWAP OP_DUP OP_PICK } @@ -390,25 +769,22 @@ macro_rules! fp_lc_mul { } } - { T::is_positive(size_table(MOD_WIDTH) + // q was negative - N_LC * size_table(VAR_WIDTH) + N_LC) } OP_TOALTSTACK // {-q_table} {x0_table} {x1_table} {y0} {y1} {r} -> {0/1} - { T::toaltstack() } // {-q_table} {x0_table} {x1_table} {y0} {y1} -> {r} {0/1} + { T::toaltstack() } // {-q_table} {x0_table} {x1_table} {y0} {y1} -> {r} // Cleanup - for _ in 0..N_LC { { T::drop() } } // {-q_table} {x0_table} {x1_table} -> {r} {0/1} - for _ in 0..N_LC { { drop_table(VAR_WIDTH) } } // {-q_table} -> {r} {0/1} - { drop_table(MOD_WIDTH) } // -> {r} {0/1} + for _ in 0..N_LC { { T::drop() } } // {-q_table} {x0_table} {x1_table} -> {r} + for _ in 0..N_LC { { drop_table(VAR_WIDTH) } } // {-q_table} -> {r} + { drop_table(MOD_WIDTH) } // -> {r} // Correction/validation // r = if q < 0 { r + p } else { r }; assert(r < p) - { T::push_u32_le(&Fq::modulus_as_bigint().to_u32_digits().1) } // {MODULUS} -> {r} {0/1} - { T::fromaltstack() } OP_FROMALTSTACK // {MODULUS} {r} {0/1} - OP_IF { T::add_ref(1) } OP_ENDIF // {MODULUS} {-r/r} - { T::copy(0) } // {MODULUS} {-r/r} {-r/r} - { T::lessthan(0, 2) } OP_VERIFY // {-r/r} + { T::push_u32_le(&Fq::modulus_as_bigint().to_u32_digits().1) } // {MODULUS} -> {r} + { T::fromaltstack() } // {MODULUS} {r} + { T::copy(0) } // {MODULUS} {r} {r} + { T::lessthan(0, 2) } OP_VERIFY // {r} // Resize res back to N_BITS - { T::resize::() } // {r} + { T::resize::() } // {r} } } } @@ -424,6 +800,7 @@ fp_lc_mul!(Mul4LC, 3, 3, [true, true, true, true]); #[cfg(test)] mod test { use super::*; + use crate::bigint::U254; use crate::bn254::fq::Fq; use crate::{bn254::fp254impl::Fp254Impl, ExecuteInfo}; use ark_ff::AdditiveGroup; @@ -662,40 +1039,6 @@ mod test { } } - #[test] - fn test_is_field() { - let m = BigUint::from_str_radix(Fq::MODULUS, 16).unwrap(); - let mut prng = ChaCha20Rng::seed_from_u64(0); - - println!("Fq.is_field: {} bytes", Fq::is_field().len()); - - for _ in 0..10 { - let a: BigUint = prng.sample(RandomBits::new(254)); - let a = a.rem(&m); - - let script = script! { - { Fq::push_u32_le(&a.to_u32_digits()) } - { Fq::is_field() } - }; - run(script); - } - - let script = script! { - { Fq::push_modulus() } OP_1 OP_ADD - { Fq::is_field() } - OP_NOT - }; - run(script); - - let script = script! { - { Fq::push_modulus() } OP_1 OP_SUB - OP_NEGATE - { Fq::is_field() } - OP_NOT - }; - run(script); - } - #[test] fn test_hinted_mul() { let mut prng: ChaCha20Rng = ChaCha20Rng::seed_from_u64(0); @@ -1038,4 +1381,159 @@ mod test { max_stack ); } + + #[test] + fn test_check_validity() { + let mut prng = ChaCha20Rng::seed_from_u64(37); + let x = Fq::modulus_as_bigint().to_biguint().unwrap(); + let verification_script = Fq::check_validity(); + let clearing_script = script! { + for _ in 0..U254::N_LIMBS { OP_FROMALTSTACK } + { U254::equalverify(0, 1) } + OP_TRUE + }; + + // -1 + { + run(script! { + { U254::push_biguint(x.clone() - 1u32) } + { verification_script.clone() } + { U254::push_biguint(x.clone() - 1u32) } { clearing_script.clone() } + }) + } + + // equal + { + assert!( + execute_script(script! { + { U254::push_biguint(x.clone()) } + { verification_script.clone() } + { U254::push_biguint(x.clone()) } { clearing_script.clone() } + }) + .success + == false + ) + } + + // +1 + { + assert!( + execute_script(script! { + { U254::push_biguint(x.clone() + 1u32) } + { verification_script.clone() } + { U254::push_biguint(x.clone() + 1u32) } { clearing_script.clone() } + }) + .success + == false + ) + } + + // random less than + for _ in 0..100 { + let n = prng.gen_biguint_range(&BigUint::from(0u32), &x); + run(script! { + { U254::push_biguint(n.clone()) } + { verification_script.clone() } + { U254::push_biguint(n) } { clearing_script.clone() } + }) + } + + // random geq + for _ in 0..100 { + let n = prng.gen_biguint_range(&x, &BigUint::from(2u32).pow(254)); + assert!( + execute_script(script! { + { U254::push_biguint(n.clone()) } + { verification_script.clone() } + { U254::push_biguint(n) } { clearing_script.clone() } + }) + .success + == false + ); + } + + //corrupted data with negative + { + let limbs = U254::biguint_to_limbs(x.clone()); + assert!( + execute_script(script! { + for i in (1..U254::N_LIMBS as usize).rev() { + { limbs[i] } + } + { -1 } + + { verification_script.clone() } + { U254::push_biguint(x.clone()) } { clearing_script.clone() } + }) + .success + == false + ) + } + } + + #[test] + fn test_is_valid() { + let mut prng = ChaCha20Rng::seed_from_u64(37); + let x = Fq::modulus_as_bigint().to_biguint().unwrap(); + let verification_script = Fq::is_valid(); + + // -1 + { + run(script! { + { U254::push_biguint(x.clone() - 1u32) } + { verification_script.clone() } + }) + } + + // equal + { + run(script! { + { U254::push_biguint(x.clone()) } + { verification_script.clone() } + OP_NOT + }) + } + + // +1 + { + run(script! { + { U254::push_biguint(x.clone() + 1u32) } + { verification_script.clone() } + OP_NOT + }) + } + + // random less than + for _ in 0..100 { + let n = prng.gen_biguint_range(&BigUint::from(0u32), &x); + run(script! { + { U254::push_biguint(n.clone()) } + { verification_script.clone() } + }) + } + + // random geq + for _ in 0..100 { + let n = prng.gen_biguint_range(&x, &BigUint::from(2u32).pow(254)); + run(script! { + { U254::push_biguint(n.clone()) } + { verification_script.clone() } + OP_NOT + }); + } + + //corrupted data with negative + { + let limbs = U254::biguint_to_limbs(x.clone()); + run(script! { + for i in (1..U254::N_LIMBS as usize).rev() { + { limbs[i] } + } + { -1 } + + { verification_script.clone() } + OP_NOT + }) + } + } } diff --git a/bitvm/src/bn254/fq12.rs b/bitvm/src/bn254/fq12.rs index f5391298..e1fc5872 100644 --- a/bitvm/src/bn254/fq12.rs +++ b/bitvm/src/bn254/fq12.rs @@ -282,6 +282,14 @@ impl Fq12 { (script, hints) } + + pub fn check_validity() -> Script { + script! { + for _ in 0..2 { + { Fq6::check_validity() } + } + } + } } #[cfg(test)] diff --git a/bitvm/src/bn254/fq2.rs b/bitvm/src/bn254/fq2.rs index 5245c0d4..0483d6c4 100644 --- a/bitvm/src/bn254/fq2.rs +++ b/bitvm/src/bn254/fq2.rs @@ -156,6 +156,16 @@ impl Fq2 { } } + pub fn check_validity_and_keep_element() -> Script { + script! { + { Fq::check_validity() } + { Fq::check_validity_and_keep_element() } + for _ in 0..Fq::N_LIMBS { + OP_FROMALTSTACK + } + } + } + pub fn hinted_mul( mut a_depth: u32, mut a: ark_bn254::Fq2, @@ -350,6 +360,14 @@ impl Fq2 { [i % ark_bn254::Fq2Config::FROBENIUS_COEFF_FP2_C1.len()], ) } + + pub fn check_validity() -> Script { + script! { + for _ in 0..2 { + { Fq::check_validity() } + } + } + } } #[cfg(test)] diff --git a/bitvm/src/bn254/fq6.rs b/bitvm/src/bn254/fq6.rs index adc3c887..78b502b9 100644 --- a/bitvm/src/bn254/fq6.rs +++ b/bitvm/src/bn254/fq6.rs @@ -1,6 +1,5 @@ use crate::bn254::fp254impl::Fp254Impl; use crate::bn254::fq::Fq; -use crate::bn254::fq12::Fq12; use crate::bn254::fq2::Fq2; use crate::bn254::utils::Hint; use crate::treepp::{script, Script}; @@ -218,37 +217,10 @@ impl Fq6 { // Input: [A, B] // Output: [C] where C = A x B pub fn hinted_mul( - a_depth: u32, - a: ark_bn254::Fq6, - b_depth: u32, - b: ark_bn254::Fq6, - ) -> (Script, Vec) { - Self::hinted_mul_core(a_depth, a, b_depth, b, script! {}) - } - - // Input: [A, B] - // Output: [A, B, C] where C = A x B - pub fn hinted_mul_keep_elements( - a_depth: u32, - a: ark_bn254::Fq6, - b_depth: u32, - b: ark_bn254::Fq6, - ) -> (Script, Vec) { - let preserve_scr = script! { - {Fq6::toaltstack()} - {Fq12::copy(0)} - {Fq6::fromaltstack()} - }; - - Self::hinted_mul_core(a_depth, a, b_depth, b, preserve_scr) - } - - fn hinted_mul_core( mut a_depth: u32, mut a: ark_bn254::Fq6, mut b_depth: u32, mut b: ark_bn254::Fq6, - keep_elements_scr: Script, ) -> (Script, Vec) { // The degree-6 extension on BN254 Fq2 is under the polynomial y^3 - x - 9 // Toom-Cook-3 from https://eprint.iacr.org/2006/471.pdf @@ -306,8 +278,6 @@ impl Fq6 { // compute (a-b+c)(d-e+f) = P(-1) { hinted_script3 } - { keep_elements_scr } - // compute 2b { Fq2::roll(a_depth + 8) } { Fq2::double(0) } @@ -422,6 +392,184 @@ impl Fq6 { (script, hints) } + // Input: [A, B] + // Output: [A, B, C] where C = A x B + pub fn hinted_mul_keep_elements( + mut a_depth: u32, + mut a: ark_bn254::Fq6, + mut b_depth: u32, + mut b: ark_bn254::Fq6, + ) -> (Script, Vec) { + // The degree-6 extension on BN254 Fq2 is under the polynomial y^3 - x - 9 + // Toom-Cook-3 from https://eprint.iacr.org/2006/471.pdf + 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(); + + 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(a_depth + 4) } + { Fq2::copy(b_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::copy(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::copy(a_depth + 12) } + { Fq2::add(2, 0) } + + // compute 2e + { Fq2::copy(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::copy(b_depth + 14) } + { Fq2::add(2, 0) } + + // compute (a+2b+4c)(d+2e+4f) = P(2) + { hinted_script4 } + + // compute cf = P(inf) + { Fq2::copy(a_depth + 8) } + { Fq2::copy(b_depth + 10) } + { 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) @@ -577,6 +725,14 @@ impl Fq6 { (script, hints) } + + pub fn check_validity() -> Script { + script! { + for _ in 0..3 { + { Fq2::check_validity() } + } + } + } } #[cfg(test)] @@ -669,13 +825,46 @@ mod test { fn test_bn254_fq6_hinted_mul() { let mut prng: ChaCha20Rng = ChaCha20Rng::seed_from_u64(0); - for _ in 0..100 { + for i 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); - println!("Fq6::hinted_mul: {} bytes", hinted_mul.len()); + if i == 0 { + println!("Fq6::hinted_mul: {} bytes", hinted_mul.len()); + } + + 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 + }; + run(script); + } + } + + #[test] + fn test_bn254_fq6_hinted_mul_keep_elements() { + let mut prng: ChaCha20Rng = ChaCha20Rng::seed_from_u64(0); + + for i in 0..100 { + let a = ark_bn254::Fq6::rand(&mut prng); + let b = ark_bn254::Fq6::rand(&mut prng); + let c = ark_bn254::Fq6::rand(&mut prng); + let d = a.mul(&b); + let e = b.mul(&c); + + let (hinted_mul, hints) = Fq6::hinted_mul_keep_elements(12, a, 6, b); + if i == 0 { + println!("Fq6::hinted_mul(12,6): {} bytes", hinted_mul.len()); + } let script = script! { for hint in hints { @@ -683,9 +872,45 @@ mod test { } { Fq6::push(a) } { Fq6::push(b) } + { Fq6::push(c) } { hinted_mul.clone() } + { Fq6::push(d) } + { Fq6::equalverify() } + { Fq6::push(c) } { Fq6::equalverify() } + { Fq6::push(b) } + { Fq6::equalverify() } + { Fq6::push(a) } + { Fq6::equalverify() } + + OP_TRUE + }; + run(script); + + let (hinted_mul, hints) = Fq6::hinted_mul_keep_elements(6, b, 0, c); + if i == 0 { + println!("Fq6::hinted_mul(12,6): {} bytes", hinted_mul.len()); + } + + let script = script! { + for hint in hints { + { hint.push() } + } + { Fq6::push(a) } + { Fq6::push(b) } + { Fq6::push(c) } + { hinted_mul.clone() } + { Fq6::push(e) } + { Fq6::equalverify() } + + { Fq6::push(c) } + { Fq6::equalverify() } + { Fq6::push(b) } + { Fq6::equalverify() } + { Fq6::push(a) } + { Fq6::equalverify() } + OP_TRUE }; run(script); diff --git a/bitvm/src/bn254/fr.rs b/bitvm/src/bn254/fr.rs index f9b9693a..2bcfaa33 100644 --- a/bitvm/src/bn254/fr.rs +++ b/bitvm/src/bn254/fr.rs @@ -256,38 +256,4 @@ mod test { run(script); } } - - #[test] - fn test_is_field() { - let m = BigUint::from_str_radix(Fr::MODULUS, 16).unwrap(); - let mut prng = ChaCha20Rng::seed_from_u64(0); - - println!("Fr.is_field: {} bytes", Fr::is_field().len()); - - for _ in 0..10 { - let a: BigUint = prng.sample(RandomBits::new(254)); - let a = a.rem(&m); - - let script = script! { - { Fr::push_u32_le(&a.to_u32_digits()) } - { Fr::is_field() } - }; - run(script); - } - - let script = script! { - { Fr::push_modulus() } OP_1 OP_ADD - { Fr::is_field() } - OP_NOT - }; - run(script); - - let script = script! { - { Fr::push_modulus() } OP_1 OP_SUB - OP_NEGATE - { Fr::is_field() } - OP_NOT - }; - run(script); - } } diff --git a/bitvm/src/bn254/g1.rs b/bitvm/src/bn254/g1.rs index 7b8b3769..4c51780d 100644 --- a/bitvm/src/bn254/g1.rs +++ b/bitvm/src/bn254/g1.rs @@ -1,3 +1,4 @@ +use super::fq2::Fq2; use crate::bn254::fp254impl::Fp254Impl; use crate::bn254::fq::Fq; use crate::bn254::utils::Hint; @@ -5,8 +6,7 @@ use crate::treepp::{script, Script}; use ark_ec::AffineRepr; use ark_ff::{AdditiveGroup, Field}; use num_bigint::BigUint; - -use super::fq2::Fq2; +use num_traits::Zero; pub struct G1Affine; @@ -132,33 +132,50 @@ impl G1Affine { (script, hints) } - pub fn push_zero() -> Script { + pub fn push(element: ark_bn254::G1Affine) -> Script { script! { - { Fq::push_zero() } - { Fq::push_zero() } + { Fq::push_u32_le(&BigUint::from(element.x).to_u32_digits()) } + { Fq::push_u32_le(&BigUint::from(element.y).to_u32_digits()) } } } - pub fn push(element: ark_bn254::G1Affine) -> Script { + pub fn toaltstack() -> Script { script! { - { Fq::push_u32_le(&BigUint::from(element.x).to_u32_digits()) } - { Fq::push_u32_le(&BigUint::from(element.y).to_u32_digits()) } + {Fq::toaltstack()} + {Fq::toaltstack()} + } + } + + pub fn fromaltstack() -> Script { + script! { + {Fq::fromaltstack()} + {Fq::fromaltstack()} } } pub fn read_from_stack(witness: Vec>) -> ark_bn254::G1Affine { assert_eq!(witness.len() as u32, Fq::N_LIMBS * 2); - let x = Fq::read_u32_le(witness[0..Fq::N_LIMBS as usize].to_vec()); - let y = Fq::read_u32_le(witness[Fq::N_LIMBS as usize..2 * Fq::N_LIMBS as usize].to_vec()); + let x: ark_bn254::Fq = + BigUint::from_slice(&Fq::read_u32_le(witness[0..Fq::N_LIMBS as usize].to_vec())).into(); + let y: ark_bn254::Fq = BigUint::from_slice(&Fq::read_u32_le( + witness[Fq::N_LIMBS as usize..2 * Fq::N_LIMBS as usize].to_vec(), + )) + .into(); + let is_inf = x.is_zero() && y.is_zero(); ark_bn254::G1Affine { - x: BigUint::from_slice(&x).into(), - y: BigUint::from_slice(&y).into(), - infinity: false, + x, + y, + infinity: is_inf, } } - pub fn hinted_check_add(t: ark_bn254::G1Affine, q: ark_bn254::G1Affine) -> (Script, Vec) { + pub fn hinted_check_add_prevent_degenerate( + t: ark_bn254::G1Affine, + q: ark_bn254::G1Affine, + ) -> (Script, Vec) { let mut hints = vec![]; + assert_ne!(t, q); + assert_ne!(t, -q); let (alpha, bias) = if !t.is_zero() && !q.is_zero() { let alpha = (t.y - q.y) / (t.x - q.x); @@ -176,7 +193,7 @@ impl G1Affine { OP_IF { G1Affine::drop() } OP_ELSE - { G1Affine::roll(1) } + { G1Affine::roll(2) } { G1Affine::is_zero_keep_element() } OP_IF { G1Affine::drop() } @@ -184,9 +201,11 @@ impl G1Affine { for _ in 0..Fq::N_LIMBS { OP_DEPTH OP_1SUB OP_ROLL } + { Fq::check_validity_and_keep_element() } for _ in 0..Fq::N_LIMBS { OP_DEPTH OP_1SUB OP_ROLL } // qx qy tx ty c3 c4 + { Fq::check_validity_and_keep_element() } { Fq::copy(1) } { Fq::copy(1) } // qx qy tx ty c3 c4 c3 c4 { Fq::copy(5) } @@ -212,6 +231,106 @@ impl G1Affine { (script, hints) } + pub fn hinted_check_add(t: ark_bn254::G1Affine, q: ark_bn254::G1Affine) -> (Script, Vec) { + let mut hints = vec![]; + + let (alpha, bias) = if t.is_zero() || q.is_zero() { + // no hint, dummy zero + (ark_bn254::Fq::ZERO, ark_bn254::Fq::ZERO) + } else if t == -q { + // no hint, dummy zero + (ark_bn254::Fq::ZERO, ark_bn254::Fq::ZERO) + } else if t == q { + // doubling hints + let alpha = (t.x.square() + t.x.square() + t.x.square()) / (t.y + t.y); + let bias = t.y - alpha * t.x; + (alpha, bias) + } else { + // adding hints + let alpha = (t.y - q.y) / (t.x - q.x); + let bias = t.y - alpha * t.x; + (alpha, bias) + }; + + let (hinted_script1, hint1) = Self::hinted_check_chord_line(t, q, alpha); + let (hinted_script2, hint2) = Self::hinted_add(t.x, q.x, alpha); + let (hinted_script3, hint3) = Self::hinted_check_tangent_line(t, alpha); + + let script = script! { // tx ty qx qy + { G1Affine::is_zero_keep_element() } + OP_IF + { G1Affine::drop() } + OP_ELSE + { G1Affine::roll(2) } + { G1Affine::is_zero_keep_element() } + OP_IF + { G1Affine::drop() } + OP_ELSE // qx qy tx ty + { Fq::equal_keep_elements(3, 1) } // t == q or t == -q + OP_DUP + OP_TOALTSTACK + OP_TOALTSTACK + { Fq::equal_keep_elements(2, 0) } + OP_NOT + OP_FROMALTSTACK + OP_BOOLAND + OP_IF // case: t == -q + OP_FROMALTSTACK OP_DROP + { G1Affine::drop() } + { G1Affine::drop() } + { G1Affine::identity() } + OP_ELSE // case: t != -q + for _ in 0..Fq::N_LIMBS { + OP_DEPTH OP_1SUB OP_ROLL + } + { Fq::check_validity_and_keep_element() } + for _ in 0..Fq::N_LIMBS { + OP_DEPTH OP_1SUB OP_ROLL + } // qx qy tx ty c3 c4 + { Fq::check_validity_and_keep_element() } + { Fq::copy(1) } // qx qy tx ty c3 c4 c3 + { Fq::copy(1) } // qx qy tx ty c3 c4 c3 c4 + { Fq::copy(5) } // qx qy tx ty c3 c4 c3 c4 tx + { Fq::roll(5) } // qx qy tx c3 c4 c3 c4 tx ty + OP_FROMALTSTACK + OP_IF // case: t == q + { hinted_script3 } // qx qy tx c3 c4 is_tangent_line_correct + OP_VERIFY // qx qy tx c3 c4 + { Fq::roll(3) } // qx tx c3 c4 qy + { Fq::drop() } // qx tx c3 c4 + OP_ELSE // case: general case + { Fq::copy(8) } // qx qy tx c3 c4 c3 c4 tx ty qx + { Fq::roll(8) } // qx tx c3 c4 c3 c4 tx ty qx qy + { hinted_script1 } // qx tx c3 c4 0/1 + OP_VERIFY // qx tx c3 c4 + OP_ENDIF + { Fq::roll(2) } // qx c3 c4 tx + { Fq::roll(3) } // c3 c4 tx qx + { hinted_script2 } // x' y' + OP_ENDIF + OP_ENDIF + OP_ENDIF + }; + + if t.is_zero() || q.is_zero() { + // no hint + } else if t == -q { + // no hint + } else if t == q { + hints.push(Hint::Fq(alpha)); + hints.push(Hint::Fq(-bias)); + hints.extend(hint3); + hints.extend(hint2); + } else { + hints.push(Hint::Fq(alpha)); + hints.push(Hint::Fq(-bias)); + hints.extend(hint1); + hints.extend(hint2); + }; + + (script, hints) + } + /// add two points T and Q /// x' = alpha^2 - T.x - Q.x /// y' = -bias - alpha * x' @@ -317,9 +436,11 @@ impl G1Affine { for _ in 0..Fq::N_LIMBS { OP_DEPTH OP_1SUB OP_ROLL } // -bias, ..., x, y, alpha + { Fq::check_validity_and_keep_element() } for _ in 0..Fq::N_LIMBS { OP_DEPTH OP_1SUB OP_ROLL } // x, y, alpha, -bias + { Fq::check_validity_and_keep_element() } { Fq::copy(1) } // x, y, alpha, -bias, alpha { Fq::copy(1) } // x, y, alpha, -bias, alpha, -bias { Fq::copy(5) } // x, y, alpha, -bias, alpha, -bias, x @@ -346,18 +467,17 @@ impl G1Affine { let (y_sq, y_sq_hint) = Fq::hinted_square(y); let mut hints = Vec::new(); + hints.extend(y_sq_hint); hints.extend(x_sq_hint); hints.extend(x_cu_hint); - hints.extend(y_sq_hint); let scr = script! { + { y_sq } { Fq::copy(1) } { x_sq } { Fq::roll(2) } { x_cu } { Fq::push_hex("3") } { Fq::add(1, 0) } - { Fq::roll(1) } - { y_sq } { Fq::equal(1, 0) } }; (scr, hints) @@ -399,8 +519,7 @@ impl G1Affine { } } - pub fn roll(mut a: u32) -> Script { - a *= 2; + pub fn roll(a: u32) -> Script { script! { { Fq::roll(a + 1) } { Fq::roll(a + 1) } @@ -408,53 +527,6 @@ impl G1Affine { } } -/// input of func (params): -/// p.x, p.y -/// Input Hints On Stack -/// tmul hints, p.y_inverse -/// output on stack: -/// x' = -p.x / p.y -pub fn hinted_x_from_eval_point( - p: ark_bn254::G1Affine, - py_inv: ark_bn254::Fq, -) -> (Script, Vec) { - let mut hints = Vec::new(); - - let (hinted_script1, hint1) = Fq::hinted_mul(1, p.y, 0, py_inv); - let (hinted_script2, hint2) = Fq::hinted_mul(1, py_inv, 0, -p.x); - let script = script! { // Stack: [hints, pyd, px, py] - {Fq::copy(2)} // Stack: [hints, pyd, px, py, pyd] - {hinted_script1} - {Fq::push_one()} - {Fq::equalverify(1, 0)} // Stack: [hints, pyd, px] - {Fq::neg(0)} // Stack: [hints, pyd, -px] - {hinted_script2} - }; - hints.extend(hint1); - hints.extend(hint2); - (script, hints) -} - -/// input of func (params): -/// p.y -/// Input Hints On Stack -/// tmul hints, p.y_inverse -/// output on stack: -/// [] -pub fn hinted_y_from_eval_point(py: ark_bn254::Fq, py_inv: ark_bn254::Fq) -> (Script, Vec) { - let mut hints = Vec::new(); - - let (hinted_script1, hint1) = Fq::hinted_mul(1, py_inv, 0, py); - let script = script! {// [hints,..., pyd_calc, py] - {hinted_script1} - {Fq::push_one()} - {Fq::equalverify(1,0)} - }; - hints.extend(hint1); - - (script, hints) -} - /// input of func (params): /// p.x, p.y /// Input Hints On Stack @@ -466,20 +538,21 @@ 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) = hinted_y_from_eval_point(p.y, py_inv); - let (hinted_script2, hint2) = hinted_x_from_eval_point(p, py_inv); + let (hinted_script1, hint1) = Fq::hinted_mul(1, p.y, 0, py_inv); + let (hinted_script2, hint2) = Fq::hinted_mul(1, py_inv, 0, -p.x); let script = script! { // [hints, yinv, x, y] {Fq::copy(2)} - {Fq::copy(1)} {hinted_script1} + {Fq::push_one()} + {Fq::equalverify(1,0)} - // [hints, yinv, x, y] - {Fq::copy(2)} + // [hints, yinv, x] + {Fq::copy(1)} {Fq::toaltstack()} + {Fq::neg(0)} {hinted_script2} {Fq::fromaltstack()} }; @@ -494,8 +567,8 @@ pub fn hinted_from_eval_points(p: ark_bn254::G1Affine) -> (Script, Vec) { let py_inv = p.y().unwrap().inverse().unwrap(); - let (hinted_script1, hint1) = hinted_y_from_eval_point(p.y, py_inv); - let (hinted_script2, hint2) = hinted_x_from_eval_point(p, py_inv); + let (hinted_script1, hint1) = Fq::hinted_mul(1, p.y, 0, py_inv); + let (hinted_script2, hint2) = Fq::hinted_mul(1, py_inv, 0, -p.x); let script = script! { // [yinv, hints,.., x, y] @@ -503,14 +576,19 @@ pub fn hinted_from_eval_points(p: ark_bn254::G1Affine) -> (Script, Vec) { for _ in 0..Fq::N_LIMBS { OP_DEPTH OP_1SUB OP_ROLL } + { Fq::check_validity_and_keep_element() } {Fq2::fromaltstack()} // [hints, yinv, x, y] {Fq::copy(2)} - {Fq::copy(1)} + {hinted_script1} - // [hints, yinv, x, y] - {Fq::copy(2)} + {Fq::push_one()} + {Fq::equalverify(1,0)} + + // [hints, yinv, x] + {Fq::copy(1)} {Fq::toaltstack()} + {Fq::neg(0)} {hinted_script2} {Fq::fromaltstack()} }; @@ -528,7 +606,6 @@ mod test { use crate::bn254::fq::Fq; use crate::bn254::fq2::Fq2; use crate::bn254::g1::G1Affine; - use crate::bn254::g2::G2Affine; use super::*; use crate::{treepp::*, ExecuteInfo}; @@ -561,14 +638,14 @@ mod test { assert_eq!(a, recovered_a); - let b = ark_bn254::G2Affine::rand(&mut prng); + let b = ark_bn254::G1Affine::identity(); let script = script! { - {G2Affine::push(b)} + {G1Affine::push(b)} }; let res = execute_script(script); let witness = extract_witness_from_stack(res); - let recovered_b = G2Affine::read_from_stack(witness); + let recovered_b = G1Affine::read_from_stack(witness); assert_eq!(b, recovered_b); } @@ -701,50 +778,68 @@ mod test { } #[test] - fn test_g1_affine_hinted_check_add() { - //println!("G1.hinted_add: {} bytes", G1Affine::check_add().len()); + fn test_g1_affine_hinted_check_add_prevent_degenerate() { let mut prng = ChaCha20Rng::seed_from_u64(0); let t = ark_bn254::G1Affine::rand(&mut prng); let q = ark_bn254::G1Affine::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 r = (t + q).into_affine(); - let (hinted_check_add, hints) = G1Affine::hinted_check_add(t, q); + let (hinted_check_add, hints) = G1Affine::hinted_check_add_prevent_degenerate(t, q); let script = script! { for hint in hints { { hint.push() } } - { Fq::push(t.x) } - { Fq::push(t.y) } - { Fq::push(q.x) } - { Fq::push(q.y) } + { G1Affine::push(t) } + { G1Affine::push(q) } { hinted_check_add.clone() } - // [x'] - { Fq::push(y) } - // [x', y', y] - { Fq::equalverify(1,0) } - // [x'] - { Fq::push(x) } - // [x', x] - { Fq::equalverify(1,0) } - // [] + { G1Affine::push(r) } + { G1Affine::equalverify() } OP_TRUE - // [OP_TRUE] }; let exec_result = execute_script(script); assert!(exec_result.success); println!( - "hinted_check_add: {} @ {} stack", + "hinted_check_add_prevent_degenerate: {} @ {} stack", hinted_check_add.len(), exec_result.stats.max_nb_stack_items ); } + #[test] + fn test_g1_affine_hinted_check_add() { + let mut prng = ChaCha20Rng::seed_from_u64(0); + let t = ark_bn254::G1Affine::rand(&mut prng); + let q = ark_bn254::G1Affine::rand(&mut prng); + let r = (t + q).into_affine(); + let tt = (t + t).into_affine(); + let negt = -t; + let z = ark_bn254::G1Affine::zero(); + + for (t, q, r) in [(t, q, r), (t, t, tt), (t, negt, z), (t, z, t)] { + let (hinted_check_add, hints) = G1Affine::hinted_check_add(t, q); + + let script = script! { + for hint in hints { + { hint.push() } + } + { G1Affine::push(t) } + { G1Affine::push(q) } + { hinted_check_add.clone() } + { G1Affine::push(r) } + { G1Affine::equalverify() } + OP_TRUE + }; + let exec_result = execute_script(script); + assert!(exec_result.success); + println!( + "hinted_check_add: {} @ {} stack", + hinted_check_add.len(), + exec_result.stats.max_nb_stack_items + ); + } + } + #[test] fn test_g1_affine_hinted_check_double() { //println!("G1.hinted_add: {} bytes", G1Affine::check_add().len()); @@ -865,45 +960,4 @@ mod test { let exec_result = execute_script(script); assert!(exec_result.success); } - - #[test] - fn test_hintedx_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_x_from_eval_point(p, p.y.inverse().unwrap()); - let script = script! { - for tmp in hints { - { tmp.push() } - } - { Fq::push_u32_le(&BigUint::from(p.y.inverse().unwrap()).to_u32_digits()) } - { Fq::push_u32_le(&BigUint::from(p.x).to_u32_digits()) } - { Fq::push_u32_le(&BigUint::from(p.y).to_u32_digits()) } - { ell_by_constant_affine_script.clone() } - { Fq::push_u32_le(&BigUint::from(-p.x / p.y).to_u32_digits()) } - {Fq::equalverify(1,0)} - OP_TRUE - }; - let exec_result = execute_script(script); - assert!(exec_result.success); - } - - #[test] - fn test_hintedy_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_y_from_eval_point(p.y, p.y.inverse().unwrap()); - let script = script! { - for tmp in hints { - { tmp.push() } - } - { Fq::push_u32_le(&BigUint::from(p.y.inverse().unwrap()).to_u32_digits()) } - { Fq::push_u32_le(&BigUint::from(p.y).to_u32_digits()) } - { ell_by_constant_affine_script.clone() } - OP_TRUE - }; - let exec_result = execute_script(script); - assert!(exec_result.success); - } } diff --git a/bitvm/src/bn254/g2.rs b/bitvm/src/bn254/g2.rs index 151f89a6..859f5e93 100644 --- a/bitvm/src/bn254/g2.rs +++ b/bitvm/src/bn254/g2.rs @@ -9,6 +9,7 @@ use crate::treepp::{script, Script}; use ark_ec::bn::BnConfig; use ark_ff::{AdditiveGroup, Field}; use num_bigint::BigUint; +use num_traits::Zero; use std::str::FromStr; pub struct G2Affine; @@ -37,8 +38,7 @@ impl G2Affine { } } - pub fn roll(mut a: u32) -> Script { - a *= 4; + pub fn roll(a: u32) -> Script { script! { { Fq::roll(a + 3) } { Fq::roll(a + 3) } @@ -48,8 +48,7 @@ impl G2Affine { } // [ax, ay, bx, by] - pub fn copy(mut a: u32) -> Script { - a *= 4; + pub fn copy(a: u32) -> Script { script! { { Fq::copy(a + 3) } { Fq::copy(a + 3) } @@ -90,11 +89,12 @@ impl G2Affine { let (y_sq, y_sq_hint) = Fq2::hinted_square(y); let mut hints = Vec::new(); + hints.extend(y_sq_hint); hints.extend(x_sq_hint); hints.extend(x_cu_hint); - hints.extend(y_sq_hint); let scr = script! { + { y_sq } { Fq2::copy(2) } { x_sq } { Fq2::roll(4) } @@ -102,8 +102,6 @@ impl G2Affine { { Fq::push_dec("19485874751759354771024239261021720505790618469301721065564631296452457478373") } { Fq::push_dec("266929791119991161246907387137283842545076965332900288569378510910307636690") } { Fq2::add(2, 0) } - { Fq2::roll(2) } - { y_sq } { Fq2::equal() } }; (scr, hints) @@ -122,10 +120,13 @@ impl G2Affine { let y = Fq2::read_from_stack( witness[2 * Fq::N_LIMBS as usize..4 * Fq::N_LIMBS as usize].to_vec(), ); + + let is_inf = x.is_zero() && y.is_zero(); + ark_bn254::G2Affine { x, y, - infinity: false, + infinity: is_inf, } } } @@ -303,11 +304,12 @@ pub fn hinted_ell_by_constant_affine_and_sparse_mul( let (hinted_script5, hint5) = Fq12::hinted_mul_by_34(f, c1, c2); let hinted_script_constant = script! { - for _ in 0..4 { - for _ in 0..Fq::N_LIMBS { - OP_DEPTH OP_1SUB OP_ROLL - } - } + for _ in 0..4 { + for _ in 0..Fq::N_LIMBS { + OP_DEPTH OP_1SUB OP_ROLL + } + { Fq::check_validity_and_keep_element() } + } }; let script = script! { {hinted_script_constant} @@ -770,7 +772,7 @@ mod test { use super::*; use crate::bn254::fq::Fq; use crate::bn254::fq2::Fq2; - use crate::bn254::g1::{hinted_from_eval_point, G1Affine}; + use crate::bn254::g1::hinted_from_eval_point; use crate::bn254::g2::G2Affine; use crate::{treepp::*, ExecuteInfo}; use ark_ff::AdditiveGroup; @@ -789,18 +791,18 @@ mod test { #[test] fn test_read_from_stack() { let mut prng = ChaCha20Rng::seed_from_u64(0); - let a = ark_bn254::G1Affine::rand(&mut prng); + let a = ark_bn254::G2Affine::rand(&mut prng); let script = script! { - {G1Affine::push(a)} + {G2Affine::push(a)} }; let res = execute_script(script); let witness = extract_witness_from_stack(res); - let recovered_a = G1Affine::read_from_stack(witness); + let recovered_a = G2Affine::read_from_stack(witness); assert_eq!(a, recovered_a); - let b = ark_bn254::G2Affine::rand(&mut prng); + let b = ark_bn254::G2Affine::identity(); let script = script! { {G2Affine::push(b)} }; diff --git a/bitvm/src/bn254/msm.rs b/bitvm/src/bn254/msm.rs index 3483e165..98b7faf9 100644 --- a/bitvm/src/bn254/msm.rs +++ b/bitvm/src/bn254/msm.rs @@ -39,7 +39,7 @@ pub fn hinted_msm_with_constant_bases_affine( let num_scalars = scalars.len(); let psm_len = all_rows.len() / num_scalars; - for (idx, (row_out, row_scr, row_hints)) in all_rows.into_iter().enumerate() { + for (idx, ((row_out, row_scr, row_hints), _)) in all_rows.into_iter().enumerate() { all_hints.extend_from_slice(&row_hints); let temp_scr = script! { @@ -87,7 +87,7 @@ pub(crate) fn dfs_with_constant_mul( { G1Affine::push(p_mul[(mask + (1 << index)) as usize]) } OP_ELSE if mask == 0 { - { G1Affine::push_zero() } + { G1Affine::identity() } } else { { G1Affine::push(p_mul[mask as usize]) } } @@ -230,7 +230,7 @@ fn accumulate_addition_chain_for_a_scalar_mul( ); // accumulate value using hinted_check_add - let (add_scr, add_hints) = G1Affine::hinted_check_add(prev, row_g1); + let (add_scr, add_hints) = G1Affine::hinted_check_add_prevent_degenerate(prev, row_g1); prev = (prev + row_g1).into_affine(); // output of this chunk: t + q vec_row_g1_scr.push(row_g1_scr); @@ -275,7 +275,7 @@ fn accumulate_addition_chain_for_a_scalar_mul( pub(crate) fn g1_multi_scalar_mul( bases: Vec, scalars: Vec, -) -> Vec<(ark_bn254::G1Affine, Script, Vec)> { +) -> Vec<((ark_bn254::G1Affine, Script, Vec), usize)> { assert_eq!(bases.len(), scalars.len()); let mut prev = ark_bn254::G1Affine::identity(); let window = WINDOW_G1_MSM as usize; @@ -285,7 +285,9 @@ pub(crate) fn g1_multi_scalar_mul( let scalar_mul_res = accumulate_addition_chain_for_a_scalar_mul(prev, bases[i], scalars[i], window); prev = scalar_mul_res[scalar_mul_res.len() - 1].0; - aggregate_result_of_all_scalar_muls.extend_from_slice(&scalar_mul_res); + for x in scalar_mul_res { + aggregate_result_of_all_scalar_muls.push((x, i)); + } } aggregate_result_of_all_scalar_muls } @@ -486,11 +488,11 @@ mod test { let psm_len = all_rows.len() / num_scalars; let expected_msm = (q0 * fq0 + q1 * fq1).into_affine(); - let calculated_msm = all_rows[all_rows.len() - 1].0; + let calculated_msm = all_rows[all_rows.len() - 1].0 .0; assert_eq!(expected_msm, calculated_msm); let mut prev = ark_bn254::G1Affine::identity(); - for (idx, (row_out, row_scr, row_hints)) in all_rows.into_iter().enumerate() { + for (idx, ((row_out, row_scr, row_hints), _)) in all_rows.into_iter().enumerate() { let scr = script! { // [hints, t, scalar] for h in &row_hints { diff --git a/bitvm/src/bn254/utils.rs b/bitvm/src/bn254/utils.rs index 5b92f09d..07fcff67 100644 --- a/bitvm/src/bn254/utils.rs +++ b/bitvm/src/bn254/utils.rs @@ -14,16 +14,19 @@ pub enum Hint { U256(num_bigint::BigInt), BigIntegerTmulLC1(num_bigint::BigInt), BigIntegerTmulLC2(num_bigint::BigInt), + BigIntegerTmulLC2W4(num_bigint::BigInt), BigIntegerTmulLC4(num_bigint::BigInt), } impl Hint { pub fn push(&self) -> Script { - const K1: (u32, u32) = Fq::bigint_tmul_lc_1(); - const K2: (u32, u32) = Fq::bigint_tmul_lc_2(); - const K4: (u32, u32) = Fq::bigint_tmul_lc_4(); + const K1: (u32, u32, u32) = Fq::bigint_tmul_lc_1(); + const K2: (u32, u32, u32) = Fq::bigint_tmul_lc_2(); + const K3: (u32, u32, u32) = Fq::bigint_tmul_lc_2_w4(); + const K4: (u32, u32, u32) = Fq::bigint_tmul_lc_4(); pub type T1 = BigIntImpl<{ K1.0 }, { K1.1 }>; pub type T2 = BigIntImpl<{ K2.0 }, { K2.1 }>; + pub type T3 = BigIntImpl<{ K3.0 }, { K3.1 }>; pub type T4 = BigIntImpl<{ K4.0 }, { K4.1 }>; match self { Hint::Fq(fq) => script! { @@ -46,8 +49,11 @@ impl Hint { Hint::BigIntegerTmulLC2(a) => script! { { T2::push_u32_le(&bigint_to_u32_limbs(a.clone(), T2::N_BITS)) } }, + Hint::BigIntegerTmulLC2W4(a) => script! { + { T3::push_u32_le(&bigint_to_u32_limbs(a.clone(), T3::N_BITS)) } + }, Hint::BigIntegerTmulLC4(a) => script! { - { T2::push_u32_le(&bigint_to_u32_limbs(a.clone(), T4::N_BITS)) } + { T4::push_u32_le(&bigint_to_u32_limbs(a.clone(), T4::N_BITS)) } }, } } diff --git a/bitvm/src/chunk/api.rs b/bitvm/src/chunk/api.rs index 2d4465df..fc8ae840 100644 --- a/bitvm/src/chunk/api.rs +++ b/bitvm/src/chunk/api.rs @@ -21,7 +21,7 @@ use super::wrap_hasher::BLAKE3_HASH_LENGTH; pub const NUM_PUBS: usize = 2; pub const NUM_U256: usize = 14; pub const NUM_HASH: usize = 367; -const VALIDATING_TAPS: usize = 1; +pub const VALIDATING_TAPS: usize = 1; const HASHING_TAPS: usize = NUM_HASH; pub const NUM_TAPS: usize = HASHING_TAPS + VALIDATING_TAPS; @@ -391,7 +391,9 @@ mod test { use crate::chunk::api::generate_signatures_for_any_proof; - use crate::chunk::api_compiletime_utils::generate_segments_using_mock_vk_and_mock_proof; + use crate::chunk::api_runtime_utils::{ + analyze_largest_segments_from_signatures, get_segments_from_assertion, + }; use crate::chunk::wrap_hasher::BLAKE3_HASH_LENGTH; use ark_bn254::Bn254; use ark_ff::UniformRand; @@ -548,6 +550,102 @@ mod test { } } + #[test] + fn test_largest_chunks() { + println!("Use mock groth16 proof"); + let vk_bytes = [ + 115, 158, 251, 51, 106, 255, 102, 248, 22, 171, 229, 158, 80, 192, 240, 217, 99, 162, + 65, 107, 31, 137, 197, 79, 11, 210, 74, 65, 65, 203, 243, 14, 123, 2, 229, 125, 198, + 247, 76, 241, 176, 116, 6, 3, 241, 1, 134, 195, 39, 5, 124, 47, 31, 43, 164, 48, 120, + 207, 150, 125, 108, 100, 48, 155, 137, 132, 16, 193, 139, 74, 179, 131, 42, 119, 25, + 185, 98, 13, 235, 118, 92, 11, 154, 142, 134, 220, 191, 220, 169, 250, 244, 104, 123, + 7, 247, 33, 178, 155, 121, 59, 75, 188, 206, 198, 182, 97, 0, 64, 231, 45, 55, 92, 100, + 17, 56, 159, 79, 13, 219, 221, 33, 39, 193, 24, 36, 58, 105, 8, 70, 206, 176, 209, 146, + 45, 201, 157, 226, 84, 213, 135, 143, 178, 156, 112, 137, 246, 123, 248, 215, 168, 51, + 95, 177, 47, 57, 29, 199, 224, 98, 48, 144, 253, 15, 201, 192, 142, 62, 143, 13, 228, + 89, 51, 58, 6, 226, 139, 99, 207, 22, 113, 215, 79, 91, 158, 166, 210, 28, 90, 218, + 111, 151, 4, 55, 230, 76, 90, 209, 149, 113, 248, 245, 50, 231, 137, 51, 157, 40, 29, + 184, 198, 201, 108, 199, 89, 67, 136, 239, 96, 216, 237, 172, 29, 84, 3, 128, 240, 2, + 218, 169, 217, 118, 179, 34, 226, 19, 227, 59, 193, 131, 108, 20, 113, 46, 170, 196, + 156, 45, 39, 151, 218, 22, 132, 250, 209, 183, 46, 249, 115, 239, 14, 176, 200, 134, + 158, 148, 139, 212, 167, 152, 205, 183, 236, 242, 176, 96, 177, 187, 184, 252, 14, 226, + 127, 127, 173, 147, 224, 220, 8, 29, 63, 73, 215, 92, 161, 110, 20, 154, 131, 23, 217, + 116, 145, 196, 19, 167, 84, 185, 16, 89, 175, 180, 110, 116, 57, 198, 237, 147, 183, + 164, 169, 220, 172, 52, 68, 175, 113, 244, 62, 104, 134, 215, 99, 132, 199, 139, 172, + 108, 143, 25, 238, 201, 128, 85, 24, 73, 30, 186, 142, 186, 201, 79, 3, 176, 185, 70, + 66, 89, 127, 188, 158, 209, 83, 17, 22, 187, 153, 8, 63, 58, 174, 236, 132, 226, 43, + 145, 97, 242, 198, 117, 105, 161, 21, 241, 23, 84, 32, 62, 155, 245, 172, 30, 78, 41, + 199, 219, 180, 149, 193, 163, 131, 237, 240, 46, 183, 186, 42, 201, 49, 249, 142, 188, + 59, 212, 26, 253, 23, 27, 205, 231, 163, 76, 179, 135, 193, 152, 110, 91, 5, 218, 67, + 204, 164, 128, 183, 221, 82, 16, 72, 249, 111, 118, 182, 24, 249, 91, 215, 215, 155, 2, + 0, 0, 0, 0, 0, 0, 0, 212, 110, 6, 228, 73, 146, 46, 184, 158, 58, 94, 4, 141, 241, 158, + 0, 175, 140, 72, 75, 52, 6, 72, 49, 112, 215, 21, 243, 151, 67, 106, 22, 158, 237, 80, + 204, 41, 128, 69, 52, 154, 189, 124, 203, 35, 107, 132, 241, 234, 31, 3, 165, 87, 58, + 10, 92, 252, 227, 214, 99, 176, 66, 118, 22, 177, 20, 120, 198, 252, 236, 7, 148, 207, + 78, 152, 132, 94, 207, 50, 243, 4, 169, 146, 240, 79, 98, 0, 212, 106, 137, 36, 193, + 21, 175, 180, 1, 26, 107, 39, 198, 89, 152, 26, 220, 138, 105, 243, 45, 63, 106, 163, + 80, 74, 253, 176, 207, 47, 52, 7, 84, 59, 151, 47, 178, 165, 112, 251, 161, + ] + .to_vec(); + let proof_bytes: Vec = [ + 162, 50, 57, 98, 3, 171, 250, 108, 49, 206, 73, 126, 25, 35, 178, 148, 35, 219, 98, 90, + 122, 177, 16, 91, 233, 215, 222, 12, 72, 184, 53, 2, 62, 166, 50, 68, 98, 171, 218, + 218, 151, 177, 133, 223, 129, 53, 114, 236, 181, 215, 223, 91, 102, 225, 52, 122, 122, + 206, 36, 122, 213, 38, 186, 170, 235, 210, 179, 221, 122, 37, 74, 38, 79, 0, 26, 94, + 59, 146, 46, 252, 70, 153, 236, 126, 194, 169, 17, 144, 100, 218, 118, 22, 99, 226, + 132, 40, 24, 248, 232, 197, 195, 220, 254, 52, 36, 248, 18, 167, 167, 206, 108, 29, + 120, 188, 18, 78, 86, 8, 121, 217, 144, 185, 122, 58, 12, 34, 44, 6, 233, 80, 177, 183, + 5, 8, 150, 74, 241, 141, 65, 150, 35, 98, 15, 150, 137, 254, 132, 167, 228, 104, 63, + 133, 11, 209, 39, 79, 138, 185, 88, 20, 242, 102, 69, 73, 243, 88, 29, 91, 127, 157, + 82, 192, 52, 95, 143, 49, 227, 83, 19, 26, 108, 63, 232, 213, 169, 64, 221, 159, 214, + 220, 246, 174, 35, 43, 143, 80, 168, 142, 29, 103, 179, 58, 235, 33, 163, 198, 255, + 188, 20, 3, 91, 47, 158, 122, 226, 201, 175, 138, 18, 24, 178, 219, 78, 12, 96, 10, 2, + 133, 35, 230, 149, 235, 206, 1, 177, 211, 245, 168, 74, 62, 25, 115, 70, 42, 38, 131, + 92, 103, 103, 176, 212, 223, 177, 242, 94, 14, + ] + .to_vec(); + let scalar = [ + 232, 255, 255, 239, 147, 245, 225, 67, 145, 112, 185, 121, 72, 232, 51, 40, 93, 88, + 129, 129, 182, 69, 80, 184, 41, 160, 49, 225, 114, 78, 100, 48, + ] + .to_vec(); + + let proof: ark_groth16::Proof = + ark_groth16::Proof::deserialize_uncompressed(&proof_bytes[..]).unwrap(); + let vk: ark_groth16::VerifyingKey = + ark_groth16::VerifyingKey::deserialize_uncompressed(&vk_bytes[..]).unwrap(); + let scalar: ark_bn254::Fr = ark_bn254::Fr::deserialize_uncompressed(&scalar[..]).unwrap(); + let scalars = [scalar]; + + println!("STEP 1 GENERATE TAPSCRIPTS"); + let secret_key: &str = "a138982ce17ac813d505a5b40b665d404e9528e7"; + let secrets = (0..NUM_PUBS + NUM_U256 + NUM_HASH) + .map(|idx| format!("{secret_key}{:04x}", idx)) + .collect::>(); + let pubkeys = get_pubkeys(secrets.clone()); + + let partial_scripts = api_generate_partial_script(&vk); + let disprove_scripts = api_generate_full_tapscripts(pubkeys, &partial_scripts); + + println!("STEP 2 GENERATE SIGNED ASSERTIONS"); + let proof_sigs = + generate_signatures(proof, scalars.to_vec(), &vk, secrets.clone()).unwrap(); + + println!("num assertion; 256-bit numbers {}", NUM_PUBS + NUM_U256); + println!("num assertion; 160-bit numbers {}", NUM_HASH); + + let proof_asserts = get_assertions_from_signature(proof_sigs); + let signed_asserts = get_signature_from_assertion(proof_asserts, secrets); + let disprove_scripts: [ScriptBuf; NUM_TAPS] = disprove_scripts.try_into().unwrap(); + + let asserts = get_assertions_from_signature(signed_asserts.clone()); + let (success, segments) = get_segments_from_assertion(asserts, vk.clone()); + if !success { + println!("invalid tapscript at segment {}", segments.len()); + } + analyze_largest_segments_from_signatures(&segments, signed_asserts, &disprove_scripts); + } + #[test] fn full_e2e_execution() { println!("Use mock groth16 proof"); diff --git a/bitvm/src/chunk/api_compiletime_utils.rs b/bitvm/src/chunk/api_compiletime_utils.rs index 4d59d1c4..b32e4a68 100644 --- a/bitvm/src/chunk/api_compiletime_utils.rs +++ b/bitvm/src/chunk/api_compiletime_utils.rs @@ -5,7 +5,7 @@ use crate::bn254::ell_coeffs::AffinePairing; use crate::bn254::ell_coeffs::BnAffinePairing; use crate::bn254::fp254impl::Fp254Impl; use crate::bn254::fq::Fq; -use crate::chunk::api::{NUM_PUBS, NUM_TAPS}; +use crate::chunk::api::{NUM_HASH, NUM_PUBS, NUM_TAPS, NUM_U256, VALIDATING_TAPS}; use crate::chunk::elements::ElementType; use crate::treepp; use ark_bn254::Bn254; @@ -175,6 +175,10 @@ fn generate_segments_using_mock_proof(vk: Vkey, skip_evaluation: bool) -> Vec>, + bc_hints: Vec