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