Add fees to tx-wide caps (#22081)

This commit is contained in:
Jack May
2022-02-11 16:23:16 -08:00
committed by GitHub
parent 12dffc105a
commit 3d9874b95a
15 changed files with 1149 additions and 186 deletions

View File

@ -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),

View File

@ -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();
}