Limit number of accounts that a transaction can lock (#22201)

This commit is contained in:
Justin Starry
2022-01-04 14:25:23 +08:00
committed by GitHub
parent 8b6310b179
commit 2b5e00d36d
10 changed files with 250 additions and 113 deletions

View File

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

View File

@ -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
}
}

View File

@ -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