ant_evm/merkle_payments/
merkle_tree.rs

1// Copyright 2025 MaidSafe.net limited.
2//
3// This SAFE Network Software is licensed to you under The General Public License (GPL), version 3.
4// Unless required by applicable law or agreed to in writing, the SAFE Network Software distributed
5// under the GPL Licence is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
6// KIND, either express or implied. Please review the Licences for the specific language governing
7// permissions and limitations relating to use of the SAFE Network Software.
8
9use ant_merkle::Hasher;
10use serde::{Deserialize, Serialize};
11use std::time::{SystemTime, UNIX_EPOCH};
12use thiserror::Error;
13use xor_name::XorName;
14
15/// Maximum tree depth
16pub use evmlib::merkle_batch_payment::MAX_MERKLE_DEPTH;
17
18/// Minimum number of leaves (addresses) for a Merkle tree
19pub const MIN_LEAVES: usize = 2;
20
21/// Maximum number of leaves (2^MAX_MERKLE_DEPTH)
22pub const MAX_LEAVES: usize = 1 << MAX_MERKLE_DEPTH;
23
24/// Maximum age of a Merkle payment (one week in seconds)
25/// Payments older than this are considered expired and nodes will reject addresses
26pub const MERKLE_PAYMENT_EXPIRATION: u64 = 7 * 24 * 60 * 60; // 7 days
27
28/// Calculate the expected number of reward candidate pools for a given tree depth
29///
30/// This is used throughout the payment system to determine how many candidate pools
31/// should exist for a Merkle tree of a given depth.
32///
33/// # Formula
34/// Number of pools = 2^ceil(depth/2)
35///
36/// # Examples
37/// - depth 4 → ceil(4/2) = 2 → 2^2 = 4 pools
38/// - depth 7 → ceil(7/2) = 4 → 2^4 = 16 pools
39/// - depth 8 → ceil(8/2) = 4 → 2^4 = 16 pools
40///
41/// # Arguments
42/// * `depth` - The depth of the Merkle tree
43///
44/// # Returns
45/// The expected number of reward candidate pools
46pub fn expected_reward_pools(depth: u8) -> usize {
47    1 << midpoint_proof_depth(depth)
48}
49
50/// Errors that can occur when working with Merkle trees
51#[derive(Debug, Error)]
52pub enum MerkleTreeError {
53    #[error("Too few leaves: got {got}, minimum is {MIN_LEAVES}")]
54    TooFewLeaves { got: usize },
55    #[error("Too many leaves: got {got}, maximum is {MAX_LEAVES}")]
56    TooManyLeaves { got: usize },
57    #[error("Invalid leaf index: {index} (tree has {leaf_count} leaves)")]
58    InvalidLeafIndex { index: usize, leaf_count: usize },
59    #[error("Invalid midpoint index: {index} (tree has {midpoint_count} midpoints)")]
60    InvalidMidpointIndex { index: usize, midpoint_count: usize },
61    #[error("Invalid proof")]
62    InvalidProof,
63    #[error("Internal error: {0}")]
64    Internal(String),
65}
66
67pub type Result<T> = std::result::Result<T, MerkleTreeError>;
68
69/// A Merkle tree built from XorNames (content addresses)
70///
71/// Used for batch payment system where:
72/// - Build tree from XorNames (min: 2, max: 65,536)
73/// - Tree is automatically padded to next power of 2
74/// - Root is committed on-chain for payment
75/// - Intersections at level depth/2 determine candidate pools
76/// - Individual address proofs verify addresses belong to paid batch
77pub struct MerkleTree {
78    /// The underlying rs_merkle tree
79    inner: ant_merkle::MerkleTree<XorNameHasher>,
80
81    /// Original leaf count (before padding)
82    leaf_count: usize,
83
84    /// Tree depth
85    depth: u8,
86
87    /// The root hash of the tree
88    root: XorName,
89
90    /// Salts for each original leaf
91    /// Used to compute salted hashes: hash(address || salt)
92    salts: Vec<[u8; 32]>,
93}
94
95impl MerkleTree {
96    /// Create a new Merkle tree from XorNames
97    ///
98    /// # Arguments
99    ///
100    /// * `leaves` - Vector of XorNames (address addresses). Min: 2, Max: 65,536
101    ///
102    /// # Returns
103    ///
104    /// A new MerkleTree with automatic padding to next power of 2
105    ///
106    /// # Errors
107    ///
108    /// - `TooFewLeaves` if less than 2 leaves
109    /// - `TooManyLeaves` if more than 65,536 leaves
110    ///
111    /// # Example
112    ///
113    /// ```ignore
114    /// let addresses: Vec<XorName> = vec![
115    ///     XorName::from_content(b"address1"),
116    ///     XorName::from_content(b"address2"),
117    ///     XorName::from_content(b"address3"),
118    /// ];
119    ///
120    /// let tree = MerkleTree::from_xornames(addresses)?;
121    /// println!("Root: {:?}", tree.root());
122    /// println!("Depth: {}", tree.depth());
123    /// ```
124    pub fn from_xornames(leaves: Vec<XorName>) -> Result<Self> {
125        let leaf_count = leaves.len();
126
127        // Validate leaf count
128        if leaf_count < MIN_LEAVES {
129            return Err(MerkleTreeError::TooFewLeaves { got: leaf_count });
130        }
131        if leaf_count > MAX_LEAVES {
132            return Err(MerkleTreeError::TooManyLeaves { got: leaf_count });
133        }
134
135        // Generate random salt for each real leaf (privacy protection)
136        let mut rng = rand::thread_rng();
137        let salts: Vec<[u8; 32]> = (0..leaf_count)
138            .map(|_| {
139                let mut salt = [0u8; 32];
140                rand::Rng::fill(&mut rng, &mut salt);
141                salt
142            })
143            .collect();
144
145        // Calculate depth and pad to next power of 2
146        let depth = tree_depth(leaf_count);
147        let padded_size = 1 << depth;
148
149        // Apply salt to each real leaf: hash(address || salt)
150        let mut salted_leaves: Vec<[u8; 32]> = leaves
151            .iter()
152            .zip(&salts)
153            .map(|(address, salt)| {
154                // Compute hash(address || salt)
155                let mut data = Vec::with_capacity(64);
156                data.extend_from_slice(address.as_ref());
157                data.extend_from_slice(salt);
158                XorNameHasher::hash(&data)
159            })
160            .collect();
161
162        // Add random dummy padding leaves (no salt needed - already random)
163        if leaf_count < padded_size {
164            for _ in leaf_count..padded_size {
165                let mut dummy = [0u8; 32];
166                rand::Rng::fill(&mut rng, &mut dummy);
167                salted_leaves.push(dummy);
168            }
169        }
170
171        // Build rs_merkle tree from salted hashes
172        let inner = ant_merkle::MerkleTree::<XorNameHasher>::from_leaves(&salted_leaves);
173
174        let root = inner.root().ok_or(MerkleTreeError::Internal(
175            "Tree must have root after construction".to_string(),
176        ))?;
177
178        Ok(Self {
179            inner,
180            root: XorName(root),
181            leaf_count,
182            depth,
183            salts,
184        })
185    }
186
187    /// Get the root hash of the tree
188    ///
189    /// This is the hash committed on-chain for batch payment
190    pub fn root(&self) -> XorName {
191        self.root
192    }
193
194    /// Get the depth of the tree
195    pub fn depth(&self) -> u8 {
196        self.depth
197    }
198
199    /// Get the original leaf count (before padding)
200    pub fn leaf_count(&self) -> usize {
201        self.leaf_count
202    }
203
204    /// Get midpoint nodes at depth/2
205    ///
206    /// These are the nodes used internally to determine candidate pools for payment routing.
207    /// Returns intersections nodes at level depth/2
208    ///
209    /// Note: Users typically don't need this directly - use `reward_candidates()` instead.
210    fn midpoints(&self) -> Result<Vec<MerkleMidpoint>> {
211        let level = midpoint_level(self.depth);
212
213        let nodes = self
214            .inner
215            .get_nodes_at_level(level)
216            .ok_or(MerkleTreeError::Internal(
217                "Midpoint level must exist".to_string(),
218            ))?;
219
220        let midpoints: Vec<MerkleMidpoint> = nodes
221            .into_iter()
222            .map(|(index, hash)| MerkleMidpoint {
223                hash: XorName(hash),
224                index,
225            })
226            .collect();
227
228        Ok(midpoints)
229    }
230
231    /// Get reward candidates for batch payment
232    ///
233    /// Computes candidate addresses as hash(midpoint_hash || root || merkle_payment_timestamp).
234    /// Network nodes closest to these addresses are eligible for payment rewards.
235    /// Each candidate contains a proof that the midpoint belongs to the tree.
236    /// Returns 2^ceil(depth/2) reward candidates.
237    ///
238    /// # Arguments
239    ///
240    /// * `merkle_payment_timestamp` - Unix timestamp of the transaction (seconds since epoch)
241    ///
242    /// # Returns
243    ///
244    /// A vector of `RewardCandidatePool` or an error if proof generation fails
245    ///
246    /// # Example
247    ///
248    /// ```ignore
249    /// let tree = MerkleTree::from_xornames(addresses)?;
250    /// let merkle_payment_timestamp = SystemTime::now()
251    ///     .duration_since(UNIX_EPOCH)
252    ///     .expect("Failed to get current time")
253    ///     .as_secs();
254    ///
255    /// let candidates = tree.reward_candidates(merkle_payment_timestamp)?;
256    ///
257    /// // Each candidate's branch can be verified independently
258    /// for candidate in candidates {
259    ///     assert!(candidate.branch.verify());
260    /// }
261    /// ```
262    pub fn reward_candidates(&self, merkle_payment_timestamp: u64) -> Result<Vec<MidpointProof>> {
263        let midpoints = self.midpoints()?;
264
265        midpoints
266            .into_iter()
267            .map(|midpoint| {
268                // Generate proof for this midpoint
269                let branch = self.generate_midpoint_proof(midpoint.index, midpoint.hash)?;
270
271                Ok(MidpointProof {
272                    branch,
273                    merkle_payment_timestamp,
274                })
275            })
276            .collect()
277    }
278
279    /// Generate a proof that a address belongs to this tree
280    ///
281    /// # Arguments
282    ///
283    /// * `address_index` - Index of the address (0-based, must be < leaf_count)
284    /// * `address_hash` - The XorName hash of the address data
285    ///
286    /// # Important: Index vs Hash
287    ///
288    /// Both the **index** and **hash** are required:
289    /// - **Index**: Tells us the address's position in the tree (which leaf)
290    /// - **Hash**: The actual XorName we're proving belongs at that position
291    ///
292    /// The proof verifies: "This specific hash is at this specific index in the tree"
293    ///
294    /// # Example
295    ///
296    /// ```ignore
297    /// // You have addresses with their original order preserved
298    /// let addresses: Vec<XorName> = vec![
299    ///     XorName::from_content(b"address 0 data"),
300    ///     XorName::from_content(b"address 1 data"),
301    ///     XorName::from_content(b"address 2 data"),
302    /// ];
303    ///
304    /// // Build tree from the addresses
305    /// let tree = MerkleTree::from_xornames(addresses.clone())?;
306    ///
307    /// // Generate proofs for all addresses
308    /// for (index, address_hash) in addresses.iter().enumerate() {
309    ///     // index: position in the tree (0, 1, 2)
310    ///     // address_hash: the actual XorName at that position
311    ///     let proof = tree.generate_address_proof(index, *address_hash)?;
312    ///
313    ///     // Each proof can be verified independently
314    ///     assert!(proof.verify());
315    /// }
316    /// ```
317    ///
318    /// # Returns
319    ///
320    /// A `MerkleBranch` proof from address to root
321    ///
322    /// # Errors
323    ///
324    /// - `InvalidLeafIndex` if index >= leaf_count
325    pub fn generate_address_proof(
326        &self,
327        address_index: usize,
328        address_hash: XorName,
329    ) -> Result<MerkleBranch> {
330        if address_index >= self.leaf_count {
331            return Err(MerkleTreeError::InvalidLeafIndex {
332                index: address_index,
333                leaf_count: self.leaf_count,
334            });
335        }
336
337        let indices = vec![address_index];
338        let proof = self.inner.proof(&indices);
339
340        // Padded size is 2^depth
341        let padded_size = 1 << self.depth;
342
343        let root = self.root();
344
345        // Get the salt for this address
346        let salt = self.salts[address_index];
347
348        Ok(MerkleBranch::from_rs_merkle_proof(
349            proof,
350            address_index,
351            padded_size,
352            address_hash,
353            root,
354            Some(salt),
355        ))
356    }
357
358    /// Generate a proof that a midpoint exists at the midpoint level
359    ///
360    /// Midpoints are 2^ceil(depth/2) nodes at level depth/2
361    ///
362    /// # Arguments
363    ///
364    /// * `midpoint_index` - Index of the midpoint at the midpoint level
365    /// * `midpoint_hash` - Hash of the midpoint node
366    ///
367    /// # Returns
368    ///
369    /// A `MerkleBranch` proof from midpoint to root
370    ///
371    /// # Errors
372    ///
373    /// - `InvalidMidpointIndex` if index is out of bounds
374    fn generate_midpoint_proof(
375        &self,
376        midpoint_index: usize,
377        midpoint_hash: XorName,
378    ) -> Result<MerkleBranch> {
379        // Midpoints are at level depth/2, giving us 2^ceil(depth/2) nodes
380        let level = midpoint_level(self.depth);
381        let midpoint_count = expected_reward_pools(self.depth);
382
383        if midpoint_index >= midpoint_count {
384            return Err(MerkleTreeError::InvalidMidpointIndex {
385                index: midpoint_index,
386                midpoint_count,
387            });
388        }
389
390        let proof = self
391            .inner
392            .proof_from_node(level, midpoint_index)
393            .ok_or_else(|| {
394                MerkleTreeError::Internal("Failed to generate midpoint proof".to_string())
395            })?;
396
397        // For midpoint proofs, treat nodes at midpoint level as "leaves"
398        // Total count is the number of nodes at that level (2^midpoint_level)
399        let effective_leaf_count = midpoint_count;
400
401        let root = self.root();
402
403        Ok(MerkleBranch::from_rs_merkle_proof(
404            proof,
405            midpoint_index,
406            effective_leaf_count,
407            midpoint_hash,
408            root,
409            None, // Midpoint proofs don't need salt
410        ))
411    }
412}
413
414/// A node at the depth/2 layer of the Merkle tree
415///
416/// Used internally to determine candidate pools for batch payment routing
417#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
418struct MerkleMidpoint {
419    /// Hash of the midpoint node
420    hash: XorName,
421
422    /// Index at this level
423    index: usize,
424}
425
426/// A reward candidate derived from a midpoint
427///
428/// The candidate pool address is computed as hash(midpoint_hash || root || merkle_payment_timestamp).
429/// Network nodes closest to this address are eligible for batch payment rewards.
430/// Contains everything needed to verify the candidate is valid.
431#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
432pub struct MidpointProof {
433    /// Proof that the midpoint belongs to the Merkle tree
434    pub branch: MerkleBranch,
435
436    /// Merkle payment timestamp provided by client (used to compute candidate address)
437    /// This is the timestamp that all nodes in the pool must use for their quotes
438    pub merkle_payment_timestamp: u64,
439}
440
441impl MidpointProof {
442    /// Get the Merkle root from the candidate pool's branch
443    ///
444    /// Returns the root hash that the branch proves membership in.
445    pub fn root(&self) -> &XorName {
446        self.branch.root()
447    }
448
449    /// Get the candidate address for this pool
450    ///
451    /// The address is computed as hash(midpoint_hash || root || merkle_payment_timestamp).
452    /// Network nodes closest to this address are eligible for batch payment rewards.
453    pub fn address(&self) -> XorName {
454        let mut data = Vec::with_capacity(32 + 32 + 8);
455        data.extend_from_slice(self.branch.leaf_hash().as_ref());
456        data.extend_from_slice(self.branch.root().as_ref());
457        data.extend_from_slice(&self.merkle_payment_timestamp.to_le_bytes());
458        XorName::from_content(&data)
459    }
460
461    /// Compute deterministic hash for storage/verification
462    ///
463    /// Uses fixed-width encoding (u64) for numeric fields to ensure
464    /// architecture-independent hashing across 32-bit and 64-bit platforms.
465    pub fn hash(&self) -> XorName {
466        let mut bytes = Vec::new();
467
468        // Serialize MerkleBranch fields
469        for proof_hash in &self.branch.proof_hashes {
470            bytes.extend_from_slice(proof_hash);
471        }
472
473        // usize fields - cast to u64 for fixed-width encoding
474        bytes.extend_from_slice(&(self.branch.leaf_index as u64).to_le_bytes());
475        bytes.extend_from_slice(&(self.branch.total_leaves_count as u64).to_le_bytes());
476
477        bytes.extend_from_slice(self.branch.unsalted_leaf_hash.as_ref());
478        bytes.extend_from_slice(self.branch.root.as_ref());
479        if let Some(salt) = &self.branch.salt {
480            bytes.push(1); // Option::Some marker
481            bytes.extend_from_slice(salt);
482        } else {
483            bytes.push(0); // Option::None marker
484        }
485
486        // Add timestamp (u64 - native width)
487        bytes.extend_from_slice(&self.merkle_payment_timestamp.to_le_bytes());
488
489        XorName::from_content(&bytes)
490    }
491}
492
493/// A Merkle branch (proof) from a leaf or midpoint to the root
494///
495/// Used to prove that a address or midpoint belongs to a paid batch.
496/// Contains everything needed for verification - just call `verify()` with no arguments.
497///
498/// For leaf proofs:
499/// - leaf_index is the address index
500/// - total_leaves_count is the padded tree size (2^depth)
501/// - salt is included for privacy (prevents address content from being revealed)
502///
503/// For midpoint proofs:
504/// - leaf_index is the midpoint index at its level
505/// - total_leaves_count is the number of midpoints
506/// - salt is None (midpoints are intermediate hashes, not raw addresses)
507#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
508pub struct MerkleBranch {
509    /// The proof hashes (sibling hashes only) from leaf/midpoint to root
510    proof_hashes: Vec<[u8; 32]>,
511
512    /// Index of the leaf/node in the tree
513    leaf_index: usize,
514
515    /// Total number of leaves/nodes at the starting level
516    /// - For leaf proofs: padded tree size (2^depth)
517    /// - For midpoint proofs: number of midpoints
518    total_leaves_count: usize,
519
520    /// The unsalted leaf hash (address or midpoint) being proven
521    /// For address proofs: this is the address XorName (before salting)
522    /// For midpoint proofs: this is the midpoint hash
523    unsalted_leaf_hash: XorName,
524
525    /// The expected Merkle root
526    root: XorName,
527
528    /// Salt used for address privacy (None for midpoint proofs)
529    /// For address proofs: random salt applied as hash(unsalted_leaf_hash || salt)
530    /// For midpoint proofs: None (intermediate hashes don't need salting)
531    salt: Option<[u8; 32]>,
532}
533
534impl MerkleBranch {
535    /// Create from rs_merkle proof
536    fn from_rs_merkle_proof(
537        proof: ant_merkle::MerkleProof<XorNameHasher>,
538        leaf_index: usize,
539        total_leaves_count: usize,
540        unsalted_leaf_hash: XorName,
541        root: XorName,
542        salt: Option<[u8; 32]>,
543    ) -> Self {
544        let proof_hashes = proof.proof_hashes().to_vec();
545        Self {
546            proof_hashes,
547            leaf_index,
548            total_leaves_count,
549            unsalted_leaf_hash,
550            root,
551            salt,
552        }
553    }
554
555    /// Get the unsalted leaf hash (address or intersection) being proven
556    /// For address proofs: returns the address XorName (before salting)
557    /// For midpoint proofs: returns the midpoint hash
558    pub fn leaf_hash(&self) -> &XorName {
559        &self.unsalted_leaf_hash
560    }
561
562    /// Get the expected Merkle root
563    pub fn root(&self) -> &XorName {
564        &self.root
565    }
566
567    /// Verify this proof
568    ///
569    /// # Returns
570    ///
571    /// `true` if the proof is valid, `false` otherwise
572    ///
573    /// # Example
574    ///
575    /// ```ignore
576    /// let proof = tree.generate_address_proof(0)?;
577    ///
578    /// // The proof contains everything needed for verification
579    /// println!("Proving leaf: {:?}", proof.leaf_hash());
580    /// println!("Against root: {:?}", proof.root());
581    ///
582    /// assert!(proof.verify());
583    /// ```
584    pub fn verify(&self) -> bool {
585        // Compute the hash to verify
586        let hash = if let Some(salt) = &self.salt {
587            // For address proofs: compute hash(unsalted_leaf_hash || salt)
588            let mut data = Vec::with_capacity(64);
589            data.extend_from_slice(self.unsalted_leaf_hash.as_ref());
590            data.extend_from_slice(salt);
591            XorNameHasher::hash(&data)
592        } else {
593            // For midpoint proofs: use unsalted_leaf_hash directly
594            let leaf_bytes = self.unsalted_leaf_hash.as_ref();
595            let mut hash = [0u8; 32];
596            hash.copy_from_slice(leaf_bytes);
597            hash
598        };
599
600        let root_bytes = self.root.as_ref();
601        let mut expected_root = [0u8; 32];
602        expected_root.copy_from_slice(root_bytes);
603
604        // Use rs_merkle's verify for both leaves and midpoints
605        // For midpoints, we treat nodes at that level as "leaves" of a smaller tree
606        let proof = ant_merkle::MerkleProof::<XorNameHasher>::new(self.proof_hashes.clone());
607        proof.verify(
608            expected_root,
609            &[self.leaf_index],
610            &[hash],
611            self.total_leaves_count,
612        )
613    }
614
615    /// Get the depth (number of hashing steps) of this proof
616    pub fn depth(&self) -> usize {
617        self.proof_hashes.len()
618    }
619}
620
621/// Calculate tree depth from leaf count: ceil(log2(n))
622pub fn tree_depth(leaf_count: usize) -> u8 {
623    if leaf_count <= 1 {
624        return 0;
625    }
626
627    let mut depth = 0;
628    let mut n = leaf_count - 1;
629    while n > 0 {
630        depth += 1;
631        n >>= 1;
632    }
633    depth
634}
635
636/// Calculate the proof depth from midpoint to root: ceil(depth/2)
637pub fn midpoint_proof_depth(depth: u8) -> u8 {
638    depth.div_ceil(2)
639}
640
641/// Calculate the level in the tree where midpoints are located: floor(depth/2)
642fn midpoint_level(depth: u8) -> usize {
643    (depth / 2) as usize
644}
645
646/// Errors that can occur when verifying a Merkle proof for batch payments
647///
648/// Nodes verify address proofs without access to the original Merkle tree,
649/// using only the proof data and information stored on the smart contract.
650#[derive(Debug, Clone, PartialEq, Eq, Error)]
651pub enum BadMerkleProof {
652    #[error("Address branch proof failed Merkle verification")]
653    InvalidAddressBranchProof,
654    #[error("Winner/intersection branch proof failed Merkle verification")]
655    InvalidWinnerBranchProof,
656    #[error("Address proof depth mismatch: expected {expected}, got {got}")]
657    AddressProofDepthMismatch { expected: usize, got: usize },
658    #[error("Winner proof depth mismatch: expected {expected}, got {got}")]
659    WinnerProofDepthMismatch { expected: usize, got: usize },
660    #[error(
661        "Address branch root doesn't match smart contract root: smart_contract={smart_contract_root}, branch={branch_root}"
662    )]
663    AddressBranchRootMismatch {
664        smart_contract_root: XorName,
665        branch_root: XorName,
666    },
667    #[error(
668        "Winner branch root doesn't match smart contract root: smart_contract={smart_contract_root}, branch={branch_root}"
669    )]
670    WinnerBranchRootMismatch {
671        smart_contract_root: XorName,
672        branch_root: XorName,
673    },
674    #[error(
675        "Payment timestamp {payment_timestamp} is in the future (current time: {current_time})"
676    )]
677    TimestampInFuture {
678        payment_timestamp: u64,
679        current_time: u64,
680    },
681    #[error(
682        "Payment expired: timestamp {payment_timestamp} is {age_seconds}s old (max: {MERKLE_PAYMENT_EXPIRATION}s)"
683    )]
684    PaymentExpired {
685        payment_timestamp: u64,
686        current_time: u64,
687        age_seconds: u64,
688    },
689    #[error("Failed to get current system time: {0}")]
690    SystemTimeError(String),
691    #[error(
692        "Winner pool timestamp {pool_timestamp} doesn't match smart contract timestamp {contract_timestamp}"
693    )]
694    TimestampMismatch {
695        pool_timestamp: u64,
696        contract_timestamp: u64,
697    },
698    #[error("Address hash not matching branch leaf: leaf={leaf}, address={address}")]
699    AddressHashNotBranchLeaf { leaf: XorName, address: XorName },
700}
701
702/// Validate payment timestamp against current time
703///
704/// Checks:
705/// 1. Timestamp is not in the future
706/// 2. Payment has not expired (older than MERKLE_PAYMENT_EXPIRATION)
707/// 3. Winner pool timestamp matches smart contract timestamp
708fn validate_payment_timestamp(
709    payment_timestamp: u64,
710    pool_timestamp: u64,
711) -> std::result::Result<(), BadMerkleProof> {
712    let current_time = SystemTime::now()
713        .duration_since(UNIX_EPOCH)
714        .map_err(|e| BadMerkleProof::SystemTimeError(e.to_string()))?
715        .as_secs();
716
717    // Verify timestamp is not in the future
718    if payment_timestamp > current_time {
719        return Err(BadMerkleProof::TimestampInFuture {
720            payment_timestamp,
721            current_time,
722        });
723    }
724
725    // Verify payment has not expired
726    let age = current_time - payment_timestamp;
727    if age > MERKLE_PAYMENT_EXPIRATION {
728        return Err(BadMerkleProof::PaymentExpired {
729            payment_timestamp,
730            current_time,
731            age_seconds: age,
732        });
733    }
734
735    // Verify pool timestamp matches contract timestamp
736    if pool_timestamp != payment_timestamp {
737        return Err(BadMerkleProof::TimestampMismatch {
738            pool_timestamp,
739            contract_timestamp: payment_timestamp,
740        });
741    }
742
743    Ok(())
744}
745
746/// Verify a address proof against smart contract payment data
747///
748/// This is the complete verification flow that nodes perform when receiving addresses.
749/// Nodes don't have access to the tree, only the proofs and on-chain data.
750///
751/// # Security
752///
753/// - Validates proof depths BEFORE calling expensive verify() to prevent DoS
754/// - Verifies address hash matches the provided address bytes to prevent malicious data storage
755///
756/// # Arguments
757///
758/// * `address_hash` - The actual hash of the address being stored
759/// * `address_branch` - Merkle proof for the address (leaf to root)
760/// * `winner_pool_midpoint_proof` - The winner midpoint proof with timestamp
761/// * `smart_contract_depth` - Tree depth claimed on smart contract
762/// * `smart_contract_root` - Merkle root from smart contract payment
763/// * `smart_contract_timestamp` - Payment timestamp from smart contract (Unix seconds)
764///
765/// # Returns
766///
767/// `Ok(())` if all verifications pass, otherwise returns specific error
768///
769/// # Example
770///
771/// ```ignore
772/// // Node receives address and proofs from client
773/// verify_merkle_proof(
774///     &address_hash,
775///     &address_proof,
776///     &winner_pool_midpoint_proof,
777///     contract_data.depth,
778///     &contract_data.root,
779///     contract_data.timestamp,
780/// )?;
781///
782/// // If we get here, address is verified - store it
783/// ```
784pub fn verify_merkle_proof(
785    address_hash: &XorName,
786    address_branch: &MerkleBranch,
787    winner_pool_midpoint_proof: &MidpointProof,
788    smart_contract_depth: u8,
789    smart_contract_root: &XorName,
790    smart_contract_timestamp: u64,
791) -> std::result::Result<(), BadMerkleProof> {
792    // Validate payment timestamp
793    validate_payment_timestamp(
794        smart_contract_timestamp,
795        winner_pool_midpoint_proof.merkle_payment_timestamp,
796    )?;
797
798    // Verify address proof depth matches smart contract claimed depth
799    let address_depth = address_branch.depth();
800    let expected_address_depth = smart_contract_depth as usize;
801    if address_depth != expected_address_depth {
802        return Err(BadMerkleProof::AddressProofDepthMismatch {
803            expected: expected_address_depth,
804            got: address_depth,
805        });
806    }
807
808    // Verify winner proof depth matches expected for midpoint (ceil(depth/2))
809    let winner_depth = winner_pool_midpoint_proof.branch.depth();
810    let expected_winner_depth = midpoint_proof_depth(smart_contract_depth) as usize;
811    if winner_depth != expected_winner_depth {
812        return Err(BadMerkleProof::WinnerProofDepthMismatch {
813            expected: expected_winner_depth,
814            got: winner_depth,
815        });
816    }
817
818    // Verify Merkle inclusion (address belongs to tree)
819    if !address_branch.verify() {
820        return Err(BadMerkleProof::InvalidAddressBranchProof);
821    }
822
823    // Verify winner pool (intersection legitimacy)
824    if !winner_pool_midpoint_proof.branch.verify() {
825        return Err(BadMerkleProof::InvalidWinnerBranchProof);
826    }
827
828    // Verify address hash matches the provided address bytes
829    if address_hash != address_branch.leaf_hash() {
830        return Err(BadMerkleProof::AddressHashNotBranchLeaf {
831            leaf: *address_branch.leaf_hash(),
832            address: *address_hash,
833        });
834    }
835
836    // Verify address proof root matches on-chain root
837    if address_branch.root() != smart_contract_root {
838        return Err(BadMerkleProof::AddressBranchRootMismatch {
839            smart_contract_root: *smart_contract_root,
840            branch_root: *address_branch.root(),
841        });
842    }
843
844    // Verify winner proof root matches on-chain root
845    if winner_pool_midpoint_proof.branch.root() != smart_contract_root {
846        return Err(BadMerkleProof::WinnerBranchRootMismatch {
847            smart_contract_root: *smart_contract_root,
848            branch_root: *winner_pool_midpoint_proof.branch.root(),
849        });
850    }
851
852    Ok(())
853}
854
855/// XorName hasher for rs_merkle
856///
857/// Uses XorNameHasher (32-byte output) for hashing, consistent with Autonomi network
858#[derive(Clone)]
859struct XorNameHasher;
860
861impl ant_merkle::Hasher for XorNameHasher {
862    type Hash = [u8; 32];
863
864    fn hash(data: &[u8]) -> Self::Hash {
865        XorName::from_content(data).0
866    }
867
868    fn concat_and_hash(left: &Self::Hash, right: Option<&Self::Hash>) -> Self::Hash {
869        if let Some(right) = right {
870            XorName::from_content_parts(&[left, right]).0
871        } else {
872            XorName::from_content(left).0
873        }
874    }
875
876    fn hash_size() -> usize {
877        32
878    }
879}
880
881#[cfg(test)]
882mod tests {
883    use super::*;
884
885    fn make_test_leaves(count: usize) -> Vec<XorName> {
886        (0..count)
887            .map(|i| XorName::from_content(&i.to_le_bytes()))
888            .collect()
889    }
890
891    #[test]
892    fn test_reward_candidate_pool_hash_fixed_width_encoding() {
893        // Test that RewardCandidatePool::hash uses fixed-width encoding
894        let leaves = make_test_leaves(16);
895        let tree = MerkleTree::from_xornames(leaves).unwrap();
896        let timestamp = 1234567890u64;
897        let pools = tree.reward_candidates(timestamp).unwrap();
898        let pool = &pools[0];
899
900        // Get the hash
901        let hash1 = pool.hash();
902
903        // Manually reconstruct the hash with explicit u64 encoding to verify
904        let mut bytes = Vec::new();
905        for proof_hash in &pool.branch.proof_hashes {
906            bytes.extend_from_slice(proof_hash);
907        }
908        bytes.extend_from_slice(&(pool.branch.leaf_index as u64).to_le_bytes());
909        bytes.extend_from_slice(&(pool.branch.total_leaves_count as u64).to_le_bytes());
910        bytes.extend_from_slice(pool.branch.unsalted_leaf_hash.as_ref());
911        bytes.extend_from_slice(pool.branch.root.as_ref());
912        if let Some(salt) = &pool.branch.salt {
913            bytes.push(1);
914            bytes.extend_from_slice(salt);
915        } else {
916            bytes.push(0);
917        }
918        bytes.extend_from_slice(&pool.merkle_payment_timestamp.to_le_bytes());
919
920        let hash2 = XorName::from_content(&bytes);
921
922        assert_eq!(
923            hash1, hash2,
924            "RewardCandidatePool::hash should match manual u64-encoded hash"
925        );
926    }
927
928    #[test]
929    fn test_reward_candidate_pool_hash_architecture_independence() {
930        // Create a pool with maximum usize values to test encoding
931        let leaves = make_test_leaves(4);
932        let tree = MerkleTree::from_xornames(leaves).unwrap();
933        let timestamp = u64::MAX;
934        let pools = tree.reward_candidates(timestamp).unwrap();
935
936        // Get hashes for all pools - they should be deterministic
937        let hash1 = pools[0].hash();
938        let hash2 = pools[0].hash();
939
940        assert_eq!(hash1, hash2, "Same pool should produce identical hash");
941
942        // Verify that the serialization uses 8 bytes for usize fields
943        let pool = &pools[0];
944        let mut bytes = Vec::new();
945        for proof_hash in &pool.branch.proof_hashes {
946            bytes.extend_from_slice(proof_hash);
947        }
948
949        let start_offset = bytes.len();
950        bytes.extend_from_slice(&(pool.branch.leaf_index as u64).to_le_bytes());
951        bytes.extend_from_slice(&(pool.branch.total_leaves_count as u64).to_le_bytes());
952
953        // Verify 8 bytes were written for each usize field
954        assert_eq!(
955            bytes.len() - start_offset,
956            16, // 2 * 8 bytes
957            "Should use 8 bytes per usize field regardless of platform"
958        );
959
960        // Verify values are preserved correctly
961        let leaf_index_bytes = &bytes[start_offset..start_offset + 8];
962        let leaf_index = u64::from_le_bytes(leaf_index_bytes.try_into().unwrap());
963        assert_eq!(
964            leaf_index, pool.branch.leaf_index as u64,
965            "leaf_index should be preserved in u64 encoding"
966        );
967    }
968
969    #[test]
970    fn test_expected_reward_pools() {
971        // Test the formula: 2^ceil(depth/2)
972        assert_eq!(expected_reward_pools(1), 2); // ceil(1/2) = 1 → 2^1 = 2
973        assert_eq!(expected_reward_pools(2), 2); // ceil(2/2) = 1 → 2^1 = 2
974        assert_eq!(expected_reward_pools(3), 4); // ceil(3/2) = 2 → 2^2 = 4
975        assert_eq!(expected_reward_pools(4), 4); // ceil(4/2) = 2 → 2^2 = 4
976        assert_eq!(expected_reward_pools(5), 8); // ceil(5/2) = 3 → 2^3 = 8
977        assert_eq!(expected_reward_pools(6), 8); // ceil(6/2) = 3 → 2^3 = 8
978        assert_eq!(expected_reward_pools(7), 16); // ceil(7/2) = 4 → 2^4 = 16
979        assert_eq!(expected_reward_pools(8), 16); // ceil(8/2) = 4 → 2^4 = 16
980        assert_eq!(expected_reward_pools(16), 256); // ceil(16/2) = 8 → 2^8 = 256
981    }
982
983    #[test]
984    fn test_blake2b_output_size() {
985        // Verify that Blake2b::<U32> produces 32-byte (256-bit) hashes
986        let hash1 = XorNameHasher::hash(b"test data");
987        let hash2 = XorNameHasher::concat_and_hash(&hash1, Some(&hash1));
988
989        // These should compile - proving the type is [u8; 32]
990        assert_eq!(hash1.len(), 32, "Hash should be 32 bytes (256 bits)");
991        assert_eq!(
992            hash2.len(),
993            32,
994            "Concatenated hash should be 32 bytes (256 bits)"
995        );
996
997        // Verify hashes are different for different inputs
998        let hash3 = XorNameHasher::hash(b"different data");
999        assert_ne!(
1000            hash1, hash3,
1001            "Different inputs should produce different hashes"
1002        );
1003
1004        println!("Blake2b hash size verified: 32 bytes (256 bits)");
1005        println!("Sample hash: {:02x?}", &hash1[..8]);
1006    }
1007
1008    #[test]
1009    fn test_reward_candidate_pool_hash() {
1010        let leaves = make_test_leaves(16);
1011        let tree = MerkleTree::from_xornames(leaves).unwrap();
1012        let candidates = tree.reward_candidates(12345).unwrap();
1013
1014        // Verify we can use RewardCandidatePool in a HashSet (tests std::hash::Hash trait)
1015        let mut seen = std::collections::HashSet::new();
1016        for candidate in &candidates {
1017            assert!(seen.insert(candidate));
1018        }
1019        assert_eq!(seen.len(), candidates.len());
1020
1021        // Verify our hash() method is deterministic
1022        let hash1 = candidates[0].hash();
1023        let hash2 = candidates[0].hash();
1024        assert_eq!(hash1, hash2, "Hash should be deterministic");
1025
1026        // Verify different candidates have different hashes
1027        let hash3 = candidates[1].hash();
1028        assert_ne!(
1029            hash1, hash3,
1030            "Different candidates should have different hashes"
1031        );
1032    }
1033
1034    #[test]
1035    fn test_min_leaves_validation() {
1036        let leaves = make_test_leaves(1);
1037        let result = MerkleTree::from_xornames(leaves);
1038        assert!(matches!(result, Err(MerkleTreeError::TooFewLeaves { .. })));
1039    }
1040
1041    #[test]
1042    fn test_max_leaves_validation() {
1043        let leaves = make_test_leaves(MAX_LEAVES + 1);
1044        let result = MerkleTree::from_xornames(leaves);
1045        assert!(matches!(result, Err(MerkleTreeError::TooManyLeaves { .. })));
1046    }
1047
1048    #[test]
1049    fn test_basic_tree_construction() {
1050        let leaves = make_test_leaves(100);
1051        let tree = MerkleTree::from_xornames(leaves).unwrap();
1052
1053        assert_eq!(tree.leaf_count(), 100);
1054        assert_eq!(tree.depth(), 7); // ceil(log2(100)) = 7
1055    }
1056
1057    #[test]
1058    fn test_power_of_two_leaves() {
1059        for power in 1..=MAX_MERKLE_DEPTH {
1060            let count = 1 << power; // 2^power
1061            let leaves = make_test_leaves(count);
1062            let tree = MerkleTree::from_xornames(leaves).unwrap();
1063
1064            assert_eq!(tree.depth(), power);
1065            assert_eq!(tree.leaf_count(), count);
1066        }
1067    }
1068
1069    #[test]
1070    fn test_midpoints() {
1071        let leaves = make_test_leaves(1024);
1072        let tree = MerkleTree::from_xornames(leaves).unwrap();
1073
1074        let midpoints = tree.midpoints().unwrap();
1075
1076        // Depth = 10, (depth+1)/2 = 5 (rounded up), so 2^5 = 32 midpoints
1077        assert_eq!(midpoints.len(), 32);
1078
1079        // Check all midpoints have valid indices
1080        for (i, midpoint) in midpoints.iter().enumerate() {
1081            assert_eq!(midpoint.index, i);
1082        }
1083    }
1084
1085    #[test]
1086    fn test_reward_candidates() {
1087        let leaves = make_test_leaves(1024);
1088        let tree = MerkleTree::from_xornames(leaves).unwrap();
1089
1090        let merkle_payment_timestamp = 1234567890u64;
1091        let candidates = tree.reward_candidates(merkle_payment_timestamp).unwrap();
1092
1093        // Should have same number as midpoints
1094        assert_eq!(candidates.len(), 32);
1095
1096        // Each candidate should have unique address
1097        let mut addresses = std::collections::HashSet::new();
1098        for candidate in &candidates {
1099            assert!(addresses.insert(candidate.address()));
1100        }
1101
1102        // Verify all candidates are valid
1103        for candidate in &candidates {
1104            assert!(
1105                candidate.branch.verify(),
1106                "Candidate branch should be valid"
1107            );
1108        }
1109
1110        // Verify deterministic - same timestamp gives same candidates
1111        let candidates2 = tree.reward_candidates(merkle_payment_timestamp).unwrap();
1112        assert_eq!(candidates, candidates2);
1113
1114        // Different timestamp gives different candidates
1115        let candidates3 = tree
1116            .reward_candidates(merkle_payment_timestamp + 1)
1117            .unwrap();
1118        assert_ne!(candidates[0].address(), candidates3[0].address());
1119
1120        // But they should still be valid
1121        for candidate in &candidates3 {
1122            assert!(
1123                candidate.branch.verify(),
1124                "Candidate branch with different timestamp should still be valid"
1125            );
1126        }
1127
1128        // Verify candidate address is hash(midpoint || root || timestamp)
1129        let tree_root = tree.root();
1130        let expected_address = candidates[0].address();
1131
1132        // Manually compute to verify the address calculation
1133        let mut data = Vec::with_capacity(32 + 32 + 8);
1134        data.extend_from_slice(candidates[0].branch.leaf_hash().as_ref());
1135        data.extend_from_slice(tree_root.as_ref());
1136        data.extend_from_slice(&merkle_payment_timestamp.to_le_bytes());
1137        let manually_computed = XorName::from_content(&data);
1138        assert_eq!(expected_address, manually_computed);
1139
1140        // Verify branch is valid
1141        assert!(candidates[0].branch.verify());
1142
1143        // Verify direct field access
1144        assert_eq!(
1145            candidates[0].merkle_payment_timestamp,
1146            merkle_payment_timestamp
1147        );
1148        assert_eq!(candidates[0].address(), candidates[0].address()); // Address calculation
1149        assert_eq!(candidates[0].branch.root(), &tree_root);
1150        assert_eq!(
1151            candidates[0].branch.leaf_hash(),
1152            candidates[0].branch.leaf_hash()
1153        );
1154    }
1155
1156    #[test]
1157    fn test_address_proof_generation_and_verification() {
1158        let leaves = make_test_leaves(100);
1159        let tree = MerkleTree::from_xornames(leaves.clone()).unwrap();
1160
1161        // Test proof for first address
1162        let proof = tree.generate_address_proof(0, leaves[0]).unwrap();
1163        assert!(proof.verify());
1164
1165        // Test proof for last address
1166        let proof = tree.generate_address_proof(99, leaves[99]).unwrap();
1167        assert!(proof.verify());
1168
1169        // Test proof for middle address
1170        let proof = tree.generate_address_proof(50, leaves[50]).unwrap();
1171        assert!(proof.verify());
1172    }
1173
1174    #[test]
1175    fn test_invalid_address_index() {
1176        let leaves = make_test_leaves(100);
1177        let tree = MerkleTree::from_xornames(leaves.clone()).unwrap();
1178
1179        let dummy_hash = leaves[0]; // Just need any hash for this test
1180        let result = tree.generate_address_proof(100, dummy_hash);
1181        assert!(matches!(
1182            result,
1183            Err(MerkleTreeError::InvalidLeafIndex { .. })
1184        ));
1185    }
1186
1187    #[test]
1188    fn test_midpoint_proof_generation_and_verification() {
1189        let leaves = make_test_leaves(1024);
1190        let tree = MerkleTree::from_xornames(leaves).unwrap();
1191
1192        let midpoints = tree.midpoints().unwrap();
1193
1194        // Test proof for first midpoint
1195        let proof = tree.generate_midpoint_proof(0, midpoints[0].hash).unwrap();
1196        assert!(proof.verify());
1197
1198        // Test proof for last midpoint
1199        let proof = tree
1200            .generate_midpoint_proof(31, midpoints[31].hash)
1201            .unwrap();
1202        assert!(proof.verify());
1203    }
1204
1205    #[test]
1206    fn test_proof_depth() {
1207        let leaves = make_test_leaves(16);
1208        let tree = MerkleTree::from_xornames(leaves.clone()).unwrap();
1209
1210        // Address proof should go from leaf to root (depth 4)
1211        let address_proof = tree.generate_address_proof(0, leaves[0]).unwrap();
1212        assert_eq!(address_proof.depth(), 4);
1213
1214        // Midpoint proof should go from depth/2 to root (depth 2)
1215        let midpoints = tree.midpoints().unwrap();
1216        let midpoint_proof = tree.generate_midpoint_proof(0, midpoints[0].hash).unwrap();
1217        assert_eq!(midpoint_proof.depth(), 2);
1218    }
1219
1220    #[test]
1221    fn test_non_deterministic_root_due_to_salts() {
1222        // With random salts, the same leaves produce different roots
1223        // This is a privacy feature - prevents address content from being revealed
1224        let leaves = make_test_leaves(100);
1225
1226        let tree1 = MerkleTree::from_xornames(leaves.clone()).unwrap();
1227        let tree2 = MerkleTree::from_xornames(leaves).unwrap();
1228
1229        // Roots should be different due to random salts
1230        assert_ne!(tree1.root(), tree2.root());
1231
1232        // But both trees should still work correctly
1233        assert_eq!(tree1.depth(), tree2.depth());
1234        assert_eq!(tree1.leaf_count(), tree2.leaf_count());
1235    }
1236
1237    #[test]
1238    fn test_invalid_proof_rejection() {
1239        let leaves = make_test_leaves(10);
1240        let tree = MerkleTree::from_xornames(leaves.clone()).unwrap();
1241
1242        // Wrong leaf should fail - create new proof with wrong leaf hash
1243        let wrong_leaf = XorName::from_content(b"wrong");
1244        let wrong_proof = tree.generate_address_proof(0, wrong_leaf).unwrap();
1245        assert!(!wrong_proof.verify());
1246
1247        // Wrong root should fail - we can't easily test this with the new API
1248        // since the root is embedded in the proof during generation
1249        // This test case is no longer applicable with the new API design
1250    }
1251
1252    #[test]
1253    fn test_proof_hashes_length_for_depth_4() {
1254        // Simple test to print and verify proof_hashes length for depth 4 tree
1255        let leaves = make_test_leaves(16); // 16 = 2^4
1256        let tree = MerkleTree::from_xornames(leaves.clone()).unwrap();
1257
1258        println!("Tree depth: {}", tree.depth());
1259        println!("Tree leaf count: {}", tree.leaf_count());
1260
1261        let address_proof = tree.generate_address_proof(0, leaves[0]).unwrap();
1262        println!(
1263            "Address proof depth (proof_hashes.len()): {}",
1264            address_proof.depth()
1265        );
1266
1267        let midpoints = tree.midpoints().unwrap();
1268        let midpoint_proof = tree.generate_midpoint_proof(0, midpoints[0].hash).unwrap();
1269        println!(
1270            "Midpoint proof depth (proof_hashes.len()): {}",
1271            midpoint_proof.depth()
1272        );
1273
1274        // Verify expectations
1275        assert_eq!(tree.depth(), 4);
1276        assert_eq!(
1277            address_proof.depth(),
1278            4,
1279            "Address proof should have 4 siblings (levels 0->1->2->3->4)"
1280        );
1281        assert_eq!(
1282            midpoint_proof.depth(),
1283            2,
1284            "Midpoint proof should have 2 siblings (levels 2->3->4)"
1285        );
1286    }
1287
1288    #[test]
1289    fn test_verify_works_correctly() {
1290        // Test that verify() correctly validates proofs for depth 4 tree
1291        let leaves = make_test_leaves(16); // 16 = 2^4, depth = 4
1292        let tree = MerkleTree::from_xornames(leaves.clone()).unwrap();
1293
1294        println!("Testing address proof verification...");
1295
1296        // Test address proof for first leaf
1297        let proof_0 = tree.generate_address_proof(0, leaves[0]).unwrap();
1298        println!("Address 0 proof depth: {}", proof_0.depth());
1299        let valid = proof_0.verify();
1300        println!("Address 0 verification: {valid}");
1301        assert!(valid, "Proof for address 0 should be valid");
1302
1303        // Test address proof for last leaf
1304        let proof_15 = tree.generate_address_proof(15, leaves[15]).unwrap();
1305        println!("Address 15 proof depth: {}", proof_15.depth());
1306        let valid = proof_15.verify();
1307        println!("Address 15 verification: {valid}");
1308        assert!(valid, "Proof for address 15 should be valid");
1309
1310        // Test address proof for middle leaf
1311        let proof_7 = tree.generate_address_proof(7, leaves[7]).unwrap();
1312        println!("Address 7 proof depth: {}", proof_7.depth());
1313        let valid = proof_7.verify();
1314        println!("Address 7 verification: {valid}");
1315        assert!(valid, "Proof for address 7 should be valid");
1316
1317        println!("\nTesting midpoint proof verification...");
1318
1319        // Test midpoint proofs
1320        let midpoints = tree.midpoints().unwrap();
1321        println!("Number of midpoints: {}", midpoints.len());
1322
1323        let int_proof_0 = tree.generate_midpoint_proof(0, midpoints[0].hash).unwrap();
1324        println!("Midpoint 0 proof depth: {}", int_proof_0.depth());
1325        let valid = int_proof_0.verify();
1326        println!("Midpoint 0 verification: {valid}");
1327        assert!(valid, "Proof for midpoint 0 should be valid");
1328
1329        let int_proof_3 = tree.generate_midpoint_proof(3, midpoints[3].hash).unwrap();
1330        println!("Midpoint 3 proof depth: {}", int_proof_3.depth());
1331        let valid = int_proof_3.verify();
1332        println!("Midpoint 3 verification: {valid}");
1333        assert!(valid, "Proof for midpoint 3 should be valid");
1334
1335        println!("\nTesting invalid proofs are rejected...");
1336
1337        // Test wrong leaf hash fails - create proof with wrong hash
1338        let wrong_leaf = XorName::from_content(b"wrong_leaf");
1339        let wrong_proof = tree.generate_address_proof(0, wrong_leaf).unwrap();
1340        let valid = wrong_proof.verify();
1341        println!("Wrong leaf verification: {valid}");
1342        assert!(!valid, "Proof with wrong leaf should fail");
1343
1344        // Test using proof for wrong leaf index fails
1345        let wrong_index_proof = tree.generate_address_proof(0, leaves[1]).unwrap();
1346        let valid = wrong_index_proof.verify();
1347        println!("Wrong leaf index verification: {valid}");
1348        assert!(!valid, "Proof for leaf 0 with hash from leaf 1 should fail");
1349
1350        println!("\nAll verification tests passed!");
1351    }
1352
1353    #[test]
1354    fn test_complete_batch_payment_flow() {
1355        // This test simulates the complete Merkle batch payment flow as described in the spec:
1356        // 1. Client prepares data and builds tree
1357        // 2. Client extracts midpoints (intersections)
1358        // 3. Client generates reward candidates for payment
1359        // 4. Winner pool is selected (simulated)
1360        // 5. Client uploads addresses with proofs
1361        // 6. Nodes verify addresses belong to paid batch
1362
1363        println!("\n=== SIMULATING COMPLETE MERKLE BATCH PAYMENT FLOW ===\n");
1364
1365        // ==================================================================
1366        // PHASE 1: CLIENT PREPARES DATA
1367        // ==================================================================
1368        println!("PHASE 1: CLIENT PREPARES DATA");
1369        println!("------------------------------");
1370
1371        // Simulate uploading 100 addresses (self-encrypted file)
1372        let real_address_count = 100;
1373        let addresses = make_test_leaves(real_address_count);
1374        println!("✓ Generated {real_address_count} real addresses from self-encryption");
1375
1376        // ==================================================================
1377        // PHASE 2: CLIENT BUILDS MERKLE TREE
1378        // ==================================================================
1379        println!("\nPHASE 2: CLIENT BUILDS MERKLE TREE");
1380        println!("----------------------------------");
1381
1382        let tree = MerkleTree::from_xornames(addresses.clone()).unwrap();
1383        let depth = tree.depth();
1384        let root = tree.root();
1385        let leaf_count = tree.leaf_count();
1386
1387        println!("✓ Tree depth: {depth}");
1388        println!("✓ Real addresses: {leaf_count}");
1389        println!("✓ Padded size: {} (2^{})", 1 << depth, depth);
1390        println!("✓ Dummy addresses added: {}", (1 << depth) - leaf_count);
1391        println!("✓ Merkle root: {root:?}");
1392
1393        assert_eq!(depth, 7); // ceil(log2(100)) = 7
1394        assert_eq!(leaf_count, 100);
1395
1396        // ==================================================================
1397        // PHASE 3: CLIENT GETS REWARD CANDIDATES
1398        // ==================================================================
1399        println!("\nPHASE 3: CLIENT GETS REWARD CANDIDATES");
1400        println!("---------------------------------------");
1401
1402        // Simulate payment timestamp (use current time for realistic test)
1403        let merkle_payment_timestamp = SystemTime::now()
1404            .duration_since(UNIX_EPOCH)
1405            .expect("Failed to get current time")
1406            .as_secs();
1407        println!("✓ Transaction timestamp: {merkle_payment_timestamp}");
1408
1409        // Get all reward candidates (this represents all candidate pools)
1410        let candidates = tree.reward_candidates(merkle_payment_timestamp).unwrap();
1411        let midpoint_count = expected_reward_pools(depth);
1412        let level = midpoint_level(depth);
1413        let proof_depth = midpoint_proof_depth(depth);
1414
1415        println!("✓ Midpoint level: {level}");
1416        println!("✓ Midpoint proof depth: {proof_depth}");
1417        println!("✓ Number of midpoint nodes (candidate pools): {midpoint_count}");
1418        println!("✓ Tree depth: {depth}");
1419        println!(
1420            "✓ Total nodes queried: {} × {} = {}",
1421            candidates.len(),
1422            depth,
1423            candidates.len() * depth as usize
1424        );
1425
1426        assert_eq!(candidates.len(), midpoint_count);
1427
1428        // In production, all candidates would be verified successfully
1429        // For this test, we're demonstrating the structure and flow
1430        println!("✓ Generated {} candidate pools", candidates.len());
1431
1432        // Show example candidate address calculation
1433        let first_candidate = &candidates[0];
1434        let midpoint_hash = first_candidate.branch.leaf_hash();
1435        let candidate_root = first_candidate.branch.root();
1436
1437        println!("\n  Example candidate #0:");
1438        println!("    Midpoint hash: {midpoint_hash:?}");
1439        println!("    Root: {candidate_root:?}");
1440        println!(
1441            "    Timestamp: {}",
1442            first_candidate.merkle_payment_timestamp
1443        );
1444        println!("    Address: {:?}", first_candidate.address());
1445        println!("    (Address = hash(midpoint || root || timestamp))");
1446
1447        // ==================================================================
1448        // PHASE 4: SMART CONTRACT RECEIVES PAYMENT AND STORES DATA
1449        // ==================================================================
1450        println!("\nPHASE 4: SMART CONTRACT RECEIVES PAYMENT");
1451        println!("-----------------------------------------");
1452
1453        // Client submits payment to smart contract with:
1454        // - Merkle root
1455        // - Tree depth
1456        // - Candidate pools (with intersection proofs)
1457        // - Transaction timestamp
1458
1459        // Smart contract stores this data on-chain for nodes to verify against
1460        let smart_contract_root = root;
1461        let smart_contract_depth = depth;
1462        let smart_contract_timestamp = merkle_payment_timestamp;
1463
1464        println!("✓ Smart contract received payment");
1465        println!("✓ Stored root: {smart_contract_root:?}");
1466        println!("✓ Stored depth: {smart_contract_depth}");
1467        println!("✓ Stored timestamp: {smart_contract_timestamp}");
1468        println!("✓ Stored {} candidate pools", candidates.len());
1469
1470        // Smart contract randomly selects winner pool
1471        let winner_pool_midpoint_proof_index = 0; // In reality this is random
1472        let winner_candidate = &candidates[winner_pool_midpoint_proof_index];
1473
1474        // Smart contract stores hash of the entire winner pool for nodes to verify
1475        let smart_contract_winner_pool_midpoint_proof_hash = winner_candidate.hash();
1476
1477        println!("✓ Winner pool selected: index {winner_pool_midpoint_proof_index}");
1478        println!("✓ Winner pool hash stored: {smart_contract_winner_pool_midpoint_proof_hash:?}");
1479        println!("✓ Payment distributed to {depth} nodes (depth)");
1480
1481        // ==================================================================
1482        // PHASE 5: CLIENT UPLOADS CHUNKS WITH PROOFS
1483        // ==================================================================
1484        println!("\nPHASE 5: CLIENT UPLOADS CHUNKS WITH PROOFS");
1485        println!("-------------------------------------------");
1486
1487        // Generate proofs for all real addresses (not dummies)
1488        let mut address_proofs = Vec::new();
1489        for (i, address_hash) in addresses.iter().enumerate() {
1490            let proof = tree.generate_address_proof(i, *address_hash).unwrap();
1491            address_proofs.push(proof);
1492        }
1493
1494        println!("✓ Generated {} address proofs", address_proofs.len());
1495        println!("✓ Each proof includes:");
1496        println!("  - Merkle proof (siblings from leaf to root)");
1497        println!("  - Salt (for privacy)");
1498        println!("  - Node hash (address being proven)");
1499        println!("  - Root (expected Merkle root)");
1500
1501        // ==================================================================
1502        // PHASE 6: NODES VERIFY AND STORE CHUNKS
1503        // ==================================================================
1504        println!("\nPHASE 6: NODES VERIFY AND STORE CHUNKS");
1505        println!("---------------------------------------");
1506
1507        // Simulate node verification for each address
1508        // Nodes receive from client: address, address_proof, winner_proof
1509        // Nodes query smart contract for: root, depth, winner_hash, timestamp
1510        // verify_merkle_proof() automatically checks current system time
1511
1512        let mut verified_count = 0;
1513        for (i, address_proof) in address_proofs.iter().enumerate() {
1514            // In reality, client sends the address hash
1515            // The node receives the address data and hashes it to get address_hash
1516            let address_hash = &addresses[i];
1517
1518            // Node uses the complete verification function
1519            // This performs all 10 verification steps from the spec
1520            let result = verify_merkle_proof(
1521                address_hash,
1522                address_proof,
1523                winner_candidate,
1524                smart_contract_depth,
1525                &smart_contract_root,
1526                smart_contract_timestamp,
1527            );
1528
1529            assert!(
1530                result.is_ok(),
1531                "Address {} verification failed: {:?}",
1532                i,
1533                result.err()
1534            );
1535
1536            // Additional checks nodes would perform:
1537            // - Target was computed correctly
1538            // - Candidates were closest to target at payment time
1539            // - Majority of candidates still alive
1540            // - All candidate signatures are valid
1541
1542            verified_count += 1;
1543        }
1544
1545        println!("✓ All {verified_count} addresses verified using verify_merkle_proof()");
1546        println!("✓ Core Merkle verification includes:");
1547        println!("  1. Timestamp not in future");
1548        println!("  2. Payment not expired (< {MERKLE_PAYMENT_EXPIRATION} seconds old)");
1549        println!("  3. Winner pool timestamp matches smart contract timestamp");
1550        println!("  4. Address Merkle proof valid (address ∈ tree)");
1551        println!("  5. Winner Merkle proof valid (midpoint ∈ tree)");
1552        println!("  6. Address proof depth matches on-chain depth");
1553        println!("  7. Winner proof depth matches expected for midpoint");
1554        println!("  8. Address proof root matches on-chain root");
1555        println!("  9. Winner proof root matches on-chain root");
1556        println!("  Note: Winner pool hash verification happens in MerklePaymentProof::verify()");
1557
1558        // ==================================================================
1559        // PHASE 7: VERIFY PROOF STRUCTURE AND PROPERTIES
1560        // ==================================================================
1561        println!("\nPHASE 7: VERIFY PROOF STRUCTURE");
1562        println!("--------------------------------");
1563
1564        // Check first address proof structure (using claimed depth, not tree)
1565        let first_proof = &address_proofs[0];
1566        let claimed_depth = depth; // From on-chain
1567        let expected_address_depth = claimed_depth as usize;
1568        println!("✓ Address proof depth: {}", first_proof.depth());
1569        println!(
1570            "✓ Expected address proof depth (from claimed depth {claimed_depth}): {expected_address_depth}"
1571        );
1572        println!("✓ Number of sibling hashes: {}", first_proof.depth());
1573        println!("✓ Has salt: {}", first_proof.salt.is_some());
1574
1575        assert_eq!(
1576            first_proof.depth(),
1577            expected_address_depth,
1578            "Proof depth should match expected"
1579        );
1580
1581        // Verify winner candidate's branch to midpoint (using claimed depth, not tree)
1582        let winner_branch = &winner_candidate.branch;
1583        let expected_midpoint_depth = midpoint_proof_depth(claimed_depth) as usize;
1584        let level = midpoint_level(claimed_depth);
1585
1586        println!("\n✓ Winner midpoint proof depth: {}", winner_branch.depth());
1587        println!("✓ Expected midpoint proof depth: {expected_midpoint_depth}");
1588        println!("✓ Midpoint level: {level}");
1589        println!("✓ Tree depth: {claimed_depth}");
1590        println!("✓ No salt (midpoints are intermediate hashes)");
1591
1592        assert_eq!(
1593            winner_branch.depth(),
1594            expected_midpoint_depth,
1595            "Midpoint proof depth should match expected"
1596        );
1597        assert!(
1598            winner_branch.salt.is_none(),
1599            "Midpoint proofs should not have salt"
1600        );
1601
1602        // ==================================================================
1603        // PHASE 8: VERIFY PRIVACY PROPERTIES
1604        // ==================================================================
1605        println!("\nPHASE 8: VERIFY PRIVACY PROPERTIES");
1606        println!("-----------------------------------");
1607
1608        // Each address has a unique salt
1609        let salts: Vec<_> = address_proofs.iter().map(|p| p.salt.unwrap()).collect();
1610
1611        let unique_salts: std::collections::HashSet<_> = salts.iter().collect();
1612        assert_eq!(
1613            unique_salts.len(),
1614            salts.len(),
1615            "All salts should be unique"
1616        );
1617        println!("✓ All {} addresses have unique salts", salts.len());
1618
1619        // Different trees from same addresses have different roots (due to random salts)
1620        let tree2 = MerkleTree::from_xornames(addresses.clone()).unwrap();
1621        assert_ne!(tree.root(), tree2.root(), "Different salt → different root");
1622        println!("✓ Random salts ensure non-deterministic roots");
1623        println!("✓ Privacy: address content cannot be inferred from tree structure");
1624
1625        // ==================================================================
1626        // PHASE 9: COST COMPARISON
1627        // ==================================================================
1628        println!("\nPHASE 9: COST COMPARISON");
1629        println!("-------------------------");
1630
1631        let old_payments = real_address_count * 3; // 3 nodes per address
1632        let new_payments = depth as usize; // Only winner pool paid
1633
1634        println!("Old system (per-address payment):");
1635        println!(
1636            "  {real_address_count} addresses × 3 nodes = {old_payments} payment transactions"
1637        );
1638
1639        println!("\nNew system (Merkle batch payment):");
1640        println!("  1 batch payment → {new_payments} winner nodes");
1641        println!(
1642            "  Nodes queried: {} (only query phase, no storage payment)",
1643            candidates.len() * depth as usize
1644        );
1645
1646        let savings_pct = ((old_payments - new_payments) as f64 / old_payments as f64) * 100.0;
1647        println!("\n✓ Gas savings: {savings_pct:.1}% reduction");
1648        println!(
1649            "✓ Network query overhead: {}% of old system",
1650            (candidates.len() * depth as usize * 100) / old_payments
1651        );
1652
1653        // ==================================================================
1654        // SUMMARY
1655        // ==================================================================
1656        println!("\n=== FLOW COMPLETE ===");
1657        println!("✓ {real_address_count} real addresses uploaded");
1658        println!("✓ {} dummy addresses padded", (1 << depth) - leaf_count);
1659        println!("✓ {} candidate pools formed", candidates.len());
1660        println!("✓ 1 winner pool paid ({depth} nodes)");
1661        println!("✓ All addresses verified and stored");
1662        println!("✓ Privacy preserved with random salts");
1663        println!("✓ {savings_pct:.1}% gas cost reduction achieved\n");
1664    }
1665
1666    #[test]
1667    fn test_get_nodes_at_level_with_padding() {
1668        // Verify that our padded tree has the correct number of nodes at each level
1669        println!("\n=== TESTING OUR PADDED TREE STRUCTURE ===\n");
1670
1671        let leaves = make_test_leaves(100); // 100 real addresses
1672        let tree = MerkleTree::from_xornames(leaves).unwrap();
1673
1674        let depth = tree.depth();
1675        println!("Tree with 100 leaves:");
1676        println!("  Depth: {depth}");
1677        println!("  Original leaves: {}", tree.leaf_count());
1678        println!("  Padded size: {} (2^{})", 1 << depth, depth);
1679
1680        // Check each level
1681        for level in 0..=depth {
1682            let expected_count = 1 << (depth - level); // 2^(depth - level)
1683
1684            if let Some(nodes) = tree.inner.get_nodes_at_level(level as usize) {
1685                let actual_count = nodes.len();
1686
1687                println!("\nLevel {level}:");
1688                println!("  Expected: {} nodes (2^{})", expected_count, depth - level);
1689                println!("  Actual: {actual_count} nodes");
1690
1691                if level as usize == midpoint_level(depth) {
1692                    println!("  >>> MIDPOINT LEVEL <<<");
1693                    println!(
1694                        "  Our workaround takes: {} nodes",
1695                        std::cmp::min(actual_count, 1 << midpoint_proof_depth(depth))
1696                    );
1697                }
1698
1699                if actual_count != expected_count {
1700                    println!("  ⚠ Mismatch! This is why we need .take() workaround");
1701                }
1702            }
1703        }
1704
1705        println!("\n=== END TEST ===\n");
1706    }
1707
1708    #[test]
1709    fn test_proof_hashes_length_matches_depth() {
1710        // Test with various tree sizes to verify proof_hashes.len() == depth
1711
1712        // 16 leaves = 2^4, depth = 4
1713        let leaves = make_test_leaves(16);
1714        let tree = MerkleTree::from_xornames(leaves.clone()).unwrap();
1715        assert_eq!(tree.depth(), 4);
1716
1717        let address_proof = tree.generate_address_proof(0, leaves[0]).unwrap();
1718        // From leaf (level 0) to root (level 4) = 4 hashing steps = 4 siblings
1719        assert_eq!(address_proof.depth(), 4);
1720
1721        let midpoints = tree.midpoints().unwrap();
1722        let midpoint_proof = tree.generate_midpoint_proof(0, midpoints[0].hash).unwrap();
1723        // From midpoint (level 2) to root (level 4) = 2 hashing steps = 2 siblings
1724        assert_eq!(midpoint_proof.depth(), 2);
1725
1726        // 1024 leaves = 2^10, depth = 10
1727        let leaves = make_test_leaves(1024);
1728        let tree = MerkleTree::from_xornames(leaves.clone()).unwrap();
1729        assert_eq!(tree.depth(), 10);
1730
1731        let address_proof = tree.generate_address_proof(0, leaves[0]).unwrap();
1732        // From leaf (level 0) to root (level 10) = 10 hashing steps = 10 siblings
1733        assert_eq!(address_proof.depth(), 10);
1734
1735        let midpoints = tree.midpoints().unwrap();
1736        let midpoint_proof = tree.generate_midpoint_proof(0, midpoints[0].hash).unwrap();
1737        // From midpoint (level 5) to root (level 10) = 5 hashing steps = 5 siblings
1738        assert_eq!(midpoint_proof.depth(), 5);
1739
1740        // 100 leaves = padded to 128 = 2^7, depth = 7
1741        let leaves = make_test_leaves(100);
1742        let tree = MerkleTree::from_xornames(leaves.clone()).unwrap();
1743        assert_eq!(tree.depth(), 7);
1744
1745        let address_proof = tree.generate_address_proof(0, leaves[0]).unwrap();
1746        // From leaf (level 0) to root (level 7) = 7 hashing steps = 7 siblings
1747        assert_eq!(address_proof.depth(), 7);
1748
1749        let midpoints = tree.midpoints().unwrap();
1750        let midpoint_proof = tree.generate_midpoint_proof(0, midpoints[0].hash).unwrap();
1751        // Midpoint bits: (7+1)/2 = 4, level: 7-4 = 3, proof: 7-3 = 4 siblings
1752        assert_eq!(midpoint_proof.depth(), 4);
1753    }
1754
1755    #[test]
1756    fn test_verify_merkle_proof_errors() {
1757        use std::time::{SystemTime, UNIX_EPOCH};
1758
1759        let leaves = make_test_leaves(16);
1760        let tree = MerkleTree::from_xornames(leaves.clone()).unwrap();
1761        let merkle_payment_timestamp = SystemTime::now()
1762            .duration_since(UNIX_EPOCH)
1763            .unwrap()
1764            .as_secs();
1765
1766        let candidates = tree.reward_candidates(merkle_payment_timestamp).unwrap();
1767        let winner_pool_midpoint_proof = &candidates[0];
1768        let address_proof = tree.generate_address_proof(0, leaves[0]).unwrap();
1769        let root = tree.root();
1770        let depth = tree.depth();
1771
1772        // Test 1: Invalid address proof (wrong root)
1773        let wrong_root = XorName::from_content(b"wrong root");
1774        let result = verify_merkle_proof(
1775            &leaves[0],
1776            &address_proof,
1777            winner_pool_midpoint_proof,
1778            depth,
1779            &wrong_root,
1780            merkle_payment_timestamp,
1781        );
1782        assert!(matches!(
1783            result,
1784            Err(BadMerkleProof::AddressBranchRootMismatch { .. })
1785        ));
1786
1787        // Test 2: Address proof depth mismatch
1788        let result = verify_merkle_proof(
1789            &leaves[0],
1790            &address_proof,
1791            winner_pool_midpoint_proof,
1792            depth + 1, // Wrong depth
1793            &root,
1794            merkle_payment_timestamp,
1795        );
1796        assert!(matches!(
1797            result,
1798            Err(BadMerkleProof::AddressProofDepthMismatch { .. })
1799        ));
1800
1801        // Test 3: Winner proof root mismatch
1802        let mut wrong_winner = winner_pool_midpoint_proof.clone();
1803        // Create a different proof with wrong root
1804        let wrong_tree = MerkleTree::from_xornames(make_test_leaves(16)).unwrap();
1805        let wrong_candidates = wrong_tree
1806            .reward_candidates(merkle_payment_timestamp)
1807            .unwrap();
1808        wrong_winner.branch = wrong_candidates[0].branch.clone();
1809
1810        let result = verify_merkle_proof(
1811            &leaves[0],
1812            &address_proof,
1813            &wrong_winner,
1814            depth,
1815            &root,
1816            merkle_payment_timestamp,
1817        );
1818        assert!(matches!(
1819            result,
1820            Err(BadMerkleProof::WinnerBranchRootMismatch { .. })
1821        ));
1822
1823        // Test 4: Timestamp in future
1824        let future_timestamp = merkle_payment_timestamp + 1000;
1825        let result = verify_merkle_proof(
1826            &leaves[0],
1827            &address_proof,
1828            winner_pool_midpoint_proof,
1829            depth,
1830            &root,
1831            future_timestamp,
1832        );
1833        assert!(matches!(
1834            result,
1835            Err(BadMerkleProof::TimestampInFuture { .. })
1836        ));
1837
1838        // Test 5: Payment expired
1839        let old_timestamp = merkle_payment_timestamp - MERKLE_PAYMENT_EXPIRATION - 1;
1840        let old_candidates = tree.reward_candidates(old_timestamp).unwrap();
1841        let result = verify_merkle_proof(
1842            &leaves[0],
1843            &address_proof,
1844            &old_candidates[0],
1845            depth,
1846            &root,
1847            old_timestamp,
1848        );
1849        assert!(matches!(result, Err(BadMerkleProof::PaymentExpired { .. })));
1850
1851        // Test 6: Timestamp mismatch between pool and contract
1852        // Use a different timestamp that's still valid (not future, not expired)
1853        let different_timestamp = merkle_payment_timestamp - 100;
1854        let result = verify_merkle_proof(
1855            &leaves[0],
1856            &address_proof,
1857            winner_pool_midpoint_proof,
1858            depth,
1859            &root,
1860            different_timestamp,
1861        );
1862        assert!(matches!(
1863            result,
1864            Err(BadMerkleProof::TimestampMismatch { .. })
1865        ));
1866    }
1867
1868    #[test]
1869    fn test_invalid_midpoint_index() {
1870        let leaves = make_test_leaves(16);
1871        let tree = MerkleTree::from_xornames(leaves).unwrap();
1872
1873        let midpoints = tree.midpoints().unwrap();
1874        let midpoint_count = midpoints.len();
1875
1876        // Try to generate proof for invalid midpoint index
1877        let result = tree.generate_midpoint_proof(midpoint_count, XorName::from_content(b"test"));
1878
1879        assert!(matches!(
1880            result,
1881            Err(MerkleTreeError::InvalidMidpointIndex { .. })
1882        ));
1883    }
1884
1885    #[test]
1886    fn test_reward_candidate_pool_address() {
1887        let leaves = make_test_leaves(16);
1888        let tree = MerkleTree::from_xornames(leaves).unwrap();
1889
1890        let timestamp1 = 12345u64;
1891        let timestamp2 = 67890u64;
1892
1893        let candidates1 = tree.reward_candidates(timestamp1).unwrap();
1894        let candidates2 = tree.reward_candidates(timestamp2).unwrap();
1895
1896        // Same tree, same candidate index, different timestamp = different address
1897        assert_ne!(candidates1[0].address(), candidates2[0].address());
1898
1899        // Same candidate, same call = deterministic address
1900        assert_eq!(candidates1[0].address(), candidates1[0].address());
1901
1902        // Verify address is hash(midpoint || root || timestamp)
1903        let addr = candidates1[0].address();
1904        let mut data = Vec::with_capacity(32 + 32 + 8);
1905        data.extend_from_slice(candidates1[0].branch.leaf_hash().as_ref());
1906        data.extend_from_slice(candidates1[0].branch.root().as_ref());
1907        data.extend_from_slice(&timestamp1.to_le_bytes());
1908        let expected = XorName::from_content(&data);
1909        assert_eq!(addr, expected);
1910    }
1911
1912    #[test]
1913    fn test_calculate_depth_edge_cases() {
1914        // Test the calculate_depth function via tree construction
1915        let test_cases = vec![
1916            (2, 1),     // 2 leaves = depth 1
1917            (3, 2),     // 3 leaves = depth 2 (padded to 4)
1918            (4, 2),     // 4 leaves = depth 2
1919            (5, 3),     // 5 leaves = depth 3 (padded to 8)
1920            (8, 3),     // 8 leaves = depth 3
1921            (9, 4),     // 9 leaves = depth 4 (padded to 16)
1922            (16, 4),    // 16 leaves = depth 4
1923            (17, 5),    // 17 leaves = depth 5 (padded to 32)
1924            (100, 7),   // 100 leaves = depth 7 (padded to 128)
1925            (1024, 10), // 1024 leaves = depth 10
1926        ];
1927
1928        for (leaf_count, expected_depth) in test_cases {
1929            let leaves = make_test_leaves(leaf_count);
1930            let tree = MerkleTree::from_xornames(leaves).unwrap();
1931            assert_eq!(
1932                tree.depth(),
1933                expected_depth,
1934                "Depth mismatch for {leaf_count} leaves"
1935            );
1936        }
1937    }
1938}