penumbra_sdk_tct/internal/frontier/
node.rs

1use std::{fmt::Debug, sync::Arc};
2
3use serde::{Deserialize, Serialize};
4
5use crate::prelude::*;
6
7/// A frontier of a node in a tree, into which items can be inserted.
8#[derive(Clone, Derivative, Serialize, Deserialize)]
9#[serde(bound(serialize = "Child: Serialize, Child::Complete: Serialize"))]
10#[serde(bound(deserialize = "Child: Deserialize<'de>, Child::Complete: Deserialize<'de>"))]
11#[derivative(Debug(bound = "Child: Debug, Child::Complete: Debug"))]
12pub struct Node<Child: Focus> {
13    #[derivative(PartialEq = "ignore", Debug)]
14    #[serde(skip)]
15    hash: CachedHash,
16    #[serde(skip)]
17    forgotten: [Forgotten; 4],
18    siblings: Three<Arc<Insert<Child::Complete>>>,
19    focus: Arc<Child>,
20}
21
22impl<Child: Focus> Node<Child> {
23    /// Construct a new node from parts.
24    pub(crate) fn from_parts(
25        forgotten: [Forgotten; 4],
26        siblings: Three<Arc<Insert<Child::Complete>>>,
27        focus: Child,
28    ) -> Self
29    where
30        Child: Frontier + GetHash,
31    {
32        Self {
33            hash: Default::default(),
34            forgotten,
35            siblings,
36            focus: Arc::new(focus),
37        }
38    }
39
40    /// Get the list of forgotten counts for the children of this node.
41    #[inline]
42    pub(crate) fn forgotten(&self) -> &[Forgotten; 4] {
43        &self.forgotten
44    }
45}
46
47impl<Child: Focus> Height for Node<Child> {
48    type Height = Succ<Child::Height>;
49}
50
51impl<Child: Focus> GetHash for Node<Child> {
52    fn hash(&self) -> Hash {
53        // Extract the hashes of an array of `Insert<T>`s.
54        fn hashes_of_all<T: GetHash, const N: usize>(full: [&Arc<Insert<T>>; N]) -> [Hash; N] {
55            full.map(|hash_or_t| match &**hash_or_t {
56                Insert::Hash(hash) => *hash,
57                Insert::Keep(t) => t.hash(),
58            })
59        }
60
61        self.hash.set_if_empty(|| {
62            // Get the four hashes of the node's siblings + focus, *in that order*, adding
63            // zero-padding when there are less than four elements
64            let zero = Hash::zero();
65            let focus = self.focus.hash();
66
67            let (a, b, c, d) = match self.siblings.elems() {
68                Elems::_0([]) => (focus, zero, zero, zero),
69                Elems::_1(full) => {
70                    let [a] = hashes_of_all(full);
71                    (a, focus, zero, zero)
72                }
73                Elems::_2(full) => {
74                    let [a, b] = hashes_of_all(full);
75                    (a, b, focus, zero)
76                }
77                Elems::_3(full) => {
78                    let [a, b, c] = hashes_of_all(full);
79                    (a, b, c, focus)
80                }
81            };
82
83            // Compute the hash of the node based on its height and the height of its children,
84            // and cache it in the node
85            Hash::node(<Self as Height>::Height::HEIGHT, a, b, c, d)
86        })
87    }
88
89    #[inline]
90    fn cached_hash(&self) -> Option<Hash> {
91        self.hash.get()
92    }
93
94    #[inline]
95    fn clear_cached_hash(&self) {
96        self.hash.clear();
97    }
98}
99
100impl<Child: Focus + Clone> Focus for Node<Child>
101where
102    Child::Complete: Clone,
103{
104    type Complete = complete::Node<Child::Complete>;
105
106    #[inline]
107    fn finalize_owned(self) -> Insert<Self::Complete> {
108        let one = || Insert::Hash(Hash::one());
109
110        // Avoid cloning the `Arc` when possible
111        fn get<T: Clone>(arc: Arc<T>) -> T {
112            Arc::try_unwrap(arc).unwrap_or_else(|arc| (*arc).clone())
113        }
114
115        let Self {
116            hash: _, // We ignore the hash because we're going to recompute it
117            forgotten,
118            siblings,
119            focus,
120        } = self;
121
122        // This avoids cloning the focus when we have the only reference to it
123        let focus = Arc::try_unwrap(focus).unwrap_or_else(|arc| (*arc).clone());
124
125        // Push the focus into the siblings, and fill any empty children with the *ONE* hash, which
126        // causes the hash of a complete node to deliberately differ from that of a frontier node,
127        // which uses *ZERO* padding
128        complete::Node::from_children_or_else_hash(
129            forgotten,
130            match siblings.push(Arc::new(focus.finalize_owned())) {
131                Err([a, b, c, d]) => [get(a), get(b), get(c), get(d)],
132                Ok(siblings) => match siblings.into_elems() {
133                    IntoElems::_3([a, b, c]) => [get(a), get(b), get(c), one()],
134                    IntoElems::_2([a, b]) => [get(a), get(b), one(), one()],
135                    IntoElems::_1([a]) => [get(a), one(), one(), one()],
136                    IntoElems::_0([]) => [one(), one(), one(), one()],
137                },
138            },
139        )
140    }
141}
142
143impl<Child: Clone> Frontier for Node<Child>
144where
145    Child: Focus + Frontier + GetHash,
146    Child::Complete: Clone,
147{
148    type Item = Child::Item;
149
150    #[inline]
151    fn new(item: Self::Item) -> Self {
152        let focus = Child::new(item);
153        let siblings = Three::new();
154        Self::from_parts(Default::default(), siblings, focus)
155    }
156
157    #[inline]
158    fn update<T>(&mut self, f: impl FnOnce(&mut Self::Item) -> T) -> Option<T> {
159        let before_hash = self.focus.cached_hash();
160        let output = Arc::make_mut(&mut self.focus).update(f);
161        let after_hash = self.focus.cached_hash();
162
163        // If the cached hash of the focus changed, clear the cached hash here, because it is now
164        // invalid and needs to be recalculated
165        if before_hash != after_hash {
166            self.hash = CachedHash::default();
167        }
168
169        output
170    }
171
172    #[inline]
173    fn focus(&self) -> Option<&Self::Item> {
174        self.focus.focus()
175    }
176
177    #[inline]
178    fn insert_owned(self, item: Self::Item) -> Result<Self, Full<Self>> {
179        let Self {
180            hash: _, // We ignore the cached hash because it changes on insertion
181            forgotten,
182            siblings,
183            focus,
184        } = self;
185
186        // This avoids cloning the focus when we have the only reference to it
187        let focus = Arc::try_unwrap(focus).unwrap_or_else(|arc| (*arc).clone());
188
189        match focus.insert_owned(item) {
190            // We successfully inserted at the focus, so siblings don't need to be changed
191            Ok(focus) => Ok(Self::from_parts(forgotten, siblings, focus)),
192
193            // We couldn't insert at the focus because it was full, so we need to move our path
194            // rightwards and insert into a newly created focus
195            Err(Full {
196                item,
197                complete: sibling,
198            }) => match siblings.push(Arc::new(sibling)) {
199                // We had enough room to add another sibling, so we set our focus to a new focus
200                // containing only the item we couldn't previously insert
201                Ok(siblings) => Ok(Self::from_parts(forgotten, siblings, Child::new(item))),
202
203                // We didn't have enough room to add another sibling, so we return a complete node
204                // as a carry, to be propagated up above us and added to some ancestor segment's
205                // siblings, along with the item we couldn't insert
206                Err(children) => Err(Full {
207                    item,
208                    complete: complete::Node::from_children_or_else_hash(
209                        forgotten,
210                        children
211                            // Avoid cloning the `Arc`s when possible
212                            .map(|arc| Arc::try_unwrap(arc).unwrap_or_else(|arc| (*arc).clone())),
213                    ),
214                }),
215            },
216        }
217    }
218
219    #[inline]
220    fn is_full(&self) -> bool {
221        self.siblings.is_full() && self.focus.is_full()
222    }
223}
224
225impl<Child: Focus + GetPosition> GetPosition for Node<Child> {
226    #[inline]
227    fn position(&self) -> Option<u64> {
228        let child_capacity: u64 = 4u64.pow(Child::Height::HEIGHT.into());
229        let siblings = self.siblings.len() as u64;
230
231        if let Some(focus_position) = self.focus.position() {
232            // next insertion would be at: siblings * 4^height + focus_position
233            // because we don't need to add a new child
234            Some(siblings * child_capacity + focus_position)
235        } else if siblings + 1 < 4
236        /* this means adding a new child is possible */
237        {
238            // next insertion would be at: (siblings + 1) * 4^height
239            // because we have to add a new child, and we can
240            Some((siblings + 1) * child_capacity)
241        } else {
242            None
243        }
244    }
245}
246
247impl<Child: Focus + Witness> Witness for Node<Child>
248where
249    Child::Complete: Witness,
250{
251    fn witness(&self, index: impl Into<u64>) -> Option<(AuthPath<Self>, Hash)> {
252        use Elems::*;
253        use WhichWay::*;
254
255        let index = index.into();
256
257        // The zero padding hash for frontier nodes
258        let zero = Hash::zero();
259
260        // Which direction should we go from this node?
261        let (which_way, index) = WhichWay::at(Self::Height::HEIGHT, index);
262
263        let (siblings, (child, leaf)) = match (self.siblings.elems(), &self.focus) {
264            // Zero siblings to the left
265            (_0([]), a) => match which_way {
266                Leftmost => (
267                    // All sibling hashes are default for the left, right, and rightmost
268                    [zero; 3],
269                    // Authentication path is to the leftmost child
270                    a.witness(index)?,
271                ),
272                Left | Right | Rightmost => return None,
273            },
274
275            // One sibling to the left
276            (_1([a]), b) => match which_way {
277                Leftmost => (
278                    // Sibling hashes are the left child and default for right and rightmost
279                    [b.hash(), zero, zero],
280                    // Authentication path is to the leftmost child
281                    (**a).as_ref().keep()?.witness(index)?,
282                ),
283                Left => (
284                    // Sibling hashes are the leftmost child and default for right and rightmost
285                    [a.hash(), zero, zero],
286                    // Authentication path is to the left child
287                    b.witness(index)?,
288                ),
289                Right | Rightmost => return None,
290            },
291
292            // Two siblings to the left
293            (_2([a, b]), c) => match which_way {
294                Leftmost => (
295                    // Sibling hashes are the left child and right child and default for rightmost
296                    [b.hash(), c.hash(), zero],
297                    // Authentication path is to the leftmost child
298                    (**a).as_ref().keep()?.witness(index)?,
299                ),
300                Left => (
301                    // Sibling hashes are the leftmost child and right child and default for rightmost
302                    [a.hash(), c.hash(), zero],
303                    // Authentication path is to the left child
304                    (**b).as_ref().keep()?.witness(index)?,
305                ),
306                Right => (
307                    // Sibling hashes are the leftmost child and left child and default for rightmost
308                    [a.hash(), b.hash(), zero],
309                    // Authentication path is to the right child
310                    c.witness(index)?,
311                ),
312                Rightmost => return None,
313            },
314
315            // Three siblings to the left
316            (_3([a, b, c]), d) => match which_way {
317                Leftmost => (
318                    // Sibling hashes are the left child, right child, and rightmost child
319                    [b.hash(), c.hash(), d.hash()],
320                    // Authentication path is to the leftmost child
321                    (**a).as_ref().keep()?.witness(index)?,
322                ),
323                Left => (
324                    // Sibling hashes are the leftmost child, right child, and rightmost child
325                    [a.hash(), c.hash(), d.hash()],
326                    // Authentication path is to the left child
327                    (**b).as_ref().keep()?.witness(index)?,
328                ),
329                Right => (
330                    // Sibling hashes are the leftmost child, left child, and rightmost child
331                    [a.hash(), b.hash(), d.hash()],
332                    // Authentication path is to the right child
333                    (**c).as_ref().keep()?.witness(index)?,
334                ),
335                Rightmost => (
336                    // Sibling hashes are the leftmost child, left child, and right child
337                    [a.hash(), b.hash(), c.hash()],
338                    // Authentication path is to the rightmost child
339                    d.witness(index)?,
340                ),
341            },
342        };
343
344        Some((path::Node { siblings, child }, leaf))
345    }
346}
347
348impl<Child: Focus + Forget + Clone> Forget for Node<Child>
349where
350    Child::Complete: ForgetOwned + Clone,
351{
352    fn forget(&mut self, forgotten: Option<Forgotten>, index: impl Into<u64>) -> bool {
353        use ElemsMut::*;
354        use WhichWay::*;
355
356        let index = index.into();
357
358        // Which direction should we forget from this node?
359        let (which_way, index) = WhichWay::at(Self::Height::HEIGHT, index);
360
361        let was_forgotten = match (self.siblings.elems_mut(), &mut self.focus) {
362            (_0([]), a) => match which_way {
363                Leftmost => Arc::make_mut(a).forget(forgotten, index),
364                Left | Right | Rightmost => false,
365            },
366            (_1([a]), b) => match which_way {
367                Leftmost => Arc::make_mut(a).forget(forgotten, index),
368                Left => Arc::make_mut(b).forget(forgotten, index),
369                Right | Rightmost => false,
370            },
371            (_2([a, b]), c) => match which_way {
372                Leftmost => Arc::make_mut(a).forget(forgotten, index),
373                Left => Arc::make_mut(b).forget(forgotten, index),
374                Right => Arc::make_mut(c).forget(forgotten, index),
375                Rightmost => false,
376            },
377            (_3([a, b, c]), d) => match which_way {
378                Leftmost => Arc::make_mut(a).forget(forgotten, index),
379                Left => Arc::make_mut(b).forget(forgotten, index),
380                Right => Arc::make_mut(c).forget(forgotten, index),
381                Rightmost => Arc::make_mut(d).forget(forgotten, index),
382            },
383        };
384
385        // If we forgot something, mark the location at which we forgot it
386        if was_forgotten {
387            if let Some(forgotten) = forgotten {
388                self.forgotten[which_way] = forgotten.next();
389            }
390        }
391
392        was_forgotten
393    }
394}
395
396impl<'tree, Child: Focus + GetPosition + Height + structure::Any<'tree>> structure::Any<'tree>
397    for Node<Child>
398where
399    Child::Complete: structure::Any<'tree>,
400{
401    fn kind(&self) -> Kind {
402        Kind::Internal {
403            height: <Self as Height>::Height::HEIGHT,
404        }
405    }
406
407    fn forgotten(&self) -> Forgotten {
408        self.forgotten().iter().copied().max().unwrap_or_default()
409    }
410
411    fn children(&'tree self) -> Vec<HashOrNode<'tree>> {
412        let children = self
413            .siblings
414            .iter()
415            .map(|child| (**child).as_ref().map(|child| child as &dyn structure::Any))
416            .chain(std::iter::once(Insert::Keep(
417                &*self.focus as &dyn structure::Any,
418            )));
419
420        self.forgotten
421            .iter()
422            .copied()
423            .zip(children)
424            .map(|(forgotten, child)| match child {
425                Insert::Keep(node) => HashOrNode::Node(node),
426                Insert::Hash(hash) => HashOrNode::Hash(HashedNode {
427                    hash,
428                    forgotten,
429                    height: <Child as Height>::Height::HEIGHT,
430                }),
431            })
432            .collect()
433    }
434}
435
436impl<Child: Height + Focus + OutOfOrder + Clone> OutOfOrder for Node<Child>
437where
438    Child::Complete: OutOfOrderOwned + Clone,
439{
440    fn uninitialized(position: Option<u64>, forgotten: Forgotten) -> Self {
441        // The number of siblings is the bits of the position at this node's height
442        let siblings_len = if let Some(position) = position {
443            // We subtract 1 from the position, because the position is 1 + the position of the
444            // latest commitment, and we want to know what the arity of this node is, not the
445            // arity it will have after adding something -- note that the position for a node will
446            // never be zero, because tiers and tops steal these cases
447            debug_assert!(position > 0, "position for frontier node is never zero");
448            let path_bits = position - 1;
449            (path_bits >> (Child::Height::HEIGHT * 2)) & 0b11
450        } else {
451            // When the position is `None`, we add all siblings, because the tree is entirely full
452            0b11
453        };
454
455        let mut siblings = Three::new();
456        for _ in 0..siblings_len {
457            siblings =
458                if let Ok(siblings) = siblings.push(Arc::new(Insert::Hash(Hash::uninitialized()))) {
459                    siblings
460                } else {
461                    unreachable!("for all x, 0b11 & x < 4, so siblings can't overflow")
462                }
463        }
464
465        let focus = Arc::new(Child::uninitialized(position, forgotten));
466        let hash = CachedHash::default();
467        let forgotten = [forgotten; 4];
468
469        Node {
470            siblings,
471            focus,
472            hash,
473            forgotten,
474        }
475    }
476
477    fn uninitialized_out_of_order_insert_commitment(
478        &mut self,
479        index: u64,
480        commitment: StateCommitment,
481    ) {
482        use ElemsMut::*;
483        use WhichWay::*;
484
485        let (which_way, index) = WhichWay::at(<Self as Height>::Height::HEIGHT, index);
486
487        // When we recur down into a sibling, we invoke the owned version of `insert_commitment`,
488        // and we need a little wrapper to handle the impedance mismatch between `&mut` and owned
489        // calling convention:
490        fn recur_sibling<Sibling>(
491            sibling: &mut Arc<Insert<Sibling>>,
492            index: u64,
493            commitment: StateCommitment,
494        ) where
495            Sibling: OutOfOrderOwned + Clone,
496        {
497            let sibling = Arc::make_mut(sibling);
498
499            *sibling = Insert::Keep(Sibling::uninitialized_out_of_order_insert_commitment_owned(
500                // Very temporarily swap out sibling for the uninitialized hash, so we can
501                // manipulate it as an owned value (we immediately put something legit back into it,
502                // in this very line)
503                std::mem::replace(sibling, Insert::Hash(Hash::uninitialized())),
504                index,
505                commitment,
506            ));
507        }
508
509        match (self.siblings.elems_mut(), &mut self.focus) {
510            (_0([]), a) => match which_way {
511                Leftmost => {
512                    Arc::make_mut(a).uninitialized_out_of_order_insert_commitment(index, commitment)
513                }
514                Left | Right | Rightmost => {}
515            },
516            (_1([a]), b) => match which_way {
517                Leftmost => recur_sibling(a, index, commitment),
518                Left => {
519                    Arc::make_mut(b).uninitialized_out_of_order_insert_commitment(index, commitment)
520                }
521                Right | Rightmost => {}
522            },
523            (_2([a, b]), c) => match which_way {
524                Leftmost => recur_sibling(a, index, commitment),
525                Left => recur_sibling(b, index, commitment),
526                Right => {
527                    Arc::make_mut(c).uninitialized_out_of_order_insert_commitment(index, commitment)
528                }
529                Rightmost => {}
530            },
531            (_3([a, b, c]), d) => match which_way {
532                Leftmost => recur_sibling(a, index, commitment),
533                Left => recur_sibling(b, index, commitment),
534                Right => recur_sibling(c, index, commitment),
535                Rightmost => {
536                    Arc::make_mut(d).uninitialized_out_of_order_insert_commitment(index, commitment)
537                }
538            },
539        }
540    }
541}
542
543impl<Child: Focus + UncheckedSetHash + Clone> UncheckedSetHash for Node<Child>
544where
545    Child::Complete: UncheckedSetHash + Clone,
546{
547    fn unchecked_set_hash(&mut self, index: u64, height: u8, hash: Hash) {
548        use std::cmp::Ordering::*;
549        use ElemsMut::*;
550        use WhichWay::*;
551
552        // For a sibling, which may be hashed, we need to handle both the possibility that it
553        // exists, or it is hashed
554        fn recur_sibling<T: Height + UncheckedSetHash + Clone>(
555            insert: &mut Arc<Insert<T>>,
556            index: u64,
557            height: u8,
558            hash: Hash,
559        ) {
560            match Arc::make_mut(insert) {
561                // Recur normally if the sibling exists
562                Insert::Keep(item) => item.unchecked_set_hash(index, height, hash),
563                // If the sibling is hashed and the height is right, set the hash there
564                Insert::Hash(this_hash) => {
565                    if height == <T as Height>::Height::HEIGHT {
566                        *this_hash = hash;
567                    }
568                }
569            };
570        }
571
572        match height.cmp(&Self::Height::HEIGHT) {
573            Greater => panic!("height too large when setting hash: {height}"),
574            // Set the hash here
575            Equal => self.hash = hash.into(),
576            // Set the hash below
577            Less => {
578                let (which_way, index) = WhichWay::at(Self::Height::HEIGHT, index);
579
580                match (self.siblings.elems_mut(), &mut self.focus) {
581                    (_0([]), a) => match which_way {
582                        Leftmost => Arc::make_mut(a).unchecked_set_hash(index, height, hash),
583                        Left | Right | Rightmost => {}
584                    },
585                    (_1([a]), b) => match which_way {
586                        Leftmost => recur_sibling(a, index, height, hash),
587                        Left => Arc::make_mut(b).unchecked_set_hash(index, height, hash),
588                        Right | Rightmost => {}
589                    },
590                    (_2([a, b]), c) => match which_way {
591                        Leftmost => recur_sibling(a, index, height, hash),
592                        Left => recur_sibling(b, index, height, hash),
593                        Right => Arc::make_mut(c).unchecked_set_hash(index, height, hash),
594                        Rightmost => {}
595                    },
596                    (_3([a, b, c]), d) => match which_way {
597                        Leftmost => recur_sibling(a, index, height, hash),
598                        Left => recur_sibling(b, index, height, hash),
599                        Right => recur_sibling(c, index, height, hash),
600                        Rightmost => Arc::make_mut(d).unchecked_set_hash(index, height, hash),
601                    },
602                }
603            }
604        }
605    }
606
607    fn finish_initialize(&mut self) {
608        // Finish the focus
609        Arc::make_mut(&mut self.focus).finish_initialize();
610
611        // Finish each of the siblings
612        for sibling in self.siblings.iter_mut() {
613            match Arc::make_mut(sibling) {
614                Insert::Keep(item) => item.finish_initialize(),
615                Insert::Hash(hash) => {
616                    if hash.is_uninitialized() {
617                        // Siblings are complete, so we finish them using `Hash::one()`
618                        *hash = Hash::one();
619                    }
620                }
621            }
622        }
623
624        // Unlike in the complete case, we don't need to touch the hash, because it's a
625        // `CachedHash`, so we've never set it to an uninitialized value; we've only ever touched it
626        // if we've set it to a real hash
627    }
628}