Limit number of accounts that a transaction can lock (#22201)
This commit is contained in:
@ -287,6 +287,10 @@ pub mod cap_accounts_data_len {
|
||||
solana_sdk::declare_id!("capRxUrBjNkkCpjrJxPGfPaWijB7q3JoDfsWXAnt46r");
|
||||
}
|
||||
|
||||
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> = [
|
||||
@ -353,6 +357,7 @@ lazy_static! {
|
||||
(allow_votes_to_directly_update_vote_state::id(), "enable direct vote state update"),
|
||||
(reject_all_elf_rw::id(), "reject all read-write data in program elfs"),
|
||||
(cap_accounts_data_len::id(), "cap the accounts data len"),
|
||||
(max_tx_account_locks::id(), "enforce max number of locked accounts per transaction"),
|
||||
/*************** ADD NEW FEATURES HERE ***************/
|
||||
]
|
||||
.iter()
|
||||
|
@ -105,6 +105,10 @@ pub enum TransactionError {
|
||||
/// 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 {
|
||||
@ -114,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