mpl_account_compression/state/
concurrent_merkle_tree_header.rs

1use anchor_lang::prelude::*;
2use borsh::{BorshDeserialize, BorshSerialize};
3
4use spl_concurrent_merkle_tree::concurrent_merkle_tree::ConcurrentMerkleTree;
5use std::mem::size_of;
6
7use crate::error::AccountCompressionError;
8
9pub const CONCURRENT_MERKLE_TREE_HEADER_SIZE_V1: usize = 2 + 54;
10
11#[derive(Debug, Copy, Clone, PartialEq, BorshDeserialize, BorshSerialize)]
12#[repr(u8)]
13pub enum CompressionAccountType {
14    /// Uninitialized
15    Uninitialized,
16
17    /// SPL ConcurrentMerkleTree data structure, may include a Canopy
18    ConcurrentMerkleTree,
19}
20
21impl std::fmt::Display for CompressionAccountType {
22    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
23        write!(f, "{:?}", &self)
24    }
25}
26
27#[cfg(feature = "idl-build")]
28impl anchor_lang::IdlBuild for CompressionAccountType {}
29
30/// Initialization parameters for an SPL ConcurrentMerkleTree.
31///
32/// Only the following permutations are valid:
33///
34/// | max_depth | max_buffer_size       |
35/// | --------- | --------------------- |
36/// | 14        | (64, 256, 1024, 2048) |           
37/// | 20        | (64, 256, 1024, 2048) |           
38/// | 24        | (64, 256, 512, 1024, 2048) |           
39/// | 26        | (64, 256, 512, 1024, 2048) |           
40/// | 30        | (512, 1024, 2048) |           
41///
42#[repr(C)]
43#[derive(AnchorDeserialize, AnchorSerialize)]
44pub struct ConcurrentMerkleTreeHeader {
45    /// Account type
46    pub account_type: CompressionAccountType,
47    /// Versioned header
48    pub header: ConcurrentMerkleTreeHeaderData,
49}
50
51#[repr(C)]
52#[derive(AnchorDeserialize, AnchorSerialize)]
53pub struct ConcurrentMerkleTreeHeaderDataV1 {
54    /// Buffer of changelogs stored on-chain.
55    /// Must be a power of 2; see above table for valid combinations.
56    max_buffer_size: u32,
57
58    /// Depth of the SPL ConcurrentMerkleTree to store.
59    /// Tree capacity can be calculated as power(2, max_depth).
60    /// See above table for valid options.
61    max_depth: u32,
62
63    /// Authority that validates the content of the trees.
64    /// Typically a program, e.g., the Bubblegum contract validates that leaves are valid NFTs.
65    authority: Pubkey,
66
67    /// Slot corresponding to when the Merkle tree was created.
68    /// Provides a lower-bound on what slot to start (re-)building a tree from.
69    creation_slot: u64,
70
71    /// Needs padding for the account to be 8-byte aligned
72    /// 8-byte alignment is necessary to zero-copy the SPL ConcurrentMerkleTree
73    _padding: [u8; 6],
74}
75
76#[repr(C)]
77#[derive(AnchorDeserialize, AnchorSerialize)]
78pub enum ConcurrentMerkleTreeHeaderData {
79    V1(ConcurrentMerkleTreeHeaderDataV1),
80}
81
82impl ConcurrentMerkleTreeHeader {
83    pub fn initialize(
84        &mut self,
85        max_depth: u32,
86        max_buffer_size: u32,
87        authority: &Pubkey,
88        creation_slot: u64,
89    ) {
90        self.account_type = CompressionAccountType::ConcurrentMerkleTree;
91
92        match self.header {
93            ConcurrentMerkleTreeHeaderData::V1(ref mut header) => {
94                // Double check header is empty after deserialization from zero'd bytes
95                assert_eq!(header.max_buffer_size, 0);
96                assert_eq!(header.max_depth, 0);
97                header.max_buffer_size = max_buffer_size;
98                header.max_depth = max_depth;
99                header.authority = *authority;
100                header.creation_slot = creation_slot;
101            }
102        }
103    }
104
105    pub fn get_max_depth(&self) -> u32 {
106        match &self.header {
107            ConcurrentMerkleTreeHeaderData::V1(header) => header.max_depth,
108        }
109    }
110
111    pub fn get_max_buffer_size(&self) -> u32 {
112        match &self.header {
113            ConcurrentMerkleTreeHeaderData::V1(header) => header.max_buffer_size,
114        }
115    }
116
117    pub fn get_creation_slot(&self) -> u64 {
118        match &self.header {
119            ConcurrentMerkleTreeHeaderData::V1(header) => header.creation_slot,
120        }
121    }
122
123    pub fn set_new_authority(&mut self, new_authority: &Pubkey) {
124        match self.header {
125            ConcurrentMerkleTreeHeaderData::V1(ref mut header) => {
126                header.authority = new_authority.clone();
127                msg!("Authority transferred to: {:?}", header.authority);
128            }
129        }
130    }
131
132    pub fn assert_valid(&self) -> Result<()> {
133        require_eq!(
134            self.account_type,
135            CompressionAccountType::ConcurrentMerkleTree,
136            AccountCompressionError::IncorrectAccountType,
137        );
138        Ok(())
139    }
140
141    pub fn assert_valid_authority(&self, expected_authority: &Pubkey) -> Result<()> {
142        self.assert_valid()?;
143        match &self.header {
144            ConcurrentMerkleTreeHeaderData::V1(header) => {
145                require_eq!(
146                    header.authority,
147                    *expected_authority,
148                    AccountCompressionError::IncorrectAuthority,
149                );
150            }
151        }
152        Ok(())
153    }
154
155    pub fn assert_valid_leaf_index(&self, leaf_index: u32) -> Result<()> {
156        if leaf_index >= (1 << self.get_max_depth()) {
157            return Err(AccountCompressionError::LeafIndexOutOfBounds.into());
158        }
159        Ok(())
160    }
161}
162
163pub fn merkle_tree_get_size(header: &ConcurrentMerkleTreeHeader) -> Result<usize> {
164    // Note: max_buffer_size MUST be a power of 2
165    match (header.get_max_depth(), header.get_max_buffer_size()) {
166        (3, 8) => Ok(size_of::<ConcurrentMerkleTree<3, 8>>()),
167        (5, 8) => Ok(size_of::<ConcurrentMerkleTree<5, 8>>()),
168        (6, 16) => Ok(size_of::<ConcurrentMerkleTree<6, 16>>()),
169        (7, 16) => Ok(size_of::<ConcurrentMerkleTree<7, 16>>()),
170        (8, 16) => Ok(size_of::<ConcurrentMerkleTree<8, 16>>()),
171        (9, 16) => Ok(size_of::<ConcurrentMerkleTree<9, 16>>()),
172        (10, 32) => Ok(size_of::<ConcurrentMerkleTree<10, 32>>()),
173        (11, 32) => Ok(size_of::<ConcurrentMerkleTree<11, 32>>()),
174        (12, 32) => Ok(size_of::<ConcurrentMerkleTree<12, 32>>()),
175        (13, 32) => Ok(size_of::<ConcurrentMerkleTree<13, 32>>()),
176        (14, 64) => Ok(size_of::<ConcurrentMerkleTree<14, 64>>()),
177        (14, 256) => Ok(size_of::<ConcurrentMerkleTree<14, 256>>()),
178        (14, 1024) => Ok(size_of::<ConcurrentMerkleTree<14, 1024>>()),
179        (14, 2048) => Ok(size_of::<ConcurrentMerkleTree<14, 2048>>()),
180        (15, 64) => Ok(size_of::<ConcurrentMerkleTree<15, 64>>()),
181        (16, 64) => Ok(size_of::<ConcurrentMerkleTree<16, 64>>()),
182        (17, 64) => Ok(size_of::<ConcurrentMerkleTree<17, 64>>()),
183        (18, 64) => Ok(size_of::<ConcurrentMerkleTree<18, 64>>()),
184        (19, 64) => Ok(size_of::<ConcurrentMerkleTree<19, 64>>()),
185        (20, 64) => Ok(size_of::<ConcurrentMerkleTree<20, 64>>()),
186        (20, 256) => Ok(size_of::<ConcurrentMerkleTree<20, 256>>()),
187        (20, 1024) => Ok(size_of::<ConcurrentMerkleTree<20, 1024>>()),
188        (20, 2048) => Ok(size_of::<ConcurrentMerkleTree<20, 2048>>()),
189        (24, 64) => Ok(size_of::<ConcurrentMerkleTree<24, 64>>()),
190        (24, 256) => Ok(size_of::<ConcurrentMerkleTree<24, 256>>()),
191        (24, 512) => Ok(size_of::<ConcurrentMerkleTree<24, 512>>()),
192        (24, 1024) => Ok(size_of::<ConcurrentMerkleTree<24, 1024>>()),
193        (24, 2048) => Ok(size_of::<ConcurrentMerkleTree<24, 2048>>()),
194        (26, 512) => Ok(size_of::<ConcurrentMerkleTree<26, 512>>()),
195        (26, 1024) => Ok(size_of::<ConcurrentMerkleTree<26, 1024>>()),
196        (26, 2048) => Ok(size_of::<ConcurrentMerkleTree<26, 2048>>()),
197        (30, 512) => Ok(size_of::<ConcurrentMerkleTree<30, 512>>()),
198        (30, 1024) => Ok(size_of::<ConcurrentMerkleTree<30, 1024>>()),
199        (30, 2048) => Ok(size_of::<ConcurrentMerkleTree<30, 2048>>()),
200        _ => {
201            msg!(
202                "Failed to get size of max depth {} and max buffer size {}",
203                header.get_max_depth(),
204                header.get_max_buffer_size()
205            );
206            err!(AccountCompressionError::ConcurrentMerkleTreeConstantsError)
207        }
208    }
209}