Skip to content

Commit 888f9e2

Browse files
authored
Improve code quality and documentation for better maintainability (#12)
1 parent d5cfd89 commit 888f9e2

File tree

12 files changed

+313
-24
lines changed

12 files changed

+313
-24
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ eyre = "0.6"
3131
jsonrpsee-core = "0.25.1"
3232
log = "0.4.27"
3333
serde = { version = "1.0", features = ["derive"], default-features = false }
34+
thiserror = "2.0"
3435

3536

3637
[patch.crates-io]

README.md

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,44 @@ The binary will be at `target/release/bera-reth`.
5252

5353
---
5454

55-
## ▶️ Running Locally with BeaconKit
55+
## ▶️ Running with BeaconKit (Local Development)
5656

57-
1. Run `make start` from **your Beacon-Kit repository**.
58-
2. Save the path to your BeaconKit repository in the `BEACON_KIT` envar, e.g. `export BEACON_KIT=/Users/rezbera/Code/beacon-kit`
59-
3. Run `make start-bera-reth-local` from **this repository**
57+
For local development and testing, you can use the provided script to test BeaconKit integration:
58+
59+
```bash
60+
# Basic usage (tests progression to block 10 with 120s timeout)
61+
BEACON_KIT_PATH=/path/to/beacon-kit ./scripts/test-block-progression.sh
62+
63+
# Custom configuration
64+
BEACON_KIT_PATH=/path/to/beacon-kit TARGET_BLOCK=5 TIMEOUT=180 ./scripts/test-block-progression.sh
65+
```
66+
67+
### Prerequisites
68+
69+
- Local BeaconKit repository cloned and built
70+
- Set `BEACON_KIT_PATH` to your BeaconKit directory
71+
72+
### Environment Variables
73+
74+
- `BEACON_KIT_PATH`: Path to your BeaconKit repository (required)
75+
- `TARGET_BLOCK`: Target block number to reach (default: `10`)
76+
- `TIMEOUT`: Maximum time to wait in seconds (default: `120`)
77+
78+
### What the script does
79+
80+
1. Cleans up any existing data directories
81+
2. Starts BeaconKit locally with `[BEACONKIT]` log prefixes
82+
3. Starts bera-reth with `[RETH]` log prefixes
83+
4. Monitors block progression via JSON-RPC calls
84+
5. Reports success when target block is reached
85+
6. Automatically cleans up all processes on exit or Ctrl+C
86+
87+
### Manual Setup (Alternative)
88+
89+
If you prefer to run the components manually:
90+
91+
1. Run `make start` from **your BeaconKit repository**
92+
2. Run `BEACON_KIT=/path/to/beacon-kit make start-bera-reth-local` from **this repository**
6093

6194
---
6295

@@ -81,6 +114,19 @@ cargo audit
81114
cargo udeps --all-features --locked
82115
```
83116

117+
---
118+
119+
## 📚 Documentation
120+
121+
View the comprehensive code documentation locally:
122+
123+
```bash
124+
# Build and open documentation in your browser
125+
cargo doc --open --no-deps --document-private-items
126+
```
127+
128+
This will generate and open detailed API documentation including all modules, types, and examples.
129+
84130
## 📜 License
85131

86132
Licensed under the Apache-2.0 License. See [LICENSE](LICENSE) for details.

scripts/test-block-progression.sh

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
#!/bin/bash
2+
# Test BeaconKit + bera-reth block progression
3+
set -ex
4+
5+
BEACON_KIT_PATH="${BEACON_KIT_PATH:-../beacon-kit}"
6+
TARGET_BLOCK="${TARGET_BLOCK:-10}"
7+
TIMEOUT="${TIMEOUT:-120}"
8+
9+
[ ! -d "$BEACON_KIT_PATH" ] && { echo "ERROR: Set BEACON_KIT_PATH"; exit 1; }
10+
11+
# Check if required ports are available
12+
for port in 8545 8551 30303 3500; do
13+
if lsof -i :$port >/dev/null 2>&1; then
14+
echo "ERROR: Port $port is in use"
15+
exit 1
16+
fi
17+
done
18+
19+
cleanup() {
20+
echo "Cleaning up processes..."
21+
[ -n "$BEACON_PID" ] && kill $BEACON_PID 2>/dev/null || true
22+
[ -n "$RETH_PID" ] && kill $RETH_PID 2>/dev/null || true
23+
pkill -f "beacond\|bera-reth" 2>/dev/null || true
24+
pkill -f "make start" 2>/dev/null || true
25+
pkill -f "make start-bera-reth-local" 2>/dev/null || true
26+
jobs -p | xargs -r kill 2>/dev/null || true
27+
}
28+
trap cleanup EXIT INT TERM
29+
30+
get_block() {
31+
result=$(curl -s -X POST -H "Content-Type: application/json" \
32+
--data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' \
33+
http://localhost:8545 2>/dev/null | \
34+
grep -o '"result":"[^"]*"' | cut -d'"' -f4 2>/dev/null)
35+
if [ -n "$result" ]; then
36+
printf "%d\n" "$result" 2>/dev/null || echo "0"
37+
else
38+
echo "0"
39+
fi
40+
}
41+
42+
echo "Testing block progression to $TARGET_BLOCK (timeout: ${TIMEOUT}s)"
43+
44+
# Clean directories
45+
rm -rf /.tmp/beacond ~/.bera-reth 2>/dev/null || true
46+
47+
# Start BeaconKit with timeout protection
48+
echo "Starting BeaconKit..."
49+
cd "$BEACON_KIT_PATH"
50+
bash -c 'echo "y" | make start' 2>&1 | sed 's/^/[BEACONKIT] /' &
51+
BEACON_PID=$!
52+
53+
# Wait for BeaconKit to initialize with timeout
54+
WAIT_TIME=0
55+
while [ $WAIT_TIME -lt 10 ]; do
56+
if [ -f "$BEACON_KIT_PATH/.tmp/beacond/eth-genesis.json" ]; then
57+
echo "BeaconKit initialized successfully"
58+
break
59+
fi
60+
sleep 2
61+
WAIT_TIME=$((WAIT_TIME + 2))
62+
done
63+
64+
# Verify genesis file exists
65+
[ ! -f "$BEACON_KIT_PATH/.tmp/beacond/eth-genesis.json" ] && {
66+
echo "ERROR: Genesis file not found after ${WAIT_TIME}s";
67+
kill $BEACON_PID 2>/dev/null || true
68+
exit 1;
69+
}
70+
71+
# Start bera-reth
72+
echo "Starting bera-reth..."
73+
cd - >/dev/null
74+
BEACON_KIT="$BEACON_KIT_PATH" make start-bera-reth-local 2>&1 | sed 's/^/[RETH] /' &
75+
RETH_PID=$!
76+
sleep 10
77+
78+
# Monitor block progression
79+
start_time=$(date +%s)
80+
prev_block=0
81+
82+
while [ $(($(date +%s) - start_time)) -lt $TIMEOUT ]; do
83+
current_block=$(get_block)
84+
85+
if [ "$current_block" != "0" ] && [ "$current_block" -gt "$prev_block" ]; then
86+
echo "Block: $prev_block -> $current_block"
87+
prev_block=$current_block
88+
89+
[ "$current_block" -ge "$TARGET_BLOCK" ] && {
90+
echo "SUCCESS: Reached block $current_block in $(($(date +%s) - start_time))s"
91+
exit 0
92+
}
93+
fi
94+
95+
sleep 3
96+
done
97+
98+
echo "TIMEOUT: Only reached block $prev_block"
99+
exit 1

src/chainspec/mod.rs

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//! Berachain chain spec
1+
//! Berachain chain specification with Ethereum hardforks plus Prague1 minimum base fee
22
33
use crate::{
44
genesis::BerachainGenesisConfig,
@@ -22,9 +22,16 @@ use reth_ethereum_cli::chainspec::SUPPORTED_CHAINS;
2222
use reth_evm::eth::spec::EthExecutorSpec;
2323
use std::{fmt::Display, sync::Arc};
2424

25-
/// Berachain chain spec
25+
/// Minimum base fee enforced after Prague1 hardfork (1 gwei)
26+
const PRAGUE1_MIN_BASE_FEE_WEI: u64 = 1_000_000_000;
27+
28+
/// Default minimum base fee when Prague1 is not active.
29+
const DEFAULT_MIN_BASE_FEE_WEI: u64 = 0;
30+
31+
/// Berachain chain specification wrapping Reth's ChainSpec with Prague1 hardfork
2632
#[derive(Debug, Clone, Into, Constructor, PartialEq, Eq, Default)]
2733
pub struct BerachainChainSpec {
34+
/// The underlying Reth chain specification
2835
inner: ChainSpec,
2936
}
3037
impl EthChainSpec for BerachainChainSpec {
@@ -90,8 +97,11 @@ impl EthChainSpec for BerachainChainSpec {
9097
// Note that we use this parent block timestamp to determine whether Prague 1 is active.
9198
// This means that we technically start the base_fee enforcement the block after the fork
9299
// block. This is a conscious decision to minimize fork diffs across execution clients.
93-
let min_base_fee =
94-
if self.is_prague1_active_at_timestamp(parent.timestamp()) { 1_000_000_000 } else { 0 };
100+
let min_base_fee = if self.is_prague1_active_at_timestamp(parent.timestamp()) {
101+
PRAGUE1_MIN_BASE_FEE_WEI
102+
} else {
103+
DEFAULT_MIN_BASE_FEE_WEI
104+
};
95105

96106
raw.max(min_base_fee)
97107
}
@@ -137,7 +147,7 @@ impl EthExecutorSpec for BerachainChainSpec {
137147
}
138148
}
139149

140-
/// Berachain chain specification parser.
150+
/// Parser for Berachain chain specifications
141151
#[derive(Debug, Clone, Default)]
142152
#[non_exhaustive]
143153
pub struct BerachainChainSpecParser;
@@ -201,9 +211,12 @@ impl From<Genesis> for BerachainChainSpec {
201211
};
202212

203213
// Time-based hardforks
214+
// For the From implementation, we use a default config if parsing fails
215+
// This maintains backward compatibility while preventing panics
204216
let berachain_genesis_config =
205217
BerachainGenesisConfig::try_from(&genesis.config.extra_fields).unwrap_or_else(|e| {
206-
panic!("failed to parse berachain genesis config from genesis file: {e}")
218+
tracing::warn!("Failed to parse berachain genesis config, using defaults: {}", e);
219+
BerachainGenesisConfig::default()
207220
});
208221

209222
let time_hardfork_opts = [

src/genesis/mod.rs

Lines changed: 71 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,92 @@
1+
//! Berachain genesis configuration parsing and validation
2+
13
use jsonrpsee_core::__reexports::serde_json;
24
use reth::rpc::types::serde_helpers::OtherFields;
3-
use serde::{Deserialize, Serialize, de::Error};
5+
use serde::{Deserialize, Serialize};
6+
use thiserror::Error;
7+
8+
/// Errors for Berachain genesis configuration parsing
9+
#[derive(Debug, Error)]
10+
pub enum BerachainConfigError {
11+
/// The required 'berachain' field is missing from the genesis configuration
12+
#[error("Missing required 'berachain' field in genesis configuration")]
13+
MissingBerachainField,
14+
15+
/// Invalid configuration format or values
16+
#[error("Invalid berachain configuration: {0}")]
17+
InvalidConfig(#[from] serde_json::Error),
18+
19+
/// Base fee change denominator cannot be zero as it would cause division by zero
20+
#[error("Base fee change denominator cannot be zero")]
21+
InvalidDenominator,
422

23+
/// Fork activation time is invalid (e.g., in the past for future forks)
24+
#[error("Invalid fork activation time: {0}")]
25+
InvalidActivationTime(u64),
26+
}
27+
28+
/// Configuration for a Berachain hardfork activation
529
#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
630
#[serde(rename_all = "camelCase")]
731
pub struct BerachainForkConfig {
32+
/// Unix timestamp when this hardfork activates
833
pub time: u64,
34+
/// Denominator for base fee change calculations (must be > 0)
935
pub base_fee_change_denominator: u128,
36+
/// Minimum base fee in wei enforced after activation
1037
pub minimum_base_fee_wei: u64,
1138
}
1239

40+
/// Complete Berachain genesis configuration from JSON "berachain" field
1341
#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
1442
#[serde(rename_all = "camelCase")]
1543
pub struct BerachainGenesisConfig {
44+
/// Configuration for the Prague1 hardfork, which introduces minimum base fee enforcement
1645
pub prague1: BerachainForkConfig,
1746
}
1847

48+
impl Default for BerachainGenesisConfig {
49+
/// Default config with Prague1 activated immediately at genesis
50+
fn default() -> Self {
51+
Self {
52+
prague1: BerachainForkConfig {
53+
time: 0, // Activate immediately at genesis
54+
base_fee_change_denominator: 48, // Berachain standard value
55+
minimum_base_fee_wei: 1_000_000_000, // 1 gwei
56+
},
57+
}
58+
}
59+
}
60+
61+
impl BerachainForkConfig {
62+
/// Creates validated config. Returns error if denominator is 0.
63+
pub fn new(
64+
time: u64,
65+
base_fee_change_denominator: u128,
66+
minimum_base_fee_wei: u64,
67+
) -> Result<Self, BerachainConfigError> {
68+
if base_fee_change_denominator == 0 {
69+
return Err(BerachainConfigError::InvalidDenominator);
70+
}
71+
Ok(Self { time, base_fee_change_denominator, minimum_base_fee_wei })
72+
}
73+
}
74+
1975
impl TryFrom<&OtherFields> for BerachainGenesisConfig {
20-
type Error = serde_json::Error;
76+
type Error = BerachainConfigError;
2177

78+
/// Parse BerachainGenesisConfig from genesis "berachain" field
2279
fn try_from(others: &OtherFields) -> Result<Self, Self::Error> {
2380
match others.get_deserialized::<Self>("berachain") {
24-
Some(Ok(cfg)) => Ok(cfg),
25-
Some(Err(e)) => Err(e), // propagate the real serde error inside
26-
None => Err(Error::missing_field("berachain")),
81+
Some(Ok(cfg)) => {
82+
// Validate the parsed configuration
83+
if cfg.prague1.base_fee_change_denominator == 0 {
84+
return Err(BerachainConfigError::InvalidDenominator);
85+
}
86+
Ok(cfg)
87+
}
88+
Some(Err(e)) => Err(BerachainConfigError::InvalidConfig(e)),
89+
None => Err(BerachainConfigError::MissingBerachainField),
2790
}
2891
}
2992
}
@@ -45,7 +108,9 @@ mod tests {
45108
let other_fields = OtherFields::try_from(v).expect("must be a valid genesis config");
46109
let res = BerachainGenesisConfig::try_from(&other_fields);
47110
assert!(
48-
res.expect_err("must be an error").to_string().contains("missing field `berachain`")
111+
res.expect_err("must be an error")
112+
.to_string()
113+
.contains("Missing required 'berachain' field")
49114
);
50115
}
51116

src/hardforks/mod.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
1+
//! Berachain hardfork definitions for use alongside Ethereum hardforks
2+
13
use reth::chainspec::{EthereumHardforks, ForkCondition, hardfork};
24

35
hardfork!(
4-
/// The name of a berachain hardfork.
5-
///
6-
/// When building a list of hardforks for a chain, it's still expected to mix with [`EthereumHardfork`].
6+
/// Berachain hardforks to be mixed with [`EthereumHardfork`]
77
BerachainHardfork {
8-
/// Berachain `Prague1` hardfork
8+
/// Prague1 hardfork: Enforces 1 gwei minimum base fee
99
Prague1,
1010
}
1111
);
1212

13+
/// Trait for querying Berachain hardfork activation status
1314
pub trait BerachainHardforks: EthereumHardforks {
15+
/// Returns activation condition for a Berachain hardfork
1416
fn berachain_fork_activation(&self, fork: BerachainHardfork) -> ForkCondition;
17+
18+
/// Checks if Prague1 hardfork is active at given timestamp
1519
fn is_prague1_active_at_timestamp(&self, timestamp: u64) -> bool {
1620
self.berachain_fork_activation(BerachainHardfork::Prague1).active_at_timestamp(timestamp)
1721
}

src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
//! Bera-Reth: Ethereum execution client for Berachain
2+
//!
3+
//! Built on Reth SDK with Ethereum compatibility plus Prague1 hardfork for minimum base fee.
4+
15
pub mod chainspec;
26
pub mod genesis;
37
pub mod hardforks;

0 commit comments

Comments
 (0)