Skip to main content

thru_base/
bintrie_proof.rs

1use crate::bintrie_types::{Hash, Pubkey};
2
3// State proof type constants (matching C implementation in tn_state_proof.h)
4pub const TN_STATE_PROOF_TYPE_EXISTING: u64 = 0x0;
5pub const TN_STATE_PROOF_TYPE_UPDATING: u64 = 0x1;
6pub const TN_STATE_PROOF_TYPE_CREATION: u64 = 0x2;
7
8/// Proof structure for existence/non-existence proofs
9#[derive(Debug, Clone)]
10pub struct Proof {
11    pub proof_indices: Vec<u8>,
12    pub sibling_hashes: Vec<Hash>,
13    pub existing_leaf_hash: Option<Hash>,
14}
15
16impl Proof {
17    pub fn new() -> Self {
18        Self {
19            proof_indices: Vec::new(),
20            sibling_hashes: Vec::new(),
21            existing_leaf_hash: None,
22        }
23    }
24    pub fn to_wire(&self, slot: u64) -> Vec<u8> {
25        // Convert proof indices to path bitset (matching C tn_state_proof_idx_list_to_path_bitset)
26        let mut path_bitset = [0u8; 32];
27        for &idx in &self.proof_indices {
28            let bit_idx = (idx % 64) as usize;
29            let word_idx = (idx / 64) as usize;
30            if word_idx < 4 {
31                // Convert to little-endian u64 array like C implementation
32                let start = word_idx * 8;
33                let mut word_bytes = [0u8; 8];
34                word_bytes.copy_from_slice(&path_bitset[start..start + 8]);
35                let mut word = u64::from_le_bytes(word_bytes);
36                word |= 1u64 << bit_idx;
37                path_bitset[start..start + 8].copy_from_slice(&word.to_le_bytes());
38            }
39        }
40
41        // Create header: type_slot (8 bytes) + path_bitset (32 bytes)
42        let mut result = Vec::with_capacity(40 + 64 + self.sibling_hashes.len() * 32);
43
44        // Encode type_slot: slot in low 62 bits, type (EXISTING=0) in high 2 bits
45        let type_slot = slot
46            | (if self.existing_leaf_hash.is_some() {
47                TN_STATE_PROOF_TYPE_UPDATING
48            } else {
49                TN_STATE_PROOF_TYPE_EXISTING
50            } << 62);
51        result.extend_from_slice(&type_slot.to_le_bytes());
52
53        // Add path bitset
54        result.extend_from_slice(&path_bitset);
55
56        if let Some(existing_leaf_hash) = self.existing_leaf_hash {
57            result.extend_from_slice(existing_leaf_hash.as_bytes());
58        }
59
60        for sibling_hash in &self.sibling_hashes {
61            result.extend_from_slice(sibling_hash.as_bytes());
62        }
63
64        result
65    }
66}
67
68/// Result of a non-existence proof
69#[derive(Debug, Clone)]
70pub struct NonExistenceProof {
71    pub proof: Proof,
72    pub existing_pubkey: Pubkey,
73    pub existing_hash: Hash,
74}
75
76impl NonExistenceProof {
77    /// Convert the non-existence proof to wire format compatible with C tn_state_proof_t
78    /// This creates a CREATION type state proof with the proof data
79    ///
80    /// Wire format layout:
81    /// - Header (40 bytes):
82    ///   - type_slot (8 bytes): slot in low 62 bits, proof type (2=CREATION) in high 2 bits
83    ///   - path_bitset (32 bytes): bitset indicating which proof indices are used
84    /// - Body (variable length):
85    ///   - existing_leaf_pubkey (32 bytes): pubkey of the existing leaf found
86    ///   - existing_leaf_hash (32 bytes): hash of the existing leaf value
87    ///   - sibling_hashes (32 * n bytes): sibling hashes for the proof path
88    ///
89    /// # Arguments
90    /// * `slot` - The slot number to encode in the proof header
91    ///
92    /// # Returns
93    /// A Vec<u8> containing the binary representation compatible with C tn_state_proof_t
94    ///
95    /// # Example
96    /// ```
97    /// use thru_base::bintrie::BinTrie;
98    /// use thru_base::bintrie_types::{Pubkey, Hash};
99    ///
100    /// let mut trie = BinTrie::new();
101    /// let existing_key = Pubkey::new([1u8; 32]);
102    /// let existing_value = Hash::new([2u8; 32]);
103    /// trie.insert(existing_key, existing_value).unwrap();
104    ///
105    /// let missing_key = Pubkey::new([3u8; 32]);
106    /// let proof = trie.prove_non_existence(&missing_key).unwrap();
107    ///
108    /// let wire_data = proof.to_wire(12345);
109    /// assert!(wire_data.len() >= 104); // At least header + existing pubkey + hash
110    /// ```
111    pub fn to_wire(&self, slot: u64) -> Vec<u8> {
112        // Convert proof indices to path bitset (matching C tn_state_proof_idx_list_to_path_bitset)
113        let mut path_bitset = [0u8; 32];
114        for &idx in &self.proof.proof_indices {
115            let bit_idx = (idx % 64) as usize;
116            let word_idx = (idx / 64) as usize;
117            if word_idx < 4 {
118                // Convert to little-endian u64 array like C implementation
119                let start = word_idx * 8;
120                let mut word_bytes = [0u8; 8];
121                word_bytes.copy_from_slice(&path_bitset[start..start + 8]);
122                let mut word = u64::from_le_bytes(word_bytes);
123                word |= 1u64 << bit_idx;
124                path_bitset[start..start + 8].copy_from_slice(&word.to_le_bytes());
125            }
126        }
127
128        // Create header: type_slot (8 bytes) + path_bitset (32 bytes)
129        let mut result = Vec::with_capacity(40 + 64 + self.proof.sibling_hashes.len() * 32);
130
131        // Encode type_slot: slot in low 62 bits, type (CREATION=2) in high 2 bits
132        let type_slot = slot | (TN_STATE_PROOF_TYPE_CREATION << 62);
133        result.extend_from_slice(&type_slot.to_le_bytes());
134
135        // Add path bitset
136        result.extend_from_slice(&path_bitset);
137
138        // Add body for CREATION type:
139        // 1. existing_leaf_pubkey (32 bytes)
140        // 2. existing_leaf_hash (32 bytes)
141        // 3. sibling_hashes (32 bytes each)
142        result.extend_from_slice(self.existing_pubkey.as_bytes());
143        result.extend_from_slice(self.existing_hash.as_bytes());
144
145        for sibling_hash in &self.proof.sibling_hashes {
146            result.extend_from_slice(sibling_hash.as_bytes());
147        }
148
149        result
150    }
151}