Skip to main content

light_merkle_tree_metadata/
queue.rs

1use bytemuck::{Pod, Zeroable};
2use light_compressed_account::{pubkey::Pubkey, QueueType};
3use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
4
5use crate::{
6    access::AccessMetadata, errors::MerkleTreeMetadataError, rollover::RolloverMetadata,
7    AnchorDeserialize, AnchorSerialize,
8};
9
10#[repr(C)]
11#[derive(
12    AnchorDeserialize,
13    AnchorSerialize,
14    Debug,
15    PartialEq,
16    Default,
17    Pod,
18    Zeroable,
19    Clone,
20    Copy,
21    Immutable,
22    FromBytes,
23    IntoBytes,
24    KnownLayout,
25)]
26pub struct QueueMetadata {
27    pub access_metadata: AccessMetadata,
28    pub rollover_metadata: RolloverMetadata,
29    // Queue associated with this Merkle tree.
30    pub associated_merkle_tree: Pubkey,
31    // Next queue to be used after rollover.
32    pub next_queue: Pubkey,
33    pub queue_type: u64,
34}
35
36pub fn check_queue_type(
37    queue_type: &u64,
38    expected_queue_type: &QueueType,
39) -> Result<(), MerkleTreeMetadataError> {
40    if *queue_type != (*expected_queue_type) as u64 {
41        Err(MerkleTreeMetadataError::InvalidQueueType)
42    } else {
43        Ok(())
44    }
45}
46
47impl QueueMetadata {
48    pub fn init(
49        &mut self,
50        access_metadata: AccessMetadata,
51        rollover_metadata: RolloverMetadata,
52        associated_merkle_tree: Pubkey,
53        queue_type: QueueType,
54    ) {
55        self.access_metadata = access_metadata;
56        self.rollover_metadata = rollover_metadata;
57        self.associated_merkle_tree = associated_merkle_tree;
58        self.queue_type = queue_type as u64;
59    }
60
61    pub fn rollover(
62        &mut self,
63        old_associated_merkle_tree: Pubkey,
64        next_queue: Pubkey,
65    ) -> Result<(), MerkleTreeMetadataError> {
66        if self.associated_merkle_tree != old_associated_merkle_tree {
67            return Err(MerkleTreeMetadataError::MerkleTreeAndQueueNotAssociated);
68        }
69
70        self.rollover_metadata.rollover()?;
71        self.next_queue = next_queue;
72
73        Ok(())
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use super::*;
80    use crate::access;
81
82    /// Helper function to create a default `QueueMetadata` struct for testing.
83    fn create_queue_metadata(
84        associated_merkle_tree: Pubkey,
85        queue_type: QueueType,
86    ) -> QueueMetadata {
87        QueueMetadata {
88            access_metadata: AccessMetadata {
89                owner: Pubkey::new_unique(),
90                program_owner: Pubkey::new_unique(),
91                forester: Pubkey::new_unique(),
92            },
93            rollover_metadata: RolloverMetadata {
94                index: 0,
95                rollover_fee: 1000,
96                rollover_threshold: 95,
97                network_fee: 10,
98                rolledover_slot: u64::MAX,
99                close_threshold: 200,
100                additional_bytes: 0,
101            },
102            associated_merkle_tree,
103            next_queue: Pubkey::default(),
104            queue_type: queue_type as u64,
105        }
106    }
107
108    #[test]
109    fn test_check_queue_type_valid() {
110        let valid_queue_type = QueueType::NullifierV1;
111        assert!(check_queue_type(&(valid_queue_type as u64), &valid_queue_type).is_ok());
112    }
113
114    #[test]
115    fn test_check_queue_type_invalid() {
116        let queue_type = QueueType::NullifierV1;
117        let expected_queue_type = QueueType::AddressV1;
118        assert!(matches!(
119            check_queue_type(&(queue_type as u64), &expected_queue_type),
120            Err(MerkleTreeMetadataError::InvalidQueueType)
121        ));
122    }
123
124    #[test]
125    fn test_init_method() {
126        let associated_merkle_tree = Pubkey::new_unique();
127        let queue_type = QueueType::InputStateV2;
128        let access_metadata = access::AccessMetadata {
129            owner: Pubkey::new_unique(),
130            program_owner: Pubkey::new_unique(),
131            forester: Pubkey::new_unique(),
132        };
133        let rollover_metadata = RolloverMetadata {
134            index: 1,
135            rollover_fee: 1000,
136            rollover_threshold: 95,
137            network_fee: 10,
138            rolledover_slot: u64::MAX,
139            close_threshold: 200,
140            additional_bytes: 1,
141        };
142        let mut queue_metadata = QueueMetadata::default();
143        queue_metadata.init(
144            access_metadata,
145            rollover_metadata,
146            associated_merkle_tree,
147            queue_type,
148        );
149        assert_eq!(queue_metadata.access_metadata, access_metadata);
150        assert_eq!(queue_metadata.rollover_metadata, rollover_metadata);
151        assert_eq!(
152            queue_metadata.associated_merkle_tree,
153            associated_merkle_tree
154        );
155        assert_eq!(queue_metadata.queue_type, queue_type as u64);
156    }
157
158    #[test]
159    fn test_rollover_method_valid() {
160        let associated_merkle_tree = Pubkey::new_unique();
161        let next_queue = Pubkey::new_unique();
162        let mut queue_metadata =
163            create_queue_metadata(associated_merkle_tree, QueueType::NullifierV1);
164
165        // Update the next queue as part of the method.
166        assert!(queue_metadata
167            .rollover(associated_merkle_tree, next_queue)
168            .is_ok());
169        assert_eq!(queue_metadata.next_queue, next_queue);
170    }
171
172    #[test]
173    fn test_rollover_method_invalid_merkle_tree() {
174        let associated_merkle_tree = Pubkey::new_unique();
175        let wrong_tree = Pubkey::new_unique();
176        let next_queue = Pubkey::new_unique();
177        let mut queue_metadata =
178            create_queue_metadata(associated_merkle_tree, QueueType::NullifierV1);
179
180        // Should fail because `wrong_tree` does not match the associated merkle tree.
181        assert!(matches!(
182            queue_metadata.rollover(wrong_tree, next_queue),
183            Err(MerkleTreeMetadataError::MerkleTreeAndQueueNotAssociated)
184        ));
185    }
186
187    #[test]
188    fn test_rollover_method_not_configured() {
189        let associated_merkle_tree = Pubkey::new_unique();
190        let mut queue_metadata =
191            create_queue_metadata(associated_merkle_tree, QueueType::NullifierV1);
192
193        // Simulate a case where rollover threshold is not configured.
194        queue_metadata.rollover_metadata.rollover_threshold = u64::MAX;
195        assert!(matches!(
196            queue_metadata.rollover_metadata.rollover(),
197            Err(MerkleTreeMetadataError::RolloverNotConfigured)
198        ));
199    }
200
201    #[test]
202    fn test_rollover_method_already_rolled_over() {
203        let associated_merkle_tree = Pubkey::new_unique();
204        let mut queue_metadata =
205            create_queue_metadata(associated_merkle_tree, QueueType::NullifierV1);
206
207        // Simulate a case where it is already rolled over.
208        queue_metadata.rollover_metadata.rolledover_slot = 10;
209        assert!(matches!(
210            queue_metadata.rollover_metadata.rollover(),
211            Err(MerkleTreeMetadataError::MerkleTreeAlreadyRolledOver)
212        ));
213    }
214
215    #[test]
216    fn test_queue_type_from() {
217        assert_eq!(QueueType::NullifierV1, QueueType::from(1));
218        assert_eq!(QueueType::AddressV1, QueueType::from(2));
219        assert_eq!(QueueType::InputStateV2, QueueType::from(3));
220        assert_eq!(QueueType::AddressV2, QueueType::from(4));
221        assert_eq!(QueueType::OutputStateV2, QueueType::from(5));
222    }
223
224    #[should_panic = "Invalid queue type"]
225    #[test]
226    fn test_queue_type_from_invalid() {
227        let _ = QueueType::from(0);
228    }
229}