Skip to content

Commit 065ea15

Browse files
authored
Added purge subcommand to purge beacon chain db (#971)
1 parent 869b062 commit 065ea15

File tree

4 files changed

+94
-4
lines changed

4 files changed

+94
-4
lines changed

beacon_node/beacon_chain/src/builder.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ where
221221
.get::<PersistedBeaconChain>(&Hash256::from_slice(&BEACON_CHAIN_DB_KEY))
222222
.map_err(|e| format!("DB error when reading persisted beacon chain: {:?}", e))?
223223
.ok_or_else(|| {
224-
"No persisted beacon chain found in store. Try deleting the .lighthouse/beacon dir."
224+
"No persisted beacon chain found in store. Try purging the beacon chain database."
225225
.to_string()
226226
})?;
227227

beacon_node/client/src/config.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use beacon_chain::builder::PUBKEY_CACHE_FILENAME;
12
use network::NetworkConfig;
23
use serde_derive::{Deserialize, Serialize};
34
use std::fs;
@@ -11,6 +12,9 @@ const TESTNET_SPEC_CONSTANTS: &str = "minimal";
1112
/// Default directory name for the freezer database under the top-level data dir.
1213
const DEFAULT_FREEZER_DB_DIR: &str = "freezer_db";
1314

15+
/// Trap file indicating if chain_db was purged
16+
const CHAIN_DB_PURGED_TRAP_FILE: &str = ".db_purged";
17+
1418
/// Defines how the client should initialize the `BeaconChain` and other components.
1519
#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
1620
pub enum ClientGenesis {
@@ -97,6 +101,68 @@ impl Config {
97101
.map(|data_dir| data_dir.join(&self.db_name))
98102
}
99103

104+
/// Get the path of the chain db purged trap file
105+
pub fn get_db_purged_trap_file_path(&self) -> Option<PathBuf> {
106+
self.get_data_dir()
107+
.map(|data_dir| data_dir.join(CHAIN_DB_PURGED_TRAP_FILE))
108+
}
109+
110+
/// returns whether chain_db was recently purged
111+
pub fn chain_db_was_purged(&self) -> bool {
112+
self.get_db_purged_trap_file_path()
113+
.map_or(false, |trap_file| trap_file.exists())
114+
}
115+
116+
/// purges the chain_db and creates trap file
117+
pub fn purge_chain_db(&self) -> Result<(), String> {
118+
// create the trap file
119+
let trap_file = self
120+
.get_db_purged_trap_file_path()
121+
.ok_or("Failed to get trap file path".to_string())?;
122+
fs::File::create(trap_file)
123+
.map_err(|err| format!("Failed to create trap file: {}", err))?;
124+
125+
// remove the chain_db
126+
fs::remove_dir_all(
127+
self.get_db_path()
128+
.ok_or("Failed to get db_path".to_string())?,
129+
)
130+
.map_err(|err| format!("Failed to remove chain_db: {}", err))?;
131+
132+
// remove the freezer db
133+
fs::remove_dir_all(
134+
self.get_freezer_db_path()
135+
.ok_or("Failed to get freezer db path".to_string())?,
136+
)
137+
.map_err(|err| format!("Failed to remove chain_db: {}", err))?;
138+
139+
// also need to remove pubkey cache file if it exists
140+
let pubkey_cache_file = self
141+
.get_data_dir()
142+
.map(|data_dir| data_dir.join(PUBKEY_CACHE_FILENAME))
143+
.ok_or("Failed to get pubkey cache file path".to_string())?;
144+
if !pubkey_cache_file.exists() {
145+
return Ok(());
146+
}
147+
fs::remove_file(pubkey_cache_file)
148+
.map_err(|err| format!("Failed to remove pubkey cache: {}", err))?;
149+
150+
Ok(())
151+
}
152+
153+
/// cleans up purge_db trap file
154+
pub fn cleanup_after_purge_db(&self) -> Result<(), String> {
155+
let trap_file = self
156+
.get_db_purged_trap_file_path()
157+
.ok_or("Failed to get trap file path".to_string())?;
158+
if !trap_file.exists() {
159+
return Ok(());
160+
}
161+
fs::remove_file(trap_file).map_err(|err| format!("Failed to remove trap file: {}", err))?;
162+
163+
Ok(())
164+
}
165+
100166
/// Get the database path, creating it if necessary.
101167
pub fn create_db_path(&self) -> Result<PathBuf, String> {
102168
let db_path = self

beacon_node/src/cli.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,4 +309,12 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
309309
.help("A file from which to read the state"))
310310
)
311311
)
312+
/*
313+
* The "purge" sub-command.
314+
*
315+
* Allows user to purge beacon database
316+
*/
317+
.subcommand(SubCommand::with_name("purge")
318+
.about("Purge the beacon chain database.")
319+
)
312320
}

beacon_node/src/config.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -189,15 +189,27 @@ pub fn get_config<E: EthSpec>(
189189
("testnet", Some(sub_cmd_args)) => {
190190
process_testnet_subcommand(&mut client_config, &eth2_config, sub_cmd_args)?
191191
}
192+
("purge", _) => {
193+
client_config.purge_chain_db()?;
194+
println!("Successfully purged chain db");
195+
std::process::exit(0);
196+
}
192197
// No sub-command assumes a resume operation.
193198
_ => {
194199
// If no primary subcommand was given, start the beacon chain from an existing
195200
// database.
196201
client_config.genesis = ClientGenesis::Resume;
197202

203+
let db_path_exists: bool = match client_config.get_db_path() {
204+
Some(path) => path.exists(),
205+
None => false,
206+
};
207+
198208
// Whilst there is no large testnet or mainnet force the user to specify how they want
199209
// to start a new chain (e.g., from a genesis YAML file, another node, etc).
200-
if !client_config.data_dir.exists() {
210+
if !client_config.data_dir.exists()
211+
|| (!db_path_exists && client_config.chain_db_was_purged())
212+
{
201213
info!(
202214
log,
203215
"Starting from an empty database";
@@ -392,7 +404,8 @@ fn init_new_client<E: EthSpec>(
392404
///
393405
/// Returns an error if `self.data_dir` already exists.
394406
pub fn create_new_datadir(client_config: &ClientConfig, eth2_config: &Eth2Config) -> Result<()> {
395-
if client_config.data_dir.exists() {
407+
let rebuild_db = client_config.chain_db_was_purged();
408+
if client_config.data_dir.exists() && !rebuild_db {
396409
return Err(format!(
397410
"Data dir already exists at {:?}",
398411
client_config.data_dir
@@ -407,7 +420,9 @@ pub fn create_new_datadir(client_config: &ClientConfig, eth2_config: &Eth2Config
407420
($file: ident, $variable: ident) => {
408421
let file = client_config.data_dir.join($file);
409422
if file.exists() {
410-
return Err(format!("Datadir is not clean, {} exists.", $file));
423+
if !rebuild_db {
424+
return Err(format!("Datadir is not clean, {} exists.", $file));
425+
}
411426
} else {
412427
// Write the onfig to a TOML file in the datadir.
413428
write_to_file(client_config.data_dir.join($file), $variable)
@@ -418,6 +433,7 @@ pub fn create_new_datadir(client_config: &ClientConfig, eth2_config: &Eth2Config
418433

419434
write_to_file!(CLIENT_CONFIG_FILENAME, client_config);
420435
write_to_file!(ETH2_CONFIG_FILENAME, eth2_config);
436+
client_config.cleanup_after_purge_db()?;
421437

422438
Ok(())
423439
}

0 commit comments

Comments
 (0)