1use {
2 super::{Bank, BankStatusCache},
3 solana_accounts_db::blockhash_queue::BlockhashQueue,
4 solana_perf::perf_libs,
5 solana_runtime_transaction::transaction_with_meta::TransactionWithMeta,
6 solana_sdk::{
7 account::AccountSharedData,
8 account_utils::StateMut,
9 clock::{
10 MAX_PROCESSING_AGE, MAX_TRANSACTION_FORWARDING_DELAY,
11 MAX_TRANSACTION_FORWARDING_DELAY_GPU,
12 },
13 nonce::{
14 state::{
15 Data as NonceData, DurableNonce, State as NonceState, Versions as NonceVersions,
16 },
17 NONCED_TX_MARKER_IX_INDEX,
18 },
19 nonce_account,
20 pubkey::Pubkey,
21 transaction::{Result as TransactionResult, TransactionError},
22 },
23 solana_svm::{
24 account_loader::{CheckedTransactionDetails, TransactionCheckResult},
25 nonce_info::NonceInfo,
26 transaction_error_metrics::TransactionErrorMetrics,
27 },
28 solana_svm_transaction::svm_message::SVMMessage,
29};
30
31impl Bank {
32 pub fn check_transactions_with_forwarding_delay(
34 &self,
35 transactions: &[impl TransactionWithMeta],
36 filter: &[TransactionResult<()>],
37 forward_transactions_to_leader_at_slot_offset: u64,
38 ) -> Vec<TransactionCheckResult> {
39 let mut error_counters = TransactionErrorMetrics::default();
40 let api = perf_libs::api();
46 let max_tx_fwd_delay = if api.is_none() {
47 MAX_TRANSACTION_FORWARDING_DELAY
48 } else {
49 MAX_TRANSACTION_FORWARDING_DELAY_GPU
50 };
51
52 self.check_transactions(
53 transactions,
54 filter,
55 (MAX_PROCESSING_AGE)
56 .saturating_sub(max_tx_fwd_delay)
57 .saturating_sub(forward_transactions_to_leader_at_slot_offset as usize),
58 &mut error_counters,
59 )
60 }
61
62 pub fn check_transactions<Tx: TransactionWithMeta>(
63 &self,
64 sanitized_txs: &[impl core::borrow::Borrow<Tx>],
65 lock_results: &[TransactionResult<()>],
66 max_age: usize,
67 error_counters: &mut TransactionErrorMetrics,
68 ) -> Vec<TransactionCheckResult> {
69 let lock_results = self.check_age(sanitized_txs, lock_results, max_age, error_counters);
70 self.check_status_cache(sanitized_txs, lock_results, error_counters)
71 }
72
73 fn check_age<Tx: TransactionWithMeta>(
74 &self,
75 sanitized_txs: &[impl core::borrow::Borrow<Tx>],
76 lock_results: &[TransactionResult<()>],
77 max_age: usize,
78 error_counters: &mut TransactionErrorMetrics,
79 ) -> Vec<TransactionCheckResult> {
80 let hash_queue = self.blockhash_queue.read().unwrap();
81 let last_blockhash = hash_queue.last_hash();
82 let next_durable_nonce = DurableNonce::from_blockhash(&last_blockhash);
83 let next_lamports_per_signature = hash_queue
85 .get_lamports_per_signature(&last_blockhash)
86 .unwrap();
87
88 sanitized_txs
89 .iter()
90 .zip(lock_results)
91 .map(|(tx, lock_res)| match lock_res {
92 Ok(()) => self.check_transaction_age(
93 tx.borrow(),
94 max_age,
95 &next_durable_nonce,
96 &hash_queue,
97 next_lamports_per_signature,
98 error_counters,
99 ),
100 Err(e) => Err(e.clone()),
101 })
102 .collect()
103 }
104
105 fn check_transaction_age(
106 &self,
107 tx: &impl SVMMessage,
108 max_age: usize,
109 next_durable_nonce: &DurableNonce,
110 hash_queue: &BlockhashQueue,
111 next_lamports_per_signature: u64,
112 error_counters: &mut TransactionErrorMetrics,
113 ) -> TransactionCheckResult {
114 let recent_blockhash = tx.recent_blockhash();
115 if let Some(hash_info) = hash_queue.get_hash_info_if_valid(recent_blockhash, max_age) {
116 Ok(CheckedTransactionDetails::new(
117 None,
118 hash_info.lamports_per_signature(),
119 ))
120 } else if let Some((nonce, previous_lamports_per_signature)) = self
121 .check_load_and_advance_message_nonce_account(
122 tx,
123 next_durable_nonce,
124 next_lamports_per_signature,
125 )
126 {
127 Ok(CheckedTransactionDetails::new(
128 Some(nonce),
129 previous_lamports_per_signature,
130 ))
131 } else {
132 error_counters.blockhash_not_found += 1;
133 Err(TransactionError::BlockhashNotFound)
134 }
135 }
136
137 pub(super) fn check_load_and_advance_message_nonce_account(
138 &self,
139 message: &impl SVMMessage,
140 next_durable_nonce: &DurableNonce,
141 next_lamports_per_signature: u64,
142 ) -> Option<(NonceInfo, u64)> {
143 let nonce_is_advanceable = message.recent_blockhash() != next_durable_nonce.as_hash();
144 if !nonce_is_advanceable {
145 return None;
146 }
147
148 let (nonce_address, mut nonce_account, nonce_data) =
149 self.load_message_nonce_account(message)?;
150
151 let previous_lamports_per_signature = nonce_data.get_lamports_per_signature();
152 let next_nonce_state = NonceState::new_initialized(
153 &nonce_data.authority,
154 *next_durable_nonce,
155 next_lamports_per_signature,
156 );
157 nonce_account
158 .set_state(&NonceVersions::new(next_nonce_state))
159 .ok()?;
160
161 Some((
162 NonceInfo::new(nonce_address, nonce_account),
163 previous_lamports_per_signature,
164 ))
165 }
166
167 pub(super) fn load_message_nonce_account(
168 &self,
169 message: &impl SVMMessage,
170 ) -> Option<(Pubkey, AccountSharedData, NonceData)> {
171 let nonce_address = message.get_durable_nonce()?;
172 let nonce_account = self.get_account_with_fixed_root(nonce_address)?;
173 let nonce_data =
174 nonce_account::verify_nonce_account(&nonce_account, message.recent_blockhash())?;
175
176 let nonce_is_authorized = message
177 .get_ix_signers(NONCED_TX_MARKER_IX_INDEX as usize)
178 .any(|signer| signer == &nonce_data.authority);
179 if !nonce_is_authorized {
180 return None;
181 }
182
183 Some((*nonce_address, nonce_account, nonce_data))
184 }
185
186 fn check_status_cache<Tx: TransactionWithMeta>(
187 &self,
188 sanitized_txs: &[impl core::borrow::Borrow<Tx>],
189 lock_results: Vec<TransactionCheckResult>,
190 error_counters: &mut TransactionErrorMetrics,
191 ) -> Vec<TransactionCheckResult> {
192 let mut check_results = Vec::with_capacity(sanitized_txs.len());
194 let rcache = self.status_cache.read().unwrap();
195
196 check_results.extend(sanitized_txs.iter().zip(lock_results).map(
197 |(sanitized_tx, lock_result)| {
198 let sanitized_tx = sanitized_tx.borrow();
199 if lock_result.is_ok()
200 && self.is_transaction_already_processed(sanitized_tx, &rcache)
201 {
202 error_counters.already_processed += 1;
203 return Err(TransactionError::AlreadyProcessed);
204 }
205
206 lock_result
207 },
208 ));
209 check_results
210 }
211
212 fn is_transaction_already_processed(
213 &self,
214 sanitized_tx: &impl TransactionWithMeta,
215 status_cache: &BankStatusCache,
216 ) -> bool {
217 let key = sanitized_tx.message_hash();
218 let transaction_blockhash = sanitized_tx.recent_blockhash();
219 status_cache
220 .get_status(key, transaction_blockhash, &self.ancestors)
221 .is_some()
222 }
223}
224
225#[cfg(test)]
226mod tests {
227 use {
228 super::*,
229 crate::bank::tests::{
230 get_nonce_blockhash, get_nonce_data_from_account, new_sanitized_message,
231 setup_nonce_with_bank,
232 },
233 solana_sdk::{
234 feature_set::FeatureSet, hash::Hash, message::Message, signature::Keypair,
235 signer::Signer, system_instruction,
236 },
237 };
238
239 #[test]
240 fn test_check_and_load_message_nonce_account_ok() {
241 const STALE_LAMPORTS_PER_SIGNATURE: u64 = 42;
242 let (bank, _mint_keypair, custodian_keypair, nonce_keypair, _) = setup_nonce_with_bank(
243 10_000_000,
244 |_| {},
245 5_000_000,
246 250_000,
247 None,
248 FeatureSet::all_enabled(),
249 )
250 .unwrap();
251 let custodian_pubkey = custodian_keypair.pubkey();
252 let nonce_pubkey = nonce_keypair.pubkey();
253
254 let nonce_hash = get_nonce_blockhash(&bank, &nonce_pubkey).unwrap();
255 let message = new_sanitized_message(Message::new_with_blockhash(
256 &[
257 system_instruction::advance_nonce_account(&nonce_pubkey, &nonce_pubkey),
258 system_instruction::transfer(&custodian_pubkey, &nonce_pubkey, 100_000),
259 ],
260 Some(&custodian_pubkey),
261 &nonce_hash,
262 ));
263
264 let mut nonce_account = bank.get_account(&nonce_pubkey).unwrap();
266 let nonce_data = get_nonce_data_from_account(&nonce_account).unwrap();
267 nonce_account
268 .set_state(&NonceVersions::new(NonceState::new_initialized(
269 &nonce_data.authority,
270 nonce_data.durable_nonce,
271 STALE_LAMPORTS_PER_SIGNATURE,
272 )))
273 .unwrap();
274 bank.store_account(&nonce_pubkey, &nonce_account);
275
276 let nonce_account = bank.get_account(&nonce_pubkey).unwrap();
277 let (_, next_lamports_per_signature) = bank.last_blockhash_and_lamports_per_signature();
278 let mut expected_nonce_info = NonceInfo::new(nonce_pubkey, nonce_account);
279 expected_nonce_info
280 .try_advance_nonce(bank.next_durable_nonce(), next_lamports_per_signature)
281 .unwrap();
282
283 assert_eq!(
288 bank.check_load_and_advance_message_nonce_account(
289 &message,
290 &bank.next_durable_nonce(),
291 next_lamports_per_signature
292 ),
293 Some((expected_nonce_info, STALE_LAMPORTS_PER_SIGNATURE)),
294 );
295 }
296
297 #[test]
298 fn test_check_and_load_message_nonce_account_not_nonce_fail() {
299 let (bank, _mint_keypair, custodian_keypair, nonce_keypair, _) = setup_nonce_with_bank(
300 10_000_000,
301 |_| {},
302 5_000_000,
303 250_000,
304 None,
305 FeatureSet::all_enabled(),
306 )
307 .unwrap();
308 let custodian_pubkey = custodian_keypair.pubkey();
309 let nonce_pubkey = nonce_keypair.pubkey();
310
311 let nonce_hash = get_nonce_blockhash(&bank, &nonce_pubkey).unwrap();
312 let message = new_sanitized_message(Message::new_with_blockhash(
313 &[
314 system_instruction::transfer(&custodian_pubkey, &nonce_pubkey, 100_000),
315 system_instruction::advance_nonce_account(&nonce_pubkey, &nonce_pubkey),
316 ],
317 Some(&custodian_pubkey),
318 &nonce_hash,
319 ));
320 let (_, lamports_per_signature) = bank.last_blockhash_and_lamports_per_signature();
321 assert!(bank
322 .check_load_and_advance_message_nonce_account(
323 &message,
324 &bank.next_durable_nonce(),
325 lamports_per_signature
326 )
327 .is_none());
328 }
329
330 #[test]
331 fn test_check_and_load_message_nonce_account_missing_ix_pubkey_fail() {
332 let (bank, _mint_keypair, custodian_keypair, nonce_keypair, _) = setup_nonce_with_bank(
333 10_000_000,
334 |_| {},
335 5_000_000,
336 250_000,
337 None,
338 FeatureSet::all_enabled(),
339 )
340 .unwrap();
341 let custodian_pubkey = custodian_keypair.pubkey();
342 let nonce_pubkey = nonce_keypair.pubkey();
343
344 let nonce_hash = get_nonce_blockhash(&bank, &nonce_pubkey).unwrap();
345 let mut message = Message::new_with_blockhash(
346 &[
347 system_instruction::advance_nonce_account(&nonce_pubkey, &nonce_pubkey),
348 system_instruction::transfer(&custodian_pubkey, &nonce_pubkey, 100_000),
349 ],
350 Some(&custodian_pubkey),
351 &nonce_hash,
352 );
353 message.instructions[0].accounts.clear();
354 let (_, lamports_per_signature) = bank.last_blockhash_and_lamports_per_signature();
355 assert!(bank
356 .check_load_and_advance_message_nonce_account(
357 &new_sanitized_message(message),
358 &bank.next_durable_nonce(),
359 lamports_per_signature,
360 )
361 .is_none());
362 }
363
364 #[test]
365 fn test_check_and_load_message_nonce_account_nonce_acc_does_not_exist_fail() {
366 let (bank, _mint_keypair, custodian_keypair, nonce_keypair, _) = setup_nonce_with_bank(
367 10_000_000,
368 |_| {},
369 5_000_000,
370 250_000,
371 None,
372 FeatureSet::all_enabled(),
373 )
374 .unwrap();
375 let custodian_pubkey = custodian_keypair.pubkey();
376 let nonce_pubkey = nonce_keypair.pubkey();
377 let missing_keypair = Keypair::new();
378 let missing_pubkey = missing_keypair.pubkey();
379
380 let nonce_hash = get_nonce_blockhash(&bank, &nonce_pubkey).unwrap();
381 let message = new_sanitized_message(Message::new_with_blockhash(
382 &[
383 system_instruction::advance_nonce_account(&missing_pubkey, &nonce_pubkey),
384 system_instruction::transfer(&custodian_pubkey, &nonce_pubkey, 100_000),
385 ],
386 Some(&custodian_pubkey),
387 &nonce_hash,
388 ));
389 let (_, lamports_per_signature) = bank.last_blockhash_and_lamports_per_signature();
390 assert!(bank
391 .check_load_and_advance_message_nonce_account(
392 &message,
393 &bank.next_durable_nonce(),
394 lamports_per_signature
395 )
396 .is_none());
397 }
398
399 #[test]
400 fn test_check_and_load_message_nonce_account_bad_tx_hash_fail() {
401 let (bank, _mint_keypair, custodian_keypair, nonce_keypair, _) = setup_nonce_with_bank(
402 10_000_000,
403 |_| {},
404 5_000_000,
405 250_000,
406 None,
407 FeatureSet::all_enabled(),
408 )
409 .unwrap();
410 let custodian_pubkey = custodian_keypair.pubkey();
411 let nonce_pubkey = nonce_keypair.pubkey();
412
413 let message = new_sanitized_message(Message::new_with_blockhash(
414 &[
415 system_instruction::advance_nonce_account(&nonce_pubkey, &nonce_pubkey),
416 system_instruction::transfer(&custodian_pubkey, &nonce_pubkey, 100_000),
417 ],
418 Some(&custodian_pubkey),
419 &Hash::default(),
420 ));
421 let (_, lamports_per_signature) = bank.last_blockhash_and_lamports_per_signature();
422 assert!(bank
423 .check_load_and_advance_message_nonce_account(
424 &message,
425 &bank.next_durable_nonce(),
426 lamports_per_signature
427 )
428 .is_none());
429 }
430}