1use crate::{
4 ordering::TempoTipOrdering, transaction::TempoPooledTransaction,
5 tt_2d_pool::BestAA2dTransactions,
6};
7use alloy_primitives::{Address, U256, map::HashMap};
8use reth_evm::block::TxResult;
9use reth_primitives_traits::transaction::error::InvalidTransactionError;
10use reth_transaction_pool::{
11 BestTransactions, Priority, TransactionOrdering, ValidPoolTransaction,
12 error::InvalidPoolTransactionError,
13};
14use std::sync::Arc;
15use tempo_evm::TempoTxResult;
16use tempo_precompiles::tip20::is_tip20_prefix;
17
18pub type BestTransaction = Arc<ValidPoolTransaction<TempoPooledTransaction>>;
19type BestTransactionWithPriority = (BestTransaction, Priority<u64>);
20
21pub struct MergeBestTransactions {
24 protocol_pool: Box<dyn BestTransactions<Item = BestTransaction>>,
25 aa_2d_pool: BestAA2dTransactions,
26 next_protocol_pool: Option<BestTransactionWithPriority>,
27 next_aa_2d_pool: Option<BestTransactionWithPriority>,
28 base_fee: u64,
29}
30
31impl MergeBestTransactions {
32 pub(crate) fn new(
34 protocol_pool: Box<dyn BestTransactions<Item = BestTransaction>>,
35 aa_2d_pool: BestAA2dTransactions,
36 base_fee: u64,
37 ) -> Self {
38 Self {
39 protocol_pool,
40 aa_2d_pool,
41 next_protocol_pool: None,
42 next_aa_2d_pool: None,
43 base_fee,
44 }
45 }
46}
47
48impl MergeBestTransactions {
49 fn next_best(&mut self) -> Option<BestTransactionWithPriority> {
51 if self.next_protocol_pool.is_none() {
52 self.next_protocol_pool = self.protocol_pool.next().map(|tx| {
53 let priority = TempoTipOrdering::default().priority(&tx.transaction, self.base_fee);
54 (tx, priority)
55 });
56 }
57 if self.next_aa_2d_pool.is_none() {
58 self.next_aa_2d_pool = self.aa_2d_pool.next_tx_and_priority();
59 }
60
61 match (&mut self.next_protocol_pool, &mut self.next_aa_2d_pool) {
62 (None, None) => {
63 None
65 }
66 (Some(_), None) => {
68 let (item, priority) = self.next_protocol_pool.take()?;
69 Some((item, priority))
70 }
71 (None, Some(_)) => {
73 let (item, priority) = self.next_aa_2d_pool.take()?;
74 Some((item, priority))
75 }
76 (Some((_, protocol_priority)), Some((_, aa_2d_priority))) => {
78 if protocol_priority >= aa_2d_priority {
80 let (item, priority) = self.next_protocol_pool.take()?;
81 Some((item, priority))
82 } else {
83 let (item, priority) = self.next_aa_2d_pool.take()?;
84 Some((item, priority))
85 }
86 }
87 }
88 }
89}
90
91impl Iterator for MergeBestTransactions {
92 type Item = BestTransaction;
93
94 fn next(&mut self) -> Option<Self::Item> {
95 self.next_best().map(|(tx, _)| tx)
96 }
97
98 fn size_hint(&self) -> (usize, Option<usize>) {
99 let buffered = usize::from(self.next_protocol_pool.is_some())
100 + usize::from(self.next_aa_2d_pool.is_some());
101 let (protocol_lower, protocol_upper) = self.protocol_pool.size_hint();
102 let (aa_2d_lower, aa_2d_upper) = self.aa_2d_pool.size_hint();
103
104 (
105 buffered
106 .saturating_add(protocol_lower)
107 .saturating_add(aa_2d_lower),
108 protocol_upper
109 .zip(aa_2d_upper)
110 .and_then(|(protocol_upper, aa_2d_upper)| protocol_upper.checked_add(aa_2d_upper))
111 .and_then(|upper| upper.checked_add(buffered)),
112 )
113 }
114}
115
116impl BestTransactions for MergeBestTransactions {
117 fn mark_invalid(&mut self, transaction: &Self::Item, kind: InvalidPoolTransactionError) {
118 if transaction.transaction.is_aa_2d() {
119 self.aa_2d_pool.mark_invalid(transaction, kind);
120 } else {
121 self.protocol_pool.mark_invalid(transaction, kind);
122 }
123 }
124
125 fn no_updates(&mut self) {
126 self.protocol_pool.no_updates();
127 self.aa_2d_pool.no_updates();
128 }
129
130 fn set_skip_blobs(&mut self, skip_blobs: bool) {
131 self.protocol_pool.set_skip_blobs(skip_blobs);
132 self.aa_2d_pool.set_skip_blobs(skip_blobs);
133 }
134}
135
136pub struct StateAwareBestTransactions<I> {
140 inner: I,
141 decreased_balances: HashMap<(Address, U256), U256>,
145}
146
147impl<I> StateAwareBestTransactions<I>
148where
149 I: BestTransactions<Item = Arc<ValidPoolTransaction<TempoPooledTransaction>>>,
150{
151 pub fn new(inner: I) -> Self {
153 Self {
154 inner,
155 decreased_balances: HashMap::default(),
156 }
157 }
158
159 pub fn on_new_result(&mut self, result: &TempoTxResult) {
162 for (&address, account) in &result.result().state {
163 if !is_tip20_prefix(address) {
164 continue;
165 }
166
167 for (&slot, storage_slot) in &account.storage {
168 if storage_slot.present_value < storage_slot.original_value {
169 self.decreased_balances
170 .insert((address, slot), storage_slot.present_value);
171 } else if let Some(balance) = self.decreased_balances.get_mut(&(address, slot)) {
172 *balance = storage_slot.present_value;
173 }
174 }
175 }
176 }
177}
178
179impl<I> Iterator for StateAwareBestTransactions<I>
180where
181 I: BestTransactions<Item = Arc<ValidPoolTransaction<TempoPooledTransaction>>>,
182{
183 type Item = Arc<ValidPoolTransaction<TempoPooledTransaction>>;
184
185 fn next(&mut self) -> Option<Self::Item> {
186 loop {
187 let tx = self.inner.next()?;
188
189 let Some(key) = tx.transaction.fee_balance_slot() else {
190 debug_assert!(false, "pool transaction must have cached fee_balance_slot");
191 continue;
192 };
193
194 if let Some(&balance) = self.decreased_balances.get(&key)
195 && balance < tx.transaction.fee_token_cost()
196 {
197 self.inner.mark_invalid(
198 &tx,
199 InvalidPoolTransactionError::Consensus(
200 InvalidTransactionError::InsufficientFunds(
201 (balance, tx.transaction.fee_token_cost()).into(),
202 ),
203 ),
204 );
205 continue;
206 }
207
208 return Some(tx);
209 }
210 }
211}
212
213impl<I> BestTransactions for StateAwareBestTransactions<I>
214where
215 I: BestTransactions<Item = Arc<ValidPoolTransaction<TempoPooledTransaction>>> + Send,
216{
217 fn mark_invalid(&mut self, transaction: &Self::Item, kind: InvalidPoolTransactionError) {
218 self.inner.mark_invalid(transaction, kind);
219 }
220
221 fn no_updates(&mut self) {
222 self.inner.no_updates();
223 }
224
225 fn set_skip_blobs(&mut self, skip_blobs: bool) {
226 self.inner.set_skip_blobs(skip_blobs);
227 }
228}
229
230#[cfg(test)]
231mod tests {
232 use super::*;
233 use crate::{
234 ordering::TempoTipOrdering,
235 test_utils::{TxBuilder, wrap_valid_tx},
236 tt_2d_pool::AA2dPool,
237 };
238 use alloy_primitives::Address;
239 use futures::executor::block_on;
240 use reth_primitives_traits::transaction::error::InvalidTransactionError;
241 use reth_transaction_pool::{
242 Pool, PoolConfig, TransactionOrigin, TransactionPool, blobstore::InMemoryBlobStore,
243 test_utils::OkValidator,
244 };
245 use std::sync::Arc;
246 use tempo_chainspec::{hardfork::TempoHardfork, spec::TEMPO_T1_BASE_FEE};
247
248 type TestTx = Arc<ValidPoolTransaction<TempoPooledTransaction>>;
249
250 fn tx_with_nonce_key(nonce_key: U256, sender: Address, nonce: u64, priority: u128) -> TestTx {
251 Arc::new(wrap_valid_tx(
252 TxBuilder::aa(sender)
253 .nonce_key(nonce_key)
254 .nonce(nonce)
255 .max_priority_fee(priority)
256 .max_fee(u128::from(TEMPO_T1_BASE_FEE) + priority)
257 .build(),
258 TransactionOrigin::External,
259 ))
260 }
261
262 fn protocol_tx(nonce: u64, priority: u128) -> TestTx {
263 protocol_tx_for_sender(Address::random(), nonce, priority)
264 }
265
266 fn protocol_tx_for_sender(sender: Address, nonce: u64, priority: u128) -> TestTx {
267 tx_with_nonce_key(U256::ZERO, sender, nonce, priority)
268 }
269
270 fn aa_2d_tx(nonce: u64, priority: u128) -> TestTx {
271 aa_2d_tx_for_sequence(Address::random(), nonce, priority)
272 }
273
274 fn aa_2d_tx_for_sequence(sender: Address, nonce: u64, priority: u128) -> TestTx {
275 tx_with_nonce_key(U256::from(1), sender, nonce, priority)
276 }
277
278 fn protocol_best_transactions(
279 txs: Vec<TestTx>,
280 ) -> Box<dyn BestTransactions<Item = BestTransaction>> {
281 let pool = Pool::new(
282 OkValidator::<TempoPooledTransaction>::default(),
283 TempoTipOrdering::default(),
284 InMemoryBlobStore::default(),
285 PoolConfig::default(),
286 );
287
288 let results = block_on(pool.add_transactions(
289 TransactionOrigin::External,
290 txs.into_iter().map(|tx| tx.transaction.clone()).collect(),
291 ));
292 assert!(
293 results.iter().all(Result::is_ok),
294 "all protocol transactions must be added successfully: {results:?}"
295 );
296 Box::new(pool.inner().best_transactions())
297 }
298
299 fn aa_2d_best_transactions(txs: Vec<TestTx>) -> BestAA2dTransactions {
300 let mut pool = AA2dPool::default();
301 let mut on_chain_nonces: HashMap<crate::tt_2d_pool::AASequenceId, u64> = HashMap::default();
302 for tx in &txs {
303 let id = tx
304 .transaction
305 .aa_transaction_id()
306 .expect("AA2D transaction must have an AA transaction id");
307 on_chain_nonces
308 .entry(id.seq_id)
309 .and_modify(|nonce: &mut u64| *nonce = (*nonce).min(id.nonce))
310 .or_insert(id.nonce);
311 }
312
313 pool.set_base_fee(TEMPO_T1_BASE_FEE);
314 for tx in txs {
315 let id = tx
316 .transaction
317 .aa_transaction_id()
318 .expect("AA2D transaction must have an AA transaction id");
319 let on_chain_nonce = on_chain_nonces[&id.seq_id];
320 pool.add_transaction(tx, on_chain_nonce, TempoHardfork::T1)
321 .expect("AA2D transaction must be added successfully");
322 }
323 pool.best_transactions()
324 }
325
326 fn merged_best_transactions(
327 protocol_txs: Vec<TestTx>,
328 aa_2d_txs: Vec<TestTx>,
329 ) -> MergeBestTransactions {
330 MergeBestTransactions::new(
331 protocol_best_transactions(protocol_txs),
332 aa_2d_best_transactions(aa_2d_txs),
333 TEMPO_T1_BASE_FEE,
334 )
335 }
336
337 #[test]
338 fn test_merge_best_transactions_basic() {
339 let tx_a = protocol_tx(0, 10);
344 let tx_b = protocol_tx(1, 5);
345 let tx_c = protocol_tx(2, 3);
346 let tx_d = aa_2d_tx(3, 8);
347 let tx_e = aa_2d_tx(4, 4);
348 let tx_f = aa_2d_tx(5, 1);
349 let mut merged = merged_best_transactions(
350 vec![tx_a.clone(), tx_b.clone(), tx_c.clone()],
351 vec![tx_d.clone(), tx_e.clone(), tx_f.clone()],
352 );
353
354 assert_eq!(merged.next().map(|tx| *tx.hash()), Some(*tx_a.hash())); assert_eq!(merged.next().map(|tx| *tx.hash()), Some(*tx_d.hash())); assert_eq!(merged.next().map(|tx| *tx.hash()), Some(*tx_b.hash())); assert_eq!(merged.next().map(|tx| *tx.hash()), Some(*tx_e.hash())); assert_eq!(merged.next().map(|tx| *tx.hash()), Some(*tx_c.hash())); assert_eq!(merged.next().map(|tx| *tx.hash()), Some(*tx_f.hash())); assert!(merged.next().is_none());
361 }
362
363 #[test]
364 fn test_merge_best_transactions_size_hint() {
365 let protocol_sender = Address::random();
366 let protocol_tx_0 = protocol_tx_for_sender(protocol_sender, 0, 10);
367 let protocol_tx_1 = protocol_tx_for_sender(protocol_sender, 1, 9);
368 let aa_2d_tx = aa_2d_tx(0, 8);
369 let mut merged = merged_best_transactions(
370 vec![protocol_tx_0.clone(), protocol_tx_1.clone()],
371 vec![aa_2d_tx.clone()],
372 );
373 merged.no_updates();
374
375 assert_eq!(merged.size_hint(), (0, Some(3)));
376
377 assert_eq!(
378 merged.next().map(|tx| *tx.hash()),
379 Some(*protocol_tx_0.hash())
380 );
381 assert_eq!(merged.size_hint(), (1, Some(2)));
382
383 assert_eq!(
384 merged.next().map(|tx| *tx.hash()),
385 Some(*protocol_tx_1.hash())
386 );
387 assert_eq!(merged.size_hint(), (1, Some(1)));
388
389 assert_eq!(merged.next().map(|tx| *tx.hash()), Some(*aa_2d_tx.hash()));
390 assert_eq!(merged.size_hint(), (0, Some(0)));
391 }
392
393 #[test]
394 fn test_merge_best_transactions_empty_left() {
395 let tx_a = aa_2d_tx(0, 10);
397 let tx_b = aa_2d_tx(1, 5);
398 let mut merged = merged_best_transactions(vec![], vec![tx_a.clone(), tx_b.clone()]);
399
400 assert_eq!(merged.next().map(|tx| *tx.hash()), Some(*tx_a.hash()));
401 assert_eq!(merged.next().map(|tx| *tx.hash()), Some(*tx_b.hash()));
402 assert!(merged.next().is_none());
403 }
404
405 #[test]
406 fn test_merge_best_transactions_empty_right() {
407 let tx_a = protocol_tx(0, 10);
409 let tx_b = protocol_tx(1, 5);
410 let mut merged = merged_best_transactions(vec![tx_a.clone(), tx_b.clone()], vec![]);
411
412 assert_eq!(merged.next().map(|tx| *tx.hash()), Some(*tx_a.hash()));
413 assert_eq!(merged.next().map(|tx| *tx.hash()), Some(*tx_b.hash()));
414 assert!(merged.next().is_none());
415 }
416
417 #[test]
418 fn test_merge_best_transactions_both_empty() {
419 let mut merged = merged_best_transactions(vec![], vec![]);
420
421 assert!(merged.next().is_none());
422 }
423
424 #[test]
425 fn test_merge_best_transactions_equal_priorities() {
426 let tx_a = protocol_tx(0, 10);
428 let tx_b = protocol_tx(1, 5);
429 let tx_c = aa_2d_tx(2, 10);
430 let tx_d = aa_2d_tx(3, 5);
431 let mut merged = merged_best_transactions(
432 vec![tx_a.clone(), tx_b.clone()],
433 vec![tx_c.clone(), tx_d.clone()],
434 );
435
436 assert_eq!(merged.next().map(|tx| *tx.hash()), Some(*tx_a.hash())); assert_eq!(merged.next().map(|tx| *tx.hash()), Some(*tx_c.hash()));
438 assert_eq!(merged.next().map(|tx| *tx.hash()), Some(*tx_b.hash())); assert_eq!(merged.next().map(|tx| *tx.hash()), Some(*tx_d.hash()));
440 assert!(merged.next().is_none());
441 }
442
443 #[test]
448 fn test_merge_best_transactions_single_left() {
449 let tx_a = protocol_tx(0, 10);
450 let mut merged = merged_best_transactions(vec![tx_a.clone()], vec![]);
451
452 assert_eq!(merged.next().map(|tx| *tx.hash()), Some(*tx_a.hash()));
453 assert!(merged.next().is_none());
454 }
455
456 #[test]
457 fn test_merge_best_transactions_single_right() {
458 let tx_a = aa_2d_tx(0, 10);
459 let mut merged = merged_best_transactions(vec![], vec![tx_a.clone()]);
460
461 assert_eq!(merged.next().map(|tx| *tx.hash()), Some(*tx_a.hash()));
462 assert!(merged.next().is_none());
463 }
464
465 #[test]
470 fn test_merge_best_transactions_interleaved() {
471 let l1 = protocol_tx(0, 9);
473 let l2 = protocol_tx(1, 7);
474 let l3 = protocol_tx(2, 5);
475 let r1 = aa_2d_tx(3, 10);
476 let r2 = aa_2d_tx(4, 6);
477 let r3 = aa_2d_tx(5, 4);
478 let mut merged = merged_best_transactions(
479 vec![l1.clone(), l2.clone(), l3.clone()],
480 vec![r1.clone(), r2.clone(), r3.clone()],
481 );
482
483 assert_eq!(merged.next().map(|tx| *tx.hash()), Some(*r1.hash())); assert_eq!(merged.next().map(|tx| *tx.hash()), Some(*l1.hash())); assert_eq!(merged.next().map(|tx| *tx.hash()), Some(*l2.hash())); assert_eq!(merged.next().map(|tx| *tx.hash()), Some(*r2.hash())); assert_eq!(merged.next().map(|tx| *tx.hash()), Some(*l3.hash())); assert_eq!(merged.next().map(|tx| *tx.hash()), Some(*r3.hash())); assert!(merged.next().is_none());
490 }
491
492 #[test]
493 fn test_mark_invalid_routes_aa_2d_to_right_pool() {
494 let aa_2d_sender = Address::random();
497 let l1 = protocol_tx(0, 9);
498 let l2 = protocol_tx(1, 7);
499 let r1 = aa_2d_tx_for_sequence(aa_2d_sender, 0, 10);
500 let r2 = aa_2d_tx_for_sequence(aa_2d_sender, 1, 8);
501 let mut merged =
502 merged_best_transactions(vec![l1.clone(), l2.clone()], vec![r1.clone(), r2]);
503
504 let first = merged.next().unwrap();
506 assert_eq!(*first.hash(), *r1.hash());
507
508 let kind =
510 InvalidPoolTransactionError::Consensus(InvalidTransactionError::TxTypeNotSupported);
511 merged.mark_invalid(&first, kind);
512
513 assert_eq!(merged.next().map(|tx| *tx.hash()), Some(*l1.hash()));
515 assert_eq!(merged.next().map(|tx| *tx.hash()), Some(*l2.hash()));
516 assert!(merged.next().is_none());
517 }
518
519 #[test]
520 fn test_mark_invalid_routes_aa_2d_after_later_protocol_next() {
521 let aa_2d_sender = Address::random();
522 let protocol_sender = Address::random();
523 let l1 = protocol_tx_for_sender(protocol_sender, 0, 9);
524 let l2 = protocol_tx_for_sender(protocol_sender, 1, 7);
525 let r1 = aa_2d_tx_for_sequence(aa_2d_sender, 0, 10);
526 let mut merged = merged_best_transactions(vec![l1.clone(), l2.clone()], vec![r1.clone()]);
527 let first = merged.next().unwrap();
528 let second = merged.next().unwrap();
529
530 assert_eq!(*first.hash(), *r1.hash());
531 assert_eq!(*second.hash(), *l1.hash());
532
533 let kind =
534 InvalidPoolTransactionError::Consensus(InvalidTransactionError::TxTypeNotSupported);
535 merged.mark_invalid(&first, kind);
536
537 assert_eq!(merged.next().map(|tx| *tx.hash()), Some(*l2.hash()));
538 assert!(merged.next().is_none());
539 }
540
541 #[test]
542 fn test_mark_invalid_routes_protocol_aa_to_left_pool() {
543 let protocol_sender = Address::random();
544 let left_tx = protocol_tx_for_sender(protocol_sender, 0, 10);
545 let left_descendant = protocol_tx_for_sender(protocol_sender, 1, 9);
546 let right_tx = aa_2d_tx(0, 8);
547 assert!(left_tx.transaction.is_aa());
548 assert!(!left_tx.transaction.is_aa_2d());
549 assert!(right_tx.transaction.is_aa_2d());
550
551 let mut merged = merged_best_transactions(
552 vec![left_tx.clone(), left_descendant],
553 vec![right_tx.clone()],
554 );
555 let first = merged.next().unwrap();
556 assert_eq!(*first.hash(), *left_tx.hash());
557
558 let kind =
559 InvalidPoolTransactionError::Consensus(InvalidTransactionError::TxTypeNotSupported);
560 merged.mark_invalid(&first, kind);
561
562 assert_eq!(merged.next().map(|tx| *tx.hash()), Some(*right_tx.hash()));
563 assert!(merged.next().is_none());
564 }
565}