Skip to main content

sapling_crypto/
tree.rs

1use crate::once::OnceTable;
2use bitvec::{order::Lsb0, view::AsBits};
3use group::{Curve, ff::PrimeField};
4use incrementalmerkletree::{Hashable, Level};
5use subtle::CtOption;
6
7use alloc::vec::Vec;
8use core::fmt;
9
10use super::{
11    note::ExtractedNoteCommitment,
12    pedersen_hash::{Personalization, pedersen_hash},
13};
14
15pub const NOTE_COMMITMENT_TREE_DEPTH: u8 = 32;
16pub type CommitmentTree =
17    incrementalmerkletree::frontier::CommitmentTree<Node, NOTE_COMMITMENT_TREE_DEPTH>;
18pub type IncrementalWitness =
19    incrementalmerkletree::witness::IncrementalWitness<Node, NOTE_COMMITMENT_TREE_DEPTH>;
20pub type MerklePath = incrementalmerkletree::MerklePath<Node, NOTE_COMMITMENT_TREE_DEPTH>;
21
22fn uncommitted_sapling() -> bls12_381::Scalar {
23    bls12_381::Scalar::one()
24}
25
26static EMPTY_ROOTS: OnceTable<Vec<Node>> = OnceTable::new();
27
28fn empty_roots() -> &'static [Node] {
29    EMPTY_ROOTS.get_or_init(|| {
30        let mut v = vec![Node::empty_leaf()];
31        for d in 0..NOTE_COMMITMENT_TREE_DEPTH {
32            let next = Node::combine(d.into(), &v[usize::from(d)], &v[usize::from(d)]);
33            v.push(next);
34        }
35        v
36    })
37}
38
39/// Compute a parent node in the Sapling commitment tree given its two children.
40pub fn merkle_hash(depth: usize, lhs: &[u8; 32], rhs: &[u8; 32]) -> [u8; 32] {
41    merkle_hash_field(depth, lhs, rhs).to_repr()
42}
43
44fn merkle_hash_field(depth: usize, lhs: &[u8; 32], rhs: &[u8; 32]) -> jubjub::Base {
45    let lhs = {
46        let mut tmp = [false; 256];
47        for (a, b) in tmp.iter_mut().zip(lhs.as_bits::<Lsb0>()) {
48            *a = *b;
49        }
50        tmp
51    };
52
53    let rhs = {
54        let mut tmp = [false; 256];
55        for (a, b) in tmp.iter_mut().zip(rhs.as_bits::<Lsb0>()) {
56            *a = *b;
57        }
58        tmp
59    };
60
61    jubjub::ExtendedPoint::from(pedersen_hash(
62        Personalization::MerkleTree(depth),
63        lhs.iter()
64            .copied()
65            .take(bls12_381::Scalar::NUM_BITS as usize)
66            .chain(
67                rhs.iter()
68                    .copied()
69                    .take(bls12_381::Scalar::NUM_BITS as usize),
70            ),
71    ))
72    .to_affine()
73    .get_u()
74}
75
76/// The root of a Sapling commitment tree.
77#[derive(Eq, PartialEq, Clone, Copy, Debug)]
78pub struct Anchor(jubjub::Base);
79
80impl From<jubjub::Base> for Anchor {
81    fn from(anchor_field: jubjub::Base) -> Anchor {
82        Anchor(anchor_field)
83    }
84}
85
86impl From<Node> for Anchor {
87    fn from(anchor: Node) -> Anchor {
88        Anchor(anchor.0)
89    }
90}
91
92impl Anchor {
93    /// The anchor of the empty Sapling note commitment tree.
94    ///
95    /// This anchor does not correspond to any valid anchor for a spend, so it
96    /// may only be used for coinbase bundles or in circumstances where Sapling
97    /// functionality is not active.
98    pub fn empty_tree() -> Anchor {
99        Anchor(Node::empty_root(NOTE_COMMITMENT_TREE_DEPTH.into()).0)
100    }
101
102    #[cfg(feature = "wallet")]
103    pub(crate) fn inner(&self) -> jubjub::Base {
104        self.0
105    }
106
107    /// Parses a Sapling anchor from a byte encoding.
108    pub fn from_bytes(bytes: [u8; 32]) -> CtOption<Anchor> {
109        jubjub::Base::from_repr(bytes).map(Self)
110    }
111
112    /// Returns the byte encoding of this anchor.
113    pub fn to_bytes(self) -> [u8; 32] {
114        self.0.to_repr()
115    }
116}
117
118/// A node within the Sapling commitment tree.
119#[derive(Clone, Copy, PartialEq, Eq)]
120pub struct Node(jubjub::Base);
121
122impl fmt::Debug for Node {
123    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124        f.debug_struct("Node")
125            .field("repr", &hex::encode(self.0.to_bytes()))
126            .finish()
127    }
128}
129
130impl Node {
131    /// Creates a tree leaf from the given Sapling note commitment.
132    pub fn from_cmu(value: &ExtractedNoteCommitment) -> Self {
133        Node(value.inner())
134    }
135
136    /// Constructs a new note commitment tree node from a [`bls12_381::Scalar`]
137    pub fn from_scalar(cmu: bls12_381::Scalar) -> Self {
138        Self(cmu)
139    }
140
141    /// Parses a tree leaf from the bytes of a Sapling note commitment.
142    ///
143    /// Returns `None` if the provided bytes represent a non-canonical encoding.
144    pub fn from_bytes(bytes: [u8; 32]) -> CtOption<Self> {
145        jubjub::Base::from_repr(bytes).map(Self)
146    }
147
148    /// Returns the canonical byte representation of this node.
149    pub fn to_bytes(&self) -> [u8; 32] {
150        self.0.to_repr()
151    }
152
153    /// Returns the wrapped value
154    #[cfg(feature = "circuit")]
155    pub(crate) fn inner(&self) -> &jubjub::Base {
156        &self.0
157    }
158}
159
160impl Hashable for Node {
161    fn empty_leaf() -> Self {
162        Node(uncommitted_sapling())
163    }
164
165    fn combine(level: Level, lhs: &Self, rhs: &Self) -> Self {
166        Node(merkle_hash_field(
167            level.into(),
168            &lhs.0.to_bytes(),
169            &rhs.0.to_bytes(),
170        ))
171    }
172
173    fn empty_root(level: Level) -> Self {
174        empty_roots()[<usize>::from(level)]
175    }
176}
177
178impl From<Node> for bls12_381::Scalar {
179    fn from(node: Node) -> Self {
180        node.0
181    }
182}
183
184#[cfg(any(test, feature = "test-dependencies"))]
185pub(super) mod testing {
186    use ff::Field;
187    use proptest::prelude::*;
188    use rand::{
189        Rng as RandRng,
190        distr::{Distribution, StandardUniform},
191    };
192
193    use super::Node;
194    use crate::note::testing::arb_cmu;
195
196    prop_compose! {
197        pub fn arb_node()(cmu in arb_cmu()) -> Node {
198            Node::from_cmu(&cmu)
199        }
200    }
201
202    impl Node {
203        /// Return a random fake `MerkleHashOrchard`.
204        pub fn random(rng: &mut impl RandRng) -> Self {
205            StandardUniform.sample(rng)
206        }
207    }
208
209    impl Distribution<Node> for StandardUniform {
210        fn sample<R: RandRng + ?Sized>(&self, rng: &mut R) -> Node {
211            Node::from_scalar(bls12_381::Scalar::random(rng))
212        }
213    }
214}