Skip to content

Commit 02e0fe9

Browse files
authored
Feat(aurora-evm-tests): Add new CLI logic for ttransactions dump and slow tests bench (#108)
* Added new CLI logic for tx dump and slow tests * Bench fill refactoring * Fix RawSpec * Change has_smaller to iterator check
1 parent fd56fb8 commit 02e0fe9

5 files changed

Lines changed: 279 additions & 12 deletions

File tree

evm-tests/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ nursery = { level = "deny", priority = -1 }
1616

1717
[dependencies]
1818
aurora-engine-precompiles = "2.1.0"
19-
aurora-evm.workspace = true
19+
aurora-evm = { workspace = true, features = ["with-serde"] }
2020
bytecount = "0.6"
2121
clap = { version = "4.5", features = ["cargo"] }
2222
c-kzg = "1.0"

evm-tests/src/config.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ pub struct VerboseOutput {
88
pub verbose_failed: bool,
99
pub very_verbose: bool,
1010
pub print_state: bool,
11+
pub print_slow: bool,
12+
pub dump_transactions: Option<PathBuf>,
1113
}
1214

1315
#[derive(Default, Debug, Clone)]

evm-tests/src/execution_results.rs

Lines changed: 182 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
use crate::types::Spec;
2-
use aurora_evm::backend::MemoryAccount;
3-
use primitive_types::{H160, H256};
2+
use aurora_evm::backend::{Apply, Basic, MemoryAccount};
3+
use aurora_evm::executor::stack::Authorization;
4+
use primitive_types::{H160, H256, U256};
5+
use serde::{Deserialize, Serialize};
46
use std::collections::BTreeMap;
7+
use std::time::Duration;
58

69
#[derive(Clone, Debug)]
710
pub struct FailedTestDetails {
@@ -18,6 +21,106 @@ pub struct TestExecutionResult {
1821
pub total: u64,
1922
pub failed: u64,
2023
pub failed_tests: Vec<FailedTestDetails>,
24+
pub bench: Vec<TestBench>,
25+
pub dump_successful_txs: Vec<RawInput>,
26+
}
27+
28+
#[derive(Clone, Debug, Serialize, Deserialize)]
29+
pub struct RawInput {
30+
pub spec: RawSpec,
31+
pub caller: H160,
32+
pub value: U256,
33+
pub data: Vec<u8>,
34+
pub gas_limit: u64,
35+
pub access_list: Vec<(H160, Vec<H256>)>,
36+
pub authorization_list: Vec<Authorization>,
37+
pub apply_values: Vec<RawApply>,
38+
}
39+
40+
#[derive(Clone, Debug, Serialize, Deserialize)]
41+
pub enum RawApply {
42+
Modify {
43+
address: H160,
44+
basic: Basic,
45+
code: Option<Vec<u8>>,
46+
storage: Vec<(H256, H256)>,
47+
reset_storage: bool,
48+
},
49+
Delete {
50+
address: H160,
51+
},
52+
}
53+
54+
impl<I> From<Apply<I>> for RawApply
55+
where
56+
I: IntoIterator<Item = (H256, H256)>,
57+
{
58+
fn from(value: Apply<I>) -> Self {
59+
match value {
60+
Apply::Modify {
61+
address,
62+
basic,
63+
code,
64+
storage,
65+
reset_storage,
66+
} => Self::Modify {
67+
address,
68+
basic,
69+
code,
70+
storage: storage.into_iter().collect(),
71+
reset_storage,
72+
},
73+
Apply::Delete { address } => Self::Delete { address },
74+
}
75+
}
76+
}
77+
78+
#[derive(Clone, Debug, Serialize, Deserialize)]
79+
pub enum RawSpec {
80+
Frontier,
81+
Homestead,
82+
TangerineWhistle,
83+
SpuriousDragon,
84+
Byzantium,
85+
Constantinople,
86+
Petersburg,
87+
Istanbul,
88+
Berlin,
89+
London,
90+
Merge,
91+
Shanghai,
92+
Cancun,
93+
Prague,
94+
Osaka,
95+
}
96+
97+
impl From<Spec> for RawSpec {
98+
fn from(spec: Spec) -> Self {
99+
match spec {
100+
Spec::Frontier => Self::Frontier,
101+
Spec::Homestead => Self::Homestead,
102+
Spec::Tangerine => Self::TangerineWhistle,
103+
Spec::SpuriousDragon => Self::SpuriousDragon,
104+
Spec::Byzantium => Self::Byzantium,
105+
Spec::Constantinople => Self::Constantinople,
106+
Spec::Petersburg => Self::Petersburg,
107+
Spec::Istanbul => Self::Istanbul,
108+
Spec::Berlin => Self::Berlin,
109+
Spec::London => Self::London,
110+
Spec::Merge => Self::Merge,
111+
Spec::Shanghai => Self::Shanghai,
112+
Spec::Cancun => Self::Cancun,
113+
Spec::Prague => Self::Prague,
114+
Spec::Osaka => Self::Osaka,
115+
}
116+
}
117+
}
118+
119+
#[derive(Clone, Debug)]
120+
pub struct TestBench {
121+
pub name: String,
122+
pub spec: Spec,
123+
pub elapsed: Duration,
21124
}
22125

23126
impl TestExecutionResult {
@@ -28,12 +131,89 @@ impl TestExecutionResult {
28131
total: 0,
29132
failed: 0,
30133
failed_tests: Vec::new(),
134+
bench: Vec::new(),
135+
dump_successful_txs: Vec::new(),
31136
}
32137
}
33138

34139
pub fn merge(&mut self, src: Self) {
35140
self.failed_tests.extend(src.failed_tests);
36141
self.total += src.total;
37142
self.failed += src.failed;
143+
144+
for bench in src.bench {
145+
self.set_benchmark(bench);
146+
}
147+
148+
self.dump_successful_txs.extend(src.dump_successful_txs);
149+
}
150+
151+
pub fn set_benchmark(&mut self, bench: TestBench) {
152+
if self.bench.is_empty() {
153+
self.bench.push(bench);
154+
return;
155+
}
156+
157+
if self.bench.len() < 100 {
158+
self.bench.push(bench);
159+
return;
160+
}
161+
162+
// If has smaller elapsed than all existing then skip
163+
if !self.bench.iter().any(|b| bench.elapsed > b.elapsed) {
164+
return;
165+
}
166+
167+
let mut min_idx = 0usize;
168+
let mut min_elapsed = self.bench[0].elapsed;
169+
for (i, b) in self.bench.iter().enumerate().skip(1) {
170+
if b.elapsed < min_elapsed {
171+
min_elapsed = b.elapsed;
172+
min_idx = i;
173+
}
174+
}
175+
176+
if bench.elapsed > min_elapsed {
177+
self.bench[min_idx] = bench;
178+
}
179+
}
180+
181+
pub fn print_bench(&self) {
182+
let mut items = self.bench.clone();
183+
items.sort_unstable_by(|a, b| b.elapsed.cmp(&a.elapsed));
184+
185+
if items.is_empty() {
186+
return;
187+
}
188+
189+
let formatted: Vec<(String, String, String)> = items
190+
.iter()
191+
.map(|b| {
192+
let elapsed_str = format!("{:.6}s", b.elapsed.as_secs_f64());
193+
let spec_str = format!("{:?}", b.spec);
194+
let name_str = b.name.clone();
195+
(elapsed_str, spec_str, name_str)
196+
})
197+
.collect();
198+
199+
let mut w_elapsed = 0usize;
200+
let mut w_spec = 0usize;
201+
let mut w_name = 0usize;
202+
for (e, s, n) in &formatted {
203+
w_elapsed = w_elapsed.max(e.len());
204+
w_spec = w_spec.max(s.len());
205+
w_name = w_name.max(n.len());
206+
}
207+
208+
let bold_on = "\x1b[1m";
209+
let gray_on = "\x1b[90m";
210+
let reset = "\x1b[0m";
211+
212+
for (e, s, n) in formatted {
213+
let e_pad = format!("{e:w_elapsed$}");
214+
let s_pad = format!("{s:w_spec$}");
215+
let n_pad = format!("{n:w_name$}");
216+
println!("{bold_on}{e_pad}{reset} {gray_on}{s_pad}{reset} {n_pad}",);
217+
}
38218
}
39219
}

evm-tests/src/main.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,16 @@ fn main() -> Result<(), String> {
8282
arg!(-p --print_state "Print state when the test fails")
8383
.default_value("false")
8484
.action(ArgAction::SetTrue),
85+
)
86+
.arg(
87+
arg!(--dump_successful_tx <FILE_NAME> "Optional file name to dump all successful transactions")
88+
.required(false)
89+
.value_parser(value_parser!(PathBuf)),
90+
)
91+
.arg(
92+
arg!(--slow_tests "Print state slow tests")
93+
.default_value("false")
94+
.action(ArgAction::SetTrue),
8595
),
8696
)
8797
.get_matches();
@@ -92,6 +102,8 @@ fn main() -> Result<(), String> {
92102
verbose_failed: matches.get_flag("verbose_failed"),
93103
very_verbose: false,
94104
print_state: false,
105+
print_slow: false,
106+
dump_transactions: None,
95107
};
96108
let mut tests_result = TestExecutionResult::new();
97109
for src_path in matches.get_many::<PathBuf>("PATH").unwrap() {
@@ -122,6 +134,8 @@ fn main() -> Result<(), String> {
122134
verbose_failed: matches.get_flag("verbose_failed"),
123135
very_verbose: matches.get_flag("very_verbose"),
124136
print_state: matches.get_flag("print_state"),
137+
print_slow: matches.get_flag("slow_tests"),
138+
dump_transactions: matches.get_one::<PathBuf>("dump_successful_tx").cloned(),
125139
};
126140
let mut tests_result = TestExecutionResult::new();
127141
for src_path in matches.get_many::<PathBuf>("PATH").unwrap() {
@@ -150,9 +164,26 @@ fn main() -> Result<(), String> {
150164
}
151165
println!("\nTOTAL: {}", tests_result.total);
152166
println!("FAILED: {}\n", tests_result.failed);
167+
153168
if tests_result.failed != 0 {
154169
return Err(format!("tests failed: {}", tests_result.failed));
155170
}
171+
172+
if verbose_output.print_slow {
173+
println!("SLOW TESTS:");
174+
tests_result.print_bench();
175+
}
176+
177+
if let Some(dunp_to_file) = verbose_output.dump_transactions {
178+
let txs = tests_result.dump_successful_txs;
179+
let data = serde_json::to_string(&txs).expect("JSON serialization failed");
180+
fs::write(&dunp_to_file, data).expect("Unable to write file");
181+
println!(
182+
"TEST SUCCESSFUL TRANSACTIONS DUMPED TO: {} [{}]",
183+
dunp_to_file.display(),
184+
txs.len()
185+
);
186+
}
156187
}
157188
Ok(())
158189
}

0 commit comments

Comments
 (0)