1
1
use std:: path:: PathBuf ;
2
2
3
+ use alloy:: primitives:: { keccak256, B256 } ;
3
4
use anyhow:: bail;
4
5
use clap:: Parser ;
5
- use e2store:: era2:: { AccountOrStorageEntry , Era2Reader } ;
6
+ use e2store:: era2:: { self , AccountOrStorageEntry , Era2Reader } ;
6
7
use tracing:: info;
7
8
8
9
#[ derive( Debug , Parser ) ]
9
10
#[ command(
10
11
name = "Era2 stats" ,
11
- about = "Reads the era2 file and prints stats about it."
12
+ about = "Reads the era2 file, validates the format, and prints stats about it."
12
13
) ]
13
14
struct Config {
14
15
#[ arg( help = "The path to the era2 file." ) ]
@@ -23,12 +24,12 @@ struct Stats {
23
24
storage_item_count : usize ,
24
25
}
25
26
26
- /// Reads the era2 file and prints stats about it.
27
+ /// Reads the era2 file, validates the format, and prints stats about it.
27
28
///
28
29
/// It can be run with following command:
29
30
///
30
31
/// ```bash
31
- /// cargo run -p e2store --bin era2-stats --features era2-stats-binary -- <path>
32
+ /// cargo run --release - p e2store --bin era2-stats --features era2-stats-binary -- <path>
32
33
/// ```
33
34
fn main ( ) -> anyhow:: Result < ( ) > {
34
35
trin_utils:: log:: init_tracing_logger ( ) ;
@@ -41,18 +42,34 @@ fn main() -> anyhow::Result<()> {
41
42
reader. header. header. number
42
43
) ;
43
44
45
+ let mut last_address_hash = B256 :: ZERO ;
46
+
44
47
let mut stats = Stats :: default ( ) ;
45
48
while let Some ( entry) = reader. next ( ) {
46
49
let AccountOrStorageEntry :: Account ( account) = entry else {
47
50
bail ! ( "Expected account entry" ) ;
48
51
} ;
49
52
stats. account_count += 1 ;
50
53
54
+ assert ! (
55
+ account. address_hash > last_address_hash,
56
+ "Expected increasing order of accounts: {last_address_hash} is before {}" ,
57
+ account. address_hash
58
+ ) ;
59
+ last_address_hash = account. address_hash ;
60
+
61
+ assert_eq ! (
62
+ keccak256( & account. bytecode) ,
63
+ account. account_state. code_hash,
64
+ "Invalid code hash" ,
65
+ ) ;
66
+
51
67
if !account. bytecode . is_empty ( ) {
52
68
stats. bytecode_count += 1 ;
53
69
}
54
70
55
- for _ in 0 ..account. storage_count {
71
+ let mut last_storage_item = B256 :: ZERO ;
72
+ for index in 0 ..account. storage_count {
56
73
let Some ( AccountOrStorageEntry :: Storage ( storage) ) = reader. next ( ) else {
57
74
bail ! (
58
75
"Expected storage entry for account: {}" ,
@@ -61,6 +78,28 @@ fn main() -> anyhow::Result<()> {
61
78
} ;
62
79
stats. storage_entry_count += 1 ;
63
80
stats. storage_item_count += storage. len ( ) ;
81
+
82
+ // Check that every storage entry is full (has 10M items), except the last one.
83
+ if index + 1 < account. storage_count {
84
+ assert_eq ! (
85
+ storage. len( ) ,
86
+ era2:: MAX_STORAGE_ITEMS ,
87
+ "Non-final storage entry (address_hash: {}, index: {index}/{}) should be full, but has {} items instead" ,
88
+ account. address_hash,
89
+ account. storage_count,
90
+ storage. len( ) ,
91
+ ) ;
92
+ }
93
+
94
+ for item in storage. iter ( ) {
95
+ assert ! (
96
+ item. storage_index_hash > last_storage_item,
97
+ "Expected increasing order of storage items (address hash: {}): {last_storage_item} is before {}" ,
98
+ account. address_hash,
99
+ item. storage_index_hash,
100
+ ) ;
101
+ last_storage_item = item. storage_index_hash ;
102
+ }
64
103
}
65
104
66
105
if stats. account_count % 1_000_000 == 0 {
0 commit comments