Ensure blocks do not exceed the max accounts data size during Replay Stage (#23422)

This commit is contained in:
Brooks Prumo 2022-03-10 10:24:31 -06:00 committed by GitHub
parent 58c0db9704
commit 3c6840050c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 56 additions and 24 deletions

View File

@ -42,6 +42,7 @@ use {
feature_set, feature_set,
genesis_config::GenesisConfig, genesis_config::GenesisConfig,
hash::Hash, hash::Hash,
instruction::InstructionError,
pubkey::Pubkey, pubkey::Pubkey,
signature::{Keypair, Signature}, signature::{Keypair, Signature},
timing, timing,
@ -224,6 +225,13 @@ fn execute_batch(
.. ..
} = tx_results; } = tx_results;
if bank
.feature_set
.is_active(&feature_set::cap_accounts_data_len::id())
{
check_accounts_data_size(&execution_results)?;
}
if let Some(transaction_status_sender) = transaction_status_sender { if let Some(transaction_status_sender) = transaction_status_sender {
let transactions = batch.sanitized_transactions().to_vec(); let transactions = batch.sanitized_transactions().to_vec();
let post_token_balances = if record_token_balances { let post_token_balances = if record_token_balances {
@ -1540,6 +1548,30 @@ pub fn fill_blockstore_slot_with_ticks(
last_entry_hash last_entry_hash
} }
/// Check the transaction execution results to see if any instruction errored by exceeding the max
/// accounts data size limit for all slots. If yes, the whole block needs to be failed.
fn check_accounts_data_size<'a>(
execution_results: impl IntoIterator<Item = &'a TransactionExecutionResult>,
) -> Result<()> {
if let Some(result) = execution_results
.into_iter()
.map(|execution_result| execution_result.flattened_result())
.find(|result| {
matches!(
result,
Err(TransactionError::InstructionError(
_,
InstructionError::MaxAccountsDataSizeExceeded
)),
)
})
{
return result;
}
Ok(())
}
#[cfg(test)] #[cfg(test)]
pub mod tests { pub mod tests {
use { use {

View File

@ -80,7 +80,7 @@ impl AccountsDataMeter {
/// the remaining space, return an error and *do not* adjust accounts data space. /// the remaining space, return an error and *do not* adjust accounts data space.
pub fn adjust_delta(&mut self, amount: i64) -> Result<(), InstructionError> { pub fn adjust_delta(&mut self, amount: i64) -> Result<(), InstructionError> {
if amount > self.remaining() as i64 { if amount > self.remaining() as i64 {
return Err(InstructionError::AccountsDataBudgetExceeded); return Err(InstructionError::MaxAccountsDataSizeExceeded);
} }
self.adjust_delta_unchecked(amount); self.adjust_delta_unchecked(amount);
Ok(()) Ok(())

View File

@ -1745,7 +1745,7 @@ mod tests {
assert!(result.is_err()); assert!(result.is_err());
assert!(matches!( assert!(matches!(
result, result,
Err(solana_sdk::instruction::InstructionError::AccountsDataBudgetExceeded) Err(solana_sdk::instruction::InstructionError::MaxAccountsDataSizeExceeded)
)); ));
assert_eq!(invoke_context.accounts_data_meter.remaining(), 0); assert_eq!(invoke_context.accounts_data_meter.remaining(), 0);
} }

View File

@ -45,7 +45,7 @@ use {
instruction::{AccountMeta, InstructionError}, instruction::{AccountMeta, InstructionError},
loader_instruction::LoaderInstruction, loader_instruction::LoaderInstruction,
loader_upgradeable_instruction::UpgradeableLoaderInstruction, loader_upgradeable_instruction::UpgradeableLoaderInstruction,
program_error::ACCOUNTS_DATA_BUDGET_EXCEEDED, program_error::MAX_ACCOUNTS_DATA_SIZE_EXCEEDED,
program_utils::limited_deserialize, program_utils::limited_deserialize,
pubkey::Pubkey, pubkey::Pubkey,
saturating_add_assign, saturating_add_assign,
@ -1332,13 +1332,13 @@ impl Executor for BpfExecutor {
} }
match result { match result {
Ok(status) if status != SUCCESS => { Ok(status) if status != SUCCESS => {
let error: InstructionError = if status == ACCOUNTS_DATA_BUDGET_EXCEEDED let error: InstructionError = if status == MAX_ACCOUNTS_DATA_SIZE_EXCEEDED
&& !invoke_context && !invoke_context
.feature_set .feature_set
.is_active(&cap_accounts_data_len::id()) .is_active(&cap_accounts_data_len::id())
{ {
// Until the cap_accounts_data_len feature is enabled, map the // Until the cap_accounts_data_len feature is enabled, map the
// ACCOUNTS_DATA_BUDGET_EXCEEDED error to InvalidError // MAX_ACCOUNTS_DATA_SIZE_EXCEEDED error to InvalidError
InstructionError::InvalidError InstructionError::InvalidError
} else { } else {
status.into() status.into()

View File

@ -215,7 +215,7 @@ impl RentDebits {
} }
type BankStatusCache = StatusCache<Result<()>>; type BankStatusCache = StatusCache<Result<()>>;
#[frozen_abi(digest = "6XkxpmzmKZguLZMS1KmU7N2dAcv8MmNhyobJCwRLkTdi")] #[frozen_abi(digest = "HdYCU65Jwfv9sF3C8k6ZmjUAaXSkJwazebuur21v8JtY")]
pub type BankSlotDelta = SlotDelta<Result<()>>; pub type BankSlotDelta = SlotDelta<Result<()>>;
// Eager rent collection repeats in cyclic manner. // Eager rent collection repeats in cyclic manner.
@ -16147,9 +16147,9 @@ pub(crate) mod tests {
); );
} }
/// Test exceeding the accounts data budget by creating accounts in a loop /// Test exceeding the max accounts data size by creating accounts in a loop
#[test] #[test]
fn test_accounts_data_budget_exceeded() { fn test_max_accounts_data_size_exceeded() {
use { use {
solana_program_runtime::accounts_data_meter::MAX_ACCOUNTS_DATA_LEN, solana_program_runtime::accounts_data_meter::MAX_ACCOUNTS_DATA_LEN,
solana_sdk::system_instruction::MAX_PERMITTED_DATA_LENGTH, solana_sdk::system_instruction::MAX_PERMITTED_DATA_LENGTH,
@ -16188,7 +16188,7 @@ pub(crate) mod tests {
result, result,
Err(TransactionError::InstructionError( Err(TransactionError::InstructionError(
_, _,
solana_sdk::instruction::InstructionError::AccountsDataBudgetExceeded, solana_sdk::instruction::InstructionError::MaxAccountsDataSizeExceeded,
)) ))
)); ));
} }

View File

@ -249,9 +249,9 @@ pub enum InstructionError {
#[error("Provided owner is not allowed")] #[error("Provided owner is not allowed")]
IllegalOwner, IllegalOwner,
/// Accounts data budget exceeded /// Account data allocation exceeded the maximum accounts data size limit
#[error("Requested account data allocation exceeded the accounts data budget")] #[error("Account data allocation exceeded the maximum accounts data size limit")]
AccountsDataBudgetExceeded, MaxAccountsDataSizeExceeded,
/// Active vote account close /// Active vote account close
#[error("Cannot close vote account unless it stopped voting at least one full epoch ago")] #[error("Cannot close vote account unless it stopped voting at least one full epoch ago")]

View File

@ -49,8 +49,8 @@ pub enum ProgramError {
UnsupportedSysvar, UnsupportedSysvar,
#[error("Provided owner is not allowed")] #[error("Provided owner is not allowed")]
IllegalOwner, IllegalOwner,
#[error("Requested account data allocation exceeded the accounts data budget")] #[error("Account data allocation exceeded the maximum accounts data size limit")]
AccountsDataBudgetExceeded, MaxAccountsDataSizeExceeded,
#[error("Cannot close vote account unless it stopped voting at least one full epoch ago")] #[error("Cannot close vote account unless it stopped voting at least one full epoch ago")]
ActiveVoteAccountClose, ActiveVoteAccountClose,
} }
@ -91,7 +91,7 @@ impl PrintProgramError for ProgramError {
Self::AccountNotRentExempt => msg!("Error: AccountNotRentExempt"), Self::AccountNotRentExempt => msg!("Error: AccountNotRentExempt"),
Self::UnsupportedSysvar => msg!("Error: UnsupportedSysvar"), Self::UnsupportedSysvar => msg!("Error: UnsupportedSysvar"),
Self::IllegalOwner => msg!("Error: IllegalOwner"), Self::IllegalOwner => msg!("Error: IllegalOwner"),
Self::AccountsDataBudgetExceeded => msg!("Error: AccountsDataBudgetExceeded"), Self::MaxAccountsDataSizeExceeded => msg!("Error: MaxAccountsDataSizeExceeded"),
Self::ActiveVoteAccountClose => msg!("Error: ActiveVoteAccountClose"), Self::ActiveVoteAccountClose => msg!("Error: ActiveVoteAccountClose"),
} }
} }
@ -123,7 +123,7 @@ pub const BORSH_IO_ERROR: u64 = to_builtin!(15);
pub const ACCOUNT_NOT_RENT_EXEMPT: u64 = to_builtin!(16); pub const ACCOUNT_NOT_RENT_EXEMPT: u64 = to_builtin!(16);
pub const UNSUPPORTED_SYSVAR: u64 = to_builtin!(17); pub const UNSUPPORTED_SYSVAR: u64 = to_builtin!(17);
pub const ILLEGAL_OWNER: u64 = to_builtin!(18); pub const ILLEGAL_OWNER: u64 = to_builtin!(18);
pub const ACCOUNTS_DATA_BUDGET_EXCEEDED: u64 = to_builtin!(19); pub const MAX_ACCOUNTS_DATA_SIZE_EXCEEDED: u64 = to_builtin!(19);
pub const ACTIVE_VOTE_ACCOUNT_CLOSE: u64 = to_builtin!(20); pub const ACTIVE_VOTE_ACCOUNT_CLOSE: u64 = to_builtin!(20);
// Warning: Any new program errors added here must also be: // Warning: Any new program errors added here must also be:
// - Added to the below conversions // - Added to the below conversions
@ -151,7 +151,7 @@ impl From<ProgramError> for u64 {
ProgramError::AccountNotRentExempt => ACCOUNT_NOT_RENT_EXEMPT, ProgramError::AccountNotRentExempt => ACCOUNT_NOT_RENT_EXEMPT,
ProgramError::UnsupportedSysvar => UNSUPPORTED_SYSVAR, ProgramError::UnsupportedSysvar => UNSUPPORTED_SYSVAR,
ProgramError::IllegalOwner => ILLEGAL_OWNER, ProgramError::IllegalOwner => ILLEGAL_OWNER,
ProgramError::AccountsDataBudgetExceeded => ACCOUNTS_DATA_BUDGET_EXCEEDED, ProgramError::MaxAccountsDataSizeExceeded => MAX_ACCOUNTS_DATA_SIZE_EXCEEDED,
ProgramError::ActiveVoteAccountClose => ACTIVE_VOTE_ACCOUNT_CLOSE, ProgramError::ActiveVoteAccountClose => ACTIVE_VOTE_ACCOUNT_CLOSE,
ProgramError::Custom(error) => { ProgramError::Custom(error) => {
if error == 0 { if error == 0 {
@ -185,7 +185,7 @@ impl From<u64> for ProgramError {
ACCOUNT_NOT_RENT_EXEMPT => Self::AccountNotRentExempt, ACCOUNT_NOT_RENT_EXEMPT => Self::AccountNotRentExempt,
UNSUPPORTED_SYSVAR => Self::UnsupportedSysvar, UNSUPPORTED_SYSVAR => Self::UnsupportedSysvar,
ILLEGAL_OWNER => Self::IllegalOwner, ILLEGAL_OWNER => Self::IllegalOwner,
ACCOUNTS_DATA_BUDGET_EXCEEDED => Self::AccountsDataBudgetExceeded, MAX_ACCOUNTS_DATA_SIZE_EXCEEDED => Self::MaxAccountsDataSizeExceeded,
ACTIVE_VOTE_ACCOUNT_CLOSE => Self::ActiveVoteAccountClose, ACTIVE_VOTE_ACCOUNT_CLOSE => Self::ActiveVoteAccountClose,
_ => Self::Custom(error as u32), _ => Self::Custom(error as u32),
} }
@ -215,7 +215,7 @@ impl TryFrom<InstructionError> for ProgramError {
Self::Error::AccountNotRentExempt => Ok(Self::AccountNotRentExempt), Self::Error::AccountNotRentExempt => Ok(Self::AccountNotRentExempt),
Self::Error::UnsupportedSysvar => Ok(Self::UnsupportedSysvar), Self::Error::UnsupportedSysvar => Ok(Self::UnsupportedSysvar),
Self::Error::IllegalOwner => Ok(Self::IllegalOwner), Self::Error::IllegalOwner => Ok(Self::IllegalOwner),
Self::Error::AccountsDataBudgetExceeded => Ok(Self::AccountsDataBudgetExceeded), Self::Error::MaxAccountsDataSizeExceeded => Ok(Self::MaxAccountsDataSizeExceeded),
Self::Error::ActiveVoteAccountClose => Ok(Self::ActiveVoteAccountClose), Self::Error::ActiveVoteAccountClose => Ok(Self::ActiveVoteAccountClose),
_ => Err(error), _ => Err(error),
} }
@ -247,7 +247,7 @@ where
ACCOUNT_NOT_RENT_EXEMPT => Self::AccountNotRentExempt, ACCOUNT_NOT_RENT_EXEMPT => Self::AccountNotRentExempt,
UNSUPPORTED_SYSVAR => Self::UnsupportedSysvar, UNSUPPORTED_SYSVAR => Self::UnsupportedSysvar,
ILLEGAL_OWNER => Self::IllegalOwner, ILLEGAL_OWNER => Self::IllegalOwner,
ACCOUNTS_DATA_BUDGET_EXCEEDED => Self::AccountsDataBudgetExceeded, MAX_ACCOUNTS_DATA_SIZE_EXCEEDED => Self::MaxAccountsDataSizeExceeded,
ACTIVE_VOTE_ACCOUNT_CLOSE => Self::ActiveVoteAccountClose, ACTIVE_VOTE_ACCOUNT_CLOSE => Self::ActiveVoteAccountClose,
_ => { _ => {
// A valid custom error has no bits set in the upper 32 // A valid custom error has no bits set in the upper 32

View File

@ -112,7 +112,7 @@ enum InstructionErrorType {
ARITHMETIC_OVERFLOW = 47; ARITHMETIC_OVERFLOW = 47;
UNSUPPORTED_SYSVAR = 48; UNSUPPORTED_SYSVAR = 48;
ILLEGAL_OWNER = 49; ILLEGAL_OWNER = 49;
ACCOUNTS_DATA_BUDGET_EXCEEDED = 50; MAX_ACCOUNTS_DATA_SIZE_EXCEEDED = 50;
ACTIVE_VOTE_ACCOUNT_CLOSE = 51; ACTIVE_VOTE_ACCOUNT_CLOSE = 51;
} }

View File

@ -670,7 +670,7 @@ impl TryFrom<tx_by_addr::TransactionError> for TransactionError {
47 => InstructionError::ArithmeticOverflow, 47 => InstructionError::ArithmeticOverflow,
48 => InstructionError::UnsupportedSysvar, 48 => InstructionError::UnsupportedSysvar,
49 => InstructionError::IllegalOwner, 49 => InstructionError::IllegalOwner,
50 => InstructionError::AccountsDataBudgetExceeded, 50 => InstructionError::MaxAccountsDataSizeExceeded,
51 => InstructionError::ActiveVoteAccountClose, 51 => InstructionError::ActiveVoteAccountClose,
_ => return Err("Invalid InstructionError"), _ => return Err("Invalid InstructionError"),
}; };
@ -959,8 +959,8 @@ impl From<TransactionError> for tx_by_addr::TransactionError {
InstructionError::IllegalOwner => { InstructionError::IllegalOwner => {
tx_by_addr::InstructionErrorType::IllegalOwner tx_by_addr::InstructionErrorType::IllegalOwner
} }
InstructionError::AccountsDataBudgetExceeded => { InstructionError::MaxAccountsDataSizeExceeded => {
tx_by_addr::InstructionErrorType::AccountsDataBudgetExceeded tx_by_addr::InstructionErrorType::MaxAccountsDataSizeExceeded
} }
InstructionError::ActiveVoteAccountClose => { InstructionError::ActiveVoteAccountClose => {
tx_by_addr::InstructionErrorType::ActiveVoteAccountClose tx_by_addr::InstructionErrorType::ActiveVoteAccountClose