* Limit number of accounts that a transaction can lock (#22201)
(cherry picked from commit 2b5e00d36d)
# Conflicts:
# accountsdb-plugin-postgres/src/postgres_client/postgres_client_transaction.rs
# runtime/src/accounts.rs
# runtime/src/bank.rs
# sdk/src/feature_set.rs
# sdk/src/transaction/error.rs
# storage-proto/proto/transaction_by_addr.proto
# storage-proto/src/convert.rs
* resolve conflicts
Co-authored-by: Justin Starry <justin@solana.com>
This commit is contained in:
@@ -275,6 +275,10 @@ pub mod reject_all_elf_rw {
|
||||
solana_sdk::declare_id!("DeMpxgMq51j3rZfNK2hQKZyXknQvqevPSFPJFNTbXxsS");
|
||||
}
|
||||
|
||||
pub mod max_tx_account_locks {
|
||||
solana_sdk::declare_id!("CBkDroRDqm8HwHe6ak9cguPjUomrASEkfmxEaZ5CNNxz");
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
/// Map of feature identifiers to user-visible description
|
||||
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
|
||||
@@ -338,6 +342,7 @@ lazy_static! {
|
||||
(reject_non_rent_exempt_vote_withdraws::id(), "fail vote withdraw instructions which leave the account non-rent-exempt"),
|
||||
(evict_invalid_stakes_cache_entries::id(), "evict invalid stakes cache entries on epoch boundaries"),
|
||||
(reject_all_elf_rw::id(), "reject all read-write data in program elfs"),
|
||||
(max_tx_account_locks::id(), "enforce max number of locked accounts per transaction"),
|
||||
/*************** ADD NEW FEATURES HERE ***************/
|
||||
]
|
||||
.iter()
|
||||
|
||||
@@ -101,6 +101,14 @@ pub enum TransactionError {
|
||||
/// Transaction would exceed max account limit within the block
|
||||
#[error("Transaction would exceed max account limit within the block")]
|
||||
WouldExceedMaxAccountCostLimit,
|
||||
|
||||
/// Transaction would exceed max account data limit within the block
|
||||
#[error("Transaction would exceed max account data limit within the block")]
|
||||
WouldExceedMaxAccountDataCostLimit,
|
||||
|
||||
/// Transaction locked too many accounts
|
||||
#[error("Transaction locked too many accounts")]
|
||||
TooManyAccountLocks,
|
||||
}
|
||||
|
||||
impl From<SanitizeError> for TransactionError {
|
||||
@@ -110,12 +118,7 @@ impl From<SanitizeError> for TransactionError {
|
||||
}
|
||||
|
||||
impl From<SanitizeMessageError> for TransactionError {
|
||||
fn from(err: SanitizeMessageError) -> Self {
|
||||
match err {
|
||||
SanitizeMessageError::IndexOutOfBounds
|
||||
| SanitizeMessageError::ValueOutOfBounds
|
||||
| SanitizeMessageError::InvalidValue => Self::SanitizeFailure,
|
||||
SanitizeMessageError::DuplicateAccountKey => Self::AccountLoadedTwice,
|
||||
}
|
||||
fn from(_err: SanitizeMessageError) -> Self {
|
||||
Self::SanitizeFailure
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,11 @@ use {
|
||||
std::sync::Arc,
|
||||
};
|
||||
|
||||
/// Maximum number of accounts that a transaction may lock.
|
||||
/// 64 was chosen because it is roughly twice the previous
|
||||
/// number of account keys that could fit in a legacy tx.
|
||||
pub const MAX_TX_ACCOUNT_LOCKS: usize = 64;
|
||||
|
||||
/// Sanitized transaction and the hash of its message
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SanitizedTransaction {
|
||||
@@ -59,10 +64,6 @@ impl SanitizedTransaction {
|
||||
}),
|
||||
};
|
||||
|
||||
if message.has_duplicates() {
|
||||
return Err(TransactionError::AccountLoadedTwice);
|
||||
}
|
||||
|
||||
let is_simple_vote_tx = is_simple_vote_tx.unwrap_or_else(|| {
|
||||
let mut ix_iter = message.program_instructions_iter();
|
||||
ix_iter.next().map(|(program_id, _ix)| program_id) == Some(&crate::vote::program::id())
|
||||
@@ -79,10 +80,6 @@ impl SanitizedTransaction {
|
||||
pub fn try_from_legacy_transaction(tx: Transaction) -> Result<Self> {
|
||||
tx.sanitize()?;
|
||||
|
||||
if tx.message.has_duplicates() {
|
||||
return Err(TransactionError::AccountLoadedTwice);
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
message_hash: tx.message.hash(),
|
||||
message: SanitizedMessage::Legacy(tx.message),
|
||||
@@ -143,8 +140,24 @@ impl SanitizedTransaction {
|
||||
}
|
||||
}
|
||||
|
||||
/// Validate and return the account keys locked by this transaction
|
||||
pub fn get_account_locks(
|
||||
&self,
|
||||
feature_set: &feature_set::FeatureSet,
|
||||
) -> Result<TransactionAccountLocks> {
|
||||
if self.message.has_duplicates() {
|
||||
Err(TransactionError::AccountLoadedTwice)
|
||||
} else if feature_set.is_active(&feature_set::max_tx_account_locks::id())
|
||||
&& self.message.account_keys_len() > MAX_TX_ACCOUNT_LOCKS
|
||||
{
|
||||
Err(TransactionError::TooManyAccountLocks)
|
||||
} else {
|
||||
Ok(self.get_account_locks_unchecked())
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the list of accounts that must be locked during processing this transaction.
|
||||
pub fn get_account_locks(&self) -> TransactionAccountLocks {
|
||||
pub fn get_account_locks_unchecked(&self) -> TransactionAccountLocks {
|
||||
let message = &self.message;
|
||||
let num_readonly_accounts = message.num_readonly_accounts();
|
||||
let num_writable_accounts = message
|
||||
|
||||
Reference in New Issue
Block a user