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#[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 pub fn initialize(&mut self) -> Result<()> {
38 self.__initialize()
39 }
40
41 pub fn get_nonce(&self, call: INonce::getNonceCall) -> Result<u64> {
43 if call.nonceKey == 0 {
46 return Err(NonceError::protocol_nonce_not_supported().into());
47 }
48
49 self.nonces.at(call.account).at(call.nonceKey).read()
51 }
52
53 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 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 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 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 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 let count =
191 mgr.get_active_nonce_key_count(INonce::getActiveNonceKeyCountCall { account })?;
192 assert_eq!(count, U256::ZERO);
193
194 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 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 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 let mut mgr = NonceManager::new();
256 mgr.increment_nonce(account, nonce_key)?;
257 assert_eq!(mgr.emitted_events().len(), 1);
258
259 mgr.increment_nonce(account, nonce_key)?;
261 assert_eq!(mgr.emitted_events().len(), 1);
262
263 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 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 let mut mgr = NonceManager::new();
320 mgr.increment_nonce(account, nonce_key)?;
321 assert_eq!(mgr.emitted_events().len(), 2);
322
323 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}