1use roshi_interface::access::{
4 access_merkle_leaf, access_merkle_node, EMPTY_ACCESS_MERKLE_ROOT, MAX_ACCESS_PROOF_LEN,
5};
6use solana_pubkey::Pubkey;
7
8#[derive(Clone, Debug, Eq, PartialEq)]
9pub struct AccessMerkleTree {
10 entries: Vec<AccessMerkleEntry>,
11 layers: Vec<Vec<[u8; 32]>>,
12}
13
14#[derive(Clone, Copy, Debug, Eq, PartialEq)]
15struct AccessMerkleEntry {
16 owner: Pubkey,
17 leaf: [u8; 32],
18}
19
20impl AccessMerkleTree {
21 pub fn new(owners: impl IntoIterator<Item = Pubkey>) -> Self {
22 let mut entries = owners
23 .into_iter()
24 .map(|owner| AccessMerkleEntry {
25 owner,
26 leaf: access_merkle_leaf(&owner),
27 })
28 .collect::<Vec<_>>();
29
30 entries.sort_by(|left, right| {
31 left.leaf
32 .cmp(&right.leaf)
33 .then_with(|| left.owner.to_bytes().cmp(&right.owner.to_bytes()))
34 });
35 entries.dedup_by(|left, right| left.owner == right.owner);
36
37 let mut layers = Vec::new();
38 if !entries.is_empty() {
39 layers.push(entries.iter().map(|entry| entry.leaf).collect::<Vec<_>>());
40
41 while layers.last().is_some_and(|layer| layer.len() > 1) {
42 let current = layers.last().expect("layer exists");
43 let mut next = Vec::with_capacity(current.len().div_ceil(2));
44
45 for pair in current.chunks(2) {
46 if let [left, right] = pair {
47 next.push(access_merkle_node(left, right));
48 } else {
49 next.push(pair[0]);
50 }
51 }
52
53 layers.push(next);
54 }
55 }
56
57 Self { entries, layers }
58 }
59
60 pub fn is_empty(&self) -> bool {
61 self.entries.is_empty()
62 }
63
64 pub fn root(&self) -> [u8; 32] {
65 self.layers
66 .last()
67 .and_then(|layer| layer.first())
68 .copied()
69 .unwrap_or(EMPTY_ACCESS_MERKLE_ROOT)
70 }
71
72 pub fn proof(&self, owner: &Pubkey) -> Option<Vec<[u8; 32]>> {
73 let mut index = self
74 .entries
75 .iter()
76 .position(|entry| entry.owner == *owner)?;
77 let mut proof = Vec::new();
78
79 for layer in &self.layers {
80 if layer.len() <= 1 {
81 break;
82 }
83
84 let sibling_index = if index % 2 == 0 {
85 index + 1
86 } else {
87 index.saturating_sub(1)
88 };
89
90 if let Some(sibling) = layer.get(sibling_index) {
91 proof.push(*sibling);
92 }
93
94 index /= 2;
95 }
96
97 if proof.len() <= MAX_ACCESS_PROOF_LEN {
98 Some(proof)
99 } else {
100 None
101 }
102 }
103
104 pub fn contains(&self, owner: &Pubkey) -> bool {
105 self.entries.iter().any(|entry| entry.owner == *owner)
106 }
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112 use roshi_interface::access::verify_access_merkle_proof;
113
114 #[test]
115 fn empty_tree_uses_closed_root_and_has_no_proofs() {
116 let tree = AccessMerkleTree::new(Vec::<Pubkey>::new());
117
118 assert!(tree.is_empty());
119 assert_eq!(tree.root(), EMPTY_ACCESS_MERKLE_ROOT);
120 assert_eq!(tree.proof(&Pubkey::new_unique()), None);
121 }
122
123 #[test]
124 fn builds_verifiable_proofs() {
125 let owners = [
126 Pubkey::new_unique(),
127 Pubkey::new_unique(),
128 Pubkey::new_unique(),
129 Pubkey::new_unique(),
130 Pubkey::new_unique(),
131 ];
132 let tree = AccessMerkleTree::new(owners);
133 let root = tree.root();
134
135 for owner in owners {
136 let proof = tree.proof(&owner).unwrap();
137 assert!(verify_access_merkle_proof(&owner, &root, &proof));
138 }
139 }
140
141 #[test]
142 fn root_is_stable_across_input_order() {
143 let owners = [
144 Pubkey::new_unique(),
145 Pubkey::new_unique(),
146 Pubkey::new_unique(),
147 Pubkey::new_unique(),
148 Pubkey::new_unique(),
149 ];
150 let mut reversed = owners;
151 reversed.reverse();
152
153 assert_eq!(
154 AccessMerkleTree::new(owners).root(),
155 AccessMerkleTree::new(reversed).root()
156 );
157 }
158
159 #[test]
160 fn duplicate_owners_do_not_change_root() {
161 let owner = Pubkey::new_unique();
162 let other = Pubkey::new_unique();
163
164 assert_eq!(
165 AccessMerkleTree::new([owner, other]).root(),
166 AccessMerkleTree::new([owner, other, owner]).root()
167 );
168 }
169
170 #[test]
171 fn missing_owner_has_no_proof() {
172 let tree = AccessMerkleTree::new([Pubkey::new_unique()]);
173
174 assert_eq!(tree.proof(&Pubkey::new_unique()), None);
175 }
176}