diff --git a/accounts-db/src/nonce_info.rs b/accounts-db/src/nonce_info.rs index 16917be15c3d9a..713ba05b429ec5 100644 --- a/accounts-db/src/nonce_info.rs +++ b/accounts-db/src/nonce_info.rs @@ -125,6 +125,7 @@ mod tests { instruction::Instruction, message::Message, nonce::{self, state::DurableNonce}, + reserved_account_keys::ReservedAccountKeys, signature::{keypair_from_seed, Signer}, system_instruction, system_program, }, @@ -134,7 +135,11 @@ mod tests { instructions: &[Instruction], payer: Option<&Pubkey>, ) -> SanitizedMessage { - Message::new(instructions, payer).try_into().unwrap() + SanitizedMessage::try_from_legacy_message( + Message::new(instructions, payer), + &ReservedAccountKeys::empty(), + ) + .unwrap() } #[test] diff --git a/banks-server/src/banks_server.rs b/banks-server/src/banks_server.rs index 1fcdce1ad436c5..7998c94536f693 100644 --- a/banks-server/src/banks_server.rs +++ b/banks-server/src/banks_server.rs @@ -31,7 +31,6 @@ use { tpu_info::NullTpuInfo, }, std::{ - convert::TryFrom, io, net::{Ipv4Addr, SocketAddr}, sync::{atomic::AtomicBool, Arc, RwLock}, @@ -179,6 +178,7 @@ fn simulate_transaction( MessageHash::Compute, Some(false), // is_simple_vote_tx bank, + &bank.reserved_account_keys, ) { Err(err) => { return BanksTransactionResultWithSimulation { @@ -333,6 +333,7 @@ impl Banks for BanksServer { MessageHash::Compute, Some(false), // is_simple_vote_tx bank.as_ref(), + &bank.reserved_account_keys, ) { Ok(tx) => tx, Err(err) => return Some(Err(err)), @@ -418,7 +419,8 @@ impl Banks for BanksServer { commitment: CommitmentLevel, ) -> Option { let bank = self.bank(commitment); - let sanitized_message = SanitizedMessage::try_from(message).ok()?; + let sanitized_message = + SanitizedMessage::try_from_legacy_message(message, &bank.reserved_account_keys).ok()?; bank.get_fee_for_message(&sanitized_message) } } diff --git a/core/src/banking_stage/consumer.rs b/core/src/banking_stage/consumer.rs index 64b68889747633..87e52e93683632 100644 --- a/core/src/banking_stage/consumer.rs +++ b/core/src/banking_stage/consumer.rs @@ -836,6 +836,7 @@ mod tests { nonce_account::verify_nonce_account, poh_config::PohConfig, pubkey::Pubkey, + reserved_account_keys::ReservedAccountKeys, signature::Keypair, signer::Signer, system_instruction, system_program, system_transaction, @@ -2002,6 +2003,7 @@ mod tests { MessageHash::Compute, Some(false), bank.as_ref(), + &ReservedAccountKeys::empty(), ) .unwrap(); diff --git a/core/src/banking_stage/immutable_deserialized_packet.rs b/core/src/banking_stage/immutable_deserialized_packet.rs index 8a9d82e32a38c0..f6b452f543d07b 100644 --- a/core/src/banking_stage/immutable_deserialized_packet.rs +++ b/core/src/banking_stage/immutable_deserialized_packet.rs @@ -7,6 +7,7 @@ use { feature_set, hash::Hash, message::Message, + pubkey::Pubkey, sanitize::SanitizeError, short_vec::decode_shortu16_len, signature::Signature, @@ -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, }; @@ -107,6 +108,7 @@ impl ImmutableDeserializedPacket { feature_set: &Arc, votes_only: bool, address_loader: impl AddressLoader, + reserved_account_keys: &HashSet, ) -> Option { if votes_only && !self.is_simple_vote() { return None; @@ -116,6 +118,7 @@ impl ImmutableDeserializedPacket { *self.message_hash(), self.is_simple_vote(), address_loader, + reserved_account_keys, ) .ok()?; tx.verify_precompiles(feature_set).ok()?; diff --git a/core/src/banking_stage/latest_unprocessed_votes.rs b/core/src/banking_stage/latest_unprocessed_votes.rs index a62e5bf9b3e455..70fc282d02ed75 100644 --- a/core/src/banking_stage/latest_unprocessed_votes.rs +++ b/core/src/banking_stage/latest_unprocessed_votes.rs @@ -283,6 +283,7 @@ impl LatestUnprocessedVotes { &bank.feature_set, bank.vote_only_bank(), bank.as_ref(), + &bank.reserved_account_keys, ) { if forward_packet_batches_by_accounts.try_add_packet( diff --git a/core/src/banking_stage/read_write_account_set.rs b/core/src/banking_stage/read_write_account_set.rs index b9d65ff4756857..0e1feeca530982 100644 --- a/core/src/banking_stage/read_write_account_set.rs +++ b/core/src/banking_stage/read_write_account_set.rs @@ -139,6 +139,7 @@ mod tests { MessageHash::Compute, Some(false), bank, + &bank.reserved_account_keys, ) .unwrap() } diff --git a/core/src/banking_stage/transaction_scheduler/scheduler_controller.rs b/core/src/banking_stage/transaction_scheduler/scheduler_controller.rs index 225ff6a53e18c5..01a047f9b510b2 100644 --- a/core/src/banking_stage/transaction_scheduler/scheduler_controller.rs +++ b/core/src/banking_stage/transaction_scheduler/scheduler_controller.rs @@ -313,7 +313,12 @@ impl SchedulerController { .iter() .filter_map(|packet| { packet - .build_sanitized_transaction(feature_set, vote_only, bank.as_ref()) + .build_sanitized_transaction( + feature_set, + vote_only, + bank.as_ref(), + &bank.reserved_account_keys, + ) .map(|tx| (tx, packet.priority_details())) }) .inspect(|_| saturating_add_assign!(post_sanitization_count, 1)) diff --git a/core/src/banking_stage/unprocessed_packet_batches.rs b/core/src/banking_stage/unprocessed_packet_batches.rs index ff323ef25f18ee..3d9ae8a0f018f4 100644 --- a/core/src/banking_stage/unprocessed_packet_batches.rs +++ b/core/src/banking_stage/unprocessed_packet_batches.rs @@ -298,6 +298,7 @@ mod tests { solana_sdk::{ compute_budget::ComputeBudgetInstruction, message::Message, + reserved_account_keys::ReservedAccountKeys, signature::{Keypair, Signer}, system_instruction, system_transaction, transaction::{SimpleAddressLoader, Transaction}, @@ -475,6 +476,7 @@ mod tests { &Arc::new(FeatureSet::default()), votes_only, SimpleAddressLoader::Disabled, + &ReservedAccountKeys::empty(), ) }); assert_eq!(2, txs.count()); @@ -485,6 +487,7 @@ mod tests { &Arc::new(FeatureSet::default()), votes_only, SimpleAddressLoader::Disabled, + &ReservedAccountKeys::empty(), ) }); assert_eq!(0, txs.count()); @@ -504,6 +507,7 @@ mod tests { &Arc::new(FeatureSet::default()), votes_only, SimpleAddressLoader::Disabled, + &ReservedAccountKeys::empty(), ) }); assert_eq!(3, txs.count()); @@ -514,6 +518,7 @@ mod tests { &Arc::new(FeatureSet::default()), votes_only, SimpleAddressLoader::Disabled, + &ReservedAccountKeys::empty(), ) }); assert_eq!(2, txs.count()); @@ -533,6 +538,7 @@ mod tests { &Arc::new(FeatureSet::default()), votes_only, SimpleAddressLoader::Disabled, + &ReservedAccountKeys::empty(), ) }); assert_eq!(3, txs.count()); @@ -543,6 +549,7 @@ mod tests { &Arc::new(FeatureSet::default()), votes_only, SimpleAddressLoader::Disabled, + &ReservedAccountKeys::empty(), ) }); assert_eq!(3, txs.count()); diff --git a/core/src/banking_stage/unprocessed_transaction_storage.rs b/core/src/banking_stage/unprocessed_transaction_storage.rs index f8d99c77900c51..f697d52c69f679 100644 --- a/core/src/banking_stage/unprocessed_transaction_storage.rs +++ b/core/src/banking_stage/unprocessed_transaction_storage.rs @@ -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.reserved_account_keys, + )); payload .slot_metrics_tracker @@ -744,7 +748,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.reserved_account_keys, + ) .map(|transaction| (transaction, packet_index)) }) .unzip(); diff --git a/cost-model/src/cost_tracker.rs b/cost-model/src/cost_tracker.rs index 9d2b3b624afeb4..51804a85debd40 100644 --- a/cost-model/src/cost_tracker.rs +++ b/cost-model/src/cost_tracker.rs @@ -270,6 +270,7 @@ mod tests { crate::transaction_cost::*, solana_sdk::{ hash::Hash, + reserved_account_keys::ReservedAccountKeys, signature::{Keypair, Signer}, system_transaction, transaction::{ @@ -332,6 +333,7 @@ mod tests { MessageHash::Compute, Some(true), SimpleAddressLoader::Disabled, + &ReservedAccountKeys::empty(), ) .unwrap(); diff --git a/cost-model/src/transaction_cost.rs b/cost-model/src/transaction_cost.rs index e765eee3bc7038..42fa0021b70c84 100644 --- a/cost-model/src/transaction_cost.rs +++ b/cost-model/src/transaction_cost.rs @@ -172,6 +172,7 @@ mod tests { feature_set::FeatureSet, hash::Hash, message::SimpleAddressLoader, + reserved_account_keys::ReservedAccountKeys, signer::keypair::Keypair, transaction::{MessageHash, SanitizedTransaction, VersionedTransaction}, }, @@ -200,6 +201,7 @@ mod tests { MessageHash::Compute, Some(true), SimpleAddressLoader::Disabled, + &ReservedAccountKeys::empty(), ) .unwrap(); @@ -209,6 +211,7 @@ mod tests { MessageHash::Compute, Some(false), SimpleAddressLoader::Disabled, + &ReservedAccountKeys::empty(), ) .unwrap(); diff --git a/entry/benches/entry_sigverify.rs b/entry/benches/entry_sigverify.rs index b3a1b7b5cdb3e6..7e24a5bca02053 100644 --- a/entry/benches/entry_sigverify.rs +++ b/entry/benches/entry_sigverify.rs @@ -5,6 +5,7 @@ use { solana_perf::test_tx::test_tx, solana_sdk::{ hash::Hash, + reserved_account_keys::ReservedAccountKeys, transaction::{ Result, SanitizedTransaction, SimpleAddressLoader, TransactionVerificationMode, VersionedTransaction, @@ -40,6 +41,7 @@ fn bench_gpusigverify(bencher: &mut Bencher) { message_hash, None, SimpleAddressLoader::Disabled, + &ReservedAccountKeys::empty(), ) }?; @@ -81,6 +83,7 @@ fn bench_cpusigverify(bencher: &mut Bencher) { message_hash, None, SimpleAddressLoader::Disabled, + &ReservedAccountKeys::empty(), ) }?; diff --git a/entry/src/entry.rs b/entry/src/entry.rs index af3fdca9518e83..44fea579c454df 100644 --- a/entry/src/entry.rs +++ b/entry/src/entry.rs @@ -946,6 +946,7 @@ mod tests { solana_sdk::{ hash::{hash, Hash}, pubkey::Pubkey, + reserved_account_keys::ReservedAccountKeys, signature::{Keypair, Signer}, system_transaction, transaction::{ @@ -1039,6 +1040,7 @@ mod tests { message_hash, None, SimpleAddressLoader::Disabled, + &ReservedAccountKeys::empty(), ) }?; diff --git a/ledger-tool/src/main.rs b/ledger-tool/src/main.rs index d81d9c212ba432..f77fdbb9616d06 100644 --- a/ledger-tool/src/main.rs +++ b/ledger-tool/src/main.rs @@ -59,6 +59,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, @@ -489,6 +490,9 @@ fn compute_slot_cost(blockstore: &Blockstore, slot: Slot) -> Result<(), String> let mut program_ids = HashMap::new(); let mut cost_tracker = CostTracker::default(); + let feature_set = FeatureSet::all_enabled(); + let reserved_account_keys = ReservedAccountKeys::active_and_inactive(); + for entry in entries { num_transactions += entry.transactions.len(); entry @@ -500,6 +504,7 @@ fn compute_slot_cost(blockstore: &Blockstore, slot: Slot) -> Result<(), String> MessageHash::Compute, None, SimpleAddressLoader::Disabled, + &reserved_account_keys, ) .map_err(|err| { warn!("Failed to compute cost of transaction: {:?}", err); @@ -509,7 +514,7 @@ fn compute_slot_cost(blockstore: &Blockstore, slot: Slot) -> Result<(), String> .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!( @@ -2230,7 +2235,8 @@ fn main() { if let Some((pubkey, account, slot)) = some_account_tuple .filter(|(_, account, _)| Accounts::is_loadable(account.lamports())) { - if !include_sysvars && solana_sdk::sysvar::is_sysvar_id(pubkey) { + if !include_sysvars && solana_sdk::sysvar::check_id(account.owner()) + { return; } @@ -2608,7 +2614,7 @@ fn main() { for (pubkey, warped_account) in all_accounts { // Don't output sysvars; it's always updated but not related to // inflation. - if solana_sdk::sysvar::is_sysvar_id(&pubkey) { + if solana_sdk::sysvar::check_id(warped_account.owner()) { continue; } diff --git a/program-runtime/src/message_processor.rs b/program-runtime/src/message_processor.rs index a428cf930efeca..5e0049aefd6e80 100644 --- a/program-runtime/src/message_processor.rs +++ b/program-runtime/src/message_processor.rs @@ -180,10 +180,11 @@ mod tests { solana_sdk::{ account::{AccountSharedData, ReadableAccount}, instruction::{AccountMeta, Instruction, InstructionError}, - message::{AccountKeys, LegacyMessage, Message}, + message::{AccountKeys, Message}, native_loader::{self, create_loadable_account_for_test}, pubkey::Pubkey, rent::Rent, + reserved_account_keys::ReservedAccountKeys, secp256k1_instruction::new_secp256k1_instruction, secp256k1_program, }, @@ -198,6 +199,10 @@ mod tests { ModifyReadonly, } + fn new_sanitized_message(message: Message) -> SanitizedMessage { + SanitizedMessage::try_from_legacy_message(message, &ReservedAccountKeys::empty()).unwrap() + } + #[test] fn test_process_message_readonly_handling() { #[derive(Serialize, Deserialize)] @@ -272,21 +277,20 @@ mod tests { AccountMeta::new_readonly(readonly_pubkey, false), ]; - let message = - SanitizedMessage::Legacy(LegacyMessage::new(Message::new_with_compiled_instructions( - 1, - 0, - 2, - account_keys.clone(), - Hash::default(), - AccountKeys::new(&account_keys, None).compile_instructions(&[ - Instruction::new_with_bincode( - mock_system_program_id, - &MockSystemInstruction::Correct, - account_metas.clone(), - ), - ]), - ))); + let message = new_sanitized_message(Message::new_with_compiled_instructions( + 1, + 0, + 2, + account_keys.clone(), + Hash::default(), + AccountKeys::new(&account_keys, None).compile_instructions(&[ + Instruction::new_with_bincode( + mock_system_program_id, + &MockSystemInstruction::Correct, + account_metas.clone(), + ), + ]), + )); let sysvar_cache = SysvarCache::default(); let mut programs_modified_by_tx = LoadedProgramsForTxBatch::default(); let result = MessageProcessor::process_message( @@ -322,21 +326,20 @@ mod tests { 0 ); - let message = - SanitizedMessage::Legacy(LegacyMessage::new(Message::new_with_compiled_instructions( - 1, - 0, - 2, - account_keys.clone(), - Hash::default(), - AccountKeys::new(&account_keys, None).compile_instructions(&[ - Instruction::new_with_bincode( - mock_system_program_id, - &MockSystemInstruction::TransferLamports { lamports: 50 }, - account_metas.clone(), - ), - ]), - ))); + let message = new_sanitized_message(Message::new_with_compiled_instructions( + 1, + 0, + 2, + account_keys.clone(), + Hash::default(), + AccountKeys::new(&account_keys, None).compile_instructions(&[ + Instruction::new_with_bincode( + mock_system_program_id, + &MockSystemInstruction::TransferLamports { lamports: 50 }, + account_metas.clone(), + ), + ]), + )); let mut programs_modified_by_tx = LoadedProgramsForTxBatch::default(); let result = MessageProcessor::process_message( &message, @@ -361,21 +364,20 @@ mod tests { )) ); - let message = - SanitizedMessage::Legacy(LegacyMessage::new(Message::new_with_compiled_instructions( - 1, - 0, - 2, - account_keys.clone(), - Hash::default(), - AccountKeys::new(&account_keys, None).compile_instructions(&[ - Instruction::new_with_bincode( - mock_system_program_id, - &MockSystemInstruction::ChangeData { data: 50 }, - account_metas, - ), - ]), - ))); + let message = new_sanitized_message(Message::new_with_compiled_instructions( + 1, + 0, + 2, + account_keys.clone(), + Hash::default(), + AccountKeys::new(&account_keys, None).compile_instructions(&[ + Instruction::new_with_bincode( + mock_system_program_id, + &MockSystemInstruction::ChangeData { data: 50 }, + account_metas, + ), + ]), + )); let mut programs_modified_by_tx = LoadedProgramsForTxBatch::default(); let result = MessageProcessor::process_message( &message, @@ -496,14 +498,14 @@ mod tests { ]; // Try to borrow mut the same account - let message = SanitizedMessage::Legacy(LegacyMessage::new(Message::new( + let message = new_sanitized_message(Message::new( &[Instruction::new_with_bincode( mock_program_id, &MockSystemInstruction::BorrowFail, account_metas.clone(), )], Some(transaction_context.get_key_of_account_at_index(0).unwrap()), - ))); + )); let sysvar_cache = SysvarCache::default(); let mut programs_modified_by_tx = LoadedProgramsForTxBatch::default(); let result = MessageProcessor::process_message( @@ -530,14 +532,14 @@ mod tests { ); // Try to borrow mut the same account in a safe way - let message = SanitizedMessage::Legacy(LegacyMessage::new(Message::new( + let message = new_sanitized_message(Message::new( &[Instruction::new_with_bincode( mock_program_id, &MockSystemInstruction::MultiBorrowMut, account_metas.clone(), )], Some(transaction_context.get_key_of_account_at_index(0).unwrap()), - ))); + )); let mut programs_modified_by_tx = LoadedProgramsForTxBatch::default(); let result = MessageProcessor::process_message( &message, @@ -557,7 +559,7 @@ mod tests { assert!(result.is_ok()); // Do work on the same transaction account but at different instruction accounts - let message = SanitizedMessage::Legacy(LegacyMessage::new(Message::new( + let message = new_sanitized_message(Message::new( &[Instruction::new_with_bincode( mock_program_id, &MockSystemInstruction::DoWork { @@ -567,7 +569,7 @@ mod tests { account_metas, )], Some(transaction_context.get_key_of_account_at_index(0).unwrap()), - ))); + )); let mut programs_modified_by_tx = LoadedProgramsForTxBatch::default(); let result = MessageProcessor::process_message( &message, @@ -642,13 +644,13 @@ mod tests { } } }; - let message = SanitizedMessage::Legacy(LegacyMessage::new(Message::new( + let message = new_sanitized_message(Message::new( &[ new_secp256k1_instruction(&secret_key, b"hello"), Instruction::new_with_bytes(mock_program_id, &[], vec![]), ], None, - ))); + )); let sysvar_cache = SysvarCache::default(); let mut programs_loaded_for_tx_batch = LoadedProgramsForTxBatch::default(); programs_loaded_for_tx_batch.replenish( diff --git a/programs/sbf/tests/programs.rs b/programs/sbf/tests/programs.rs index 8f8f9d2ffec92d..29cd8d6551146e 100644 --- a/programs/sbf/tests/programs.rs +++ b/programs/sbf/tests/programs.rs @@ -82,6 +82,7 @@ use { message::Message, pubkey::Pubkey, rent::Rent, + reserved_account_keys::ReservedAccountKeys, signature::{Keypair, Signer}, system_program, transaction::{SanitizedTransaction, Transaction, TransactionError}, @@ -207,7 +208,11 @@ fn execute_transactions( } .expect("lamports_per_signature must be available"); let fee = bank.get_fee_for_message_with_lamports_per_signature( - &SanitizedMessage::try_from(tx.message().clone()).unwrap(), + &SanitizedMessage::try_from_legacy_message( + tx.message().clone(), + &ReservedAccountKeys::empty(), + ) + .unwrap(), lamports_per_signature, ); @@ -3982,7 +3987,9 @@ fn test_program_fees() { Some(&mint_keypair.pubkey()), ); - let sanitized_message = SanitizedMessage::try_from(message.clone()).unwrap(); + let sanitized_message = + SanitizedMessage::try_from_legacy_message(message.clone(), &ReservedAccountKeys::empty()) + .unwrap(); let expected_normal_fee = fee_structure.calculate_fee( &sanitized_message, congestion_multiplier, @@ -4005,7 +4012,9 @@ fn test_program_fees() { ], Some(&mint_keypair.pubkey()), ); - let sanitized_message = SanitizedMessage::try_from(message.clone()).unwrap(); + let sanitized_message = + SanitizedMessage::try_from_legacy_message(message.clone(), &ReservedAccountKeys::empty()) + .unwrap(); let expected_prioritized_fee = fee_structure.calculate_fee( &sanitized_message, congestion_multiplier, diff --git a/rpc-client/src/nonblocking/rpc_client.rs b/rpc-client/src/nonblocking/rpc_client.rs index 01f9510b68c9f2..83bb4387dafbe1 100644 --- a/rpc-client/src/nonblocking/rpc_client.rs +++ b/rpc-client/src/nonblocking/rpc_client.rs @@ -675,7 +675,7 @@ impl RpcClient { 'sending: for _ in 0..SEND_RETRIES { let signature = self.send_transaction(transaction).await?; - let recent_blockhash = if transaction.uses_durable_nonce() { + let recent_blockhash = if transaction.maybe_uses_durable_nonce() { let (recent_blockhash, ..) = self .get_latest_blockhash_with_commitment(CommitmentConfig::processed()) .await?; @@ -753,7 +753,7 @@ impl RpcClient { commitment: CommitmentConfig, config: RpcSendTransactionConfig, ) -> ClientResult { - let recent_blockhash = if transaction.uses_durable_nonce() { + let recent_blockhash = if transaction.maybe_uses_durable_nonce() { self.get_latest_blockhash_with_commitment(CommitmentConfig::processed()) .await? .0 diff --git a/rpc-client/src/rpc_client.rs b/rpc-client/src/rpc_client.rs index 0c5f1fdc4ac549..ab62cd9df796b9 100644 --- a/rpc-client/src/rpc_client.rs +++ b/rpc-client/src/rpc_client.rs @@ -45,7 +45,7 @@ use { message::{v0, Message as LegacyMessage}, pubkey::Pubkey, signature::Signature, - transaction::{self, uses_durable_nonce, Transaction, VersionedTransaction}, + transaction::{self, maybe_uses_durable_nonce, Transaction, VersionedTransaction}, }, solana_transaction_status::{ EncodedConfirmedBlock, EncodedConfirmedTransactionWithStatusMeta, TransactionStatus, @@ -80,7 +80,7 @@ impl SerializableMessage for v0::Message {} pub trait SerializableTransaction: Serialize { fn get_signature(&self) -> &Signature; fn get_recent_blockhash(&self) -> &Hash; - fn uses_durable_nonce(&self) -> bool; + fn maybe_uses_durable_nonce(&self) -> bool; } impl SerializableTransaction for Transaction { fn get_signature(&self) -> &Signature { @@ -89,8 +89,8 @@ impl SerializableTransaction for Transaction { fn get_recent_blockhash(&self) -> &Hash { &self.message.recent_blockhash } - fn uses_durable_nonce(&self) -> bool { - uses_durable_nonce(self).is_some() + fn maybe_uses_durable_nonce(&self) -> bool { + maybe_uses_durable_nonce(self).is_some() } } impl SerializableTransaction for VersionedTransaction { @@ -100,8 +100,8 @@ impl SerializableTransaction for VersionedTransaction { fn get_recent_blockhash(&self) -> &Hash { self.message.recent_blockhash() } - fn uses_durable_nonce(&self) -> bool { - self.uses_durable_nonce() + fn maybe_uses_durable_nonce(&self) -> bool { + self.maybe_uses_durable_nonce() } } diff --git a/rpc/src/rpc.rs b/rpc/src/rpc.rs index 5cc5b82344e0d1..7b2d2cf0c156fb 100644 --- a/rpc/src/rpc.rs +++ b/rpc/src/rpc.rs @@ -3637,7 +3637,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.reserved_account_keys, + )?; let signature = *transaction.signature(); let mut last_valid_block_height = preflight_bank @@ -3759,7 +3763,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.reserved_account_keys)?; if sig_verify { verify_transaction(&transaction, &bank.feature_set)?; } @@ -4013,10 +4018,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.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)) } @@ -4545,9 +4552,16 @@ where fn sanitize_transaction( transaction: VersionedTransaction, address_loader: impl AddressLoader, + reserved_account_keys: &HashSet, ) -> Result { - 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) -> Arc> { @@ -4694,6 +4708,7 @@ pub mod tests { Message, MessageHeader, VersionedMessage, }, nonce::{self, state::DurableNonce}, + reserved_account_keys::ReservedAccountKeys, rpc_port, signature::{Keypair, Signer}, slot_hashes::SlotHashes, @@ -5061,7 +5076,7 @@ pub mod tests { let prioritization_fee_cache = &self.meta.prioritization_fee_cache; let transactions: Vec<_> = transactions .into_iter() - .map(|tx| SanitizedTransaction::try_from_legacy_transaction(tx).unwrap()) + .map(SanitizedTransaction::from_transaction_for_tests) .collect(); prioritization_fee_cache.update(&bank, transactions.iter()); } @@ -8955,8 +8970,12 @@ pub mod tests { .to_string(), ); assert_eq!( - sanitize_transaction(unsanitary_versioned_tx, SimpleAddressLoader::Disabled) - .unwrap_err(), + sanitize_transaction( + unsanitary_versioned_tx, + SimpleAddressLoader::Disabled, + &ReservedAccountKeys::empty() + ) + .unwrap_err(), expect58 ); } @@ -8976,7 +8995,12 @@ pub mod tests { }; assert_eq!( - sanitize_transaction(versioned_tx, SimpleAddressLoader::Disabled).unwrap_err(), + sanitize_transaction( + versioned_tx, + SimpleAddressLoader::Disabled, + &ReservedAccountKeys::empty() + ) + .unwrap_err(), Error::invalid_params( "invalid transaction: Transaction version is unsupported".to_string(), ) diff --git a/rpc/src/transaction_status_service.rs b/rpc/src/transaction_status_service.rs index eca53c66658766..a2a19da874654a 100644 --- a/rpc/src/transaction_status_service.rs +++ b/rpc/src/transaction_status_service.rs @@ -227,6 +227,7 @@ pub(crate) mod tests { nonce::{self, state::DurableNonce}, nonce_account, pubkey::Pubkey, + reserved_account_keys::ReservedAccountKeys, signature::{Keypair, Signature, Signer}, system_transaction, transaction::{ @@ -337,6 +338,7 @@ pub(crate) mod tests { MessageHash::Compute, None, SimpleAddressLoader::Disabled, + &ReservedAccountKeys::empty(), ) .unwrap(); @@ -366,7 +368,10 @@ pub(crate) mod tests { durable_nonce_fee: Some(DurableNonceFee::from( &NonceFull::from_partial( rollback_partial, - &SanitizedMessage::Legacy(LegacyMessage::new(message)), + &SanitizedMessage::Legacy(LegacyMessage::new( + message, + &ReservedAccountKeys::empty(), + )), &[(pubkey, nonce_account)], &rent_debits, ) diff --git a/runtime-transaction/src/runtime_transaction.rs b/runtime-transaction/src/runtime_transaction.rs index 3ca7d4fb7920cd..8e4abcbd0cdca6 100644 --- a/runtime-transaction/src/runtime_transaction.rs +++ b/runtime-transaction/src/runtime_transaction.rs @@ -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)] @@ -101,12 +103,14 @@ impl RuntimeTransaction { pub fn try_from( statically_loaded_runtime_tx: RuntimeTransaction, address_loader: impl AddressLoader, + reserved_account_keys: &HashSet, ) -> Result { 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, }; @@ -132,6 +136,7 @@ mod tests { compute_budget::ComputeBudgetInstruction, instruction::Instruction, message::Message, + reserved_account_keys::ReservedAccountKeys, signer::{keypair::Keypair, Signer}, transaction::{SimpleAddressLoader, Transaction, VersionedTransaction}, }, @@ -256,6 +261,7 @@ mod tests { let dynamically_loaded_transaction = RuntimeTransaction::::try_from( statically_loaded_transaction, SimpleAddressLoader::Disabled, + &ReservedAccountKeys::empty(), ); let dynamically_loaded_transaction = dynamically_loaded_transaction.expect("created from statically loaded tx"); diff --git a/runtime/benches/prioritization_fee_cache.rs b/runtime/benches/prioritization_fee_cache.rs index 506aac4fb729a3..8c6bf1fe0a7d68 100644 --- a/runtime/benches/prioritization_fee_cache.rs +++ b/runtime/benches/prioritization_fee_cache.rs @@ -36,7 +36,7 @@ fn build_sanitized_transaction( Some(signer_account), )); - SanitizedTransaction::try_from_legacy_transaction(transaction).unwrap() + SanitizedTransaction::from_transaction_for_tests(transaction) } #[bench] diff --git a/runtime/src/accounts/mod.rs b/runtime/src/accounts/mod.rs index 9c99143416a345..830cb3e749df2c 100644 --- a/runtime/src/accounts/mod.rs +++ b/runtime/src/accounts/mod.rs @@ -513,6 +513,7 @@ mod tests { message::{Message, SanitizedMessage}, nonce, rent::Rent, + reserved_account_keys::ReservedAccountKeys, signature::{Keypair, Signer}, system_program, sysvar, transaction::{Result, Transaction, TransactionError}, @@ -687,7 +688,11 @@ mod tests { instructions, ); - let message = SanitizedMessage::try_from(tx.message().clone()).unwrap(); + let message = SanitizedMessage::try_from_legacy_message( + tx.message().clone(), + &ReservedAccountKeys::empty(), + ) + .unwrap(); let fee = FeeStructure::default().calculate_fee( &message, lamports_per_signature, @@ -1221,7 +1226,11 @@ mod tests { Hash::default(), ); - let message = SanitizedMessage::try_from(tx.message().clone()).unwrap(); + let message = SanitizedMessage::try_from_legacy_message( + tx.message().clone(), + &ReservedAccountKeys::empty(), + ) + .unwrap(); let fee = FeeStructure::default().calculate_fee( &message, lamports_per_signature, diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 0c13e30ff7f6f8..f1c2a5be476f32 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -165,6 +165,7 @@ use { precompiles::get_precompiles, pubkey::Pubkey, rent::RentDue, + reserved_account_keys::ReservedAccountKeys, saturating_add_assign, signature::{Keypair, Signature}, slot_hashes::SlotHashes, @@ -562,6 +563,7 @@ impl PartialEq for Bank { transaction_log_collector_config: _, transaction_log_collector: _, feature_set: _, + reserved_account_keys: _, drop_callback: _, freeze_started: _, vote_only_bank: _, @@ -805,6 +807,9 @@ pub struct Bank { pub feature_set: Arc, + /// Set of reserved account keys that cannot be write locked + pub reserved_account_keys: Arc>, + /// callback function only to be called when dropping and should only be called once pub drop_callback: RwLock, @@ -1012,6 +1017,7 @@ impl Bank { ), transaction_log_collector: Arc::>::default(), feature_set: Arc::::default(), + reserved_account_keys: Arc::>::default(), drop_callback: RwLock::new(OptionalDropCallback(None)), freeze_started: AtomicBool::default(), vote_only_bank: false, @@ -1318,6 +1324,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 @@ -1826,6 +1833,7 @@ impl Bank { ), transaction_log_collector: Arc::>::default(), feature_set: Arc::::default(), + reserved_account_keys: Arc::>::default(), drop_callback: RwLock::new(OptionalDropCallback(None)), freeze_started: AtomicBool::new(fields.hash != Hash::default()), vote_only_bank: false, @@ -4225,7 +4233,15 @@ impl Bank { pub fn prepare_entry_batch(&self, txs: Vec) -> Result { 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.reserved_account_keys, + ) + }) .collect::>>()?; let tx_account_lock_limit = self.get_transaction_account_lock_limit(); let lock_results = self @@ -7218,7 +7234,13 @@ impl Bank { tx.message.hash() }; - SanitizedTransaction::try_create(tx, message_hash, None, self) + SanitizedTransaction::try_create( + tx, + message_hash, + None, + self, + &self.reserved_account_keys, + ) }?; if verification_mode == TransactionVerificationMode::HashAndVerifyPrecompiles @@ -7867,6 +7889,9 @@ impl Bank { } } + // Update active set of reserved account keys which are not allowed to be write locked + self.reserved_account_keys = Arc::new(ReservedAccountKeys::active(&self.feature_set)); + 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 diff --git a/runtime/src/bank/tests.rs b/runtime/src/bank/tests.rs index 33b60b42564682..588ee35104e1c3 100644 --- a/runtime/src/bank/tests.rs +++ b/runtime/src/bank/tests.rs @@ -114,7 +114,7 @@ use { }, std::{ collections::{HashMap, HashSet}, - convert::{TryFrom, TryInto}, + convert::TryInto, fs::File, io::Read, str::FromStr, @@ -2672,7 +2672,7 @@ fn test_bank_tx_compute_unit_fee() { let (bank, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config); let expected_fee_paid = calculate_test_fee( - &SanitizedMessage::try_from(Message::new(&[], Some(&Pubkey::new_unique()))).unwrap(), + &new_sanitized_message(Message::new(&[], Some(&Pubkey::new_unique()))), genesis_config .fee_rate_governor .create_fee_calculator() @@ -2800,7 +2800,7 @@ fn test_bank_blockhash_fee_structure() { assert_eq!(bank.process_transaction(&tx), Ok(())); assert_eq!(bank.get_balance(&key), 1); let cheap_fee = calculate_test_fee( - &SanitizedMessage::try_from(Message::new(&[], Some(&Pubkey::new_unique()))).unwrap(), + &new_sanitized_message(Message::new(&[], Some(&Pubkey::new_unique()))), cheap_lamports_per_signature, &bank.fee_structure, ); @@ -2816,7 +2816,7 @@ fn test_bank_blockhash_fee_structure() { assert_eq!(bank.process_transaction(&tx), Ok(())); assert_eq!(bank.get_balance(&key), 1); let expensive_fee = calculate_test_fee( - &SanitizedMessage::try_from(Message::new(&[], Some(&Pubkey::new_unique()))).unwrap(), + &new_sanitized_message(Message::new(&[], Some(&Pubkey::new_unique()))), expensive_lamports_per_signature, &bank.fee_structure, ); @@ -2862,7 +2862,7 @@ fn test_bank_blockhash_compute_unit_fee_structure() { assert_eq!(bank.process_transaction(&tx), Ok(())); assert_eq!(bank.get_balance(&key), 1); let cheap_fee = calculate_test_fee( - &SanitizedMessage::try_from(Message::new(&[], Some(&Pubkey::new_unique()))).unwrap(), + &new_sanitized_message(Message::new(&[], Some(&Pubkey::new_unique()))), cheap_lamports_per_signature, &bank.fee_structure, ); @@ -2878,7 +2878,7 @@ fn test_bank_blockhash_compute_unit_fee_structure() { assert_eq!(bank.process_transaction(&tx), Ok(())); assert_eq!(bank.get_balance(&key), 1); let expensive_fee = calculate_test_fee( - &SanitizedMessage::try_from(Message::new(&[], Some(&Pubkey::new_unique()))).unwrap(), + &new_sanitized_message(Message::new(&[], Some(&Pubkey::new_unique()))), expensive_lamports_per_signature, &bank.fee_structure, ); @@ -2985,8 +2985,7 @@ fn test_filter_program_errors_and_collect_compute_unit_fee() { .fee_rate_governor .burn( calculate_test_fee( - &SanitizedMessage::try_from(Message::new(&[], Some(&Pubkey::new_unique()))) - .unwrap(), + &new_sanitized_message(Message::new(&[], Some(&Pubkey::new_unique()))), genesis_config .fee_rate_governor .create_fee_calculator() @@ -5282,7 +5281,7 @@ fn test_nonce_transaction() { recent_message.recent_blockhash = bank.last_blockhash(); let mut expected_balance = 4_650_000 - bank - .get_fee_for_message(&recent_message.try_into().unwrap()) + .get_fee_for_message(&new_sanitized_message(recent_message)) .unwrap(); assert_eq!(bank.get_balance(&custodian_pubkey), expected_balance); assert_eq!(bank.get_balance(&nonce_pubkey), 250_000); @@ -5341,7 +5340,7 @@ fn test_nonce_transaction() { let mut recent_message = nonce_tx.message.clone(); recent_message.recent_blockhash = bank.last_blockhash(); expected_balance -= bank - .get_fee_for_message(&SanitizedMessage::try_from(recent_message).unwrap()) + .get_fee_for_message(&new_sanitized_message(recent_message)) .unwrap(); assert_eq!(bank.get_balance(&custodian_pubkey), expected_balance); assert_ne!( @@ -5409,7 +5408,7 @@ fn test_nonce_transaction_with_tx_wide_caps() { recent_message.recent_blockhash = bank.last_blockhash(); let mut expected_balance = 4_650_000 - bank - .get_fee_for_message(&recent_message.try_into().unwrap()) + .get_fee_for_message(&new_sanitized_message(recent_message)) .unwrap(); assert_eq!(bank.get_balance(&custodian_pubkey), expected_balance); assert_eq!(bank.get_balance(&nonce_pubkey), 250_000); @@ -5468,7 +5467,7 @@ fn test_nonce_transaction_with_tx_wide_caps() { let mut recent_message = nonce_tx.message.clone(); recent_message.recent_blockhash = bank.last_blockhash(); expected_balance -= bank - .get_fee_for_message(&SanitizedMessage::try_from(recent_message).unwrap()) + .get_fee_for_message(&new_sanitized_message(recent_message)) .unwrap(); assert_eq!(bank.get_balance(&custodian_pubkey), expected_balance); assert_ne!( @@ -5600,7 +5599,7 @@ fn test_nonce_payer() { bank.get_balance(&nonce_pubkey), nonce_starting_balance - bank - .get_fee_for_message(&recent_message.try_into().unwrap()) + .get_fee_for_message(&new_sanitized_message(recent_message)) .unwrap() ); assert_ne!( @@ -5667,7 +5666,7 @@ fn test_nonce_payer_tx_wide_cap() { bank.get_balance(&nonce_pubkey), nonce_starting_balance - bank - .get_fee_for_message(&recent_message.try_into().unwrap()) + .get_fee_for_message(&new_sanitized_message(recent_message)) .unwrap() ); assert_ne!( @@ -7960,9 +7959,7 @@ fn test_compute_active_feature_set() { 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); - - let mut feature_set = FeatureSet::default(); - bank.feature_set = Arc::new(feature_set.clone()); + bank.feature_set = Arc::new(FeatureSet::default()); assert_eq!( bank.reserved_account_keys.len(), @@ -7971,17 +7968,15 @@ fn test_reserved_account_keys() { ); // Activate `add_new_reserved_account_keys` feature - let feature = Feature::default(); - assert_eq!(feature.activated_at, None); bank.store_account( &feature_set::add_new_reserved_account_keys::id(), - &feature::create_account(&feature, 42), + &feature::create_account(&Feature::default(), 42), ); bank.apply_feature_activations(ApplyFeatureActivationsCaller::NewFromParent, true); assert_eq!( bank.reserved_account_keys.len(), - 28, + 29, "after activating the new feature, bank should have new active reserved keys" ); } @@ -10069,8 +10064,7 @@ fn calculate_test_fee( #[test] fn test_calculate_fee() { // Default: no fee. - let message = - SanitizedMessage::try_from(Message::new(&[], Some(&Pubkey::new_unique()))).unwrap(); + let message = new_sanitized_message(Message::new(&[], Some(&Pubkey::new_unique()))); assert_eq!( calculate_test_fee( &message, @@ -10101,7 +10095,7 @@ fn test_calculate_fee() { let key1 = Pubkey::new_unique(); let ix0 = system_instruction::transfer(&key0, &key1, 1); let ix1 = system_instruction::transfer(&key1, &key0, 1); - let message = SanitizedMessage::try_from(Message::new(&[ix0, ix1], Some(&key0))).unwrap(); + let message = new_sanitized_message(Message::new(&[ix0, ix1], Some(&key0))); assert_eq!( calculate_test_fee( &message, @@ -10126,8 +10120,7 @@ fn test_calculate_fee_compute_units() { // One signature, no unit request - let message = - SanitizedMessage::try_from(Message::new(&[], Some(&Pubkey::new_unique()))).unwrap(); + let message = new_sanitized_message(Message::new(&[], Some(&Pubkey::new_unique()))); assert_eq!( calculate_test_fee(&message, 1, &fee_structure,), max_fee + lamports_per_signature @@ -10137,8 +10130,7 @@ fn test_calculate_fee_compute_units() { let ix0 = system_instruction::transfer(&Pubkey::new_unique(), &Pubkey::new_unique(), 1); let ix1 = system_instruction::transfer(&Pubkey::new_unique(), &Pubkey::new_unique(), 1); - let message = - SanitizedMessage::try_from(Message::new(&[ix0, ix1], Some(&Pubkey::new_unique()))).unwrap(); + let message = new_sanitized_message(Message::new(&[ix0, ix1], Some(&Pubkey::new_unique()))); assert_eq!( calculate_test_fee(&message, 1, &fee_structure,), max_fee + 3 * lamports_per_signature @@ -10164,15 +10156,14 @@ fn test_calculate_fee_compute_units() { PrioritizationFeeType::ComputeUnitPrice(PRIORITIZATION_FEE_RATE), requested_compute_units as u64, ); - let message = SanitizedMessage::try_from(Message::new( + let message = new_sanitized_message(Message::new( &[ ComputeBudgetInstruction::set_compute_unit_limit(requested_compute_units), ComputeBudgetInstruction::set_compute_unit_price(PRIORITIZATION_FEE_RATE), Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]), ], Some(&Pubkey::new_unique()), - )) - .unwrap(); + )); let fee = calculate_test_fee(&message, 1, &fee_structure); assert_eq!( fee, @@ -10196,14 +10187,13 @@ fn test_calculate_prioritization_fee() { ); let prioritization_fee = prioritization_fee_details.get_fee(); - let message = SanitizedMessage::try_from(Message::new( + let message = new_sanitized_message(Message::new( &[ ComputeBudgetInstruction::set_compute_unit_limit(request_units), ComputeBudgetInstruction::set_compute_unit_price(request_unit_price), ], Some(&Pubkey::new_unique()), - )) - .unwrap(); + )); let fee = calculate_test_fee( &message, @@ -10237,24 +10227,22 @@ fn test_calculate_fee_secp256k1() { data: vec![1], }; - let message = SanitizedMessage::try_from(Message::new( + let message = new_sanitized_message(Message::new( &[ ix0.clone(), secp_instruction1.clone(), secp_instruction2.clone(), ], Some(&key0), - )) - .unwrap(); + )); assert_eq!(calculate_test_fee(&message, 1, &fee_structure,), 2); secp_instruction1.data = vec![0]; secp_instruction2.data = vec![10]; - let message = SanitizedMessage::try_from(Message::new( + let message = new_sanitized_message(Message::new( &[ix0, secp_instruction1, secp_instruction2], Some(&key0), - )) - .unwrap(); + )); assert_eq!(calculate_test_fee(&message, 1, &fee_structure,), 11); } @@ -10780,7 +10768,7 @@ fn test_invalid_rent_state_changes_fee_payer() { .unwrap(); // Dummy message to determine fee amount - let dummy_message = SanitizedMessage::try_from(Message::new_with_blockhash( + let dummy_message = new_sanitized_message(Message::new_with_blockhash( &[system_instruction::transfer( &rent_exempt_fee_payer.pubkey(), &recipient, @@ -10788,8 +10776,7 @@ fn test_invalid_rent_state_changes_fee_payer() { )], Some(&rent_exempt_fee_payer.pubkey()), &recent_blockhash, - )) - .unwrap(); + )); let fee = bank.get_fee_for_message(&dummy_message).unwrap(); // RentPaying fee-payer can remain RentPaying @@ -11018,7 +11005,7 @@ fn test_rent_state_list_len() { bank.last_blockhash(), ); let num_accounts = tx.message().account_keys.len(); - let sanitized_tx = SanitizedTransaction::try_from_legacy_transaction(tx).unwrap(); + let sanitized_tx = SanitizedTransaction::from_transaction_for_tests(tx); let mut error_counters = TransactionErrorMetrics::default(); let loaded_txs = load_accounts( &bank.accounts().accounts_db, @@ -11990,7 +11977,7 @@ fn test_calculate_fee_with_congestion_multiplier() { let key1 = Pubkey::new_unique(); let ix0 = system_instruction::transfer(&key0, &key1, 1); let ix1 = system_instruction::transfer(&key1, &key0, 1); - let message = SanitizedMessage::try_from(Message::new(&[ix0, ix1], Some(&key0))).unwrap(); + let message = new_sanitized_message(Message::new(&[ix0, ix1], Some(&key0))); // assert when lamports_per_signature is less than BASE_LAMPORTS, turnning on/off // congestion_multiplier has no effect on fee. @@ -12019,7 +12006,7 @@ fn test_calculate_fee_with_request_heap_frame_flag() { lamports_per_signature: signature_fee, ..FeeStructure::default() }; - let message = SanitizedMessage::try_from(Message::new( + let message = new_sanitized_message(Message::new( &[ system_instruction::transfer(&key0, &key1, 1), ComputeBudgetInstruction::set_compute_unit_limit(request_cu as u32), @@ -12027,8 +12014,7 @@ fn test_calculate_fee_with_request_heap_frame_flag() { ComputeBudgetInstruction::set_compute_unit_price(lamports_per_cu * 1_000_000), ], Some(&key0), - )) - .unwrap(); + )); // assert when request_heap_frame is presented in tx, prioritization fee will be counted // into transaction fee diff --git a/runtime/src/bank_client.rs b/runtime/src/bank_client.rs index 7fe6418d4110b2..e9f9e6f1deea75 100644 --- a/runtime/src/bank_client.rs +++ b/runtime/src/bank_client.rs @@ -19,7 +19,6 @@ use { transport::{Result, TransportError}, }, std::{ - convert::TryFrom, io, sync::{Arc, Mutex}, thread::{sleep, Builder}, @@ -286,7 +285,7 @@ impl SyncClient for BankClient { } fn get_fee_for_message(&self, message: &Message) -> Result { - SanitizedMessage::try_from(message.clone()) + SanitizedMessage::try_from_legacy_message(message.clone(), &self.bank.reserved_account_keys) .ok() .and_then(|sanitized_message| self.bank.get_fee_for_message(&sanitized_message)) .ok_or_else(|| { diff --git a/runtime/src/prioritization_fee_cache.rs b/runtime/src/prioritization_fee_cache.rs index ece749387a9147..5e5ca40ca194bc 100644 --- a/runtime/src/prioritization_fee_cache.rs +++ b/runtime/src/prioritization_fee_cache.rs @@ -462,7 +462,7 @@ mod tests { Some(signer_account), )); - SanitizedTransaction::try_from_legacy_transaction(transaction).unwrap() + SanitizedTransaction::from_transaction_for_tests(transaction) } // update fee cache is asynchronous, this test helper blocks until update is completed. diff --git a/runtime/src/snapshot_minimizer.rs b/runtime/src/snapshot_minimizer.rs index 4e7d576f0b6c95..06beeb4f960a9a 100644 --- a/runtime/src/snapshot_minimizer.rs +++ b/runtime/src/snapshot_minimizer.rs @@ -21,7 +21,7 @@ use { bpf_loader_upgradeable::{self, UpgradeableLoaderState}, clock::Slot, pubkey::Pubkey, - sdk_ids, + reserved_account_keys::ReservedAccountKeys, }, std::{ collections::HashSet, @@ -127,9 +127,11 @@ impl<'a> SnapshotMinimizer<'a> { /// Used to get sdk accounts in `minimize` fn get_sdk_accounts(&self) { - sdk_ids::SDK_IDS.iter().for_each(|pubkey| { - self.minimized_account_set.insert(*pubkey); - }); + ReservedAccountKeys::active_and_inactive() + .iter() + .for_each(|pubkey| { + self.minimized_account_set.insert(*pubkey); + }) } /// Used to get rent collection accounts in `minimize` diff --git a/runtime/src/transaction_priority_details.rs b/runtime/src/transaction_priority_details.rs index 284acb791a2e6a..11e836fb05d855 100644 --- a/runtime/src/transaction_priority_details.rs +++ b/runtime/src/transaction_priority_details.rs @@ -95,8 +95,7 @@ mod tests { ); // assert for SanitizedTransaction - let sanitized_transaction = - SanitizedTransaction::try_from_legacy_transaction(transaction).unwrap(); + let sanitized_transaction = SanitizedTransaction::from_transaction_for_tests(transaction); assert_eq!( sanitized_transaction.get_transaction_priority_details(false), Some(TransactionPriorityDetails { @@ -133,8 +132,7 @@ mod tests { ); // assert for SanitizedTransaction - let sanitized_transaction = - SanitizedTransaction::try_from_legacy_transaction(transaction).unwrap(); + let sanitized_transaction = SanitizedTransaction::from_transaction_for_tests(transaction); assert_eq!( sanitized_transaction.get_transaction_priority_details(false), Some(TransactionPriorityDetails { @@ -171,8 +169,7 @@ mod tests { ); // assert for SanitizedTransaction - let sanitized_transaction = - SanitizedTransaction::try_from_legacy_transaction(transaction).unwrap(); + let sanitized_transaction = SanitizedTransaction::from_transaction_for_tests(transaction); assert_eq!( sanitized_transaction.get_transaction_priority_details(false), Some(TransactionPriorityDetails { diff --git a/sdk/benches/serialize_instructions.rs b/sdk/benches/serialize_instructions.rs index 955bb948fca0d2..e622175bc14fbd 100644 --- a/sdk/benches/serialize_instructions.rs +++ b/sdk/benches/serialize_instructions.rs @@ -7,9 +7,9 @@ use { instruction::{AccountMeta, Instruction}, message::{Message, SanitizedMessage}, pubkey::{self, Pubkey}, + reserved_account_keys::ReservedAccountKeys, sysvar::instructions::{self, construct_instructions_data}, }, - std::convert::TryFrom, test::Bencher, }; @@ -30,9 +30,11 @@ fn bench_bincode_instruction_serialize(b: &mut Bencher) { #[bench] fn bench_construct_instructions_data(b: &mut Bencher) { let instructions = make_instructions(); - let message = - SanitizedMessage::try_from(Message::new(&instructions, Some(&Pubkey::new_unique()))) - .unwrap(); + let message = SanitizedMessage::try_from_legacy_message( + Message::new(&instructions, Some(&Pubkey::new_unique())), + &ReservedAccountKeys::empty(), + ) + .unwrap(); b.iter(|| { let instructions = message.decompile_instructions(); test::black_box(construct_instructions_data(&instructions)); @@ -51,9 +53,11 @@ fn bench_bincode_instruction_deserialize(b: &mut Bencher) { #[bench] fn bench_manual_instruction_deserialize(b: &mut Bencher) { let instructions = make_instructions(); - let message = - SanitizedMessage::try_from(Message::new(&instructions, Some(&Pubkey::new_unique()))) - .unwrap(); + let message = SanitizedMessage::try_from_legacy_message( + Message::new(&instructions, Some(&Pubkey::new_unique())), + &ReservedAccountKeys::empty(), + ) + .unwrap(); let serialized = construct_instructions_data(&message.decompile_instructions()); b.iter(|| { for i in 0..instructions.len() { @@ -66,9 +70,11 @@ fn bench_manual_instruction_deserialize(b: &mut Bencher) { #[bench] fn bench_manual_instruction_deserialize_single(b: &mut Bencher) { let instructions = make_instructions(); - let message = - SanitizedMessage::try_from(Message::new(&instructions, Some(&Pubkey::new_unique()))) - .unwrap(); + let message = SanitizedMessage::try_from_legacy_message( + Message::new(&instructions, Some(&Pubkey::new_unique())), + &ReservedAccountKeys::empty(), + ) + .unwrap(); let serialized = construct_instructions_data(&message.decompile_instructions()); b.iter(|| { #[allow(deprecated)] diff --git a/sdk/program/src/lib.rs b/sdk/program/src/lib.rs index 016585d403ae2a..b10befd421180d 100644 --- a/sdk/program/src/lib.rs +++ b/sdk/program/src/lib.rs @@ -536,6 +536,7 @@ pub mod system_program; pub mod sysvar; pub mod vote; pub mod wasm; +pub mod zk_token_proof_program; #[deprecated( since = "1.17.0", diff --git a/sdk/program/src/message/legacy.rs b/sdk/program/src/message/legacy.rs index 1a6a9239f4e0aa..87e9a6c897920b 100644 --- a/sdk/program/src/message/legacy.rs +++ b/sdk/program/src/message/legacy.rs @@ -22,7 +22,7 @@ use { short_vec, system_instruction, system_program, sysvar, wasm_bindgen, }, lazy_static::lazy_static, - std::{convert::TryFrom, str::FromStr}, + std::{collections::HashSet, convert::TryFrom, str::FromStr}, }; lazy_static! { @@ -58,6 +58,11 @@ lazy_static! { }; } +#[deprecated( + since = "1.18.0", + note = "please use solana_sdk::reserved_account_keys::ReservedAccountKeys instead" +)] +#[allow(deprecated)] pub fn is_builtin_key_or_sysvar(key: &Pubkey) -> bool { if MAYBE_BUILTIN_KEY_OR_SYSVAR[key.0[0] as usize] { return sysvar::is_sysvar_id(key) || BUILTIN_PROGRAMS_KEYS.contains(key); @@ -548,26 +553,37 @@ impl Message { self.is_key_called_as_program(i) && !self.is_upgradeable_loader_present() } - pub fn is_writable(&self, i: usize) -> bool { + pub fn is_writable(&self, i: usize, reserved_account_keys: &HashSet) -> bool { (i < (self.header.num_required_signatures - self.header.num_readonly_signed_accounts) as usize || (i >= self.header.num_required_signatures as usize && i < self.account_keys.len() - self.header.num_readonly_unsigned_accounts as usize)) - && !is_builtin_key_or_sysvar(&self.account_keys[i]) + && !reserved_account_keys.contains(&self.account_keys[i]) && !self.demote_program_id(i) } + /// Returns true if the account at the specified index is writable by the + /// instructions in this message. Since the dynamic set of reserved accounts + /// isn't used here to demote write locks, this shouldn't be used in the + /// runtime. + pub fn is_maybe_writable(&self, i: usize) -> bool { + self.is_writable(i, &HashSet::default()) + } + pub fn is_signer(&self, i: usize) -> bool { i < self.header.num_required_signatures as usize } #[deprecated] - pub fn get_account_keys_by_lock_type(&self) -> (Vec<&Pubkey>, Vec<&Pubkey>) { + pub fn get_account_keys_by_lock_type( + &self, + reserved_account_keys: &HashSet, + ) -> (Vec<&Pubkey>, Vec<&Pubkey>) { let mut writable_keys = vec![]; let mut readonly_keys = vec![]; for (i, key) in self.account_keys.iter().enumerate() { - if self.is_writable(i) { + if self.is_writable(i, reserved_account_keys) { writable_keys.push(key); } else { readonly_keys.push(key); @@ -747,12 +763,14 @@ mod tests { recent_blockhash: Hash::default(), instructions: vec![], }; - assert!(message.is_writable(0)); - assert!(!message.is_writable(1)); - assert!(!message.is_writable(2)); - assert!(message.is_writable(3)); - assert!(message.is_writable(4)); - assert!(!message.is_writable(5)); + + let reserved_keys = HashSet::from_iter([key2, key3]); + assert!(message.is_writable(0, &reserved_keys)); + assert!(!message.is_writable(1, &reserved_keys)); + assert!(!message.is_writable(2, &reserved_keys)); + assert!(!message.is_writable(3, &reserved_keys)); + assert!(message.is_writable(4, &reserved_keys)); + assert!(!message.is_writable(5, &reserved_keys)); } #[test] @@ -780,7 +798,7 @@ mod tests { Some(&id1), ); assert_eq!( - message.get_account_keys_by_lock_type(), + message.get_account_keys_by_lock_type(&HashSet::default()), (vec![&id1, &id0], vec![&id3, &program_id, &id2]) ); } diff --git a/sdk/program/src/message/sanitized.rs b/sdk/program/src/message/sanitized.rs index 098a781ea4dbf7..8a059e307d09a5 100644 --- a/sdk/program/src/message/sanitized.rs +++ b/sdk/program/src/message/sanitized.rs @@ -17,7 +17,7 @@ use { solana_program::{system_instruction::SystemInstruction, system_program}, sysvar::instructions::{BorrowedAccountMeta, BorrowedInstruction}, }, - std::{borrow::Cow, convert::TryFrom}, + std::{borrow::Cow, collections::HashSet, convert::TryFrom}, thiserror::Error, }; @@ -31,12 +31,12 @@ pub struct LegacyMessage<'a> { } impl<'a> LegacyMessage<'a> { - pub fn new(message: legacy::Message) -> Self { + pub fn new(message: legacy::Message, reserved_account_keys: &HashSet) -> Self { let is_writable_account_cache = message .account_keys .iter() .enumerate() - .map(|(i, _key)| message.is_writable(i)) + .map(|(i, _key)| message.is_writable(i, reserved_account_keys)) .collect::>(); Self { message: Cow::Owned(message), @@ -98,14 +98,6 @@ impl From for SanitizeMessageError { } } -impl TryFrom for SanitizedMessage { - type Error = SanitizeMessageError; - fn try_from(message: legacy::Message) -> Result { - message.sanitize()?; - Ok(Self::Legacy(LegacyMessage::new(message))) - } -} - impl SanitizedMessage { /// Create a sanitized message from a sanitized versioned message. /// If the input message uses address tables, attempt to look up the @@ -113,19 +105,36 @@ impl SanitizedMessage { pub fn try_new( sanitized_msg: SanitizedVersionedMessage, address_loader: impl AddressLoader, + reserved_account_keys: &HashSet, ) -> Result { Ok(match sanitized_msg.message { VersionedMessage::Legacy(message) => { - SanitizedMessage::Legacy(LegacyMessage::new(message)) + SanitizedMessage::Legacy(LegacyMessage::new(message, reserved_account_keys)) } VersionedMessage::V0(message) => { let loaded_addresses = 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 + pub fn try_from_legacy_message( + message: legacy::Message, + reserved_account_keys: &HashSet, + ) -> Result { + message.sanitize()?; + Ok(Self::Legacy(LegacyMessage::new( + message, + reserved_account_keys, + ))) + } + /// Return true if this message contains duplicate account keys pub fn has_duplicates(&self) -> bool { match self { @@ -374,14 +383,18 @@ mod tests { use {super::*, crate::message::v0, std::collections::HashSet}; #[test] - fn test_try_from_message() { + fn test_try_from_legacy_message() { let legacy_message_with_no_signers = legacy::Message { account_keys: vec![Pubkey::new_unique()], ..legacy::Message::default() }; assert_eq!( - SanitizedMessage::try_from(legacy_message_with_no_signers).err(), + SanitizedMessage::try_from_legacy_message( + legacy_message_with_no_signers, + &HashSet::default(), + ) + .err(), Some(SanitizeMessageError::IndexOutOfBounds), ); } @@ -396,14 +409,17 @@ mod tests { CompiledInstruction::new(2, &(), vec![0, 1]), ]; - let message = SanitizedMessage::try_from(legacy::Message::new_with_compiled_instructions( - 1, - 0, - 2, - vec![key0, key1, loader_key], - Hash::default(), - instructions, - )) + let message = SanitizedMessage::try_from_legacy_message( + legacy::Message::new_with_compiled_instructions( + 1, + 0, + 2, + vec![key0, key1, loader_key], + Hash::default(), + instructions, + ), + &HashSet::default(), + ) .unwrap(); assert!(message.is_non_loader_key(0)); @@ -420,15 +436,18 @@ mod tests { let key4 = Pubkey::new_unique(); let key5 = Pubkey::new_unique(); - let legacy_message = SanitizedMessage::try_from(legacy::Message { - header: MessageHeader { - num_required_signatures: 2, - num_readonly_signed_accounts: 1, - num_readonly_unsigned_accounts: 1, + let legacy_message = SanitizedMessage::try_from_legacy_message( + legacy::Message { + header: MessageHeader { + num_required_signatures: 2, + 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], - ..legacy::Message::default() - }) + &HashSet::default(), + ) .unwrap(); assert_eq!(legacy_message.num_readonly_accounts(), 2); @@ -447,6 +466,7 @@ mod tests { writable: vec![key4], readonly: vec![key5], }, + &HashSet::default(), )); assert_eq!(v0_message.num_readonly_accounts(), 3); @@ -464,14 +484,17 @@ mod tests { CompiledInstruction::new(3, &(), vec![0, 0]), ]; - let message = SanitizedMessage::try_from(legacy::Message::new_with_compiled_instructions( - 2, - 1, - 2, - vec![signer0, signer1, non_signer, loader_key], - Hash::default(), - instructions, - )) + let message = SanitizedMessage::try_from_legacy_message( + legacy::Message::new_with_compiled_instructions( + 2, + 1, + 2, + vec![signer0, signer1, non_signer, loader_key], + Hash::default(), + instructions, + ), + &HashSet::default(), + ) .unwrap(); assert_eq!( @@ -502,15 +525,18 @@ mod tests { let key4 = Pubkey::new_unique(); let key5 = Pubkey::new_unique(); - let legacy_message = SanitizedMessage::try_from(legacy::Message { - header: MessageHeader { - num_required_signatures: 2, - num_readonly_signed_accounts: 1, - num_readonly_unsigned_accounts: 1, + let legacy_message = SanitizedMessage::try_from_legacy_message( + legacy::Message { + header: MessageHeader { + num_required_signatures: 2, + 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], - ..legacy::Message::default() - }) + &HashSet::default(), + ) .unwrap(); match legacy_message { SanitizedMessage::Legacy(message) => { @@ -542,6 +568,7 @@ mod tests { writable: vec![key4], readonly: vec![key5], }, + &HashSet::default(), )); match v0_message { SanitizedMessage::V0(message) => { diff --git a/sdk/program/src/message/versions/mod.rs b/sdk/program/src/message/versions/mod.rs index 301490a2aa7e7d..34fa027b9b8ad5 100644 --- a/sdk/program/src/message/versions/mod.rs +++ b/sdk/program/src/message/versions/mod.rs @@ -79,7 +79,7 @@ impl VersionedMessage { /// used in the runtime. pub fn is_maybe_writable(&self, index: usize) -> bool { match self { - Self::Legacy(message) => message.is_writable(index), + Self::Legacy(message) => message.is_maybe_writable(index), Self::V0(message) => message.is_maybe_writable(index), } } diff --git a/sdk/program/src/message/versions/v0/loaded.rs b/sdk/program/src/message/versions/v0/loaded.rs index c8edbff58b5522..1825c5e748e3f4 100644 --- a/sdk/program/src/message/versions/v0/loaded.rs +++ b/sdk/program/src/message/versions/v0/loaded.rs @@ -1,7 +1,7 @@ use { crate::{ bpf_loader_upgradeable, - message::{legacy::is_builtin_key_or_sysvar, v0, AccountKeys}, + message::{v0, AccountKeys}, pubkey::Pubkey, }, std::{borrow::Cow, collections::HashSet}, @@ -55,32 +55,40 @@ impl LoadedAddresses { } 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, + ) -> Self { let mut loaded_message = Self { message: Cow::Owned(message), loaded_addresses: Cow::Owned(loaded_addresses), 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 } - 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, + ) -> Self { let mut loaded_message = Self { message: Cow::Borrowed(message), loaded_addresses: Cow::Borrowed(loaded_addresses), 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 } - fn set_is_writable_account_cache(&mut self) { + fn set_is_writable_account_cache(&mut self, reserved_account_keys: &HashSet) { let is_writable_account_cache = self .account_keys() .iter() .enumerate() - .map(|(i, _key)| self.is_writable_internal(i)) + .map(|(i, _key)| self.is_writable_internal(i, reserved_account_keys)) .collect::>(); let _ = std::mem::replace( &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 - fn is_writable_internal(&self, key_index: usize) -> bool { + fn is_writable_internal( + &self, + key_index: usize, + reserved_account_keys: &HashSet, + ) -> bool { if self.is_writable_index(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 @@ -201,6 +213,7 @@ mod tests { writable: vec![key4], readonly: vec![key5], }, + &HashSet::default(), ); (message, [key0, key1, key2, key3, key4, key5]) @@ -225,6 +238,7 @@ mod tests { writable: keys.split_off(2), readonly: keys, }, + &HashSet::default(), ) }; @@ -257,6 +271,8 @@ mod tests { #[test] fn test_is_writable() { solana_logger::setup(); + + let reserved_account_keys = HashSet::from_iter([sysvar::clock::id(), system_program::id()]); let create_message_with_keys = |keys: Vec| { LoadedMessage::new( v0::Message { @@ -272,6 +288,7 @@ mod tests { writable: keys[2..=2].to_vec(), readonly: keys[3..].to_vec(), }, + &reserved_account_keys, ) }; @@ -321,6 +338,7 @@ mod tests { writable: vec![key1, key2], readonly: vec![], }, + &HashSet::default(), ); assert!(message.is_writable_index(2)); diff --git a/sdk/program/src/message/versions/v0/mod.rs b/sdk/program/src/message/versions/v0/mod.rs index df001bb19ce0bc..056d62407856ae 100644 --- a/sdk/program/src/message/versions/v0/mod.rs +++ b/sdk/program/src/message/versions/v0/mod.rs @@ -16,7 +16,6 @@ use crate::{ instruction::{CompiledInstruction, Instruction}, message::{ compiled_keys::{CompileError, CompiledKeys}, - legacy::is_builtin_key_or_sysvar, AccountKeys, MessageHeader, MESSAGE_VERSION_PREFIX, }, pubkey::Pubkey, @@ -334,18 +333,12 @@ impl Message { .any(|&key| key == bpf_loader_upgradeable::id()) } - /// Returns true if the account at the specified index was requested as writable. - /// Before loading addresses, we can't demote write locks for dynamically loaded - /// addresses so this should not be used by the runtime. + /// Returns true if the account at the specified index was requested as + /// writable. Before loading addresses and without the reserved account keys + /// set, we can't demote write locks properly so this should not be used by + /// the runtime. pub fn is_maybe_writable(&self, key_index: usize) -> bool { self.is_writable_index(key_index) - && !{ - // demote reserved ids - self.account_keys - .get(key_index) - .map(is_builtin_key_or_sysvar) - .unwrap_or_default() - } && !{ // demote program ids self.is_key_called_as_program(key_index) diff --git a/sdk/program/src/sysvar/instructions.rs b/sdk/program/src/sysvar/instructions.rs index a5a31735795832..4222f3627cdb42 100644 --- a/sdk/program/src/sysvar/instructions.rs +++ b/sdk/program/src/sysvar/instructions.rs @@ -302,7 +302,7 @@ mod tests { message::{Message as LegacyMessage, SanitizedMessage}, pubkey::Pubkey, }, - std::convert::TryFrom, + std::collections::HashSet, }; #[test] @@ -327,10 +327,13 @@ mod tests { &0, vec![AccountMeta::new(Pubkey::new_unique(), false)], ); - let sanitized_message = SanitizedMessage::try_from(LegacyMessage::new( - &[instruction0.clone(), instruction1.clone()], - Some(&Pubkey::new_unique()), - )) + let sanitized_message = SanitizedMessage::try_from_legacy_message( + LegacyMessage::new( + &[instruction0.clone(), instruction1.clone()], + Some(&Pubkey::new_unique()), + ), + &HashSet::default(), + ) .unwrap(); let key = id(); @@ -381,10 +384,10 @@ mod tests { &0, vec![AccountMeta::new(Pubkey::new_unique(), false)], ); - let sanitized_message = SanitizedMessage::try_from(LegacyMessage::new( - &[instruction0, instruction1], - Some(&Pubkey::new_unique()), - )) + let sanitized_message = SanitizedMessage::try_from_legacy_message( + LegacyMessage::new(&[instruction0, instruction1], Some(&Pubkey::new_unique())), + &HashSet::default(), + ) .unwrap(); let key = id(); @@ -435,14 +438,17 @@ mod tests { &0, vec![AccountMeta::new(Pubkey::new_unique(), false)], ); - let sanitized_message = SanitizedMessage::try_from(LegacyMessage::new( - &[ - instruction0.clone(), - instruction1.clone(), - instruction2.clone(), - ], - Some(&Pubkey::new_unique()), - )) + let sanitized_message = SanitizedMessage::try_from_legacy_message( + LegacyMessage::new( + &[ + instruction0.clone(), + instruction1.clone(), + instruction2.clone(), + ], + Some(&Pubkey::new_unique()), + ), + &HashSet::default(), + ) .unwrap(); let key = id(); @@ -538,7 +544,8 @@ mod tests { ]; let message = LegacyMessage::new(&instructions, Some(&id1)); - let sanitized_message = SanitizedMessage::try_from(message).unwrap(); + let sanitized_message = + SanitizedMessage::try_from_legacy_message(message, &HashSet::default()).unwrap(); let serialized = serialize_instructions(&sanitized_message.decompile_instructions()); // assert that deserialize_instruction is compatible with SanitizedMessage::serialize_instructions @@ -560,8 +567,11 @@ mod tests { Instruction::new_with_bincode(program_id0, &0, vec![AccountMeta::new(id1, true)]), ]; - let message = - SanitizedMessage::try_from(LegacyMessage::new(&instructions, Some(&id1))).unwrap(); + let message = SanitizedMessage::try_from_legacy_message( + LegacyMessage::new(&instructions, Some(&id1)), + &HashSet::default(), + ) + .unwrap(); let serialized = serialize_instructions(&message.decompile_instructions()); assert_eq!( deserialize_instruction(instructions.len(), &serialized).unwrap_err(), diff --git a/sdk/program/src/sysvar/mod.rs b/sdk/program/src/sysvar/mod.rs index 4aabbce336619a..2f9ec6390c2b98 100644 --- a/sdk/program/src/sysvar/mod.rs +++ b/sdk/program/src/sysvar/mod.rs @@ -118,6 +118,10 @@ lazy_static! { } /// Returns `true` of the given `Pubkey` is a sysvar account. +#[deprecated( + since = "1.18.0", + note = "please check the account's owner or use solana_sdk::reserved_account_keys::ReservedAccountKeys instead" +)] pub fn is_sysvar_id(id: &Pubkey) -> bool { ALL_IDS.iter().any(|key| key == id) } diff --git a/sdk/program/src/zk_token_proof_program.rs b/sdk/program/src/zk_token_proof_program.rs new file mode 100644 index 00000000000000..1730cbc04a6e6b --- /dev/null +++ b/sdk/program/src/zk_token_proof_program.rs @@ -0,0 +1,9 @@ +//! The [ZK Token Proof Program][np]. +//! +//! [np]: https://docs.solanalabs.com/runtime/zk-token-proof +//! +//! Documentation can be found in [`solana-zk-token-sdk`]. +//! +//! [`solana-zk-token-sdk`]: https://docs.rs/solana-zk-token-sdk/latest/solana_zk_token_sdk + +crate::declare_id!("ZkTokenProof1111111111111111111111111111111"); diff --git a/sdk/src/feature_set.rs b/sdk/src/feature_set.rs index 2941c94ae81cb3..3298d71a8ea0ce 100644 --- a/sdk/src/feature_set.rs +++ b/sdk/src/feature_set.rs @@ -744,6 +744,10 @@ pub mod allow_commission_decrease_at_any_time { solana_sdk::declare_id!("decoMktMcnmiq6t3u7g5BfgcQu91nKZr6RvMYf9z1Jb"); } +pub mod add_new_reserved_account_keys { + solana_sdk::declare_id!("8U4skmMVnF6k2kMvrWbQuRUT3qQSiTYpSjqmhmgfthZu"); +} + pub mod consume_blockstore_duplicate_proofs { solana_sdk::declare_id!("6YsBCejwK96GZCkJ6mkZ4b68oP63z2PLoQmWjC7ggTqZ"); } @@ -953,6 +957,7 @@ lazy_static! { (drop_legacy_shreds::id(), "drops legacy shreds #34328"), (allow_commission_decrease_at_any_time::id(), "Allow commission decrease at any time in epoch #33843"), (consume_blockstore_duplicate_proofs::id(), "consume duplicate proofs from blockstore in consensus #34372"), + (add_new_reserved_account_keys::id(), "add new unwritable reserved accounts #???"), (index_erasure_conflict_duplicate_proofs::id(), "generate duplicate proofs for index and erasure conflicts #34360"), (merkle_conflict_duplicate_proofs::id(), "generate duplicate proofs for merkle root conflicts #34270"), (disable_bpf_loader_instructions::id(), "disable bpf loader management instructions #34194"), diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs index 720d5198ab950c..19e80b8e90b8ca 100644 --- a/sdk/src/lib.rs +++ b/sdk/src/lib.rs @@ -55,7 +55,7 @@ pub use solana_program::{ program_pack, rent, sanitize, sdk_ids, secp256k1_program, secp256k1_recover, serde_varint, serialize_utils, short_vec, slot_hashes, slot_history, stable_layout, stake, stake_history, syscalls, system_instruction, system_program, sysvar, unchecked_div_by_const, vote, - wasm_bindgen, + wasm_bindgen, zk_token_proof_program, }; pub mod account; @@ -91,6 +91,7 @@ pub mod program_utils; pub mod pubkey; pub mod quic; pub mod recent_blockhashes_account; +pub mod reserved_account_keys; pub mod reward_type; pub mod rpc_port; pub mod secp256k1_instruction; diff --git a/sdk/src/reserved_account_keys.rs b/sdk/src/reserved_account_keys.rs new file mode 100644 index 00000000000000..4ac5e7757308ef --- /dev/null +++ b/sdk/src/reserved_account_keys.rs @@ -0,0 +1,171 @@ +//! Collection of reserved account keys that cannot be write-locked by transactions. +//! New reserved account keys may be added as long as they specify a feature +//! gate that transitions the key into read-only at an epoch boundary. + +use { + crate::{ + address_lookup_table, bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable, + compute_budget, config, ed25519_program, feature, + feature_set::{self, FeatureSet}, + loader_v4, native_loader, + pubkey::Pubkey, + secp256k1_program, stake, system_program, sysvar, vote, zk_token_proof_program, + }, + lazy_static::lazy_static, + std::collections::HashSet, +}; + +pub struct ReservedAccountKeys; +impl ReservedAccountKeys { + /// Compute a set of all reserved keys, regardless of if they are active or not + pub fn active_and_inactive() -> HashSet { + RESERVED_ACCOUNT_KEYS + .iter() + .map(|reserved_key| reserved_key.key) + .collect() + } + + /// Compute the active set of reserved keys based on activated features + pub fn active(feature_set: &FeatureSet) -> HashSet { + Self::compute_active(&RESERVED_ACCOUNT_KEYS, feature_set) + } + + // Method only exists for unit testing + fn compute_active( + account_keys: &[ReservedAccountKey], + feature_set: &FeatureSet, + ) -> HashSet { + account_keys + .iter() + .filter(|reserved_key| reserved_key.is_active(feature_set)) + .map(|reserved_key| reserved_key.key) + .collect() + } + + /// Return an empty list of reserved keys for visibility when using in + /// places where the dynamic reserved key set is not available + pub fn empty() -> HashSet { + HashSet::default() + } +} + +/// `ReservedAccountKey` represents a reserved account key that will not be +/// write-lockable by transactions. If a feature id is set, the account key will +/// become read-only only after the feature has been activated. +#[derive(Debug, Clone, Eq, PartialEq)] +struct ReservedAccountKey { + key: Pubkey, + feature_id: Option, +} + +impl ReservedAccountKey { + fn new(key: Pubkey, feature_id: Option) -> Self { + Self { key, feature_id } + } + + /// Returns true if no feature id is set or if the feature id is activated + /// in the feature set. + fn is_active(&self, feature_set: &FeatureSet) -> bool { + self.feature_id + .map_or(true, |feature_id| feature_set.is_active(&feature_id)) + } +} + +// New reserved account keys should be added in alphabetical order and must +// specify a feature id for activation. +lazy_static! { + static ref RESERVED_ACCOUNT_KEYS: Vec = [ + // builtin programs + ReservedAccountKey::new(address_lookup_table::program::id(), Some(feature_set::add_new_reserved_account_keys::id())), + ReservedAccountKey::new(bpf_loader::id(), None), + ReservedAccountKey::new(bpf_loader_deprecated::id(), None), + ReservedAccountKey::new(bpf_loader_upgradeable::id(), None), + ReservedAccountKey::new(compute_budget::id(), Some(feature_set::add_new_reserved_account_keys::id())), + ReservedAccountKey::new(config::program::id(), None), + ReservedAccountKey::new(ed25519_program::id(), Some(feature_set::add_new_reserved_account_keys::id())), + ReservedAccountKey::new(feature::id(), None), + ReservedAccountKey::new(loader_v4::id(), Some(feature_set::add_new_reserved_account_keys::id())), + ReservedAccountKey::new(secp256k1_program::id(), Some(feature_set::add_new_reserved_account_keys::id())), + #[allow(deprecated)] + ReservedAccountKey::new(stake::config::id(), None), + ReservedAccountKey::new(stake::program::id(), None), + ReservedAccountKey::new(system_program::id(), None), + ReservedAccountKey::new(vote::program::id(), None), + ReservedAccountKey::new(zk_token_proof_program::id(), Some(feature_set::add_new_reserved_account_keys::id())), + + // sysvars + ReservedAccountKey::new(sysvar::clock::id(), None), + ReservedAccountKey::new(sysvar::epoch_rewards::id(), Some(feature_set::add_new_reserved_account_keys::id())), + ReservedAccountKey::new(sysvar::epoch_schedule::id(), None), + #[allow(deprecated)] + ReservedAccountKey::new(sysvar::fees::id(), None), + ReservedAccountKey::new(sysvar::instructions::id(), None), + ReservedAccountKey::new(sysvar::last_restart_slot::id(), Some(feature_set::add_new_reserved_account_keys::id())), + #[allow(deprecated)] + ReservedAccountKey::new(sysvar::recent_blockhashes::id(), None), + ReservedAccountKey::new(sysvar::rent::id(), None), + ReservedAccountKey::new(sysvar::rewards::id(), None), + ReservedAccountKey::new(sysvar::slot_hashes::id(), None), + ReservedAccountKey::new(sysvar::slot_history::id(), None), + ReservedAccountKey::new(sysvar::stake_history::id(), None), + + // other + ReservedAccountKey::new(native_loader::id(), None), + ReservedAccountKey::new(sysvar::id(), Some(feature_set::add_new_reserved_account_keys::id())), + ].to_vec(); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_reserved_account_key_is_active() { + let feature_id = Pubkey::new_unique(); + let old_reserved_account_key = ReservedAccountKey::new(Pubkey::new_unique(), None); + let new_reserved_account_key = + ReservedAccountKey::new(Pubkey::new_unique(), Some(feature_id)); + + let mut feature_set = FeatureSet::default(); + assert!( + old_reserved_account_key.is_active(&feature_set), + "if not feature id is set, the key should be active" + ); + assert!( + !new_reserved_account_key.is_active(&feature_set), + "if feature id is set but not in the feature set, the key should not be active" + ); + + feature_set.active.insert(feature_id, 0); + assert!( + new_reserved_account_key.is_active(&feature_set), + "if feature id is set and is in the feature set, the key should be active" + ); + } + + #[test] + fn test_reserved_account_keys_compute_active() { + let feature_id = Pubkey::new_unique(); + let key0 = Pubkey::new_unique(); + let key1 = Pubkey::new_unique(); + let test_account_keys = vec![ + ReservedAccountKey::new(key0, None), + ReservedAccountKey::new(key1, Some(feature_id)), + ReservedAccountKey::new(Pubkey::new_unique(), Some(Pubkey::new_unique())), + ]; + + let mut feature_set = FeatureSet::default(); + assert_eq!( + HashSet::from_iter([key0].into_iter()), + ReservedAccountKeys::compute_active(&test_account_keys, &feature_set), + "should only contain keys without feature ids" + ); + + feature_set.active.insert(feature_id, 0); + assert_eq!( + HashSet::from_iter([key0, key1].into_iter()), + ReservedAccountKeys::compute_active(&test_account_keys, &feature_set), + "should only contain keys without feature ids or with active feature ids" + ); + } +} diff --git a/sdk/src/transaction/mod.rs b/sdk/src/transaction/mod.rs index 4173a93b62215e..9e9f124e8e44ac 100644 --- a/sdk/src/transaction/mod.rs +++ b/sdk/src/transaction/mod.rs @@ -1074,7 +1074,7 @@ impl Transaction { } } -pub fn uses_durable_nonce(tx: &Transaction) -> Option<&CompiledInstruction> { +pub fn maybe_uses_durable_nonce(tx: &Transaction) -> Option<&CompiledInstruction> { let message = tx.message(); message .instructions @@ -1093,7 +1093,7 @@ pub fn uses_durable_nonce(tx: &Transaction) -> Option<&CompiledInstruction> { // Nonce account is writable && matches!( instruction.accounts.first(), - Some(index) if message.is_writable(*index as usize) + Some(index) if message.is_maybe_writable(*index as usize) ) }) } @@ -1553,19 +1553,19 @@ mod tests { #[test] fn tx_uses_nonce_ok() { let (_, _, tx) = nonced_transfer_tx(); - assert!(uses_durable_nonce(&tx).is_some()); + assert!(maybe_uses_durable_nonce(&tx).is_some()); } #[test] fn tx_uses_nonce_empty_ix_fail() { - assert!(uses_durable_nonce(&Transaction::default()).is_none()); + assert!(maybe_uses_durable_nonce(&Transaction::default()).is_none()); } #[test] fn tx_uses_nonce_bad_prog_id_idx_fail() { let (_, _, mut tx) = nonced_transfer_tx(); tx.message.instructions.get_mut(0).unwrap().program_id_index = 255u8; - assert!(uses_durable_nonce(&tx).is_none()); + assert!(maybe_uses_durable_nonce(&tx).is_none()); } #[test] @@ -1580,7 +1580,7 @@ mod tests { ]; let message = Message::new(&instructions, Some(&from_pubkey)); let tx = Transaction::new(&[&from_keypair, &nonce_keypair], message, Hash::default()); - assert!(uses_durable_nonce(&tx).is_none()); + assert!(maybe_uses_durable_nonce(&tx).is_none()); } #[test] @@ -1606,7 +1606,7 @@ mod tests { &[&from_keypair, &nonce_keypair], Hash::default(), ); - assert!(uses_durable_nonce(&tx).is_none()); + assert!(maybe_uses_durable_nonce(&tx).is_none()); } #[test] @@ -1626,13 +1626,13 @@ mod tests { ]; let message = Message::new(&instructions, Some(&nonce_pubkey)); let tx = Transaction::new(&[&from_keypair, &nonce_keypair], message, Hash::default()); - assert!(uses_durable_nonce(&tx).is_none()); + assert!(maybe_uses_durable_nonce(&tx).is_none()); } #[test] fn get_nonce_pub_from_ix_ok() { let (_, nonce_pubkey, tx) = nonced_transfer_tx(); - let nonce_ix = uses_durable_nonce(&tx).unwrap(); + let nonce_ix = maybe_uses_durable_nonce(&tx).unwrap(); assert_eq!( get_nonce_pubkey_from_instruction(nonce_ix, &tx), Some(&nonce_pubkey), @@ -1642,7 +1642,7 @@ mod tests { #[test] fn get_nonce_pub_from_ix_no_accounts_fail() { let (_, _, tx) = nonced_transfer_tx(); - let nonce_ix = uses_durable_nonce(&tx).unwrap(); + let nonce_ix = maybe_uses_durable_nonce(&tx).unwrap(); let mut nonce_ix = nonce_ix.clone(); nonce_ix.accounts.clear(); assert_eq!(get_nonce_pubkey_from_instruction(&nonce_ix, &tx), None,); @@ -1651,7 +1651,7 @@ mod tests { #[test] fn get_nonce_pub_from_ix_bad_acc_idx_fail() { let (_, _, tx) = nonced_transfer_tx(); - let nonce_ix = uses_durable_nonce(&tx).unwrap(); + let nonce_ix = maybe_uses_durable_nonce(&tx).unwrap(); let mut nonce_ix = nonce_ix.clone(); nonce_ix.accounts[0] = 255u8; assert_eq!(get_nonce_pubkey_from_instruction(&nonce_ix, &tx), None,); diff --git a/sdk/src/transaction/sanitized.rs b/sdk/src/transaction/sanitized.rs index 4189f1b64b86e2..21b60b7f9009f1 100644 --- a/sdk/src/transaction/sanitized.rs +++ b/sdk/src/transaction/sanitized.rs @@ -12,6 +12,7 @@ use { }, precompiles::verify_if_precompile, pubkey::Pubkey, + reserved_account_keys::ReservedAccountKeys, sanitize::Sanitize, signature::Signature, simple_vote_transaction_checker::is_simple_vote_transaction, @@ -19,6 +20,7 @@ use { transaction::{Result, Transaction, TransactionError, VersionedTransaction}, }, solana_program::message::SanitizedVersionedMessage, + std::collections::HashSet, }; /// Maximum number of accounts that a transaction may lock. @@ -66,17 +68,22 @@ impl SanitizedTransaction { message_hash: Hash, is_simple_vote_tx: bool, address_loader: impl AddressLoader, + reserved_account_keys: &HashSet, ) -> Result { let signatures = tx.signatures; let SanitizedVersionedMessage { message } = tx.message; let message = match message { VersionedMessage::Legacy(message) => { - SanitizedMessage::Legacy(LegacyMessage::new(message)) + SanitizedMessage::Legacy(LegacyMessage::new(message, reserved_account_keys)) } VersionedMessage::V0(message) => { let loaded_addresses = 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, is_simple_vote_tx: Option, address_loader: impl AddressLoader, + reserved_account_keys: &HashSet, ) -> Result { let sanitized_versioned_tx = SanitizedVersionedTransaction::try_from(tx)?; let is_simple_vote_tx = is_simple_vote_tx @@ -109,15 +117,23 @@ impl SanitizedTransaction { message_hash, is_simple_vote_tx, address_loader, + reserved_account_keys, ) } - pub fn try_from_legacy_transaction(tx: Transaction) -> Result { + /// Create a sanitized transaction from a legacy transaction + pub fn try_from_legacy_transaction( + tx: Transaction, + reserved_account_keys: &HashSet, + ) -> Result { tx.sanitize()?; Ok(Self { 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, signatures: tx.signatures, }) @@ -125,7 +141,7 @@ impl SanitizedTransaction { /// Create a sanitized transaction from a legacy transaction. Used for tests only. pub fn from_transaction_for_tests(tx: Transaction) -> Self { - Self::try_from_legacy_transaction(tx).unwrap() + Self::try_from_legacy_transaction(tx, &ReservedAccountKeys::empty()).unwrap() } /// Return the first signature for this transaction. @@ -278,7 +294,10 @@ impl SanitizedTransaction { mod tests { use { super::*, - crate::signer::{keypair::Keypair, Signer}, + crate::{ + reserved_account_keys::ReservedAccountKeys, + signer::{keypair::Keypair, Signer}, + }, solana_program::vote::{self, state::Vote}, }; @@ -303,6 +322,7 @@ mod tests { MessageHash::Compute, None, SimpleAddressLoader::Disabled, + &ReservedAccountKeys::empty(), ) .unwrap(); assert!(vote_transaction.is_simple_vote_transaction()); @@ -315,6 +335,7 @@ mod tests { MessageHash::Compute, Some(false), SimpleAddressLoader::Disabled, + &ReservedAccountKeys::empty(), ) .unwrap(); assert!(!vote_transaction.is_simple_vote_transaction()); @@ -329,6 +350,7 @@ mod tests { MessageHash::Compute, None, SimpleAddressLoader::Disabled, + &ReservedAccountKeys::empty(), ) .unwrap(); assert!(!vote_transaction.is_simple_vote_transaction()); @@ -341,6 +363,7 @@ mod tests { MessageHash::Compute, Some(true), SimpleAddressLoader::Disabled, + &ReservedAccountKeys::empty(), ) .unwrap(); assert!(vote_transaction.is_simple_vote_transaction()); diff --git a/sdk/src/transaction/versioned/mod.rs b/sdk/src/transaction/versioned/mod.rs index 9faecf2dceb7eb..28e03e7804fbcf 100644 --- a/sdk/src/transaction/versioned/mod.rs +++ b/sdk/src/transaction/versioned/mod.rs @@ -189,7 +189,7 @@ impl VersionedTransaction { /// instruction. Since dynamically loaded addresses can't have write locks /// demoted without loading addresses, this shouldn't be used in the /// runtime. - pub fn uses_durable_nonce(&self) -> bool { + pub fn maybe_uses_durable_nonce(&self) -> bool { let message = &self.message; message .instructions() @@ -291,12 +291,12 @@ mod tests { #[test] fn tx_uses_nonce_ok() { let (_, _, tx) = nonced_transfer_tx(); - assert!(tx.uses_durable_nonce()); + assert!(tx.maybe_uses_durable_nonce()); } #[test] fn tx_uses_nonce_empty_ix_fail() { - assert!(!VersionedTransaction::default().uses_durable_nonce()); + assert!(!VersionedTransaction::default().maybe_uses_durable_nonce()); } #[test] @@ -308,7 +308,7 @@ mod tests { } VersionedMessage::V0(_) => unreachable!(), }; - assert!(!tx.uses_durable_nonce()); + assert!(!tx.maybe_uses_durable_nonce()); } #[test] @@ -324,7 +324,7 @@ mod tests { let message = LegacyMessage::new(&instructions, Some(&from_pubkey)); let tx = Transaction::new(&[&from_keypair, &nonce_keypair], message, Hash::default()); let tx = VersionedTransaction::from(tx); - assert!(!tx.uses_durable_nonce()); + assert!(!tx.maybe_uses_durable_nonce()); } #[test] @@ -351,7 +351,7 @@ mod tests { Hash::default(), ); let tx = VersionedTransaction::from(tx); - assert!(!tx.uses_durable_nonce()); + assert!(!tx.maybe_uses_durable_nonce()); } #[test] @@ -372,6 +372,6 @@ mod tests { let message = LegacyMessage::new(&instructions, Some(&nonce_pubkey)); let tx = Transaction::new(&[&from_keypair, &nonce_keypair], message, Hash::default()); let tx = VersionedTransaction::from(tx); - assert!(!tx.uses_durable_nonce()); + assert!(!tx.maybe_uses_durable_nonce()); } } diff --git a/storage-bigtable/src/lib.rs b/storage-bigtable/src/lib.rs index 85c714c635b5b4..e9c0f8c80217bc 100644 --- a/storage-bigtable/src/lib.rs +++ b/storage-bigtable/src/lib.rs @@ -10,8 +10,8 @@ use { deserialize_utils::default_on_eof, message::v0::LoadedAddresses, pubkey::Pubkey, + reserved_account_keys::ReservedAccountKeys, signature::Signature, - sysvar::is_sysvar_id, timing::AtomicInterval, transaction::{TransactionError, VersionedTransaction}, }, @@ -938,6 +938,7 @@ impl LedgerStorage { entries, } = confirmed_block; + let reserved_account_keys = ReservedAccountKeys::active_and_inactive(); let mut tx_cells = Vec::with_capacity(confirmed_block.transactions.len()); for (index, transaction_with_meta) in confirmed_block.transactions.iter().enumerate() { let VersionedTransactionWithStatusMeta { meta, transaction } = transaction_with_meta; @@ -947,7 +948,7 @@ impl LedgerStorage { let memo = extract_and_fmt_memos(transaction_with_meta); for address in transaction_with_meta.account_keys().iter() { - if !is_sysvar_id(address) { + if !reserved_account_keys.contains(address) { by_addr .entry(address) .or_default() @@ -1083,9 +1084,9 @@ impl LedgerStorage { let err = None; for address in transaction.message.account_keys.iter() { - if !is_sysvar_id(address) { - addresses.insert(address); - } + // Some of these addresses might be reserved keys but it's + // ok if we attempt to delete a row that doesn't exist + addresses.insert(address); } expected_tx_infos.insert( @@ -1100,9 +1101,9 @@ impl LedgerStorage { let err = meta.status.clone().err(); for address in tx_with_meta.account_keys().iter() { - if !is_sysvar_id(address) { - addresses.insert(address); - } + // Some of these addresses might be reserved keys but it's + // ok if we attempt to delete a row that doesn't exist + addresses.insert(address); } expected_tx_infos.insert( diff --git a/transaction-status/src/lib.rs b/transaction-status/src/lib.rs index 0eb13d36819c4a..3c5658725fdb3e 100644 --- a/transaction-status/src/lib.rs +++ b/transaction-status/src/lib.rs @@ -19,6 +19,7 @@ use { AccountKeys, Message, MessageHeader, VersionedMessage, }, pubkey::Pubkey, + reserved_account_keys::ReservedAccountKeys, signature::Signature, transaction::{ Result as TransactionResult, Transaction, TransactionError, TransactionVersion, @@ -980,12 +981,16 @@ impl VersionedTransactionWithStatusMeta { show_rewards: bool, ) -> Result { let version = self.validate_version(max_supported_transaction_version)?; + let reserved_account_keys = ReservedAccountKeys::active_and_inactive(); let account_keys = match &self.transaction.message { VersionedMessage::Legacy(message) => parse_legacy_message_accounts(message), VersionedMessage::V0(message) => { - let loaded_message = - LoadedMessage::new_borrowed(message, &self.meta.loaded_addresses); + let loaded_message = LoadedMessage::new_borrowed( + message, + &self.meta.loaded_addresses, + &reserved_account_keys, + ); parse_v0_message_accounts(&loaded_message) } }; @@ -1228,8 +1233,10 @@ impl EncodableWithMeta for v0::Message { meta: &TransactionStatusMeta, ) -> Self::Encoded { if encoding == UiTransactionEncoding::JsonParsed { + let reserved_account_keys = ReservedAccountKeys::active_and_inactive(); 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); UiMessage::Parsed(UiParsedMessage { account_keys: parse_v0_message_accounts(&loaded_message), recent_blockhash: self.recent_blockhash.to_string(), diff --git a/transaction-status/src/parse_accounts.rs b/transaction-status/src/parse_accounts.rs index 6ad0ec82a6fdad..0bc78c158f58e7 100644 --- a/transaction-status/src/parse_accounts.rs +++ b/transaction-status/src/parse_accounts.rs @@ -21,7 +21,7 @@ pub fn parse_legacy_message_accounts(message: &Message) -> Vec { for (i, account_key) in message.account_keys.iter().enumerate() { accounts.push(ParsedAccount { pubkey: account_key.to_string(), - writable: message.is_writable(i), + writable: message.is_maybe_writable(i), signer: message.is_signer(i), source: Some(ParsedAccountSource::Transaction), }); @@ -54,6 +54,7 @@ mod test { solana_sdk::{ message::{v0, v0::LoadedAddresses, MessageHeader}, pubkey::Pubkey, + reserved_account_keys::ReservedAccountKeys, }, }; @@ -126,6 +127,7 @@ mod test { writable: vec![pubkey4], readonly: vec![pubkey5], }, + &ReservedAccountKeys::empty(), ); assert_eq!(