Add fees to tx-wide caps (#22081)
This commit is contained in:
@ -1,16 +1,13 @@
|
||||
use {
|
||||
solana_sdk::{
|
||||
borsh::try_from_slice_unchecked,
|
||||
compute_budget::{self, ComputeBudgetInstruction},
|
||||
entrypoint::HEAP_LENGTH as MIN_HEAP_FRAME_BYTES,
|
||||
feature_set::{requestable_heap_size, FeatureSet},
|
||||
instruction::InstructionError,
|
||||
transaction::{SanitizedTransaction, TransactionError},
|
||||
},
|
||||
std::sync::Arc,
|
||||
use solana_sdk::{
|
||||
borsh::try_from_slice_unchecked,
|
||||
compute_budget::{self, ComputeBudgetInstruction},
|
||||
entrypoint::HEAP_LENGTH as MIN_HEAP_FRAME_BYTES,
|
||||
instruction::InstructionError,
|
||||
message::SanitizedMessage,
|
||||
transaction::TransactionError,
|
||||
};
|
||||
|
||||
const MAX_UNITS: u32 = 1_000_000;
|
||||
const MAX_UNITS: u32 = 1_400_000;
|
||||
const MAX_HEAP_FRAME_BYTES: u32 = 256 * 1024;
|
||||
|
||||
#[cfg(RUSTC_WITH_SPECIALIZATION)]
|
||||
@ -68,14 +65,19 @@ pub struct ComputeBudget {
|
||||
|
||||
impl Default for ComputeBudget {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
Self::new(true)
|
||||
}
|
||||
}
|
||||
|
||||
impl ComputeBudget {
|
||||
pub fn new() -> Self {
|
||||
pub fn new(use_max_units_default: bool) -> Self {
|
||||
let max_units = if use_max_units_default {
|
||||
MAX_UNITS
|
||||
} else {
|
||||
200_000
|
||||
} as u64;
|
||||
ComputeBudget {
|
||||
max_units: 200_000,
|
||||
max_units,
|
||||
log_64_units: 100,
|
||||
create_program_address_units: 1500,
|
||||
invoke_units: 1000,
|
||||
@ -97,25 +99,27 @@ impl ComputeBudget {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn process_transaction(
|
||||
pub fn process_message(
|
||||
&mut self,
|
||||
tx: &SanitizedTransaction,
|
||||
feature_set: Arc<FeatureSet>,
|
||||
) -> Result<(), TransactionError> {
|
||||
message: &SanitizedMessage,
|
||||
requestable_heap_size: bool,
|
||||
) -> Result<u64, TransactionError> {
|
||||
let mut requested_additional_fee = 0;
|
||||
let error = TransactionError::InstructionError(0, InstructionError::InvalidInstructionData);
|
||||
// Compute budget instruction must be in the 1st 3 instructions (avoid
|
||||
// nonce marker), otherwise ignored
|
||||
for (program_id, instruction) in tx.message().program_instructions_iter().take(3) {
|
||||
for (program_id, instruction) in message.program_instructions_iter().take(3) {
|
||||
if compute_budget::check_id(program_id) {
|
||||
match try_from_slice_unchecked(&instruction.data) {
|
||||
Ok(ComputeBudgetInstruction::RequestUnits(units)) => {
|
||||
if units > MAX_UNITS {
|
||||
return Err(error);
|
||||
}
|
||||
self.max_units = units as u64;
|
||||
Ok(ComputeBudgetInstruction::RequestUnits {
|
||||
units,
|
||||
additional_fee,
|
||||
}) => {
|
||||
self.max_units = units.min(MAX_UNITS) as u64;
|
||||
requested_additional_fee = additional_fee as u64;
|
||||
}
|
||||
Ok(ComputeBudgetInstruction::RequestHeapFrame(bytes)) => {
|
||||
if !feature_set.is_active(&requestable_heap_size::id())
|
||||
if !requestable_heap_size
|
||||
|| bytes > MAX_HEAP_FRAME_BYTES
|
||||
|| bytes < MIN_HEAP_FRAME_BYTES as u32
|
||||
|| bytes % 1024 != 0
|
||||
@ -128,7 +132,7 @@ impl ComputeBudget {
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
Ok(requested_additional_fee)
|
||||
}
|
||||
}
|
||||
|
||||
@ -137,8 +141,13 @@ mod tests {
|
||||
use {
|
||||
super::*,
|
||||
solana_sdk::{
|
||||
hash::Hash, instruction::Instruction, message::Message, pubkey::Pubkey,
|
||||
signature::Keypair, signer::Signer, transaction::Transaction,
|
||||
hash::Hash,
|
||||
instruction::Instruction,
|
||||
message::Message,
|
||||
pubkey::Pubkey,
|
||||
signature::Keypair,
|
||||
signer::Signer,
|
||||
transaction::{SanitizedTransaction, Transaction},
|
||||
},
|
||||
};
|
||||
|
||||
@ -150,24 +159,23 @@ mod tests {
|
||||
Message::new($instructions, Some(&payer_keypair.pubkey())),
|
||||
Hash::default(),
|
||||
));
|
||||
let feature_set = Arc::new(FeatureSet::all_enabled());
|
||||
let mut compute_budget = ComputeBudget::default();
|
||||
let result = compute_budget.process_transaction(&tx, feature_set);
|
||||
assert_eq!($expected_error as Result<(), TransactionError>, result);
|
||||
let result = compute_budget.process_message(&tx.message(), true);
|
||||
assert_eq!($expected_error, result);
|
||||
assert_eq!(compute_budget, $expected_budget);
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_process_transaction() {
|
||||
fn test_process_mesage() {
|
||||
// Units
|
||||
test!(&[], Ok(()), ComputeBudget::default());
|
||||
test!(&[], Ok(0), ComputeBudget::default());
|
||||
test!(
|
||||
&[
|
||||
ComputeBudgetInstruction::request_units(1),
|
||||
ComputeBudgetInstruction::request_units(1, 0),
|
||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
|
||||
],
|
||||
Ok(()),
|
||||
Ok(0),
|
||||
ComputeBudget {
|
||||
max_units: 1,
|
||||
..ComputeBudget::default()
|
||||
@ -175,21 +183,18 @@ mod tests {
|
||||
);
|
||||
test!(
|
||||
&[
|
||||
ComputeBudgetInstruction::request_units(MAX_UNITS + 1),
|
||||
ComputeBudgetInstruction::request_units(MAX_UNITS + 1, 0),
|
||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
|
||||
],
|
||||
Err(TransactionError::InstructionError(
|
||||
0,
|
||||
InstructionError::InvalidInstructionData,
|
||||
)),
|
||||
Ok(0),
|
||||
ComputeBudget::default()
|
||||
);
|
||||
test!(
|
||||
&[
|
||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
|
||||
ComputeBudgetInstruction::request_units(MAX_UNITS),
|
||||
ComputeBudgetInstruction::request_units(MAX_UNITS, 0),
|
||||
],
|
||||
Ok(()),
|
||||
Ok(0),
|
||||
ComputeBudget {
|
||||
max_units: MAX_UNITS as u64,
|
||||
..ComputeBudget::default()
|
||||
@ -200,20 +205,20 @@ mod tests {
|
||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
|
||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
|
||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
|
||||
ComputeBudgetInstruction::request_units(1),
|
||||
ComputeBudgetInstruction::request_units(1, 0),
|
||||
],
|
||||
Ok(()),
|
||||
Ok(0),
|
||||
ComputeBudget::default()
|
||||
);
|
||||
|
||||
// HeapFrame
|
||||
test!(&[], Ok(()), ComputeBudget::default());
|
||||
test!(&[], Ok(0), ComputeBudget::default());
|
||||
test!(
|
||||
&[
|
||||
ComputeBudgetInstruction::request_heap_frame(40 * 1024),
|
||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
|
||||
],
|
||||
Ok(()),
|
||||
Ok(0),
|
||||
ComputeBudget {
|
||||
heap_size: Some(40 * 1024),
|
||||
..ComputeBudget::default()
|
||||
@ -257,7 +262,7 @@ mod tests {
|
||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
|
||||
ComputeBudgetInstruction::request_heap_frame(MAX_HEAP_FRAME_BYTES),
|
||||
],
|
||||
Ok(()),
|
||||
Ok(0),
|
||||
ComputeBudget {
|
||||
heap_size: Some(MAX_HEAP_FRAME_BYTES as usize),
|
||||
..ComputeBudget::default()
|
||||
@ -270,7 +275,7 @@ mod tests {
|
||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
|
||||
ComputeBudgetInstruction::request_heap_frame(1), // ignored
|
||||
],
|
||||
Ok(()),
|
||||
Ok(0),
|
||||
ComputeBudget::default()
|
||||
);
|
||||
|
||||
@ -279,9 +284,9 @@ mod tests {
|
||||
&[
|
||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
|
||||
ComputeBudgetInstruction::request_heap_frame(MAX_HEAP_FRAME_BYTES),
|
||||
ComputeBudgetInstruction::request_units(MAX_UNITS),
|
||||
ComputeBudgetInstruction::request_units(MAX_UNITS, 0),
|
||||
],
|
||||
Ok(()),
|
||||
Ok(0),
|
||||
ComputeBudget {
|
||||
max_units: MAX_UNITS as u64,
|
||||
heap_size: Some(MAX_HEAP_FRAME_BYTES as usize),
|
||||
|
@ -1610,11 +1610,12 @@ mod tests {
|
||||
let mut transaction_context = TransactionContext::new(accounts, 1, 3);
|
||||
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
||||
invoke_context.feature_set = Arc::new(feature_set);
|
||||
invoke_context.compute_budget = ComputeBudget::new(false);
|
||||
|
||||
invoke_context.push(&[], &[0], &[]).unwrap();
|
||||
assert_eq!(
|
||||
*invoke_context.get_compute_budget(),
|
||||
ComputeBudget::default()
|
||||
ComputeBudget::new(false)
|
||||
);
|
||||
invoke_context.pop().unwrap();
|
||||
|
||||
@ -1622,7 +1623,7 @@ mod tests {
|
||||
let expected_compute_budget = ComputeBudget {
|
||||
max_units: 500_000,
|
||||
heap_size: Some(256_usize.saturating_mul(1024)),
|
||||
..ComputeBudget::default()
|
||||
..ComputeBudget::new(false)
|
||||
};
|
||||
assert_eq!(
|
||||
*invoke_context.get_compute_budget(),
|
||||
@ -1633,7 +1634,7 @@ mod tests {
|
||||
invoke_context.push(&[], &[0], &[]).unwrap();
|
||||
assert_eq!(
|
||||
*invoke_context.get_compute_budget(),
|
||||
ComputeBudget::default()
|
||||
ComputeBudget::new(false)
|
||||
);
|
||||
invoke_context.pop().unwrap();
|
||||
}
|
||||
|
Reference in New Issue
Block a user