Skip to content

Commit c97cd1e

Browse files
committed
test: port TransactionQueue tests
1 parent 62708ef commit c97cd1e

File tree

6 files changed

+293
-679
lines changed

6 files changed

+293
-679
lines changed

crates/edr_evm/Cargo.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,16 @@ tracing = { version = "0.1.37", features = ["attributes", "std"], optional = tru
3232
[dev-dependencies]
3333
anyhow = "1.0.75"
3434
criterion = { version = "0.4.0", default-features = false, features = ["cargo_bench_support", "html_reports", "plotters"] }
35-
lazy_static = "1.4.0"
3635
edr_test_utils = { version = "0.2.0-dev", path = "../edr_test_utils" }
36+
lazy_static = "1.4.0"
3737
paste = { version = "1.0.14", default-features = false }
3838
serial_test = "2.0.0"
3939
tempfile = "3.7.1"
4040

4141
[features]
4242
bench-once = [] # limits the benchmark variants to one
4343
test-remote = []
44+
test-utils = []
4445
tracing = ["dep:tracing", "edr_eth/tracing"]
4546

4647
[[bench]]

crates/edr_evm/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ pub mod mempool;
4848
mod miner;
4949
pub(crate) mod random;
5050
mod runtime;
51+
/// Utilities for testing
52+
#[cfg(any(test, feature = "test-utils"))]
53+
pub mod test_utils;
5154
mod transaction;
5255

5356
/// Types for managing Ethereum precompiles

crates/edr_evm/src/miner.rs

+278-25
Original file line numberDiff line numberDiff line change
@@ -119,31 +119,10 @@ where
119119

120120
let base_fee = block_builder.header().base_fee;
121121
let comparator: Box<MineOrderComparator> = match mine_ordering {
122-
MineOrdering::Fifo => Box::new(|lhs, rhs| lhs.order_id().cmp(&rhs.order_id())),
123-
MineOrdering::Priority => Box::new(move |lhs, rhs| {
124-
let effective_miner_fee = |transaction: &ExecutableTransaction| {
125-
let max_fee_per_gas = transaction.gas_price();
126-
let max_priority_fee_per_gas = transaction
127-
.max_priority_fee_per_gas()
128-
.unwrap_or(max_fee_per_gas);
129-
130-
base_fee.map_or(max_fee_per_gas, |base_fee| {
131-
max_priority_fee_per_gas.min(max_fee_per_gas - base_fee)
132-
})
133-
};
134-
135-
// Invert lhs and rhs to get decreasing order by effective miner fee
136-
let ordering =
137-
effective_miner_fee(rhs.pending()).cmp(&effective_miner_fee(lhs.pending()));
138-
139-
// If two txs have the same effective miner fee we want to sort them
140-
// in increasing order by orderId
141-
if ordering == Ordering::Equal {
142-
lhs.order_id().cmp(&rhs.order_id())
143-
} else {
144-
ordering
145-
}
146-
}),
122+
MineOrdering::Fifo => Box::new(first_in_first_out_comparator),
123+
MineOrdering::Priority => {
124+
Box::new(move |lhs, rhs| priority_comparator(lhs, rhs, base_fee))
125+
}
147126
};
148127

149128
mem_pool.iter(comparator)
@@ -199,3 +178,277 @@ where
199178
transaction_traces: traces,
200179
})
201180
}
181+
182+
fn effective_miner_fee(transaction: &ExecutableTransaction, base_fee: Option<U256>) -> U256 {
183+
let max_fee_per_gas = transaction.gas_price();
184+
let max_priority_fee_per_gas = transaction
185+
.max_priority_fee_per_gas()
186+
.unwrap_or(max_fee_per_gas);
187+
188+
base_fee.map_or(max_fee_per_gas, |base_fee| {
189+
max_priority_fee_per_gas.min(max_fee_per_gas - base_fee)
190+
})
191+
}
192+
193+
fn first_in_first_out_comparator(lhs: &OrderedTransaction, rhs: &OrderedTransaction) -> Ordering {
194+
lhs.order_id().cmp(&rhs.order_id())
195+
}
196+
197+
fn priority_comparator(
198+
lhs: &OrderedTransaction,
199+
rhs: &OrderedTransaction,
200+
base_fee: Option<U256>,
201+
) -> Ordering {
202+
let effective_miner_fee =
203+
move |transaction: &ExecutableTransaction| effective_miner_fee(transaction, base_fee);
204+
205+
// Invert lhs and rhs to get decreasing order by effective miner fee
206+
let ordering = effective_miner_fee(rhs.pending()).cmp(&effective_miner_fee(lhs.pending()));
207+
208+
// If two txs have the same effective miner fee we want to sort them
209+
// in increasing order by orderId
210+
if ordering == Ordering::Equal {
211+
lhs.order_id().cmp(&rhs.order_id())
212+
} else {
213+
ordering
214+
}
215+
}
216+
217+
#[cfg(test)]
218+
mod tests {
219+
use edr_eth::{AccountInfo, Address};
220+
221+
use super::*;
222+
use crate::test_utils::{
223+
dummy_eip1559_transaction, dummy_eip155_transaction_with_price, MemPoolTestFixture,
224+
};
225+
226+
#[test]
227+
fn fifo_ordering() -> anyhow::Result<()> {
228+
let sender1 = Address::random();
229+
let sender2 = Address::random();
230+
let sender3 = Address::random();
231+
232+
let account_with_balance = AccountInfo {
233+
balance: U256::from(100_000_000u64),
234+
..AccountInfo::default()
235+
};
236+
let mut fixture = MemPoolTestFixture::with_accounts(&[
237+
(sender1, account_with_balance.clone()),
238+
(sender2, account_with_balance.clone()),
239+
(sender3, account_with_balance),
240+
]);
241+
242+
let base_fee = Some(U256::from(15));
243+
244+
let transaction1 = dummy_eip155_transaction_with_price(sender1, 0, U256::from(111))?;
245+
assert_eq!(effective_miner_fee(&transaction1, base_fee), U256::from(96));
246+
fixture.add_transaction(transaction1.clone())?;
247+
248+
let transaction2 = dummy_eip1559_transaction(sender2, 0, U256::from(120), U256::from(100))?;
249+
assert_eq!(
250+
effective_miner_fee(&transaction2, base_fee),
251+
U256::from(100)
252+
);
253+
fixture.add_transaction(transaction2.clone())?;
254+
255+
let transaction3 = dummy_eip1559_transaction(sender3, 0, U256::from(140), U256::from(110))?;
256+
assert_eq!(
257+
effective_miner_fee(&transaction3, base_fee),
258+
U256::from(110)
259+
);
260+
fixture.add_transaction(transaction3.clone())?;
261+
262+
let mut ordered_transactions = fixture.mem_pool.iter(first_in_first_out_comparator);
263+
264+
assert_eq!(ordered_transactions.next(), Some(transaction1));
265+
assert_eq!(ordered_transactions.next(), Some(transaction2));
266+
assert_eq!(ordered_transactions.next(), Some(transaction3));
267+
268+
Ok(())
269+
}
270+
271+
#[test]
272+
fn priority_ordering_gas_price_without_base_fee() -> anyhow::Result<()> {
273+
let sender1 = Address::random();
274+
let sender2 = Address::random();
275+
let sender3 = Address::random();
276+
let sender4 = Address::random();
277+
278+
let account_with_balance = AccountInfo {
279+
balance: U256::from(100_000_000u64),
280+
..AccountInfo::default()
281+
};
282+
let mut fixture = MemPoolTestFixture::with_accounts(&[
283+
(sender1, account_with_balance.clone()),
284+
(sender2, account_with_balance.clone()),
285+
(sender3, account_with_balance.clone()),
286+
(sender4, account_with_balance),
287+
]);
288+
289+
let transaction1 = dummy_eip155_transaction_with_price(sender1, 0, U256::from(123))?;
290+
fixture.add_transaction(transaction1.clone())?;
291+
292+
let transaction2 = dummy_eip155_transaction_with_price(sender2, 0, U256::from(1_000))?;
293+
fixture.add_transaction(transaction2.clone())?;
294+
295+
// This has the same gasPrice than tx2, but arrived later, so it's placed later
296+
// in the queue
297+
let transaction3 = dummy_eip155_transaction_with_price(sender3, 0, U256::from(1_000))?;
298+
fixture.add_transaction(transaction3.clone())?;
299+
300+
let transaction4 = dummy_eip155_transaction_with_price(sender4, 0, U256::from(2_000))?;
301+
fixture.add_transaction(transaction4.clone())?;
302+
303+
let mut ordered_transactions = fixture
304+
.mem_pool
305+
.iter(|lhs, rhs| priority_comparator(lhs, rhs, None));
306+
307+
assert_eq!(ordered_transactions.next(), Some(transaction4));
308+
assert_eq!(ordered_transactions.next(), Some(transaction2));
309+
assert_eq!(ordered_transactions.next(), Some(transaction3));
310+
assert_eq!(ordered_transactions.next(), Some(transaction1));
311+
312+
Ok(())
313+
}
314+
315+
#[test]
316+
fn priority_ordering_gas_price_with_base_fee() -> anyhow::Result<()> {
317+
let sender1 = Address::random();
318+
let sender2 = Address::random();
319+
let sender3 = Address::random();
320+
let sender4 = Address::random();
321+
let sender5 = Address::random();
322+
323+
let account_with_balance = AccountInfo {
324+
balance: U256::from(100_000_000u64),
325+
..AccountInfo::default()
326+
};
327+
let mut fixture = MemPoolTestFixture::with_accounts(&[
328+
(sender1, account_with_balance.clone()),
329+
(sender2, account_with_balance.clone()),
330+
(sender3, account_with_balance.clone()),
331+
(sender4, account_with_balance.clone()),
332+
(sender5, account_with_balance),
333+
]);
334+
335+
let base_fee = Some(U256::from(15));
336+
337+
let transaction1 = dummy_eip155_transaction_with_price(sender1, 0, U256::from(111))?;
338+
assert_eq!(effective_miner_fee(&transaction1, base_fee), U256::from(96));
339+
fixture.add_transaction(transaction1.clone())?;
340+
341+
let transaction2 = dummy_eip1559_transaction(sender2, 0, U256::from(120), U256::from(100))?;
342+
assert_eq!(
343+
effective_miner_fee(&transaction2, base_fee),
344+
U256::from(100)
345+
);
346+
fixture.add_transaction(transaction2.clone())?;
347+
348+
let transaction3 = dummy_eip1559_transaction(sender3, 0, U256::from(140), U256::from(110))?;
349+
assert_eq!(
350+
effective_miner_fee(&transaction3, base_fee),
351+
U256::from(110)
352+
);
353+
fixture.add_transaction(transaction3.clone())?;
354+
355+
let transaction4 = dummy_eip1559_transaction(sender4, 0, U256::from(140), U256::from(130))?;
356+
assert_eq!(
357+
effective_miner_fee(&transaction4, base_fee),
358+
U256::from(125)
359+
);
360+
fixture.add_transaction(transaction4.clone())?;
361+
362+
let transaction5 = dummy_eip155_transaction_with_price(sender5, 0, U256::from(170))?;
363+
assert_eq!(
364+
effective_miner_fee(&transaction5, base_fee),
365+
U256::from(155)
366+
);
367+
fixture.add_transaction(transaction5.clone())?;
368+
369+
let mut ordered_transactions = fixture
370+
.mem_pool
371+
.iter(|lhs, rhs| priority_comparator(lhs, rhs, base_fee));
372+
373+
assert_eq!(ordered_transactions.next(), Some(transaction5));
374+
assert_eq!(ordered_transactions.next(), Some(transaction4));
375+
assert_eq!(ordered_transactions.next(), Some(transaction3));
376+
assert_eq!(ordered_transactions.next(), Some(transaction2));
377+
assert_eq!(ordered_transactions.next(), Some(transaction1));
378+
379+
Ok(())
380+
}
381+
382+
#[test]
383+
fn ordering_remove_caller() -> anyhow::Result<()> {
384+
let sender1 = Address::random();
385+
let sender2 = Address::random();
386+
let sender3 = Address::random();
387+
let sender4 = Address::random();
388+
389+
let account_with_balance = AccountInfo {
390+
balance: U256::from(100_000_000u64),
391+
..AccountInfo::default()
392+
};
393+
let mut fixture = MemPoolTestFixture::with_accounts(&[
394+
(sender1, account_with_balance.clone()),
395+
(sender2, account_with_balance.clone()),
396+
(sender3, account_with_balance.clone()),
397+
(sender4, account_with_balance),
398+
]);
399+
400+
// Insert 9 transactions sequentially (no for loop)
401+
let transaction1 = dummy_eip155_transaction_with_price(sender1, 0, U256::from(100))?;
402+
fixture.add_transaction(transaction1.clone())?;
403+
404+
let transaction2 = dummy_eip155_transaction_with_price(sender1, 1, U256::from(99))?;
405+
fixture.add_transaction(transaction2.clone())?;
406+
407+
let transaction3 = dummy_eip155_transaction_with_price(sender2, 0, U256::from(98))?;
408+
fixture.add_transaction(transaction3.clone())?;
409+
410+
let transaction4 = dummy_eip155_transaction_with_price(sender2, 1, U256::from(97))?;
411+
fixture.add_transaction(transaction4.clone())?;
412+
413+
let transaction5 = dummy_eip155_transaction_with_price(sender3, 0, U256::from(96))?;
414+
fixture.add_transaction(transaction5.clone())?;
415+
416+
let transaction6 = dummy_eip155_transaction_with_price(sender3, 1, U256::from(95))?;
417+
fixture.add_transaction(transaction6.clone())?;
418+
419+
let transaction7 = dummy_eip155_transaction_with_price(sender3, 2, U256::from(94))?;
420+
fixture.add_transaction(transaction7.clone())?;
421+
422+
let transaction8 = dummy_eip155_transaction_with_price(sender3, 3, U256::from(93))?;
423+
fixture.add_transaction(transaction8.clone())?;
424+
425+
let transaction9 = dummy_eip155_transaction_with_price(sender4, 0, U256::from(92))?;
426+
fixture.add_transaction(transaction9.clone())?;
427+
428+
let transaction10 = dummy_eip155_transaction_with_price(sender4, 1, U256::from(91))?;
429+
fixture.add_transaction(transaction10.clone())?;
430+
431+
let mut ordered_transactions = fixture
432+
.mem_pool
433+
.iter(|lhs, rhs| priority_comparator(lhs, rhs, None));
434+
435+
assert_eq!(ordered_transactions.next(), Some(transaction1));
436+
assert_eq!(ordered_transactions.next(), Some(transaction2));
437+
assert_eq!(ordered_transactions.next(), Some(transaction3));
438+
439+
// Remove all transactions for sender 2
440+
ordered_transactions.remove_caller(&sender2);
441+
442+
assert_eq!(ordered_transactions.next(), Some(transaction5));
443+
assert_eq!(ordered_transactions.next(), Some(transaction6));
444+
assert_eq!(ordered_transactions.next(), Some(transaction7));
445+
446+
// Remove all transactions for sender 3
447+
ordered_transactions.remove_caller(&sender3);
448+
449+
assert_eq!(ordered_transactions.next(), Some(transaction9));
450+
assert_eq!(ordered_transactions.next(), Some(transaction10));
451+
452+
Ok(())
453+
}
454+
}

0 commit comments

Comments
 (0)