Skip to content

Commit 9bcf6d6

Browse files
committed
add dump subcommand which can dump all espace account as json file
1 parent fbcf102 commit 9bcf6d6

File tree

18 files changed

+716
-1
lines changed

18 files changed

+716
-1
lines changed

Cargo.lock

Lines changed: 71 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,7 @@ serde = { version = "1.0", features = [
272272
], default-features = false }
273273
serde_json = { version = "1.0", default-features = false, features = ["alloc"] }
274274
serde_derive = { version = "1.0", default-features = false }
275+
serde_with = { version = "3", default-features = false, features = ["macros"] }
275276
hex = "0.4"
276277
rustc-hex = "2.1"
277278
hex-literal = "1.0"

bins/conflux/src/cli.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::command::dump::DumpCommand;
12
use clap::{Args, Parser, Subcommand, ValueEnum};
23

34
/// Conflux client
@@ -267,6 +268,9 @@ pub enum Commands {
267268
/// Manage accounts
268269
#[command(subcommand_required = true, arg_required_else_help = true)]
269270
Account(AccountSubcommands),
271+
/// Dump eSpace account state at a given block number
272+
#[command(subcommand_required = false, arg_required_else_help = false)]
273+
Dump(DumpCommand),
270274
/// RPC based subcommands to query blockchain information and send
271275
/// transactions
272276
#[command(subcommand_required = true, arg_required_else_help = true)]

bins/conflux/src/command/dump.rs

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
use cfx_types::Address;
2+
use clap::{ArgMatches, Args};
3+
use client::{
4+
configuration::Configuration,
5+
state_dump::{state_dump, StateDumpConfig},
6+
};
7+
use parking_lot::{Condvar, Mutex};
8+
use std::{fs, path::Path, str::FromStr, sync::Arc};
9+
10+
#[derive(Args, Debug)]
11+
pub struct DumpCommand {
12+
/// Include accounts for which we don't have the address (missing preimage)
13+
// #[arg(id = "incompletes", long = "incompletes")]
14+
// incompletes: bool,
15+
/// Print streaming JSON iteratively, delimited by newlines
16+
// #[arg(id = "iterative", long = "iterative", default_value = "true")]
17+
// iterative: bool,
18+
/// Max number of elements (0 = no limit)
19+
#[arg(
20+
id = "limit",
21+
long = "limit",
22+
value_name = "NUM",
23+
default_value = "0"
24+
)]
25+
limit: u64,
26+
/// Target block number, if not specified, the latest block will be used
27+
#[arg(id = "block", long = "block", value_name = "NUM")]
28+
block: Option<u64>,
29+
/// Exclude contract code (save db lookups)
30+
#[arg(id = "nocode", long = "nocode")]
31+
no_code: bool,
32+
/// Exclude storage entries (save db lookups)
33+
#[arg(id = "nostorage", long = "nostorage")]
34+
no_storage: bool,
35+
/// Start position address
36+
#[arg(
37+
id = "start",
38+
long = "start",
39+
value_name = "String",
40+
default_value = "0x0000000000000000000000000000000000000000"
41+
)]
42+
start: String,
43+
/// Path to the output folder (default: ./dump)
44+
#[arg(id = "output", long = "output", value_name = "PATH")]
45+
output: Option<String>,
46+
}
47+
48+
impl DumpCommand {
49+
pub fn parse(matches: &ArgMatches) -> Result<Self, String> {
50+
let output = matches.get_one::<String>("output").cloned();
51+
Ok(Self {
52+
block: matches.get_one::<u64>("block").cloned(),
53+
// incompletes: matches.get_flag("incompletes"),
54+
// iterative: matches.get_flag("iterative"),
55+
limit: matches.get_one::<u64>("limit").cloned().unwrap_or(0),
56+
no_code: matches.get_flag("nocode"),
57+
no_storage: matches.get_flag("nostorage"),
58+
start: matches.get_one::<String>("start").cloned().unwrap_or(
59+
"0x0000000000000000000000000000000000000000".to_string(),
60+
),
61+
output,
62+
})
63+
}
64+
65+
fn get_state_dump_config(&self) -> Result<StateDumpConfig, String> {
66+
let address_str = self.start.strip_prefix("0x").unwrap_or(&self.start);
67+
let start_address = Address::from_str(address_str)
68+
.map_err(|e| format!("Invalid address: {}", e))?;
69+
Ok(StateDumpConfig {
70+
start_address,
71+
limit: self.limit,
72+
block: self.block,
73+
no_code: self.no_code,
74+
no_storage: self.no_storage,
75+
})
76+
}
77+
78+
pub fn execute(&self, conf: &mut Configuration) -> Result<String, String> {
79+
// Determine output directory
80+
let output_path = match self.output {
81+
Some(ref path) => path,
82+
None => {
83+
"./dump" // Default to "./dump" if no output specified
84+
}
85+
};
86+
87+
// Ensure the directory exists
88+
if !Path::new(output_path).exists() {
89+
fs::create_dir_all(output_path).map_err(|e| {
90+
format!("Failed to create output directory: {}", e)
91+
})?;
92+
}
93+
94+
let exit = Arc::new((Mutex::new(false), Condvar::new()));
95+
96+
let config = self.get_state_dump_config()?;
97+
let accounts = state_dump(conf, exit, &config)?;
98+
99+
// TODO export to json file
100+
101+
println!("accounts: {:?}", accounts.len());
102+
for account in accounts {
103+
println!("{:?}", account.address);
104+
}
105+
106+
Ok(format!(
107+
"dump account state at block number: {:?} to output directory: {}",
108+
self.block, output_path
109+
))
110+
}
111+
}

bins/conflux/src/command/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@
33
// See http://www.gnu.org/licenses/
44

55
pub mod account;
6+
pub mod dump;
67
pub mod helpers;
78
pub mod rpc;

bins/conflux/src/main.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@ use client::{
1919
full::FullClient,
2020
light::LightClient,
2121
};
22-
use command::account::{AccountCmd, ImportAccounts, ListAccounts, NewAccount};
22+
use command::{
23+
account::{AccountCmd, ImportAccounts, ListAccounts, NewAccount},
24+
dump::DumpCommand,
25+
};
2326
use log::{info, LevelFilter};
2427
use log4rs::{
2528
append::{console::ConsoleAppender, file::FileAppender},
@@ -144,6 +147,16 @@ fn handle_sub_command(matches: &ArgMatches) -> Result<Option<String>, String> {
144147
return Ok(Some(execute_output));
145148
}
146149

150+
// dump sub-commands
151+
if let Some(("dump", dump_matches)) = matches.subcommand() {
152+
let dump_cmd = DumpCommand::parse(dump_matches).map_err(|e| {
153+
format!("Failed to parse dump command arguments: {}", e)
154+
})?;
155+
let mut conf = Configuration::parse(&matches)?;
156+
let execute_output = dump_cmd.execute(&mut conf)?;
157+
return Ok(Some(execute_output));
158+
}
159+
147160
// general RPC commands
148161
let mut subcmd_matches = matches;
149162
while let Some(m) = subcmd_matches.subcommand() {

crates/cfx_types/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ pub use ethereum_types::{
77
H160, H256, H512, H520, H64, U128, U256, U512, U64,
88
};
99

10+
pub type StorageKey = H256;
11+
pub type StorageValue = U256;
12+
1013
mod space;
1114
pub use space::{Space, SpaceMap};
1215

crates/client/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@ mod node_types;
1313
pub mod rpc;
1414
pub use cfx_config as configuration;
1515
pub use node_types::{archive, full, light};
16+
pub mod state_dump;

0 commit comments

Comments
 (0)