1use {
2 super::{Bank, BankStatusCache},
3 agave_feature_set::FeatureSet,
4 solana_account::{state_traits::StateMut, AccountSharedData},
5 solana_accounts_db::blockhash_queue::BlockhashQueue,
6 solana_clock::{
7 MAX_PROCESSING_AGE, MAX_TRANSACTION_FORWARDING_DELAY, MAX_TRANSACTION_FORWARDING_DELAY_GPU,
8 },
9 solana_fee::{calculate_fee_details, FeeFeatures},
10 solana_fee_structure::{FeeBudgetLimits, FeeDetails},
11 solana_nonce::{
12 state::{Data as NonceData, DurableNonce, State as NonceState},
13 versions::Versions as NonceVersions,
14 NONCED_TX_MARKER_IX_INDEX,
15 },
16 solana_nonce_account as nonce_account,
17 solana_perf::perf_libs,
18 solana_program_runtime::execution_budget::SVMTransactionExecutionAndFeeBudgetLimits,
19 solana_pubkey::Pubkey,
20 solana_runtime_transaction::transaction_with_meta::TransactionWithMeta,
21 solana_svm::{
22 account_loader::{CheckedTransactionDetails, TransactionCheckResult},
23 nonce_info::NonceInfo,
24 transaction_error_metrics::TransactionErrorMetrics,
25 },
26 solana_svm_transaction::svm_message::SVMMessage,
27 solana_transaction_error::{TransactionError, TransactionResult},
28};
29
30impl Bank {
31 pub fn check_transactions_with_forwarding_delay(
33 &self,
34 transactions: &[impl TransactionWithMeta],
35 filter: &[TransactionResult<()>],
36 forward_transactions_to_leader_at_slot_offset: u64,
37 ) -> Vec<TransactionCheckResult> {
38 let mut error_counters = TransactionErrorMetrics::default();
39 let api = perf_libs::api();
45 let max_tx_fwd_delay = if api.is_none() {
46 MAX_TRANSACTION_FORWARDING_DELAY
47 } else {
48 MAX_TRANSACTION_FORWARDING_DELAY_GPU
49 };
50
51 self.check_transactions(
52 transactions,
53 filter,
54 (MAX_PROCESSING_AGE)
55 .saturating_sub(max_tx_fwd_delay)
56 .saturating_sub(forward_transactions_to_leader_at_slot_offset as usize),
57 &mut error_counters,
58 )
59 }
60
61 pub fn check_transactions<Tx: TransactionWithMeta>(
62 &self,
63 sanitized_txs: &[impl core::borrow::Borrow<Tx>],
64 lock_results: &[TransactionResult<()>],
65 max_age: usize,
66 error_counters: &mut TransactionErrorMetrics,
67 ) -> Vec<TransactionCheckResult> {
68 let lock_results = self.check_age_and_compute_budget_limits(
69 sanitized_txs,
70 lock_results,
71 max_age,
72 error_counters,
73 );
74 self.check_status_cache(sanitized_txs, lock_results, error_counters)
75 }
76
77 fn check_age_and_compute_budget_limits<Tx: TransactionWithMeta>(
78 &self,
79 sanitized_txs: &[impl core::borrow::Borrow<Tx>],
80 lock_results: &[TransactionResult<()>],
81 max_age: usize,
82 error_counters: &mut TransactionErrorMetrics,
83 ) -> Vec<TransactionCheckResult> {
84 let hash_queue = self.blockhash_queue.read().unwrap();
85 let last_blockhash = hash_queue.last_hash();
86 let next_durable_nonce = DurableNonce::from_blockhash(&last_blockhash);
87 let next_lamports_per_signature = hash_queue
89 .get_lamports_per_signature(&last_blockhash)
90 .unwrap();
91
92 let feature_set: &FeatureSet = &self.feature_set;
93 let fee_features = FeeFeatures::from(feature_set);
94
95 sanitized_txs
96 .iter()
97 .zip(lock_results)
98 .map(|(tx, lock_res)| match lock_res {
99 Ok(()) => {
100 let compute_budget_and_limits = tx
101 .borrow()
102 .compute_budget_instruction_details()
103 .sanitize_and_convert_to_compute_budget_limits(feature_set)
104 .map(|limit| {
105 let fee_budget = FeeBudgetLimits::from(limit);
106 let fee_details = calculate_fee_details(
107 tx.borrow(),
108 false,
109 self.fee_structure.lamports_per_signature,
110 fee_budget.prioritization_fee,
111 fee_features,
112 );
113 if let Some(compute_budget) = self.compute_budget {
114 compute_budget.get_compute_budget_and_limits(
118 fee_budget.loaded_accounts_data_size_limit,
119 fee_details,
120 )
121 } else {
122 limit.get_compute_budget_and_limits(
123 fee_budget.loaded_accounts_data_size_limit,
124 fee_details,
125 )
126 }
127 });
128 self.check_transaction_age(
129 tx.borrow(),
130 max_age,
131 &next_durable_nonce,
132 &hash_queue,
133 next_lamports_per_signature,
134 error_counters,
135 compute_budget_and_limits,
136 )
137 }
138 Err(e) => Err(e.clone()),
139 })
140 .collect()
141 }
142
143 fn checked_transactions_details_with_test_override(
144 nonce: Option<NonceInfo>,
145 lamports_per_signature: u64,
146 compute_budget_and_limits: Result<
147 SVMTransactionExecutionAndFeeBudgetLimits,
148 TransactionError,
149 >,
150 ) -> CheckedTransactionDetails {
151 let compute_budget_and_limits = if lamports_per_signature == 0 {
152 compute_budget_and_limits.map(|v| SVMTransactionExecutionAndFeeBudgetLimits {
155 budget: v.budget,
156 loaded_accounts_data_size_limit: v.loaded_accounts_data_size_limit,
157 fee_details: FeeDetails::default(),
158 })
159 } else {
160 compute_budget_and_limits
161 };
162 CheckedTransactionDetails::new(nonce, compute_budget_and_limits)
163 }
164
165 fn check_transaction_age(
166 &self,
167 tx: &impl SVMMessage,
168 max_age: usize,
169 next_durable_nonce: &DurableNonce,
170 hash_queue: &BlockhashQueue,
171 next_lamports_per_signature: u64,
172 error_counters: &mut TransactionErrorMetrics,
173 compute_budget: Result<SVMTransactionExecutionAndFeeBudgetLimits, TransactionError>,
174 ) -> TransactionCheckResult {
175 let recent_blockhash = tx.recent_blockhash();
176 if let Some(hash_info) = hash_queue.get_hash_info_if_valid(recent_blockhash, max_age) {
177 Ok(Self::checked_transactions_details_with_test_override(
178 None,
179 hash_info.lamports_per_signature(),
180 compute_budget,
181 ))
182 } else if let Some((nonce, previous_lamports_per_signature)) = self
183 .check_load_and_advance_message_nonce_account(
184 tx,
185 next_durable_nonce,
186 next_lamports_per_signature,
187 )
188 {
189 Ok(Self::checked_transactions_details_with_test_override(
190 Some(nonce),
191 previous_lamports_per_signature,
192 compute_budget,
193 ))
194 } else {
195 error_counters.blockhash_not_found += 1;
196 Err(TransactionError::BlockhashNotFound)
197 }
198 }
199
200 pub(super) fn check_load_and_advance_message_nonce_account(
201 &self,
202 message: &impl SVMMessage,
203 next_durable_nonce: &DurableNonce,
204 next_lamports_per_signature: u64,
205 ) -> Option<(NonceInfo, u64)> {
206 let nonce_is_advanceable = message.recent_blockhash() != next_durable_nonce.as_hash();
207 if !nonce_is_advanceable {
208 return None;
209 }
210
211 let (nonce_address, mut nonce_account, nonce_data) =
212 self.load_message_nonce_account(message)?;
213
214 let previous_lamports_per_signature = nonce_data.get_lamports_per_signature();
215 let next_nonce_state = NonceState::new_initialized(
216 &nonce_data.authority,
217 *next_durable_nonce,
218 next_lamports_per_signature,
219 );
220 nonce_account
221 .set_state(&NonceVersions::new(next_nonce_state))
222 .ok()?;
223
224 Some((
225 NonceInfo::new(nonce_address, nonce_account),
226 previous_lamports_per_signature,
227 ))
228 }
229
230 pub(super) fn load_message_nonce_account(
231 &self,
232 message: &impl SVMMessage,
233 ) -> Option<(Pubkey, AccountSharedData, NonceData)> {
234 let require_static_nonce_account = self
235 .feature_set
236 .is_active(&agave_feature_set::require_static_nonce_account::id());
237 let nonce_address = message.get_durable_nonce(require_static_nonce_account)?;
238 let nonce_account = self.get_account_with_fixed_root(nonce_address)?;
239 let nonce_data =
240 nonce_account::verify_nonce_account(&nonce_account, message.recent_blockhash())?;
241
242 let nonce_is_authorized = message
243 .get_ix_signers(NONCED_TX_MARKER_IX_INDEX as usize)
244 .any(|signer| signer == &nonce_data.authority);
245 if !nonce_is_authorized {
246 return None;
247 }
248
249 Some((*nonce_address, nonce_account, nonce_data))
250 }
251
252 fn check_status_cache<Tx: TransactionWithMeta>(
253 &self,
254 sanitized_txs: &[impl core::borrow::Borrow<Tx>],
255 lock_results: Vec<TransactionCheckResult>,
256 error_counters: &mut TransactionErrorMetrics,
257 ) -> Vec<TransactionCheckResult> {
258 let mut check_results = Vec::with_capacity(sanitized_txs.len());
260 let rcache = self.status_cache.read().unwrap();
261
262 check_results.extend(sanitized_txs.iter().zip(lock_results).map(
263 |(sanitized_tx, lock_result)| {
264 let sanitized_tx = sanitized_tx.borrow();
265 if lock_result.is_ok()
266 && self.is_transaction_already_processed(sanitized_tx, &rcache)
267 {
268 error_counters.already_processed += 1;
269 return Err(TransactionError::AlreadyProcessed);
270 }
271
272 lock_result
273 },
274 ));
275 check_results
276 }
277
278 fn is_transaction_already_processed(
279 &self,
280 sanitized_tx: &impl TransactionWithMeta,
281 status_cache: &BankStatusCache,
282 ) -> bool {
283 let key = sanitized_tx.message_hash();
284 let transaction_blockhash = sanitized_tx.recent_blockhash();
285 status_cache
286 .get_status(key, transaction_blockhash, &self.ancestors)
287 .is_some()
288 }
289}
290
291#[cfg(test)]
292mod tests {
293 use {
294 super::*,
295 crate::bank::tests::{
296 get_nonce_blockhash, get_nonce_data_from_account, new_sanitized_message,
297 setup_nonce_with_bank,
298 },
299 solana_hash::Hash,
300 solana_keypair::Keypair,
301 solana_message::{
302 compiled_instruction::CompiledInstruction,
303 v0::{self, LoadedAddresses, MessageAddressTableLookup},
304 Message, MessageHeader, SanitizedMessage, SanitizedVersionedMessage,
305 SimpleAddressLoader, VersionedMessage,
306 },
307 solana_signer::Signer,
308 solana_system_interface::{
309 instruction::{self as system_instruction, SystemInstruction},
310 program as system_program,
311 },
312 std::collections::HashSet,
313 test_case::test_case,
314 };
315
316 #[test]
317 fn test_check_and_load_message_nonce_account_ok() {
318 const STALE_LAMPORTS_PER_SIGNATURE: u64 = 42;
319 let (bank, _mint_keypair, custodian_keypair, nonce_keypair, _) = setup_nonce_with_bank(
320 10_000_000,
321 |_| {},
322 5_000_000,
323 250_000,
324 None,
325 FeatureSet::all_enabled(),
326 )
327 .unwrap();
328 let custodian_pubkey = custodian_keypair.pubkey();
329 let nonce_pubkey = nonce_keypair.pubkey();
330
331 let nonce_hash = get_nonce_blockhash(&bank, &nonce_pubkey).unwrap();
332 let message = new_sanitized_message(Message::new_with_blockhash(
333 &[
334 system_instruction::advance_nonce_account(&nonce_pubkey, &nonce_pubkey),
335 system_instruction::transfer(&custodian_pubkey, &nonce_pubkey, 100_000),
336 ],
337 Some(&custodian_pubkey),
338 &nonce_hash,
339 ));
340
341 let mut nonce_account = bank.get_account(&nonce_pubkey).unwrap();
343 let nonce_data = get_nonce_data_from_account(&nonce_account).unwrap();
344 nonce_account
345 .set_state(&NonceVersions::new(NonceState::new_initialized(
346 &nonce_data.authority,
347 nonce_data.durable_nonce,
348 STALE_LAMPORTS_PER_SIGNATURE,
349 )))
350 .unwrap();
351 bank.store_account(&nonce_pubkey, &nonce_account);
352
353 let nonce_account = bank.get_account(&nonce_pubkey).unwrap();
354 let (_, next_lamports_per_signature) = bank.last_blockhash_and_lamports_per_signature();
355 let mut expected_nonce_info = NonceInfo::new(nonce_pubkey, nonce_account);
356 expected_nonce_info
357 .try_advance_nonce(bank.next_durable_nonce(), next_lamports_per_signature)
358 .unwrap();
359
360 assert_eq!(
365 bank.check_load_and_advance_message_nonce_account(
366 &message,
367 &bank.next_durable_nonce(),
368 next_lamports_per_signature
369 ),
370 Some((expected_nonce_info, STALE_LAMPORTS_PER_SIGNATURE)),
371 );
372 }
373
374 #[test]
375 fn test_check_and_load_message_nonce_account_not_nonce_fail() {
376 let (bank, _mint_keypair, custodian_keypair, nonce_keypair, _) = setup_nonce_with_bank(
377 10_000_000,
378 |_| {},
379 5_000_000,
380 250_000,
381 None,
382 FeatureSet::all_enabled(),
383 )
384 .unwrap();
385 let custodian_pubkey = custodian_keypair.pubkey();
386 let nonce_pubkey = nonce_keypair.pubkey();
387
388 let nonce_hash = get_nonce_blockhash(&bank, &nonce_pubkey).unwrap();
389 let message = new_sanitized_message(Message::new_with_blockhash(
390 &[
391 system_instruction::transfer(&custodian_pubkey, &nonce_pubkey, 100_000),
392 system_instruction::advance_nonce_account(&nonce_pubkey, &nonce_pubkey),
393 ],
394 Some(&custodian_pubkey),
395 &nonce_hash,
396 ));
397 let (_, lamports_per_signature) = bank.last_blockhash_and_lamports_per_signature();
398 assert!(bank
399 .check_load_and_advance_message_nonce_account(
400 &message,
401 &bank.next_durable_nonce(),
402 lamports_per_signature
403 )
404 .is_none());
405 }
406
407 #[test]
408 fn test_check_and_load_message_nonce_account_missing_ix_pubkey_fail() {
409 let (bank, _mint_keypair, custodian_keypair, nonce_keypair, _) = setup_nonce_with_bank(
410 10_000_000,
411 |_| {},
412 5_000_000,
413 250_000,
414 None,
415 FeatureSet::all_enabled(),
416 )
417 .unwrap();
418 let custodian_pubkey = custodian_keypair.pubkey();
419 let nonce_pubkey = nonce_keypair.pubkey();
420
421 let nonce_hash = get_nonce_blockhash(&bank, &nonce_pubkey).unwrap();
422 let mut message = Message::new_with_blockhash(
423 &[
424 system_instruction::advance_nonce_account(&nonce_pubkey, &nonce_pubkey),
425 system_instruction::transfer(&custodian_pubkey, &nonce_pubkey, 100_000),
426 ],
427 Some(&custodian_pubkey),
428 &nonce_hash,
429 );
430 message.instructions[0].accounts.clear();
431 let (_, lamports_per_signature) = bank.last_blockhash_and_lamports_per_signature();
432 assert!(bank
433 .check_load_and_advance_message_nonce_account(
434 &new_sanitized_message(message),
435 &bank.next_durable_nonce(),
436 lamports_per_signature,
437 )
438 .is_none());
439 }
440
441 #[test]
442 fn test_check_and_load_message_nonce_account_nonce_acc_does_not_exist_fail() {
443 let (bank, _mint_keypair, custodian_keypair, nonce_keypair, _) = setup_nonce_with_bank(
444 10_000_000,
445 |_| {},
446 5_000_000,
447 250_000,
448 None,
449 FeatureSet::all_enabled(),
450 )
451 .unwrap();
452 let custodian_pubkey = custodian_keypair.pubkey();
453 let nonce_pubkey = nonce_keypair.pubkey();
454 let missing_keypair = Keypair::new();
455 let missing_pubkey = missing_keypair.pubkey();
456
457 let nonce_hash = get_nonce_blockhash(&bank, &nonce_pubkey).unwrap();
458 let message = new_sanitized_message(Message::new_with_blockhash(
459 &[
460 system_instruction::advance_nonce_account(&missing_pubkey, &nonce_pubkey),
461 system_instruction::transfer(&custodian_pubkey, &nonce_pubkey, 100_000),
462 ],
463 Some(&custodian_pubkey),
464 &nonce_hash,
465 ));
466 let (_, lamports_per_signature) = bank.last_blockhash_and_lamports_per_signature();
467 assert!(bank
468 .check_load_and_advance_message_nonce_account(
469 &message,
470 &bank.next_durable_nonce(),
471 lamports_per_signature
472 )
473 .is_none());
474 }
475
476 #[test]
477 fn test_check_and_load_message_nonce_account_bad_tx_hash_fail() {
478 let (bank, _mint_keypair, custodian_keypair, nonce_keypair, _) = setup_nonce_with_bank(
479 10_000_000,
480 |_| {},
481 5_000_000,
482 250_000,
483 None,
484 FeatureSet::all_enabled(),
485 )
486 .unwrap();
487 let custodian_pubkey = custodian_keypair.pubkey();
488 let nonce_pubkey = nonce_keypair.pubkey();
489
490 let message = new_sanitized_message(Message::new_with_blockhash(
491 &[
492 system_instruction::advance_nonce_account(&nonce_pubkey, &nonce_pubkey),
493 system_instruction::transfer(&custodian_pubkey, &nonce_pubkey, 100_000),
494 ],
495 Some(&custodian_pubkey),
496 &Hash::default(),
497 ));
498 let (_, lamports_per_signature) = bank.last_blockhash_and_lamports_per_signature();
499 assert!(bank
500 .check_load_and_advance_message_nonce_account(
501 &message,
502 &bank.next_durable_nonce(),
503 lamports_per_signature,
504 )
505 .is_none());
506 }
507
508 #[test_case(true; "test_check_and_load_message_nonce_account_nonce_is_alt_disallowed")]
509 #[test_case(false; "test_check_and_load_message_nonce_account_nonce_is_alt_allowed")]
510 fn test_check_and_load_message_nonce_account_nonce_is_alt(require_static_nonce_account: bool) {
511 let feature_set = if require_static_nonce_account {
512 FeatureSet::all_enabled()
513 } else {
514 FeatureSet::default()
515 };
516 let nonce_authority = Pubkey::new_unique();
517 let (bank, _mint_keypair, _custodian_keypair, nonce_keypair, _) = setup_nonce_with_bank(
518 10_000_000,
519 |_| {},
520 5_000_000,
521 250_000,
522 Some(nonce_authority),
523 feature_set,
524 )
525 .unwrap();
526
527 let nonce_pubkey = nonce_keypair.pubkey();
528 let nonce_hash = get_nonce_blockhash(&bank, &nonce_pubkey).unwrap();
529 let loaded_addresses = LoadedAddresses {
530 writable: vec![nonce_pubkey],
531 readonly: vec![],
532 };
533
534 let message = SanitizedMessage::try_new(
535 SanitizedVersionedMessage::try_new(VersionedMessage::V0(v0::Message {
536 header: MessageHeader {
537 num_required_signatures: 1,
538 num_readonly_signed_accounts: 0,
539 num_readonly_unsigned_accounts: 1,
540 },
541 account_keys: vec![nonce_authority, system_program::id()],
542 recent_blockhash: nonce_hash,
543 instructions: vec![CompiledInstruction::new(
544 1, &SystemInstruction::AdvanceNonceAccount,
546 vec![
547 2, 0, ],
550 )],
551 address_table_lookups: vec![MessageAddressTableLookup {
552 account_key: Pubkey::new_unique(),
553 writable_indexes: (0..loaded_addresses.writable.len())
554 .map(|x| x as u8)
555 .collect(),
556 readonly_indexes: (0..loaded_addresses.readonly.len())
557 .map(|x| (loaded_addresses.writable.len() + x) as u8)
558 .collect(),
559 }],
560 }))
561 .unwrap(),
562 SimpleAddressLoader::Enabled(loaded_addresses),
563 &HashSet::new(),
564 )
565 .unwrap();
566
567 let (_, lamports_per_signature) = bank.last_blockhash_and_lamports_per_signature();
568 assert_eq!(
569 bank.check_load_and_advance_message_nonce_account(
570 &message,
571 &bank.next_durable_nonce(),
572 lamports_per_signature
573 )
574 .is_none(),
575 require_static_nonce_account,
576 );
577 }
578}