reth_cli_commands/db/
account_storage.rs1use alloy_primitives::{keccak256, Address};
2use clap::Parser;
3use human_bytes::human_bytes;
4use reth_codecs::Compact;
5use reth_db_api::{cursor::DbDupCursorRO, database::Database, tables, transaction::DbTx};
6use reth_db_common::DbTool;
7use reth_node_builder::NodeTypesWithDB;
8use std::time::{Duration, Instant};
9use tracing::info;
10
11const LOG_INTERVAL: Duration = Duration::from_secs(5);
13
14#[derive(Parser, Debug)]
16pub struct Command {
17 address: Address,
19}
20
21impl Command {
22 pub fn execute<N: NodeTypesWithDB>(self, tool: &DbTool<N>) -> eyre::Result<()> {
24 let address = self.address;
25 let (slot_count, plain_size) = tool.provider_factory.db_ref().view(|tx| {
26 let mut cursor = tx.cursor_dup_read::<tables::PlainStorageState>()?;
27 let mut count = 0usize;
28 let mut total_value_bytes = 0usize;
29 let mut last_log = Instant::now();
30
31 let walker = cursor.walk_dup(Some(address), None)?;
33 for entry in walker {
34 let (_, storage_entry) = entry?;
35 count += 1;
36 let mut buf = Vec::new();
38 let entry_len = storage_entry.to_compact(&mut buf);
39 total_value_bytes += entry_len;
40
41 if last_log.elapsed() >= LOG_INTERVAL {
42 info!(
43 target: "reth::cli",
44 address = %address,
45 slots = count,
46 key = %storage_entry.key,
47 "Processing storage slots"
48 );
49 last_log = Instant::now();
50 }
51 }
52
53 let total_size = if count > 0 { 20 + total_value_bytes } else { 0 };
55
56 Ok::<_, eyre::Report>((count, total_size))
57 })??;
58
59 let hashed_size_estimate = if slot_count > 0 { plain_size + 12 } else { 0 };
61 let total_estimate = plain_size + hashed_size_estimate;
62
63 let hashed_address = keccak256(address);
64
65 println!("Account: {address}");
66 println!("Hashed address: {hashed_address}");
67 println!("Storage slots: {slot_count}");
68 println!("Plain storage size: {} (estimated)", human_bytes(plain_size as f64));
69 println!("Hashed storage size: {} (estimated)", human_bytes(hashed_size_estimate as f64));
70 println!("Total estimated size: {}", human_bytes(total_estimate as f64));
71
72 Ok(())
73 }
74}
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79
80 #[test]
81 fn parse_address_arg() {
82 let cmd = Command::try_parse_from([
83 "account-storage",
84 "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
85 ])
86 .unwrap();
87 assert_eq!(
88 cmd.address,
89 "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045".parse::<Address>().unwrap()
90 );
91 }
92}