verbs_rs/env/
validator.rs

1//! Validator trait and implementations
2//!
3use alloy_primitives::{Address, U256};
4use rand::{seq::SliceRandom, Rng};
5
6use crate::contract::Transaction;
7use std::collections::HashMap;
8
9/// Trait for a block validator
10///
11/// Validators are (currently) responsible
12/// for the ordering of transactions in the
13/// next block during a simulation.
14pub trait Validator {
15    /// Sort transaction queue
16    ///
17    /// Sort a vector of transactions for processing
18    /// in the next simulated block.
19    ///
20    /// # Arguments
21    ///
22    /// - `rng` - Random generator.
23    /// - `transactions` Vector submitted transactions.
24    ///
25    fn order_transactions<R: Rng>(
26        &mut self,
27        rng: &mut R,
28        transactions: Vec<Transaction>,
29    ) -> Vec<Transaction>;
30}
31
32/// Validator that randomly shuffles transactions
33pub struct RandomValidator {}
34
35impl Validator for RandomValidator {
36    fn order_transactions<R: Rng>(
37        &mut self,
38        rng: &mut R,
39        mut transactions: Vec<Transaction>,
40    ) -> Vec<Transaction> {
41        transactions.as_mut_slice().shuffle(rng);
42        transactions
43    }
44}
45
46/// Validator that sorts transactions by Nonce and priority fee
47///
48/// This implementation is adapted from the geth implementation:
49///
50/// > This method first sorts the separates the list of transactions into individual
51/// sender accounts and sorts them by nonce. After the account nonce ordering is
52/// satisfied, the results are merged back together by price, always comparing only
53/// the head transaction from each account.
54///
55/// As such it performs the following steps:
56///
57/// - Group transactions by sender address
58/// - Sort individual groups by nonce
59/// - Sort the groups by the gas-priority of the first transaction in the group
60/// - Flatten the groups into a single vector for processing
61///
62pub struct GasPriorityValidator {}
63
64impl Validator for GasPriorityValidator {
65    fn order_transactions<R: Rng>(
66        &mut self,
67        _rng: &mut R,
68        transactions: Vec<Transaction>,
69    ) -> Vec<Transaction> {
70        let mut transaction_by_address = HashMap::<Address, Vec<Transaction>>::new();
71
72        for t in transactions.into_iter() {
73            match transaction_by_address.entry(t.callee) {
74                std::collections::hash_map::Entry::Occupied(mut o) => {
75                    o.get_mut().push(t);
76                }
77                std::collections::hash_map::Entry::Vacant(v) => {
78                    v.insert(vec![t]);
79                }
80            };
81        }
82
83        let mut transactions: Vec<Vec<Transaction>> = transaction_by_address
84            .into_values()
85            .map(|mut v| {
86                v.sort_by_key(|x| x.nonce);
87                v
88            })
89            .collect();
90
91        transactions.sort_by_key(|x| U256::MAX - x[0].gas_priority_fee.unwrap_or(U256::ZERO));
92
93        transactions.into_iter().flatten().collect()
94    }
95}
96
97#[cfg(test)]
98mod tests {
99
100    use super::*;
101    use alloy_primitives::{Address, Uint, U256};
102    use rand::SeedableRng;
103    use rand_xoshiro::Xoroshiro128StarStar;
104
105    #[test]
106    fn test_gas_priority() {
107        let mut rng = Xoroshiro128StarStar::seed_from_u64(101);
108
109        let address_a = Address::from(Uint::from(101u128));
110        let address_b = Address::from(Uint::from(202u128));
111
112        let transactions = vec![
113            Transaction {
114                function_selector: [0; 4],
115                callee: address_a,
116                transact_to: Address::ZERO,
117                args: Vec::default(),
118                gas_priority_fee: Some(U256::from(10)),
119                nonce: Some(1),
120                value: U256::ZERO,
121                checked: false,
122            },
123            Transaction {
124                function_selector: [0; 4],
125                callee: address_b,
126                transact_to: Address::ZERO,
127                args: Vec::default(),
128                gas_priority_fee: None,
129                nonce: Some(1),
130                value: U256::ZERO,
131                checked: false,
132            },
133            Transaction {
134                function_selector: [0; 4],
135                callee: address_a,
136                transact_to: Address::ZERO,
137                args: Vec::default(),
138                gas_priority_fee: None,
139                nonce: Some(2),
140                value: U256::ZERO,
141                checked: false,
142            },
143            Transaction {
144                function_selector: [0; 4],
145                callee: address_b,
146                transact_to: Address::ZERO,
147                args: Vec::default(),
148                gas_priority_fee: None,
149                nonce: Some(2),
150                value: U256::ZERO,
151                checked: false,
152            },
153        ];
154
155        let mut validator = GasPriorityValidator {};
156
157        let transactions = validator.order_transactions(&mut rng, transactions);
158
159        assert!(transactions.len() == 4);
160
161        assert!(transactions[0].callee == address_a);
162        assert!(transactions[0].nonce == Some(1));
163
164        assert!(transactions[1].callee == address_a);
165        assert!(transactions[1].nonce == Some(2));
166
167        assert!(transactions[2].callee == address_b);
168        assert!(transactions[2].nonce == Some(1));
169
170        assert!(transactions[3].callee == address_b);
171        assert!(transactions[3].nonce == Some(2));
172    }
173}