Skip to content

Commit

Permalink
Plumb through reserved account keys set
Browse files Browse the repository at this point in the history
  • Loading branch information
jstarry committed Apr 2, 2024
1 parent 798cb56 commit 330a98b
Show file tree
Hide file tree
Showing 16 changed files with 216 additions and 57 deletions.
6 changes: 5 additions & 1 deletion banks-server/src/banks_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ fn simulate_transaction(
MessageHash::Compute,
Some(false), // is_simple_vote_tx
bank,
bank.get_reserved_account_keys(),
) {
Err(err) => {
return BanksTransactionResultWithSimulation {
Expand Down Expand Up @@ -332,6 +333,7 @@ impl Banks for BanksServer {
MessageHash::Compute,
Some(false), // is_simple_vote_tx
bank.as_ref(),
bank.get_reserved_account_keys(),
) {
Ok(tx) => tx,
Err(err) => return Some(Err(err)),
Expand Down Expand Up @@ -417,7 +419,9 @@ impl Banks for BanksServer {
commitment: CommitmentLevel,
) -> Option<u64> {
let bank = self.bank(commitment);
let sanitized_message = SanitizedMessage::try_from_legacy_message(message).ok()?;
let sanitized_message =
SanitizedMessage::try_from_legacy_message(message, bank.get_reserved_account_keys())
.ok()?;
bank.get_fee_for_message(&sanitized_message)
}
}
Expand Down
5 changes: 4 additions & 1 deletion core/src/banking_stage/immutable_deserialized_packet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use {
feature_set,
hash::Hash,
message::Message,
pubkey::Pubkey,
sanitize::SanitizeError,
saturating_add_assign,
short_vec::decode_shortu16_len,
Expand All @@ -15,7 +16,7 @@ use {
VersionedTransaction,
},
},
std::{cmp::Ordering, mem::size_of, sync::Arc},
std::{cmp::Ordering, collections::HashSet, mem::size_of, sync::Arc},
thiserror::Error,
};

Expand Down Expand Up @@ -123,6 +124,7 @@ impl ImmutableDeserializedPacket {
feature_set: &Arc<feature_set::FeatureSet>,
votes_only: bool,
address_loader: impl AddressLoader,
reserved_account_keys: &HashSet<Pubkey>,
) -> Option<SanitizedTransaction> {
if votes_only && !self.is_simple_vote() {
return None;
Expand All @@ -132,6 +134,7 @@ impl ImmutableDeserializedPacket {
*self.message_hash(),
self.is_simple_vote(),
address_loader,
reserved_account_keys,
)
.ok()?;
tx.verify_precompiles(feature_set).ok()?;
Expand Down
1 change: 1 addition & 0 deletions core/src/banking_stage/latest_unprocessed_votes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,7 @@ impl LatestUnprocessedVotes {
&bank.feature_set,
bank.vote_only_bank(),
bank.as_ref(),
bank.get_reserved_account_keys(),
)
{
if forward_packet_batches_by_accounts.try_add_packet(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,12 @@ impl SchedulerController {
let (transactions, fee_budget_limits_vec): (Vec<_>, Vec<_>) = chunk
.iter()
.filter_map(|packet| {
packet.build_sanitized_transaction(feature_set, vote_only, bank.as_ref())
packet.build_sanitized_transaction(
feature_set,
vote_only,
bank.as_ref(),
bank.get_reserved_account_keys(),
)
})
.inspect(|_| saturating_add_assign!(post_sanitization_count, 1))
.filter(|tx| {
Expand Down
17 changes: 13 additions & 4 deletions core/src/banking_stage/unprocessed_transaction_storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,13 @@ fn consume_scan_should_process_packet(
}

// Try to sanitize the packet
let (maybe_sanitized_transaction, sanitization_time_us) = measure_us!(
packet.build_sanitized_transaction(&bank.feature_set, bank.vote_only_bank(), bank)
);
let (maybe_sanitized_transaction, sanitization_time_us) = measure_us!(packet
.build_sanitized_transaction(
&bank.feature_set,
bank.vote_only_bank(),
bank,
bank.get_reserved_account_keys(),
));

payload
.slot_metrics_tracker
Expand Down Expand Up @@ -770,7 +774,12 @@ impl ThreadLocalUnprocessedPackets {
.enumerate()
.filter_map(|(packet_index, deserialized_packet)| {
deserialized_packet
.build_sanitized_transaction(&bank.feature_set, bank.vote_only_bank(), bank)
.build_sanitized_transaction(
&bank.feature_set,
bank.vote_only_bank(),
bank,
bank.get_reserved_account_keys(),
)
.map(|transaction| (transaction, packet_index))
})
.unzip();
Expand Down
7 changes: 6 additions & 1 deletion ledger-tool/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ use {
native_token::{lamports_to_sol, sol_to_lamports, Sol},
pubkey::Pubkey,
rent::Rent,
reserved_account_keys::ReservedAccountKeys,
shred_version::compute_shred_version,
stake::{self, state::StakeStateV2},
system_program,
Expand Down Expand Up @@ -461,6 +462,9 @@ fn compute_slot_cost(
let mut program_ids = HashMap::new();
let mut cost_tracker = CostTracker::default();

let feature_set = FeatureSet::all_enabled();
let reserved_account_keys = ReservedAccountKeys::new_all_activated();

for entry in entries {
num_transactions += entry.transactions.len();
entry
Expand All @@ -472,6 +476,7 @@ fn compute_slot_cost(
MessageHash::Compute,
None,
SimpleAddressLoader::Disabled,
&reserved_account_keys.active,
)
.map_err(|err| {
warn!("Failed to compute cost of transaction: {:?}", err);
Expand All @@ -481,7 +486,7 @@ fn compute_slot_cost(
.for_each(|transaction| {
num_programs += transaction.message().instructions().len();

let tx_cost = CostModel::calculate_cost(&transaction, &FeatureSet::all_enabled());
let tx_cost = CostModel::calculate_cost(&transaction, &feature_set);
let result = cost_tracker.try_add(&tx_cost);
if result.is_err() {
println!(
Expand Down
30 changes: 22 additions & 8 deletions rpc/src/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3654,7 +3654,11 @@ pub mod rpc_full {
min_context_slot,
})?;

let transaction = sanitize_transaction(unsanitized_tx, preflight_bank)?;
let transaction = sanitize_transaction(
unsanitized_tx,
preflight_bank,
preflight_bank.get_reserved_account_keys(),
)?;
let signature = *transaction.signature();

let mut last_valid_block_height = preflight_bank
Expand Down Expand Up @@ -3776,7 +3780,8 @@ pub mod rpc_full {
.set_recent_blockhash(bank.last_blockhash());
}

let transaction = sanitize_transaction(unsanitized_tx, bank)?;
let transaction =
sanitize_transaction(unsanitized_tx, bank, bank.get_reserved_account_keys())?;
if sig_verify {
verify_transaction(&transaction, &bank.feature_set)?;
}
Expand Down Expand Up @@ -4027,10 +4032,12 @@ pub mod rpc_full {
.map_err(|err| {
Error::invalid_params(format!("invalid transaction message: {err}"))
})?;
let sanitized_message = SanitizedMessage::try_new(sanitized_versioned_message, bank)
.map_err(|err| {
Error::invalid_params(format!("invalid transaction message: {err}"))
})?;
let sanitized_message = SanitizedMessage::try_new(
sanitized_versioned_message,
bank,
bank.get_reserved_account_keys(),
)
.map_err(|err| Error::invalid_params(format!("invalid transaction message: {err}")))?;
let fee = bank.get_fee_for_message(&sanitized_message);
Ok(new_response(bank, fee))
}
Expand Down Expand Up @@ -4609,9 +4616,16 @@ where
fn sanitize_transaction(
transaction: VersionedTransaction,
address_loader: impl AddressLoader,
reserved_account_keys: &HashSet<Pubkey>,
) -> Result<SanitizedTransaction> {
SanitizedTransaction::try_create(transaction, MessageHash::Compute, None, address_loader)
.map_err(|err| Error::invalid_params(format!("invalid transaction: {err}")))
SanitizedTransaction::try_create(
transaction,
MessageHash::Compute,
None,
address_loader,
reserved_account_keys,
)
.map_err(|err| Error::invalid_params(format!("invalid transaction: {err}")))
}

pub fn create_validator_exit(exit: Arc<AtomicBool>) -> Arc<RwLock<Exit>> {
Expand Down
4 changes: 4 additions & 0 deletions runtime-transaction/src/runtime_transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ use {
solana_sdk::{
hash::Hash,
message::{AddressLoader, SanitizedMessage, SanitizedVersionedMessage},
pubkey::Pubkey,
signature::Signature,
simple_vote_transaction_checker::is_simple_vote_transaction,
transaction::{Result, SanitizedVersionedTransaction},
},
std::collections::HashSet,
};

#[derive(Debug, Clone, Eq, PartialEq)]
Expand Down Expand Up @@ -101,12 +103,14 @@ impl RuntimeTransaction<SanitizedMessage> {
pub fn try_from(
statically_loaded_runtime_tx: RuntimeTransaction<SanitizedVersionedMessage>,
address_loader: impl AddressLoader,
reserved_account_keys: &HashSet<Pubkey>,
) -> Result<Self> {
let mut tx = Self {
signatures: statically_loaded_runtime_tx.signatures,
message: SanitizedMessage::try_new(
statically_loaded_runtime_tx.message,
address_loader,
reserved_account_keys,
)?,
meta: statically_loaded_runtime_tx.meta,
};
Expand Down
39 changes: 37 additions & 2 deletions runtime/src/bank.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ use {
rent::RentDue,
rent_collector::{CollectedInfo, RentCollector, RENT_EXEMPT_RENT_EPOCH},
rent_debits::RentDebits,
reserved_account_keys::ReservedAccountKeys,
reward_info::RewardInfo,
saturating_add_assign,
signature::{Keypair, Signature},
Expand Down Expand Up @@ -567,6 +568,7 @@ impl PartialEq for Bank {
transaction_log_collector_config: _,
transaction_log_collector: _,
feature_set: _,
reserved_account_keys: _,
drop_callback: _,
freeze_started: _,
vote_only_bank: _,
Expand Down Expand Up @@ -789,6 +791,9 @@ pub struct Bank {

pub feature_set: Arc<FeatureSet>,

/// Set of reserved account keys that cannot be write locked
reserved_account_keys: Arc<ReservedAccountKeys>,

/// callback function only to be called when dropping and should only be called once
pub drop_callback: RwLock<OptionalDropCallback>,

Expand Down Expand Up @@ -936,6 +941,7 @@ impl Bank {
),
transaction_log_collector: Arc::<RwLock<TransactionLogCollector>>::default(),
feature_set: Arc::<FeatureSet>::default(),
reserved_account_keys: Arc::<ReservedAccountKeys>::default(),
drop_callback: RwLock::new(OptionalDropCallback(None)),
freeze_started: AtomicBool::default(),
vote_only_bank: false,
Expand Down Expand Up @@ -1182,6 +1188,7 @@ impl Bank {
transaction_log_collector_config,
transaction_log_collector: Arc::new(RwLock::new(TransactionLogCollector::default())),
feature_set: Arc::clone(&feature_set),
reserved_account_keys: parent.reserved_account_keys.clone(),
drop_callback: RwLock::new(OptionalDropCallback(
parent
.drop_callback
Expand Down Expand Up @@ -1721,6 +1728,7 @@ impl Bank {
),
transaction_log_collector: Arc::<RwLock<TransactionLogCollector>>::default(),
feature_set: Arc::<FeatureSet>::default(),
reserved_account_keys: Arc::<ReservedAccountKeys>::default(),
drop_callback: RwLock::new(OptionalDropCallback(None)),
freeze_started: AtomicBool::new(fields.hash != Hash::default()),
vote_only_bank: false,
Expand Down Expand Up @@ -4032,7 +4040,15 @@ impl Bank {
pub fn prepare_entry_batch(&self, txs: Vec<VersionedTransaction>) -> Result<TransactionBatch> {
let sanitized_txs = txs
.into_iter()
.map(|tx| SanitizedTransaction::try_create(tx, MessageHash::Compute, None, self))
.map(|tx| {
SanitizedTransaction::try_create(
tx,
MessageHash::Compute,
None,
self,
self.get_reserved_account_keys(),
)
})
.collect::<Result<Vec<_>>>()?;
let tx_account_lock_limit = self.get_transaction_account_lock_limit();
let lock_results = self
Expand Down Expand Up @@ -6460,7 +6476,13 @@ impl Bank {
tx.message.hash()
};

SanitizedTransaction::try_create(tx, message_hash, None, self)
SanitizedTransaction::try_create(
tx,
message_hash,
None,
self,
self.get_reserved_account_keys(),
)
}?;

if verification_mode == TransactionVerificationMode::HashAndVerifyPrecompiles
Expand Down Expand Up @@ -7080,6 +7102,12 @@ impl Bank {
}
}

/// Get a set of all actively reserved account keys that are not allowed to
/// be write-locked during transaction processing.
pub fn get_reserved_account_keys(&self) -> &HashSet<Pubkey> {
&self.reserved_account_keys.active
}

// This is called from snapshot restore AND for each epoch boundary
// The entire code path herein must be idempotent
fn apply_feature_activations(
Expand Down Expand Up @@ -7110,6 +7138,13 @@ impl Bank {
}
}

// Update active set of reserved account keys which are not allowed to be write locked
self.reserved_account_keys = {
let mut reserved_keys = ReservedAccountKeys::clone(&self.reserved_account_keys);
reserved_keys.update_active_set(&self.feature_set);
Arc::new(reserved_keys)
};

if new_feature_activations.contains(&feature_set::pico_inflation::id()) {
*self.inflation.write().unwrap() = Inflation::pico();
self.fee_rate_governor.burn_percent = 50; // 50% fee burn
Expand Down
29 changes: 28 additions & 1 deletion runtime/src/bank/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,8 @@ pub(in crate::bank) fn create_genesis_config(lamports: u64) -> (GenesisConfig, K
}

fn new_sanitized_message(message: Message) -> SanitizedMessage {
SanitizedMessage::try_from_legacy_message(message).unwrap()
SanitizedMessage::try_from_legacy_message(message, &ReservedAccountKeys::empty_key_set())
.unwrap()
}

#[test]
Expand Down Expand Up @@ -7925,6 +7926,32 @@ fn test_compute_active_feature_set() {
assert!(feature_set.is_active(&test_feature));
}

#[test]
fn test_reserved_account_keys() {
let bank0 = create_simple_test_arc_bank(100_000).0;
let mut bank = Bank::new_from_parent(bank0, &Pubkey::default(), 1);
bank.feature_set = Arc::new(FeatureSet::default());

assert_eq!(
bank.get_reserved_account_keys().len(),
20,
"before activating the new feature, bank should already have active reserved keys"
);

// Activate `add_new_reserved_account_keys` feature
bank.store_account(
&feature_set::add_new_reserved_account_keys::id(),
&feature::create_account(&Feature::default(), 42),
);
bank.apply_feature_activations(ApplyFeatureActivationsCaller::NewFromParent, true);

assert_eq!(
bank.get_reserved_account_keys().len(),
29,
"after activating the new feature, bank should have new active reserved keys"
);
}

#[test]
fn test_program_replacement() {
let mut bank = create_simple_test_bank(0);
Expand Down
Loading

0 comments on commit 330a98b

Please sign in to comment.