tempo_precompiles/nonce/
mod.rs

1pub mod dispatch;
2
3pub use tempo_contracts::precompiles::INonce;
4use tempo_contracts::precompiles::{NonceError, NonceEvent};
5use tempo_precompiles_macros::contract;
6
7use crate::{
8    NONCE_PRECOMPILE_ADDRESS,
9    error::Result,
10    storage::{Handler, Mapping},
11};
12use alloy::primitives::{Address, U256};
13
14/// NonceManager contract for managing 2D nonces as per the AA spec
15///
16/// Storage Layout (similar to Solidity contract):
17/// ```solidity
18/// contract Nonce {
19///     mapping(address => mapping(uint256 => uint64)) public nonces;      // slot 0
20///     mapping(address => uint256) public activeKeyCount;                  // slot 1
21/// }
22/// ```
23///
24/// - Slot 0: 2D nonce mapping - keccak256(abi.encode(nonce_key, keccak256(abi.encode(account, 0))))
25/// - Slot 1: Active key count - keccak256(abi.encode(account, 1))
26///
27/// Note: Protocol nonce (key 0) is stored directly in account state, not here.
28/// Only user nonce keys (1-N) are managed by this precompile.
29#[contract(addr = NONCE_PRECOMPILE_ADDRESS)]
30pub struct NonceManager {
31    nonces: Mapping<Address, Mapping<U256, u64>>,
32    active_key_count: Mapping<Address, U256>,
33}
34
35impl NonceManager {
36    /// Initializes the nonce manager contract.
37    pub fn initialize(&mut self) -> Result<()> {
38        self.__initialize()
39    }
40
41    /// Get the nonce for a specific account and nonce key
42    pub fn get_nonce(&self, call: INonce::getNonceCall) -> Result<u64> {
43        // Protocol nonce (key 0) is stored in account state, not in this precompile
44        // Users should query account nonce directly, not through this precompile
45        if call.nonceKey == 0 {
46            return Err(NonceError::protocol_nonce_not_supported().into());
47        }
48
49        // For user nonce keys, read from precompile storage
50        self.nonces.at(call.account).at(call.nonceKey).read()
51    }
52
53    /// Get the number of active user nonce keys for an account
54    pub fn get_active_nonce_key_count(
55        &self,
56        call: INonce::getActiveNonceKeyCountCall,
57    ) -> Result<U256> {
58        self.active_key_count.at(call.account).read()
59    }
60
61    /// Internal: Increment nonce for a specific account and nonce key
62    pub fn increment_nonce(&mut self, account: Address, nonce_key: U256) -> Result<u64> {
63        if nonce_key == 0 {
64            return Err(NonceError::invalid_nonce_key().into());
65        }
66
67        let current = self.nonces.at(account).at(nonce_key).read()?;
68
69        // If transitioning from 0 to 1, increment active key count
70        if current == 0 {
71            self.increment_active_key_count(account)?;
72        }
73
74        let new_nonce = current
75            .checked_add(1)
76            .ok_or_else(NonceError::nonce_overflow)?;
77
78        self.nonces.at(account).at(nonce_key).write(new_nonce)?;
79
80        if self.storage.spec().is_allegretto() {
81            self.emit_event(NonceEvent::NonceIncremented(INonce::NonceIncremented {
82                account,
83                nonceKey: nonce_key,
84                newNonce: new_nonce,
85            }))?;
86        }
87
88        Ok(new_nonce)
89    }
90
91    /// Increment the active key count for an account
92    fn increment_active_key_count(&mut self, account: Address) -> Result<()> {
93        let current = self.active_key_count.at(account).read()?;
94
95        let new_count = current
96            .checked_add(U256::ONE)
97            .ok_or_else(NonceError::nonce_overflow)?;
98
99        self.active_key_count.at(account).write(new_count)?;
100
101        // Emit ActiveKeyCountChanged event (only after Moderato hardfork)
102        if self.storage.spec().is_moderato() {
103            self.emit_event(NonceEvent::ActiveKeyCountChanged(
104                INonce::ActiveKeyCountChanged {
105                    account,
106                    newCount: new_count,
107                },
108            ))?;
109        }
110
111        Ok(())
112    }
113}
114
115#[cfg(test)]
116mod tests {
117    use crate::{
118        error::TempoPrecompileError,
119        storage::{StorageCtx, hashmap::HashMapStorageProvider},
120    };
121    use tempo_chainspec::hardfork::TempoHardfork;
122
123    use super::*;
124    use alloy::primitives::address;
125
126    #[test]
127    fn test_get_nonce_returns_zero_for_new_key() -> eyre::Result<()> {
128        let mut storage = HashMapStorageProvider::new(1);
129        StorageCtx::enter(&mut storage, || {
130            let mgr = NonceManager::new();
131
132            let account = address!("0x1111111111111111111111111111111111111111");
133            let nonce = mgr.get_nonce(INonce::getNonceCall {
134                account,
135                nonceKey: U256::from(5),
136            })?;
137
138            assert_eq!(nonce, 0);
139            Ok(())
140        })
141    }
142
143    #[test]
144    fn test_get_nonce_rejects_protocol_nonce() -> eyre::Result<()> {
145        let mut storage = HashMapStorageProvider::new(1);
146        StorageCtx::enter(&mut storage, || {
147            let mgr = NonceManager::new();
148
149            let account = address!("0x1111111111111111111111111111111111111111");
150            let result = mgr.get_nonce(INonce::getNonceCall {
151                account,
152                nonceKey: U256::ZERO,
153            });
154
155            assert_eq!(
156                result.unwrap_err(),
157                TempoPrecompileError::NonceError(NonceError::protocol_nonce_not_supported())
158            );
159            Ok(())
160        })
161    }
162
163    #[test]
164    fn test_increment_nonce() -> eyre::Result<()> {
165        let mut storage = HashMapStorageProvider::new(1);
166        StorageCtx::enter(&mut storage, || {
167            let mut mgr = NonceManager::new();
168
169            let account = address!("0x1111111111111111111111111111111111111111");
170            let nonce_key = U256::from(5);
171
172            let new_nonce = mgr.increment_nonce(account, nonce_key)?;
173            assert_eq!(new_nonce, 1);
174
175            let new_nonce = mgr.increment_nonce(account, nonce_key)?;
176            assert_eq!(new_nonce, 2);
177            Ok(())
178        })
179    }
180
181    #[test]
182    fn test_active_key_count() -> eyre::Result<()> {
183        let mut storage = HashMapStorageProvider::new(1);
184        StorageCtx::enter(&mut storage, || {
185            let mut mgr = NonceManager::new();
186
187            let account = address!("0x1111111111111111111111111111111111111111");
188
189            // Initially, no active keys
190            let count =
191                mgr.get_active_nonce_key_count(INonce::getActiveNonceKeyCountCall { account })?;
192            assert_eq!(count, U256::ZERO);
193
194            // Increment a nonce key - should increase active count
195            mgr.increment_nonce(account, U256::ONE)?;
196            let count =
197                mgr.get_active_nonce_key_count(INonce::getActiveNonceKeyCountCall { account })?;
198            assert_eq!(count, U256::ONE);
199
200            // Increment same key again - count should stay the same
201            mgr.increment_nonce(account, U256::ONE)?;
202            let count =
203                mgr.get_active_nonce_key_count(INonce::getActiveNonceKeyCountCall { account })?;
204            assert_eq!(count, U256::ONE);
205
206            // Increment a different key - count should increase
207            mgr.increment_nonce(account, U256::from(2))?;
208            let count =
209                mgr.get_active_nonce_key_count(INonce::getActiveNonceKeyCountCall { account })?;
210            assert_eq!(count, U256::from(2));
211            Ok(())
212        })
213    }
214
215    #[test]
216    fn test_different_accounts_independent() -> eyre::Result<()> {
217        let mut storage = HashMapStorageProvider::new(1);
218        StorageCtx::enter(&mut storage, || {
219            let mut mgr = NonceManager::new();
220
221            let account1 = address!("0x1111111111111111111111111111111111111111");
222            let account2 = address!("0x2222222222222222222222222222222222222222");
223            let nonce_key = U256::from(5);
224
225            for _ in 0..10 {
226                mgr.increment_nonce(account1, nonce_key)?;
227            }
228            for _ in 0..20 {
229                mgr.increment_nonce(account2, nonce_key)?;
230            }
231
232            let nonce1 = mgr.get_nonce(INonce::getNonceCall {
233                account: account1,
234                nonceKey: nonce_key,
235            })?;
236            let nonce2 = mgr.get_nonce(INonce::getNonceCall {
237                account: account2,
238                nonceKey: nonce_key,
239            })?;
240
241            assert_eq!(nonce1, 10);
242            assert_eq!(nonce2, 20);
243            Ok(())
244        })
245    }
246
247    #[test]
248    fn test_active_key_count_event_emitted_post_moderato() -> eyre::Result<()> {
249        let mut storage = HashMapStorageProvider::new(1).with_spec(TempoHardfork::Moderato);
250        StorageCtx::enter(&mut storage, || {
251            let account = address!("0x1111111111111111111111111111111111111111");
252            let nonce_key = U256::from(5);
253
254            // First increment should emit ActiveKeyCountChanged event
255            let mut mgr = NonceManager::new();
256            mgr.increment_nonce(account, nonce_key)?;
257            assert_eq!(mgr.emitted_events().len(), 1);
258
259            // Second increment on same key should NOT emit ActiveKeyCountChanged
260            mgr.increment_nonce(account, nonce_key)?;
261            assert_eq!(mgr.emitted_events().len(), 1);
262
263            // Increment on different key SHOULD emit ActiveKeyCountChanged again
264            let nonce_key2 = U256::from(10);
265            mgr.increment_nonce(account, nonce_key2)?;
266            mgr.assert_emitted_events(vec![
267                NonceEvent::ActiveKeyCountChanged(INonce::ActiveKeyCountChanged {
268                    account,
269                    newCount: U256::ONE,
270                }),
271                NonceEvent::ActiveKeyCountChanged(INonce::ActiveKeyCountChanged {
272                    account,
273                    newCount: U256::from(2),
274                }),
275            ]);
276
277            // Second increment on same key should NOT emit ActiveKeyCountChanged
278            mgr.increment_nonce(account, nonce_key2)?;
279            assert_eq!(mgr.emitted_events().len(), 2);
280
281            Ok(())
282        })
283    }
284
285    #[test]
286    fn test_active_key_count_event_not_emitted_pre_moderato() -> eyre::Result<()> {
287        let mut storage = HashMapStorageProvider::new(1).with_spec(TempoHardfork::Adagio);
288        StorageCtx::enter(&mut storage, || {
289            let account = address!("0x1111111111111111111111111111111111111111");
290            let nonce_key = U256::from(5);
291
292            let mut mgr = NonceManager::new();
293            mgr.increment_nonce(account, nonce_key)?;
294
295            assert!(
296                mgr.emitted_events().is_empty(),
297                "No events should be emitted pre-Moderato"
298            );
299
300            let nonce_key2 = U256::from(10);
301            mgr.increment_nonce(account, nonce_key2)?;
302
303            assert!(
304                mgr.emitted_events().is_empty(),
305                "No events should be emitted pre-Moderato"
306            );
307            Ok(())
308        })
309    }
310
311    #[test]
312    fn test_increment_nonce_post_allegretto() -> eyre::Result<()> {
313        let mut storage = HashMapStorageProvider::new(1).with_spec(TempoHardfork::Allegretto);
314        StorageCtx::enter(&mut storage, || {
315            let account = address!("0x1111111111111111111111111111111111111111");
316            let nonce_key = U256::from(5);
317
318            // First increment emits ActiveKeyCountChanged + NonceIncremented
319            let mut mgr = NonceManager::new();
320            mgr.increment_nonce(account, nonce_key)?;
321            assert_eq!(mgr.emitted_events().len(), 2);
322
323            // Second increment on same key only emits NonceIncremented (no new key)
324            mgr.increment_nonce(account, nonce_key)?;
325            assert_eq!(mgr.emitted_events().len(), 3);
326
327            mgr.assert_emitted_events(vec![
328                NonceEvent::ActiveKeyCountChanged(INonce::ActiveKeyCountChanged {
329                    account,
330                    newCount: U256::ONE,
331                }),
332                NonceEvent::NonceIncremented(INonce::NonceIncremented {
333                    account,
334                    nonceKey: nonce_key,
335                    newNonce: 1,
336                }),
337                NonceEvent::NonceIncremented(INonce::NonceIncremented {
338                    account,
339                    nonceKey: nonce_key,
340                    newNonce: 2,
341                }),
342            ]);
343
344            Ok(())
345        })
346    }
347}