light_merkle_tree_metadata/
rollover.rs1use bytemuck::{Pod, Zeroable};
2use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
3
4use crate::{errors::MerkleTreeMetadataError, AnchorDeserialize, AnchorSerialize};
5
6#[repr(C)]
7#[derive(
8 AnchorDeserialize,
9 AnchorSerialize,
10 Debug,
11 PartialEq,
12 Default,
13 Pod,
14 Zeroable,
15 Clone,
16 Copy,
17 FromBytes,
18 IntoBytes,
19 KnownLayout,
20 Immutable,
21)]
22pub struct RolloverMetadata {
23 pub index: u64,
25 pub rollover_fee: u64,
28 pub rollover_threshold: u64,
30 pub network_fee: u64,
32 pub rolledover_slot: u64,
34 pub close_threshold: u64,
38 pub additional_bytes: u64,
41}
42
43impl RolloverMetadata {
44 pub fn new(
45 index: u64,
46 rollover_fee: u64,
47 rollover_threshold: Option<u64>,
48 network_fee: u64,
49 close_threshold: Option<u64>,
50 additional_bytes: Option<u64>,
51 ) -> Self {
52 Self {
53 index,
54 rollover_fee,
55 rollover_threshold: rollover_threshold.unwrap_or(u64::MAX),
56 network_fee,
57 rolledover_slot: u64::MAX,
58 close_threshold: close_threshold.unwrap_or(u64::MAX),
59 additional_bytes: additional_bytes.unwrap_or_default(),
60 }
61 }
62
63 pub fn rollover(&mut self) -> Result<(), MerkleTreeMetadataError> {
64 if self.rollover_threshold == u64::MAX {
65 return Err(MerkleTreeMetadataError::RolloverNotConfigured);
66 }
67 if self.rolledover_slot != u64::MAX {
68 return Err(MerkleTreeMetadataError::MerkleTreeAlreadyRolledOver);
69 }
70 #[cfg(target_os = "solana")]
71 {
72 #[cfg(feature = "pinocchio")]
73 {
74 use pinocchio::sysvars::{clock::Clock, Sysvar};
75 self.rolledover_slot = Clock::get().unwrap().slot;
76 }
77 #[cfg(not(feature = "pinocchio"))]
78 {
79 use solana_sysvar::{clock::Clock, Sysvar};
80 self.rolledover_slot = Clock::get().unwrap().slot;
81 }
82 }
83 #[cfg(not(target_os = "solana"))]
84 {
85 self.rolledover_slot = 1;
87 }
88 Ok(())
89 }
90}
91
92pub fn check_rollover_fee_sufficient(
93 rollover_fee: u64,
94 queue_rent: u64,
95 merkle_tree_rent: u64,
96 rollover_threshold: u64,
97 height: u32,
98) -> Result<(), MerkleTreeMetadataError> {
99 if rollover_threshold == 0 && rollover_fee >= queue_rent + merkle_tree_rent {
100 return Ok(());
101 }
102 if (rollover_fee * rollover_threshold * (2u64.pow(height))) / 100
103 < queue_rent + merkle_tree_rent
104 {
105 #[cfg(feature = "solana")]
106 {
107 use solana_msg::msg;
108 msg!("rollover_fee: {}", rollover_fee);
109 msg!("rollover_threshold: {}", rollover_threshold);
110 msg!("height: {}", height);
111 msg!("merkle_tree_rent: {}", merkle_tree_rent);
112 msg!("queue_rent: {}", queue_rent);
113 msg!(
114 "((rollover_fee * rollover_threshold * (2u64.pow(height))) / 100): {} < {} rent",
115 ((rollover_fee * rollover_threshold * (2u64.pow(height))) / 100),
116 queue_rent + merkle_tree_rent
117 );
118 }
119 return Err(MerkleTreeMetadataError::InsufficientRolloverFee);
120 }
121 Ok(())
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127 use crate::fee::compute_rollover_fee;
128
129 #[test]
130 fn test_rollover_metadata() {
131 let mut metadata = RolloverMetadata::new(0, 0, Some(95), 0, Some(100), Some(1));
132 assert_eq!(metadata.rollover_threshold, 95);
133 assert_eq!(metadata.close_threshold, 100);
134 assert_eq!(metadata.rolledover_slot, u64::MAX);
135 assert_eq!(metadata.additional_bytes, 1);
136
137 metadata.rollover().unwrap();
138
139 let mut metadata = RolloverMetadata::new(0, 0, None, 0, None, None);
140 assert_eq!(metadata.rollover_threshold, u64::MAX);
141 assert_eq!(metadata.close_threshold, u64::MAX);
142 assert_eq!(metadata.additional_bytes, 0);
143
144 assert_eq!(
145 metadata.rollover(),
146 Err(MerkleTreeMetadataError::RolloverNotConfigured)
147 );
148 let mut metadata = RolloverMetadata::new(0, 0, Some(95), 0, None, None);
149 assert_eq!(metadata.close_threshold, u64::MAX);
150
151 metadata.rollover().unwrap();
152 let mut metadata = RolloverMetadata::new(0, 0, Some(95), 0, None, None);
153 metadata.rolledover_slot = 0;
154 assert_eq!(metadata.close_threshold, u64::MAX);
155
156 assert_eq!(
157 metadata.rollover(),
158 Err(MerkleTreeMetadataError::MerkleTreeAlreadyRolledOver)
159 );
160 }
161
162 #[test]
163 fn test_check_rollover_fee_sufficient() {
164 let queue_rent = 1_000_000_000;
165 let merkle_tree_rent = 1_000_000_000;
166 let rollover_threshold = 95;
167 let tree_height = 20;
168 let total_rent = queue_rent + merkle_tree_rent;
169 let rollover_fee =
170 compute_rollover_fee(rollover_threshold, tree_height, total_rent).unwrap();
171 println!("rollover_fee: {}", rollover_fee);
172 assert!(check_rollover_fee_sufficient(
173 rollover_fee,
174 queue_rent,
175 merkle_tree_rent,
176 rollover_threshold,
177 tree_height
178 )
179 .is_ok());
180
181 {
182 let invalid_height = 19;
183 assert_eq!(
184 check_rollover_fee_sufficient(
185 rollover_fee,
186 queue_rent,
187 merkle_tree_rent,
188 rollover_threshold,
189 invalid_height
190 ),
191 Err(MerkleTreeMetadataError::InsufficientRolloverFee)
192 );
193 }
194 {
195 let invalid_threshold = 90;
196 assert_eq!(
197 check_rollover_fee_sufficient(
198 rollover_fee,
199 queue_rent,
200 merkle_tree_rent,
201 invalid_threshold,
202 tree_height
203 ),
204 Err(MerkleTreeMetadataError::InsufficientRolloverFee)
205 );
206 }
207 {
208 let invalid_queue_rent = queue_rent + 1_000_000_000;
209 assert_eq!(
210 check_rollover_fee_sufficient(
211 rollover_fee,
212 invalid_queue_rent,
213 merkle_tree_rent,
214 rollover_threshold,
215 tree_height
216 ),
217 Err(MerkleTreeMetadataError::InsufficientRolloverFee)
218 );
219 }
220 {
221 let rollover_fee = rollover_fee - 1;
222 assert_eq!(
223 check_rollover_fee_sufficient(
224 rollover_fee,
225 queue_rent,
226 merkle_tree_rent,
227 rollover_threshold,
228 tree_height
229 ),
230 Err(MerkleTreeMetadataError::InsufficientRolloverFee)
231 );
232 }
233 }
234}