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,
genesis_config::GenesisConfig,
hash::Hash,
instruction::InstructionError,
pubkey::Pubkey,
signature::{Keypair, Signature},
timing,
@ -224,6 +225,13 @@ fn execute_batch(
..
} = 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 {
let transactions = batch.sanitized_transactions().to_vec();
let post_token_balances = if record_token_balances {
@ -1540,6 +1548,30 @@ pub fn fill_blockstore_slot_with_ticks(
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)]
pub mod tests {
use {

View File

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

View File

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

View File

@ -45,7 +45,7 @@ use {
instruction::{AccountMeta, InstructionError},
loader_instruction::LoaderInstruction,
loader_upgradeable_instruction::UpgradeableLoaderInstruction,
program_error::ACCOUNTS_DATA_BUDGET_EXCEEDED,
program_error::MAX_ACCOUNTS_DATA_SIZE_EXCEEDED,
program_utils::limited_deserialize,
pubkey::Pubkey,
saturating_add_assign,
@ -1332,13 +1332,13 @@ impl Executor for BpfExecutor {
}
match result {
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
.feature_set
.is_active(&cap_accounts_data_len::id())
{
// 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
} else {
status.into()

View File

@ -215,7 +215,7 @@ impl RentDebits {
}
type BankStatusCache = StatusCache<Result<()>>;
#[frozen_abi(digest = "6XkxpmzmKZguLZMS1KmU7N2dAcv8MmNhyobJCwRLkTdi")]
#[frozen_abi(digest = "HdYCU65Jwfv9sF3C8k6ZmjUAaXSkJwazebuur21v8JtY")]
pub type BankSlotDelta = SlotDelta<Result<()>>;
// 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]
fn test_accounts_data_budget_exceeded() {
fn test_max_accounts_data_size_exceeded() {
use {
solana_program_runtime::accounts_data_meter::MAX_ACCOUNTS_DATA_LEN,
solana_sdk::system_instruction::MAX_PERMITTED_DATA_LENGTH,
@ -16188,7 +16188,7 @@ pub(crate) mod tests {
result,
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")]
IllegalOwner,
/// Accounts data budget exceeded
#[error("Requested account data allocation exceeded the accounts data budget")]
AccountsDataBudgetExceeded,
/// Account data allocation exceeded the maximum accounts data size limit
#[error("Account data allocation exceeded the maximum accounts data size limit")]
MaxAccountsDataSizeExceeded,
/// Active vote account close
#[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,
#[error("Provided owner is not allowed")]
IllegalOwner,
#[error("Requested account data allocation exceeded the accounts data budget")]
AccountsDataBudgetExceeded,
#[error("Account data allocation exceeded the maximum accounts data size limit")]
MaxAccountsDataSizeExceeded,
#[error("Cannot close vote account unless it stopped voting at least one full epoch ago")]
ActiveVoteAccountClose,
}
@ -91,7 +91,7 @@ impl PrintProgramError for ProgramError {
Self::AccountNotRentExempt => msg!("Error: AccountNotRentExempt"),
Self::UnsupportedSysvar => msg!("Error: UnsupportedSysvar"),
Self::IllegalOwner => msg!("Error: IllegalOwner"),
Self::AccountsDataBudgetExceeded => msg!("Error: AccountsDataBudgetExceeded"),
Self::MaxAccountsDataSizeExceeded => msg!("Error: MaxAccountsDataSizeExceeded"),
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 UNSUPPORTED_SYSVAR: u64 = to_builtin!(17);
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);
// Warning: Any new program errors added here must also be:
// - Added to the below conversions
@ -151,7 +151,7 @@ impl From<ProgramError> for u64 {
ProgramError::AccountNotRentExempt => ACCOUNT_NOT_RENT_EXEMPT,
ProgramError::UnsupportedSysvar => UNSUPPORTED_SYSVAR,
ProgramError::IllegalOwner => ILLEGAL_OWNER,
ProgramError::AccountsDataBudgetExceeded => ACCOUNTS_DATA_BUDGET_EXCEEDED,
ProgramError::MaxAccountsDataSizeExceeded => MAX_ACCOUNTS_DATA_SIZE_EXCEEDED,
ProgramError::ActiveVoteAccountClose => ACTIVE_VOTE_ACCOUNT_CLOSE,
ProgramError::Custom(error) => {
if error == 0 {
@ -185,7 +185,7 @@ impl From<u64> for ProgramError {
ACCOUNT_NOT_RENT_EXEMPT => Self::AccountNotRentExempt,
UNSUPPORTED_SYSVAR => Self::UnsupportedSysvar,
ILLEGAL_OWNER => Self::IllegalOwner,
ACCOUNTS_DATA_BUDGET_EXCEEDED => Self::AccountsDataBudgetExceeded,
MAX_ACCOUNTS_DATA_SIZE_EXCEEDED => Self::MaxAccountsDataSizeExceeded,
ACTIVE_VOTE_ACCOUNT_CLOSE => Self::ActiveVoteAccountClose,
_ => Self::Custom(error as u32),
}
@ -215,7 +215,7 @@ impl TryFrom<InstructionError> for ProgramError {
Self::Error::AccountNotRentExempt => Ok(Self::AccountNotRentExempt),
Self::Error::UnsupportedSysvar => Ok(Self::UnsupportedSysvar),
Self::Error::IllegalOwner => Ok(Self::IllegalOwner),
Self::Error::AccountsDataBudgetExceeded => Ok(Self::AccountsDataBudgetExceeded),
Self::Error::MaxAccountsDataSizeExceeded => Ok(Self::MaxAccountsDataSizeExceeded),
Self::Error::ActiveVoteAccountClose => Ok(Self::ActiveVoteAccountClose),
_ => Err(error),
}
@ -247,7 +247,7 @@ where
ACCOUNT_NOT_RENT_EXEMPT => Self::AccountNotRentExempt,
UNSUPPORTED_SYSVAR => Self::UnsupportedSysvar,
ILLEGAL_OWNER => Self::IllegalOwner,
ACCOUNTS_DATA_BUDGET_EXCEEDED => Self::AccountsDataBudgetExceeded,
MAX_ACCOUNTS_DATA_SIZE_EXCEEDED => Self::MaxAccountsDataSizeExceeded,
ACTIVE_VOTE_ACCOUNT_CLOSE => Self::ActiveVoteAccountClose,
_ => {
// A valid custom error has no bits set in the upper 32

View File

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

View File

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