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