Skip to content

Commit d47f185

Browse files
Merge pull request #39 from georgepisaltu/remove-block
Add `remove-block` subcommand
2 parents 5d29738 + 431a6ee commit d47f185

File tree

5 files changed

+816
-1
lines changed

5 files changed

+816
-1
lines changed

src/main.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use log::error;
1111

1212
use subcommands::{
1313
archive, check, execution_results_summary, extract_slice, latest_block_summary,
14-
purge_signatures, trie_compact, unsparse, Error,
14+
purge_signatures, remove_block, trie_compact, unsparse, Error,
1515
};
1616

1717
const LOGGING: &str = "logging";
@@ -23,6 +23,7 @@ enum DisplayOrder {
2323
ExtractSlice,
2424
LatestBlock,
2525
PurgeSignatures,
26+
RemoveBlock,
2627
TrieCompact,
2728
Unsparse,
2829
}
@@ -44,6 +45,7 @@ fn cli() -> Command<'static> {
4445
.subcommand(purge_signatures::command(
4546
DisplayOrder::PurgeSignatures as usize,
4647
))
48+
.subcommand(remove_block::command(DisplayOrder::RemoveBlock as usize))
4749
.subcommand(trie_compact::command(DisplayOrder::TrieCompact as usize))
4850
.subcommand(unsparse::command(DisplayOrder::Unsparse as usize))
4951
.arg(
@@ -92,6 +94,7 @@ fn main() {
9294
latest_block_summary::run(matches).map_err(Error::from)
9395
}
9496
purge_signatures::COMMAND_NAME => purge_signatures::run(matches).map_err(Error::from),
97+
remove_block::COMMAND_NAME => remove_block::run(matches).map_err(Error::from),
9598
trie_compact::COMMAND_NAME => trie_compact::run(matches).map_err(Error::from),
9699
unsparse::COMMAND_NAME => unsparse::run(matches).map_err(Error::from),
97100
_ => unreachable!("{} should be handled above", subcommand_name),

src/subcommands.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ pub mod execution_results_summary;
44
pub mod extract_slice;
55
pub mod latest_block_summary;
66
pub mod purge_signatures;
7+
pub mod remove_block;
78
pub mod trie_compact;
89
pub mod unsparse;
910

@@ -15,6 +16,7 @@ use execution_results_summary::Error as ExecutionResultsSummaryError;
1516
use extract_slice::Error as ExtractSliceError;
1617
use latest_block_summary::Error as LatestBlockSummaryError;
1718
use purge_signatures::Error as PurgeSignaturesError;
19+
use remove_block::Error as RemoveBlockError;
1820
use trie_compact::Error as TrieCompactError;
1921
use unsparse::Error as UnsparseError;
2022

@@ -34,6 +36,8 @@ pub enum Error {
3436
LatestBlockSummary(#[from] LatestBlockSummaryError),
3537
#[error("Purge signatures failed: {0}")]
3638
PurgeSignatures(#[from] PurgeSignaturesError),
39+
#[error("Remove block failed: {0}")]
40+
RemoveBlock(#[from] RemoveBlockError),
3741
#[error("Trie compact failed: {0}")]
3842
TrieCompact(#[from] TrieCompactError),
3943
#[error("Unsparse failed: {0}")]

src/subcommands/remove_block.rs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
mod remove;
2+
#[cfg(test)]
3+
mod tests;
4+
5+
use std::path::Path;
6+
7+
use bincode::Error as BincodeError;
8+
use casper_hashing::Digest;
9+
use casper_node::types::{BlockHash, DeployHash};
10+
use clap::{Arg, ArgMatches, Command};
11+
use lmdb::Error as LmdbError;
12+
use thiserror::Error as ThisError;
13+
14+
pub const COMMAND_NAME: &str = "remove-block";
15+
const BLOCK_HASH: &str = "block-hash";
16+
const DB_PATH: &str = "db-path";
17+
18+
/// Errors encountered when operating on the storage database.
19+
#[derive(Debug, ThisError)]
20+
pub enum Error {
21+
/// Parsing error on entry in the block body database.
22+
#[error("Error parsing block body for block with hash {0}: {1}")]
23+
BodyParsing(BlockHash, BincodeError),
24+
/// Database operation error.
25+
#[error("Error operating the database: {0}")]
26+
Database(#[from] LmdbError),
27+
/// Parsing error on entry in the deploy metadata database.
28+
#[error("Error parsing execution results for block with hash {0} at deploy {1}: {2}")]
29+
ExecutionResultsParsing(BlockHash, DeployHash, BincodeError),
30+
/// Parsing error on entry in the block header database.
31+
#[error("Error parsing block header with hash {0}: {1}")]
32+
HeaderParsing(BlockHash, BincodeError),
33+
/// Missing entry in the deploy metadata database.
34+
#[error("Deploy with hash {0} not present in the database")]
35+
MissingDeploy(DeployHash),
36+
/// Missing entry in the block header database.
37+
#[error("Block header for block hash {0} not present in the database")]
38+
MissingHeader(BlockHash),
39+
/// Serialization error on entry in the deploy metadata database.
40+
#[error("Error serializing execution results for deploy {0}: {1}")]
41+
Serialization(DeployHash, BincodeError),
42+
}
43+
44+
enum DisplayOrder {
45+
DbPath,
46+
BlockHash,
47+
}
48+
49+
pub fn command(display_order: usize) -> Command<'static> {
50+
Command::new(COMMAND_NAME)
51+
.display_order(display_order)
52+
.about(
53+
"Removes the block header, body and execution results for a given \
54+
block hash from a storage database.",
55+
)
56+
.arg(
57+
Arg::new(DB_PATH)
58+
.display_order(DisplayOrder::DbPath as usize)
59+
.required(true)
60+
.short('d')
61+
.long(DB_PATH)
62+
.takes_value(true)
63+
.value_name("DB_PATH")
64+
.help("Path of the directory with the `storage.lmdb` file."),
65+
)
66+
.arg(
67+
Arg::new(BLOCK_HASH)
68+
.display_order(DisplayOrder::BlockHash as usize)
69+
.short('b')
70+
.long(BLOCK_HASH)
71+
.takes_value(true)
72+
.value_name("BLOCK_HASH")
73+
.help("Hash of the block to be removed."),
74+
)
75+
}
76+
77+
pub fn run(matches: &ArgMatches) -> Result<(), Error> {
78+
let path = Path::new(matches.value_of(DB_PATH).expect("should have db-path arg"));
79+
let block_hash: BlockHash = matches
80+
.value_of(BLOCK_HASH)
81+
.map(|block_hash_str| {
82+
Digest::from_hex(block_hash_str)
83+
.expect("should parse block hash to hex format")
84+
.into()
85+
})
86+
.expect("should have block-hash arg");
87+
remove::remove_block(path, block_hash)
88+
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
use std::path::Path;
2+
3+
use casper_node::types::{BlockHash, BlockHeader, DeployMetadata};
4+
use lmdb::{Error as LmdbError, Transaction, WriteFlags};
5+
use log::warn;
6+
7+
use crate::{
8+
common::db::{
9+
self, BlockBodyDatabase, BlockHeaderDatabase, Database, DeployMetadataDatabase,
10+
STORAGE_FILE_NAME,
11+
},
12+
subcommands::execution_results_summary::block_body::BlockBody,
13+
};
14+
15+
use super::Error;
16+
17+
pub(crate) fn remove_block<P: AsRef<Path>>(db_path: P, block_hash: BlockHash) -> Result<(), Error> {
18+
let storage_path = db_path.as_ref().join(STORAGE_FILE_NAME);
19+
let env = db::db_env(storage_path)?;
20+
21+
let mut txn = env.begin_rw_txn()?;
22+
let header_db = unsafe { txn.open_db(Some(BlockHeaderDatabase::db_name()))? };
23+
let body_db = unsafe { txn.open_db(Some(BlockBodyDatabase::db_name()))? };
24+
let deploy_metadata_db = unsafe { txn.open_db(Some(DeployMetadataDatabase::db_name()))? };
25+
26+
let header: BlockHeader = match txn.get(header_db, &block_hash) {
27+
Ok(raw_header) => bincode::deserialize(raw_header)
28+
.map_err(|bincode_err| Error::HeaderParsing(block_hash, bincode_err))?,
29+
Err(LmdbError::NotFound) => {
30+
return Err(Error::MissingHeader(block_hash));
31+
}
32+
Err(lmdb_err) => {
33+
return Err(lmdb_err.into());
34+
}
35+
};
36+
37+
let maybe_body: Option<BlockBody> = match txn.get(body_db, header.body_hash()) {
38+
Ok(raw_body) => Some(
39+
bincode::deserialize(raw_body)
40+
.map_err(|bincode_err| Error::BodyParsing(block_hash, bincode_err))?,
41+
),
42+
Err(LmdbError::NotFound) => {
43+
warn!(
44+
"No block body found for block header with hash {}",
45+
block_hash
46+
);
47+
None
48+
}
49+
Err(lmdb_err) => {
50+
return Err(lmdb_err.into());
51+
}
52+
};
53+
54+
if let Some(body) = maybe_body {
55+
// Go through all the deploys in this block and get the execution
56+
// result of each one.
57+
for deploy_hash in body.deploy_hashes() {
58+
// Get this deploy's metadata.
59+
let mut metadata: DeployMetadata = match txn.get(deploy_metadata_db, deploy_hash) {
60+
Ok(raw_metadata) => bincode::deserialize(raw_metadata).map_err(|bincode_err| {
61+
Error::ExecutionResultsParsing(block_hash, *deploy_hash, bincode_err)
62+
})?,
63+
Err(LmdbError::NotFound) => return Err(Error::MissingDeploy(*deploy_hash)),
64+
Err(lmdb_error) => return Err(lmdb_error.into()),
65+
};
66+
// Extract the execution result of this deploy for the current block.
67+
if let Some(_execution_result) = metadata.execution_results.remove(&block_hash) {
68+
if metadata.execution_results.is_empty() {
69+
txn.del(deploy_metadata_db, deploy_hash, None)?;
70+
} else {
71+
let encoded_metadata = bincode::serialize(&metadata)
72+
.map_err(|bincode_err| Error::Serialization(*deploy_hash, bincode_err))?;
73+
txn.put(
74+
deploy_metadata_db,
75+
deploy_hash,
76+
&encoded_metadata,
77+
WriteFlags::default(),
78+
)?;
79+
}
80+
}
81+
}
82+
83+
txn.del(body_db, header.body_hash(), None)?;
84+
}
85+
86+
txn.del(header_db, &block_hash, None)?;
87+
txn.commit()?;
88+
Ok(())
89+
}

0 commit comments

Comments
 (0)