Skip to main content

irontide_core/
merkle_state.rs

1#![allow(
2    clippy::cast_possible_truncation,
3    reason = "M175: BEP 52 Merkle tree — proof depth bounded by tree height (≤ 32 for any realistic file)"
4)]
5
6//! Per-file Merkle tree verification state for BEP 52 downloads.
7//!
8//! Tracks which blocks have been verified against the file's Merkle tree.
9//! Handles the case where block data arrives before piece-layer hashes
10//! (deferred verification via `SetBlockResult::Unknown`).
11
12use crate::{Id32, MerkleTree};
13
14/// Result of setting a block hash in the Merkle tree state.
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum SetBlockResult {
17    /// Block hash matches the Merkle tree — all blocks in the piece verified.
18    Ok,
19    /// Piece-layer hash not yet available, or not all sibling blocks present;
20    /// block hash stored for deferred verification.
21    Unknown,
22    /// Block hash doesn't match the Merkle tree (bad peer).
23    HashFailed,
24}
25
26/// Per-file Merkle tree verification state for BEP 52 downloads.
27///
28/// Tracks which blocks have been verified against the file's Merkle tree.
29/// Handles the case where block data arrives before piece-layer hashes
30/// (deferred verification via `SetBlockResult::Unknown`).
31pub struct MerkleTreeState {
32    /// File root hash (from torrent metadata, immutable).
33    root: Id32,
34    /// Total number of 16 KiB blocks in this file.
35    num_blocks: u32,
36    /// Total number of pieces in this file.
37    num_pieces: u32,
38    /// Piece-layer hashes (one per piece). None until received.
39    piece_hashes: Option<Vec<Id32>>,
40    /// Block-layer hashes (one per block). Populated during download.
41    block_hashes: Vec<Option<Id32>>,
42    /// Which blocks have been verified against the Merkle tree.
43    /// Uses `Vec<bool>` to avoid circular dependency on `irontide-storage::Bitfield`.
44    block_verified: Vec<bool>,
45}
46
47impl MerkleTreeState {
48    /// Create a new empty tree state for a file.
49    #[must_use]
50    pub fn new(root: Id32, num_blocks: u32, num_pieces: u32) -> Self {
51        Self {
52            root,
53            num_blocks,
54            num_pieces,
55            piece_hashes: None,
56            block_hashes: vec![None; num_blocks as usize],
57            block_verified: vec![false; num_blocks as usize],
58        }
59    }
60
61    /// The file's Merkle root hash.
62    #[must_use]
63    pub fn root(&self) -> Id32 {
64        self.root
65    }
66
67    /// Total number of pieces.
68    #[must_use]
69    pub fn num_pieces(&self) -> u32 {
70        self.num_pieces
71    }
72
73    /// Total number of blocks.
74    #[must_use]
75    pub fn num_blocks(&self) -> u32 {
76        self.num_blocks
77    }
78
79    /// Whether the piece-layer hash is available for a given piece.
80    #[must_use]
81    pub fn has_piece_hash(&self, piece_index: u32) -> bool {
82        self.piece_hashes
83            .as_ref()
84            .is_some_and(|ph| (piece_index as usize) < ph.len())
85    }
86
87    /// Set piece-layer hashes (from hash response).
88    pub fn set_piece_hashes(&mut self, hashes: Vec<Id32>) {
89        self.piece_hashes = Some(hashes);
90    }
91
92    /// Set piece-layer hashes and retroactively verify any stored block hashes.
93    ///
94    /// Returns a list of piece indices that were fully verified by this operation.
95    pub fn set_piece_hashes_and_verify(
96        &mut self,
97        hashes: Vec<Id32>,
98        blocks_per_piece: u32,
99    ) -> Vec<u32> {
100        self.piece_hashes = Some(hashes);
101
102        let mut verified_pieces = Vec::new();
103
104        for piece in 0..self.num_pieces {
105            let first_block = piece * blocks_per_piece;
106            let last_block = ((piece + 1) * blocks_per_piece).min(self.num_blocks);
107
108            // Check if all blocks in this piece have stored hashes
109            let all_stored =
110                (first_block..last_block).all(|b| self.block_hashes[b as usize].is_some());
111
112            if !all_stored {
113                continue;
114            }
115
116            // Collect stored block hashes and verify against piece hash
117            let block_slice: Vec<Id32> = (first_block..last_block)
118                .map(|b| self.block_hashes[b as usize].unwrap())
119                .collect();
120
121            let piece_hash = MerkleTree::root_from_hashes(&block_slice);
122            if let Some(ref ph) = self.piece_hashes {
123                if piece as usize >= ph.len() {
124                    continue;
125                }
126                if piece_hash == ph[piece as usize] {
127                    for b in first_block..last_block {
128                        self.block_verified[b as usize] = true;
129                    }
130                    verified_pieces.push(piece);
131                }
132            }
133        }
134
135        verified_pieces
136    }
137
138    /// Record a computed block hash. Returns the verification result.
139    ///
140    /// - `Ok`: all blocks in the piece verified against piece-layer hash.
141    /// - `Unknown`: piece-layer hash unavailable, or not all sibling blocks present yet.
142    /// - `HashFailed`: block hash doesn't match the Merkle tree.
143    pub fn set_block_hash(&mut self, block_index: u32, hash: Id32) -> SetBlockResult {
144        if block_index >= self.num_blocks {
145            return SetBlockResult::HashFailed;
146        }
147
148        // Store the block hash for potential deferred verification
149        self.block_hashes[block_index as usize] = Some(hash);
150
151        let Some(piece_hashes) = &self.piece_hashes else {
152            return SetBlockResult::Unknown;
153        };
154
155        // Determine which piece this block belongs to
156        let blocks_per_piece = if self.num_pieces > 0 {
157            self.num_blocks.div_ceil(self.num_pieces)
158        } else {
159            return SetBlockResult::Unknown;
160        };
161        let piece_index = block_index / blocks_per_piece;
162
163        if piece_index as usize >= piece_hashes.len() {
164            return SetBlockResult::HashFailed;
165        }
166
167        // Check if all blocks for this piece have hashes
168        let first_block = piece_index * blocks_per_piece;
169        let last_block = ((piece_index + 1) * blocks_per_piece).min(self.num_blocks);
170
171        let all_present =
172            (first_block..last_block).all(|b| self.block_hashes[b as usize].is_some());
173
174        if !all_present {
175            // Gap 3 fix: return Unknown, not Ok — block hash stored but can't
176            // verify until all sibling blocks arrive for the full sub-tree.
177            return SetBlockResult::Unknown;
178        }
179
180        // All blocks present — verify piece hash
181        let block_slice: Vec<Id32> = (first_block..last_block)
182            .map(|b| self.block_hashes[b as usize].unwrap())
183            .collect();
184
185        let computed = MerkleTree::root_from_hashes(&block_slice);
186        if computed == piece_hashes[piece_index as usize] {
187            for b in first_block..last_block {
188                self.block_verified[b as usize] = true;
189            }
190            SetBlockResult::Ok
191        } else {
192            SetBlockResult::HashFailed
193        }
194    }
195
196    /// Check if a specific block has been verified.
197    #[must_use]
198    pub fn is_block_verified(&self, block_index: u32) -> bool {
199        self.block_verified
200            .get(block_index as usize)
201            .copied()
202            .unwrap_or(false)
203    }
204
205    /// Check if all blocks in a piece have been verified.
206    #[must_use]
207    pub fn piece_verified(&self, piece_index: u32, blocks_per_piece: u32) -> bool {
208        let first = piece_index * blocks_per_piece;
209        let last = ((piece_index + 1) * blocks_per_piece).min(self.num_blocks);
210        (first..last).all(|b| self.block_verified[b as usize])
211    }
212
213    /// Get a reference to the piece hashes, if available.
214    #[must_use]
215    pub fn piece_hashes(&self) -> Option<&[Id32]> {
216        self.piece_hashes.as_deref()
217    }
218}
219
220#[cfg(test)]
221mod tests {
222    use super::*;
223    use crate::{Id32, MerkleTree, sha256};
224
225    fn make_block_hashes(n: usize) -> Vec<Id32> {
226        (0..n).map(|i| sha256(&[i as u8])).collect()
227    }
228
229    #[test]
230    fn new_state_has_no_piece_hashes() {
231        let state = MerkleTreeState::new(Id32::ZERO, 16, 1);
232        assert!(!state.has_piece_hash(0));
233    }
234
235    #[test]
236    fn set_piece_hashes_and_query() {
237        let block_hashes = make_block_hashes(4);
238        let tree = MerkleTree::from_leaves(&block_hashes);
239        let pieces = tree.piece_layer(2).to_vec(); // 2 blocks per piece → 2 piece hashes
240
241        let mut state = MerkleTreeState::new(tree.root(), 4, 2);
242        state.set_piece_hashes(pieces);
243        assert!(state.has_piece_hash(0));
244        assert!(state.has_piece_hash(1));
245        assert!(!state.has_piece_hash(2)); // out of range
246    }
247
248    #[test]
249    fn set_block_hash_ok_when_all_siblings_present() {
250        // Build a tree: 4 blocks, 2 per piece
251        let block_hashes = make_block_hashes(4);
252        let tree = MerkleTree::from_leaves(&block_hashes);
253        let pieces = tree.piece_layer(2).to_vec();
254
255        let mut state = MerkleTreeState::new(tree.root(), 4, 2);
256        state.set_piece_hashes(pieces);
257
258        // First block → Unknown (sibling not present yet)
259        let result = state.set_block_hash(0, block_hashes[0]);
260        assert_eq!(result, SetBlockResult::Unknown);
261        assert!(!state.is_block_verified(0));
262
263        // Second block completes the piece → Ok
264        let result = state.set_block_hash(1, block_hashes[1]);
265        assert_eq!(result, SetBlockResult::Ok);
266        assert!(state.is_block_verified(0));
267        assert!(state.is_block_verified(1));
268    }
269
270    #[test]
271    fn set_block_hash_unknown_no_piece_hashes() {
272        // No piece hashes loaded → unknown
273        let mut state = MerkleTreeState::new(Id32::ZERO, 4, 2);
274        let hash = sha256(b"block0");
275        let result = state.set_block_hash(0, hash);
276        assert_eq!(result, SetBlockResult::Unknown);
277        assert!(!state.is_block_verified(0));
278    }
279
280    #[test]
281    fn set_block_hash_failed() {
282        let block_hashes = make_block_hashes(4);
283        let tree = MerkleTree::from_leaves(&block_hashes);
284        let pieces = tree.piece_layer(2).to_vec();
285
286        let mut state = MerkleTreeState::new(tree.root(), 4, 2);
287        state.set_piece_hashes(pieces);
288
289        // Set correct first block
290        state.set_block_hash(0, block_hashes[0]);
291        // Set wrong second block → fails when piece hash is computed
292        let wrong_hash = sha256(b"wrong");
293        let result = state.set_block_hash(1, wrong_hash);
294        assert_eq!(result, SetBlockResult::HashFailed);
295    }
296
297    #[test]
298    fn piece_verified_after_all_blocks() {
299        let block_hashes = make_block_hashes(4);
300        let tree = MerkleTree::from_leaves(&block_hashes);
301        let pieces = tree.piece_layer(2).to_vec();
302
303        let mut state = MerkleTreeState::new(tree.root(), 4, 2);
304        state.set_piece_hashes(pieces);
305
306        // Verify blocks 0,1 (piece 0)
307        assert_eq!(
308            state.set_block_hash(0, block_hashes[0]),
309            SetBlockResult::Unknown
310        );
311        assert!(!state.piece_verified(0, 2)); // only 1 of 2 blocks
312        assert_eq!(state.set_block_hash(1, block_hashes[1]), SetBlockResult::Ok);
313        assert!(state.piece_verified(0, 2)); // both blocks done
314    }
315
316    #[test]
317    fn deferred_verification_unknown_then_ok() {
318        // Block hashes arrive before piece-layer hashes
319        let block_hashes = make_block_hashes(4);
320        let tree = MerkleTree::from_leaves(&block_hashes);
321        let pieces = tree.piece_layer(2).to_vec();
322
323        let mut state = MerkleTreeState::new(tree.root(), 4, 2);
324
325        // Blocks arrive → Unknown (no piece hashes yet)
326        assert_eq!(
327            state.set_block_hash(0, block_hashes[0]),
328            SetBlockResult::Unknown
329        );
330        assert_eq!(
331            state.set_block_hash(1, block_hashes[1]),
332            SetBlockResult::Unknown
333        );
334
335        // Piece hashes arrive → retroactively verify stored blocks
336        let verified = state.set_piece_hashes_and_verify(pieces, 2);
337        assert_eq!(verified.len(), 1);
338        assert!(verified.contains(&0u32)); // piece 0 now verified
339        assert!(state.is_block_verified(0));
340        assert!(state.is_block_verified(1));
341    }
342}