solana_program_runtime/
compute_budget.rs

1use {
2    crate::prioritization_fee::{PrioritizationFeeDetails, PrioritizationFeeType},
3    solana_sdk::{
4        borsh::try_from_slice_unchecked,
5        compute_budget::{self, ComputeBudgetInstruction},
6        entrypoint::HEAP_LENGTH as MIN_HEAP_FRAME_BYTES,
7        instruction::{CompiledInstruction, InstructionError},
8        pubkey::Pubkey,
9        transaction::TransactionError,
10    },
11};
12
13pub const DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT: u32 = 200_000;
14pub const MAX_COMPUTE_UNIT_LIMIT: u32 = 1_400_000;
15const MAX_HEAP_FRAME_BYTES: u32 = 256 * 1024;
16
17#[cfg(RUSTC_WITH_SPECIALIZATION)]
18impl ::safecoin_frozen_abi::abi_example::AbiExample for ComputeBudget {
19    fn example() -> Self {
20        // ComputeBudget is not Serialize so just rely on Default.
21        ComputeBudget::default()
22    }
23}
24
25#[derive(Clone, Copy, Debug, PartialEq, Eq)]
26pub struct ComputeBudget {
27    /// Number of compute units that a transaction or individual instruction is
28    /// allowed to consume. Compute units are consumed by program execution,
29    /// resources they use, etc...
30    pub compute_unit_limit: u64,
31    /// Number of compute units consumed by a log_u64 call
32    pub log_64_units: u64,
33    /// Number of compute units consumed by a create_program_address call
34    pub create_program_address_units: u64,
35    /// Number of compute units consumed by an invoke call (not including the cost incurred by
36    /// the called program)
37    pub invoke_units: u64,
38    /// Maximum cross-program invocation depth allowed
39    pub max_invoke_depth: usize,
40    /// Base number of compute units consumed to call SHA256
41    pub sha256_base_cost: u64,
42    /// Incremental number of units consumed by SHA256 (based on bytes)
43    pub sha256_byte_cost: u64,
44    /// Maximum number of slices hashed per syscall
45    pub sha256_max_slices: u64,
46    /// Maximum BPF to BPF call depth
47    pub max_call_depth: usize,
48    /// Size of a stack frame in bytes, must match the size specified in the LLVM BPF backend
49    pub stack_frame_size: usize,
50    /// Number of compute units consumed by logging a `Pubkey`
51    pub log_pubkey_units: u64,
52    /// Maximum cross-program invocation instruction size
53    pub max_cpi_instruction_size: usize,
54    /// Number of account data bytes per compute unit charged during a cross-program invocation
55    pub cpi_bytes_per_unit: u64,
56    /// Base number of compute units consumed to get a sysvar
57    pub sysvar_base_cost: u64,
58    /// Number of compute units consumed to call secp256k1_recover
59    pub secp256k1_recover_cost: u64,
60    /// Number of compute units consumed to do a syscall without any work
61    pub syscall_base_cost: u64,
62    /// Number of compute units consumed to validate a curve25519 edwards point
63    pub curve25519_edwards_validate_point_cost: u64,
64    /// Number of compute units consumed to add two curve25519 edwards points
65    pub curve25519_edwards_add_cost: u64,
66    /// Number of compute units consumed to subtract two curve25519 edwards points
67    pub curve25519_edwards_subtract_cost: u64,
68    /// Number of compute units consumed to multiply a curve25519 edwards point
69    pub curve25519_edwards_multiply_cost: u64,
70    /// Number of compute units consumed to validate a curve25519 ristretto point
71    pub curve25519_ristretto_validate_point_cost: u64,
72    /// Number of compute units consumed to add two curve25519 ristretto points
73    pub curve25519_ristretto_add_cost: u64,
74    /// Number of compute units consumed to subtract two curve25519 ristretto points
75    pub curve25519_ristretto_subtract_cost: u64,
76    /// Number of compute units consumed to multiply a curve25519 ristretto point
77    pub curve25519_ristretto_multiply_cost: u64,
78    /// Optional program heap region size, if `None` then loader default
79    pub heap_size: Option<usize>,
80    /// Number of compute units per additional 32k heap above the default (~.5
81    /// us per 32k at 15 units/us rounded up)
82    pub heap_cost: u64,
83    /// Memory operation syscall base cost
84    pub mem_op_base_cost: u64,
85}
86
87impl Default for ComputeBudget {
88    fn default() -> Self {
89        Self::new(MAX_COMPUTE_UNIT_LIMIT as u64)
90    }
91}
92
93impl ComputeBudget {
94    pub fn new(compute_unit_limit: u64) -> Self {
95        ComputeBudget {
96            compute_unit_limit,
97            log_64_units: 100,
98            create_program_address_units: 1500,
99            invoke_units: 1000,
100            max_invoke_depth: 4,
101            sha256_base_cost: 85,
102            sha256_byte_cost: 1,
103            sha256_max_slices: 20_000,
104            max_call_depth: 64,
105            stack_frame_size: 4_096,
106            log_pubkey_units: 100,
107            max_cpi_instruction_size: 1280, // IPv6 Min MTU size
108            cpi_bytes_per_unit: 250,        // ~50MB at 200,000 units
109            sysvar_base_cost: 100,
110            secp256k1_recover_cost: 25_000,
111            syscall_base_cost: 100,
112            curve25519_edwards_validate_point_cost: 111,
113            curve25519_edwards_add_cost: 331,
114            curve25519_edwards_subtract_cost: 329,
115            curve25519_edwards_multiply_cost: 1_753,
116            curve25519_ristretto_validate_point_cost: 117,
117            curve25519_ristretto_add_cost: 367,
118            curve25519_ristretto_subtract_cost: 366,
119            curve25519_ristretto_multiply_cost: 1_804,
120            heap_size: None,
121            heap_cost: 8,
122            mem_op_base_cost: 10,
123        }
124    }
125
126    pub fn process_instructions<'a>(
127        &mut self,
128        instructions: impl Iterator<Item = (&'a Pubkey, &'a CompiledInstruction)>,
129        default_units_per_instruction: bool,
130        support_set_compute_unit_price_ix: bool,
131        enable_request_heap_frame_ix: bool,
132    ) -> Result<PrioritizationFeeDetails, TransactionError> {
133        let mut num_non_compute_budget_instructions: usize = 0;
134        let mut updated_compute_unit_limit = None;
135        let mut requested_heap_size = None;
136        let mut prioritization_fee = None;
137
138        for (i, (program_id, instruction)) in instructions.enumerate() {
139            if compute_budget::check_id(program_id) {
140                if support_set_compute_unit_price_ix {
141                    let invalid_instruction_data_error = TransactionError::InstructionError(
142                        i as u8,
143                        InstructionError::InvalidInstructionData,
144                    );
145                    let duplicate_instruction_error =
146                        TransactionError::DuplicateInstruction(i as u8);
147
148                    match try_from_slice_unchecked(&instruction.data) {
149                        Ok(ComputeBudgetInstruction::RequestUnitsDeprecated {
150                            units: compute_unit_limit,
151                            additional_fee,
152                        }) => {
153                            if updated_compute_unit_limit.is_some() {
154                                return Err(duplicate_instruction_error);
155                            }
156                            if prioritization_fee.is_some() {
157                                return Err(duplicate_instruction_error);
158                            }
159                            updated_compute_unit_limit = Some(compute_unit_limit);
160                            prioritization_fee =
161                                Some(PrioritizationFeeType::Deprecated(additional_fee as u64));
162                        }
163                        Ok(ComputeBudgetInstruction::RequestHeapFrame(bytes)) => {
164                            if requested_heap_size.is_some() {
165                                return Err(duplicate_instruction_error);
166                            }
167                            requested_heap_size = Some((bytes, i as u8));
168                        }
169                        Ok(ComputeBudgetInstruction::SetComputeUnitLimit(compute_unit_limit)) => {
170                            if updated_compute_unit_limit.is_some() {
171                                return Err(duplicate_instruction_error);
172                            }
173                            updated_compute_unit_limit = Some(compute_unit_limit);
174                        }
175                        Ok(ComputeBudgetInstruction::SetComputeUnitPrice(micro_lamports)) => {
176                            if prioritization_fee.is_some() {
177                                return Err(duplicate_instruction_error);
178                            }
179                            prioritization_fee =
180                                Some(PrioritizationFeeType::ComputeUnitPrice(micro_lamports));
181                        }
182                        _ => return Err(invalid_instruction_data_error),
183                    }
184                } else if i < 3 {
185                    match try_from_slice_unchecked(&instruction.data) {
186                        Ok(ComputeBudgetInstruction::RequestUnitsDeprecated {
187                            units: compute_unit_limit,
188                            additional_fee,
189                        }) => {
190                            updated_compute_unit_limit = Some(compute_unit_limit);
191                            prioritization_fee =
192                                Some(PrioritizationFeeType::Deprecated(additional_fee as u64));
193                        }
194                        Ok(ComputeBudgetInstruction::RequestHeapFrame(bytes)) => {
195                            requested_heap_size = Some((bytes, 0));
196                        }
197                        _ => {
198                            return Err(TransactionError::InstructionError(
199                                0,
200                                InstructionError::InvalidInstructionData,
201                            ))
202                        }
203                    }
204                }
205            } else {
206                // only include non-request instructions in default max calc
207                num_non_compute_budget_instructions =
208                    num_non_compute_budget_instructions.saturating_add(1);
209            }
210        }
211
212        if let Some((bytes, i)) = requested_heap_size {
213            if !enable_request_heap_frame_ix
214                || bytes > MAX_HEAP_FRAME_BYTES
215                || bytes < MIN_HEAP_FRAME_BYTES as u32
216                || bytes % 1024 != 0
217            {
218                return Err(TransactionError::InstructionError(
219                    i,
220                    InstructionError::InvalidInstructionData,
221                ));
222            }
223            self.heap_size = Some(bytes as usize);
224        }
225
226        self.compute_unit_limit = if default_units_per_instruction {
227            updated_compute_unit_limit.or_else(|| {
228                Some(
229                    (num_non_compute_budget_instructions as u32)
230                        .saturating_mul(DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT),
231                )
232            })
233        } else {
234            updated_compute_unit_limit
235        }
236        .unwrap_or(MAX_COMPUTE_UNIT_LIMIT)
237        .min(MAX_COMPUTE_UNIT_LIMIT) as u64;
238
239        Ok(prioritization_fee
240            .map(|fee_type| PrioritizationFeeDetails::new(fee_type, self.compute_unit_limit))
241            .unwrap_or_default())
242    }
243}
244
245#[cfg(test)]
246mod tests {
247    use {
248        super::*,
249        solana_sdk::{
250            hash::Hash,
251            instruction::Instruction,
252            message::Message,
253            pubkey::Pubkey,
254            signature::Keypair,
255            signer::Signer,
256            transaction::{SanitizedTransaction, Transaction},
257        },
258    };
259
260    fn request_units_deprecated(units: u32, additional_fee: u32) -> Instruction {
261        Instruction::new_with_borsh(
262            compute_budget::id(),
263            &ComputeBudgetInstruction::RequestUnitsDeprecated {
264                units,
265                additional_fee,
266            },
267            vec![],
268        )
269    }
270
271    macro_rules! test {
272        ( $instructions: expr, $expected_result: expr, $expected_budget: expr, $type_change: expr, $enable_request_heap_frame_ix: expr) => {
273            let payer_keypair = Keypair::new();
274            let tx = SanitizedTransaction::from_transaction_for_tests(Transaction::new(
275                &[&payer_keypair],
276                Message::new($instructions, Some(&payer_keypair.pubkey())),
277                Hash::default(),
278            ));
279            let mut compute_budget = ComputeBudget::default();
280            let result = compute_budget.process_instructions(
281                tx.message().program_instructions_iter(),
282                true,
283                $type_change,
284                $enable_request_heap_frame_ix,
285            );
286            assert_eq!($expected_result, result);
287            assert_eq!(compute_budget, $expected_budget);
288        };
289        ( $instructions: expr, $expected_result: expr, $expected_budget: expr) => {
290            test!(
291                $instructions,
292                $expected_result,
293                $expected_budget,
294                true,
295                true
296            );
297        };
298    }
299
300    #[test]
301    fn test_process_instructions() {
302        // Units
303        test!(
304            &[],
305            Ok(PrioritizationFeeDetails::default()),
306            ComputeBudget {
307                compute_unit_limit: 0,
308                ..ComputeBudget::default()
309            }
310        );
311        test!(
312            &[
313                ComputeBudgetInstruction::set_compute_unit_limit(1),
314                Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
315            ],
316            Ok(PrioritizationFeeDetails::default()),
317            ComputeBudget {
318                compute_unit_limit: 1,
319                ..ComputeBudget::default()
320            }
321        );
322        test!(
323            &[
324                ComputeBudgetInstruction::set_compute_unit_limit(MAX_COMPUTE_UNIT_LIMIT + 1),
325                Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
326            ],
327            Ok(PrioritizationFeeDetails::default()),
328            ComputeBudget {
329                compute_unit_limit: MAX_COMPUTE_UNIT_LIMIT as u64,
330                ..ComputeBudget::default()
331            }
332        );
333        test!(
334            &[
335                Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
336                ComputeBudgetInstruction::set_compute_unit_limit(MAX_COMPUTE_UNIT_LIMIT),
337            ],
338            Ok(PrioritizationFeeDetails::default()),
339            ComputeBudget {
340                compute_unit_limit: MAX_COMPUTE_UNIT_LIMIT as u64,
341                ..ComputeBudget::default()
342            }
343        );
344        test!(
345            &[
346                Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
347                Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
348                Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
349                ComputeBudgetInstruction::set_compute_unit_limit(1),
350            ],
351            Ok(PrioritizationFeeDetails::default()),
352            ComputeBudget {
353                compute_unit_limit: 1,
354                ..ComputeBudget::default()
355            }
356        );
357
358        test!(
359            &[
360                Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
361                Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
362                Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
363                ComputeBudgetInstruction::set_compute_unit_limit(1), // ignored
364            ],
365            Ok(PrioritizationFeeDetails::default()),
366            ComputeBudget {
367                compute_unit_limit: DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT as u64 * 3,
368                ..ComputeBudget::default()
369            },
370            false,
371            true
372        );
373
374        // Prioritization fee
375        test!(
376            &[request_units_deprecated(1, 42)],
377            Ok(PrioritizationFeeDetails::new(
378                PrioritizationFeeType::Deprecated(42),
379                1,
380            )),
381            ComputeBudget {
382                compute_unit_limit: 1,
383                ..ComputeBudget::default()
384            },
385            false,
386            true
387        );
388
389        test!(
390            &[
391                ComputeBudgetInstruction::set_compute_unit_limit(1),
392                ComputeBudgetInstruction::set_compute_unit_price(42)
393            ],
394            Ok(PrioritizationFeeDetails::new(
395                PrioritizationFeeType::ComputeUnitPrice(42),
396                1
397            )),
398            ComputeBudget {
399                compute_unit_limit: 1,
400                ..ComputeBudget::default()
401            }
402        );
403
404        test!(
405            &[request_units_deprecated(1, u32::MAX)],
406            Ok(PrioritizationFeeDetails::new(
407                PrioritizationFeeType::Deprecated(u32::MAX as u64),
408                1
409            )),
410            ComputeBudget {
411                compute_unit_limit: 1,
412                ..ComputeBudget::default()
413            },
414            false,
415            true
416        );
417
418        // HeapFrame
419        test!(
420            &[],
421            Ok(PrioritizationFeeDetails::default()),
422            ComputeBudget {
423                compute_unit_limit: 0,
424                ..ComputeBudget::default()
425            }
426        );
427        test!(
428            &[
429                ComputeBudgetInstruction::request_heap_frame(40 * 1024),
430                Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
431            ],
432            Ok(PrioritizationFeeDetails::default()),
433            ComputeBudget {
434                compute_unit_limit: DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT as u64,
435                heap_size: Some(40 * 1024),
436                ..ComputeBudget::default()
437            }
438        );
439        test!(
440            &[
441                ComputeBudgetInstruction::request_heap_frame(40 * 1024 + 1),
442                Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
443            ],
444            Err(TransactionError::InstructionError(
445                0,
446                InstructionError::InvalidInstructionData,
447            )),
448            ComputeBudget::default()
449        );
450        test!(
451            &[
452                ComputeBudgetInstruction::request_heap_frame(31 * 1024),
453                Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
454            ],
455            Err(TransactionError::InstructionError(
456                0,
457                InstructionError::InvalidInstructionData,
458            )),
459            ComputeBudget::default()
460        );
461        test!(
462            &[
463                ComputeBudgetInstruction::request_heap_frame(MAX_HEAP_FRAME_BYTES + 1),
464                Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
465            ],
466            Err(TransactionError::InstructionError(
467                0,
468                InstructionError::InvalidInstructionData,
469            )),
470            ComputeBudget::default()
471        );
472        test!(
473            &[
474                Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
475                ComputeBudgetInstruction::request_heap_frame(MAX_HEAP_FRAME_BYTES),
476            ],
477            Ok(PrioritizationFeeDetails::default()),
478            ComputeBudget {
479                compute_unit_limit: DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT as u64,
480                heap_size: Some(MAX_HEAP_FRAME_BYTES as usize),
481                ..ComputeBudget::default()
482            }
483        );
484        test!(
485            &[
486                Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
487                Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
488                Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
489                ComputeBudgetInstruction::request_heap_frame(1),
490            ],
491            Err(TransactionError::InstructionError(
492                3,
493                InstructionError::InvalidInstructionData,
494            )),
495            ComputeBudget::default()
496        );
497
498        test!(
499            &[
500                Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
501                Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
502                Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
503                Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
504                Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
505                Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
506                Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
507                Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
508            ],
509            Ok(PrioritizationFeeDetails::default()),
510            ComputeBudget {
511                compute_unit_limit: DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT as u64 * 7,
512                ..ComputeBudget::default()
513            }
514        );
515
516        // Combined
517        test!(
518            &[
519                Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
520                ComputeBudgetInstruction::request_heap_frame(MAX_HEAP_FRAME_BYTES),
521                ComputeBudgetInstruction::set_compute_unit_limit(MAX_COMPUTE_UNIT_LIMIT),
522                ComputeBudgetInstruction::set_compute_unit_price(u64::MAX),
523            ],
524            Ok(PrioritizationFeeDetails::new(
525                PrioritizationFeeType::ComputeUnitPrice(u64::MAX),
526                MAX_COMPUTE_UNIT_LIMIT as u64,
527            )),
528            ComputeBudget {
529                compute_unit_limit: MAX_COMPUTE_UNIT_LIMIT as u64,
530                heap_size: Some(MAX_HEAP_FRAME_BYTES as usize),
531                ..ComputeBudget::default()
532            }
533        );
534
535        test!(
536            &[
537                Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
538                ComputeBudgetInstruction::request_heap_frame(MAX_HEAP_FRAME_BYTES),
539                ComputeBudgetInstruction::set_compute_unit_limit(MAX_COMPUTE_UNIT_LIMIT),
540                ComputeBudgetInstruction::set_compute_unit_price(u64::MAX),
541            ],
542            Err(TransactionError::InstructionError(
543                0,
544                InstructionError::InvalidInstructionData,
545            )),
546            ComputeBudget::default(),
547            false,
548            true
549        );
550
551        test!(
552            &[
553                Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
554                ComputeBudgetInstruction::set_compute_unit_limit(1),
555                ComputeBudgetInstruction::request_heap_frame(MAX_HEAP_FRAME_BYTES),
556                ComputeBudgetInstruction::set_compute_unit_price(u64::MAX),
557            ],
558            Ok(PrioritizationFeeDetails::new(
559                PrioritizationFeeType::ComputeUnitPrice(u64::MAX),
560                1
561            )),
562            ComputeBudget {
563                compute_unit_limit: 1,
564                heap_size: Some(MAX_HEAP_FRAME_BYTES as usize),
565                ..ComputeBudget::default()
566            }
567        );
568
569        test!(
570            &[
571                Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
572                request_units_deprecated(MAX_COMPUTE_UNIT_LIMIT, u32::MAX),
573                ComputeBudgetInstruction::request_heap_frame(MIN_HEAP_FRAME_BYTES as u32),
574            ],
575            Ok(PrioritizationFeeDetails::new(
576                PrioritizationFeeType::Deprecated(u32::MAX as u64),
577                MAX_COMPUTE_UNIT_LIMIT as u64,
578            )),
579            ComputeBudget {
580                compute_unit_limit: MAX_COMPUTE_UNIT_LIMIT as u64,
581                heap_size: Some(MIN_HEAP_FRAME_BYTES as usize),
582                ..ComputeBudget::default()
583            },
584            false,
585            true
586        );
587
588        // Duplicates
589        test!(
590            &[
591                Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
592                ComputeBudgetInstruction::set_compute_unit_limit(MAX_COMPUTE_UNIT_LIMIT),
593                ComputeBudgetInstruction::set_compute_unit_limit(MAX_COMPUTE_UNIT_LIMIT - 1),
594            ],
595            Err(TransactionError::DuplicateInstruction(2)),
596            ComputeBudget::default()
597        );
598
599        test!(
600            &[
601                Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
602                ComputeBudgetInstruction::request_heap_frame(MIN_HEAP_FRAME_BYTES as u32),
603                ComputeBudgetInstruction::request_heap_frame(MAX_HEAP_FRAME_BYTES as u32),
604            ],
605            Err(TransactionError::DuplicateInstruction(2)),
606            ComputeBudget::default()
607        );
608
609        test!(
610            &[
611                Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
612                ComputeBudgetInstruction::set_compute_unit_price(0),
613                ComputeBudgetInstruction::set_compute_unit_price(u64::MAX),
614            ],
615            Err(TransactionError::DuplicateInstruction(2)),
616            ComputeBudget::default()
617        );
618    }
619
620    #[test]
621    fn test_process_instructions_disable_request_heap_frame() {
622        // assert empty message results default compute budget and fee
623        test!(
624            &[],
625            Ok(PrioritizationFeeDetails::default()),
626            ComputeBudget {
627                compute_unit_limit: 0,
628                ..ComputeBudget::default()
629            },
630            true,
631            false
632        );
633
634        // assert requesting heap frame when feature is disable will result instruction error
635        test!(
636            &[
637                ComputeBudgetInstruction::request_heap_frame(40 * 1024),
638                Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
639            ],
640            Err(TransactionError::InstructionError(
641                0,
642                InstructionError::InvalidInstructionData
643            )),
644            ComputeBudget::default(),
645            true,
646            false
647        );
648        test!(
649            &[
650                Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
651                ComputeBudgetInstruction::request_heap_frame(MAX_HEAP_FRAME_BYTES),
652            ],
653            Err(TransactionError::InstructionError(
654                1,
655                InstructionError::InvalidInstructionData,
656            )),
657            ComputeBudget::default(),
658            true,
659            false
660        );
661        test!(
662            &[
663                Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
664                ComputeBudgetInstruction::request_heap_frame(MAX_HEAP_FRAME_BYTES),
665                ComputeBudgetInstruction::set_compute_unit_limit(MAX_COMPUTE_UNIT_LIMIT),
666                ComputeBudgetInstruction::set_compute_unit_price(u64::MAX),
667            ],
668            Err(TransactionError::InstructionError(
669                1,
670                InstructionError::InvalidInstructionData,
671            )),
672            ComputeBudget::default(),
673            true,
674            false
675        );
676        test!(
677            &[
678                Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
679                ComputeBudgetInstruction::set_compute_unit_limit(1),
680                ComputeBudgetInstruction::request_heap_frame(MAX_HEAP_FRAME_BYTES),
681                ComputeBudgetInstruction::set_compute_unit_price(u64::MAX),
682            ],
683            Err(TransactionError::InstructionError(
684                2,
685                InstructionError::InvalidInstructionData,
686            )),
687            ComputeBudget::default(),
688            true,
689            false
690        );
691
692        // assert normal results when not requesting heap frame when the feature is disabled
693        test!(
694            &[
695                Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
696                Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
697                Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
698                Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
699                Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
700                Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
701                Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
702                Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]),
703            ],
704            Ok(PrioritizationFeeDetails::default()),
705            ComputeBudget {
706                compute_unit_limit: DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT as u64 * 7,
707                ..ComputeBudget::default()
708            },
709            true,
710            false
711        );
712    }
713}