Skip to main content

roshi_interface/
access.rs

1//! Vault access-list helpers.
2
3use solana_pubkey::Pubkey;
4use solana_sha256_hasher::hashv;
5
6pub const ACCESS_LEAF_DOMAIN: &[u8] = b"roshi:vault-access:leaf:v1";
7pub const ACCESS_NODE_DOMAIN: &[u8] = b"roshi:vault-access:node:v1";
8pub const EMPTY_ACCESS_MERKLE_ROOT: [u8; 32] = [0; 32];
9pub const MAX_ACCESS_PROOF_LEN: usize = 32;
10
11pub fn access_merkle_leaf(owner: &Pubkey) -> [u8; 32] {
12    hashv(&[ACCESS_LEAF_DOMAIN, owner.as_ref()]).to_bytes()
13}
14
15pub fn access_merkle_node(left: &[u8; 32], right: &[u8; 32]) -> [u8; 32] {
16    let (first, second) = if left <= right {
17        (left.as_slice(), right.as_slice())
18    } else {
19        (right.as_slice(), left.as_slice())
20    };
21
22    hashv(&[ACCESS_NODE_DOMAIN, first, second]).to_bytes()
23}
24
25pub fn verify_access_merkle_proof(
26    owner: &Pubkey,
27    merkle_root: &[u8; 32],
28    proof: &[[u8; 32]],
29) -> bool {
30    if proof.len() > MAX_ACCESS_PROOF_LEN {
31        return false;
32    }
33
34    let mut node = access_merkle_leaf(owner);
35
36    for sibling in proof {
37        node = access_merkle_node(&node, sibling);
38    }
39
40    &node == merkle_root
41}
42
43#[cfg(test)]
44mod tests {
45    use super::*;
46
47    #[test]
48    fn verifies_single_leaf_root() {
49        let owner = Pubkey::new_unique();
50        let root = access_merkle_leaf(&owner);
51
52        assert!(verify_access_merkle_proof(&owner, &root, &[]));
53    }
54
55    #[test]
56    fn verifies_directionless_two_leaf_tree() {
57        let left = Pubkey::new_unique();
58        let right = Pubkey::new_unique();
59        let left_leaf = access_merkle_leaf(&left);
60        let right_leaf = access_merkle_leaf(&right);
61        let root = access_merkle_node(&left_leaf, &right_leaf);
62
63        assert!(verify_access_merkle_proof(&left, &root, &[right_leaf]));
64        assert!(verify_access_merkle_proof(&right, &root, &[left_leaf]));
65    }
66
67    #[test]
68    fn verifies_four_leaf_tree() {
69        let owners = [
70            Pubkey::new_unique(),
71            Pubkey::new_unique(),
72            Pubkey::new_unique(),
73            Pubkey::new_unique(),
74        ];
75        let leaves = owners.map(|owner| access_merkle_leaf(&owner));
76        let left_node = access_merkle_node(&leaves[0], &leaves[1]);
77        let right_node = access_merkle_node(&leaves[2], &leaves[3]);
78        let root = access_merkle_node(&left_node, &right_node);
79
80        assert!(verify_access_merkle_proof(
81            &owners[2],
82            &root,
83            &[leaves[3], left_node],
84        ));
85        assert!(verify_access_merkle_proof(
86            &owners[0],
87            &root,
88            &[leaves[1], right_node],
89        ));
90    }
91
92    #[test]
93    fn rejects_wrong_owner_or_proof() {
94        let owner = Pubkey::new_unique();
95        let other = Pubkey::new_unique();
96        let sibling = access_merkle_leaf(&Pubkey::new_unique());
97        let root = access_merkle_node(&access_merkle_leaf(&owner), &sibling);
98
99        assert!(!verify_access_merkle_proof(&other, &root, &[sibling]));
100        assert!(!verify_access_merkle_proof(&owner, &root, &[]));
101    }
102
103    #[test]
104    fn rejects_oversized_proof() {
105        let owner = Pubkey::new_unique();
106        let root = access_merkle_leaf(&owner);
107        let oversized_proof = vec![[0; 32]; MAX_ACCESS_PROOF_LEN + 1];
108
109        assert!(!verify_access_merkle_proof(&owner, &root, &oversized_proof));
110    }
111}