From 4f036eabc48677a64b9ae07c501f684c28e7fb94 Mon Sep 17 00:00:00 2001 From: PayneJoe Date: Fri, 16 Aug 2024 21:09:43 +0800 Subject: [PATCH 01/10] redirect stwo branch --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index d13604c..ab675b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ bitcoin-scriptexec = { git = "https://github.com/Bitcoin-Wildlife-Sanctuary/rust sha2 = "0.10.8" rand = "0.8.5" rand_chacha = "0.3.1" -stwo-prover = { git = "https://github.com/Bitcoin-Wildlife-Sanctuary/stwo" } +stwo-prover = { git = "https://github.com/Bitcoin-Wildlife-Sanctuary/stwo", branch = "dev-3" } num-traits = "0.2.0" lazy_static = "1.4.0" ctor = "0.2.8" From 8879a908e048a051633d83556434009dd9e16d01 Mon Sep 17 00:00:00 2001 From: PayneJoe Date: Thu, 22 Aug 2024 11:30:48 +0800 Subject: [PATCH 02/10] fix draw_numbers_with_hint for big query numbers exceeding 8 --- src/channel/bitcoin_script.rs | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/channel/bitcoin_script.rs b/src/channel/bitcoin_script.rs index 16164b9..9b8f7ec 100644 --- a/src/channel/bitcoin_script.rs +++ b/src/channel/bitcoin_script.rs @@ -143,6 +143,8 @@ impl Sha256ChannelGadget { { Self::reconstruct() } } + OP_RETURN + for _ in 0..m-1 { OP_CAT } @@ -338,7 +340,7 @@ mod test { fn test_draw_5numbers_with_hint() { let mut prng = ChaCha20Rng::seed_from_u64(0); - let channel_script = Sha256ChannelGadget::draw_numbers_with_hint(5, 15); + let channel_script = Sha256ChannelGadget::draw_numbers_with_hint(64, 10); report_bitcoin_script_size("Channel", "draw_5numbers_with_hint", channel_script.len()); @@ -349,24 +351,26 @@ mod test { let mut channel = Sha256Channel::default(); channel.update_digest(a); - let (b, hint) = channel.draw_queries_and_hints(5, 15); + let (b, hint) = channel.draw_queries_and_hints(64, 10); let c = channel.digest; let script = script! { - { hint } { a } - { channel_script.clone() } - { b[4] } OP_EQUALVERIFY - { b[3] } OP_EQUALVERIFY - { b[2] } OP_EQUALVERIFY - { b[1] } OP_EQUALVERIFY - { b[0] } OP_EQUALVERIFY - { c } - OP_EQUAL + OP_DUP hash OP_SWAP + OP_PUSHBYTES_1 OP_PUSHBYTES_0 OP_CAT hash + // { hint } + // { a } + // { channel_script.clone() } + // for i in 0..64 { + // { b[63 - i] } OP_EQUALVERIFY + // } + // { c } + // OP_EQUAL }; let exec_result = execute_script(script); - assert!(exec_result.success); + println!("{:?}", exec_result.final_stack); + // assert!(exec_result.success); } } From d5be4043f1497d669a8569f310e3931d4dfdc088 Mon Sep 17 00:00:00 2001 From: PayneJoe Date: Thu, 22 Aug 2024 11:34:19 +0800 Subject: [PATCH 03/10] supporting large draw numbers --- src/channel/bitcoin_script.rs | 61 ++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/src/channel/bitcoin_script.rs b/src/channel/bitcoin_script.rs index 9b8f7ec..e1d4c13 100644 --- a/src/channel/bitcoin_script.rs +++ b/src/channel/bitcoin_script.rs @@ -81,12 +81,16 @@ impl Sha256ChannelGadget { /// channel digest /// all the numbers (m) pub fn draw_numbers_with_hint(m: usize, logn: usize) -> Script { + assert_eq!(m % 8, 0); script! { - OP_DUP hash OP_SWAP - OP_PUSHBYTES_1 OP_PUSHBYTES_0 OP_CAT hash - { Self::unpack_multi_m31(m) } - for i in 0..m { - { i } OP_ROLL { trim_m31_gadget(logn) } + for _ in 0..(m / 8) { + OP_DUP hash OP_SWAP + OP_PUSHBYTES_1 OP_PUSHBYTES_0 OP_CAT hash + { Self::unpack_multi_m31(8) } + for i in 0..8 { + { i } OP_ROLL { trim_m31_gadget(logn) } + } + 8 OP_ROLL } } } @@ -143,8 +147,6 @@ impl Sha256ChannelGadget { { Self::reconstruct() } } - OP_RETURN - for _ in 0..m-1 { OP_CAT } @@ -272,7 +274,7 @@ mod test { } #[test] - fn test_draw_8_elements() { + fn test_draw_64_elements() { let mut prng = ChaCha20Rng::seed_from_u64(0); for _ in 0..100 { @@ -282,19 +284,21 @@ mod test { let mut channel = Sha256Channel::default(); channel.update_digest(a); - let (b, hint) = channel.draw_m31_and_hints(8); + let (b, hint) = channel.draw_m31_and_hints(64); let c = channel.digest; let script = script! { { hint } { a } - OP_DUP hash OP_SWAP - OP_PUSHBYTES_1 OP_PUSHBYTES_0 OP_CAT hash - { Sha256ChannelGadget::unpack_multi_m31(8) } - for i in 0..8 { - { b[i] } - OP_EQUALVERIFY + for i in 0..(64 / 8) { + OP_DUP hash OP_SWAP + OP_PUSHBYTES_1 OP_PUSHBYTES_0 OP_CAT hash + { Sha256ChannelGadget::unpack_multi_m31(8) } + for j in 0..8 { + { b[i * 8 + j] } + OP_EQUALVERIFY + } } { c } OP_EQUAL @@ -337,14 +341,14 @@ mod test { } #[test] - fn test_draw_5numbers_with_hint() { + fn test_draw_64numbers_with_hint() { let mut prng = ChaCha20Rng::seed_from_u64(0); let channel_script = Sha256ChannelGadget::draw_numbers_with_hint(64, 10); report_bitcoin_script_size("Channel", "draw_5numbers_with_hint", channel_script.len()); - for _ in 0..100 { + for _ in 0..1 { let mut a = [0u8; 32]; a.iter_mut().for_each(|v| *v = prng.gen()); let a = Sha256Hash::from(a.to_vec()); @@ -356,21 +360,20 @@ mod test { let c = channel.digest; let script = script! { + { hint } { a } - OP_DUP hash OP_SWAP - OP_PUSHBYTES_1 OP_PUSHBYTES_0 OP_CAT hash - // { hint } - // { a } - // { channel_script.clone() } - // for i in 0..64 { - // { b[63 - i] } OP_EQUALVERIFY - // } - // { c } - // OP_EQUAL + { channel_script.clone() } + OP_TOALTSTACK + for i in 0..64 { + { b[63 - i] } + OP_EQUALVERIFY + } + OP_FROMALTSTACK + { c } + OP_EQUAL }; let exec_result = execute_script(script); - println!("{:?}", exec_result.final_stack); - // assert!(exec_result.success); + assert!(exec_result.success); } } From ba5c92da078b412fe4e97d21f0073a4f96a0d6d5 Mon Sep 17 00:00:00 2001 From: PayneJoe Date: Thu, 22 Aug 2024 11:42:11 +0800 Subject: [PATCH 04/10] update comment for draw_numbers_with_hint --- src/channel/bitcoin_script.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/channel/bitcoin_script.rs b/src/channel/bitcoin_script.rs index e1d4c13..e50558e 100644 --- a/src/channel/bitcoin_script.rs +++ b/src/channel/bitcoin_script.rs @@ -78,8 +78,8 @@ impl Sha256ChannelGadget { /// Draw queries from the channel, each of logn bits, using hints. /// /// Output: - /// channel digest /// all the numbers (m) + /// channel digest pub fn draw_numbers_with_hint(m: usize, logn: usize) -> Script { assert_eq!(m % 8, 0); script! { From 0c4a84c2eac04bfd0daa06ad7629326304836338 Mon Sep 17 00:00:00 2001 From: PayneJoe Date: Fri, 23 Aug 2024 00:13:56 +0800 Subject: [PATCH 05/10] add support for siged shift mask point --- src/air/bitcoin_script.rs | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/air/bitcoin_script.rs b/src/air/bitcoin_script.rs index de75849..ea0db6d 100644 --- a/src/air/bitcoin_script.rs +++ b/src/air/bitcoin_script.rs @@ -39,6 +39,39 @@ impl AirGadget { } } + /// Mask a point by shifting it with signed mask + /// + /// Input: + /// - point (in qm31) + /// + /// Output: + /// - shifted point (in qm31) + pub fn shifted_signed_mask_points( + mask: &ColumnVec>, + domains: &[CanonicCoset], + ) -> Script { + let mut shifted = vec![]; + for (mask_entry, domain) in mask.iter().zip(domains.iter()) { + let step_point = domain.step(); + for mask_item in mask_entry.iter() { + shifted.push(step_point.mul_signed(*mask_item)); + } + } + + script! { + if !shifted.is_empty() { + for elem in shifted.iter().take(shifted.len() - 1) { + { CirclePointGadget::dup() } + { CirclePointGadget::add_constant_m31_point(elem) } + { CirclePointGadget::swap() } + } + { CirclePointGadget::add_constant_m31_point(shifted.last().unwrap()) } + } else { + { CirclePointGadget::drop() } + } + } + } + /// Combine the evaluation on four points into one. /// /// Input: From ac5349a0aad663f7640f4cd7bc218e059ecb36d8 Mon Sep 17 00:00:00 2001 From: Weikeng Chen Date: Fri, 23 Aug 2024 11:07:51 +0800 Subject: [PATCH 06/10] Update Cargo.toml --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ab675b4..d3032c1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ bitcoin-scriptexec = { git = "https://github.com/Bitcoin-Wildlife-Sanctuary/rust sha2 = "0.10.8" rand = "0.8.5" rand_chacha = "0.3.1" -stwo-prover = { git = "https://github.com/Bitcoin-Wildlife-Sanctuary/stwo", branch = "dev-3" } +stwo-prover = { git = "https://github.com/Bitcoin-Wildlife-Sanctuary/stwo" } num-traits = "0.2.0" lazy_static = "1.4.0" ctor = "0.2.8" @@ -35,4 +35,4 @@ opt-level = 3 lto = true [features] -profiler = ["bitcoin-scriptexec/profiler"] \ No newline at end of file +profiler = ["bitcoin-scriptexec/profiler"] From 9a4356ab8e16e467c2974bb923c73fbe19c411df Mon Sep 17 00:00:00 2001 From: Weikeng Chen Date: Fri, 23 Aug 2024 11:08:28 +0800 Subject: [PATCH 07/10] Update Cargo.toml From c424ac82e5d3cbc4150cc51778d87567beba71b3 Mon Sep 17 00:00:00 2001 From: weikengchen Date: Fri, 23 Aug 2024 12:00:13 +0800 Subject: [PATCH 08/10] add tests to increase the coverage --- Cargo.toml | 2 +- src/air/bitcoin_script.rs | 67 ++++++++++++++++++++++++++++++++++++++- src/air/mod.rs | 21 ++++++++++++ 3 files changed, 88 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d3032c1..d13604c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,4 +35,4 @@ opt-level = 3 lto = true [features] -profiler = ["bitcoin-scriptexec/profiler"] +profiler = ["bitcoin-scriptexec/profiler"] \ No newline at end of file diff --git a/src/air/bitcoin_script.rs b/src/air/bitcoin_script.rs index ea0db6d..3293b77 100644 --- a/src/air/bitcoin_script.rs +++ b/src/air/bitcoin_script.rs @@ -98,13 +98,16 @@ impl AirGadget { #[cfg(test)] mod test { - use crate::air::AirGadget; + use crate::air::{shifted_signed_mask_points, AirGadget}; use crate::circle::CirclePointGadget; use crate::treepp::*; + use crate::utils::get_rand_qm31; use rand::{Rng, SeedableRng}; use rand_chacha::ChaCha20Rng; + use rust_bitcoin_m31::qm31_equalverify; use stwo_prover::core::air::mask::shifted_mask_points; use stwo_prover::core::circle::{CirclePoint, SECURE_FIELD_CIRCLE_ORDER}; + use stwo_prover::core::fields::qm31::QM31; use stwo_prover::core::poly::circle::CanonicCoset; #[test] @@ -137,4 +140,66 @@ mod test { assert!(exec_result.success); } } + + #[test] + fn test_shifted_signed_mask_points() { + let mut prng = ChaCha20Rng::seed_from_u64(0); + + for _ in 0..100 { + let p = CirclePoint::get_point(prng.gen::() % SECURE_FIELD_CIRCLE_ORDER); + + let mask = vec![vec![0, -1, 2]]; + let domains = [CanonicCoset::new(5)]; + + let expected = shifted_signed_mask_points(&mask, &domains, p); + assert_eq!(expected.len(), 1); + assert_eq!(expected[0].len(), 3); + + let script = script! { + { p } + { AirGadget::shifted_signed_mask_points(&mask, &domains) } + { expected[0][2] } + { CirclePointGadget::equalverify() } + { expected[0][1] } + { CirclePointGadget::equalverify() } + { expected[0][0] } + { CirclePointGadget::equalverify() } + OP_TRUE + }; + + let exec_result = execute_script(script); + assert!(exec_result.success); + } + } + + #[test] + fn test_eval_from_partial_evals() { + let mut prng = ChaCha20Rng::seed_from_u64(0); + + for _ in 0..100 { + let a = get_rand_qm31(&mut prng); + let b = get_rand_qm31(&mut prng); + let c = get_rand_qm31(&mut prng); + let d = get_rand_qm31(&mut prng); + + let mut res = a; + res += b * QM31::from_u32_unchecked(0, 1, 0, 0); + res += c * QM31::from_u32_unchecked(0, 0, 1, 0); + res += d * QM31::from_u32_unchecked(0, 0, 0, 1); + + let script = script! { + { a } + { b } + { c } + { d } + { AirGadget::eval_from_partial_evals() } + { res } + qm31_equalverify + OP_TRUE + }; + + let exec_result = execute_script(script); + assert!(exec_result.success); + } + } } diff --git a/src/air/mod.rs b/src/air/mod.rs index 63fc800..3559a0a 100644 --- a/src/air/mod.rs +++ b/src/air/mod.rs @@ -2,7 +2,10 @@ mod bitcoin_script; use crate::treepp::pushable::{Builder, Pushable}; pub use bitcoin_script::*; +use stwo_prover::core::circle::CirclePoint; use stwo_prover::core::fields::qm31::SecureField; +use stwo_prover::core::poly::circle::CanonicCoset; +use stwo_prover::core::ColumnVec; #[derive(Clone)] /// Hint for the two eval quotient results involved in the composition polynomial. @@ -20,3 +23,21 @@ impl Pushable for CompositionHint { builder } } + +/// A helper function to shift points, but with signed masks. +pub fn shifted_signed_mask_points( + mask: &ColumnVec>, + domains: &[CanonicCoset], + point: CirclePoint, +) -> ColumnVec>> { + mask.iter() + .zip(domains.iter()) + .map(|(mask_entry, domain)| { + let trace_step = CanonicCoset::new(domain.log_size()).step(); + mask_entry + .iter() + .map(|mask_item| point + trace_step.mul_signed(*mask_item).into_ef()) + .collect() + }) + .collect() +} From ec561d16ee2a2784b386d44c8a137896a811ad3a Mon Sep 17 00:00:00 2001 From: weikengchen Date: Fri, 23 Aug 2024 16:28:26 +0800 Subject: [PATCH 09/10] fix --- src/channel/bitcoin_script.rs | 72 ++++++++++++++++++++++++++--------- 1 file changed, 54 insertions(+), 18 deletions(-) diff --git a/src/channel/bitcoin_script.rs b/src/channel/bitcoin_script.rs index e50558e..ff564aa 100644 --- a/src/channel/bitcoin_script.rs +++ b/src/channel/bitcoin_script.rs @@ -81,7 +81,6 @@ impl Sha256ChannelGadget { /// all the numbers (m) /// channel digest pub fn draw_numbers_with_hint(m: usize, logn: usize) -> Script { - assert_eq!(m % 8, 0); script! { for _ in 0..(m / 8) { OP_DUP hash OP_SWAP @@ -92,6 +91,15 @@ impl Sha256ChannelGadget { } 8 OP_ROLL } + if m % 8 != 0 { + OP_DUP hash OP_SWAP + OP_PUSHBYTES_1 OP_PUSHBYTES_0 OP_CAT hash + { Self::unpack_multi_m31(m % 8) } + for i in 0..m % 8 { + { i } OP_ROLL { trim_m31_gadget(logn) } + } + { m % 8 } OP_ROLL + } } } @@ -274,7 +282,7 @@ mod test { } #[test] - fn test_draw_64_elements() { + fn test_draw_8_elements() { let mut prng = ChaCha20Rng::seed_from_u64(0); for _ in 0..100 { @@ -284,21 +292,19 @@ mod test { let mut channel = Sha256Channel::default(); channel.update_digest(a); - let (b, hint) = channel.draw_m31_and_hints(64); + let (b, hint) = channel.draw_m31_and_hints(8); let c = channel.digest; let script = script! { { hint } { a } - for i in 0..(64 / 8) { - OP_DUP hash OP_SWAP - OP_PUSHBYTES_1 OP_PUSHBYTES_0 OP_CAT hash - { Sha256ChannelGadget::unpack_multi_m31(8) } - for j in 0..8 { - { b[i * 8 + j] } - OP_EQUALVERIFY - } + OP_DUP hash OP_SWAP + OP_PUSHBYTES_1 OP_PUSHBYTES_0 OP_CAT hash + { Sha256ChannelGadget::unpack_multi_m31(8) } + for i in 0..8 { + { b[i] } + OP_EQUALVERIFY } { c } OP_EQUAL @@ -341,21 +347,51 @@ mod test { } #[test] - fn test_draw_64numbers_with_hint() { + fn test_draw_many_numbers_with_hint() { let mut prng = ChaCha20Rng::seed_from_u64(0); - let channel_script = Sha256ChannelGadget::draw_numbers_with_hint(64, 10); + let channel_script = Sha256ChannelGadget::draw_numbers_with_hint(8, 10); + report_bitcoin_script_size("Channel", "draw_8numbers_with_hint", channel_script.len()); + + for _ in 0..10 { + let mut a = [0u8; 32]; + a.iter_mut().for_each(|v| *v = prng.gen()); + let a = Sha256Hash::from(a.to_vec()); + + let mut channel = Sha256Channel::default(); + channel.update_digest(a); + let (b, hint) = channel.draw_queries_and_hints(8, 10); + + let c = channel.digest; + + let script = script! { + { hint } + { a } + { channel_script.clone() } + OP_TOALTSTACK + for i in 0..8 { + { b[7 - i] } + OP_EQUALVERIFY + } + OP_FROMALTSTACK + { c } + OP_EQUAL + }; + let exec_result = execute_script(script); + assert!(exec_result.success); + } - report_bitcoin_script_size("Channel", "draw_5numbers_with_hint", channel_script.len()); + let channel_script = Sha256ChannelGadget::draw_numbers_with_hint(12, 10); + report_bitcoin_script_size("Channel", "draw_12numbers_with_hint", channel_script.len()); - for _ in 0..1 { + for _ in 0..10 { let mut a = [0u8; 32]; a.iter_mut().for_each(|v| *v = prng.gen()); let a = Sha256Hash::from(a.to_vec()); let mut channel = Sha256Channel::default(); channel.update_digest(a); - let (b, hint) = channel.draw_queries_and_hints(64, 10); + let (b, hint) = channel.draw_queries_and_hints(12, 10); let c = channel.digest; @@ -364,8 +400,8 @@ mod test { { a } { channel_script.clone() } OP_TOALTSTACK - for i in 0..64 { - { b[63 - i] } + for i in 0..12 { + { b[11 - i] } OP_EQUALVERIFY } OP_FROMALTSTACK From 77b3390f700087ab891440d0e6ceb87286709f80 Mon Sep 17 00:00:00 2001 From: weikengchen Date: Sat, 24 Aug 2024 00:57:40 +0800 Subject: [PATCH 10/10] coverage --- src/air/bitcoin_script.rs | 30 ++++++++++++------------------ src/air/mod.rs | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 18 deletions(-) diff --git a/src/air/bitcoin_script.rs b/src/air/bitcoin_script.rs index 3293b77..17d948f 100644 --- a/src/air/bitcoin_script.rs +++ b/src/air/bitcoin_script.rs @@ -24,18 +24,15 @@ impl AirGadget { shifted.push(domain.at(*mask_item)); } } + assert!(!shifted.is_empty()); script! { - if !shifted.is_empty() { - for elem in shifted.iter().take(shifted.len() - 1) { - { CirclePointGadget::dup() } - { CirclePointGadget::add_constant_m31_point(elem) } - { CirclePointGadget::swap() } - } - { CirclePointGadget::add_constant_m31_point(shifted.last().unwrap()) } - } else { - { CirclePointGadget::drop() } + for elem in shifted.iter().take(shifted.len() - 1) { + { CirclePointGadget::dup() } + { CirclePointGadget::add_constant_m31_point(elem) } + { CirclePointGadget::swap() } } + { CirclePointGadget::add_constant_m31_point(shifted.last().unwrap()) } } } @@ -57,18 +54,15 @@ impl AirGadget { shifted.push(step_point.mul_signed(*mask_item)); } } + assert!(!shifted.is_empty()); script! { - if !shifted.is_empty() { - for elem in shifted.iter().take(shifted.len() - 1) { - { CirclePointGadget::dup() } - { CirclePointGadget::add_constant_m31_point(elem) } - { CirclePointGadget::swap() } - } - { CirclePointGadget::add_constant_m31_point(shifted.last().unwrap()) } - } else { - { CirclePointGadget::drop() } + for elem in shifted.iter().take(shifted.len() - 1) { + { CirclePointGadget::dup() } + { CirclePointGadget::add_constant_m31_point(elem) } + { CirclePointGadget::swap() } } + { CirclePointGadget::add_constant_m31_point(shifted.last().unwrap()) } } } diff --git a/src/air/mod.rs b/src/air/mod.rs index 3559a0a..2e60272 100644 --- a/src/air/mod.rs +++ b/src/air/mod.rs @@ -41,3 +41,41 @@ pub fn shifted_signed_mask_points( }) .collect() } + +#[cfg(test)] +mod test { + use crate::air::CompositionHint; + use crate::treepp::*; + use crate::utils::get_rand_qm31; + use rand::SeedableRng; + use rand_chacha::ChaCha20Rng; + use rust_bitcoin_m31::qm31_equalverify; + + #[test] + fn test_composition_hint_pushable() { + let mut prng = ChaCha20Rng::seed_from_u64(0); + + for num in 0..10 { + let mut v = vec![]; + for _ in 0..num { + v.push(get_rand_qm31(&mut prng)); + } + + let composition_hint = CompositionHint { + constraint_eval_quotients_by_mask: v.clone(), + }; + + let script = script! { + { composition_hint } + for &elem in v.iter().rev() { + { elem } + qm31_equalverify + } + OP_TRUE + }; + + let exec_result = execute_script(script); + assert!(exec_result.success); + } + } +}