Repository: BitVM/BitVM
Pull request #263: Make BLAKE3 Limb Length Generic
Author: login: just-erray
Created: 2025-03-14T19:26:05Z | Merged: 2025-03-17T01:52:06Z

This PR changes the compact BLAKE3 code, so that it can work with various limb lengths. Previously, it worked only with 29, since the underlying BigInt limb length for field values was 29. By changing only 4 lines inside the BLAKE3 function, this update improves usability for future and outside use (since using a limb length of 4 enables the script for the unpacked version) while also serving as a small optimization for other cases.

Note that this PR also includes some small optimizations. It is currently in draft status due to significant conflicts with an [open PR](https://github.com/BitVM/BitVM/pull/257). Once that PR is merged, this code will undergo substantial revisions.


Diff:
diff --git a/bitvm/src/chunk/wrap_hasher.rs b/bitvm/src/chunk/wrap_hasher.rs
index b97a7fd5..1b711c05 100644
--- a/bitvm/src/chunk/wrap_hasher.rs
+++ b/bitvm/src/chunk/wrap_hasher.rs
@@ -4,7 +4,6 @@ use crate::{
     signatures::wots_api,
     treepp::*,
 };
-use bitcoin_script_stack::stack::StackTracker;
 use hash_utils::{
     hash_fp2, hash_fp6, hash_g2acc, hash_g2acc_with_hash_t, hash_g2acc_with_hashed_le,
 };
diff --git a/bitvm/src/hash/blake3.rs b/bitvm/src/hash/blake3.rs
index 4d7ddbc3..13ffd0e5 100644
--- a/bitvm/src/hash/blake3.rs
+++ b/bitvm/src/hash/blake3.rs
@@ -18,8 +18,14 @@ use crate::hash::blake3_utils::{compress, get_flags_for_block, TablesVars};
 ///
 /// ## See
 ///
-/// [`blake3_compute_script`].
-fn blake3(stack: &mut StackTracker, mut msg_len: u32, define_var: bool, use_full_tables: bool) {
+/// [`blake3_compute_script_with_limb`].
+fn blake3(
+    stack: &mut StackTracker,
+    mut msg_len: u32,
+    define_var: bool,
+    use_full_tables: bool,
+    limb_len: u8,
+) {
     // this assumes that the stack is empty
     if msg_len == 0 {
         // af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262
@@ -50,15 +56,20 @@ fn blake3(stack: &mut StackTracker, mut msg_len: u32, define_var: bool, use_full
         msg_len <= 1024,
         "msg length must be less than or equal to 1024 bytes"
     );
+    assert!(
+        (4..32).contains(&limb_len),
+        "limb length must be in the range [4, 32)"
+    );
 
     //number of msg blocks
-    let num_blocks = f64::ceil(msg_len as f64 / 64_f64) as u32;
+    let num_blocks = msg_len.div_ceil(64);
 
     // If the compact form of message is on stack but not associated with variable, convert it to StackVariable
     if define_var {
+        let limb_count = 256u32.div_ceil(limb_len as u32);
         for i in (0..num_blocks).rev() {
-            stack.define(9, &format!("msg{}p0", i));
-            stack.define(9, &format!("msg{}p1", i));
+            stack.define(limb_count, &format!("msg{}p0", i));
+            stack.define(limb_count, &format!("msg{}p1", i));
         }
     }
 
@@ -82,7 +93,7 @@ fn blake3(stack: &mut StackTracker, mut msg_len: u32, define_var: bool, use_full
         // unpack the compact form of message
         stack.custom(
             script!(
-                {U256::transform_limbsize(29, 4)}
+                {U256::transform_limbsize(limb_len as u32, 4)}
                 for _ in 0..64{
                     OP_TOALTSTACK
                 }
@@ -95,7 +106,7 @@ fn blake3(stack: &mut StackTracker, mut msg_len: u32, define_var: bool, use_full
 
         stack.custom(
             script!(
-                {U256::transform_limbsize(29,4)}
+                {U256::transform_limbsize(limb_len as u32, 4)}
                 for _ in 0..64{
                     OP_FROMALTSTACK
                 }
@@ -115,28 +126,36 @@ fn blake3(stack: &mut StackTracker, mut msg_len: u32, define_var: bool, use_full
             stack.custom(
                 script!(
                     //Drop whatever padding has been added for packing to limbs and pad with zeros
-                    for _ in 0..(pad_bytes*2){
-                        OP_DROP
+                    for _ in 0..pad_bytes {
+                        OP_2DROP
                     }
 
-                    for _ in 0..(j*2){
+                    for _ in 0..(j*2) {
                         OP_TOALTSTACK
                     }
 
-                    for _ in 0..(4-j) * 2{
-                        OP_DROP
+                    for _ in 0..(4-j) {
+                        OP_2DROP
                     }
 
-                    for _ in 0..(4-j) * 2{
-                        OP_0
+                    for j in 0..(4-j) * 2 {
+                        if j <= 1 {
+                            OP_0
+                        } else if j % 2 == 1 {
+                            OP_2DUP
+                        } // no else since loop is even
                     }
 
                     for _ in 0..(j*2){
                         OP_FROMALTSTACK
                     }
 
-                    for _ in 0..(pad_bytes*2){
-                        OP_0
+                    for j in 0..(pad_bytes*2) {
+                        if j <= 1 {
+                            OP_0
+                        } else if j % 2 == 1 {
+                            OP_2DUP
+                        } // no else since loop is even
                     }
                 ),
                 0,
@@ -214,7 +233,7 @@ fn chunk_message(message_bytes: &[u8]) -> Vec<[u8; 64]> {
 ///
 /// This function panics if the message is longer than 1024 bytes,
 /// since our BLAKE3 implementation doesn't support longer messages.
-pub fn blake3_push_message_script(message_bytes: &[u8]) -> Script {
+pub fn blake3_push_message_script_with_limb(message_bytes: &[u8], limb_len: u8) -> Script {
     assert!(
         message_bytes.len() <= 1024,
         "This BLAKE3 implementation doesn't support messages longer than 1024 bytes"
@@ -229,7 +248,7 @@ pub fn blake3_push_message_script(message_bytes: &[u8]) -> Script {
                 }
                 if i == 31 || i == 63 {
                     {
-                        U256::transform_limbsize(8, 29)
+                        U256::transform_limbsize(8, limb_len as u32)
                     }
                 }
             }
@@ -237,26 +256,51 @@ pub fn blake3_push_message_script(message_bytes: &[u8]) -> Script {
     }
 }
 
+/// Number of elements in total of all tables
+const SUM_OF_FULL_TABLES: usize = 384;
+
+/// Number of elements of an unpacked block
+const UNPACKED_BLOCK: usize = 128;
+
+/// Maximum number of elements in stack during the execution of BLAKE3 algorithm
+const MAX_BLAKE3_ELEMENT_COUNT: usize =
+    SUM_OF_FULL_TABLES + UNPACKED_BLOCK + /* Extra BLAKE3 variables */ 132;
+
+/// Calculates the maximum number of altstack elements one can have using the [`blake3_compute_script`] function with the following formula:
+/// ```text
+///  n (number of blocks) = ⌈msg_len / 64⌉
+///  limb_count (number of limbs in a block) = ⌈256 / limb_len⌉ * 2
+///  m (message's consumption of stack during BLAKE3) = (n - 1) * limb_count
+///  Since BLAKE3 requires an empty stack and we've calculated the usage for the message and the algorithm:
+///  MAX_NUMBEROF_ALTSTACK_ELEMENTS = 1000 (max stack limit) - m - 644 (Maximum number of elements used during BLAKE3)
+/// ```
+pub fn maximum_number_of_altstack_elements_using_blake3(message_len: usize, limb_len: u8) -> i32 {
+    let n = message_len.div_ceil(64);
+    let limb_count = 256usize.div_ceil(limb_len as usize) * 2;
+    let m = (n - 1) * limb_count;
+    1000_i32 - MAX_BLAKE3_ELEMENT_COUNT as i32 - m as i32
+}
+
 /// Returns a script that computes the BLAKE3 hash of the message on the stack.
 ///
 /// The script processes compact message blocks and only unpacks them when needed,
 /// resulting in higher stack efficiency and support for larger messages.
 ///
-/// The message length is the number of message bytes without padding.
-///
-/// ## Empty Stack Requirement
+/// ## Parameters
 ///
-/// The stack MUST contain only the message.
-/// Anything else MUST be moved to the alt stack.
-/// When hashing the empty message (length 0), the stack MUST be empty.
+/// - `msg_len`: Length of the message. (excluding the padding, number of bytes)
+/// - `limb_len`: Limb length (number of bits per element) that the input in the stack is packed, for example it is 29 for current field elements
 ///
-/// ## Message Format Requirement
+/// ## Message Format Requirements
 ///
-/// The message MUST be in compact form:
-/// The message is split into 512-bit blocks,
-/// where each block is represented by two U256 values on the stack (18 limbs of 29 bits each).
-///
-/// A message of `n` blocks looks as follows on the stack:
+/// - __The stack contains only message. Anything other has to be moved to alt stack.__ If hashing the empty message of length 0, the stack is empty.
+/// - The input message is in the form U256 where each message block is comprised of two U256, each represented with elements consisting of `limb_len` bits
+/// - The input message must unpack to a multiple of 128 nibbles, so pushing padding bytes is necessary
+/// - __BLAKE3 uses exactly [`MAX_BLAKE3_ELEMENT_COUNT`] = 644 elements at maximum, including the tables__ \
+///  With the max stack limit 1000, __you are allowed to have at most 356 elements including the message (excluding the first block of it) in stack (in total, of altstack and stack)__ \
+///  Note that smaller `limb_len`'s means more elements, hence more stack usage \
+///  For a more certain number, you can look into and use [`maximum_number_of_altstack_elements_using_blake3`]
+/// - A message of `n` blocks is expected in the following format:
 ///
 /// ```text
 /// block_n_part_0 : U256
@@ -265,17 +309,14 @@ pub fn blake3_push_message_script(message_bytes: &[u8]) -> Script {
 /// block_0_part_0 : U256
 /// block_0_part_1 : U256 (top of the stack)
 /// ```
-///
-/// The message length MUST be divisible by 64 byte (128 nibbles).
-/// The message MUST be padded so that it aligns to a multiple of  (2 * 9) limbs,
-/// resulting in a size that is a multiple of 64 bytes (128 nibbles).
-///
 /// ## Panics
 ///
-/// - The stack contains elements other than the message.
-/// - The message is longer than 1024 bytes,
-///   since our BLAKE3 implementation doesn't support longer messages.
-/// - The message is incorrectly formatted.
+/// - If `msg_len` is greater than 1024 bytes, the function panics with an assertion error.
+/// - Given script might not also fit on the max stack limit with messages smaller than 1024 bytes \
+///   if the `limb_len` is small or input stacks has other elements (in the altstack)
+/// - If `limb_len` is not in the range [4, 32)
+/// - If the input doesn't unpack to a multiple of 128 nibbles with the given limb length parameter.
+/// - If the stack contains elements other than the message.
 ///
 /// ## Implementation
 ///
@@ -291,8 +332,8 @@ pub fn blake3_push_message_script(message_bytes: &[u8]) -> Script {
 /// ## Stack Effects
 ///
 /// - Temporarily uses the alternate stack for intermediate results and hash computation tables.
-/// - Final result is left on the main stack as a BLAKE3 hash value.
-pub fn blake3_compute_script(message_len: usize) -> Script {
+/// - Final result is left on the main stack as a BLAKE3 hash value. (in nibbles)
+pub fn blake3_compute_script_with_limb(message_len: usize, limb_len: u8) -> Script {
     assert!(
         message_len <= 1024,
         "This BLAKE3 implementation doesn't support messages longer than 1024 bytes"
@@ -300,10 +341,15 @@ pub fn blake3_compute_script(message_len: usize) -> Script {
     let mut stack = StackTracker::new();
     let use_full_tables = true;
     let message_len = message_len as u32; // safety: message_len <= 1024 << u32::MAX
-    blake3(&mut stack, message_len, true, use_full_tables);
+    blake3(&mut stack, message_len, true, use_full_tables, limb_len);
     stack.get_script()
 }
 
+/// Uses [`blake3_compute_script_with_limb`].with limb length 29, see the documentation of it for more details
+pub fn blake3_compute_script(message_len: usize) -> Script {
+    blake3_compute_script_with_limb(message_len, 29)
+}
+
 /// Returns a script that verifies the BLAKE3 output on the stack.
 ///
 /// The script pops the BLAKE3 output and compares it with the given, expected output.
@@ -328,64 +374,85 @@ pub fn blake3_verify_output_script(expected_output: [u8; 32]) -> Script {
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::execute_script_buf;
+    use crate::{execute_script, execute_script_buf_without_stack_limit};
     use bitcoin::ScriptBuf;
     use bitcoin_script_stack::optimizer;
 
-    pub fn verify_blake_output(message: &[u8], expected_hash: [u8; 32]) {
-        use crate::execute_script_buf;
-        use bitcoin_script_stack::optimizer;
+    // All tests are run with `USEFUL_LIMB_LENGTHS` lengths, and ignored tests are run with `ALL_POSSIBLE_LIMB_LENGTHS`
+    // If any changes are done to the BLAKE3 code, running the tests with all possible lengths is a good idea
+    // You can use either of the following commands for that:
+    // cargo test all_limbs -- --ignored
+    // cargo test hash::blake3 -- --ignored
+
+    const USEFUL_LIMB_LENGTHS: [u8; 2] = [4, 29];
+
+    const ALL_POSSIBLE_LIMB_LENGTHS: [u8; 28] = [
+        4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
+        28, 29, 30, 31,
+    ];
 
-        let mut bytes = blake3_push_message_script(&message).compile().to_bytes();
-        let optimized = optimizer::optimize(blake3_compute_script(message.len()).compile());
-        bytes.extend(optimized.to_bytes());
-        bytes.extend(
-            blake3_verify_output_script(expected_hash)
+    fn verify_blake_output_with_limbs(message: &[u8], expected_hash: [u8; 32], limb_lens: &[u8]) {
+        for limb_len in limb_lens.iter().copied() {
+            let mut bytes = blake3_push_message_script_with_limb(message, limb_len)
                 .compile()
-                .to_bytes(),
-        );
-        let script = ScriptBuf::from_bytes(bytes);
-        assert!(execute_script_buf(script).success);
+                .to_bytes();
+            let optimized = optimizer::optimize(
+                blake3_compute_script_with_limb(message.len(), limb_len).compile(),
+            );
+            bytes.extend(optimized.to_bytes());
+            bytes.extend(
+                blake3_verify_output_script(expected_hash)
+                    .compile()
+                    .to_bytes(),
+            );
+            let script = ScriptBuf::from_bytes(bytes);
+            assert!(execute_script_buf_without_stack_limit(script).success);
+        }
     }
 
-    fn verify_blake_outputs_cached<const LEN: usize>(
+    fn verify_blake_outputs_cached_with_limbs<const LEN: usize>(
         messages: &[[u8; LEN]],
         expected_hashes: &[[u8; 32]],
+        limb_lens: &[u8],
     ) {
         assert_eq!(
             messages.len(),
             expected_hashes.len(),
             "There must be as many messages as there are expected hashes"
         );
-        let optimized = optimizer::optimize(blake3_compute_script(LEN).compile());
-
-        for (i, message) in messages.iter().enumerate() {
-            let expected_hash = expected_hashes[i];
+        for limb_len in limb_lens.iter().copied() {
+            let optimized =
+                optimizer::optimize(blake3_compute_script_with_limb(LEN, limb_len).compile());
+            for (i, message) in messages.iter().enumerate() {
+                let expected_hash = expected_hashes[i];
 
-            let mut bytes = blake3_push_message_script(message).compile().to_bytes();
-            bytes.extend_from_slice(optimized.as_bytes());
-            bytes.extend(
-                blake3_verify_output_script(expected_hash)
+                let mut bytes = blake3_push_message_script_with_limb(message, limb_len)
                     .compile()
-                    .to_bytes(),
-            );
-            let script = ScriptBuf::from_bytes(bytes);
-            assert!(execute_script_buf(script).success);
+                    .to_bytes();
+                bytes.extend_from_slice(optimized.as_bytes());
+                bytes.extend(
+                    blake3_verify_output_script(expected_hash)
+                        .compile()
+                        .to_bytes(),
+                );
+                let script = ScriptBuf::from_bytes(bytes);
+                assert!(execute_script_buf_without_stack_limit(script).success);
+            }
         }
     }
 
     #[test]
     fn test_zero_length() {
         let message = [];
-        let expected_hash = blake3::hash(&message).as_bytes().clone();
-        verify_blake_output(&message, expected_hash);
+        let expected_hash = *blake3::hash(&message).as_bytes();
+        verify_blake_output_with_limbs(&message, expected_hash, &USEFUL_LIMB_LENGTHS);
     }
 
     #[test]
     fn test_max_length() {
         let message = [0x00; 1024];
-        let expected_hash = blake3::hash(&message).as_bytes().clone();
-        verify_blake_output(&message, expected_hash);
+        let expected_hash = *blake3::hash(&message).as_bytes();
+        verify_blake_output_with_limbs(&message, expected_hash, &USEFUL_LIMB_LENGTHS);
     }
 
     #[test]
@@ -394,8 +461,8 @@ mod tests {
     )]
     fn test_too_long() {
         let message = [0x00; 1025];
-        let expected_hash = blake3::hash(&message).as_bytes().clone();
-        verify_blake_output(&message, expected_hash);
+        let expected_hash = *blake3::hash(&message).as_bytes();
+        verify_blake_output_with_limbs(&message, expected_hash, &USEFUL_LIMB_LENGTHS);
     }
 
     #[test]
@@ -403,13 +470,27 @@ mod tests {
         let messages: Vec<[u8; 1]> = (0..=255).map(|byte| [byte]).collect();
         let expected_hashes: Vec<[u8; 32]> = messages
             .iter()
-            .map(|message| blake3::hash(message).as_bytes().clone())
+            .map(|message| *blake3::hash(message).as_bytes())
             .collect();
-        verify_blake_outputs_cached(&messages, &expected_hashes);
+        verify_blake_outputs_cached_with_limbs(&messages, &expected_hashes, &USEFUL_LIMB_LENGTHS);
     }
 
     #[test]
-    fn test_official_test_vectors() {
+    #[ignore]
+    fn test_single_byte_with_all_limbs() {
+        let messages: Vec<[u8; 1]> = (0..=255).map(|byte| [byte]).collect();
+        let expected_hashes: Vec<[u8; 32]> = messages
+            .iter()
+            .map(|message| *blake3::hash(message).as_bytes())
+            .collect();
+        verify_blake_outputs_cached_with_limbs(
+            &messages,
+            &expected_hashes,
+            &ALL_POSSIBLE_LIMB_LENGTHS,
+        );
+    }
+
+    fn test_official_test_vectors_with_limbs(limb_lens: &[u8]) {
         use serde::Deserialize;
         use std::fs::File;
         use std::io::BufReader;
@@ -445,7 +526,85 @@ mod tests {
 
         let test_vectors = read_test_vectors();
         for (message, expected_hash) in test_vectors {
-            verify_blake_output(&message, expected_hash);
+            verify_blake_output_with_limbs(&message, expected_hash, limb_lens);
         }
     }
+
+    #[test]
+    fn test_official_test_vectors() {
+        test_official_test_vectors_with_limbs(&USEFUL_LIMB_LENGTHS)
+    }
+
+    #[test]
+    #[ignore]
+    fn test_official_test_vectors_with_all_limbs() {
+        test_official_test_vectors_with_limbs(&ALL_POSSIBLE_LIMB_LENGTHS)
+    }
+
+    fn test_blake3_stack_space(
+        blake3_script: Script,
+        message_len: usize,
+        limb_len: u8,
+        extra_elements: i32,
+    ) -> bool {
+        let message = vec![0u8; message_len];
+        execute_script(script! {
+            for _ in 0..extra_elements {
+                { -1 } OP_TOALTSTACK
+            }
+            { blake3_push_message_script_with_limb(&message, limb_len) }
+            { blake3_script.clone() }
+            for _ in 0..extra_elements {
+                OP_FROMALTSTACK OP_DROP
+            }
+            for _ in 0..64 {
+                OP_DROP
+            }
+            OP_TRUE
+        })
+        .success
+    }
+
+    fn test_maximum_alstack_element_calculation_with_limbs(limb_lens: &[u8]) {
+        for limb_len in limb_lens.iter().copied() {
+            for message_len in (64..=1024).step_by(64) {
+                // Block count depends on ceil(message_len / 64)
+                let blake3_script = blake3_compute_script_with_limb(message_len, limb_len);
+                let maximum_extra_elements =
+                    maximum_number_of_altstack_elements_using_blake3(message_len, limb_len);
+                if maximum_extra_elements < 0 {
+                    assert!(!test_blake3_stack_space(
+                        blake3_script.clone(),
+                        message_len,
+                        limb_len,
+                        0
+                    ));
+                } else {
+                    assert!(test_blake3_stack_space(
+                        blake3_script.clone(),
+                        message_len,
+                        limb_len,
+                        maximum_extra_elements
+                    ));
+                    assert!(!test_blake3_stack_space(
+                        blake3_script.clone(),
+                        message_len,
+                        limb_len,
+                        maximum_extra_elements + 1
+                    ));
+                }
+            }
+        }
+    }
+
+    #[test]
+    fn test_maximum_alstack_element_calculation() {
+        test_maximum_alstack_element_calculation_with_limbs(&USEFUL_LIMB_LENGTHS);
+    }
+
+    #[test]
+    #[ignore]
+    fn test_maximum_alstack_element_calculation_with_all_limbs() {
+        test_maximum_alstack_element_calculation_with_limbs(&ALL_POSSIBLE_LIMB_LENGTHS);
+    }
 }
diff --git a/bitvm/src/lib.rs b/bitvm/src/lib.rs
index fde8ad39..697aa948 100644
--- a/bitvm/src/lib.rs
+++ b/bitvm/src/lib.rs
@@ -117,13 +117,34 @@ impl fmt::Display for ExecuteInfo {
 }
 
 pub fn execute_script(script: treepp::Script) -> ExecuteInfo {
-    execute_script_buf(script.compile())
+    execute_script_buf_optional_stack_limit(script.compile(), true)
 }
 
 pub fn execute_script_buf(script: bitcoin::ScriptBuf) -> ExecuteInfo {
+    execute_script_buf_optional_stack_limit(script, true)
+}
+
+pub fn execute_script_without_stack_limit(script: treepp::Script) -> ExecuteInfo {
+    execute_script_buf_optional_stack_limit(script.compile(), false)
+}
+
+pub fn execute_script_buf_without_stack_limit(script: bitcoin::ScriptBuf) -> ExecuteInfo {
+    execute_script_buf_optional_stack_limit(script, false)
+}
+
+/// Executing a script on stack without `MAX_STACK_SIZE` limit is only for testing purposes \
+/// Don't use in production without the stack limit (i.e. `stack_limit` set to false)
+fn execute_script_buf_optional_stack_limit(
+    script: bitcoin::ScriptBuf,
+    stack_limit: bool,
+) -> ExecuteInfo {
+    let opts = Options {
+        enforce_stack_limit: stack_limit,
+        ..Default::default()
+    };
     let mut exec = Exec::new(
         ExecCtx::Tapscript,
-        Options::default(),
+        opts,
         TxTemplate {
             tx: Transaction {
                 version: bitcoin::transaction::Version::TWO,
@@ -255,53 +276,6 @@ pub fn run(script: treepp::Script) {
     assert!(exec_result.success);
 }
 
-// Execute a script on stack without `MAX_STACK_SIZE` limit.
-// This function is only used for script test, not for production.
-//
-// NOTE: Only for test purposes.
-pub fn execute_script_without_stack_limit(script: treepp::Script) -> ExecuteInfo {
-    // Get the default options for the script exec.
-    // Do not enforce the stack limit.
-    let opts = Options {
-        enforce_stack_limit: false,
-        ..Default::default()
-    };
-
-    let mut exec = Exec::new(
-        ExecCtx::Tapscript,
-        opts,
-        TxTemplate {
-            tx: Transaction {
-                version: bitcoin::transaction::Version::TWO,
-                lock_time: bitcoin::locktime::absolute::LockTime::ZERO,
-                input: vec![],
-                output: vec![],
-            },
-            prevouts: vec![],
-            input_idx: 0,
-            taproot_annex_scriptleaf: Some((TapLeafHash::all_zeros(), None)),
-        },
-        script.compile(),
-        vec![],
-    )
-    .expect("error creating exec");
-
-    loop {
-        if exec.exec_next().is_err() {
-            break;
-        }
-    }
-    let res = exec.result().unwrap();
-    ExecuteInfo {
-        success: res.success,
-        error: res.error.clone(),
-        last_opcode: res.opcode,
-        final_stack: FmtStack(exec.stack().clone()),
-        remaining_script: exec.remaining_script().to_asm_string(),
-        stats: exec.stats().clone(),
-    }
-}
-
 pub fn execute_raw_script_with_inputs(script: Vec<u8>, witness: Vec<Vec<u8>>) -> ExecuteInfo {
     // Get the default options for the script exec.
     // Do not enforce the stack limit.
diff --git a/fuzz/fuzz_targets/blake3.rs b/fuzz/fuzz_targets/blake3.rs
index 18a1a048..e8d13d0e 100644
--- a/fuzz/fuzz_targets/blake3.rs
+++ b/fuzz/fuzz_targets/blake3.rs
@@ -6,20 +6,21 @@ use std::sync::LazyLock;
 use arbitrary::Arbitrary;
 use bitcoin::ScriptBuf;
 use bitcoin_script_stack::optimizer;
-use libfuzzer_sys::fuzz_target;
 use bitvm::execute_script_buf;
 use bitvm::hash::blake3::{
-    blake3_compute_script, blake3_push_message_script, blake3_verify_output_script,
+    blake3_compute_script, blake3_push_message_script_with_limb, blake3_verify_output_script,
 };
+use libfuzzer_sys::fuzz_target;
 
-static BLAKE3_COMPUTE_SCRIPTS: LazyLock<[ScriptBuf; 4]> =
-    LazyLock::new(|| [64, 128, 192, 448]
-            .into_iter()
-            .map(|msg_len| blake3_compute_script(msg_len).compile())
-            .map(|script| optimizer::optimize(script))
-            .collect::<Vec<ScriptBuf>>()
-            .try_into().unwrap()
-    );
+static BLAKE3_COMPUTE_SCRIPTS: LazyLock<[ScriptBuf; 4]> = LazyLock::new(|| {
+    [64, 128, 192, 448]
+        .into_iter()
+        .map(|msg_len| blake3_compute_script(msg_len).compile())
+        .map(|script| optimizer::optimize(script))
+        .collect::<Vec<ScriptBuf>>()
+        .try_into()
+        .unwrap()
+});
 
 /// Cover all message sizes that are used inside BitVM.
 ///
@@ -62,9 +63,11 @@ impl MessageBytes {
 }
 
 fuzz_target!(|message: MessageBytes| {
-    let expected_hash = blake3::hash(message.as_ref()).as_bytes().clone();
-
-    let mut bytes = blake3_push_message_script(message.as_ref()).compile().to_bytes();
+    let expected_hash = *blake3::hash(message.as_ref()).as_bytes();
+    // Fuzz tests are left only for bigints (limb_len = 29)
+    let mut bytes = blake3_push_message_script_with_limb(message.as_ref(), 29)
+        .compile()
+        .to_bytes();
     bytes.extend_from_slice(message.blake3_compute_script().as_bytes());
     bytes.extend_from_slice(
         blake3_verify_output_script(expected_hash)


Reviews:
user: login: AaronFeickert
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-15T07:08:53Z
user: login: just-erray
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-15T08:17:06Z
user: login: just-erray
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-15T08:17:25Z
user: login: just-erray
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-15T08:17:30Z
user: login: just-erray
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-15T08:18:52Z
user: login: just-erray
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-15T08:18:59Z
user: login: just-erray
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-15T08:19:07Z
user: login: just-erray
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-15T08:19:30Z
user: login: just-erray
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-15T08:20:42Z
user: login: just-erray
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-15T08:20:54Z
user: login: AaronFeickert
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-15T08:29:17Z
user: login: uncomputable
body: Since we don't use limb lengths other than 29 and we have an upcoming audit, can we keep the function signatures of the blake3 functions the same and introduce private functions with the more general limb parameter?

- `blake3_compute_script` + `blake3_compute_script_with_limbs`
- `blake3_push_message_script` + `blake3_push_message_script_with_limbs`
- `blake3_verify_output_script` + `blake3_verify_output_script_with_limbs`

(Feel free to choose a different naming scheme)

Also, can you introduce a separate function to unit test different limb lengths? Iterating over all possible limb lengths just makes CI run for much longer. Especially, please use the old method for `test_single_byte` and `test_official_test_vectors`. Those tests run 10 seconds in the old version, and they will run for minutes your generalized version.
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-15T11:06:19Z
user: login: uncomputable
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-15T11:09:25Z
user: login: uncomputable
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-15T11:12:13Z
user: login: just-erray
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-15T11:23:43Z
user: login: just-erray
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-15T11:40:52Z
user: login: uncomputable
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-15T12:16:47Z
user: login: just-erray
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-15T12:20:26Z
user: login: uncomputable
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-15T12:23:34Z
user: login: uncomputable
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-15T14:20:35Z
user: login: uncomputable
body: ACK 2dcd8adb9e4e2bff208b41d091a0751790eab307 Looks good to me. Thanks for accommodating my feedback. I haven't run the tests because my laptop is too weak. @AaronFeickert 
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-15T17:21:17Z
user: login: AaronFeickert
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-16T16:03:40Z

Review comments:
diff_hunk: @@ -41,7 +47,7 @@ fn blake3(stack: &mut StackTracker, mut msg_len: u32, define_var: bool, use_full
             0,
             "push empty string hash in nibble form",
         );
-        stack.define(8_u32 * 8, "blake3-hash");
+        stack.define(8 as u32 * 8, "blake3-hash");
path: bitvm/src/hash/blake3.rs
user: login: AaronFeickert
body: Seems unnecessary.
created_at: 2025-03-15T06:59:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -50,15 +56,20 @@ fn blake3(stack: &mut StackTracker, mut msg_len: u32, define_var: bool, use_full
         msg_len <= 1024,
         "msg length must be less than or equal to 1024 bytes"
     );
+    assert!(
+        (4..32).contains(&limb_len),
+        "limb length must be in the range [4, 32)"
+    );
 
     //number of msg blocks
-    let num_blocks = f64::ceil(msg_len as f64 / 64_f64) as u32;
+    let num_blocks = (msg_len + 63) / 64;
path: bitvm/src/hash/blake3.rs
user: login: AaronFeickert
body: Should be able to use `div_ceil` here instead.
created_at: 2025-03-15T07:00:33Z
author_association: CONTRIBUTOR
diff_hunk: @@ -50,15 +56,20 @@ fn blake3(stack: &mut StackTracker, mut msg_len: u32, define_var: bool, use_full
         msg_len <= 1024,
         "msg length must be less than or equal to 1024 bytes"
     );
+    assert!(
+        (4..32).contains(&limb_len),
+        "limb length must be in the range [4, 32)"
+    );
 
     //number of msg blocks
-    let num_blocks = f64::ceil(msg_len as f64 / 64_f64) as u32;
+    let num_blocks = (msg_len + 63) / 64;
 
     // If the compact form of message is on stack but not associated with variable, convert it to StackVariable
     if define_var {
+        let limb_count = (256 + limb_len as u32 - 1) / limb_len as u32;
path: bitvm/src/hash/blake3.rs
user: login: AaronFeickert
body: Replace with `div_ceil` for clarity of intent.
created_at: 2025-03-15T07:00:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -19,7 +19,13 @@ use crate::hash::blake3_utils::{compress, get_flags_for_block, TablesVars};
 /// ## See
 ///
 /// [`blake3_compute_script`].
-fn blake3(stack: &mut StackTracker, mut msg_len: u32, define_var: bool, use_full_tables: bool) {
+fn blake3(
+    stack: &mut StackTracker,
+    mut msg_len: u32,
+    define_var: bool,
+    use_full_tables: bool,
+    limb_len: u8,
path: bitvm/src/hash/blake3.rs
user: login: AaronFeickert
body: Any restrictions on the limb length here that you're assuming?
created_at: 2025-03-15T07:01:27Z
author_association: CONTRIBUTOR
diff_hunk: @@ -184,7 +203,7 @@ fn blake3(stack: &mut StackTracker, mut msg_len: u32, define_var: bool, use_full
     tables.drop(stack);
 
     // get the result hash
-    stack.from_altstack_joined(8_u32 * 8, "blake3-hash");
+    stack.from_altstack_joined(8 as u32 * 8, "blake3-hash");
path: bitvm/src/hash/blake3.rs
user: login: AaronFeickert
body: Seems unnecessary.
created_at: 2025-03-15T07:02:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -266,44 +281,44 @@ pub fn blake3_push_message_script(message_bytes: &[u8]) -> Script {
 /// block_0_part_1 : U256 (top of the stack)
 /// ```
 ///
-/// The message length MUST be divisible by 64 byte (128 nibbles).
-/// The message MUST be padded so that it aligns to a multiple of  (2 * 9) limbs,
-/// resulting in a size that is a multiple of 64 bytes (128 nibbles).
-///
-/// ## Panics
-///
-/// - The stack contains elements other than the message.
-/// - The message is longer than 1024 bytes,
-///   since our BLAKE3 implementation doesn't support longer messages.
-/// - The message is incorrectly formatted.
-///
-/// ## Implementation
-///
-/// 1. Defines stack variables for compact message blocks.
+/// ## Behavior:
+/// 1. Defines stack variables for compact message blocks if `define_var` is enabled.
 /// 2. Moves the compact message to an alternate stack for processing.
 /// 3. Initializes hash computation tables.
 /// 4. Processes each message block:
-///     - Unpacks compact message forms.
+///     - Unpacks message forms.
 ///     - Corrects any user-provided padding if it is the last block.
 ///     - Computes the hash for the block using `compress` while maintaining intermediate states.
 /// 5. Drops intermediate states and finalizes the hash result on the stack.
 ///
-/// ## Stack Effects
-///
+/// ## Stack Effects:
 /// - Temporarily uses the alternate stack for intermediate results and hash computation tables.
-/// - Final result is left on the main stack as a BLAKE3 hash value.
-pub fn blake3_compute_script(message_len: usize) -> Script {
+/// - Final result is left on the main stack as a BLAKE3 hash value. (in nibbles)
+///
+/// ## Panics:
+/// - If `msg_len` is greater than 1024 bytes, the function panics with an assertion error.
+/// - Given script might not also fit on the max stack limit with messages smaller than 1024 bytes \
+///   if the `limb_len` is small or input stacks has other elements (in the altstack)
+/// - If `limb_len` is not in the range [4, 32)
+/// - If the input doesn't unpack to a multiple of 128 nibbles with the given limb length parameter.
+/// - If the stack contains elements other than the message.
+pub fn blake3_compute_script(message_len: usize, limb_len: u8) -> Script {
     assert!(
         message_len <= 1024,
         "This BLAKE3 implementation doesn't support messages longer than 1024 bytes"
     );
     let mut stack = StackTracker::new();
     let use_full_tables = true;
     let message_len = message_len as u32; // safety: message_len <= 1024 << u32::MAX
-    blake3(&mut stack, message_len, true, use_full_tables);
+    blake3(&mut stack, message_len, true, use_full_tables, limb_len);
     stack.get_script()
 }
 
+/// See [`blake3_compute_script`].
path: bitvm/src/hash/blake3.rs
user: login: AaronFeickert
body: Please document explicitly that this simply calls the generic function with 29-bit limbs.
created_at: 2025-03-15T07:03:47Z
author_association: CONTRIBUTOR
diff_hunk: @@ -328,24 +343,41 @@ pub fn blake3_verify_output_script(expected_output: [u8; 32]) -> Script {
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::execute_script_buf;
+    use crate::execute_script_buf_without_stack_limit;
     use bitcoin::ScriptBuf;
     use bitcoin_script_stack::optimizer;
 
+    const RUN_EXTENSIVE_TESTS: bool = false;
+
+    const ALL_POSSIBLE_LIMB_LENGTHS: [u8; 28] = [
path: bitvm/src/hash/blake3.rs
user: login: AaronFeickert
body: This seems to imply there are particular limits to allowed limbs. This is not checked anywhere.
created_at: 2025-03-15T07:04:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -328,24 +343,41 @@ pub fn blake3_verify_output_script(expected_output: [u8; 32]) -> Script {
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::execute_script_buf;
+    use crate::execute_script_buf_without_stack_limit;
     use bitcoin::ScriptBuf;
     use bitcoin_script_stack::optimizer;
 
+    const RUN_EXTENSIVE_TESTS: bool = false;
+
+    const ALL_POSSIBLE_LIMB_LENGTHS: [u8; 28] = [
path: bitvm/src/hash/blake3.rs
user: login: AaronFeickert
body: It's also not documented anywhere.
created_at: 2025-03-15T07:06:25Z
author_association: CONTRIBUTOR
diff_hunk: @@ -328,24 +343,41 @@ pub fn blake3_verify_output_script(expected_output: [u8; 32]) -> Script {
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::execute_script_buf;
+    use crate::execute_script_buf_without_stack_limit;
     use bitcoin::ScriptBuf;
     use bitcoin_script_stack::optimizer;
 
+    const RUN_EXTENSIVE_TESTS: bool = false;
path: bitvm/src/hash/blake3.rs
user: login: AaronFeickert
body: Document this.
created_at: 2025-03-15T07:06:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -117,13 +117,34 @@ impl fmt::Display for ExecuteInfo {
 }
 
 pub fn execute_script(script: treepp::Script) -> ExecuteInfo {
-    execute_script_buf(script.compile())
+    execute_script_buf_optional_stack_limit(script.compile(), true)
 }
 
 pub fn execute_script_buf(script: bitcoin::ScriptBuf) -> ExecuteInfo {
+    execute_script_buf_optional_stack_limit(script, true)
+}
+
+pub fn execute_script_without_stack_limit(script: treepp::Script) -> ExecuteInfo {
+    execute_script_buf_optional_stack_limit(script.compile(), false)
+}
+
+pub fn execute_script_buf_without_stack_limit(script: bitcoin::ScriptBuf) -> ExecuteInfo {
+    execute_script_buf_optional_stack_limit(script, false)
+}
path: bitvm/src/lib.rs
user: login: AaronFeickert
body: Do these need to be public?
created_at: 2025-03-15T07:07:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -117,13 +117,34 @@ impl fmt::Display for ExecuteInfo {
 }
 
 pub fn execute_script(script: treepp::Script) -> ExecuteInfo {
-    execute_script_buf(script.compile())
+    execute_script_buf_optional_stack_limit(script.compile(), true)
 }
 
 pub fn execute_script_buf(script: bitcoin::ScriptBuf) -> ExecuteInfo {
+    execute_script_buf_optional_stack_limit(script, true)
+}
+
+pub fn execute_script_without_stack_limit(script: treepp::Script) -> ExecuteInfo {
+    execute_script_buf_optional_stack_limit(script.compile(), false)
+}
+
+pub fn execute_script_buf_without_stack_limit(script: bitcoin::ScriptBuf) -> ExecuteInfo {
+    execute_script_buf_optional_stack_limit(script, false)
+}
+
+/// Executing a script on stack without `MAX_STACK_SIZE` limit is only for testing purposes \
+/// Don't use in production without the stack limit (i.e. `stack_limit` set to false)
+pub fn execute_script_buf_optional_stack_limit(
path: bitvm/src/lib.rs
user: login: AaronFeickert
body: Does this need to be public?
created_at: 2025-03-15T07:08:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -41,7 +47,7 @@ fn blake3(stack: &mut StackTracker, mut msg_len: u32, define_var: bool, use_full
             0,
             "push empty string hash in nibble form",
         );
-        stack.define(8_u32 * 8, "blake3-hash");
+        stack.define(8 as u32 * 8, "blake3-hash");
path: bitvm/src/hash/blake3.rs
user: login: just-erray
body: I think this is left since I started editing from an old PR, reverting it
created_at: 2025-03-15T08:17:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -50,15 +56,20 @@ fn blake3(stack: &mut StackTracker, mut msg_len: u32, define_var: bool, use_full
         msg_len <= 1024,
         "msg length must be less than or equal to 1024 bytes"
     );
+    assert!(
+        (4..32).contains(&limb_len),
+        "limb length must be in the range [4, 32)"
+    );
 
     //number of msg blocks
-    let num_blocks = f64::ceil(msg_len as f64 / 64_f64) as u32;
+    let num_blocks = (msg_len + 63) / 64;
path: bitvm/src/hash/blake3.rs
user: login: just-erray
body: Good idea
created_at: 2025-03-15T08:17:25Z
author_association: CONTRIBUTOR
diff_hunk: @@ -50,15 +56,20 @@ fn blake3(stack: &mut StackTracker, mut msg_len: u32, define_var: bool, use_full
         msg_len <= 1024,
         "msg length must be less than or equal to 1024 bytes"
     );
+    assert!(
+        (4..32).contains(&limb_len),
+        "limb length must be in the range [4, 32)"
+    );
 
     //number of msg blocks
-    let num_blocks = f64::ceil(msg_len as f64 / 64_f64) as u32;
+    let num_blocks = (msg_len + 63) / 64;
 
     // If the compact form of message is on stack but not associated with variable, convert it to StackVariable
     if define_var {
+        let limb_count = (256 + limb_len as u32 - 1) / limb_len as u32;
path: bitvm/src/hash/blake3.rs
user: login: just-erray
body: Good idea
created_at: 2025-03-15T08:17:30Z
author_association: CONTRIBUTOR
diff_hunk: @@ -19,7 +19,13 @@ use crate::hash::blake3_utils::{compress, get_flags_for_block, TablesVars};
 /// ## See
 ///
 /// [`blake3_compute_script`].
-fn blake3(stack: &mut StackTracker, mut msg_len: u32, define_var: bool, use_full_tables: bool) {
+fn blake3(
+    stack: &mut StackTracker,
+    mut msg_len: u32,
+    define_var: bool,
+    use_full_tables: bool,
+    limb_len: u8,
path: bitvm/src/hash/blake3.rs
user: login: just-erray
body: It is documented and asserted in the code, but I will make the documentation more detailed.
created_at: 2025-03-15T08:18:52Z
author_association: CONTRIBUTOR
diff_hunk: @@ -184,7 +203,7 @@ fn blake3(stack: &mut StackTracker, mut msg_len: u32, define_var: bool, use_full
     tables.drop(stack);
 
     // get the result hash
-    stack.from_altstack_joined(8_u32 * 8, "blake3-hash");
+    stack.from_altstack_joined(8 as u32 * 8, "blake3-hash");
path: bitvm/src/hash/blake3.rs
user: login: just-erray
body: Same as before
created_at: 2025-03-15T08:18:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -266,44 +281,44 @@ pub fn blake3_push_message_script(message_bytes: &[u8]) -> Script {
 /// block_0_part_1 : U256 (top of the stack)
 /// ```
 ///
-/// The message length MUST be divisible by 64 byte (128 nibbles).
-/// The message MUST be padded so that it aligns to a multiple of  (2 * 9) limbs,
-/// resulting in a size that is a multiple of 64 bytes (128 nibbles).
-///
-/// ## Panics
-///
-/// - The stack contains elements other than the message.
-/// - The message is longer than 1024 bytes,
-///   since our BLAKE3 implementation doesn't support longer messages.
-/// - The message is incorrectly formatted.
-///
-/// ## Implementation
-///
-/// 1. Defines stack variables for compact message blocks.
+/// ## Behavior:
+/// 1. Defines stack variables for compact message blocks if `define_var` is enabled.
 /// 2. Moves the compact message to an alternate stack for processing.
 /// 3. Initializes hash computation tables.
 /// 4. Processes each message block:
-///     - Unpacks compact message forms.
+///     - Unpacks message forms.
 ///     - Corrects any user-provided padding if it is the last block.
 ///     - Computes the hash for the block using `compress` while maintaining intermediate states.
 /// 5. Drops intermediate states and finalizes the hash result on the stack.
 ///
-/// ## Stack Effects
-///
+/// ## Stack Effects:
 /// - Temporarily uses the alternate stack for intermediate results and hash computation tables.
-/// - Final result is left on the main stack as a BLAKE3 hash value.
-pub fn blake3_compute_script(message_len: usize) -> Script {
+/// - Final result is left on the main stack as a BLAKE3 hash value. (in nibbles)
+///
+/// ## Panics:
+/// - If `msg_len` is greater than 1024 bytes, the function panics with an assertion error.
+/// - Given script might not also fit on the max stack limit with messages smaller than 1024 bytes \
+///   if the `limb_len` is small or input stacks has other elements (in the altstack)
+/// - If `limb_len` is not in the range [4, 32)
+/// - If the input doesn't unpack to a multiple of 128 nibbles with the given limb length parameter.
+/// - If the stack contains elements other than the message.
+pub fn blake3_compute_script(message_len: usize, limb_len: u8) -> Script {
     assert!(
         message_len <= 1024,
         "This BLAKE3 implementation doesn't support messages longer than 1024 bytes"
     );
     let mut stack = StackTracker::new();
     let use_full_tables = true;
     let message_len = message_len as u32; // safety: message_len <= 1024 << u32::MAX
-    blake3(&mut stack, message_len, true, use_full_tables);
+    blake3(&mut stack, message_len, true, use_full_tables, limb_len);
     stack.get_script()
 }
 
+/// See [`blake3_compute_script`].
path: bitvm/src/hash/blake3.rs
user: login: just-erray
body: Good idea
created_at: 2025-03-15T08:19:07Z
author_association: CONTRIBUTOR
diff_hunk: @@ -328,24 +343,41 @@ pub fn blake3_verify_output_script(expected_output: [u8; 32]) -> Script {
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::execute_script_buf;
+    use crate::execute_script_buf_without_stack_limit;
     use bitcoin::ScriptBuf;
     use bitcoin_script_stack::optimizer;
 
+    const RUN_EXTENSIVE_TESTS: bool = false;
+
+    const ALL_POSSIBLE_LIMB_LENGTHS: [u8; 28] = [
path: bitvm/src/hash/blake3.rs
user: login: just-erray
body: It is checked with asserts and is documented, but I'll document the test options
created_at: 2025-03-15T08:19:29Z
author_association: CONTRIBUTOR
diff_hunk: @@ -117,13 +117,34 @@ impl fmt::Display for ExecuteInfo {
 }
 
 pub fn execute_script(script: treepp::Script) -> ExecuteInfo {
-    execute_script_buf(script.compile())
+    execute_script_buf_optional_stack_limit(script.compile(), true)
 }
 
 pub fn execute_script_buf(script: bitcoin::ScriptBuf) -> ExecuteInfo {
+    execute_script_buf_optional_stack_limit(script, true)
+}
+
+pub fn execute_script_without_stack_limit(script: treepp::Script) -> ExecuteInfo {
+    execute_script_buf_optional_stack_limit(script.compile(), false)
+}
+
+pub fn execute_script_buf_without_stack_limit(script: bitcoin::ScriptBuf) -> ExecuteInfo {
+    execute_script_buf_optional_stack_limit(script, false)
+}
path: bitvm/src/lib.rs
user: login: just-erray
body: Yes, one of them is used in BLAKE3 tests, and in general, they were created previously and used for extensive testing.
created_at: 2025-03-15T08:20:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -117,13 +117,34 @@ impl fmt::Display for ExecuteInfo {
 }
 
 pub fn execute_script(script: treepp::Script) -> ExecuteInfo {
-    execute_script_buf(script.compile())
+    execute_script_buf_optional_stack_limit(script.compile(), true)
 }
 
 pub fn execute_script_buf(script: bitcoin::ScriptBuf) -> ExecuteInfo {
+    execute_script_buf_optional_stack_limit(script, true)
+}
+
+pub fn execute_script_without_stack_limit(script: treepp::Script) -> ExecuteInfo {
+    execute_script_buf_optional_stack_limit(script.compile(), false)
+}
+
+pub fn execute_script_buf_without_stack_limit(script: bitcoin::ScriptBuf) -> ExecuteInfo {
+    execute_script_buf_optional_stack_limit(script, false)
+}
+
+/// Executing a script on stack without `MAX_STACK_SIZE` limit is only for testing purposes \
+/// Don't use in production without the stack limit (i.e. `stack_limit` set to false)
+pub fn execute_script_buf_optional_stack_limit(
path: bitvm/src/lib.rs
user: login: just-erray
body: No, changing it
created_at: 2025-03-15T08:20:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -50,15 +56,20 @@ fn blake3(stack: &mut StackTracker, mut msg_len: u32, define_var: bool, use_full
         msg_len <= 1024,
         "msg length must be less than or equal to 1024 bytes"
     );
+    assert!(
+        (4..32).contains(&limb_len),
+        "limb length must be in the range [4, 32)"
+    );
 
     //number of msg blocks
-    let num_blocks = f64::ceil(msg_len as f64 / 64_f64) as u32;
+    let num_blocks = (msg_len + 63) / 64;
path: bitvm/src/hash/blake3.rs
user: login: AaronFeickert
body: We're standardizing on this in #264 as well.
created_at: 2025-03-15T08:29:17Z
author_association: CONTRIBUTOR
diff_hunk: @@ -229,34 +248,60 @@ pub fn blake3_push_message_script(message_bytes: &[u8]) -> Script {
                 }
                 if i == 31 || i == 63 {
                     {
-                        U256::transform_limbsize(8, 29)
+                        U256::transform_limbsize(8, limb_len as u32)
                     }
                 }
             }
         }
     }
 }
 
+/// Number of elements in total of all tables
+const SUM_OF_FULL_TABLES: usize = 384;
+
+/// Number of elements of an unpacked block
+const UNPACKED_BLOCK: usize = 128;
+
+/// Maximum number of elements in stack during the execution of BLAKE3 algorithm
+const MAX_BLAKE3_ELEMENT_COUNT: usize =
+    SUM_OF_FULL_TABLES + UNPACKED_BLOCK + /* Extra BLAKE3 variables */ 132;
+
+/// Calculates the maximum number of altstack elements one can have using the [`blake3_compute_script`] function with the following formula:
+/// ```text
+///  n (number of blocks) = ⌈msg_len / 64⌉
+///  limb_count (number of limbs in a block) = ⌈256 / limb_len⌉ * 2
+///  m (message's consumption of stack during BLAKE3) = (n - 1) * limb_count
+///  Since BLAKE3 requires an empty stack and we've calculated the usage for the message and the algorithm:
+///  MAX_NUMBEROF_ALTSTACK_ELEMENTS = 1000 (max stack limit) - m - 644 (Maximum number of elements used during BLAKE3)
+/// ```
+pub fn maximum_number_of_altstack_elements_using_blake3(message_len: usize, limb_len: u8) -> i32 {
+    let n = message_len.div_ceil(64);
+    let limb_count = 256usize.div_ceil(limb_len as usize) * 2;
+    let m = (n - 1) * limb_count;
+    let max_altstack_elements = 1000i32 - MAX_BLAKE3_ELEMENT_COUNT as i32 - m as i32;
+    max_altstack_elements
+}
+
 /// Returns a script that computes the BLAKE3 hash of the message on the stack.
 ///
 /// The script processes compact message blocks and only unpacks them when needed,
 /// resulting in higher stack efficiency and support for larger messages.
 ///
-/// The message length is the number of message bytes without padding.
-///
-/// ## Empty Stack Requirement
-///
-/// The stack MUST contain only the message.
-/// Anything else MUST be moved to the alt stack.
-/// When hashing the empty message (length 0), the stack MUST be empty.
-///
-/// ## Message Format Requirement
+/// ## Parameters:
+/// - `msg_len`: Length of the message. (excluding the padding, number of bytes)
+/// - `define_var`: Set to false if the input on stack is already defined as `StackTracker` varibles.
+/// - `use_full_tables`: toggle if you want to use full precomputation table or only half tables. Full table is script efficient but uses more stack.
+/// - `limb_len`: Limb length (number of bits per element) that the input in the stack is packed, for example it is 29 for current field elements
path: bitvm/src/hash/blake3.rs
user: login: uncomputable
body: Why do you change the format of the doc string so much?

For example, why do you move the `## Panics` section? This needlessly increases the diff.
created_at: 2025-03-15T11:09:24Z
author_association: CONTRIBUTOR
diff_hunk: @@ -229,34 +248,60 @@ pub fn blake3_push_message_script(message_bytes: &[u8]) -> Script {
                 }
                 if i == 31 || i == 63 {
                     {
-                        U256::transform_limbsize(8, 29)
+                        U256::transform_limbsize(8, limb_len as u32)
                     }
                 }
             }
         }
     }
 }
 
+/// Number of elements in total of all tables
+const SUM_OF_FULL_TABLES: usize = 384;
+
+/// Number of elements of an unpacked block
+const UNPACKED_BLOCK: usize = 128;
+
+/// Maximum number of elements in stack during the execution of BLAKE3 algorithm
+const MAX_BLAKE3_ELEMENT_COUNT: usize =
+    SUM_OF_FULL_TABLES + UNPACKED_BLOCK + /* Extra BLAKE3 variables */ 132;
+
+/// Calculates the maximum number of altstack elements one can have using the [`blake3_compute_script`] function with the following formula:
+/// ```text
+///  n (number of blocks) = ⌈msg_len / 64⌉
+///  limb_count (number of limbs in a block) = ⌈256 / limb_len⌉ * 2
+///  m (message's consumption of stack during BLAKE3) = (n - 1) * limb_count
+///  Since BLAKE3 requires an empty stack and we've calculated the usage for the message and the algorithm:
+///  MAX_NUMBEROF_ALTSTACK_ELEMENTS = 1000 (max stack limit) - m - 644 (Maximum number of elements used during BLAKE3)
+/// ```
+pub fn maximum_number_of_altstack_elements_using_blake3(message_len: usize, limb_len: u8) -> i32 {
+    let n = message_len.div_ceil(64);
+    let limb_count = 256usize.div_ceil(limb_len as usize) * 2;
+    let m = (n - 1) * limb_count;
+    let max_altstack_elements = 1000i32 - MAX_BLAKE3_ELEMENT_COUNT as i32 - m as i32;
+    max_altstack_elements
+}
+
 /// Returns a script that computes the BLAKE3 hash of the message on the stack.
 ///
 /// The script processes compact message blocks and only unpacks them when needed,
 /// resulting in higher stack efficiency and support for larger messages.
 ///
-/// The message length is the number of message bytes without padding.
-///
-/// ## Empty Stack Requirement
-///
-/// The stack MUST contain only the message.
-/// Anything else MUST be moved to the alt stack.
-/// When hashing the empty message (length 0), the stack MUST be empty.
-///
-/// ## Message Format Requirement
+/// ## Parameters:
+/// - `msg_len`: Length of the message. (excluding the padding, number of bytes)
+/// - `define_var`: Set to false if the input on stack is already defined as `StackTracker` varibles.
+/// - `use_full_tables`: toggle if you want to use full precomputation table or only half tables. Full table is script efficient but uses more stack.
+/// - `limb_len`: Limb length (number of bits per element) that the input in the stack is packed, for example it is 29 for current field elements
path: bitvm/src/hash/blake3.rs
user: login: uncomputable
body: I would keep the `blake3_compute_script` function exactly the same and add a new function `blake3_compute_script_with_limbs` that is more general. I would explain the differences in the docstring for `blake3_compute_script_with_limbs`.
created_at: 2025-03-15T11:12:13Z
author_association: CONTRIBUTOR
diff_hunk: @@ -229,34 +248,60 @@ pub fn blake3_push_message_script(message_bytes: &[u8]) -> Script {
                 }
                 if i == 31 || i == 63 {
                     {
-                        U256::transform_limbsize(8, 29)
+                        U256::transform_limbsize(8, limb_len as u32)
                     }
                 }
             }
         }
     }
 }
 
+/// Number of elements in total of all tables
+const SUM_OF_FULL_TABLES: usize = 384;
+
+/// Number of elements of an unpacked block
+const UNPACKED_BLOCK: usize = 128;
+
+/// Maximum number of elements in stack during the execution of BLAKE3 algorithm
+const MAX_BLAKE3_ELEMENT_COUNT: usize =
+    SUM_OF_FULL_TABLES + UNPACKED_BLOCK + /* Extra BLAKE3 variables */ 132;
+
+/// Calculates the maximum number of altstack elements one can have using the [`blake3_compute_script`] function with the following formula:
+/// ```text
+///  n (number of blocks) = ⌈msg_len / 64⌉
+///  limb_count (number of limbs in a block) = ⌈256 / limb_len⌉ * 2
+///  m (message's consumption of stack during BLAKE3) = (n - 1) * limb_count
+///  Since BLAKE3 requires an empty stack and we've calculated the usage for the message and the algorithm:
+///  MAX_NUMBEROF_ALTSTACK_ELEMENTS = 1000 (max stack limit) - m - 644 (Maximum number of elements used during BLAKE3)
+/// ```
+pub fn maximum_number_of_altstack_elements_using_blake3(message_len: usize, limb_len: u8) -> i32 {
+    let n = message_len.div_ceil(64);
+    let limb_count = 256usize.div_ceil(limb_len as usize) * 2;
+    let m = (n - 1) * limb_count;
+    let max_altstack_elements = 1000i32 - MAX_BLAKE3_ELEMENT_COUNT as i32 - m as i32;
+    max_altstack_elements
+}
+
 /// Returns a script that computes the BLAKE3 hash of the message on the stack.
 ///
 /// The script processes compact message blocks and only unpacks them when needed,
 /// resulting in higher stack efficiency and support for larger messages.
 ///
-/// The message length is the number of message bytes without padding.
-///
-/// ## Empty Stack Requirement
-///
-/// The stack MUST contain only the message.
-/// Anything else MUST be moved to the alt stack.
-/// When hashing the empty message (length 0), the stack MUST be empty.
-///
-/// ## Message Format Requirement
+/// ## Parameters:
+/// - `msg_len`: Length of the message. (excluding the padding, number of bytes)
+/// - `define_var`: Set to false if the input on stack is already defined as `StackTracker` varibles.
+/// - `use_full_tables`: toggle if you want to use full precomputation table or only half tables. Full table is script efficient but uses more stack.
+/// - `limb_len`: Limb length (number of bits per element) that the input in the stack is packed, for example it is 29 for current field elements
path: bitvm/src/hash/blake3.rs
user: login: just-erray
body: I agree that moving panics to the top of the behavior section is better—I unconsciously made that change since I started from an old commit. However, I didn't make any other changes besides additions and topic renamings to the docstring, so the format remains nearly the same. I don't think creating two separate docstrings makes much sense. The only differences are one line in the message format and two added panics. One should be able to get the points just as easily from this version.
created_at: 2025-03-15T11:23:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -229,34 +248,60 @@ pub fn blake3_push_message_script(message_bytes: &[u8]) -> Script {
                 }
                 if i == 31 || i == 63 {
                     {
-                        U256::transform_limbsize(8, 29)
+                        U256::transform_limbsize(8, limb_len as u32)
                     }
                 }
             }
         }
     }
 }
 
+/// Number of elements in total of all tables
+const SUM_OF_FULL_TABLES: usize = 384;
+
+/// Number of elements of an unpacked block
+const UNPACKED_BLOCK: usize = 128;
+
+/// Maximum number of elements in stack during the execution of BLAKE3 algorithm
+const MAX_BLAKE3_ELEMENT_COUNT: usize =
+    SUM_OF_FULL_TABLES + UNPACKED_BLOCK + /* Extra BLAKE3 variables */ 132;
+
+/// Calculates the maximum number of altstack elements one can have using the [`blake3_compute_script`] function with the following formula:
+/// ```text
+///  n (number of blocks) = ⌈msg_len / 64⌉
+///  limb_count (number of limbs in a block) = ⌈256 / limb_len⌉ * 2
+///  m (message's consumption of stack during BLAKE3) = (n - 1) * limb_count
+///  Since BLAKE3 requires an empty stack and we've calculated the usage for the message and the algorithm:
+///  MAX_NUMBEROF_ALTSTACK_ELEMENTS = 1000 (max stack limit) - m - 644 (Maximum number of elements used during BLAKE3)
+/// ```
+pub fn maximum_number_of_altstack_elements_using_blake3(message_len: usize, limb_len: u8) -> i32 {
+    let n = message_len.div_ceil(64);
+    let limb_count = 256usize.div_ceil(limb_len as usize) * 2;
+    let m = (n - 1) * limb_count;
+    let max_altstack_elements = 1000i32 - MAX_BLAKE3_ELEMENT_COUNT as i32 - m as i32;
+    max_altstack_elements
+}
+
 /// Returns a script that computes the BLAKE3 hash of the message on the stack.
 ///
 /// The script processes compact message blocks and only unpacks them when needed,
 /// resulting in higher stack efficiency and support for larger messages.
 ///
-/// The message length is the number of message bytes without padding.
-///
-/// ## Empty Stack Requirement
-///
-/// The stack MUST contain only the message.
-/// Anything else MUST be moved to the alt stack.
-/// When hashing the empty message (length 0), the stack MUST be empty.
-///
-/// ## Message Format Requirement
+/// ## Parameters:
+/// - `msg_len`: Length of the message. (excluding the padding, number of bytes)
+/// - `define_var`: Set to false if the input on stack is already defined as `StackTracker` varibles.
+/// - `use_full_tables`: toggle if you want to use full precomputation table or only half tables. Full table is script efficient but uses more stack.
+/// - `limb_len`: Limb length (number of bits per element) that the input in the stack is packed, for example it is 29 for current field elements
path: bitvm/src/hash/blake3.rs
user: login: just-erray
body: Moved panics to the top and reformatted docstring so it is more readable
created_at: 2025-03-15T11:40:52Z
author_association: CONTRIBUTOR
diff_hunk: @@ -229,34 +248,62 @@ pub fn blake3_push_message_script(message_bytes: &[u8]) -> Script {
                 }
                 if i == 31 || i == 63 {
                     {
-                        U256::transform_limbsize(8, 29)
+                        U256::transform_limbsize(8, limb_len as u32)
                     }
                 }
             }
         }
     }
 }
 
+/// Number of elements in total of all tables
+const SUM_OF_FULL_TABLES: usize = 384;
+
+/// Number of elements of an unpacked block
+const UNPACKED_BLOCK: usize = 128;
+
+/// Maximum number of elements in stack during the execution of BLAKE3 algorithm
+const MAX_BLAKE3_ELEMENT_COUNT: usize =
+    SUM_OF_FULL_TABLES + UNPACKED_BLOCK + /* Extra BLAKE3 variables */ 132;
+
+/// Calculates the maximum number of altstack elements one can have using the [`blake3_compute_script`] function with the following formula:
+/// ```text
+///  n (number of blocks) = ⌈msg_len / 64⌉
+///  limb_count (number of limbs in a block) = ⌈256 / limb_len⌉ * 2
+///  m (message's consumption of stack during BLAKE3) = (n - 1) * limb_count
+///  Since BLAKE3 requires an empty stack and we've calculated the usage for the message and the algorithm:
+///  MAX_NUMBEROF_ALTSTACK_ELEMENTS = 1000 (max stack limit) - m - 644 (Maximum number of elements used during BLAKE3)
+/// ```
+pub fn maximum_number_of_altstack_elements_using_blake3(message_len: usize, limb_len: u8) -> i32 {
+    let n = message_len.div_ceil(64);
+    let limb_count = 256usize.div_ceil(limb_len as usize) * 2;
+    let m = (n - 1) * limb_count;
+    let max_altstack_elements = 1000i32 - MAX_BLAKE3_ELEMENT_COUNT as i32 - m as i32;
+    max_altstack_elements
+}
+
 /// Returns a script that computes the BLAKE3 hash of the message on the stack.
 ///
 /// The script processes compact message blocks and only unpacks them when needed,
 /// resulting in higher stack efficiency and support for larger messages.
 ///
-/// The message length is the number of message bytes without padding.
+/// ## Parameters:
 ///
-/// ## Empty Stack Requirement
+/// - `msg_len`: Length of the message. (excluding the padding, number of bytes)
+/// - `define_var`: Set to false if the input on stack is already defined as `StackTracker` varibles.
+/// - `use_full_tables`: toggle if you want to use full precomputation table or only half tables. Full table is script efficient but uses more stack.
+/// - `limb_len`: Limb length (number of bits per element) that the input in the stack is packed, for example it is 29 for current field elements
path: bitvm/src/hash/blake3.rs
user: login: uncomputable
body: `define_var` and `use_full_tables` are no longer parameters of this function.
created_at: 2025-03-15T12:16:47Z
author_association: CONTRIBUTOR
diff_hunk: @@ -229,34 +248,62 @@ pub fn blake3_push_message_script(message_bytes: &[u8]) -> Script {
                 }
                 if i == 31 || i == 63 {
                     {
-                        U256::transform_limbsize(8, 29)
+                        U256::transform_limbsize(8, limb_len as u32)
                     }
                 }
             }
         }
     }
 }
 
+/// Number of elements in total of all tables
+const SUM_OF_FULL_TABLES: usize = 384;
+
+/// Number of elements of an unpacked block
+const UNPACKED_BLOCK: usize = 128;
+
+/// Maximum number of elements in stack during the execution of BLAKE3 algorithm
+const MAX_BLAKE3_ELEMENT_COUNT: usize =
+    SUM_OF_FULL_TABLES + UNPACKED_BLOCK + /* Extra BLAKE3 variables */ 132;
+
+/// Calculates the maximum number of altstack elements one can have using the [`blake3_compute_script`] function with the following formula:
+/// ```text
+///  n (number of blocks) = ⌈msg_len / 64⌉
+///  limb_count (number of limbs in a block) = ⌈256 / limb_len⌉ * 2
+///  m (message's consumption of stack during BLAKE3) = (n - 1) * limb_count
+///  Since BLAKE3 requires an empty stack and we've calculated the usage for the message and the algorithm:
+///  MAX_NUMBEROF_ALTSTACK_ELEMENTS = 1000 (max stack limit) - m - 644 (Maximum number of elements used during BLAKE3)
+/// ```
+pub fn maximum_number_of_altstack_elements_using_blake3(message_len: usize, limb_len: u8) -> i32 {
+    let n = message_len.div_ceil(64);
+    let limb_count = 256usize.div_ceil(limb_len as usize) * 2;
+    let m = (n - 1) * limb_count;
+    let max_altstack_elements = 1000i32 - MAX_BLAKE3_ELEMENT_COUNT as i32 - m as i32;
+    max_altstack_elements
+}
+
 /// Returns a script that computes the BLAKE3 hash of the message on the stack.
 ///
 /// The script processes compact message blocks and only unpacks them when needed,
 /// resulting in higher stack efficiency and support for larger messages.
 ///
-/// The message length is the number of message bytes without padding.
+/// ## Parameters:
 ///
-/// ## Empty Stack Requirement
+/// - `msg_len`: Length of the message. (excluding the padding, number of bytes)
+/// - `define_var`: Set to false if the input on stack is already defined as `StackTracker` varibles.
+/// - `use_full_tables`: toggle if you want to use full precomputation table or only half tables. Full table is script efficient but uses more stack.
+/// - `limb_len`: Limb length (number of bits per element) that the input in the stack is packed, for example it is 29 for current field elements
path: bitvm/src/hash/blake3.rs
user: login: just-erray
body: 🤦, fixing it
created_at: 2025-03-15T12:20:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -229,34 +248,60 @@ pub fn blake3_push_message_script(message_bytes: &[u8]) -> Script {
                 }
                 if i == 31 || i == 63 {
                     {
-                        U256::transform_limbsize(8, 29)
+                        U256::transform_limbsize(8, limb_len as u32)
                     }
                 }
             }
         }
     }
 }
 
+/// Number of elements in total of all tables
+const SUM_OF_FULL_TABLES: usize = 384;
+
+/// Number of elements of an unpacked block
+const UNPACKED_BLOCK: usize = 128;
+
+/// Maximum number of elements in stack during the execution of BLAKE3 algorithm
+const MAX_BLAKE3_ELEMENT_COUNT: usize =
+    SUM_OF_FULL_TABLES + UNPACKED_BLOCK + /* Extra BLAKE3 variables */ 132;
+
+/// Calculates the maximum number of altstack elements one can have using the [`blake3_compute_script`] function with the following formula:
+/// ```text
+///  n (number of blocks) = ⌈msg_len / 64⌉
+///  limb_count (number of limbs in a block) = ⌈256 / limb_len⌉ * 2
+///  m (message's consumption of stack during BLAKE3) = (n - 1) * limb_count
+///  Since BLAKE3 requires an empty stack and we've calculated the usage for the message and the algorithm:
+///  MAX_NUMBEROF_ALTSTACK_ELEMENTS = 1000 (max stack limit) - m - 644 (Maximum number of elements used during BLAKE3)
+/// ```
+pub fn maximum_number_of_altstack_elements_using_blake3(message_len: usize, limb_len: u8) -> i32 {
+    let n = message_len.div_ceil(64);
+    let limb_count = 256usize.div_ceil(limb_len as usize) * 2;
+    let m = (n - 1) * limb_count;
+    let max_altstack_elements = 1000i32 - MAX_BLAKE3_ELEMENT_COUNT as i32 - m as i32;
+    max_altstack_elements
+}
+
 /// Returns a script that computes the BLAKE3 hash of the message on the stack.
 ///
 /// The script processes compact message blocks and only unpacks them when needed,
 /// resulting in higher stack efficiency and support for larger messages.
 ///
-/// The message length is the number of message bytes without padding.
-///
-/// ## Empty Stack Requirement
-///
-/// The stack MUST contain only the message.
-/// Anything else MUST be moved to the alt stack.
-/// When hashing the empty message (length 0), the stack MUST be empty.
-///
-/// ## Message Format Requirement
+/// ## Parameters:
+/// - `msg_len`: Length of the message. (excluding the padding, number of bytes)
+/// - `define_var`: Set to false if the input on stack is already defined as `StackTracker` varibles.
+/// - `use_full_tables`: toggle if you want to use full precomputation table or only half tables. Full table is script efficient but uses more stack.
+/// - `limb_len`: Limb length (number of bits per element) that the input in the stack is packed, for example it is 29 for current field elements
path: bitvm/src/hash/blake3.rs
user: login: uncomputable
body: I think it would be easier to audit the blake function that is used in bitvm if we keep the old docstring.

The new docstring is of course more general, but we don't use that functionality inside bitvm (yet). That is why I would create a second docstring that explains the differences.
created_at: 2025-03-15T12:23:34Z
author_association: CONTRIBUTOR
diff_hunk: @@ -4,7 +4,6 @@ use crate::{
     signatures::wots_api,
path: bitvm/src/chunk/wrap_hasher.rs
user: login: uncomputable
body: Please run `clippy --all-targets` and fix the warnings in `blake3.rs`. #259 should fix the warnings in all other files.
created_at: 2025-03-15T14:20:35Z
author_association: CONTRIBUTOR

PR comments:
user: login: uncomputable
created_at: 2025-03-14T23:12:26Z
body: Thanks for waiting for #257 :) We should be able to merge that PR soon.
author_association: CONTRIBUTOR
user: login: AaronFeickert
created_at: 2025-03-15T00:37:28Z
body: Please rebase now that #257 is merged.
author_association: CONTRIBUTOR
user: login: just-erray
created_at: 2025-03-15T06:24:25Z
body: Since the two PRs were too conflicting, I reset this branch and reapplied the changes on top of it. I also cleaned up `lib.rs` a bit while I got my hands dirty, but nothing major. I left fuzz test only running with limb length 29, since other tests should be more than enough for the others. It should be ready to merge now.
author_association: CONTRIBUTOR
user: login: just-erray
created_at: 2025-03-15T11:28:13Z
body: > Since we don't use limb lengths other than 29 and we have an upcoming audit, can we keep the function signatures of the blake3 functions the same and introduce private functions with the more general limb parameter?

Renaming it to the first version seems sensible without making it private, since the point is them being usable from outside. 

> Also, can you introduce a separate function to unit test different limb lengths? Iterating over all possible limb lengths just makes CI run for much longer. Especially, please use the old method for test_single_byte and test_official_test_vectors. Those tests run 10 seconds in the old version, and they will run for minutes your generalized version.

It should only take around 3-4 times as much currently, which only takes 15 seconds in my environment. Extensive tests will be rarely necessary and aren't activated for CI. (Edit: Nevertheless, I'm removing one of the limb lengths from non-extensive tests, that should make it a little bit faster)
author_association: CONTRIBUTOR
user: login: uncomputable
created_at: 2025-03-15T12:19:02Z
body: > Extensive tests will be rarely necessary and aren't activated for CI

The point of unit tests is to run them, no?

If a test takes too long, we should mark is as `#[ignored]`. For example, we could adapt the test vectors and single byte units to iterate over all limb sizes and mark these tests as `#[ignored]`. We can still run these tests via `cargo test --ignored` but they don't slow down the remaining CI.
author_association: CONTRIBUTOR
user: login: just-erray
created_at: 2025-03-15T12:22:22Z
body: >If a test takes too long, we should mark is as #[ignored]. For example, we could adapt the test vectors and single byte units to iterate over all limb sizes and mark these tests as #[ignored]. We can still run these tests via cargo test --ignored but they don't slow down the remaining CI.

This solution seems better, changing it
author_association: CONTRIBUTOR
user: login: just-erray
created_at: 2025-03-15T12:47:20Z
body: > This solution seems better, so I am changing it.

I couldn't find a better solution than duplicating each function with wrappers in this case, and this seems like an unsustainable and messy approach. If there's a better way to handle this, adding two variants of the tests with `#[ignore]` would definitely be the better option. If not, I think leaving it as a variable and allowing developers to enable it when something related to limb length changes would be the better approach. In both cases, I don't think there will be any significant differences between them, since developers will have control over running tests while editing the code.
author_association: CONTRIBUTOR
user: login: uncomputable
created_at: 2025-03-15T13:36:15Z
body: I will try to come up with a solution and post a gist.
author_association: CONTRIBUTOR
user: login: uncomputable
created_at: 2025-03-15T14:07:59Z
body: Feel free to take a look at this: https://gist.github.com/uncomputable/021d98fac2bae02ace0b9005f4ba3484

Run the ignored tests with `cargo test -- --ignored`.
author_association: CONTRIBUTOR
user: login: just-erray
created_at: 2025-03-15T14:14:53Z
body: This looks nice. I think editing it slightly so that normal tests run with useful limbs (since it adds at most 10 seconds to the total test time) and ignored tests run with all limb lengths should make it good to go.
author_association: CONTRIBUTOR
user: login: uncomputable
created_at: 2025-03-15T14:17:31Z
body: Feel free to arrange the code as you want. I mainly wanted to show that it's possible to ignore the expensive tests without duplicating code.
author_association: CONTRIBUTOR