penumbra_tct/internal/
path.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
//! Authentication paths into the tree, generically for trees of any height.
//!
//! An authentication path of a tree is a sequence of triples of hashes equal in length to the
//! height of the tree.
//!
//! The interpretation of an authentication path is dependent on an _index_ into the tree, stored
//! separately, which indicates the position of the leaf witnessed by the authentication path.
//!
//! These are wrapped in more specific domain types by the exposed crate API to make it more
//! comprehensible.

use crate::prelude::*;

/// An authentication path into a `Tree`.
///
/// This is statically guaranteed to have the same length as the height of the tree.
pub type AuthPath<Tree> = <<Tree as Height>::Height as Path>::Path;

/// Identifies the unique type representing an authentication path for the given height.
pub trait Path: IsHeight + Sized {
    /// The authentication path for this height.
    type Path;

    /// Calculate the root hash for a path leading to a leaf with the given index and hash.
    fn root(path: &Self::Path, index: u64, leaf: Hash) -> Hash;
}

/// The empty authentication path, for the zero-height tree.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
pub struct Leaf;

impl Path for Zero {
    type Path = Leaf;

    #[inline]
    fn root(Leaf: &Leaf, _index: u64, leaf: Hash) -> Hash {
        leaf
    }
}

/// The authentication path for a node, whose height is always at least 1.
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct Node<Child> {
    /// The sibling hashes of the child.
    ///
    /// Note that this does not record which child is witnessed; that information lies in the index
    /// of the leaf.
    pub siblings: [Hash; 3],

    /// The authentication path for the witnessed child.
    pub child: Child,
}

impl<Child, N: Path<Path = Child>> Path for Succ<N> {
    type Path = Node<Child>;

    #[inline]
    fn root(Node { siblings, child }: &Node<Child>, index: u64, leaf: Hash) -> Hash {
        // Based on the index, place the root hash of the child in the correct position among its
        // sibling hashes, so that we can hash this node
        let which_way = WhichWay::at(Self::HEIGHT, index).0;
        let [leftmost, left, right, rightmost] =
            which_way.insert(N::root(child, index, leaf), *siblings);

        // Get the hash of this node at its correct height
        Hash::node(Self::HEIGHT, leftmost, left, right, rightmost)
    }
}

/// An enumeration of the different ways a path can go down a quadtree.
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum WhichWay {
    /// The leftmost (0th) child.
    Leftmost,
    /// The left (1st) child.
    Left,
    /// The right (2nd) child.
    Right,
    /// The rightmost (3rd) child.
    Rightmost,
}

impl WhichWay {
    /// Given a height and an index of a leaf, determine which direction the path down to that leaf
    /// should branch at the node at that height.
    #[inline]
    pub fn at(height: u8, index: u64) -> (WhichWay, u64) {
        // Shift the index right by (2 * (height - 1)) so that the last 2 bits are our direction, then
        // mask off just those bits and branch on them to generate the output
        let which_way = match (index >> (2 * (height - 1))) & 0b11 {
            0 => WhichWay::Leftmost,
            1 => WhichWay::Left,
            2 => WhichWay::Right,
            3 => WhichWay::Rightmost,
            _ => unreachable!(),
        };

        // The index into the child: mask off the bits we just used to determine the direction
        let index = index & !(0b11 << ((height - 1) * 2));

        (which_way, index)
    }

    /// Given a 3-element array, insert an item into the array in the place indicated by the [`WhichWay`].
    ///
    /// This is the inverse of [`WhichWay::pick`].
    #[inline]
    pub fn insert<T>(&self, item: T, siblings: [T; 3]) -> [T; 4] {
        use WhichWay::*;

        let (
            (Leftmost,  leftmost,  [/* leftmost, */ left,    right,    rightmost   ]) |
            (Left,      left,      [   leftmost, /* left, */ right,    rightmost   ]) |
            (Right,     right,     [   leftmost,    left, /* right, */ rightmost   ]) |
            (Rightmost, rightmost, [   leftmost,    left,    right, /* rightmost */])
        ) = (self, item, siblings);

        [leftmost, left, right, rightmost]
    }

    /// Given a 4-element array, pick out the item in the array indicated by the [`WhichWay`], and
    /// pair it with all the others, in the order they occurred.
    ///
    /// This is the inverse of [`WhichWay::insert`].
    #[inline]
    pub fn pick<T>(&self, siblings: [T; 4]) -> (T, [T; 3]) {
        use WhichWay::*;

        let ((Leftmost, [picked, a, b, c])
        | (Left, [a, picked, b, c])
        | (Right, [a, b, picked, c])
        | (Rightmost, [a, b, c, picked])) = (self, siblings);

        (picked, [a, b, c])
    }
}

impl<T> Index<WhichWay> for [T; 4] {
    type Output = T;

    fn index(&self, index: WhichWay) -> &T {
        match index {
            WhichWay::Leftmost => &self[0],
            WhichWay::Left => &self[1],
            WhichWay::Right => &self[2],
            WhichWay::Rightmost => &self[3],
        }
    }
}

impl<T> IndexMut<WhichWay> for [T; 4] {
    fn index_mut(&mut self, index: WhichWay) -> &mut T {
        match index {
            WhichWay::Leftmost => &mut self[0],
            WhichWay::Left => &mut self[1],
            WhichWay::Right => &mut self[2],
            WhichWay::Rightmost => &mut self[3],
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use proptest::prelude::*;

    /// Get directions from the root (at the given height)
    fn directions_of_index(height: u8, index: u64) -> Vec<WhichWay> {
        (1..=height)
            .rev() // iterate from the root to the leaf (height down to 1)
            .map(|height| WhichWay::at(height, index).0)
            .collect()
    }

    /// Get a sequence of indices representing the index of the originally specified leaf from the
    /// starting height down to zero.
    fn directions_via_indices(height: u8, index: u64) -> Vec<WhichWay> {
        (1..=height)
            .rev() // iterate from the leaf to the root (height down to 1)
            .scan(index, |index, height| {
                let (which_way, next_index) = WhichWay::at(height, *index);
                *index = next_index;
                Some(which_way)
            })
            .collect()
    }

    #[test]
    fn directions_of_index_check() {
        assert_eq!(directions_of_index(1, 0), &[WhichWay::Leftmost]);
        assert_eq!(directions_of_index(1, 1), &[WhichWay::Left]);
        assert_eq!(directions_of_index(1, 2), &[WhichWay::Right]);
        assert_eq!(directions_of_index(1, 3), &[WhichWay::Rightmost]);
    }

    /// Get the index which represents the given sequence of directions.
    fn index_of_directions(directions: &[WhichWay]) -> u64 {
        directions
            .iter()
            .rev() // Iterating rom the leaf to the root...
            .zip(1..) // Keeping track of the height (starting at 1 for the leafmost node)...
            .fold(0, |index, (&direction, height)| {
                index | // Set the bits in the index...
                (direction as u64) << (2 * (height - 1)) // ...which correspond to the direction at the height - 1.
            })
    }

    proptest! {
        #[test]
        fn which_way_indices_correct(
            (height, index) in (
                // This is a dependent generator: we ensure that the index is in-bounds for the height
                (0u8..(3 * 8)), 0u64..u64::MAX).prop_map(|(height, index)| (height, (index % (4u64.pow(height as u32))))
            )
        ) {
            assert_eq!(directions_of_index(height, index), directions_via_indices(height, index));
        }

        #[test]
        fn which_way_direction_correct(
            (height, index) in (
                // This is a dependent generator: we ensure that the index is in-bounds for the height
                (0u8..(3 * 8)), 0u64..u64::MAX).prop_map(|(height, index)| (height, (index % (4u64.pow(height as u32)))))
        ) {
            assert_eq!(index, index_of_directions(&directions_of_index(height, index)));
        }
    }
}

// All the below is just for serialization to/from protobufs:

/// When deserializing an authentication path, it was malformed.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Error)]
#[error("could not decode authentication path")]
pub struct PathDecodeError;

use decaf377::Fq;
use penumbra_proto::penumbra::crypto::tct::v1 as pb;
use std::{
    collections::VecDeque,
    ops::{Index, IndexMut},
};

impl From<Leaf> for VecDeque<pb::MerklePathChunk> {
    fn from(Leaf: Leaf) -> VecDeque<pb::MerklePathChunk> {
        VecDeque::new()
    }
}

impl From<Leaf> for Vec<pb::MerklePathChunk> {
    fn from(Leaf: Leaf) -> Vec<pb::MerklePathChunk> {
        Vec::new()
    }
}

impl TryFrom<VecDeque<pb::MerklePathChunk>> for Leaf {
    type Error = PathDecodeError;

    fn try_from(queue: VecDeque<pb::MerklePathChunk>) -> Result<Leaf, Self::Error> {
        if queue.is_empty() {
            Ok(Leaf)
        } else {
            Err(PathDecodeError)
        }
    }
}

impl TryFrom<Vec<pb::MerklePathChunk>> for Leaf {
    type Error = PathDecodeError;

    fn try_from(vec: Vec<pb::MerklePathChunk>) -> Result<Leaf, Self::Error> {
        if vec.is_empty() {
            Ok(Leaf)
        } else {
            Err(PathDecodeError)
        }
    }
}

// To create `Vec<pb::MerklePathChunk>`, we have a recursive impl for `VecDeque` which we delegate
// to, then finally turn into a `Vec` at the end.
impl<Child> From<Node<Child>> for VecDeque<pb::MerklePathChunk>
where
    VecDeque<pb::MerklePathChunk>: From<Child>,
{
    fn from(node: Node<Child>) -> VecDeque<pb::MerklePathChunk> {
        let [sibling_1, sibling_2, sibling_3] =
            node.siblings.map(|hash| Fq::from(hash).to_bytes().to_vec());
        let mut path: VecDeque<pb::MerklePathChunk> = node.child.into();
        path.push_front(pb::MerklePathChunk {
            sibling_1,
            sibling_2,
            sibling_3,
        });
        path
    }
}

impl<Child> From<Node<Child>> for Vec<pb::MerklePathChunk>
where
    VecDeque<pb::MerklePathChunk>: From<Child>,
{
    fn from(node: Node<Child>) -> Vec<pb::MerklePathChunk> {
        let [sibling_1, sibling_2, sibling_3] =
            node.siblings.map(|hash| Fq::from(hash).to_bytes().to_vec());
        let mut path = VecDeque::from(node.child);
        path.push_front(pb::MerklePathChunk {
            sibling_1,
            sibling_2,
            sibling_3,
        });
        path.into()
    }
}

// To create `Node<Child>`, we have a recursive impl for `VecDeque` which we delegate to, then
// finally turn into a `Vec` at the end.
impl<Child> TryFrom<VecDeque<pb::MerklePathChunk>> for Node<Child>
where
    Child: TryFrom<VecDeque<pb::MerklePathChunk>, Error = PathDecodeError>,
{
    type Error = PathDecodeError;

    fn try_from(mut queue: VecDeque<pb::MerklePathChunk>) -> Result<Node<Child>, Self::Error> {
        if let Some(pb::MerklePathChunk {
            sibling_1,
            sibling_2,
            sibling_3,
        }) = queue.pop_front()
        {
            let child = Child::try_from(queue)?;
            Ok(Node {
                siblings: [
                    Hash::new(
                        Fq::from_bytes_checked(&sibling_1.try_into().map_err(|_| PathDecodeError)?)
                            .map_err(|_| PathDecodeError)?,
                    ),
                    Hash::new(
                        Fq::from_bytes_checked(&sibling_2.try_into().map_err(|_| PathDecodeError)?)
                            .map_err(|_| PathDecodeError)?,
                    ),
                    Hash::new(
                        Fq::from_bytes_checked(&sibling_3.try_into().map_err(|_| PathDecodeError)?)
                            .map_err(|_| PathDecodeError)?,
                    ),
                ],
                child,
            })
        } else {
            Err(PathDecodeError)
        }
    }
}

impl<Child> TryFrom<Vec<pb::MerklePathChunk>> for Node<Child>
where
    Node<Child>: TryFrom<VecDeque<pb::MerklePathChunk>>,
{
    type Error = <Node<Child> as TryFrom<VecDeque<pb::MerklePathChunk>>>::Error;

    fn try_from(queue: Vec<pb::MerklePathChunk>) -> Result<Node<Child>, Self::Error> {
        <Node<Child>>::try_from(VecDeque::from(queue))
    }
}