Ensure blocks do not exceed the max accounts data size during Replay Stage (#23422)
This commit is contained in:
parent
58c0db9704
commit
3c6840050c
|
@ -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 {
|
||||
|
|
|
@ -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(())
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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,
|
||||
))
|
||||
));
|
||||
}
|
||||
|
|
|
@ -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")]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue