Repository: LNP-BP/client_side_validation (language: rust)

Issue:
Large MPC trees sometimes overflow during merklization
This happens only from time-to-time, in a non-deterministic manner. Example:

https://github.com/LNP-BP/client_side_validation/actions/runs/8253738768/job/22576299366#step:4:226


Discussion / hints:

Fix patch:
diff --git a/commit_verify/src/id.rs b/commit_verify/src/id.rs
index dd2f3c91..d41ad062 100644
--- a/commit_verify/src/id.rs
+++ b/commit_verify/src/id.rs
@@ -131,9 +131,9 @@ impl CommitEngine {
         self.inner_commit_to::<_, 32>(&root);
     }
 
-    pub fn commit_to_concealed<T: Conceal>(&mut self, value: &T)
+    pub fn commit_to_concealed<T>(&mut self, value: &T)
     where
-        T: StrictType,
+        T: Conceal + StrictType,
         T::Concealed: StrictEncode,
     {
         let fqn = commitment_fqn::<T>();
diff --git a/commit_verify/src/merkle.rs b/commit_verify/src/merkle.rs
index 90e87d54..ed8e1933 100644
--- a/commit_verify/src/merkle.rs
+++ b/commit_verify/src/merkle.rs
@@ -155,7 +155,7 @@ impl MerkleHash {
     pub fn merklize(leaves: &impl MerkleLeaves) -> Self {
         let mut nodes = leaves.merkle_leaves().map(|leaf| leaf.commit_id());
         let base_width =
-            u32::try_from(nodes.len()).expect("too many merkle leaves (more than 2^32)");
+            u32::try_from(nodes.len()).expect("too many merkle leaves (more than 2^31)");
         if base_width == 1 {
             // If we have just one leaf, it's MerkleNode value is the root
             nodes.next().expect("length is 1")
@@ -192,8 +192,8 @@ impl MerkleHash {
                 // TODO: Do this without allocation
                 .collect::<Vec<_>>()
                 .into_iter();
-            let branch1 = Self::_merklize(slice, depth + 1, base_width, div);
-            let branch2 = Self::_merklize(iter, depth + 1, base_width, branch_width - div);
+            let branch1 = Self::_merklize(slice, depth + 1, div, base_width);
+            let branch2 = Self::_merklize(iter, depth + 1, branch_width - div, base_width);
 
             MerkleHash::branches(depth, base_width, branch1, branch2)
         }
diff --git a/commit_verify/src/mpc/tree.rs b/commit_verify/src/mpc/tree.rs
index a0984c1f..611c1dd7 100644
--- a/commit_verify/src/mpc/tree.rs
+++ b/commit_verify/src/mpc/tree.rs
@@ -69,6 +69,7 @@ impl MerkleTree {
                 .unwrap_or_else(|| Leaf::entropy(self.entropy, pos))
         });
         let leaves = LargeVec::try_from_iter(iter).expect("tree width has u32-bound size");
+        debug_assert_eq!(leaves.len_u32(), self.width());
         MerkleHash::merklize(&leaves)
     }
 }
@@ -298,10 +299,11 @@ mod test {
         let mut counter = StreamWriter::counter::<{ usize::MAX }>();
         tree.strict_write(&mut counter).unwrap();
         eprintln!(
-            "Tree with {count} protocol-messages: depth {}, cofactor {}. Serialized length {} \
-             bytes. Takes {} msecs to generate",
+            "Tree with {count} protocol-messages: depth {}, cofactor {}, width {}.\nSerialized \
+             length {} bytes.\nTakes {} msecs to generate",
             tree.depth,
             tree.cofactor,
+            tree.width(),
             counter.unconfine().count,
             elapsed_gen.as_millis(),
         );
diff --git a/src/api.rs b/src/api.rs
index 002dfb2f..0b7bd54a 100644
--- a/src/api.rs
+++ b/src/api.rs
@@ -392,18 +392,17 @@ pub trait SealResolver<Seal> {
 
 #[cfg(test)]
 mod test {
-    //! Tests use emulation of a simple client-side-validated state, consisting
-    //! of an array of data items, each of which has a name bound to a certain
-    //! bitcoin single-use-seal.
+    // Tests use emulation of a simple client-side-validated state, consisting
+    // of an array of data items, each of which has a name bound to a certain
+    // bitcoin single-use-seal.
 
     use single_use_seals::{SealProtocol, SealStatus, SealWitness};
 
     use super::*;
 
     #[test]
+    #[allow(dead_code)]
     fn test() {
-        #![allow(dead_code)]
-
         #[derive(Clone, PartialEq, Eq, Hash, Debug, Default)]
         #[derive(Serialize, Deserialize)]
         #[serde(crate = "serde_crate")]


Test patch: