diff --git a/bin/reth/src/args/secret_key.rs b/bin/reth/src/args/secret_key.rs index 8547b2416f..644513c343 100644 --- a/bin/reth/src/args/secret_key.rs +++ b/bin/reth/src/args/secret_key.rs @@ -1,8 +1,8 @@ use hex::encode as hex_encode; use reth_network::config::rng_secret_key; +use reth_primitives::{fs, fs::FsPathError}; use secp256k1::{Error as SecretKeyBaseError, SecretKey}; use std::{ - fs::read_to_string, io, path::{Path, PathBuf}, }; @@ -14,12 +14,8 @@ use thiserror::Error; pub enum SecretKeyError { #[error(transparent)] SecretKeyDecodeError(#[from] SecretKeyBaseError), - #[error("Failed to create parent directory {dir:?} for secret key: {error}")] - FailedToCreateSecretParentDir { error: io::Error, dir: PathBuf }, - #[error("Failed to write secret key file {secret_file:?}: {error}")] - FailedToWriteSecretKeyFile { error: io::Error, secret_file: PathBuf }, - #[error("Failed to read secret key file {secret_file:?}: {error}")] - FailedToReadSecretKeyFile { error: io::Error, secret_file: PathBuf }, + #[error(transparent)] + SecretKeyFsPathError(#[from] FsPathError), #[error("Failed to access key file {secret_file:?}: {error}")] FailedToAccessKeyFile { error: io::Error, secret_file: PathBuf }, } @@ -32,30 +28,19 @@ pub fn get_secret_key(secret_key_path: &Path) -> Result { - let contents = read_to_string(secret_key_path).map_err(|error| { - SecretKeyError::FailedToReadSecretKeyFile { - error, - secret_file: secret_key_path.to_path_buf(), - } - })?; - (contents.as_str().parse::()).map_err(SecretKeyError::SecretKeyDecodeError) + let contents = fs::read_to_string(secret_key_path)?; + Ok((contents.as_str().parse::()) + .map_err(SecretKeyError::SecretKeyDecodeError)?) } Ok(false) => { if let Some(dir) = secret_key_path.parent() { // Create parent directory - std::fs::create_dir_all(dir).map_err(|error| { - SecretKeyError::FailedToCreateSecretParentDir { error, dir: dir.to_path_buf() } - })?; + fs::create_dir_all(dir)?; } let secret = rng_secret_key(); let hex = hex_encode(secret.as_ref()); - std::fs::write(secret_key_path, hex).map_err(|error| { - SecretKeyError::FailedToWriteSecretKeyFile { - error, - secret_file: secret_key_path.to_path_buf(), - } - })?; + fs::write(secret_key_path, hex)?; Ok(secret) } Err(error) => Err(SecretKeyError::FailedToAccessKeyFile { diff --git a/bin/reth/src/args/utils.rs b/bin/reth/src/args/utils.rs index 031b457d2e..4a11339450 100644 --- a/bin/reth/src/args/utils.rs +++ b/bin/reth/src/args/utils.rs @@ -1,6 +1,8 @@ //! Clap parser utilities -use reth_primitives::{AllGenesisFormats, BlockHashOrNumber, ChainSpec, GOERLI, MAINNET, SEPOLIA}; +use reth_primitives::{ + fs, AllGenesisFormats, BlockHashOrNumber, ChainSpec, GOERLI, MAINNET, SEPOLIA, +}; use reth_revm::primitives::B256 as H256; use std::{ net::{IpAddr, Ipv4Addr, SocketAddr, ToSocketAddrs}, @@ -24,7 +26,7 @@ pub fn chain_spec_value_parser(s: &str) -> eyre::Result, eyre::Er "goerli" => GOERLI.clone(), "sepolia" => SEPOLIA.clone(), _ => { - let raw = std::fs::read_to_string(PathBuf::from(shellexpand::full(s)?.into_owned()))?; + let raw = fs::read_to_string(PathBuf::from(shellexpand::full(s)?.into_owned()))?; serde_json::from_str(&raw)? } }) @@ -38,7 +40,7 @@ pub fn genesis_value_parser(s: &str) -> eyre::Result, eyre::Error "goerli" => GOERLI.clone(), "sepolia" => SEPOLIA.clone(), _ => { - let raw = std::fs::read_to_string(PathBuf::from(shellexpand::full(s)?.into_owned()))?; + let raw = fs::read_to_string(PathBuf::from(shellexpand::full(s)?.into_owned()))?; let genesis: AllGenesisFormats = serde_json::from_str(&raw)?; Arc::new(genesis.into()) } diff --git a/bin/reth/src/debug_cmd/execution.rs b/bin/reth/src/debug_cmd/execution.rs index fa3b8502a1..d6fc51add6 100644 --- a/bin/reth/src/debug_cmd/execution.rs +++ b/bin/reth/src/debug_cmd/execution.rs @@ -22,7 +22,7 @@ use reth_interfaces::{ }; use reth_network::NetworkHandle; use reth_network_api::NetworkInfo; -use reth_primitives::{stage::StageId, BlockHashOrNumber, BlockNumber, ChainSpec, H256}; +use reth_primitives::{fs, stage::StageId, BlockHashOrNumber, BlockNumber, ChainSpec, H256}; use reth_provider::{BlockExecutionWriter, ProviderFactory, StageCheckpointReader}; use reth_staged_sync::utils::init::init_genesis; use reth_stages::{ @@ -200,7 +200,7 @@ impl Command { let data_dir = self.datadir.unwrap_or_chain_default(self.chain.chain); let db_path = data_dir.db_path(); - std::fs::create_dir_all(&db_path)?; + fs::create_dir_all(&db_path)?; let db = Arc::new(init_db(db_path)?); debug!(target: "reth::cli", chain=%self.chain.chain, genesis=?self.chain.genesis_hash(), "Initializing genesis"); diff --git a/bin/reth/src/debug_cmd/merkle.rs b/bin/reth/src/debug_cmd/merkle.rs index 9b186506b1..5cfbe54fa3 100644 --- a/bin/reth/src/debug_cmd/merkle.rs +++ b/bin/reth/src/debug_cmd/merkle.rs @@ -6,6 +6,7 @@ use crate::{ use clap::Parser; use reth_db::{cursor::DbCursorRO, init_db, tables, transaction::DbTx}; use reth_primitives::{ + fs, stage::{StageCheckpoint, StageId}, ChainSpec, }; @@ -64,7 +65,7 @@ impl Command { // add network name to data dir let data_dir = self.datadir.unwrap_or_chain_default(self.chain.chain); let db_path = data_dir.db_path(); - std::fs::create_dir_all(&db_path)?; + fs::create_dir_all(&db_path)?; let db = Arc::new(init_db(db_path)?); let factory = ProviderFactory::new(&db, self.chain.clone()); diff --git a/bin/reth/src/stage/drop.rs b/bin/reth/src/stage/drop.rs index cd6e94436c..efdbaa7b86 100644 --- a/bin/reth/src/stage/drop.rs +++ b/bin/reth/src/stage/drop.rs @@ -6,7 +6,7 @@ use crate::{ }; use clap::Parser; use reth_db::{database::Database, open_db, tables, transaction::DbTxMut, DatabaseEnv}; -use reth_primitives::{stage::StageId, ChainSpec}; +use reth_primitives::{fs, stage::StageId, ChainSpec}; use reth_staged_sync::utils::init::{insert_genesis_header, insert_genesis_state}; use std::sync::Arc; use tracing::info; @@ -50,7 +50,7 @@ impl Command { // add network name to data dir let data_dir = self.datadir.unwrap_or_chain_default(self.chain.chain); let db_path = data_dir.db_path(); - std::fs::create_dir_all(&db_path)?; + fs::create_dir_all(&db_path)?; let db = open_db(db_path.as_ref())?; diff --git a/bin/reth/src/test_vectors/tables.rs b/bin/reth/src/test_vectors/tables.rs index bf447456c4..0b4cde9501 100644 --- a/bin/reth/src/test_vectors/tables.rs +++ b/bin/reth/src/test_vectors/tables.rs @@ -11,6 +11,7 @@ use reth_db::{ table::{DupSort, Table}, tables, }; +use reth_primitives::fs; use tracing::error; const VECTORS_FOLDER: &str = "testdata/micro/db"; @@ -19,7 +20,7 @@ const PER_TABLE: usize = 1000; /// Generates test vectors for specified `tables`. If list is empty, then generate for all tables. pub(crate) fn generate_vectors(mut tables: Vec) -> Result<()> { let mut runner = TestRunner::new(ProptestConfig::default()); - std::fs::create_dir_all(VECTORS_FOLDER)?; + fs::create_dir_all(VECTORS_FOLDER)?; macro_rules! generate_vector { ($table_type:ident, $per_table:expr, TABLE) => { diff --git a/bin/reth/src/utils.rs b/bin/reth/src/utils.rs index a6c1b6e0fa..1ac4f4d427 100644 --- a/bin/reth/src/utils.rs +++ b/bin/reth/src/utils.rs @@ -1,6 +1,6 @@ //! Common CLI utility functions. -use eyre::{Result, WrapErr}; +use eyre::Result; use reth_db::{ cursor::DbCursorRO, database::Database, @@ -11,7 +11,7 @@ use reth_interfaces::p2p::{ headers::client::{HeadersClient, HeadersRequest}, priority::Priority, }; -use reth_primitives::{BlockHashOrNumber, ChainSpec, HeadersDirection, SealedHeader}; +use reth_primitives::{fs, BlockHashOrNumber, ChainSpec, HeadersDirection, SealedHeader}; use std::{ env::VarError, path::{Path, PathBuf}, @@ -98,7 +98,7 @@ impl<'a, DB: Database> DbTool<'a, DB> { pub fn drop(&mut self, path: impl AsRef) -> Result<()> { let path = path.as_ref(); info!(target: "reth::cli", "Dropping database at {:?}", path); - std::fs::remove_dir_all(path).wrap_err("Dropping the database failed")?; + fs::remove_dir_all(path)?; Ok(()) } diff --git a/crates/primitives/src/fs.rs b/crates/primitives/src/fs.rs new file mode 100644 index 0000000000..f31b279c5b --- /dev/null +++ b/crates/primitives/src/fs.rs @@ -0,0 +1,110 @@ +//! Wrapper for `std::fs` methods +use std::{ + fs, io, + path::{Path, PathBuf}, +}; + +/// Various error variants for `std::fs` operations that serve as an addition to the io::Error which +/// does not provide any information about the path. +#[derive(Debug, thiserror::Error)] +#[allow(missing_docs)] +pub enum FsPathError { + /// Provides additional path context for [`std::fs::write`]. + #[error("failed to write to {path:?}: {source}")] + Write { source: io::Error, path: PathBuf }, + /// Provides additional path context for [`std::fs::read`]. + #[error("failed to read from {path:?}: {source}")] + Read { source: io::Error, path: PathBuf }, + /// Provides additional path context for [`std::fs::read_link`]. + #[error("failed to read from {path:?}: {source}")] + ReadLink { source: io::Error, path: PathBuf }, + /// Provides additional path context for [`std::fs::File::create`]. + #[error("failed to create file {path:?}: {source}")] + CreateFile { source: io::Error, path: PathBuf }, + /// Provides additional path context for [`std::fs::remove_file`]. + #[error("failed to remove file {path:?}: {source}")] + RemoveFile { source: io::Error, path: PathBuf }, + /// Provides additional path context for [`std::fs::create_dir`]. + #[error("failed to create dir {path:?}: {source}")] + CreateDir { source: io::Error, path: PathBuf }, + /// Provides additional path context for [`std::fs::remove_dir`]. + #[error("failed to remove dir {path:?}: {source}")] + RemoveDir { source: io::Error, path: PathBuf }, + /// Provides additional path context for [`std::fs::File::open`]. + #[error("failed to open file {path:?}: {source}")] + Open { source: io::Error, path: PathBuf }, + /// Provides additional path context for the file whose contents should be parsed as JSON. + #[error("failed to parse json file: {path:?}: {source}")] + ReadJson { source: serde_json::Error, path: PathBuf }, + /// Provides additional path context for the new JSON file. + #[error("failed to write to json file: {path:?}: {source}")] + WriteJson { source: serde_json::Error, path: PathBuf }, +} + +impl FsPathError { + /// Returns the complementary error variant for [`std::fs::write`]. + pub fn write(source: io::Error, path: impl Into) -> Self { + FsPathError::Write { source, path: path.into() } + } + + /// Returns the complementary error variant for [`std::fs::read`]. + pub fn read(source: io::Error, path: impl Into) -> Self { + FsPathError::Read { source, path: path.into() } + } + + /// Returns the complementary error variant for [`std::fs::read_link`]. + pub fn read_link(source: io::Error, path: impl Into) -> Self { + FsPathError::ReadLink { source, path: path.into() } + } + + /// Returns the complementary error variant for [`std::fs::File::create`]. + pub fn create_file(source: io::Error, path: impl Into) -> Self { + FsPathError::CreateFile { source, path: path.into() } + } + + /// Returns the complementary error variant for [`std::fs::remove_file`]. + pub fn remove_file(source: io::Error, path: impl Into) -> Self { + FsPathError::RemoveFile { source, path: path.into() } + } + + /// Returns the complementary error variant for [`std::fs::create_dir`]. + pub fn create_dir(source: io::Error, path: impl Into) -> Self { + FsPathError::CreateDir { source, path: path.into() } + } + + /// Returns the complementary error variant for [`std::fs::remove_dir`]. + pub fn remove_dir(source: io::Error, path: impl Into) -> Self { + FsPathError::RemoveDir { source, path: path.into() } + } + + /// Returns the complementary error variant for [`std::fs::File::open`]. + pub fn open(source: io::Error, path: impl Into) -> Self { + FsPathError::Open { source, path: path.into() } + } +} + +type Result = std::result::Result; + +/// Wrapper for `std::fs::read_to_string` +pub fn read_to_string(path: impl AsRef) -> Result { + let path = path.as_ref(); + fs::read_to_string(path).map_err(|err| FsPathError::read(err, path)) +} + +/// Wrapper for `std::fs::write` +pub fn write(path: impl AsRef, contents: impl AsRef<[u8]>) -> Result<()> { + let path = path.as_ref(); + fs::write(path, contents).map_err(|err| FsPathError::write(err, path)) +} + +/// Wrapper for `std::fs::remove_dir_all` +pub fn remove_dir_all(path: impl AsRef) -> Result<()> { + let path = path.as_ref(); + fs::remove_dir_all(path).map_err(|err| FsPathError::remove_dir(err, path)) +} + +/// Wrapper for `std::fs::create_dir_all` +pub fn create_dir_all(path: impl AsRef) -> Result<()> { + let path = path.as_ref(); + fs::create_dir_all(path).map_err(|err| FsPathError::create_dir(err, path)) +} diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index 59c1262479..eb3bcf1942 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -30,6 +30,7 @@ mod compression; pub mod constants; pub mod contract; mod forkid; +pub mod fs; mod genesis; mod hardfork; mod header; diff --git a/crates/rpc/rpc/Cargo.toml b/crates/rpc/rpc/Cargo.toml index 2b3d1631ab..c121887924 100644 --- a/crates/rpc/rpc/Cargo.toml +++ b/crates/rpc/rpc/Cargo.toml @@ -64,4 +64,4 @@ futures = { workspace = true } jsonrpsee = { version = "0.18", features = ["client"] } assert_matches = "1.5.0" tempfile = "3.5.0" -reth-interfaces = { workspace = true, features = ["test-utils"] } \ No newline at end of file +reth-interfaces = { workspace = true, features = ["test-utils"] } diff --git a/crates/rpc/rpc/src/layers/jwt_secret.rs b/crates/rpc/rpc/src/layers/jwt_secret.rs index 589308e9d3..0af6251438 100644 --- a/crates/rpc/rpc/src/layers/jwt_secret.rs +++ b/crates/rpc/rpc/src/layers/jwt_secret.rs @@ -1,9 +1,10 @@ use hex::encode as hex_encode; use jsonwebtoken::{decode, errors::ErrorKind, Algorithm, DecodingKey, Validation}; use rand::Rng; +use reth_primitives::{fs, fs::FsPathError}; use serde::{Deserialize, Serialize}; use std::{ - path::{Path, PathBuf}, + path::Path, time::{Duration, SystemTime, UNIX_EPOCH}, }; use thiserror::Error; @@ -26,10 +27,8 @@ pub enum JwtError { MissingOrInvalidAuthorizationHeader, #[error("JWT decoding error {0}")] JwtDecodingError(String), - #[error("IO error occurred while reading {path}: {err}")] - IORead { err: std::io::Error, path: PathBuf }, - #[error("IO error occurred while writing {path}: {err}")] - IOWrite { err: std::io::Error, path: PathBuf }, + #[error(transparent)] + JwtFsPathError(#[from] FsPathError), #[error("An I/O error occurred: {0}")] IOError(#[from] std::io::Error), } @@ -80,8 +79,7 @@ impl JwtSecret { /// I/O or secret validation errors might occur during read operations in the form of /// a [`JwtError`]. pub fn from_file(fpath: &Path) -> Result { - let hex = std::fs::read_to_string(fpath) - .map_err(|err| JwtError::IORead { err, path: fpath.to_path_buf() })?; + let hex = fs::read_to_string(fpath)?; let secret = JwtSecret::from_hex(hex)?; Ok(secret) } @@ -91,14 +89,13 @@ impl JwtSecret { pub fn try_create(fpath: &Path) -> Result { if let Some(dir) = fpath.parent() { // Create parent directory - std::fs::create_dir_all(dir)? + fs::create_dir_all(dir)? } let secret = JwtSecret::random(); let bytes = &secret.0; let hex = hex::encode(bytes); - std::fs::write(fpath, hex) - .map_err(|err| JwtError::IOWrite { err, path: fpath.to_path_buf() })?; + fs::write(fpath, hex)?; Ok(secret) } } @@ -204,6 +201,7 @@ mod tests { use assert_matches::assert_matches; use hex::encode as hex_encode; use jsonwebtoken::{encode, Algorithm, EncodingKey, Header}; + use reth_primitives::fs::FsPathError; use std::{ path::Path, time::{Duration, SystemTime, UNIX_EPOCH}, @@ -375,7 +373,9 @@ mod tests { fn provided_file_not_exists() { let fpath = Path::new("secret3.hex"); let result = JwtSecret::from_file(fpath); - assert_matches!(result, Err(JwtError::IORead {err: _, path}) if path == fpath.to_path_buf()); + assert_matches!(result, + Err(JwtError::JwtFsPathError(FsPathError::Read { source: _, path })) if path == fpath.to_path_buf() + ); assert!(!exists(fpath)); } @@ -383,7 +383,7 @@ mod tests { fn provided_file_is_a_directory() { let dir = tempdir().unwrap(); let result = JwtSecret::from_file(dir.path()); - assert_matches!(result, Err(JwtError::IORead {err: _, path}) if path == dir.into_path()); + assert_matches!(result, Err(JwtError::JwtFsPathError(FsPathError::Read { source: _, path })) if path == dir.into_path()); } fn hex(secret: &JwtSecret) -> String { diff --git a/crates/storage/db/benches/criterion.rs b/crates/storage/db/benches/criterion.rs index c1a078f2ce..0fb51a5837 100644 --- a/crates/storage/db/benches/criterion.rs +++ b/crates/storage/db/benches/criterion.rs @@ -130,7 +130,7 @@ where b.iter_with_setup( || { // Reset DB - let _ = std::fs::remove_dir_all(bench_db_path); + let _ = fs::remove_dir_all(bench_db_path); ( input.clone(), Arc::try_unwrap(create_test_rw_db_with_path(bench_db_path)).unwrap(), @@ -156,7 +156,7 @@ where b.iter_with_setup( || { // Reset DB - let _ = std::fs::remove_dir_all(bench_db_path); + let _ = fs::remove_dir_all(bench_db_path); (input, Arc::try_unwrap(create_test_rw_db_with_path(bench_db_path)).unwrap()) }, |(input, db)| { @@ -227,7 +227,7 @@ where b.iter_with_setup( || { // Reset DB - let _ = std::fs::remove_dir_all(bench_db_path); + let _ = fs::remove_dir_all(bench_db_path); ( input.clone(), Arc::try_unwrap(create_test_rw_db_with_path(bench_db_path)).unwrap(), @@ -253,7 +253,7 @@ where b.iter_with_setup( || { // Reset DB - let _ = std::fs::remove_dir_all(bench_db_path); + let _ = fs::remove_dir_all(bench_db_path); (input, Arc::try_unwrap(create_test_rw_db_with_path(bench_db_path)).unwrap()) }, diff --git a/crates/storage/db/benches/hash_keys.rs b/crates/storage/db/benches/hash_keys.rs index a08547f94a..49440da7cc 100644 --- a/crates/storage/db/benches/hash_keys.rs +++ b/crates/storage/db/benches/hash_keys.rs @@ -84,7 +84,7 @@ where // Setup phase before each benchmark iteration let setup = || { // Reset DB - let _ = std::fs::remove_dir_all(bench_db_path); + let _ = fs::remove_dir_all(bench_db_path); let db = Arc::try_unwrap(create_test_rw_db_with_path(bench_db_path)).unwrap(); let mut unsorted_input = unsorted_input.clone(); diff --git a/crates/storage/db/benches/utils.rs b/crates/storage/db/benches/utils.rs index 362e48eda8..ea330295bd 100644 --- a/crates/storage/db/benches/utils.rs +++ b/crates/storage/db/benches/utils.rs @@ -6,6 +6,7 @@ use reth_db::{ transaction::{DbTx, DbTxMut}, DatabaseEnv, }; +use reth_primitives::fs; use std::{path::Path, sync::Arc}; /// Path where the DB is initialized for benchmarks. @@ -59,7 +60,7 @@ where T::Value: Default + Clone, { // Reset DB - let _ = std::fs::remove_dir_all(bench_db_path); + let _ = fs::remove_dir_all(bench_db_path); let db = Arc::try_unwrap(create_test_rw_db_with_path(bench_db_path)).unwrap(); {