reth_evm/
sender_recovery.rs1use alloc::sync::Arc;
2use alloy_primitives::{map::FbBuildHasher, Address, B256};
3use reth_primitives_traits::{transaction::signed::RecoveryError, SignedTransaction};
4
5const SENDER_RECOVERY_CACHE_CAPACITY: usize = 1 << 17;
7
8#[derive(Clone, Debug)]
14pub struct SenderRecoveryCache {
15 cache: Arc<fixed_cache::Cache<B256, Address, FbBuildHasher<32>, SenderRecoveryCacheConfig>>,
16}
17
18impl SenderRecoveryCache {
19 pub fn new(capacity: usize) -> Self {
23 Self { cache: Arc::new(fixed_cache::Cache::new(capacity, FbBuildHasher::<32>::default())) }
24 }
25
26 #[inline]
28 pub fn get(&self, tx_hash: &B256) -> Option<Address> {
29 self.cache.get(tx_hash)
30 }
31
32 #[inline]
36 pub fn recover<T: SignedTransaction>(&self, transaction: &T) -> Result<Address, RecoveryError> {
37 self.cache.get_or_try_insert_with_ref(
38 transaction.tx_hash(),
39 |_| transaction.try_recover(),
40 |hash| *hash,
41 )
42 }
43}
44
45impl Default for SenderRecoveryCache {
46 fn default() -> Self {
47 Self::new(SENDER_RECOVERY_CACHE_CAPACITY)
48 }
49}
50
51struct SenderRecoveryCacheConfig;
52
53impl fixed_cache::CacheConfig for SenderRecoveryCacheConfig {
54 const STATS: bool = false;
55}
56
57#[cfg(test)]
58mod tests {
59 use super::*;
60 use alloy_consensus::TxLegacy;
61 use alloy_primitives::{Signature, U256};
62 use reth_ethereum_primitives::{Transaction, TransactionSigned};
63
64 #[test]
65 fn recover_populates_cache() {
66 let transaction = TransactionSigned::new_unhashed(
67 Transaction::Legacy(TxLegacy::default()),
68 Signature::test_signature(),
69 );
70 let cache = SenderRecoveryCache::new(4);
71 let shared_cache = cache.clone();
72
73 let sender = cache.recover(&transaction).unwrap();
74
75 assert_eq!(shared_cache.get(transaction.tx_hash()), Some(sender));
76 assert_eq!(shared_cache.recover(&transaction).unwrap(), sender);
77 }
78
79 #[test]
80 fn failed_recovery_is_not_cached() {
81 let transaction = TransactionSigned::new_unhashed(
82 Transaction::Legacy(TxLegacy::default()),
83 Signature::new(U256::ZERO, U256::ZERO, false),
84 );
85 let cache = SenderRecoveryCache::new(4);
86
87 assert!(cache.recover(&transaction).is_err());
88 assert_eq!(cache.get(transaction.tx_hash()), None);
89 }
90}