Use reserved account keys list to restrict tx write locks (#541)

* Plumb through reserved account keys set

* Plumb through tests
This commit is contained in:
Justin Starry 2024-04-13 09:37:58 +08:00 committed by GitHub
parent cb13b39118
commit 09241ae9c3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
34 changed files with 355 additions and 137 deletions

View File

@ -178,6 +178,7 @@ fn simulate_transaction(
MessageHash::Compute, MessageHash::Compute,
Some(false), // is_simple_vote_tx Some(false), // is_simple_vote_tx
bank, bank,
bank.get_reserved_account_keys(),
) { ) {
Err(err) => { Err(err) => {
return BanksTransactionResultWithSimulation { return BanksTransactionResultWithSimulation {
@ -332,6 +333,7 @@ impl Banks for BanksServer {
MessageHash::Compute, MessageHash::Compute,
Some(false), // is_simple_vote_tx Some(false), // is_simple_vote_tx
bank.as_ref(), bank.as_ref(),
bank.get_reserved_account_keys(),
) { ) {
Ok(tx) => tx, Ok(tx) => tx,
Err(err) => return Some(Err(err)), Err(err) => return Some(Err(err)),
@ -417,7 +419,9 @@ impl Banks for BanksServer {
commitment: CommitmentLevel, commitment: CommitmentLevel,
) -> Option<u64> { ) -> Option<u64> {
let bank = self.bank(commitment); 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) bank.get_fee_for_message(&sanitized_message)
} }
} }

View File

@ -868,6 +868,7 @@ mod tests {
nonce_account::verify_nonce_account, nonce_account::verify_nonce_account,
poh_config::PohConfig, poh_config::PohConfig,
pubkey::Pubkey, pubkey::Pubkey,
reserved_account_keys::ReservedAccountKeys,
signature::Keypair, signature::Keypair,
signer::Signer, signer::Signer,
system_instruction, system_program, system_transaction, system_instruction, system_program, system_transaction,
@ -2026,6 +2027,7 @@ mod tests {
MessageHash::Compute, MessageHash::Compute,
Some(false), Some(false),
bank.as_ref(), bank.as_ref(),
&ReservedAccountKeys::empty_key_set(),
) )
.unwrap(); .unwrap();

View File

@ -6,6 +6,7 @@ use {
feature_set, feature_set,
hash::Hash, hash::Hash,
message::Message, message::Message,
pubkey::Pubkey,
sanitize::SanitizeError, sanitize::SanitizeError,
saturating_add_assign, saturating_add_assign,
short_vec::decode_shortu16_len, short_vec::decode_shortu16_len,
@ -15,7 +16,7 @@ use {
VersionedTransaction, VersionedTransaction,
}, },
}, },
std::{cmp::Ordering, mem::size_of, sync::Arc}, std::{cmp::Ordering, collections::HashSet, mem::size_of, sync::Arc},
thiserror::Error, thiserror::Error,
}; };
@ -123,6 +124,7 @@ impl ImmutableDeserializedPacket {
feature_set: &Arc<feature_set::FeatureSet>, feature_set: &Arc<feature_set::FeatureSet>,
votes_only: bool, votes_only: bool,
address_loader: impl AddressLoader, address_loader: impl AddressLoader,
reserved_account_keys: &HashSet<Pubkey>,
) -> Option<SanitizedTransaction> { ) -> Option<SanitizedTransaction> {
if votes_only && !self.is_simple_vote() { if votes_only && !self.is_simple_vote() {
return None; return None;
@ -132,6 +134,7 @@ impl ImmutableDeserializedPacket {
*self.message_hash(), *self.message_hash(),
self.is_simple_vote(), self.is_simple_vote(),
address_loader, address_loader,
reserved_account_keys,
) )
.ok()?; .ok()?;
tx.verify_precompiles(feature_set).ok()?; tx.verify_precompiles(feature_set).ok()?;

View File

@ -283,6 +283,7 @@ impl LatestUnprocessedVotes {
&bank.feature_set, &bank.feature_set,
bank.vote_only_bank(), bank.vote_only_bank(),
bank.as_ref(), bank.as_ref(),
bank.get_reserved_account_keys(),
) )
{ {
if forward_packet_batches_by_accounts.try_add_packet( if forward_packet_batches_by_accounts.try_add_packet(

View File

@ -138,6 +138,7 @@ mod tests {
MessageHash::Compute, MessageHash::Compute,
Some(false), Some(false),
bank, bank,
bank.get_reserved_account_keys(),
) )
.unwrap() .unwrap()
} }

View File

@ -381,7 +381,12 @@ impl SchedulerController {
let (transactions, fee_budget_limits_vec): (Vec<_>, Vec<_>) = chunk let (transactions, fee_budget_limits_vec): (Vec<_>, Vec<_>) = chunk
.iter() .iter()
.filter_map(|packet| { .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)) .inspect(|_| saturating_add_assign!(post_sanitization_count, 1))
.filter(|tx| { .filter(|tx| {

View File

@ -310,6 +310,7 @@ mod tests {
solana_sdk::{ solana_sdk::{
compute_budget::ComputeBudgetInstruction, compute_budget::ComputeBudgetInstruction,
message::Message, message::Message,
reserved_account_keys::ReservedAccountKeys,
signature::{Keypair, Signer}, signature::{Keypair, Signer},
system_instruction, system_transaction, system_instruction, system_transaction,
transaction::{SimpleAddressLoader, Transaction}, transaction::{SimpleAddressLoader, Transaction},
@ -490,6 +491,7 @@ mod tests {
&Arc::new(FeatureSet::default()), &Arc::new(FeatureSet::default()),
votes_only, votes_only,
SimpleAddressLoader::Disabled, SimpleAddressLoader::Disabled,
&ReservedAccountKeys::empty_key_set(),
) )
}); });
assert_eq!(2, txs.count()); assert_eq!(2, txs.count());
@ -500,6 +502,7 @@ mod tests {
&Arc::new(FeatureSet::default()), &Arc::new(FeatureSet::default()),
votes_only, votes_only,
SimpleAddressLoader::Disabled, SimpleAddressLoader::Disabled,
&ReservedAccountKeys::empty_key_set(),
) )
}); });
assert_eq!(0, txs.count()); assert_eq!(0, txs.count());
@ -519,6 +522,7 @@ mod tests {
&Arc::new(FeatureSet::default()), &Arc::new(FeatureSet::default()),
votes_only, votes_only,
SimpleAddressLoader::Disabled, SimpleAddressLoader::Disabled,
&ReservedAccountKeys::empty_key_set(),
) )
}); });
assert_eq!(3, txs.count()); assert_eq!(3, txs.count());
@ -529,6 +533,7 @@ mod tests {
&Arc::new(FeatureSet::default()), &Arc::new(FeatureSet::default()),
votes_only, votes_only,
SimpleAddressLoader::Disabled, SimpleAddressLoader::Disabled,
&ReservedAccountKeys::empty_key_set(),
) )
}); });
assert_eq!(2, txs.count()); assert_eq!(2, txs.count());
@ -548,6 +553,7 @@ mod tests {
&Arc::new(FeatureSet::default()), &Arc::new(FeatureSet::default()),
votes_only, votes_only,
SimpleAddressLoader::Disabled, SimpleAddressLoader::Disabled,
&ReservedAccountKeys::empty_key_set(),
) )
}); });
assert_eq!(3, txs.count()); assert_eq!(3, txs.count());
@ -558,6 +564,7 @@ mod tests {
&Arc::new(FeatureSet::default()), &Arc::new(FeatureSet::default()),
votes_only, votes_only,
SimpleAddressLoader::Disabled, SimpleAddressLoader::Disabled,
&ReservedAccountKeys::empty_key_set(),
) )
}); });
assert_eq!(3, txs.count()); assert_eq!(3, txs.count());

View File

@ -153,9 +153,13 @@ fn consume_scan_should_process_packet(
} }
// Try to sanitize the packet // Try to sanitize the packet
let (maybe_sanitized_transaction, sanitization_time_us) = measure_us!( let (maybe_sanitized_transaction, sanitization_time_us) = measure_us!(packet
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(),
));
payload payload
.slot_metrics_tracker .slot_metrics_tracker
@ -770,7 +774,12 @@ impl ThreadLocalUnprocessedPackets {
.enumerate() .enumerate()
.filter_map(|(packet_index, deserialized_packet)| { .filter_map(|(packet_index, deserialized_packet)| {
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)) .map(|transaction| (transaction, packet_index))
}) })
.unzip(); .unzip();

View File

@ -339,6 +339,7 @@ mod tests {
crate::transaction_cost::*, crate::transaction_cost::*,
solana_sdk::{ solana_sdk::{
hash::Hash, hash::Hash,
reserved_account_keys::ReservedAccountKeys,
signature::{Keypair, Signer}, signature::{Keypair, Signer},
system_transaction, system_transaction,
transaction::{ transaction::{
@ -401,6 +402,7 @@ mod tests {
MessageHash::Compute, MessageHash::Compute,
Some(true), Some(true),
SimpleAddressLoader::Disabled, SimpleAddressLoader::Disabled,
&ReservedAccountKeys::empty_key_set(),
) )
.unwrap(); .unwrap();

View File

@ -203,6 +203,7 @@ mod tests {
feature_set::FeatureSet, feature_set::FeatureSet,
hash::Hash, hash::Hash,
message::SimpleAddressLoader, message::SimpleAddressLoader,
reserved_account_keys::ReservedAccountKeys,
signer::keypair::Keypair, signer::keypair::Keypair,
transaction::{MessageHash, SanitizedTransaction, VersionedTransaction}, transaction::{MessageHash, SanitizedTransaction, VersionedTransaction},
}, },
@ -231,6 +232,7 @@ mod tests {
MessageHash::Compute, MessageHash::Compute,
Some(true), Some(true),
SimpleAddressLoader::Disabled, SimpleAddressLoader::Disabled,
&ReservedAccountKeys::empty_key_set(),
) )
.unwrap(); .unwrap();
@ -240,6 +242,7 @@ mod tests {
MessageHash::Compute, MessageHash::Compute,
Some(false), Some(false),
SimpleAddressLoader::Disabled, SimpleAddressLoader::Disabled,
&ReservedAccountKeys::empty_key_set(),
) )
.unwrap(); .unwrap();

View File

@ -5,6 +5,7 @@ use {
solana_perf::test_tx::test_tx, solana_perf::test_tx::test_tx,
solana_sdk::{ solana_sdk::{
hash::Hash, hash::Hash,
reserved_account_keys::ReservedAccountKeys,
transaction::{ transaction::{
Result, SanitizedTransaction, SimpleAddressLoader, TransactionVerificationMode, Result, SanitizedTransaction, SimpleAddressLoader, TransactionVerificationMode,
VersionedTransaction, VersionedTransaction,
@ -41,6 +42,7 @@ fn bench_gpusigverify(bencher: &mut Bencher) {
message_hash, message_hash,
None, None,
SimpleAddressLoader::Disabled, SimpleAddressLoader::Disabled,
&ReservedAccountKeys::empty_key_set(),
) )
}?; }?;
@ -84,6 +86,7 @@ fn bench_cpusigverify(bencher: &mut Bencher) {
message_hash, message_hash,
None, None,
SimpleAddressLoader::Disabled, SimpleAddressLoader::Disabled,
&ReservedAccountKeys::empty_key_set(),
) )
}?; }?;

View File

@ -982,6 +982,7 @@ mod tests {
solana_sdk::{ solana_sdk::{
hash::{hash, Hash}, hash::{hash, Hash},
pubkey::Pubkey, pubkey::Pubkey,
reserved_account_keys::ReservedAccountKeys,
signature::{Keypair, Signer}, signature::{Keypair, Signer},
system_transaction, system_transaction,
transaction::{ transaction::{
@ -1084,6 +1085,7 @@ mod tests {
message_hash, message_hash,
None, None,
SimpleAddressLoader::Disabled, SimpleAddressLoader::Disabled,
&ReservedAccountKeys::empty_key_set(),
) )
}?; }?;

View File

@ -67,6 +67,7 @@ use {
native_token::{lamports_to_sol, sol_to_lamports, Sol}, native_token::{lamports_to_sol, sol_to_lamports, Sol},
pubkey::Pubkey, pubkey::Pubkey,
rent::Rent, rent::Rent,
reserved_account_keys::ReservedAccountKeys,
shred_version::compute_shred_version, shred_version::compute_shred_version,
stake::{self, state::StakeStateV2}, stake::{self, state::StakeStateV2},
system_program, system_program,
@ -461,6 +462,9 @@ fn compute_slot_cost(
let mut program_ids = HashMap::new(); let mut program_ids = HashMap::new();
let mut cost_tracker = CostTracker::default(); let mut cost_tracker = CostTracker::default();
let feature_set = FeatureSet::all_enabled();
let reserved_account_keys = ReservedAccountKeys::new_all_activated();
for entry in entries { for entry in entries {
num_transactions += entry.transactions.len(); num_transactions += entry.transactions.len();
entry entry
@ -472,6 +476,7 @@ fn compute_slot_cost(
MessageHash::Compute, MessageHash::Compute,
None, None,
SimpleAddressLoader::Disabled, SimpleAddressLoader::Disabled,
&reserved_account_keys.active,
) )
.map_err(|err| { .map_err(|err| {
warn!("Failed to compute cost of transaction: {:?}", err); warn!("Failed to compute cost of transaction: {:?}", err);
@ -481,7 +486,7 @@ fn compute_slot_cost(
.for_each(|transaction| { .for_each(|transaction| {
num_programs += transaction.message().instructions().len(); 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); let result = cost_tracker.try_add(&tx_cost);
if result.is_err() { if result.is_err() {
println!( println!(

View File

@ -81,6 +81,7 @@ use {
message::Message, message::Message,
pubkey::Pubkey, pubkey::Pubkey,
rent::Rent, rent::Rent,
reserved_account_keys::ReservedAccountKeys,
signature::{Keypair, Signer}, signature::{Keypair, Signer},
system_program, system_program,
transaction::{SanitizedTransaction, Transaction, TransactionError}, transaction::{SanitizedTransaction, Transaction, TransactionError},
@ -201,7 +202,11 @@ fn execute_transactions(
} }
.expect("lamports_per_signature must be available"); .expect("lamports_per_signature must be available");
let fee = bank.get_fee_for_message_with_lamports_per_signature( let fee = bank.get_fee_for_message_with_lamports_per_signature(
&SanitizedMessage::try_from_legacy_message(tx.message().clone()).unwrap(), &SanitizedMessage::try_from_legacy_message(
tx.message().clone(),
&ReservedAccountKeys::empty_key_set(),
)
.unwrap(),
lamports_per_signature, lamports_per_signature,
); );
@ -3706,7 +3711,11 @@ fn test_program_fees() {
Some(&mint_keypair.pubkey()), Some(&mint_keypair.pubkey()),
); );
let sanitized_message = SanitizedMessage::try_from_legacy_message(message.clone()).unwrap(); let sanitized_message = SanitizedMessage::try_from_legacy_message(
message.clone(),
&ReservedAccountKeys::empty_key_set(),
)
.unwrap();
let expected_normal_fee = fee_structure.calculate_fee( let expected_normal_fee = fee_structure.calculate_fee(
&sanitized_message, &sanitized_message,
congestion_multiplier, congestion_multiplier,
@ -3730,7 +3739,11 @@ fn test_program_fees() {
], ],
Some(&mint_keypair.pubkey()), Some(&mint_keypair.pubkey()),
); );
let sanitized_message = SanitizedMessage::try_from_legacy_message(message.clone()).unwrap(); let sanitized_message = SanitizedMessage::try_from_legacy_message(
message.clone(),
&ReservedAccountKeys::empty_key_set(),
)
.unwrap();
let expected_prioritized_fee = fee_structure.calculate_fee( let expected_prioritized_fee = fee_structure.calculate_fee(
&sanitized_message, &sanitized_message,
congestion_multiplier, congestion_multiplier,

View File

@ -3657,7 +3657,11 @@ pub mod rpc_full {
min_context_slot, 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 signature = *transaction.signature();
let mut last_valid_block_height = preflight_bank let mut last_valid_block_height = preflight_bank
@ -3789,7 +3793,8 @@ pub mod rpc_full {
}); });
} }
let transaction = sanitize_transaction(unsanitized_tx, bank)?; let transaction =
sanitize_transaction(unsanitized_tx, bank, bank.get_reserved_account_keys())?;
if sig_verify { if sig_verify {
verify_transaction(&transaction, &bank.feature_set)?; verify_transaction(&transaction, &bank.feature_set)?;
} }
@ -4041,10 +4046,12 @@ pub mod rpc_full {
.map_err(|err| { .map_err(|err| {
Error::invalid_params(format!("invalid transaction message: {err}")) Error::invalid_params(format!("invalid transaction message: {err}"))
})?; })?;
let sanitized_message = SanitizedMessage::try_new(sanitized_versioned_message, bank) let sanitized_message = SanitizedMessage::try_new(
.map_err(|err| { sanitized_versioned_message,
Error::invalid_params(format!("invalid transaction message: {err}")) 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); let fee = bank.get_fee_for_message(&sanitized_message);
Ok(new_response(bank, fee)) Ok(new_response(bank, fee))
} }
@ -4623,9 +4630,16 @@ where
fn sanitize_transaction( fn sanitize_transaction(
transaction: VersionedTransaction, transaction: VersionedTransaction,
address_loader: impl AddressLoader, address_loader: impl AddressLoader,
reserved_account_keys: &HashSet<Pubkey>,
) -> Result<SanitizedTransaction> { ) -> Result<SanitizedTransaction> {
SanitizedTransaction::try_create(transaction, MessageHash::Compute, None, address_loader) SanitizedTransaction::try_create(
.map_err(|err| Error::invalid_params(format!("invalid transaction: {err}"))) 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>> { pub fn create_validator_exit(exit: Arc<AtomicBool>) -> Arc<RwLock<Exit>> {
@ -4771,6 +4785,7 @@ pub mod tests {
Message, MessageHeader, VersionedMessage, Message, MessageHeader, VersionedMessage,
}, },
nonce::{self, state::DurableNonce}, nonce::{self, state::DurableNonce},
reserved_account_keys::ReservedAccountKeys,
rpc_port, rpc_port,
signature::{Keypair, Signer}, signature::{Keypair, Signer},
slot_hashes::SlotHashes, slot_hashes::SlotHashes,
@ -9049,8 +9064,12 @@ pub mod tests {
.to_string(), .to_string(),
); );
assert_eq!( assert_eq!(
sanitize_transaction(unsanitary_versioned_tx, SimpleAddressLoader::Disabled) sanitize_transaction(
.unwrap_err(), unsanitary_versioned_tx,
SimpleAddressLoader::Disabled,
&ReservedAccountKeys::empty_key_set()
)
.unwrap_err(),
expect58 expect58
); );
} }
@ -9070,7 +9089,12 @@ pub mod tests {
}; };
assert_eq!( assert_eq!(
sanitize_transaction(versioned_tx, SimpleAddressLoader::Disabled).unwrap_err(), sanitize_transaction(
versioned_tx,
SimpleAddressLoader::Disabled,
&ReservedAccountKeys::empty_key_set()
)
.unwrap_err(),
Error::invalid_params( Error::invalid_params(
"invalid transaction: Transaction version is unsupported".to_string(), "invalid transaction: Transaction version is unsupported".to_string(),
) )

View File

@ -225,6 +225,7 @@ pub(crate) mod tests {
nonce_info::{NonceFull, NoncePartial}, nonce_info::{NonceFull, NoncePartial},
pubkey::Pubkey, pubkey::Pubkey,
rent_debits::RentDebits, rent_debits::RentDebits,
reserved_account_keys::ReservedAccountKeys,
signature::{Keypair, Signature, Signer}, signature::{Keypair, Signature, Signer},
system_transaction, system_transaction,
transaction::{ transaction::{
@ -335,6 +336,7 @@ pub(crate) mod tests {
MessageHash::Compute, MessageHash::Compute,
None, None,
SimpleAddressLoader::Disabled, SimpleAddressLoader::Disabled,
&ReservedAccountKeys::empty_key_set(),
) )
.unwrap(); .unwrap();
@ -364,7 +366,10 @@ pub(crate) mod tests {
durable_nonce_fee: Some(DurableNonceFee::from( durable_nonce_fee: Some(DurableNonceFee::from(
&NonceFull::from_partial( &NonceFull::from_partial(
&rollback_partial, &rollback_partial,
&SanitizedMessage::Legacy(LegacyMessage::new(message)), &SanitizedMessage::Legacy(LegacyMessage::new(
message,
&ReservedAccountKeys::empty_key_set(),
)),
&[(pubkey, nonce_account)], &[(pubkey, nonce_account)],
&rent_debits, &rent_debits,
) )

View File

@ -17,10 +17,12 @@ use {
solana_sdk::{ solana_sdk::{
hash::Hash, hash::Hash,
message::{AddressLoader, SanitizedMessage, SanitizedVersionedMessage}, message::{AddressLoader, SanitizedMessage, SanitizedVersionedMessage},
pubkey::Pubkey,
signature::Signature, signature::Signature,
simple_vote_transaction_checker::is_simple_vote_transaction, simple_vote_transaction_checker::is_simple_vote_transaction,
transaction::{Result, SanitizedVersionedTransaction}, transaction::{Result, SanitizedVersionedTransaction},
}, },
std::collections::HashSet,
}; };
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]
@ -101,12 +103,14 @@ impl RuntimeTransaction<SanitizedMessage> {
pub fn try_from( pub fn try_from(
statically_loaded_runtime_tx: RuntimeTransaction<SanitizedVersionedMessage>, statically_loaded_runtime_tx: RuntimeTransaction<SanitizedVersionedMessage>,
address_loader: impl AddressLoader, address_loader: impl AddressLoader,
reserved_account_keys: &HashSet<Pubkey>,
) -> Result<Self> { ) -> Result<Self> {
let mut tx = Self { let mut tx = Self {
signatures: statically_loaded_runtime_tx.signatures, signatures: statically_loaded_runtime_tx.signatures,
message: SanitizedMessage::try_new( message: SanitizedMessage::try_new(
statically_loaded_runtime_tx.message, statically_loaded_runtime_tx.message,
address_loader, address_loader,
reserved_account_keys,
)?, )?,
meta: statically_loaded_runtime_tx.meta, meta: statically_loaded_runtime_tx.meta,
}; };
@ -132,6 +136,7 @@ mod tests {
compute_budget::ComputeBudgetInstruction, compute_budget::ComputeBudgetInstruction,
instruction::Instruction, instruction::Instruction,
message::Message, message::Message,
reserved_account_keys::ReservedAccountKeys,
signer::{keypair::Keypair, Signer}, signer::{keypair::Keypair, Signer},
transaction::{SimpleAddressLoader, Transaction, VersionedTransaction}, transaction::{SimpleAddressLoader, Transaction, VersionedTransaction},
}, },
@ -256,6 +261,7 @@ mod tests {
let dynamically_loaded_transaction = RuntimeTransaction::<SanitizedMessage>::try_from( let dynamically_loaded_transaction = RuntimeTransaction::<SanitizedMessage>::try_from(
statically_loaded_transaction, statically_loaded_transaction,
SimpleAddressLoader::Disabled, SimpleAddressLoader::Disabled,
&ReservedAccountKeys::empty_key_set(),
); );
let dynamically_loaded_transaction = let dynamically_loaded_transaction =
dynamically_loaded_transaction.expect("created from statically loaded tx"); dynamically_loaded_transaction.expect("created from statically loaded tx");

View File

@ -3470,7 +3470,15 @@ impl Bank {
pub fn prepare_entry_batch(&self, txs: Vec<VersionedTransaction>) -> Result<TransactionBatch> { pub fn prepare_entry_batch(&self, txs: Vec<VersionedTransaction>) -> Result<TransactionBatch> {
let sanitized_txs = txs let sanitized_txs = txs
.into_iter() .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<_>>>()?; .collect::<Result<Vec<_>>>()?;
let tx_account_lock_limit = self.get_transaction_account_lock_limit(); let tx_account_lock_limit = self.get_transaction_account_lock_limit();
let lock_results = self let lock_results = self
@ -5901,7 +5909,13 @@ impl Bank {
tx.message.hash() 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 if verification_mode == TransactionVerificationMode::HashAndVerifyPrecompiles

View File

@ -189,7 +189,8 @@ pub(in crate::bank) fn create_genesis_config(lamports: u64) -> (GenesisConfig, K
} }
fn new_sanitized_message(message: Message) -> SanitizedMessage { 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] #[test]

View File

@ -285,15 +285,15 @@ impl SyncClient for BankClient {
} }
fn get_fee_for_message(&self, message: &Message) -> Result<u64> { fn get_fee_for_message(&self, message: &Message) -> Result<u64> {
SanitizedMessage::try_from_legacy_message(message.clone()) SanitizedMessage::try_from_legacy_message(
.ok() message.clone(),
.and_then(|sanitized_message| self.bank.get_fee_for_message(&sanitized_message)) self.bank.get_reserved_account_keys(),
.ok_or_else(|| { )
TransportError::IoError(io::Error::new( .ok()
io::ErrorKind::Other, .and_then(|sanitized_message| self.bank.get_fee_for_message(&sanitized_message))
"Unable calculate fee", .ok_or_else(|| {
)) TransportError::IoError(io::Error::new(io::ErrorKind::Other, "Unable calculate fee"))
}) })
} }
} }

View File

@ -7,6 +7,7 @@ use {
instruction::{AccountMeta, Instruction}, instruction::{AccountMeta, Instruction},
message::{Message, SanitizedMessage}, message::{Message, SanitizedMessage},
pubkey::{self, Pubkey}, pubkey::{self, Pubkey},
reserved_account_keys::ReservedAccountKeys,
sysvar::instructions::{self, construct_instructions_data}, sysvar::instructions::{self, construct_instructions_data},
}, },
test::Bencher, test::Bencher,
@ -29,10 +30,10 @@ fn bench_bincode_instruction_serialize(b: &mut Bencher) {
#[bench] #[bench]
fn bench_construct_instructions_data(b: &mut Bencher) { fn bench_construct_instructions_data(b: &mut Bencher) {
let instructions = make_instructions(); let instructions = make_instructions();
let message = SanitizedMessage::try_from_legacy_message(Message::new( let message = SanitizedMessage::try_from_legacy_message(
&instructions, Message::new(&instructions, Some(&Pubkey::new_unique())),
Some(&Pubkey::new_unique()), &ReservedAccountKeys::empty_key_set(),
)) )
.unwrap(); .unwrap();
b.iter(|| { b.iter(|| {
let instructions = message.decompile_instructions(); let instructions = message.decompile_instructions();
@ -52,10 +53,10 @@ fn bench_bincode_instruction_deserialize(b: &mut Bencher) {
#[bench] #[bench]
fn bench_manual_instruction_deserialize(b: &mut Bencher) { fn bench_manual_instruction_deserialize(b: &mut Bencher) {
let instructions = make_instructions(); let instructions = make_instructions();
let message = SanitizedMessage::try_from_legacy_message(Message::new( let message = SanitizedMessage::try_from_legacy_message(
&instructions, Message::new(&instructions, Some(&Pubkey::new_unique())),
Some(&Pubkey::new_unique()), &ReservedAccountKeys::empty_key_set(),
)) )
.unwrap(); .unwrap();
let serialized = construct_instructions_data(&message.decompile_instructions()); let serialized = construct_instructions_data(&message.decompile_instructions());
b.iter(|| { b.iter(|| {
@ -69,10 +70,10 @@ fn bench_manual_instruction_deserialize(b: &mut Bencher) {
#[bench] #[bench]
fn bench_manual_instruction_deserialize_single(b: &mut Bencher) { fn bench_manual_instruction_deserialize_single(b: &mut Bencher) {
let instructions = make_instructions(); let instructions = make_instructions();
let message = SanitizedMessage::try_from_legacy_message(Message::new( let message = SanitizedMessage::try_from_legacy_message(
&instructions, Message::new(&instructions, Some(&Pubkey::new_unique())),
Some(&Pubkey::new_unique()), &ReservedAccountKeys::empty_key_set(),
)) )
.unwrap(); .unwrap();
let serialized = construct_instructions_data(&message.decompile_instructions()); let serialized = construct_instructions_data(&message.decompile_instructions());
b.iter(|| { b.iter(|| {

View File

@ -550,7 +550,7 @@ impl Message {
/// Returns true if the account at the specified index was requested to be /// Returns true if the account at the specified index was requested to be
/// writable. This method should not be used directly. /// writable. This method should not be used directly.
fn is_writable_index(&self, i: usize) -> bool { pub(super) fn is_writable_index(&self, i: usize) -> bool {
i < (self.header.num_required_signatures - self.header.num_readonly_signed_accounts) i < (self.header.num_required_signatures - self.header.num_readonly_signed_accounts)
as usize as usize
|| (i >= self.header.num_required_signatures as usize || (i >= self.header.num_required_signatures as usize
@ -558,10 +558,11 @@ impl Message {
- self.header.num_readonly_unsigned_accounts as usize) - self.header.num_readonly_unsigned_accounts as usize)
} }
/// Returns true if the account at the specified index should be write /// Returns true if the account at the specified index is writable by the
/// locked when loaded for transaction processing in the runtime. This /// instructions in this message. Since the dynamic set of reserved accounts
/// method differs from `is_maybe_writable` because it is aware of the /// isn't used here to demote write locks, this shouldn't be used in the
/// latest reserved accounts which are not allowed to be write locked. /// runtime.
#[deprecated(since = "2.0.0", note = "Please use `is_maybe_writable` instead")]
pub fn is_writable(&self, i: usize) -> bool { pub fn is_writable(&self, i: usize) -> bool {
(self.is_writable_index(i)) (self.is_writable_index(i))
&& !is_builtin_key_or_sysvar(&self.account_keys[i]) && !is_builtin_key_or_sysvar(&self.account_keys[i])
@ -587,7 +588,7 @@ impl Message {
let mut writable_keys = vec![]; let mut writable_keys = vec![];
let mut readonly_keys = vec![]; let mut readonly_keys = vec![];
for (i, key) in self.account_keys.iter().enumerate() { for (i, key) in self.account_keys.iter().enumerate() {
if self.is_writable(i) { if self.is_maybe_writable(i) {
writable_keys.push(key); writable_keys.push(key);
} else { } else {
readonly_keys.push(key); readonly_keys.push(key);

View File

@ -17,7 +17,7 @@ use {
solana_program::{system_instruction::SystemInstruction, system_program}, solana_program::{system_instruction::SystemInstruction, system_program},
sysvar::instructions::{BorrowedAccountMeta, BorrowedInstruction}, sysvar::instructions::{BorrowedAccountMeta, BorrowedInstruction},
}, },
std::{borrow::Cow, convert::TryFrom}, std::{borrow::Cow, collections::HashSet, convert::TryFrom},
thiserror::Error, thiserror::Error,
}; };
@ -31,12 +31,16 @@ pub struct LegacyMessage<'a> {
} }
impl<'a> LegacyMessage<'a> { impl<'a> LegacyMessage<'a> {
pub fn new(message: legacy::Message) -> Self { pub fn new(message: legacy::Message, reserved_account_keys: &HashSet<Pubkey>) -> Self {
let is_writable_account_cache = message let is_writable_account_cache = message
.account_keys .account_keys
.iter() .iter()
.enumerate() .enumerate()
.map(|(i, _key)| message.is_writable(i)) .map(|(i, _key)| {
message.is_writable_index(i)
&& !reserved_account_keys.contains(&message.account_keys[i])
&& !message.demote_program_id(i)
})
.collect::<Vec<_>>(); .collect::<Vec<_>>();
Self { Self {
message: Cow::Owned(message), message: Cow::Owned(message),
@ -105,23 +109,34 @@ impl SanitizedMessage {
pub fn try_new( pub fn try_new(
sanitized_msg: SanitizedVersionedMessage, sanitized_msg: SanitizedVersionedMessage,
address_loader: impl AddressLoader, address_loader: impl AddressLoader,
reserved_account_keys: &HashSet<Pubkey>,
) -> Result<Self, SanitizeMessageError> { ) -> Result<Self, SanitizeMessageError> {
Ok(match sanitized_msg.message { Ok(match sanitized_msg.message {
VersionedMessage::Legacy(message) => { VersionedMessage::Legacy(message) => {
SanitizedMessage::Legacy(LegacyMessage::new(message)) SanitizedMessage::Legacy(LegacyMessage::new(message, reserved_account_keys))
} }
VersionedMessage::V0(message) => { VersionedMessage::V0(message) => {
let loaded_addresses = let loaded_addresses =
address_loader.load_addresses(&message.address_table_lookups)?; address_loader.load_addresses(&message.address_table_lookups)?;
SanitizedMessage::V0(v0::LoadedMessage::new(message, loaded_addresses)) SanitizedMessage::V0(v0::LoadedMessage::new(
message,
loaded_addresses,
reserved_account_keys,
))
} }
}) })
} }
/// Create a sanitized legacy message /// Create a sanitized legacy message
pub fn try_from_legacy_message(message: legacy::Message) -> Result<Self, SanitizeMessageError> { pub fn try_from_legacy_message(
message: legacy::Message,
reserved_account_keys: &HashSet<Pubkey>,
) -> Result<Self, SanitizeMessageError> {
message.sanitize()?; message.sanitize()?;
Ok(Self::Legacy(LegacyMessage::new(message))) Ok(Self::Legacy(LegacyMessage::new(
message,
reserved_account_keys,
)))
} }
/// Return true if this message contains duplicate account keys /// Return true if this message contains duplicate account keys
@ -431,7 +446,11 @@ mod tests {
}; };
assert_eq!( assert_eq!(
SanitizedMessage::try_from_legacy_message(legacy_message_with_no_signers).err(), SanitizedMessage::try_from_legacy_message(
legacy_message_with_no_signers,
&HashSet::default(),
)
.err(),
Some(SanitizeMessageError::IndexOutOfBounds), Some(SanitizeMessageError::IndexOutOfBounds),
); );
} }
@ -455,6 +474,7 @@ mod tests {
Hash::default(), Hash::default(),
instructions, instructions,
), ),
&HashSet::default(),
) )
.unwrap(); .unwrap();
@ -472,15 +492,18 @@ mod tests {
let key4 = Pubkey::new_unique(); let key4 = Pubkey::new_unique();
let key5 = Pubkey::new_unique(); let key5 = Pubkey::new_unique();
let legacy_message = SanitizedMessage::try_from_legacy_message(legacy::Message { let legacy_message = SanitizedMessage::try_from_legacy_message(
header: MessageHeader { legacy::Message {
num_required_signatures: 2, header: MessageHeader {
num_readonly_signed_accounts: 1, num_required_signatures: 2,
num_readonly_unsigned_accounts: 1, num_readonly_signed_accounts: 1,
num_readonly_unsigned_accounts: 1,
},
account_keys: vec![key0, key1, key2, key3],
..legacy::Message::default()
}, },
account_keys: vec![key0, key1, key2, key3], &HashSet::default(),
..legacy::Message::default() )
})
.unwrap(); .unwrap();
assert_eq!(legacy_message.num_readonly_accounts(), 2); assert_eq!(legacy_message.num_readonly_accounts(), 2);
@ -499,6 +522,7 @@ mod tests {
writable: vec![key4], writable: vec![key4],
readonly: vec![key5], readonly: vec![key5],
}, },
&HashSet::default(),
)); ));
assert_eq!(v0_message.num_readonly_accounts(), 3); assert_eq!(v0_message.num_readonly_accounts(), 3);
@ -525,6 +549,7 @@ mod tests {
Hash::default(), Hash::default(),
instructions, instructions,
), ),
&HashSet::default(),
) )
.unwrap(); .unwrap();
@ -556,15 +581,18 @@ mod tests {
let key4 = Pubkey::new_unique(); let key4 = Pubkey::new_unique();
let key5 = Pubkey::new_unique(); let key5 = Pubkey::new_unique();
let legacy_message = SanitizedMessage::try_from_legacy_message(legacy::Message { let legacy_message = SanitizedMessage::try_from_legacy_message(
header: MessageHeader { legacy::Message {
num_required_signatures: 2, header: MessageHeader {
num_readonly_signed_accounts: 1, num_required_signatures: 2,
num_readonly_unsigned_accounts: 1, num_readonly_signed_accounts: 1,
num_readonly_unsigned_accounts: 1,
},
account_keys: vec![key0, key1, key2, key3],
..legacy::Message::default()
}, },
account_keys: vec![key0, key1, key2, key3], &HashSet::default(),
..legacy::Message::default() )
})
.unwrap(); .unwrap();
match legacy_message { match legacy_message {
SanitizedMessage::Legacy(message) => { SanitizedMessage::Legacy(message) => {
@ -596,6 +624,7 @@ mod tests {
writable: vec![key4], writable: vec![key4],
readonly: vec![key5], readonly: vec![key5],
}, },
&HashSet::default(),
)); ));
match v0_message { match v0_message {
SanitizedMessage::V0(message) => { SanitizedMessage::V0(message) => {
@ -646,6 +675,7 @@ mod tests {
mock_secp256k1_instr, mock_secp256k1_instr,
], ],
), ),
&HashSet::new(),
) )
.unwrap(); .unwrap();

View File

@ -1,7 +1,7 @@
use { use {
crate::{ crate::{
bpf_loader_upgradeable, bpf_loader_upgradeable,
message::{legacy::is_builtin_key_or_sysvar, v0, AccountKeys}, message::{v0, AccountKeys},
pubkey::Pubkey, pubkey::Pubkey,
}, },
std::{borrow::Cow, collections::HashSet}, std::{borrow::Cow, collections::HashSet},
@ -55,32 +55,40 @@ impl LoadedAddresses {
} }
impl<'a> LoadedMessage<'a> { impl<'a> LoadedMessage<'a> {
pub fn new(message: v0::Message, loaded_addresses: LoadedAddresses) -> Self { pub fn new(
message: v0::Message,
loaded_addresses: LoadedAddresses,
reserved_account_keys: &HashSet<Pubkey>,
) -> Self {
let mut loaded_message = Self { let mut loaded_message = Self {
message: Cow::Owned(message), message: Cow::Owned(message),
loaded_addresses: Cow::Owned(loaded_addresses), loaded_addresses: Cow::Owned(loaded_addresses),
is_writable_account_cache: Vec::default(), is_writable_account_cache: Vec::default(),
}; };
loaded_message.set_is_writable_account_cache(); loaded_message.set_is_writable_account_cache(reserved_account_keys);
loaded_message loaded_message
} }
pub fn new_borrowed(message: &'a v0::Message, loaded_addresses: &'a LoadedAddresses) -> Self { pub fn new_borrowed(
message: &'a v0::Message,
loaded_addresses: &'a LoadedAddresses,
reserved_account_keys: &HashSet<Pubkey>,
) -> Self {
let mut loaded_message = Self { let mut loaded_message = Self {
message: Cow::Borrowed(message), message: Cow::Borrowed(message),
loaded_addresses: Cow::Borrowed(loaded_addresses), loaded_addresses: Cow::Borrowed(loaded_addresses),
is_writable_account_cache: Vec::default(), is_writable_account_cache: Vec::default(),
}; };
loaded_message.set_is_writable_account_cache(); loaded_message.set_is_writable_account_cache(reserved_account_keys);
loaded_message loaded_message
} }
fn set_is_writable_account_cache(&mut self) { fn set_is_writable_account_cache(&mut self, reserved_account_keys: &HashSet<Pubkey>) {
let is_writable_account_cache = self let is_writable_account_cache = self
.account_keys() .account_keys()
.iter() .iter()
.enumerate() .enumerate()
.map(|(i, _key)| self.is_writable_internal(i)) .map(|(i, _key)| self.is_writable_internal(i, reserved_account_keys))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let _ = std::mem::replace( let _ = std::mem::replace(
&mut self.is_writable_account_cache, &mut self.is_writable_account_cache,
@ -127,10 +135,14 @@ impl<'a> LoadedMessage<'a> {
} }
/// Returns true if the account at the specified index was loaded as writable /// Returns true if the account at the specified index was loaded as writable
fn is_writable_internal(&self, key_index: usize) -> bool { fn is_writable_internal(
&self,
key_index: usize,
reserved_account_keys: &HashSet<Pubkey>,
) -> bool {
if self.is_writable_index(key_index) { if self.is_writable_index(key_index) {
if let Some(key) = self.account_keys().get(key_index) { if let Some(key) = self.account_keys().get(key_index) {
return !(is_builtin_key_or_sysvar(key) || self.demote_program_id(key_index)); return !(reserved_account_keys.contains(key) || self.demote_program_id(key_index));
} }
} }
false false
@ -201,6 +213,7 @@ mod tests {
writable: vec![key4], writable: vec![key4],
readonly: vec![key5], readonly: vec![key5],
}, },
&HashSet::default(),
); );
(message, [key0, key1, key2, key3, key4, key5]) (message, [key0, key1, key2, key3, key4, key5])
@ -225,6 +238,7 @@ mod tests {
writable: keys.split_off(2), writable: keys.split_off(2),
readonly: keys, readonly: keys,
}, },
&HashSet::default(),
) )
}; };
@ -257,6 +271,8 @@ mod tests {
#[test] #[test]
fn test_is_writable() { fn test_is_writable() {
solana_logger::setup(); solana_logger::setup();
let reserved_account_keys = HashSet::from_iter([sysvar::clock::id(), system_program::id()]);
let create_message_with_keys = |keys: Vec<Pubkey>| { let create_message_with_keys = |keys: Vec<Pubkey>| {
LoadedMessage::new( LoadedMessage::new(
v0::Message { v0::Message {
@ -272,6 +288,7 @@ mod tests {
writable: keys[2..=2].to_vec(), writable: keys[2..=2].to_vec(),
readonly: keys[3..].to_vec(), readonly: keys[3..].to_vec(),
}, },
&reserved_account_keys,
) )
}; };
@ -321,6 +338,7 @@ mod tests {
writable: vec![key1, key2], writable: vec![key1, key2],
readonly: vec![], readonly: vec![],
}, },
&HashSet::default(),
); );
assert!(message.is_writable_index(2)); assert!(message.is_writable_index(2));

View File

@ -302,10 +302,11 @@ mod tests {
message::{Message as LegacyMessage, SanitizedMessage}, message::{Message as LegacyMessage, SanitizedMessage},
pubkey::Pubkey, pubkey::Pubkey,
}, },
std::collections::HashSet,
}; };
fn new_sanitized_message(message: LegacyMessage) -> SanitizedMessage { fn new_sanitized_message(message: LegacyMessage) -> SanitizedMessage {
SanitizedMessage::try_from_legacy_message(message).unwrap() SanitizedMessage::try_from_legacy_message(message, &HashSet::default()).unwrap()
} }
#[test] #[test]

View File

@ -124,6 +124,7 @@ mod tests {
instruction::Instruction, instruction::Instruction,
message::Message, message::Message,
nonce::{self, state::DurableNonce}, nonce::{self, state::DurableNonce},
reserved_account_keys::ReservedAccountKeys,
signature::{keypair_from_seed, Signer}, signature::{keypair_from_seed, Signer},
system_instruction, system_program, system_instruction, system_program,
}, },
@ -133,7 +134,11 @@ mod tests {
instructions: &[Instruction], instructions: &[Instruction],
payer: Option<&Pubkey>, payer: Option<&Pubkey>,
) -> SanitizedMessage { ) -> SanitizedMessage {
SanitizedMessage::try_from_legacy_message(Message::new(instructions, payer)).unwrap() SanitizedMessage::try_from_legacy_message(
Message::new(instructions, payer),
&ReservedAccountKeys::empty_key_set(),
)
.unwrap()
} }
#[test] #[test]

View File

@ -12,6 +12,7 @@ use {
}, },
precompiles::verify_if_precompile, precompiles::verify_if_precompile,
pubkey::Pubkey, pubkey::Pubkey,
reserved_account_keys::ReservedAccountKeys,
sanitize::Sanitize, sanitize::Sanitize,
signature::Signature, signature::Signature,
simple_vote_transaction_checker::is_simple_vote_transaction, simple_vote_transaction_checker::is_simple_vote_transaction,
@ -19,6 +20,7 @@ use {
transaction::{Result, Transaction, TransactionError, VersionedTransaction}, transaction::{Result, Transaction, TransactionError, VersionedTransaction},
}, },
solana_program::message::SanitizedVersionedMessage, solana_program::message::SanitizedVersionedMessage,
std::collections::HashSet,
}; };
/// Maximum number of accounts that a transaction may lock. /// Maximum number of accounts that a transaction may lock.
@ -66,17 +68,22 @@ impl SanitizedTransaction {
message_hash: Hash, message_hash: Hash,
is_simple_vote_tx: bool, is_simple_vote_tx: bool,
address_loader: impl AddressLoader, address_loader: impl AddressLoader,
reserved_account_keys: &HashSet<Pubkey>,
) -> Result<Self> { ) -> Result<Self> {
let signatures = tx.signatures; let signatures = tx.signatures;
let SanitizedVersionedMessage { message } = tx.message; let SanitizedVersionedMessage { message } = tx.message;
let message = match message { let message = match message {
VersionedMessage::Legacy(message) => { VersionedMessage::Legacy(message) => {
SanitizedMessage::Legacy(LegacyMessage::new(message)) SanitizedMessage::Legacy(LegacyMessage::new(message, reserved_account_keys))
} }
VersionedMessage::V0(message) => { VersionedMessage::V0(message) => {
let loaded_addresses = let loaded_addresses =
address_loader.load_addresses(&message.address_table_lookups)?; address_loader.load_addresses(&message.address_table_lookups)?;
SanitizedMessage::V0(v0::LoadedMessage::new(message, loaded_addresses)) SanitizedMessage::V0(v0::LoadedMessage::new(
message,
loaded_addresses,
reserved_account_keys,
))
} }
}; };
@ -96,6 +103,7 @@ impl SanitizedTransaction {
message_hash: impl Into<MessageHash>, message_hash: impl Into<MessageHash>,
is_simple_vote_tx: Option<bool>, is_simple_vote_tx: Option<bool>,
address_loader: impl AddressLoader, address_loader: impl AddressLoader,
reserved_account_keys: &HashSet<Pubkey>,
) -> Result<Self> { ) -> Result<Self> {
let sanitized_versioned_tx = SanitizedVersionedTransaction::try_from(tx)?; let sanitized_versioned_tx = SanitizedVersionedTransaction::try_from(tx)?;
let is_simple_vote_tx = is_simple_vote_tx let is_simple_vote_tx = is_simple_vote_tx
@ -109,15 +117,23 @@ impl SanitizedTransaction {
message_hash, message_hash,
is_simple_vote_tx, is_simple_vote_tx,
address_loader, address_loader,
reserved_account_keys,
) )
} }
pub fn try_from_legacy_transaction(tx: Transaction) -> Result<Self> { /// Create a sanitized transaction from a legacy transaction
pub fn try_from_legacy_transaction(
tx: Transaction,
reserved_account_keys: &HashSet<Pubkey>,
) -> Result<Self> {
tx.sanitize()?; tx.sanitize()?;
Ok(Self { Ok(Self {
message_hash: tx.message.hash(), message_hash: tx.message.hash(),
message: SanitizedMessage::Legacy(LegacyMessage::new(tx.message)), message: SanitizedMessage::Legacy(LegacyMessage::new(
tx.message,
reserved_account_keys,
)),
is_simple_vote_tx: false, is_simple_vote_tx: false,
signatures: tx.signatures, signatures: tx.signatures,
}) })
@ -125,7 +141,7 @@ impl SanitizedTransaction {
/// Create a sanitized transaction from a legacy transaction. Used for tests only. /// Create a sanitized transaction from a legacy transaction. Used for tests only.
pub fn from_transaction_for_tests(tx: Transaction) -> Self { pub fn from_transaction_for_tests(tx: Transaction) -> Self {
Self::try_from_legacy_transaction(tx).unwrap() Self::try_from_legacy_transaction(tx, &ReservedAccountKeys::empty_key_set()).unwrap()
} }
/// Return the first signature for this transaction. /// Return the first signature for this transaction.
@ -292,7 +308,10 @@ impl SanitizedTransaction {
mod tests { mod tests {
use { use {
super::*, super::*,
crate::signer::{keypair::Keypair, Signer}, crate::{
reserved_account_keys::ReservedAccountKeys,
signer::{keypair::Keypair, Signer},
},
solana_program::vote::{self, state::Vote}, solana_program::vote::{self, state::Vote},
}; };
@ -317,6 +336,7 @@ mod tests {
MessageHash::Compute, MessageHash::Compute,
None, None,
SimpleAddressLoader::Disabled, SimpleAddressLoader::Disabled,
&ReservedAccountKeys::empty_key_set(),
) )
.unwrap(); .unwrap();
assert!(vote_transaction.is_simple_vote_transaction()); assert!(vote_transaction.is_simple_vote_transaction());
@ -329,6 +349,7 @@ mod tests {
MessageHash::Compute, MessageHash::Compute,
Some(false), Some(false),
SimpleAddressLoader::Disabled, SimpleAddressLoader::Disabled,
&ReservedAccountKeys::empty_key_set(),
) )
.unwrap(); .unwrap();
assert!(!vote_transaction.is_simple_vote_transaction()); assert!(!vote_transaction.is_simple_vote_transaction());
@ -343,6 +364,7 @@ mod tests {
MessageHash::Compute, MessageHash::Compute,
None, None,
SimpleAddressLoader::Disabled, SimpleAddressLoader::Disabled,
&ReservedAccountKeys::empty_key_set(),
) )
.unwrap(); .unwrap();
assert!(!vote_transaction.is_simple_vote_transaction()); assert!(!vote_transaction.is_simple_vote_transaction());
@ -355,6 +377,7 @@ mod tests {
MessageHash::Compute, MessageHash::Compute,
Some(true), Some(true),
SimpleAddressLoader::Disabled, SimpleAddressLoader::Disabled,
&ReservedAccountKeys::empty_key_set(),
) )
.unwrap(); .unwrap();
assert!(vote_transaction.is_simple_vote_transaction()); assert!(vote_transaction.is_simple_vote_transaction());

View File

@ -491,6 +491,7 @@ mod tests {
rent::Rent, rent::Rent,
rent_collector::{RentCollector, RENT_EXEMPT_RENT_EPOCH}, rent_collector::{RentCollector, RENT_EXEMPT_RENT_EPOCH},
rent_debits::RentDebits, rent_debits::RentDebits,
reserved_account_keys::ReservedAccountKeys,
signature::{Keypair, Signature, Signer}, signature::{Keypair, Signature, Signer},
system_program, system_transaction, sysvar, system_program, system_transaction, sysvar,
transaction::{Result, SanitizedTransaction, Transaction, TransactionError}, transaction::{Result, SanitizedTransaction, Transaction, TransactionError},
@ -570,6 +571,18 @@ mod tests {
features features
} }
fn new_sanitized_message(message: Message) -> SanitizedMessage {
SanitizedMessage::try_from_legacy_message(message, &ReservedAccountKeys::empty_key_set())
.unwrap()
}
fn new_unchecked_sanitized_message(message: Message) -> SanitizedMessage {
SanitizedMessage::Legacy(LegacyMessage::new(
message,
&ReservedAccountKeys::empty_key_set(),
))
}
fn load_accounts_with_fee( fn load_accounts_with_fee(
tx: Transaction, tx: Transaction,
ka: &[TransactionAccount], ka: &[TransactionAccount],
@ -689,7 +702,7 @@ mod tests {
instructions, instructions,
); );
let message = SanitizedMessage::try_from_legacy_message(tx.message().clone()).unwrap(); let message = new_sanitized_message(tx.message().clone());
let fee = FeeStructure::default().calculate_fee( let fee = FeeStructure::default().calculate_fee(
&message, &message,
lamports_per_signature, lamports_per_signature,
@ -1217,7 +1230,7 @@ mod tests {
Hash::default(), Hash::default(),
); );
let message = SanitizedMessage::try_from_legacy_message(tx.message().clone()).unwrap(); let message = new_sanitized_message(tx.message().clone());
let fee = FeeStructure::default().calculate_fee( let fee = FeeStructure::default().calculate_fee(
&message, &message,
lamports_per_signature, lamports_per_signature,
@ -1441,8 +1454,7 @@ mod tests {
recent_blockhash: Hash::default(), recent_blockhash: Hash::default(),
}; };
let legacy = LegacyMessage::new(message); let sanitized_message = new_unchecked_sanitized_message(message);
let sanitized_message = SanitizedMessage::Legacy(legacy);
let mock_bank = TestCallbacks::default(); let mock_bank = TestCallbacks::default();
let mut error_counter = TransactionErrorMetrics::default(); let mut error_counter = TransactionErrorMetrics::default();
let loaded_programs = LoadedProgramsForTxBatch::default(); let loaded_programs = LoadedProgramsForTxBatch::default();
@ -1479,8 +1491,7 @@ mod tests {
recent_blockhash: Hash::default(), recent_blockhash: Hash::default(),
}; };
let legacy = LegacyMessage::new(message); let sanitized_message = new_unchecked_sanitized_message(message);
let sanitized_message = SanitizedMessage::Legacy(legacy);
let mut mock_bank = TestCallbacks::default(); let mut mock_bank = TestCallbacks::default();
mock_bank mock_bank
.accounts_map .accounts_map
@ -1548,8 +1559,7 @@ mod tests {
recent_blockhash: Hash::default(), recent_blockhash: Hash::default(),
}; };
let legacy = LegacyMessage::new(message); let sanitized_message = new_unchecked_sanitized_message(message);
let sanitized_message = SanitizedMessage::Legacy(legacy);
let mut mock_bank = TestCallbacks::default(); let mut mock_bank = TestCallbacks::default();
let mut account_data = AccountSharedData::default(); let mut account_data = AccountSharedData::default();
account_data.set_lamports(200); account_data.set_lamports(200);
@ -1593,8 +1603,7 @@ mod tests {
recent_blockhash: Hash::default(), recent_blockhash: Hash::default(),
}; };
let legacy = LegacyMessage::new(message); let sanitized_message = new_unchecked_sanitized_message(message);
let sanitized_message = SanitizedMessage::Legacy(legacy);
let mut mock_bank = TestCallbacks::default(); let mut mock_bank = TestCallbacks::default();
let mut account_data = AccountSharedData::default(); let mut account_data = AccountSharedData::default();
account_data.set_lamports(200); account_data.set_lamports(200);
@ -1637,8 +1646,7 @@ mod tests {
recent_blockhash: Hash::default(), recent_blockhash: Hash::default(),
}; };
let legacy = LegacyMessage::new(message); let sanitized_message = new_unchecked_sanitized_message(message);
let sanitized_message = SanitizedMessage::Legacy(legacy);
let mut mock_bank = TestCallbacks::default(); let mut mock_bank = TestCallbacks::default();
let mut account_data = AccountSharedData::default(); let mut account_data = AccountSharedData::default();
account_data.set_lamports(200); account_data.set_lamports(200);
@ -1684,8 +1692,7 @@ mod tests {
recent_blockhash: Hash::default(), recent_blockhash: Hash::default(),
}; };
let legacy = LegacyMessage::new(message); let sanitized_message = new_unchecked_sanitized_message(message);
let sanitized_message = SanitizedMessage::Legacy(legacy);
let mut mock_bank = TestCallbacks::default(); let mut mock_bank = TestCallbacks::default();
let mut account_data = AccountSharedData::default(); let mut account_data = AccountSharedData::default();
account_data.set_owner(native_loader::id()); account_data.set_owner(native_loader::id());
@ -1754,8 +1761,7 @@ mod tests {
recent_blockhash: Hash::default(), recent_blockhash: Hash::default(),
}; };
let legacy = LegacyMessage::new(message); let sanitized_message = new_unchecked_sanitized_message(message);
let sanitized_message = SanitizedMessage::Legacy(legacy);
let mut mock_bank = TestCallbacks::default(); let mut mock_bank = TestCallbacks::default();
let mut account_data = AccountSharedData::default(); let mut account_data = AccountSharedData::default();
account_data.set_executable(true); account_data.set_executable(true);
@ -1807,8 +1813,7 @@ mod tests {
recent_blockhash: Hash::default(), recent_blockhash: Hash::default(),
}; };
let legacy = LegacyMessage::new(message); let sanitized_message = new_unchecked_sanitized_message(message);
let sanitized_message = SanitizedMessage::Legacy(legacy);
let mut mock_bank = TestCallbacks::default(); let mut mock_bank = TestCallbacks::default();
let mut account_data = AccountSharedData::default(); let mut account_data = AccountSharedData::default();
account_data.set_executable(true); account_data.set_executable(true);
@ -1868,8 +1873,7 @@ mod tests {
recent_blockhash: Hash::default(), recent_blockhash: Hash::default(),
}; };
let legacy = LegacyMessage::new(message); let sanitized_message = new_unchecked_sanitized_message(message);
let sanitized_message = SanitizedMessage::Legacy(legacy);
let mut mock_bank = TestCallbacks::default(); let mut mock_bank = TestCallbacks::default();
let mut account_data = AccountSharedData::default(); let mut account_data = AccountSharedData::default();
account_data.set_executable(true); account_data.set_executable(true);
@ -1957,8 +1961,7 @@ mod tests {
recent_blockhash: Hash::default(), recent_blockhash: Hash::default(),
}; };
let legacy = LegacyMessage::new(message); let sanitized_message = new_unchecked_sanitized_message(message);
let sanitized_message = SanitizedMessage::Legacy(legacy);
let mut mock_bank = TestCallbacks::default(); let mut mock_bank = TestCallbacks::default();
let mut account_data = AccountSharedData::default(); let mut account_data = AccountSharedData::default();
account_data.set_executable(true); account_data.set_executable(true);
@ -2110,8 +2113,7 @@ mod tests {
recent_blockhash: Hash::default(), recent_blockhash: Hash::default(),
}; };
let legacy = LegacyMessage::new(message); let sanitized_message = new_unchecked_sanitized_message(message);
let sanitized_message = SanitizedMessage::Legacy(legacy);
let mut mock_bank = TestCallbacks::default(); let mut mock_bank = TestCallbacks::default();
let mut account_data = AccountSharedData::default(); let mut account_data = AccountSharedData::default();
account_data.set_executable(true); account_data.set_executable(true);
@ -2202,8 +2204,7 @@ mod tests {
recent_blockhash: Hash::default(), recent_blockhash: Hash::default(),
}; };
let legacy = LegacyMessage::new(message); let sanitized_message = new_unchecked_sanitized_message(message);
let sanitized_message = SanitizedMessage::Legacy(legacy);
let sanitized_transaction = SanitizedTransaction::new_for_tests( let sanitized_transaction = SanitizedTransaction::new_for_tests(
sanitized_message, sanitized_message,
vec![Signature::new_unique()], vec![Signature::new_unique()],

View File

@ -160,6 +160,7 @@ mod tests {
native_loader::{self, create_loadable_account_for_test}, native_loader::{self, create_loadable_account_for_test},
pubkey::Pubkey, pubkey::Pubkey,
rent::Rent, rent::Rent,
reserved_account_keys::ReservedAccountKeys,
secp256k1_instruction::new_secp256k1_instruction, secp256k1_instruction::new_secp256k1_instruction,
secp256k1_program, system_program, secp256k1_program, system_program,
transaction_context::TransactionContext, transaction_context::TransactionContext,
@ -177,7 +178,8 @@ mod tests {
} }
fn new_sanitized_message(message: Message) -> SanitizedMessage { 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] #[test]

View File

@ -82,6 +82,7 @@ mod test {
instruction::CompiledInstruction, instruction::CompiledInstruction,
message::{LegacyMessage, Message, MessageHeader, SanitizedMessage}, message::{LegacyMessage, Message, MessageHeader, SanitizedMessage},
rent::Rent, rent::Rent,
reserved_account_keys::ReservedAccountKeys,
signature::{Keypair, Signer}, signature::{Keypair, Signer},
transaction::TransactionError, transaction::TransactionError,
transaction_context::TransactionContext, transaction_context::TransactionContext,
@ -114,8 +115,10 @@ mod test {
recent_blockhash: Hash::default(), recent_blockhash: Hash::default(),
}; };
let legacy = LegacyMessage::new(message); let sanitized_message = SanitizedMessage::Legacy(LegacyMessage::new(
let sanitized_message = SanitizedMessage::Legacy(legacy); message,
&ReservedAccountKeys::empty_key_set(),
));
let transaction_accounts = vec![ let transaction_accounts = vec![
(key1.pubkey(), AccountSharedData::default()), (key1.pubkey(), AccountSharedData::default()),
@ -166,8 +169,10 @@ mod test {
recent_blockhash: Hash::default(), recent_blockhash: Hash::default(),
}; };
let legacy = LegacyMessage::new(message); let sanitized_message = SanitizedMessage::Legacy(LegacyMessage::new(
let sanitized_message = SanitizedMessage::Legacy(legacy); message,
&ReservedAccountKeys::empty_key_set(),
));
let transaction_accounts = vec![ let transaction_accounts = vec![
(key1.pubkey(), AccountSharedData::default()), (key1.pubkey(), AccountSharedData::default()),

View File

@ -995,6 +995,7 @@ mod tests {
fee_calculator::FeeCalculator, fee_calculator::FeeCalculator,
message::{LegacyMessage, Message, MessageHeader}, message::{LegacyMessage, Message, MessageHeader},
rent_debits::RentDebits, rent_debits::RentDebits,
reserved_account_keys::ReservedAccountKeys,
signature::{Keypair, Signature}, signature::{Keypair, Signature},
sysvar::{self, rent::Rent}, sysvar::{self, rent::Rent},
transaction::{SanitizedTransaction, Transaction, TransactionError}, transaction::{SanitizedTransaction, Transaction, TransactionError},
@ -1007,6 +1008,13 @@ mod tests {
}, },
}; };
fn new_unchecked_sanitized_message(message: Message) -> SanitizedMessage {
SanitizedMessage::Legacy(LegacyMessage::new(
message,
&ReservedAccountKeys::empty_key_set(),
))
}
struct TestForkGraph {} struct TestForkGraph {}
impl ForkGraph for TestForkGraph { impl ForkGraph for TestForkGraph {
@ -1711,8 +1719,7 @@ mod tests {
recent_blockhash: Hash::default(), recent_blockhash: Hash::default(),
}; };
let legacy = LegacyMessage::new(message); let sanitized_message = new_unchecked_sanitized_message(message);
let sanitized_message = SanitizedMessage::Legacy(legacy);
let loaded_programs = LoadedProgramsForTxBatch::default(); let loaded_programs = LoadedProgramsForTxBatch::default();
let mock_bank = MockBankCallback::default(); let mock_bank = MockBankCallback::default();
let batch_processor = TransactionBatchProcessor::<TestForkGraph>::default(); let batch_processor = TransactionBatchProcessor::<TestForkGraph>::default();
@ -1836,8 +1843,7 @@ mod tests {
recent_blockhash: Hash::default(), recent_blockhash: Hash::default(),
}; };
let legacy = LegacyMessage::new(message); let sanitized_message = new_unchecked_sanitized_message(message);
let sanitized_message = SanitizedMessage::Legacy(legacy);
let loaded_programs = LoadedProgramsForTxBatch::default(); let loaded_programs = LoadedProgramsForTxBatch::default();
let mock_bank = MockBankCallback::default(); let mock_bank = MockBankCallback::default();
let batch_processor = TransactionBatchProcessor::<TestForkGraph>::default(); let batch_processor = TransactionBatchProcessor::<TestForkGraph>::default();
@ -1954,8 +1960,7 @@ mod tests {
recent_blockhash: Hash::default(), recent_blockhash: Hash::default(),
}; };
let legacy = LegacyMessage::new(message); let sanitized_message = new_unchecked_sanitized_message(message);
let sanitized_message = SanitizedMessage::Legacy(legacy);
let sanitized_transaction_1 = SanitizedTransaction::new_for_tests( let sanitized_transaction_1 = SanitizedTransaction::new_for_tests(
sanitized_message, sanitized_message,
@ -1985,8 +1990,7 @@ mod tests {
recent_blockhash: Hash::default(), recent_blockhash: Hash::default(),
}; };
let legacy = LegacyMessage::new(message); let sanitized_message = new_unchecked_sanitized_message(message);
let sanitized_message = SanitizedMessage::Legacy(legacy);
let sanitized_transaction_2 = SanitizedTransaction::new_for_tests( let sanitized_transaction_2 = SanitizedTransaction::new_for_tests(
sanitized_message, sanitized_message,

View File

@ -4,6 +4,7 @@ use {
instruction::{AccountMeta, CompiledInstruction}, instruction::{AccountMeta, CompiledInstruction},
message::{Message, MessageHeader}, message::{Message, MessageHeader},
pubkey::Pubkey, pubkey::Pubkey,
reserved_account_keys::ReservedAccountKeys,
signature::Signature, signature::Signature,
transaction::{SanitizedTransaction, Transaction}, transaction::{SanitizedTransaction, Transaction},
}, },
@ -126,7 +127,11 @@ impl SanitizedTransactionBuilder {
message, message,
}; };
SanitizedTransaction::try_from_legacy_transaction(transaction).unwrap() SanitizedTransaction::try_from_legacy_transaction(
transaction,
&ReservedAccountKeys::new_all_activated().active,
)
.unwrap()
} }
} }

View File

@ -19,6 +19,7 @@ use {
AccountKeys, Message, MessageHeader, VersionedMessage, AccountKeys, Message, MessageHeader, VersionedMessage,
}, },
pubkey::Pubkey, pubkey::Pubkey,
reserved_account_keys::ReservedAccountKeys,
signature::Signature, signature::Signature,
transaction::{ transaction::{
Result as TransactionResult, Transaction, TransactionError, TransactionVersion, Result as TransactionResult, Transaction, TransactionError, TransactionVersion,
@ -980,12 +981,16 @@ impl VersionedTransactionWithStatusMeta {
show_rewards: bool, show_rewards: bool,
) -> Result<EncodedTransactionWithStatusMeta, EncodeError> { ) -> Result<EncodedTransactionWithStatusMeta, EncodeError> {
let version = self.validate_version(max_supported_transaction_version)?; let version = self.validate_version(max_supported_transaction_version)?;
let reserved_account_keys = ReservedAccountKeys::new_all_activated();
let account_keys = match &self.transaction.message { let account_keys = match &self.transaction.message {
VersionedMessage::Legacy(message) => parse_legacy_message_accounts(message), VersionedMessage::Legacy(message) => parse_legacy_message_accounts(message),
VersionedMessage::V0(message) => { VersionedMessage::V0(message) => {
let loaded_message = let loaded_message = LoadedMessage::new_borrowed(
LoadedMessage::new_borrowed(message, &self.meta.loaded_addresses); message,
&self.meta.loaded_addresses,
&reserved_account_keys.active,
);
parse_v0_message_accounts(&loaded_message) parse_v0_message_accounts(&loaded_message)
} }
}; };
@ -1228,8 +1233,13 @@ impl EncodableWithMeta for v0::Message {
meta: &TransactionStatusMeta, meta: &TransactionStatusMeta,
) -> Self::Encoded { ) -> Self::Encoded {
if encoding == UiTransactionEncoding::JsonParsed { if encoding == UiTransactionEncoding::JsonParsed {
let reserved_account_keys = ReservedAccountKeys::new_all_activated();
let account_keys = AccountKeys::new(&self.account_keys, Some(&meta.loaded_addresses)); let account_keys = AccountKeys::new(&self.account_keys, Some(&meta.loaded_addresses));
let loaded_message = LoadedMessage::new_borrowed(self, &meta.loaded_addresses); let loaded_message = LoadedMessage::new_borrowed(
self,
&meta.loaded_addresses,
&reserved_account_keys.active,
);
UiMessage::Parsed(UiParsedMessage { UiMessage::Parsed(UiParsedMessage {
account_keys: parse_v0_message_accounts(&loaded_message), account_keys: parse_v0_message_accounts(&loaded_message),
recent_blockhash: self.recent_blockhash.to_string(), recent_blockhash: self.recent_blockhash.to_string(),

View File

@ -54,6 +54,7 @@ mod test {
solana_sdk::{ solana_sdk::{
message::{v0, v0::LoadedAddresses, MessageHeader}, message::{v0, v0::LoadedAddresses, MessageHeader},
pubkey::Pubkey, pubkey::Pubkey,
reserved_account_keys::ReservedAccountKeys,
}, },
}; };
@ -126,6 +127,7 @@ mod test {
writable: vec![pubkey4], writable: vec![pubkey4],
readonly: vec![pubkey5], readonly: vec![pubkey5],
}, },
&ReservedAccountKeys::empty_key_set(),
); );
assert_eq!( assert_eq!(