From 310c7a4f9d22f85005d53455562ccd602e4f258a Mon Sep 17 00:00:00 2001 From: Tao Zhu <82401714+taozhu-chicago@users.noreply.github.com> Date: Fri, 15 Dec 2023 18:39:19 -0600 Subject: [PATCH] add requested limits to static meta (#34361) * add requested limits to static meta * rebase to catch up on compue_budget_processor no longer needs feature_set; update requested limits as flat fields of static meta without expiry --- .../src/runtime_transaction.rs | 200 ++++++++++++------ runtime-transaction/src/transaction_meta.rs | 20 +- 2 files changed, 151 insertions(+), 69 deletions(-) diff --git a/runtime-transaction/src/runtime_transaction.rs b/runtime-transaction/src/runtime_transaction.rs index df6acbccd..3ca7d4fb7 100644 --- a/runtime-transaction/src/runtime_transaction.rs +++ b/runtime-transaction/src/runtime_transaction.rs @@ -11,6 +11,9 @@ //! with its dynamic metadata loaded. use { crate::transaction_meta::{DynamicMeta, StaticMeta, TransactionMeta}, + solana_program_runtime::compute_budget_processor::{ + process_compute_budget_instructions, ComputeBudgetLimits, + }, solana_sdk::{ hash::Hash, message::{AddressLoader, SanitizedMessage, SanitizedVersionedMessage}, @@ -48,6 +51,15 @@ impl StaticMeta for RuntimeTransaction { fn is_simple_vote_tx(&self) -> bool { self.meta.is_simple_vote_tx } + fn compute_unit_limit(&self) -> u32 { + self.meta.compute_unit_limit + } + fn compute_unit_price(&self) -> u64 { + self.meta.compute_unit_price + } + fn loaded_accounts_bytes(&self) -> u32 { + self.meta.loaded_accounts_bytes + } } impl DynamicMeta for RuntimeTransaction {} @@ -65,9 +77,18 @@ impl RuntimeTransaction { ); let (signatures, message) = sanitized_versioned_tx.destruct(); - meta.set_message_hash(message_hash.unwrap_or_else(|| message.message.hash())); + let ComputeBudgetLimits { + compute_unit_limit, + compute_unit_price, + loaded_accounts_bytes, + .. + } = process_compute_budget_instructions(message.program_instructions_iter())?; + meta.set_compute_unit_limit(compute_unit_limit); + meta.set_compute_unit_price(compute_unit_price); + meta.set_loaded_accounts_bytes(loaded_accounts_bytes); + Ok(Self { signatures, message, @@ -109,6 +130,7 @@ mod tests { }, solana_sdk::{ compute_budget::ComputeBudgetInstruction, + instruction::Instruction, message::Message, signer::{keypair::Keypair, Signer}, transaction::{SimpleAddressLoader, Transaction, VersionedTransaction}, @@ -131,91 +153,98 @@ mod tests { SanitizedVersionedTransaction::try_from(VersionedTransaction::from(vote_tx)).unwrap() } - fn non_vote_sanitized_versioned_transaction( - compute_unit_price: u64, - ) -> SanitizedVersionedTransaction { - let from_keypair = Keypair::new(); - let ixs = vec![ - system_instruction::transfer( + fn non_vote_sanitized_versioned_transaction() -> SanitizedVersionedTransaction { + TestTransaction::new().to_sanitized_versioned_transaction() + } + + // Simple transfer transaction for testing, it does not support vote instruction + // because simple vote transaction will not request limits + struct TestTransaction { + from_keypair: Keypair, + hash: Hash, + instructions: Vec, + } + + impl TestTransaction { + fn new() -> Self { + let from_keypair = Keypair::new(); + let instructions = vec![system_instruction::transfer( &from_keypair.pubkey(), &solana_sdk::pubkey::new_rand(), 1, - ), - ComputeBudgetInstruction::set_compute_unit_price(compute_unit_price), - ]; - let message = Message::new(&ixs, Some(&from_keypair.pubkey())); - let tx = Transaction::new(&[&from_keypair], message, Hash::new_unique()); - SanitizedVersionedTransaction::try_from(VersionedTransaction::from(tx)).unwrap() - } + )]; + TestTransaction { + from_keypair, + hash: Hash::new_unique(), + instructions, + } + } - fn get_transaction_meta( - svt: SanitizedVersionedTransaction, - hash: Option, - is_simple_vote: Option, - ) -> TransactionMeta { - RuntimeTransaction::::try_from(svt, hash, is_simple_vote) - .unwrap() - .meta + fn add_compute_unit_limit(&mut self, val: u32) -> &mut TestTransaction { + self.instructions + .push(ComputeBudgetInstruction::set_compute_unit_limit(val)); + self + } + + fn add_compute_unit_price(&mut self, val: u64) -> &mut TestTransaction { + self.instructions + .push(ComputeBudgetInstruction::set_compute_unit_price(val)); + self + } + + fn add_loaded_accounts_bytes(&mut self, val: u32) -> &mut TestTransaction { + self.instructions + .push(ComputeBudgetInstruction::set_loaded_accounts_data_size_limit(val)); + self + } + + fn to_sanitized_versioned_transaction(&self) -> SanitizedVersionedTransaction { + let message = Message::new(&self.instructions, Some(&self.from_keypair.pubkey())); + let tx = Transaction::new(&[&self.from_keypair], message, self.hash); + SanitizedVersionedTransaction::try_from(VersionedTransaction::from(tx)).unwrap() + } } #[test] - fn test_new_runtime_transaction_static() { - let hash = Hash::new_unique(); - let compute_unit_price = 1_000; + fn test_runtime_transaction_is_vote_meta() { + fn get_is_simple_vote( + svt: SanitizedVersionedTransaction, + is_simple_vote: Option, + ) -> bool { + RuntimeTransaction::::try_from(svt, None, is_simple_vote) + .unwrap() + .meta + .is_simple_vote_tx + } - assert_eq!( - TransactionMeta { - message_hash: hash, - is_simple_vote_tx: false, - }, - get_transaction_meta( - non_vote_sanitized_versioned_transaction(compute_unit_price), - Some(hash), - None - ) - ); + assert!(!get_is_simple_vote( + non_vote_sanitized_versioned_transaction(), + None + )); - assert_eq!( - TransactionMeta { - message_hash: hash, - is_simple_vote_tx: true, - }, - get_transaction_meta( - non_vote_sanitized_versioned_transaction(compute_unit_price), - Some(hash), - Some(true), // override - ) - ); + assert!(get_is_simple_vote( + non_vote_sanitized_versioned_transaction(), + Some(true), // override + )); - assert_eq!( - TransactionMeta { - message_hash: hash, - is_simple_vote_tx: true, - }, - get_transaction_meta(vote_sanitized_versioned_transaction(), Some(hash), None) - ); + assert!(get_is_simple_vote( + vote_sanitized_versioned_transaction(), + None + )); - assert_eq!( - TransactionMeta { - message_hash: hash, - is_simple_vote_tx: false, - }, - get_transaction_meta( - vote_sanitized_versioned_transaction(), - Some(hash), - Some(false), // override - ) - ); + assert!(!get_is_simple_vote( + vote_sanitized_versioned_transaction(), + Some(false), // override + )); } #[test] - fn test_advance_transaction_type() { + fn test_advancing_transaction_type() { let hash = Hash::new_unique(); - let compute_unit_price = 999; let statically_loaded_transaction = RuntimeTransaction::::try_from( - non_vote_sanitized_versioned_transaction(compute_unit_price), + non_vote_sanitized_versioned_transaction(), Some(hash), None, ) @@ -234,4 +263,39 @@ mod tests { assert_eq!(hash, *dynamically_loaded_transaction.message_hash()); assert!(!dynamically_loaded_transaction.is_simple_vote_tx()); } + + #[test] + fn test_runtime_transaction_static_meta() { + let hash = Hash::new_unique(); + let compute_unit_limit = 250_000; + let compute_unit_price = 1_000; + let loaded_accounts_bytes = 1_024; + let mut test_transaction = TestTransaction::new(); + + let runtime_transaction_static = RuntimeTransaction::::try_from( + test_transaction + .add_compute_unit_limit(compute_unit_limit) + .add_compute_unit_price(compute_unit_price) + .add_loaded_accounts_bytes(loaded_accounts_bytes) + .to_sanitized_versioned_transaction(), + Some(hash), + None, + ) + .unwrap(); + + assert_eq!(&hash, runtime_transaction_static.message_hash()); + assert!(!runtime_transaction_static.is_simple_vote_tx()); + assert_eq!( + compute_unit_limit, + runtime_transaction_static.compute_unit_limit() + ); + assert_eq!( + compute_unit_price, + runtime_transaction_static.compute_unit_price() + ); + assert_eq!( + loaded_accounts_bytes, + runtime_transaction_static.loaded_accounts_bytes() + ); + } } diff --git a/runtime-transaction/src/transaction_meta.rs b/runtime-transaction/src/transaction_meta.rs index 2eec699f2..f46fa39c3 100644 --- a/runtime-transaction/src/transaction_meta.rs +++ b/runtime-transaction/src/transaction_meta.rs @@ -14,10 +14,13 @@ use solana_sdk::hash::Hash; /// metadata can be extracted statically from sanitized transaction, -/// for example: message hash, simple-vote-tx flag, compute budget limits, +/// for example: message hash, simple-vote-tx flag, limits set by instructions pub trait StaticMeta { fn message_hash(&self) -> &Hash; fn is_simple_vote_tx(&self) -> bool; + fn compute_unit_limit(&self) -> u32; + fn compute_unit_price(&self) -> u64; + fn loaded_accounts_bytes(&self) -> u32; } /// Statically loaded meta is a supertrait of Dynamically loaded meta, when @@ -31,6 +34,9 @@ pub trait DynamicMeta: StaticMeta {} pub struct TransactionMeta { pub(crate) message_hash: Hash, pub(crate) is_simple_vote_tx: bool, + pub(crate) compute_unit_limit: u32, + pub(crate) compute_unit_price: u64, + pub(crate) loaded_accounts_bytes: u32, } impl TransactionMeta { @@ -41,4 +47,16 @@ impl TransactionMeta { pub(crate) fn set_is_simple_vote_tx(&mut self, is_simple_vote_tx: bool) { self.is_simple_vote_tx = is_simple_vote_tx; } + + pub(crate) fn set_compute_unit_limit(&mut self, compute_unit_limit: u32) { + self.compute_unit_limit = compute_unit_limit; + } + + pub(crate) fn set_compute_unit_price(&mut self, compute_unit_price: u64) { + self.compute_unit_price = compute_unit_price; + } + + pub(crate) fn set_loaded_accounts_bytes(&mut self, loaded_accounts_bytes: u32) { + self.loaded_accounts_bytes = loaded_accounts_bytes; + } }