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