Skip to content

Commit

Permalink
Enforce accounts data size limit per block in ReplayStage
Browse files Browse the repository at this point in the history
  • Loading branch information
brooksprumo committed Jun 9, 2022
1 parent e5f36aa commit 27ae751
Show file tree
Hide file tree
Showing 3 changed files with 162 additions and 8 deletions.
155 changes: 147 additions & 8 deletions ledger/src/blockstore_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,12 +224,7 @@ fn execute_batch(
..
} = tx_results;

if bank
.feature_set
.is_active(&feature_set::cap_accounts_data_len::id())
{
check_accounts_data_size(&execution_results)?;
}
check_accounts_data_size(bank, &execution_results)?;

if let Some(transaction_status_sender) = transaction_status_sender {
let transactions = batch.sanitized_transactions().to_vec();
Expand Down Expand Up @@ -1568,11 +1563,45 @@ pub fn fill_blockstore_slot_with_ticks(
last_entry_hash
}

/// Check to see if the transactions exceeded the accounts data size limits
fn check_accounts_data_size<'a>(
bank: &Bank,
execution_results: impl IntoIterator<Item = &'a TransactionExecutionResult>,
) -> Result<()> {
check_accounts_data_block_size(bank)?;
check_accounts_data_total_size(bank, execution_results)
}

/// Check to see if transactions exceeded the accounts data size limit per block
fn check_accounts_data_block_size(bank: &Bank) -> Result<()> {
if !bank
.feature_set
.is_active(&feature_set::cap_accounts_data_size_per_block::id())
{
return Ok(());
}

debug_assert!(MAX_ACCOUNT_DATA_BLOCK_LEN <= i64::MAX as u64);
if bank.load_accounts_data_size_delta_on_chain() > MAX_ACCOUNT_DATA_BLOCK_LEN as i64 {
Err(TransactionError::WouldExceedAccountDataBlockLimit)
} else {
Ok(())
}
}

/// 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>(
fn check_accounts_data_total_size<'a>(
bank: &Bank,
execution_results: impl IntoIterator<Item = &'a TransactionExecutionResult>,
) -> Result<()> {
if !bank
.feature_set
.is_active(&feature_set::cap_accounts_data_len::id())
{
return Ok(());
}

if let Some(result) = execution_results
.into_iter()
.map(|execution_result| execution_result.flattened_result())
Expand Down Expand Up @@ -1605,6 +1634,7 @@ pub mod tests {
matches::assert_matches,
rand::{thread_rng, Rng},
solana_entry::entry::{create_ticks, next_entry, next_entry_mut},
solana_program_runtime::accounts_data_meter::MAX_ACCOUNTS_DATA_LEN,
solana_runtime::{
genesis_utils::{
self, create_genesis_config_with_vote_accounts, ValidatorVoteKeypairs,
Expand All @@ -1615,9 +1645,11 @@ pub mod tests {
account::{AccountSharedData, WritableAccount},
epoch_schedule::EpochSchedule,
hash::Hash,
instruction::InstructionError,
native_token::LAMPORTS_PER_SOL,
pubkey::Pubkey,
signature::{Keypair, Signer},
system_instruction::SystemError,
system_instruction::{SystemError, MAX_PERMITTED_DATA_LENGTH},
system_transaction,
transaction::{Transaction, TransactionError},
},
Expand Down Expand Up @@ -4131,4 +4163,111 @@ pub mod tests {
}
}
}

#[test]
fn test_check_accounts_data_block_size() {
const ACCOUNT_SIZE: u64 = MAX_PERMITTED_DATA_LENGTH;
const NUM_ACCOUNTS: u64 = MAX_ACCOUNT_DATA_BLOCK_LEN / ACCOUNT_SIZE;

let GenesisConfigInfo {
genesis_config,
mint_keypair,
..
} = create_genesis_config((1_000_000 + NUM_ACCOUNTS + 1) * LAMPORTS_PER_SOL);
let bank = Bank::new_for_tests(&genesis_config);
let bank = Arc::new(bank);
assert!(bank
.feature_set
.is_active(&feature_set::cap_accounts_data_size_per_block::id()));

for _ in 0..NUM_ACCOUNTS {
let transaction = system_transaction::create_account(
&mint_keypair,
&Keypair::new(),
bank.last_blockhash(),
LAMPORTS_PER_SOL,
ACCOUNT_SIZE,
&solana_sdk::system_program::id(),
);
let entry = next_entry(&bank.last_blockhash(), 1, vec![transaction]);
assert!(process_entries_for_tests(&bank, vec![entry], true, None, None).is_ok());
}

let transaction = system_transaction::create_account(
&mint_keypair,
&Keypair::new(),
bank.last_blockhash(),
LAMPORTS_PER_SOL,
ACCOUNT_SIZE,
&solana_sdk::system_program::id(),
);
let entry = next_entry(&bank.last_blockhash(), 1, vec![transaction]);
assert_eq!(
process_entries_for_tests(&bank, vec![entry], true, None, None),
Err(TransactionError::WouldExceedAccountDataBlockLimit)
);
}

#[test]
fn test_check_accounts_data_total_size() {
const REMAINING_ACCOUNTS_DATA_SIZE: u64 =
MAX_ACCOUNT_DATA_BLOCK_LEN - MAX_PERMITTED_DATA_LENGTH;
const INITIAL_ACCOUNTS_DATA_SIZE: u64 =
MAX_ACCOUNTS_DATA_LEN - REMAINING_ACCOUNTS_DATA_SIZE;
const ACCOUNT_SIZE: u64 = MAX_PERMITTED_DATA_LENGTH;
const EXTRA_ON_CHAIN_DELTA: i64 = -5678;
const EXTRA_OFF_CHAIN_DELTA: i64 = -1111;
const ACCOUNTS_DATA_SIZE_DELTA_PER_ITERATION: u64 =
(ACCOUNT_SIZE as i64 + EXTRA_ON_CHAIN_DELTA + EXTRA_OFF_CHAIN_DELTA) as u64;
const NUM_ITERATIONS: u64 =
REMAINING_ACCOUNTS_DATA_SIZE / ACCOUNTS_DATA_SIZE_DELTA_PER_ITERATION;
const ACCOUNT_BALANCE: u64 = 70 * LAMPORTS_PER_SOL; // rent exempt amount for a 10MB account is a little less than 70 SOL

let GenesisConfigInfo {
genesis_config,
mint_keypair,
..
} = create_genesis_config((1_000_000 + NUM_ITERATIONS + 1) * ACCOUNT_BALANCE);
let mut bank = Bank::new_for_tests(&genesis_config);
bank.set_accounts_data_size_initial_for_tests(INITIAL_ACCOUNTS_DATA_SIZE);
let bank = Arc::new(bank);
let bank = Arc::new(Bank::new_from_parent(&bank, &Pubkey::new_unique(), 1));
assert!(bank
.feature_set
.is_active(&feature_set::cap_accounts_data_len::id()));

for _ in 0..NUM_ITERATIONS {
// Simulate *decreases* to the accounts data size as well
bank.update_accounts_data_size_delta_on_chain_for_tests(EXTRA_ON_CHAIN_DELTA);
bank.update_accounts_data_size_delta_off_chain_for_tests(EXTRA_OFF_CHAIN_DELTA);

let transaction = system_transaction::create_account(
&mint_keypair,
&Keypair::new(),
bank.last_blockhash(),
ACCOUNT_BALANCE,
ACCOUNT_SIZE,
&solana_sdk::system_program::id(),
);
let entry = next_entry(&bank.last_blockhash(), 1, vec![transaction]);
assert!(process_entries_for_tests(&bank, vec![entry], true, None, None).is_ok());
}

let transaction = system_transaction::create_account(
&mint_keypair,
&Keypair::new(),
bank.last_blockhash(),
ACCOUNT_BALANCE,
ACCOUNT_SIZE,
&solana_sdk::system_program::id(),
);
let entry = next_entry(&bank.last_blockhash(), 1, vec![transaction]);
assert!(matches!(
process_entries_for_tests(&bank, vec![entry], true, None, None),
Err(TransactionError::InstructionError(
_,
InstructionError::MaxAccountsDataSizeExceeded,
))
));
}
}
10 changes: 10 additions & 0 deletions runtime/src/bank.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4718,6 +4718,16 @@ impl Bank {
self.accounts_data_size_initial = amount;
}

/// NOTE: This fn is *ONLY FOR TESTS*
pub fn update_accounts_data_size_delta_on_chain_for_tests(&self, amount: i64) {
self.update_accounts_data_size_delta_on_chain(amount);
}

/// NOTE: This fn is *ONLY FOR TESTS*
pub fn update_accounts_data_size_delta_off_chain_for_tests(&self, amount: i64) {
self.update_accounts_data_size_delta_off_chain(amount);
}

fn get_num_signatures_in_message(message: &SanitizedMessage) -> u64 {
let mut num_signatures = u64::from(message.header().num_required_signatures);
// This next part is really calculating the number of pre-processor
Expand Down
5 changes: 5 additions & 0 deletions sdk/src/feature_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,10 @@ pub mod nonce_must_be_advanceable {
solana_sdk::declare_id!("3u3Er5Vc2jVcwz4xr2GJeSAXT3fAj6ADHZ4BJMZiScFd");
}

pub mod cap_accounts_data_size_per_block {
solana_sdk::declare_id!("qywiJyZmqTKspFg2LeuUHqcA5nNvBgobqb9UprywS9N");
}

lazy_static! {
/// Map of feature identifiers to user-visible description
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
Expand Down Expand Up @@ -543,6 +547,7 @@ lazy_static! {
(quick_bail_on_panic::id(), "quick bail on panic"),
(nonce_must_be_authorized::id(), "nonce must be authorized"),
(nonce_must_be_advanceable::id(), "durable nonces must be advanceable"),
(cap_accounts_data_size_per_block::id(), "cap the accounts data size per block #25517"),
/*************** ADD NEW FEATURES HERE ***************/
]
.iter()
Expand Down

0 comments on commit 27ae751

Please sign in to comment.