1use {
2 super::{Bank, BankStatusCache},
3 agave_feature_set::{raise_cpi_nesting_limit_to_8, 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 self.feature_set
126 .is_active(&raise_cpi_nesting_limit_to_8::id()),
127 )
128 }
129 });
130 self.check_transaction_age(
131 tx.borrow(),
132 max_age,
133 &next_durable_nonce,
134 &hash_queue,
135 next_lamports_per_signature,
136 error_counters,
137 compute_budget_and_limits,
138 )
139 }
140 Err(e) => Err(e.clone()),
141 })
142 .collect()
143 }
144
145 fn checked_transactions_details_with_test_override(
146 nonce: Option<NonceInfo>,
147 lamports_per_signature: u64,
148 compute_budget_and_limits: Result<
149 SVMTransactionExecutionAndFeeBudgetLimits,
150 TransactionError,
151 >,
152 ) -> CheckedTransactionDetails {
153 let compute_budget_and_limits = if lamports_per_signature == 0 {
154 compute_budget_and_limits.map(|v| SVMTransactionExecutionAndFeeBudgetLimits {
157 budget: v.budget,
158 loaded_accounts_data_size_limit: v.loaded_accounts_data_size_limit,
159 fee_details: FeeDetails::default(),
160 })
161 } else {
162 compute_budget_and_limits
163 };
164 CheckedTransactionDetails::new(nonce, compute_budget_and_limits)
165 }
166
167 fn check_transaction_age(
168 &self,
169 tx: &impl SVMMessage,
170 max_age: usize,
171 next_durable_nonce: &DurableNonce,
172 hash_queue: &BlockhashQueue,
173 next_lamports_per_signature: u64,
174 error_counters: &mut TransactionErrorMetrics,
175 compute_budget: Result<SVMTransactionExecutionAndFeeBudgetLimits, TransactionError>,
176 ) -> TransactionCheckResult {
177 let recent_blockhash = tx.recent_blockhash();
178 if let Some(hash_info) = hash_queue.get_hash_info_if_valid(recent_blockhash, max_age) {
179 Ok(Self::checked_transactions_details_with_test_override(
180 None,
181 hash_info.lamports_per_signature(),
182 compute_budget,
183 ))
184 } else if let Some((nonce, previous_lamports_per_signature)) = self
185 .check_load_and_advance_message_nonce_account(
186 tx,
187 next_durable_nonce,
188 next_lamports_per_signature,
189 )
190 {
191 Ok(Self::checked_transactions_details_with_test_override(
192 Some(nonce),
193 previous_lamports_per_signature,
194 compute_budget,
195 ))
196 } else {
197 error_counters.blockhash_not_found += 1;
198 Err(TransactionError::BlockhashNotFound)
199 }
200 }
201
202 pub(super) fn check_load_and_advance_message_nonce_account(
203 &self,
204 message: &impl SVMMessage,
205 next_durable_nonce: &DurableNonce,
206 next_lamports_per_signature: u64,
207 ) -> Option<(NonceInfo, u64)> {
208 let nonce_is_advanceable = message.recent_blockhash() != next_durable_nonce.as_hash();
209 if !nonce_is_advanceable {
210 return None;
211 }
212
213 let (nonce_address, mut nonce_account, nonce_data) =
214 self.load_message_nonce_account(message)?;
215
216 let previous_lamports_per_signature = nonce_data.get_lamports_per_signature();
217 let next_nonce_state = NonceState::new_initialized(
218 &nonce_data.authority,
219 *next_durable_nonce,
220 next_lamports_per_signature,
221 );
222 nonce_account
223 .set_state(&NonceVersions::new(next_nonce_state))
224 .ok()?;
225
226 Some((
227 NonceInfo::new(nonce_address, nonce_account),
228 previous_lamports_per_signature,
229 ))
230 }
231
232 pub(super) fn load_message_nonce_account(
233 &self,
234 message: &impl SVMMessage,
235 ) -> Option<(Pubkey, AccountSharedData, NonceData)> {
236 let require_static_nonce_account = self
237 .feature_set
238 .is_active(&agave_feature_set::require_static_nonce_account::id());
239 let nonce_address = message.get_durable_nonce(require_static_nonce_account)?;
240 let nonce_account = self.get_account_with_fixed_root(nonce_address)?;
241 let nonce_data =
242 nonce_account::verify_nonce_account(&nonce_account, message.recent_blockhash())?;
243
244 let nonce_is_authorized = message
245 .get_ix_signers(NONCED_TX_MARKER_IX_INDEX as usize)
246 .any(|signer| signer == &nonce_data.authority);
247 if !nonce_is_authorized {
248 return None;
249 }
250
251 Some((*nonce_address, nonce_account, nonce_data))
252 }
253
254 fn check_status_cache<Tx: TransactionWithMeta>(
255 &self,
256 sanitized_txs: &[impl core::borrow::Borrow<Tx>],
257 lock_results: Vec<TransactionCheckResult>,
258 error_counters: &mut TransactionErrorMetrics,
259 ) -> Vec<TransactionCheckResult> {
260 let mut check_results = Vec::with_capacity(sanitized_txs.len());
262 let rcache = self.status_cache.read().unwrap();
263
264 check_results.extend(sanitized_txs.iter().zip(lock_results).map(
265 |(sanitized_tx, lock_result)| {
266 let sanitized_tx = sanitized_tx.borrow();
267 if lock_result.is_ok()
268 && self.is_transaction_already_processed(sanitized_tx, &rcache)
269 {
270 error_counters.already_processed += 1;
271 return Err(TransactionError::AlreadyProcessed);
272 }
273
274 lock_result
275 },
276 ));
277 check_results
278 }
279
280 fn is_transaction_already_processed(
281 &self,
282 sanitized_tx: &impl TransactionWithMeta,
283 status_cache: &BankStatusCache,
284 ) -> bool {
285 let key = sanitized_tx.message_hash();
286 let transaction_blockhash = sanitized_tx.recent_blockhash();
287 status_cache
288 .get_status(key, transaction_blockhash, &self.ancestors)
289 .is_some()
290 }
291}
292
293#[cfg(test)]
294mod tests {
295 use {
296 super::*,
297 crate::bank::tests::{
298 get_nonce_blockhash, get_nonce_data_from_account, new_sanitized_message,
299 setup_nonce_with_bank,
300 },
301 solana_hash::Hash,
302 solana_keypair::Keypair,
303 solana_message::{
304 compiled_instruction::CompiledInstruction,
305 v0::{self, LoadedAddresses, MessageAddressTableLookup},
306 Message, MessageHeader, SanitizedMessage, SanitizedVersionedMessage,
307 SimpleAddressLoader, VersionedMessage,
308 },
309 solana_signer::Signer,
310 solana_system_interface::{
311 instruction::{self as system_instruction, SystemInstruction},
312 program as system_program,
313 },
314 std::collections::HashSet,
315 test_case::test_case,
316 };
317
318 #[test]
319 fn test_check_and_load_message_nonce_account_ok() {
320 const STALE_LAMPORTS_PER_SIGNATURE: u64 = 42;
321 let (bank, _mint_keypair, custodian_keypair, nonce_keypair, _) = setup_nonce_with_bank(
322 10_000_000,
323 |_| {},
324 5_000_000,
325 250_000,
326 None,
327 FeatureSet::all_enabled(),
328 )
329 .unwrap();
330 let custodian_pubkey = custodian_keypair.pubkey();
331 let nonce_pubkey = nonce_keypair.pubkey();
332
333 let nonce_hash = get_nonce_blockhash(&bank, &nonce_pubkey).unwrap();
334 let message = new_sanitized_message(Message::new_with_blockhash(
335 &[
336 system_instruction::advance_nonce_account(&nonce_pubkey, &nonce_pubkey),
337 system_instruction::transfer(&custodian_pubkey, &nonce_pubkey, 100_000),
338 ],
339 Some(&custodian_pubkey),
340 &nonce_hash,
341 ));
342
343 let mut nonce_account = bank.get_account(&nonce_pubkey).unwrap();
345 let nonce_data = get_nonce_data_from_account(&nonce_account).unwrap();
346 nonce_account
347 .set_state(&NonceVersions::new(NonceState::new_initialized(
348 &nonce_data.authority,
349 nonce_data.durable_nonce,
350 STALE_LAMPORTS_PER_SIGNATURE,
351 )))
352 .unwrap();
353 bank.store_account(&nonce_pubkey, &nonce_account);
354
355 let nonce_account = bank.get_account(&nonce_pubkey).unwrap();
356 let (_, next_lamports_per_signature) = bank.last_blockhash_and_lamports_per_signature();
357 let mut expected_nonce_info = NonceInfo::new(nonce_pubkey, nonce_account);
358 expected_nonce_info
359 .try_advance_nonce(bank.next_durable_nonce(), next_lamports_per_signature)
360 .unwrap();
361
362 assert_eq!(
367 bank.check_load_and_advance_message_nonce_account(
368 &message,
369 &bank.next_durable_nonce(),
370 next_lamports_per_signature
371 ),
372 Some((expected_nonce_info, STALE_LAMPORTS_PER_SIGNATURE)),
373 );
374 }
375
376 #[test]
377 fn test_check_and_load_message_nonce_account_not_nonce_fail() {
378 let (bank, _mint_keypair, custodian_keypair, nonce_keypair, _) = setup_nonce_with_bank(
379 10_000_000,
380 |_| {},
381 5_000_000,
382 250_000,
383 None,
384 FeatureSet::all_enabled(),
385 )
386 .unwrap();
387 let custodian_pubkey = custodian_keypair.pubkey();
388 let nonce_pubkey = nonce_keypair.pubkey();
389
390 let nonce_hash = get_nonce_blockhash(&bank, &nonce_pubkey).unwrap();
391 let message = new_sanitized_message(Message::new_with_blockhash(
392 &[
393 system_instruction::transfer(&custodian_pubkey, &nonce_pubkey, 100_000),
394 system_instruction::advance_nonce_account(&nonce_pubkey, &nonce_pubkey),
395 ],
396 Some(&custodian_pubkey),
397 &nonce_hash,
398 ));
399 let (_, lamports_per_signature) = bank.last_blockhash_and_lamports_per_signature();
400 assert!(bank
401 .check_load_and_advance_message_nonce_account(
402 &message,
403 &bank.next_durable_nonce(),
404 lamports_per_signature
405 )
406 .is_none());
407 }
408
409 #[test]
410 fn test_check_and_load_message_nonce_account_missing_ix_pubkey_fail() {
411 let (bank, _mint_keypair, custodian_keypair, nonce_keypair, _) = setup_nonce_with_bank(
412 10_000_000,
413 |_| {},
414 5_000_000,
415 250_000,
416 None,
417 FeatureSet::all_enabled(),
418 )
419 .unwrap();
420 let custodian_pubkey = custodian_keypair.pubkey();
421 let nonce_pubkey = nonce_keypair.pubkey();
422
423 let nonce_hash = get_nonce_blockhash(&bank, &nonce_pubkey).unwrap();
424 let mut message = Message::new_with_blockhash(
425 &[
426 system_instruction::advance_nonce_account(&nonce_pubkey, &nonce_pubkey),
427 system_instruction::transfer(&custodian_pubkey, &nonce_pubkey, 100_000),
428 ],
429 Some(&custodian_pubkey),
430 &nonce_hash,
431 );
432 message.instructions[0].accounts.clear();
433 let (_, lamports_per_signature) = bank.last_blockhash_and_lamports_per_signature();
434 assert!(bank
435 .check_load_and_advance_message_nonce_account(
436 &new_sanitized_message(message),
437 &bank.next_durable_nonce(),
438 lamports_per_signature,
439 )
440 .is_none());
441 }
442
443 #[test]
444 fn test_check_and_load_message_nonce_account_nonce_acc_does_not_exist_fail() {
445 let (bank, _mint_keypair, custodian_keypair, nonce_keypair, _) = setup_nonce_with_bank(
446 10_000_000,
447 |_| {},
448 5_000_000,
449 250_000,
450 None,
451 FeatureSet::all_enabled(),
452 )
453 .unwrap();
454 let custodian_pubkey = custodian_keypair.pubkey();
455 let nonce_pubkey = nonce_keypair.pubkey();
456 let missing_keypair = Keypair::new();
457 let missing_pubkey = missing_keypair.pubkey();
458
459 let nonce_hash = get_nonce_blockhash(&bank, &nonce_pubkey).unwrap();
460 let message = new_sanitized_message(Message::new_with_blockhash(
461 &[
462 system_instruction::advance_nonce_account(&missing_pubkey, &nonce_pubkey),
463 system_instruction::transfer(&custodian_pubkey, &nonce_pubkey, 100_000),
464 ],
465 Some(&custodian_pubkey),
466 &nonce_hash,
467 ));
468 let (_, lamports_per_signature) = bank.last_blockhash_and_lamports_per_signature();
469 assert!(bank
470 .check_load_and_advance_message_nonce_account(
471 &message,
472 &bank.next_durable_nonce(),
473 lamports_per_signature
474 )
475 .is_none());
476 }
477
478 #[test]
479 fn test_check_and_load_message_nonce_account_bad_tx_hash_fail() {
480 let (bank, _mint_keypair, custodian_keypair, nonce_keypair, _) = setup_nonce_with_bank(
481 10_000_000,
482 |_| {},
483 5_000_000,
484 250_000,
485 None,
486 FeatureSet::all_enabled(),
487 )
488 .unwrap();
489 let custodian_pubkey = custodian_keypair.pubkey();
490 let nonce_pubkey = nonce_keypair.pubkey();
491
492 let message = new_sanitized_message(Message::new_with_blockhash(
493 &[
494 system_instruction::advance_nonce_account(&nonce_pubkey, &nonce_pubkey),
495 system_instruction::transfer(&custodian_pubkey, &nonce_pubkey, 100_000),
496 ],
497 Some(&custodian_pubkey),
498 &Hash::default(),
499 ));
500 let (_, lamports_per_signature) = bank.last_blockhash_and_lamports_per_signature();
501 assert!(bank
502 .check_load_and_advance_message_nonce_account(
503 &message,
504 &bank.next_durable_nonce(),
505 lamports_per_signature,
506 )
507 .is_none());
508 }
509
510 #[test_case(true; "test_check_and_load_message_nonce_account_nonce_is_alt_disallowed")]
511 #[test_case(false; "test_check_and_load_message_nonce_account_nonce_is_alt_allowed")]
512 fn test_check_and_load_message_nonce_account_nonce_is_alt(require_static_nonce_account: bool) {
513 let feature_set = if require_static_nonce_account {
514 FeatureSet::all_enabled()
515 } else {
516 FeatureSet::default()
517 };
518 let nonce_authority = Pubkey::new_unique();
519 let (bank, _mint_keypair, _custodian_keypair, nonce_keypair, _) = setup_nonce_with_bank(
520 10_000_000,
521 |_| {},
522 5_000_000,
523 250_000,
524 Some(nonce_authority),
525 feature_set,
526 )
527 .unwrap();
528
529 let nonce_pubkey = nonce_keypair.pubkey();
530 let nonce_hash = get_nonce_blockhash(&bank, &nonce_pubkey).unwrap();
531 let loaded_addresses = LoadedAddresses {
532 writable: vec![nonce_pubkey],
533 readonly: vec![],
534 };
535
536 let message = SanitizedMessage::try_new(
537 SanitizedVersionedMessage::try_new(VersionedMessage::V0(v0::Message {
538 header: MessageHeader {
539 num_required_signatures: 1,
540 num_readonly_signed_accounts: 0,
541 num_readonly_unsigned_accounts: 1,
542 },
543 account_keys: vec![nonce_authority, system_program::id()],
544 recent_blockhash: nonce_hash,
545 instructions: vec![CompiledInstruction::new(
546 1, &SystemInstruction::AdvanceNonceAccount,
548 vec![
549 2, 0, ],
552 )],
553 address_table_lookups: vec![MessageAddressTableLookup {
554 account_key: Pubkey::new_unique(),
555 writable_indexes: (0..loaded_addresses.writable.len())
556 .map(|x| x as u8)
557 .collect(),
558 readonly_indexes: (0..loaded_addresses.readonly.len())
559 .map(|x| (loaded_addresses.writable.len() + x) as u8)
560 .collect(),
561 }],
562 }))
563 .unwrap(),
564 SimpleAddressLoader::Enabled(loaded_addresses),
565 &HashSet::new(),
566 )
567 .unwrap();
568
569 let (_, lamports_per_signature) = bank.last_blockhash_and_lamports_per_signature();
570 assert_eq!(
571 bank.check_load_and_advance_message_nonce_account(
572 &message,
573 &bank.next_durable_nonce(),
574 lamports_per_signature
575 )
576 .is_none(),
577 require_static_nonce_account,
578 );
579 }
580}