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