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,
|
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 {
|
||||||
|
|
|
@ -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(())
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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,
|
||||||
))
|
))
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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")]
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue