penumbra_sdk_tct/internal/complete/
node.rs

1use serde::{Deserialize, Serialize};
2
3use crate::prelude::*;
4
5use super::super::frontier;
6
7pub mod children;
8pub use children::Children;
9
10/// A complete sparse node in a tree, storing only the witnessed subtrees.
11#[derive(Clone, Debug)]
12pub struct Node<Child: Clone> {
13    hash: Hash,
14    forgotten: [Forgotten; 4],
15    children: Children<Child>,
16}
17
18impl<Child: Serialize + Clone> Serialize for Node<Child> {
19    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
20    where
21        S: serde::Serializer,
22    {
23        self.children.serialize(serializer)
24    }
25}
26
27impl<'de, Child: Height + GetHash + Deserialize<'de> + Clone> Deserialize<'de> for Node<Child> {
28    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
29    where
30        D: serde::Deserializer<'de>,
31    {
32        let children = Children::deserialize(deserializer)?;
33        Ok(Self {
34            hash: children.hash(),
35            forgotten: Default::default(),
36            children,
37        })
38    }
39}
40
41impl<Child: Height + Clone> Node<Child> {
42    pub(in super::super) fn from_children_or_else_hash(
43        forgotten: [Forgotten; 4],
44        children: [Insert<Child>; 4],
45    ) -> Insert<Self>
46    where
47        Child: GetHash,
48    {
49        match Children::try_from(children) {
50            Ok(children) => Insert::Keep(Self {
51                hash: children.hash(),
52                forgotten,
53                children,
54            }),
55            Err([a, b, c, d]) => {
56                // If there were no witnessed children, compute a hash for this node based on the
57                // node's height and the hashes of its children.
58                Insert::Hash(Hash::node(<Self as Height>::Height::HEIGHT, a, b, c, d))
59            }
60        }
61    }
62
63    /// Get the children of this node as an array of either children or hashes.
64    pub fn children(&self) -> [Insert<&Child>; 4] {
65        self.children.children()
66    }
67
68    /// Get the forgotten versions of the children.
69    pub fn forgotten(&self) -> [Forgotten; 4] {
70        self.forgotten
71    }
72}
73
74impl<Child: Height + Clone> Height for Node<Child> {
75    type Height = Succ<Child::Height>;
76}
77
78impl<Child: Complete + Clone> Complete for Node<Child>
79where
80    Child::Focus: Clone,
81{
82    type Focus = frontier::Node<Child::Focus>;
83}
84
85impl<Child: Height + GetHash + Clone> GetHash for Node<Child> {
86    #[inline]
87    fn hash(&self) -> Hash {
88        self.hash
89    }
90
91    #[inline]
92    fn cached_hash(&self) -> Option<Hash> {
93        Some(self.hash)
94    }
95}
96
97impl<Child: GetHash + Witness + Clone> Witness for Node<Child> {
98    #[inline]
99    fn witness(&self, index: impl Into<u64>) -> Option<(AuthPath<Self>, Hash)> {
100        let index = index.into();
101
102        // Which way to go down the tree from this node
103        let (which_way, index) = WhichWay::at(Self::Height::HEIGHT, index);
104
105        // Select the child we should be witnessing
106        let (child, siblings) = which_way.pick(self.children());
107
108        // Hash all the other siblings
109        let siblings = siblings.map(|sibling| sibling.hash());
110
111        // Witness the selected child
112        let (child, leaf) = child.keep()?.witness(index)?;
113
114        Some((path::Node { siblings, child }, leaf))
115    }
116}
117
118impl<Child: GetHash + ForgetOwned + Clone> ForgetOwned for Node<Child> {
119    #[inline]
120    fn forget_owned(
121        self,
122        forgotten: Option<Forgotten>,
123        index: impl Into<u64>,
124    ) -> (Insert<Self>, bool) {
125        let index = index.into();
126
127        let [a, b, c, d]: [Insert<Child>; 4] = self.children.into();
128
129        // Which child should we be forgetting?
130        let (which_way, index) = WhichWay::at(Self::Height::HEIGHT, index);
131
132        // Recursively forget the appropriate child
133        let (children, was_forgotten) = match which_way {
134            WhichWay::Leftmost => {
135                let (a, forgotten) = match a {
136                    Insert::Keep(a) => a.forget_owned(forgotten, index),
137                    Insert::Hash(_) => (a, false),
138                };
139                ([a, b, c, d], forgotten)
140            }
141            WhichWay::Left => {
142                let (b, forgotten) = match b {
143                    Insert::Keep(b) => b.forget_owned(forgotten, index),
144                    Insert::Hash(_) => (b, false),
145                };
146                ([a, b, c, d], forgotten)
147            }
148            WhichWay::Right => {
149                let (c, forgotten) = match c {
150                    Insert::Keep(c) => c.forget_owned(forgotten, index),
151                    Insert::Hash(_) => (c, false),
152                };
153                ([a, b, c, d], forgotten)
154            }
155            WhichWay::Rightmost => {
156                let (d, forgotten) = match d {
157                    Insert::Keep(d) => d.forget_owned(forgotten, index),
158                    Insert::Hash(_) => (d, false),
159                };
160                ([a, b, c, d], forgotten)
161            }
162        };
163
164        // Reconstruct the node from the children, or else (if all the children are hashes) hash
165        // those hashes into a single node hash
166        let reconstructed = match Children::try_from(children) {
167            Ok(children) => {
168                let mut reconstructed = Self {
169                    children,
170                    hash: self.hash,
171                    forgotten: self.forgotten,
172                };
173                // If we forgot something, mark the location of the forgetting
174                if was_forgotten {
175                    if let Some(forgotten) = forgotten {
176                        reconstructed.forgotten[which_way] = forgotten.next();
177                    }
178                }
179                Insert::Keep(reconstructed)
180            }
181            Err(_) => Insert::Hash(self.hash),
182        };
183
184        (reconstructed, was_forgotten)
185    }
186}
187
188impl<Child: Clone> GetPosition for Node<Child> {
189    fn position(&self) -> Option<u64> {
190        None
191    }
192}
193
194impl<'tree, Child: Height + structure::Any<'tree> + Clone> structure::Any<'tree> for Node<Child> {
195    fn kind(&self) -> Kind {
196        Kind::Internal {
197            height: <Self as Height>::Height::HEIGHT,
198        }
199    }
200
201    fn forgotten(&self) -> Forgotten {
202        self.forgotten.iter().copied().max().unwrap_or_default()
203    }
204
205    fn children(&'tree self) -> Vec<HashOrNode<'tree>> {
206        self.forgotten
207            .iter()
208            .copied()
209            .zip(self.children.children())
210            .map(|(forgotten, child)| match child {
211                Insert::Keep(node) => HashOrNode::Node(node),
212                Insert::Hash(hash) => HashOrNode::Hash(HashedNode {
213                    hash,
214                    forgotten,
215                    height: <Child as Height>::Height::HEIGHT,
216                }),
217            })
218            .collect()
219    }
220}
221
222impl<Child: Height + OutOfOrderOwned + Clone> OutOfOrderOwned for Node<Child> {
223    fn uninitialized_out_of_order_insert_commitment_owned(
224        this: Insert<Self>,
225        index: u64,
226        commitment: StateCommitment,
227    ) -> Self {
228        let (which_way, index) = WhichWay::at(<Self as Height>::Height::HEIGHT, index);
229
230        let (hash, forgotten, mut children) = match this {
231            // If there's an extant node, extract its contents
232            Insert::Keep(Node {
233                hash,
234                forgotten,
235                children,
236            }) => (hash, forgotten, children.into()),
237            // If there's no node here yet, grab the hash and make up the contents of a new node,
238            // into which we will insert the commitment
239            Insert::Hash(hash) => (hash, [Forgotten::default(); 4], {
240                // Initially, all the children are the uninitialized hash; these will be filled in
241                // over time, and then those that aren't filled in will be set to the appropriate
242                // finalized hash
243                let u = || Insert::Hash(Hash::uninitialized());
244                [u(), u(), u(), u()]
245            }),
246        };
247
248        // Temporarily swap in an uninitialized hash at the child, so we can directly
249        // manipulate it as an owned object
250        let child = std::mem::replace(
251            &mut children[which_way],
252            Insert::Hash(Hash::uninitialized()),
253        );
254
255        // Set that same child back to the result of inserting the commitment
256        children[which_way] = Insert::Keep(
257            Child::uninitialized_out_of_order_insert_commitment_owned(child, index, commitment),
258        );
259
260        // Convert the children back into a `Children`, which will always succeed
261        // because we've guaranteed that we have at least one `Keep` child: the one we
262        // just created
263        let children = children.try_into().expect(
264            "adding a commitment to extant children always allows them to be reconstituted",
265        );
266
267        Node {
268            hash,
269            forgotten,
270            children,
271        }
272    }
273}
274
275impl<Child: GetHash + UncheckedSetHash + Clone> UncheckedSetHash for Node<Child> {
276    fn unchecked_set_hash(&mut self, index: u64, height: u8, hash: Hash) {
277        use std::cmp::Ordering::*;
278
279        match height.cmp(&Self::Height::HEIGHT) {
280            Greater => panic!("height too large when setting hash: {height}"),
281            // Set the hash here
282            Equal => self.hash = hash,
283            // Set the hash below
284            Less => {
285                let (which_way, index) = WhichWay::at(Self::Height::HEIGHT, index);
286                let (child, _) = which_way.pick(self.children.children_mut());
287                match child {
288                    InsertMut::Keep(child) => child.unchecked_set_hash(index, height, hash),
289                    InsertMut::Hash(child_hash) => {
290                        if <Child as Height>::Height::HEIGHT == height {
291                            *child_hash = hash
292                        }
293                    }
294                }
295            }
296        }
297    }
298
299    fn finish_initialize(&mut self) {
300        // Finish all the children
301        for child in self.children.children_mut() {
302            match child {
303                InsertMut::Keep(child) => child.finish_initialize(),
304                InsertMut::Hash(hash) => {
305                    if hash.is_uninitialized() {
306                        // If the hash is not initialized, we set it to the empty finalized hash
307                        *hash = Hash::one();
308                    }
309                }
310            }
311        }
312
313        // IMPORTANT: we *must* finish the children before computing the hash at this node, or else
314        // we will potentially be computing an invalid hash, since there might be invalid hashes in
315        // the children which haven't been resolved yet!
316
317        // Then, compute the hash at this node, if necessary
318        if self.hash.is_uninitialized() {
319            self.hash = self.children.hash();
320        }
321    }
322}
323
324#[cfg(test)]
325mod test {
326    #[test]
327    fn check_node_size() {
328        // Disabled due to spurious test failure.
329        // static_assertions::assert_eq_size!(Node<()>, [u8; 72]);
330    }
331}