1use {
2 crate::compute_budget_instruction_details::*, solana_compute_budget::compute_budget_limits::*,
3 solana_feature_set::FeatureSet, solana_pubkey::Pubkey,
4 solana_svm_transaction::instruction::SVMInstruction,
5 solana_transaction_error::TransactionError,
6};
7
8pub fn process_compute_budget_instructions<'a>(
14 instructions: impl Iterator<Item = (&'a Pubkey, SVMInstruction<'a>)> + Clone,
15 feature_set: &FeatureSet,
16) -> Result<ComputeBudgetLimits, TransactionError> {
17 ComputeBudgetInstructionDetails::try_from(instructions)?
18 .sanitize_and_convert_to_compute_budget_limits(feature_set)
19}
20
21#[cfg(test)]
22mod tests {
23 use {
24 super::*,
25 solana_compute_budget_interface::ComputeBudgetInstruction,
26 solana_hash::Hash,
27 solana_instruction::{error::InstructionError, Instruction},
28 solana_keypair::Keypair,
29 solana_message::Message,
30 solana_pubkey::Pubkey,
31 solana_signer::Signer,
32 solana_svm_transaction::svm_message::SVMMessage,
33 solana_system_interface::instruction::transfer,
34 solana_transaction::{sanitized::SanitizedTransaction, Transaction},
35 solana_transaction_error::TransactionError,
36 std::num::NonZeroU32,
37 };
38
39 macro_rules! test {
40 ( $instructions: expr, $expected_result: expr) => {
41 for feature_set in [FeatureSet::default(), FeatureSet::all_enabled()] {
42 test!($instructions, $expected_result, &feature_set);
43 }
44 };
45 ( $instructions: expr, $expected_result: expr, $feature_set: expr) => {
46 let payer_keypair = Keypair::new();
47 let tx = SanitizedTransaction::from_transaction_for_tests(Transaction::new(
48 &[&payer_keypair],
49 Message::new($instructions, Some(&payer_keypair.pubkey())),
50 Hash::default(),
51 ));
52
53 let result = process_compute_budget_instructions(
54 SVMMessage::program_instructions_iter(&tx),
55 $feature_set,
56 );
57 assert_eq!($expected_result, result);
58 };
59 }
60
61 #[test]
62 fn test_process_instructions() {
63 test!(
65 &[],
66 Ok(ComputeBudgetLimits {
67 compute_unit_limit: 0,
68 ..ComputeBudgetLimits::default()
69 })
70 );
71 test!(
72 &[
73 ComputeBudgetInstruction::set_compute_unit_limit(1),
74 Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
75 ],
76 Ok(ComputeBudgetLimits {
77 compute_unit_limit: 1,
78 ..ComputeBudgetLimits::default()
79 })
80 );
81 test!(
82 &[
83 ComputeBudgetInstruction::set_compute_unit_limit(MAX_COMPUTE_UNIT_LIMIT + 1),
84 Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
85 ],
86 Ok(ComputeBudgetLimits {
87 compute_unit_limit: MAX_COMPUTE_UNIT_LIMIT,
88 ..ComputeBudgetLimits::default()
89 })
90 );
91 test!(
92 &[
93 Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
94 ComputeBudgetInstruction::set_compute_unit_limit(MAX_COMPUTE_UNIT_LIMIT),
95 ],
96 Ok(ComputeBudgetLimits {
97 compute_unit_limit: MAX_COMPUTE_UNIT_LIMIT,
98 ..ComputeBudgetLimits::default()
99 })
100 );
101 test!(
102 &[
103 Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
104 Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
105 Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
106 ComputeBudgetInstruction::set_compute_unit_limit(1),
107 ],
108 Ok(ComputeBudgetLimits {
109 compute_unit_limit: 1,
110 ..ComputeBudgetLimits::default()
111 })
112 );
113 test!(
114 &[
115 ComputeBudgetInstruction::set_compute_unit_limit(1),
116 ComputeBudgetInstruction::set_compute_unit_price(42)
117 ],
118 Ok(ComputeBudgetLimits {
119 compute_unit_limit: 1,
120 compute_unit_price: 42,
121 ..ComputeBudgetLimits::default()
122 })
123 );
124
125 test!(
127 &[],
128 Ok(ComputeBudgetLimits {
129 compute_unit_limit: 0,
130 ..ComputeBudgetLimits::default()
131 })
132 );
133 test!(
134 &[
135 ComputeBudgetInstruction::request_heap_frame(40 * 1024),
136 Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
137 ],
138 Ok(ComputeBudgetLimits {
139 compute_unit_limit: DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT,
140 updated_heap_bytes: 40 * 1024,
141 ..ComputeBudgetLimits::default()
142 }),
143 &FeatureSet::default()
144 );
145 test!(
146 &[
147 ComputeBudgetInstruction::request_heap_frame(40 * 1024),
148 Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
149 ],
150 Ok(ComputeBudgetLimits {
151 compute_unit_limit: DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT
152 + MAX_BUILTIN_ALLOCATION_COMPUTE_UNIT_LIMIT,
153 updated_heap_bytes: 40 * 1024,
154 ..ComputeBudgetLimits::default()
155 }),
156 &FeatureSet::all_enabled()
157 );
158 test!(
159 &[
160 ComputeBudgetInstruction::request_heap_frame(40 * 1024 + 1),
161 Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
162 ],
163 Err(TransactionError::InstructionError(
164 0,
165 InstructionError::InvalidInstructionData,
166 ))
167 );
168 test!(
169 &[
170 ComputeBudgetInstruction::request_heap_frame(31 * 1024),
171 Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
172 ],
173 Err(TransactionError::InstructionError(
174 0,
175 InstructionError::InvalidInstructionData,
176 ))
177 );
178 test!(
179 &[
180 ComputeBudgetInstruction::request_heap_frame(MAX_HEAP_FRAME_BYTES + 1),
181 Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
182 ],
183 Err(TransactionError::InstructionError(
184 0,
185 InstructionError::InvalidInstructionData,
186 ))
187 );
188 test!(
189 &[
190 Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
191 ComputeBudgetInstruction::request_heap_frame(MAX_HEAP_FRAME_BYTES),
192 ],
193 Ok(ComputeBudgetLimits {
194 compute_unit_limit: DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT,
195 updated_heap_bytes: MAX_HEAP_FRAME_BYTES,
196 ..ComputeBudgetLimits::default()
197 }),
198 &FeatureSet::default()
199 );
200 test!(
201 &[
202 Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
203 ComputeBudgetInstruction::request_heap_frame(MAX_HEAP_FRAME_BYTES),
204 ],
205 Ok(ComputeBudgetLimits {
206 compute_unit_limit: DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT
207 + MAX_BUILTIN_ALLOCATION_COMPUTE_UNIT_LIMIT,
208 updated_heap_bytes: MAX_HEAP_FRAME_BYTES,
209 ..ComputeBudgetLimits::default()
210 }),
211 &FeatureSet::all_enabled()
212 );
213 test!(
214 &[
215 Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
216 Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
217 Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
218 ComputeBudgetInstruction::request_heap_frame(1),
219 ],
220 Err(TransactionError::InstructionError(
221 3,
222 InstructionError::InvalidInstructionData,
223 ))
224 );
225 test!(
226 &[
227 Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
228 Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
229 Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
230 Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
231 Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
232 Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
233 Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
234 Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
235 ],
236 Ok(ComputeBudgetLimits {
237 compute_unit_limit: DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT * 7,
238 ..ComputeBudgetLimits::default()
239 })
240 );
241
242 test!(
244 &[
245 Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
246 ComputeBudgetInstruction::request_heap_frame(MAX_HEAP_FRAME_BYTES),
247 ComputeBudgetInstruction::set_compute_unit_limit(MAX_COMPUTE_UNIT_LIMIT),
248 ComputeBudgetInstruction::set_compute_unit_price(u64::MAX),
249 ],
250 Ok(ComputeBudgetLimits {
251 compute_unit_price: u64::MAX,
252 compute_unit_limit: MAX_COMPUTE_UNIT_LIMIT,
253 updated_heap_bytes: MAX_HEAP_FRAME_BYTES,
254 ..ComputeBudgetLimits::default()
255 })
256 );
257 test!(
258 &[
259 Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
260 ComputeBudgetInstruction::set_compute_unit_limit(1),
261 ComputeBudgetInstruction::request_heap_frame(MAX_HEAP_FRAME_BYTES),
262 ComputeBudgetInstruction::set_compute_unit_price(u64::MAX),
263 ],
264 Ok(ComputeBudgetLimits {
265 compute_unit_price: u64::MAX,
266 compute_unit_limit: 1,
267 updated_heap_bytes: MAX_HEAP_FRAME_BYTES,
268 ..ComputeBudgetLimits::default()
269 })
270 );
271
272 test!(
274 &[
275 Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
276 ComputeBudgetInstruction::set_compute_unit_limit(MAX_COMPUTE_UNIT_LIMIT),
277 ComputeBudgetInstruction::set_compute_unit_limit(MAX_COMPUTE_UNIT_LIMIT - 1),
278 ],
279 Err(TransactionError::DuplicateInstruction(2))
280 );
281
282 test!(
283 &[
284 Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
285 ComputeBudgetInstruction::request_heap_frame(MIN_HEAP_FRAME_BYTES),
286 ComputeBudgetInstruction::request_heap_frame(MAX_HEAP_FRAME_BYTES),
287 ],
288 Err(TransactionError::DuplicateInstruction(2))
289 );
290 test!(
291 &[
292 Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
293 ComputeBudgetInstruction::set_compute_unit_price(0),
294 ComputeBudgetInstruction::set_compute_unit_price(u64::MAX),
295 ],
296 Err(TransactionError::DuplicateInstruction(2))
297 );
298 }
299
300 #[test]
301 fn test_process_loaded_accounts_data_size_limit_instruction() {
302 test!(
303 &[],
304 Ok(ComputeBudgetLimits {
305 compute_unit_limit: 0,
306 ..ComputeBudgetLimits::default()
307 })
308 );
309
310 let data_size = 1;
313 let expected_result = Ok(ComputeBudgetLimits {
314 compute_unit_limit: DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT,
315 loaded_accounts_bytes: NonZeroU32::new(data_size).unwrap(),
316 ..ComputeBudgetLimits::default()
317 });
318 test!(
319 &[
320 ComputeBudgetInstruction::set_loaded_accounts_data_size_limit(data_size),
321 Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
322 ],
323 expected_result,
324 &FeatureSet::default()
325 );
326
327 let expected_result = Ok(ComputeBudgetLimits {
328 compute_unit_limit: DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT
329 + MAX_BUILTIN_ALLOCATION_COMPUTE_UNIT_LIMIT,
330 loaded_accounts_bytes: NonZeroU32::new(data_size).unwrap(),
331 ..ComputeBudgetLimits::default()
332 });
333 test!(
334 &[
335 ComputeBudgetInstruction::set_loaded_accounts_data_size_limit(data_size),
336 Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
337 ],
338 expected_result,
339 &FeatureSet::all_enabled()
340 );
341
342 let data_size = MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES.get() + 1;
345 let expected_result = Ok(ComputeBudgetLimits {
346 compute_unit_limit: DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT,
347 loaded_accounts_bytes: MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES,
348 ..ComputeBudgetLimits::default()
349 });
350 test!(
351 &[
352 ComputeBudgetInstruction::set_loaded_accounts_data_size_limit(data_size),
353 Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
354 ],
355 expected_result,
356 &FeatureSet::default()
357 );
358
359 let expected_result = Ok(ComputeBudgetLimits {
360 compute_unit_limit: DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT
361 + MAX_BUILTIN_ALLOCATION_COMPUTE_UNIT_LIMIT,
362 loaded_accounts_bytes: MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES,
363 ..ComputeBudgetLimits::default()
364 });
365 test!(
366 &[
367 ComputeBudgetInstruction::set_loaded_accounts_data_size_limit(data_size),
368 Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
369 ],
370 expected_result,
371 &FeatureSet::all_enabled()
372 );
373
374 let expected_result = Ok(ComputeBudgetLimits {
377 compute_unit_limit: DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT,
378 loaded_accounts_bytes: MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES,
379 ..ComputeBudgetLimits::default()
380 });
381
382 test!(
383 &[Instruction::new_with_bincode(
384 Pubkey::new_unique(),
385 &0_u8,
386 vec![]
387 ),],
388 expected_result
389 );
390
391 let data_size = MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES.get();
394 let expected_result = Err(TransactionError::DuplicateInstruction(2));
395
396 test!(
397 &[
398 Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
399 ComputeBudgetInstruction::set_loaded_accounts_data_size_limit(data_size),
400 ComputeBudgetInstruction::set_loaded_accounts_data_size_limit(data_size),
401 ],
402 expected_result
403 );
404 }
405
406 #[test]
407 fn test_process_mixed_instructions_without_compute_budget() {
408 let payer_keypair = Keypair::new();
409
410 let transaction =
411 SanitizedTransaction::from_transaction_for_tests(Transaction::new_signed_with_payer(
412 &[
413 Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
414 transfer(&payer_keypair.pubkey(), &Pubkey::new_unique(), 2),
415 ],
416 Some(&payer_keypair.pubkey()),
417 &[&payer_keypair],
418 Hash::default(),
419 ));
420
421 for (feature_set, expected_result) in [
422 (
423 FeatureSet::default(),
424 Ok(ComputeBudgetLimits {
425 compute_unit_limit: 2 * DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT,
426 ..ComputeBudgetLimits::default()
427 }),
428 ),
429 (
430 FeatureSet::all_enabled(),
431 Ok(ComputeBudgetLimits {
432 compute_unit_limit: DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT
433 + MAX_BUILTIN_ALLOCATION_COMPUTE_UNIT_LIMIT,
434 ..ComputeBudgetLimits::default()
435 }),
436 ),
437 ] {
438 let result = process_compute_budget_instructions(
439 SVMMessage::program_instructions_iter(&transaction),
440 &feature_set,
441 );
442
443 assert_eq!(result, expected_result);
447 }
448 }
449}