cbe_program_runtime/
compute_budget.rs

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