1use blake2::{Blake2b512, Digest};
4use serde::{Deserialize, Serialize};
5
6use crate::{
7 DOMAIN_WALLET_STATE, EMPTY_SMT_ROOT,
8 ORCHARD_ACTIVATION_HEIGHT, GENESIS_EPOCH_HASH,
9 Result, ZyncError,
10};
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
14pub struct WalletStateCommitment(pub [u8; 32]);
15
16impl WalletStateCommitment {
17 pub fn as_bytes(&self) -> &[u8; 32] {
18 &self.0
19 }
20}
21
22impl AsRef<[u8]> for WalletStateCommitment {
23 fn as_ref(&self) -> &[u8] {
24 &self.0
25 }
26}
27
28#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct WalletState {
31 pub nullifier_set_root: [u8; 32],
33
34 pub owned_notes_root: [u8; 32],
36
37 pub note_tree_frontier: NoteFrontier,
41
42 pub block_height: u32,
44 pub block_hash: [u8; 32],
45}
46
47impl WalletState {
48 pub fn genesis() -> Self {
50 Self {
51 nullifier_set_root: EMPTY_SMT_ROOT,
52 owned_notes_root: EMPTY_SMT_ROOT,
53 note_tree_frontier: NoteFrontier::empty(),
54 block_height: ORCHARD_ACTIVATION_HEIGHT,
55 block_hash: [0u8; 32], }
57 }
58
59 pub fn genesis_testnet() -> Self {
61 Self {
62 block_height: crate::ORCHARD_ACTIVATION_HEIGHT_TESTNET,
63 ..Self::genesis()
64 }
65 }
66
67 pub fn commit(&self) -> WalletStateCommitment {
69 let mut hasher = Blake2b512::new();
70 hasher.update(DOMAIN_WALLET_STATE);
71 hasher.update(&self.nullifier_set_root);
72 hasher.update(&self.owned_notes_root);
73 hasher.update(&self.note_tree_frontier.root());
74 hasher.update(&self.block_height.to_le_bytes());
75 hasher.update(&self.block_hash);
76
77 let hash = hasher.finalize();
78 let mut commitment = [0u8; 32];
79 commitment.copy_from_slice(&hash[..32]);
80 WalletStateCommitment(commitment)
81 }
82
83 pub fn is_genesis(&self) -> bool {
85 self.nullifier_set_root == EMPTY_SMT_ROOT
86 && self.owned_notes_root == EMPTY_SMT_ROOT
87 }
88}
89
90#[derive(Debug, Clone, Serialize, Deserialize)]
94pub struct NoteFrontier {
95 pub position: u64,
97
98 pub frontier: [Option<[u8; 32]>; 32],
101}
102
103impl NoteFrontier {
104 pub fn empty() -> Self {
105 Self {
106 position: 0,
107 frontier: [None; 32],
108 }
109 }
110
111 pub fn root(&self) -> [u8; 32] {
113 if self.position == 0 {
114 return EMPTY_SMT_ROOT;
115 }
116
117 let mut current = [0u8; 32];
118 let mut have_current = false;
119
120 for (level, frontier_hash) in self.frontier.iter().enumerate() {
121 match (frontier_hash, have_current) {
122 (Some(left), false) => {
123 current = *left;
125 have_current = true;
126 }
127 (Some(left), true) => {
128 current = hash_merkle_node(level, left, ¤t);
130 }
131 (None, true) => {
132 current = hash_merkle_node(level, ¤t, &empty_hash(level));
134 }
135 (None, false) => {
136 }
138 }
139 }
140
141 if have_current {
142 current
143 } else {
144 EMPTY_SMT_ROOT
145 }
146 }
147
148 pub fn append(&mut self, leaf: [u8; 32]) -> u64 {
150 let pos = self.position;
151 self.position += 1;
152
153 let mut current = leaf;
154 for level in 0..32 {
155 if pos & (1 << level) == 0 {
156 self.frontier[level] = Some(current);
158 return pos;
159 } else {
160 let left = self.frontier[level].take().unwrap_or_else(|| empty_hash(level));
162 current = hash_merkle_node(level, &left, ¤t);
163 }
164 }
165
166 pos
167 }
168
169 pub fn position(&self) -> u64 {
171 self.position
172 }
173}
174
175fn hash_merkle_node(level: usize, left: &[u8; 32], right: &[u8; 32]) -> [u8; 32] {
177 let mut hasher = Blake2b512::new();
178 hasher.update(b"ZYNC_merkle_node");
179 hasher.update(&[level as u8]);
180 hasher.update(left);
181 hasher.update(right);
182
183 let hash = hasher.finalize();
184 let mut result = [0u8; 32];
185 result.copy_from_slice(&hash[..32]);
186 result
187}
188
189fn empty_hash(level: usize) -> [u8; 32] {
191 if level == 0 {
193 [0u8; 32]
194 } else {
195 let child = empty_hash(level - 1);
196 hash_merkle_node(level - 1, &child, &child)
197 }
198}
199
200pub mod smt {
202 use super::*;
203
204 pub fn insert(
206 root: [u8; 32],
207 key: &[u8; 32],
208 value: &[u8],
209 ) -> [u8; 32] {
210 let mut hasher = Blake2b512::new();
213 hasher.update(b"ZYNC_smt_insert");
214 hasher.update(&root);
215 hasher.update(key);
216 hasher.update(value);
217
218 let hash = hasher.finalize();
219 let mut new_root = [0u8; 32];
220 new_root.copy_from_slice(&hash[..32]);
221 new_root
222 }
223
224 pub fn verify_membership(
226 root: &[u8; 32],
227 key: &[u8; 32],
228 value: &[u8],
229 proof: &SmtProof,
230 ) -> bool {
231 !proof.siblings.is_empty()
234 }
235
236 #[derive(Debug, Clone, Serialize, Deserialize)]
238 pub struct SmtProof {
239 pub siblings: Vec<[u8; 32]>,
240 pub path_bits: Vec<bool>,
241 }
242}
243
244#[cfg(test)]
245mod tests {
246 use super::*;
247
248 #[test]
249 fn test_genesis_state() {
250 let state = WalletState::genesis();
251 assert!(state.is_genesis());
252 assert_eq!(state.block_height, ORCHARD_ACTIVATION_HEIGHT);
253 }
254
255 #[test]
256 fn test_state_commitment_deterministic() {
257 let state = WalletState::genesis();
258 let c1 = state.commit();
259 let c2 = state.commit();
260 assert_eq!(c1, c2);
261 }
262
263 #[test]
264 fn test_frontier_empty() {
265 let frontier = NoteFrontier::empty();
266 assert_eq!(frontier.position(), 0);
267 assert_eq!(frontier.root(), EMPTY_SMT_ROOT);
268 }
269
270 #[test]
271 fn test_frontier_append() {
272 let mut frontier = NoteFrontier::empty();
273
274 let leaf1 = [1u8; 32];
275 let pos1 = frontier.append(leaf1);
276 assert_eq!(pos1, 0);
277 assert_eq!(frontier.position(), 1);
278
279 let leaf2 = [2u8; 32];
280 let pos2 = frontier.append(leaf2);
281 assert_eq!(pos2, 1);
282 assert_eq!(frontier.position(), 2);
283
284 let root = frontier.root();
286 assert_ne!(root, EMPTY_SMT_ROOT);
287 }
288}