* Add fees to tx-wide caps (#22081)
(cherry picked from commit 3d9874b95a
)
# Conflicts:
# runtime/src/bank.rs
* resolve
Co-authored-by: Jack May <jack@solana.com>
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)]
|
||||
@ -66,14 +63,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,
|
||||
@ -94,25 +96,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
|
||||
@ -125,7 +129,7 @@ impl ComputeBudget {
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
Ok(requested_additional_fee)
|
||||
}
|
||||
}
|
||||
|
||||
@ -134,8 +138,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},
|
||||
},
|
||||
};
|
||||
|
||||
@ -147,24 +156,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()
|
||||
@ -172,21 +180,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()
|
||||
@ -197,20 +202,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()
|
||||
@ -254,7 +259,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()
|
||||
@ -267,7 +272,7 @@ mod tests {
|
||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
|
||||
ComputeBudgetInstruction::request_heap_frame(1), // ignored
|
||||
],
|
||||
Ok(()),
|
||||
Ok(0),
|
||||
ComputeBudget::default()
|
||||
);
|
||||
|
||||
@ -276,9 +281,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),
|
||||
|
@ -1670,13 +1670,14 @@ mod tests {
|
||||
feature_set.deactivate(&requestable_heap_size::id());
|
||||
let mut invoke_context = InvokeContext::new_mock(&accounts, &[]);
|
||||
invoke_context.feature_set = Arc::new(feature_set);
|
||||
invoke_context.compute_budget = ComputeBudget::new(false);
|
||||
|
||||
invoke_context
|
||||
.push(&noop_message, &noop_message.instructions()[0], &[0], &[])
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
*invoke_context.get_compute_budget(),
|
||||
ComputeBudget::default()
|
||||
ComputeBudget::new(false)
|
||||
);
|
||||
invoke_context.pop();
|
||||
|
||||
@ -1686,7 +1687,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(),
|
||||
@ -1699,7 +1700,7 @@ mod tests {
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
*invoke_context.get_compute_budget(),
|
||||
ComputeBudget::default()
|
||||
ComputeBudget::new(false)
|
||||
);
|
||||
invoke_context.pop();
|
||||
}
|
||||
|
Reference in New Issue
Block a user