revm_context/journal/
warm_addresses.rs

1//! This module contains [`WarmAddresses`] struct that stores addresses that are warm loaded.
2//!
3//! It is used to optimize access to precompile addresses.
4
5use bitvec::{bitvec, order::Lsb0, vec::BitVec};
6use primitives::{short_address, Address, HashSet, SHORT_ADDRESS_CAP};
7
8/// Stores addresses that are warm loaded. Contains precompiles and coinbase address.
9#[derive(Debug, Clone, PartialEq, Eq)]
10#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
11pub struct WarmAddresses {
12    /// Set of warm loaded precompile addresses.
13    precompile_set: HashSet<Address>,
14    /// Bit vector of precompile short addresses. If address is shorter than [`SHORT_ADDRESS_CAP`] it
15    /// will be stored in this bit vector for faster access.
16    precompile_short_addresses: BitVec,
17    /// `true` if all precompiles are short addresses.
18    all_short_addresses: bool,
19    /// Coinbase address.
20    coinbase: Option<Address>,
21}
22
23impl Default for WarmAddresses {
24    fn default() -> Self {
25        Self::new()
26    }
27}
28
29impl WarmAddresses {
30    /// Create a new warm addresses instance.
31    #[inline]
32    pub fn new() -> Self {
33        Self {
34            precompile_set: HashSet::default(),
35            precompile_short_addresses: BitVec::new(),
36            all_short_addresses: true,
37            coinbase: None,
38        }
39    }
40
41    /// Returns the precompile addresses.
42    #[inline]
43    pub fn precompiles(&self) -> &HashSet<Address> {
44        &self.precompile_set
45    }
46
47    /// Returns the coinbase address.
48    #[inline]
49    pub fn coinbase(&self) -> Option<Address> {
50        self.coinbase
51    }
52
53    /// Set the precompile addresses and short addresses.
54    #[inline]
55    pub fn set_precompile_addresses(&mut self, addresses: HashSet<Address>) {
56        // short address is always smaller than SHORT_ADDRESS_CAP
57        self.precompile_short_addresses = bitvec![usize, Lsb0; 0; SHORT_ADDRESS_CAP];
58
59        let mut all_short_addresses = true;
60        for address in addresses.iter() {
61            if let Some(short_address) = short_address(address) {
62                self.precompile_short_addresses.set(short_address, true);
63            } else {
64                all_short_addresses = false;
65            }
66        }
67
68        self.all_short_addresses = all_short_addresses;
69        self.precompile_set = addresses;
70    }
71
72    /// Set the coinbase address.
73    #[inline]
74    pub fn set_coinbase(&mut self, address: Address) {
75        self.coinbase = Some(address);
76    }
77
78    /// Clear the coinbase address.
79    #[inline]
80    pub fn clear_coinbase(&mut self) {
81        self.coinbase = None;
82    }
83
84    /// Returns true if the address is warm loaded.
85    #[inline]
86    pub fn is_warm(&self, address: &Address) -> bool {
87        // check if it is coinbase
88        if Some(*address) == self.coinbase {
89            return true;
90        }
91
92        // if there are no precompiles, it is cold loaded and bitvec is not set.
93        if self.precompile_set.is_empty() {
94            return false;
95        }
96
97        // check if it is short precompile address
98        if let Some(short_address) = short_address(address) {
99            return self.precompile_short_addresses[short_address];
100        }
101
102        // if all precompiles are short addresses, it is cold loaded.
103        if self.all_short_addresses {
104            return false;
105        }
106
107        // in the end check if it is inside precompile set
108        self.precompile_set.contains(address)
109    }
110
111    /// Returns true if the address is cold loaded.
112    #[inline]
113    pub fn is_cold(&self, address: &Address) -> bool {
114        !self.is_warm(address)
115    }
116}
117
118#[cfg(test)]
119mod tests {
120    use super::*;
121    use primitives::{address, Address};
122
123    #[test]
124    fn test_initialization() {
125        let warm_addresses = WarmAddresses::new();
126        assert!(warm_addresses.precompile_set.is_empty());
127        assert!(warm_addresses.precompile_short_addresses.is_empty());
128        assert!(warm_addresses.coinbase.is_none());
129
130        // Test Default trait
131        let default_addresses = WarmAddresses::default();
132        assert_eq!(warm_addresses, default_addresses);
133    }
134
135    #[test]
136    fn test_coinbase_management() {
137        let mut warm_addresses = WarmAddresses::new();
138        let coinbase_addr = address!("1234567890123456789012345678901234567890");
139
140        // Test setting coinbase
141        warm_addresses.set_coinbase(coinbase_addr);
142        assert_eq!(warm_addresses.coinbase, Some(coinbase_addr));
143        assert!(warm_addresses.is_warm(&coinbase_addr));
144
145        // Test clearing coinbase
146        warm_addresses.clear_coinbase();
147        assert!(warm_addresses.coinbase.is_none());
148        assert!(!warm_addresses.is_warm(&coinbase_addr));
149    }
150
151    #[test]
152    fn test_short_address_precompiles() {
153        let mut warm_addresses = WarmAddresses::new();
154
155        // Create short addresses (18 leading zeros, last 2 bytes < 300)
156        let mut bytes1 = [0u8; 20];
157        bytes1[19] = 1u8;
158        let short_addr1 = Address::from(bytes1);
159
160        let mut bytes2 = [0u8; 20];
161        bytes2[19] = 5u8;
162        let short_addr2 = Address::from(bytes2);
163
164        let mut precompiles = HashSet::default();
165        precompiles.insert(short_addr1);
166        precompiles.insert(short_addr2);
167
168        warm_addresses.set_precompile_addresses(precompiles.clone());
169
170        // Verify storage
171        assert_eq!(warm_addresses.precompile_set, precompiles);
172        assert_eq!(
173            warm_addresses.precompile_short_addresses.len(),
174            SHORT_ADDRESS_CAP
175        );
176
177        // Verify bitvec optimization
178        assert!(warm_addresses.precompile_short_addresses[1]);
179        assert!(warm_addresses.precompile_short_addresses[5]);
180        assert!(!warm_addresses.precompile_short_addresses[0]);
181
182        // Verify warmth detection
183        assert!(warm_addresses.is_warm(&short_addr1));
184        assert!(warm_addresses.is_warm(&short_addr2));
185
186        // Test non-existent short address
187        let mut other_bytes = [0u8; 20];
188        other_bytes[19] = 20u8;
189        let other_short_addr = Address::from(other_bytes);
190        assert!(!warm_addresses.is_warm(&other_short_addr));
191    }
192
193    #[test]
194    fn test_regular_address_precompiles() {
195        let mut warm_addresses = WarmAddresses::new();
196
197        // Create non-short addresses
198        let regular_addr = address!("1234567890123456789012345678901234567890");
199        let mut bytes = [0u8; 20];
200        bytes[18] = 1u8;
201        bytes[19] = 44u8; // 300
202        let boundary_addr = Address::from(bytes);
203
204        let mut precompiles = HashSet::default();
205        precompiles.insert(regular_addr);
206        precompiles.insert(boundary_addr);
207
208        warm_addresses.set_precompile_addresses(precompiles.clone());
209
210        // Verify storage
211        assert_eq!(warm_addresses.precompile_set, precompiles);
212        assert!(!warm_addresses.precompile_short_addresses.any());
213
214        // Verify warmth detection
215        assert!(warm_addresses.is_warm(&regular_addr));
216        assert!(warm_addresses.is_warm(&boundary_addr));
217
218        // Test non-existent regular address
219        let other_addr = address!("0987654321098765432109876543210987654321");
220        assert!(!warm_addresses.is_warm(&other_addr));
221    }
222
223    #[test]
224    fn test_mixed_address_types() {
225        let mut warm_addresses = WarmAddresses::new();
226
227        let mut short_bytes = [0u8; 20];
228        short_bytes[19] = 7u8;
229        let short_addr = Address::from(short_bytes);
230        let regular_addr = address!("1234567890123456789012345678901234567890");
231
232        let mut precompiles = HashSet::default();
233        precompiles.insert(short_addr);
234        precompiles.insert(regular_addr);
235
236        warm_addresses.set_precompile_addresses(precompiles);
237
238        // Both types should be warm
239        assert!(warm_addresses.is_warm(&short_addr));
240        assert!(warm_addresses.is_warm(&regular_addr));
241
242        // Verify short address optimization is used
243        assert!(warm_addresses.precompile_short_addresses[7]);
244        assert!(!warm_addresses.precompile_short_addresses[8]);
245    }
246
247    #[test]
248    fn test_short_address_boundary() {
249        let mut warm_addresses = WarmAddresses::new();
250
251        // Address at boundary (SHORT_ADDRESS_CAP - 1)
252        let mut boundary_bytes = [0u8; 20];
253        let boundary_val = (SHORT_ADDRESS_CAP - 1) as u16;
254        boundary_bytes[18] = (boundary_val >> 8) as u8;
255        boundary_bytes[19] = boundary_val as u8;
256        let boundary_addr = Address::from(boundary_bytes);
257
258        let mut precompiles = HashSet::default();
259        precompiles.insert(boundary_addr);
260
261        warm_addresses.set_precompile_addresses(precompiles);
262
263        assert!(warm_addresses.is_warm(&boundary_addr));
264        assert!(warm_addresses.precompile_short_addresses[SHORT_ADDRESS_CAP - 1]);
265    }
266}