Skip to main content

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