Repository: paradigmxyz/reth (language: rust)

Issue:
Broken database access example
### Describe the bug

The [db-access example](https://github.com/paradigmxyz/reth/blob/9bc07cc5bd87a436c41a72be712c0ea5fe1d9d97/examples/db-access/src/main.rs) fails with a relatively opaque error message on a fully-synced instance of Reth 1.1.5. The instance was initialised from a [Merkle snapshot](https://snapshots.merkle.io). I've run the example both while the node is offline and whilst it's running -- both states yield the same result.

### Steps to reproduce

```
$ cd examples/db-access
$ RETH_DB_PATH="~/.local/reth/share/mainnet/db" cargo run
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.38s
     Running `/home/jack/reth/target/debug/example-db-access`
Error: Could not open database at path: ~/.local/share/reth/mainnet/db

Caused by:
    failed to open the database: unknown error code: 2 (2)

Location:
    /home/jack/reth/crates/storage/db/src/mdbx.rs:57:10
```

### Node logs

```text

```

### Platform(s)

Linux (x86)

### Container Type

Not running in a container

### What version/commit are you on?

```
$ reth --version
reth Version: 1.1.5
Commit SHA: 3212af2d85a54eb207661361ac9fe1d7de4b5b8e
Build Timestamp: 2025-01-07T09:33:21.371418260Z
Build Features: asm_keccak,jemalloc
Build Profile: release
```

### What database version are you on?

```
$ reth db version
2025-02-11T05:18:57.657852Z  INFO Initialized tracing, debug log directory: /home/jack/.cache/reth/logs/mainnet
Current database version: 2
Local database version: 2
```

### Which chain / network are you on?

mainnet

### What type of node are you running?

Archive (default)

### What prune config do you use, if any?

_No response_

### If you've built Reth from source, provide the full command you used

_No response_

### Code of Conduct

- [x] I agree to follow the Code of Conduct


Discussion / hints:
I think you should run the command like this:

`RETH_DB_PATH="~/.local/reth/share/mainnet" cargo run`

`db` dir path as well as `static_files` is automatically appended in the script.
oh yeah, this was simplified recently, let me update the example properly

ty

Fix patch:
diff --git a/crates/storage/provider/src/providers/database/builder.rs b/crates/storage/provider/src/providers/database/builder.rs
index b5044ff0253f..8e1087ef728e 100644
--- a/crates/storage/provider/src/providers/database/builder.rs
+++ b/crates/storage/provider/src/providers/database/builder.rs
@@ -119,15 +119,14 @@ impl ReadOnlyConfig {
     /// ```text
     ///  -`datadir`
     ///    |__db
-    ///        |__static_files
+    ///    |__static_files
     /// ```
     ///
     /// By default this watches the static file directory for changes, see also
     /// [`StaticFileProvider::read_only`]
     pub fn from_datadir(datadir: impl AsRef<Path>) -> Self {
         let datadir = datadir.as_ref();
-        let db_path = datadir.join("db");
-        Self::from_db_dir(db_path)
+        Self::from_dirs(datadir.join("db"), datadir.join("static_files"))
     }
 
     /// Derives the [`ReadOnlyConfig`] from the database dir.
@@ -136,16 +135,35 @@ impl ReadOnlyConfig {
     ///
     /// ```text
     ///    - db
-    ///      |__static_files
+    ///    -static_files
     /// ```
     ///
     /// By default this watches the static file directory for changes, see also
     /// [`StaticFileProvider::read_only`]
+    ///
+    /// # Panics
+    ///
+    /// If the path does not exist
     pub fn from_db_dir(db_dir: impl AsRef<Path>) -> Self {
         let db_dir = db_dir.as_ref();
+        let static_files_dir = std::fs::canonicalize(db_dir)
+            .unwrap()
+            .parent()
+            .unwrap()
+            .to_path_buf()
+            .join("static_files");
+        Self::from_dirs(db_dir, static_files_dir)
+    }
+
+    /// Creates the config for the given paths.
+    ///
+    ///
+    /// By default this watches the static file directory for changes, see also
+    /// [`StaticFileProvider::read_only`]
+    pub fn from_dirs(db_dir: impl AsRef<Path>, static_files_dir: impl AsRef<Path>) -> Self {
         Self {
-            static_files_dir: db_dir.join("static_files"),
-            db_dir: db_dir.into(),
+            static_files_dir: static_files_dir.as_ref().into(),
+            db_dir: db_dir.as_ref().into(),
             db_args: Default::default(),
             watch_static_files: true,
         }
@@ -247,8 +265,6 @@ impl<N, Val1, Val2> TypesAnd2<N, Val1, Val2> {
     {
         TypesAnd3::new(self.val_1, self.val_2, static_file_provider)
     }
-
-    // TODO: add helper fns for opening static file provider
 }
 
 /// This is staging type that contains the configured types and _three_ values.
diff --git a/examples/db-access/src/main.rs b/examples/db-access/src/main.rs
index 4d02b39d999f..25c631a206d5 100644
--- a/examples/db-access/src/main.rs
+++ b/examples/db-access/src/main.rs
@@ -20,13 +20,13 @@ use reth_ethereum::{
 // These abstractions do not include any caching and the user is responsible for doing that.
 // Other parts of the code which include caching are parts of the `EthApi` abstraction.
 fn main() -> eyre::Result<()> {
-    // Opens a RO handle to the database file.
-    let db_path = std::env::var("RETH_DB_PATH")?;
+    // The path to data directory, e.g. "~/.local/reth/share/mainnet"
+    let datadir = std::env::var("RETH_DATADIR")?;
 
-    // Instantiate a provider factory for Ethereum mainnet using the provided DB path.
+    // Instantiate a provider factory for Ethereum mainnet using the provided datadir path.
     let spec = ChainSpecBuilder::mainnet().build();
     let factory = EthereumNode::provider_factory_builder()
-        .open_read_only(spec.into(), ReadOnlyConfig::from_db_dir(db_path))?;
+        .open_read_only(spec.into(), ReadOnlyConfig::from_datadir(datadir))?;
 
     // This call opens a RO transaction on the database. To write to the DB you'd need to call
     // the `provider_rw` function and look for the `Writer` variants of the traits.


Test patch: