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 reth_storage_api::StorageSettingsCache;
9use std::time::{Duration, Instant};
10use tracing::info;
11
12const LOG_INTERVAL: Duration = Duration::from_secs(5);
14
15#[derive(Parser, Debug)]
17pub struct Command {
18 address: Address,
20}
21
22impl Command {
23 pub fn execute<N: NodeTypesWithDB>(self, tool: &DbTool<N>) -> eyre::Result<()> {
25 let address = self.address;
26 let use_hashed_state = tool.provider_factory.cached_storage_settings().use_hashed_state();
27
28 let (slot_count, storage_size) = if use_hashed_state {
29 let hashed_address = keccak256(address);
30 tool.provider_factory.db_ref().view(|tx| {
31 let mut cursor = tx.cursor_dup_read::<tables::HashedStorages>()?;
32 let mut count = 0usize;
33 let mut total_value_bytes = 0usize;
34 let mut last_log = Instant::now();
35
36 let walker = cursor.walk_dup(Some(hashed_address), None)?;
37 for entry in walker {
38 let (_, storage_entry) = entry?;
39 count += 1;
40 let mut buf = Vec::new();
41 let entry_len = storage_entry.to_compact(&mut buf);
42 total_value_bytes += entry_len;
43
44 if last_log.elapsed() >= LOG_INTERVAL {
45 info!(
46 target: "reth::cli",
47 address = %address,
48 slots = count,
49 key = %storage_entry.key,
50 "Processing hashed storage slots"
51 );
52 last_log = Instant::now();
53 }
54 }
55
56 let total_size = if count > 0 { 32 + total_value_bytes } else { 0 };
57
58 Ok::<_, eyre::Report>((count, total_size))
59 })??
60 } else {
61 tool.provider_factory.db_ref().view(|tx| {
62 let mut cursor = tx.cursor_dup_read::<tables::PlainStorageState>()?;
63 let mut count = 0usize;
64 let mut total_value_bytes = 0usize;
65 let mut last_log = Instant::now();
66
67 let walker = cursor.walk_dup(Some(address), None)?;
69 for entry in walker {
70 let (_, storage_entry) = entry?;
71 count += 1;
72 let mut buf = Vec::new();
73 let entry_len = storage_entry.to_compact(&mut buf);
75 total_value_bytes += entry_len;
76
77 if last_log.elapsed() >= LOG_INTERVAL {
78 info!(
79 target: "reth::cli",
80 address = %address,
81 slots = count,
82 key = %storage_entry.key,
83 "Processing storage slots"
84 );
85 last_log = Instant::now();
86 }
87 }
88
89 let total_size = if count > 0 { 20 + total_value_bytes } else { 0 };
91
92 Ok::<_, eyre::Report>((count, total_size))
93 })??
94 };
95
96 let hashed_address = keccak256(address);
97
98 println!("Account: {address}");
99 println!("Hashed address: {hashed_address}");
100 println!("Storage slots: {slot_count}");
101 if use_hashed_state {
102 println!("Hashed storage size: {} (estimated)", human_bytes(storage_size as f64));
103 } else {
104 let hashed_size_estimate = if slot_count > 0 { storage_size + 12 } else { 0 };
106 let total_estimate = storage_size + hashed_size_estimate;
107 println!("Plain storage size: {} (estimated)", human_bytes(storage_size as f64));
108 println!(
109 "Hashed storage size: {} (estimated)",
110 human_bytes(hashed_size_estimate as f64)
111 );
112 println!("Total estimated size: {}", human_bytes(total_estimate as f64));
113 }
114
115 Ok(())
116 }
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122
123 #[test]
124 fn parse_address_arg() {
125 let cmd = Command::try_parse_from([
126 "account-storage",
127 "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
128 ])
129 .unwrap();
130 assert_eq!(
131 cmd.address,
132 "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045".parse::<Address>().unwrap()
133 );
134 }
135}