Skip to content

Commit b8e319f

Browse files
committed
chore: refactor db to module
1 parent 2e22499 commit b8e319f

11 files changed

Lines changed: 659 additions & 456 deletions

File tree

CLAUDE.md

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
# init4 Library Best Practices
2+
3+
## High-level recommendations
4+
5+
Write code as if you were an extremely opinionated style enforcer. Prioritize
6+
matching local patterns and conventions over personal preferences. Avoid
7+
nesting and branching.
8+
9+
We care deeply about code quality in library and binary code. Quality standards
10+
may be relaxed in test code and scripts. Tests should still be human-readable.
11+
12+
We write libraries for ourselves and for others to use. Prioritize usability,
13+
clarity, and maintainability. Favor small, focused functions and types. Strive
14+
for consistency in naming, patterns, and API design across the codebase.
15+
16+
NEVER add incomplete function implementations. `TODO`s in the code and comments
17+
should be related to performance improvements, refactoring, or non-critical
18+
features only. A function that contains a `TODO` for its core logic is not
19+
implemented.
20+
21+
## Testing, linting, formatting and Workflow
22+
23+
When working on a code in a specific crate, it's best to run tests, linters,
24+
and formatters for that crate only, to save time and resources. Always lint
25+
before running testing. Be aware that integration tests may be costly in terms
26+
of time, and avoid running them unnecessarily.
27+
28+
Here are the preferred commands:
29+
30+
```
31+
# run tests (use the -p flag to specify a particular crate)
32+
cargo clippy -p <crate_name> --all-features --all-targets
33+
cargo clippy -p <crate_name> --no-default-features --all-targets
34+
cargo t -p <crate_name>
35+
```
36+
37+
The formatter can be run globally, as it is fast enough:
38+
39+
```
40+
cargo +nightly fmt
41+
```
42+
43+
1. Format.
44+
2. Lint.
45+
3. Test.
46+
47+
## Code Style
48+
49+
### API design
50+
51+
Prefer small, focused functions that do one thing well. Break complex logic
52+
into smaller helper functions. Favor composition over inheritance. Use traits
53+
to define shared behavior. Prefer enums for variants over boolean flags.
54+
55+
When designing APIs, aim for clarity and ease of use. Use descriptive names
56+
for functions, types, and variables. Avoid abbreviations unless they are widely
57+
understood. Strive for consistency in naming conventions and API patterns across
58+
the codebase.
59+
60+
When designing public APIs, consider the following principles:
61+
62+
- Minimize types the user needs to understand to use the API effectively.
63+
- Minimize generics and associated types exposed to the user by providing
64+
sane defaults and concrete types where possible.
65+
- Generics should disappear from the primary user-facing API wherever
66+
possible.
67+
68+
When writing traits, include an implementation guide that describes the intended
69+
use cases, design rationale, and any constraints or requirements for
70+
implementers.
71+
72+
When writing complex structs, include builder-pattern instantiation. Ask if you
73+
are unclear whether a struct is complex enough to warrant a builder.
74+
75+
### Imports
76+
77+
Imports should not be separated by empty lines. Remove empty lines and let the
78+
formatter alphabetize them. Ensure that imports are grouped by crate, with
79+
`crate::` imports first and all imports merged.
80+
81+
```
82+
// Avoid:
83+
use std::collections::HashMap;
84+
85+
use other_crate::SomeType;
86+
use other_crate::AnotherType;
87+
88+
use crate::MyType;
89+
90+
// Preferred:
91+
use crate::MyType;
92+
use other_crate::{AnotherType, SomeType};
93+
use std::collections::HashMap;
94+
```
95+
96+
### Comments
97+
98+
Write rust doc for all public items. Use `///` for single line comments and
99+
`//!` for module-level comments. Write example usage tests where appropriate
100+
101+
Simple functions should be explained entirely by the rustdoc. In more
102+
complex cases, use inline comments `//` to explain non-obvious parts of the
103+
implementation. When writing complex code, annotate with comments to explain
104+
the logic. Comments should be concise, and explain the design rational, and any
105+
conditions of use that must be enforced. When writing complicated code, these
106+
should occur about every 5-10 lines
107+
108+
When writing rustdoc identify the primary API types and include example tests
109+
to show high-level API usage:
110+
111+
- be aware that tests are run in doc test modules, so imports may be necessary.
112+
- prefer concise examples that focus on the key usage patterns.
113+
- keep examples short and to the point.
114+
- add inline comments `//` to explain non-obvious parts.
115+
- hide unnecessary scaffolding by prepending the line with a `#` according to
116+
rustdoc convention. See this example for hidden lines:
117+
118+
```rust
119+
//! # use trevm::{revm::database::in_memory_db::InMemoryDB, {TrevmBuilder}};
120+
//! # fn t<C: Cfg, B: Block, T: Tx>(cfg: &C, block: &B, tx: &T) {
121+
//! TrevmBuilder::new()
122+
//! .with_db(InMemoryDB::default())
123+
//! .build_trevm()
124+
//! .fill_cfg(cfg)
125+
//! .fill_block(block)
126+
//! .run_tx(tx);
127+
//! # }
128+
```
129+
130+
### Handling Options and Results
131+
132+
We prefer a terse, functional style. Avoid unnecessary nesting. Avoid unnecessary
133+
closures in functional chains
134+
135+
```rust
136+
// NEVER:
137+
if let Some(a) = option {
138+
Thing::do_something(a);
139+
} else {
140+
return;
141+
}
142+
143+
// Preferred styles:
144+
option.map(Thing::do_something);
145+
146+
let Some(a) = option else {
147+
return;
148+
};
149+
Thing::do_something(a);
150+
```
151+
152+
Strongly prefer functional combinators like `map`, `and_then`, `filter`, etc.
153+
over imperative control flow.
154+
155+
### Test style
156+
157+
Be concise. Avoid unnecessary setup/teardown. Don't bother checking options and
158+
results before unwrapping them. Use `unwrap` or `expect` directly.
159+
160+
```rust
161+
// NEVER:
162+
let result = some_function();
163+
assert!(result.is_ok());
164+
let value = result.unwrap();
165+
166+
// Preferred:
167+
let value = some_function().unwrap();
168+
```
169+
170+
Tests should fail fast and panic rather than return error types.

crates/storage/src/hot/conformance.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
#![allow(dead_code)]
22

3-
use crate::hot::model::{HotDbRead, HotDbWrite, HotHistoryRead, HotHistoryWrite, HotKv};
3+
use crate::hot::{
4+
db::{HotDbRead, HotHistoryRead, UnsafeDbWrite, UnsafeHistoryWrite},
5+
model::HotKv,
6+
};
47
use alloy::primitives::{B256, Bytes, U256, address, b256};
58
use reth::primitives::{Account, Bytecode, Header, SealedHeader};
69
use reth_db::BlockNumberList;
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
use reth::primitives::SealedHeader;
2+
3+
use crate::hot::db::{HistoryError, UnsafeDbWrite, UnsafeHistoryWrite};
4+
5+
/// Trait for database write operations on hot history tables. This trait
6+
/// maintains a consistent state of the database.
7+
pub trait HistoryWrite: UnsafeDbWrite + UnsafeHistoryWrite {
8+
/// Validate that a range of headers forms a valid chain extension.
9+
///
10+
/// Headers must be in order and each must extend the previous.
11+
/// The first header must extend the current database tip (or be the first
12+
/// block if the database is empty).
13+
///
14+
/// Returns `Ok(())` if valid, or an error describing the inconsistency.
15+
fn validate_chain_extension<'a, I>(&self, headers: I) -> Result<(), HistoryError<Self::Error>>
16+
where
17+
I: IntoIterator<Item = &'a SealedHeader>,
18+
{
19+
let headers: Vec<_> = headers.into_iter().collect();
20+
if headers.is_empty() {
21+
return Err(HistoryError::EmptyRange);
22+
}
23+
24+
// Validate first header against current DB tip
25+
let first = headers[0];
26+
match self.get_chain_tip().map_err(HistoryError::Db)? {
27+
None => {
28+
// Empty DB - first block is valid as genesis
29+
}
30+
Some((tip_number, tip_hash)) => {
31+
let expected_number = tip_number + 1;
32+
if first.number != expected_number {
33+
return Err(HistoryError::NonContiguousBlock {
34+
expected: expected_number,
35+
got: first.number,
36+
});
37+
}
38+
if first.parent_hash != tip_hash {
39+
return Err(HistoryError::ParentHashMismatch {
40+
expected: tip_hash,
41+
got: first.parent_hash,
42+
});
43+
}
44+
}
45+
}
46+
47+
// Validate each subsequent header extends the previous
48+
for window in headers.windows(2) {
49+
let prev = window[0];
50+
let curr = window[1];
51+
52+
let expected_number = prev.number + 1;
53+
if curr.number != expected_number {
54+
return Err(HistoryError::NonContiguousBlock {
55+
expected: expected_number,
56+
got: curr.number,
57+
});
58+
}
59+
60+
let expected_hash = prev.hash();
61+
if curr.parent_hash != expected_hash {
62+
return Err(HistoryError::ParentHashMismatch {
63+
expected: expected_hash,
64+
got: curr.parent_hash,
65+
});
66+
}
67+
}
68+
69+
Ok(())
70+
}
71+
}
72+
73+
impl<T> HistoryWrite for T where T: UnsafeDbWrite + UnsafeHistoryWrite {}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
use alloy::primitives::B256;
2+
use std::fmt;
3+
4+
/// A result type for history operations.
5+
pub type HistoryResult<T, E> = Result<T, HistoryError<E>>;
6+
7+
/// Error type for history operations.
8+
///
9+
/// This error is returned by methods that append or unwind history,
10+
/// and includes both chain consistency errors and database errors.
11+
#[derive(Debug, Clone, PartialEq, Eq)]
12+
pub enum HistoryError<E> {
13+
/// Block number doesn't extend the chain contiguously.
14+
NonContiguousBlock {
15+
/// The expected block number (current tip + 1).
16+
expected: u64,
17+
/// The actual block number provided.
18+
got: u64,
19+
},
20+
/// Parent hash doesn't match current tip or previous block in range.
21+
ParentHashMismatch {
22+
/// The expected parent hash.
23+
expected: B256,
24+
/// The actual parent hash provided.
25+
got: B256,
26+
},
27+
/// Empty header range provided to a method that requires at least one header.
28+
EmptyRange,
29+
/// Database error.
30+
Db(E),
31+
}
32+
33+
impl<E: fmt::Display> fmt::Display for HistoryError<E> {
34+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35+
match self {
36+
Self::NonContiguousBlock { expected, got } => {
37+
write!(f, "non-contiguous block: expected {expected}, got {got}")
38+
}
39+
Self::ParentHashMismatch { expected, got } => {
40+
write!(f, "parent hash mismatch: expected {expected}, got {got}")
41+
}
42+
Self::EmptyRange => write!(f, "empty header range provided"),
43+
Self::Db(e) => write!(f, "database error: {e}"),
44+
}
45+
}
46+
}
47+
48+
impl<E: std::error::Error + 'static> std::error::Error for HistoryError<E> {
49+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
50+
match self {
51+
Self::Db(e) => Some(e),
52+
_ => None,
53+
}
54+
}
55+
}

0 commit comments

Comments
 (0)