solana_message/versions/v1/
config.rs1#[cfg(feature = "serde")]
2use serde_derive::{Deserialize, Serialize};
3#[cfg(feature = "frozen-abi")]
4use solana_frozen_abi_macro::AbiExample;
5
6#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
8#[cfg_attr(
9 feature = "serde",
10 derive(Serialize, Deserialize),
11 serde(rename_all = "camelCase")
12)]
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
14pub struct TransactionConfig {
15 pub priority_fee: Option<u64>,
17
18 pub compute_unit_limit: Option<u32>,
20
21 pub loaded_accounts_data_size_limit: Option<u32>,
23
24 pub heap_size: Option<u32>,
26}
27
28impl TransactionConfig {
29 pub const fn empty() -> Self {
30 Self {
31 priority_fee: None,
32 compute_unit_limit: None,
33 loaded_accounts_data_size_limit: None,
34 heap_size: None,
35 }
36 }
37
38 #[must_use]
39 pub const fn with_priority_fee(mut self, fee: u64) -> Self {
40 self.priority_fee = Some(fee);
41 self
42 }
43
44 #[must_use]
45 pub const fn with_compute_unit_limit(mut self, limit: u32) -> Self {
46 self.compute_unit_limit = Some(limit);
47 self
48 }
49
50 #[must_use]
51 pub const fn with_loaded_accounts_data_size_limit(mut self, limit: u32) -> Self {
52 self.loaded_accounts_data_size_limit = Some(limit);
53 self
54 }
55
56 #[must_use]
58 pub const fn with_heap_size(mut self, size: u32) -> Self {
59 self.heap_size = Some(size);
60 self
61 }
62
63 pub const fn size(&self) -> usize {
65 let mut size: usize = 0;
66
67 if self.priority_fee.is_some() {
68 size = size.saturating_add(size_of::<u64>());
69 }
70
71 if self.compute_unit_limit.is_some() {
72 size = size.saturating_add(size_of::<u32>());
73 }
74
75 if self.loaded_accounts_data_size_limit.is_some() {
76 size = size.saturating_add(size_of::<u32>());
77 }
78
79 if self.heap_size.is_some() {
80 size = size.saturating_add(size_of::<u32>());
81 }
82
83 size
84 }
85}
86
87impl From<&TransactionConfig> for TransactionConfigMask {
88 fn from(config: &TransactionConfig) -> Self {
89 let mut mask = 0u32;
90
91 if config.priority_fee.is_some() {
92 mask |= Self::PRIORITY_FEE;
93 }
94
95 if config.compute_unit_limit.is_some() {
96 mask |= Self::COMPUTE_UNIT_LIMIT;
97 }
98
99 if config.loaded_accounts_data_size_limit.is_some() {
100 mask |= Self::LOADED_ACCOUNTS_DATA_SIZE;
101 }
102
103 if config.heap_size.is_some() {
104 mask |= Self::HEAP_SIZE;
105 }
106
107 TransactionConfigMask(mask)
108 }
109}
110
111impl From<TransactionConfig> for TransactionConfigMask {
112 fn from(config: TransactionConfig) -> Self {
113 TransactionConfigMask::from(&config)
114 }
115}
116
117#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
122#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
123pub struct TransactionConfigMask(pub u32);
124
125impl TransactionConfigMask {
126 pub const PRIORITY_FEE: u32 = 0b11;
130
131 pub const COMPUTE_UNIT_LIMIT: u32 = 0b100;
135
136 pub const LOADED_ACCOUNTS_DATA_SIZE: u32 = 0b1000;
140
141 pub const HEAP_SIZE: u32 = 0b10000;
145
146 pub const KNOWN_BITS: u32 = Self::PRIORITY_FEE
148 | Self::COMPUTE_UNIT_LIMIT
149 | Self::LOADED_ACCOUNTS_DATA_SIZE
150 | Self::HEAP_SIZE;
151
152 pub const fn new(mask: u32) -> Self {
153 Self(mask)
154 }
155
156 pub const fn has_unknown_bits(&self) -> bool {
160 (self.0 | Self::KNOWN_BITS) != Self::KNOWN_BITS
161 }
162
163 pub const fn has_priority_fee(&self) -> bool {
164 (self.0 & Self::PRIORITY_FEE) == Self::PRIORITY_FEE
165 }
166
167 pub const fn has_invalid_priority_fee_bits(&self) -> bool {
169 let bits = self.0 & Self::PRIORITY_FEE;
170 bits != 0 && bits != Self::PRIORITY_FEE
171 }
172
173 pub const fn has_compute_unit_limit(&self) -> bool {
174 (self.0 & Self::COMPUTE_UNIT_LIMIT) != 0
175 }
176
177 pub const fn has_loaded_accounts_data_size(&self) -> bool {
178 (self.0 & Self::LOADED_ACCOUNTS_DATA_SIZE) != 0
179 }
180
181 pub const fn has_heap_size(&self) -> bool {
182 (self.0 & Self::HEAP_SIZE) != 0
183 }
184
185 pub const fn size_of_config(&self) -> usize {
187 let mut size: usize = 0;
188
189 if self.has_priority_fee() {
190 size = size.saturating_add(size_of::<u64>());
191 }
192
193 if self.has_compute_unit_limit() {
194 size = size.saturating_add(size_of::<u32>());
195 }
196
197 if self.has_loaded_accounts_data_size() {
198 size = size.saturating_add(size_of::<u32>());
199 }
200
201 if self.has_heap_size() {
202 size = size.saturating_add(size_of::<u32>());
203 }
204
205 size
206 }
207}
208
209#[cfg(test)]
210mod tests {
211 use super::*;
212
213 #[test]
214 fn has_unknown_bits_detects_unsupported_bits() {
215 assert!(!TransactionConfigMask::new(0).has_unknown_bits());
216 assert!(!TransactionConfigMask::new(0b11111).has_unknown_bits());
217 assert!(TransactionConfigMask::new(0b100000).has_unknown_bits());
218 assert!(TransactionConfigMask::new(0x80000000).has_unknown_bits());
219 assert!(TransactionConfigMask::new(0b111111).has_unknown_bits());
220 }
221
222 #[test]
223 fn has_priority_fee_requires_both_bits() {
224 assert!(!TransactionConfigMask::new(0).has_priority_fee());
225 assert!(!TransactionConfigMask::new(0b01).has_priority_fee());
226 assert!(!TransactionConfigMask::new(0b10).has_priority_fee());
227 assert!(TransactionConfigMask::new(0b11).has_priority_fee());
228 }
229
230 #[test]
231 fn has_invalid_priority_fee_bits_detects_partial() {
232 assert!(!TransactionConfigMask::new(0).has_invalid_priority_fee_bits());
233 assert!(TransactionConfigMask::new(0b01).has_invalid_priority_fee_bits());
234 assert!(TransactionConfigMask::new(0b10).has_invalid_priority_fee_bits());
235 assert!(!TransactionConfigMask::new(0b11).has_invalid_priority_fee_bits());
236 }
237
238 #[test]
239 fn has_field_methods_check_individual_bits() {
240 let mask = TransactionConfigMask::new(0b11100);
241 assert!(mask.has_compute_unit_limit());
242 assert!(mask.has_loaded_accounts_data_size());
243 assert!(mask.has_heap_size());
244
245 let mask = TransactionConfigMask::new(0);
246 assert!(!mask.has_compute_unit_limit());
247 assert!(!mask.has_loaded_accounts_data_size());
248 assert!(!mask.has_heap_size());
249 }
250
251 #[test]
252 fn config_values_size_sums_field_sizes() {
253 assert_eq!(TransactionConfigMask::new(0).size_of_config(), 0);
254 assert_eq!(TransactionConfigMask::new(0b11).size_of_config(), 8);
255 assert_eq!(TransactionConfigMask::new(0b100).size_of_config(), 4);
256 assert_eq!(TransactionConfigMask::new(0b11111).size_of_config(), 20);
257 }
258
259 #[test]
260 fn from_config_sets_correct_bits() {
261 let config = TransactionConfig::empty()
262 .with_priority_fee(1000)
263 .with_compute_unit_limit(200_000);
264
265 let mask = TransactionConfigMask::from(&config);
266 assert!(mask.has_priority_fee());
267 assert!(mask.has_compute_unit_limit());
268 assert!(!mask.has_loaded_accounts_data_size());
269 assert!(!mask.has_heap_size());
270 }
271
272 #[test]
273 fn mask_invariants_hold_for_all_known_bit_patterns() {
274 for raw in 0u32..(1u32 << 5) {
275 let mask = TransactionConfigMask::new(raw);
276
277 assert!(!mask.has_unknown_bits());
278
279 if mask.has_priority_fee() {
280 assert!(!mask.has_invalid_priority_fee_bits());
281 }
282
283 let mut expected_size = 0;
284 if mask.has_priority_fee() {
285 expected_size += size_of::<u64>();
286 }
287 if mask.has_compute_unit_limit() {
288 expected_size += size_of::<u32>();
289 }
290 if mask.has_loaded_accounts_data_size() {
291 expected_size += size_of::<u32>();
292 }
293 if mask.has_heap_size() {
294 expected_size += size_of::<u32>();
295 }
296 assert_eq!(mask.size_of_config(), expected_size);
297 }
298 }
299
300 #[test]
301 fn builder_sets_all_fields() {
302 let config = TransactionConfig::empty()
303 .with_priority_fee(1000)
304 .with_compute_unit_limit(200_000)
305 .with_loaded_accounts_data_size_limit(64 * 1024)
306 .with_heap_size(64 * 1024);
307
308 assert_eq!(config.priority_fee, Some(1000));
309 assert_eq!(config.compute_unit_limit, Some(200_000));
310 assert_eq!(config.loaded_accounts_data_size_limit, Some(64 * 1024));
311 assert_eq!(config.heap_size, Some(64 * 1024));
312 }
313}