Add fees to tx-wide caps (backport #22081) (#23095)

* 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:
mergify[bot]
2022-02-15 01:36:02 +00:00
committed by GitHub
parent 85570ac207
commit 2c8cfdb3f3
15 changed files with 1145 additions and 185 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)]
@ -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),

View File

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