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
39pub 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#[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 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 pub fn from_bytes(bytes: [u8; 32]) -> CtOption<Anchor> {
109 jubjub::Base::from_repr(bytes).map(Self)
110 }
111
112 pub fn to_bytes(self) -> [u8; 32] {
114 self.0.to_repr()
115 }
116}
117
118#[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 pub fn from_cmu(value: &ExtractedNoteCommitment) -> Self {
133 Node(value.inner())
134 }
135
136 pub fn from_scalar(cmu: bls12_381::Scalar) -> Self {
138 Self(cmu)
139 }
140
141 pub fn from_bytes(bytes: [u8; 32]) -> CtOption<Self> {
145 jubjub::Base::from_repr(bytes).map(Self)
146 }
147
148 pub fn to_bytes(&self) -> [u8; 32] {
150 self.0.to_repr()
151 }
152
153 #[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 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}