1use cfx_types::parse_hex_string;
2use clap::{ArgMatches, Args};
3use client::{
4 configuration::Configuration,
5 state_dump::{dump_whole_state, iterate_dump_whole_state, StateDumpConfig},
6};
7use parking_lot::{Condvar, Mutex};
8use serde_json;
9use std::{collections::HashMap, fs, path::Path, sync::Arc};
10
11#[derive(Args, Debug)]
12pub struct DumpCommand {
13 #[arg(
21 id = "limit",
22 long = "limit",
23 value_name = "NUM",
24 default_value = "0"
25 )]
26 limit: u64,
27 #[arg(id = "block", long = "block", value_name = "NUM")]
29 block: Option<u64>,
30 #[arg(id = "nocode", long = "nocode")]
32 no_code: bool,
33 #[arg(id = "nostorage", long = "nostorage")]
35 no_storage: bool,
36 #[arg(
38 id = "start",
39 long = "start",
40 value_name = "String",
41 default_value = "0x0000000000000000000000000000000000000000"
42 )]
43 start: String,
44 #[arg(id = "output", long = "output", value_name = "PATH")]
46 output: Option<String>,
47 #[arg(id = "multifile", long = "multifile")]
49 multi_file: bool,
50}
51
52impl DumpCommand {
53 pub fn parse(matches: &ArgMatches) -> Result<Self, String> {
54 let output = matches.get_one::<String>("output").cloned();
55 Ok(Self {
56 block: matches.get_one::<u64>("block").cloned(),
57 limit: matches.get_one::<u64>("limit").cloned().unwrap_or(0),
60 no_code: matches.get_flag("nocode"),
61 no_storage: matches.get_flag("nostorage"),
62 start: matches.get_one::<String>("start").cloned().unwrap_or(
63 "0x0000000000000000000000000000000000000000".to_string(),
64 ),
65 output,
66 multi_file: matches.get_flag("multifile"),
67 })
68 }
69
70 fn get_state_dump_config(
71 &self, output_path: &str,
72 ) -> Result<StateDumpConfig, String> {
73 let start_address = parse_hex_string(&self.start)
74 .map_err(|e| format!("Invalid address: {}", e))?;
75 Ok(StateDumpConfig {
76 start_address,
77 limit: self.limit,
78 block: self.block,
79 no_code: self.no_code,
80 no_storage: self.no_storage,
81 out_put_path: output_path.to_string(),
82 })
83 }
84
85 pub fn execute(&self, conf: &mut Configuration) -> Result<String, String> {
86 let output_path = match self.output {
88 Some(ref path) => path,
89 None => {
90 "./dump" }
92 };
93
94 if !Path::new(output_path).exists() {
96 fs::create_dir_all(output_path).map_err(|e| {
97 format!("Failed to create output directory: {}", e)
98 })?;
99 }
100
101 let exit = Arc::new((Mutex::new(false), Condvar::new()));
102 let config = self.get_state_dump_config(output_path)?;
103
104 let _total_accounts = if self.multi_file {
105 let state_root = iterate_dump_whole_state(
107 conf,
108 exit,
109 &config,
110 |account_state| {
111 let address =
112 account_state.address.expect("address is not set");
113 let filename = format!("{:?}.json", address);
114 let file_path = Path::new(output_path).join(&filename);
115
116 let json_content =
118 serde_json::to_string_pretty(&account_state)
119 .map_err(|e| {
120 format!(
121 "Failed to serialize account state for {}: {}",
122 address, e
123 )
124 })
125 .expect("Failed to serialize account state");
126
127 fs::write(&file_path, json_content)
129 .map_err(|e| {
130 format!(
131 "Failed to write file {}: {}",
132 file_path.display(),
133 e
134 )
135 })
136 .expect("Failed to write file");
137 },
138 )?;
139
140 let mut meta_info = HashMap::new();
142 meta_info.insert("root".to_string(), state_root);
143 let meta_file_path = Path::new(output_path).join("meta.json");
144 let meta_content = serde_json::to_string_pretty(&meta_info)
145 .map_err(|e| format!("Failed to serialize state: {}", e))?;
146 fs::write(&meta_file_path, meta_content)
147 .map_err(|e| format!("Failed to write meta file: {}", e))?;
148 0
149 } else {
150 let state = dump_whole_state(conf, exit, &config)?;
151 let total_accounts = state.accounts.len();
152 let file_path = Path::new(output_path).join("state.json");
154 let json_content = serde_json::to_string_pretty(&state)
155 .map_err(|e| format!("Failed to serialize state: {}", e))?;
156 fs::write(&file_path, json_content).map_err(|e| {
157 format!("Failed to write file {}: {}", file_path.display(), e)
158 })?;
159 total_accounts
160 };
161
162 Ok(format!(
163 "Dumped account state to output directory: {}",
164 output_path
165 ))
166 }
167}