Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5,531 changes: 822 additions & 4,709 deletions Cargo.lock

Large diffs are not rendered by default.

15 changes: 0 additions & 15 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,12 @@ version = "0.1.0"
edition = "2021"

[dependencies]
summit-types = {git = "https://github.com/SeismicSystems/summit.git", rev = "bd13c7e80176fd9efaad6e92e80b67a7cd54423e"}

# summit-types at rev bd13c7e was written against commonware-* 2026.2.0. Cargo otherwise
# resolves these to 2026.3.0 (trait API incompatible), so pin them explicitly.
commonware-consensus = "=2026.2.0"
commonware-cryptography = "=2026.2.0"
commonware-codec = "=2026.2.0"
commonware-math = "=2026.2.0"
commonware-utils = "=2026.2.0"
commonware-resolver = "=2026.2.0"
commonware-p2p = "=2026.2.0"
commonware-runtime = "=2026.2.0"
commonware-parallel = "=2026.2.0"
# Async runtime
tokio = { version = "1.42", features = ["full"] }

# HTTP client for RPC
reqwest = { version = "0.12", features = ["json"] }

ethereum_ssz = "0.9.0"

# JSON-RPC client
jsonrpsee = { version = "0.26.0", features = ["http-client", "client-core", "server", "macros"] }

Expand Down
41 changes: 26 additions & 15 deletions config.example.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Summit-Checkpointer Example Configuration
# Copy this file to config.toml and adjust for your setup
# Copy this file to config.toml and adjust for your setup.
#
# The RPC server bind address and port are CLI options rather than TOML values:
# --bind-address 0.0.0.0 # Existing default; use 127.0.0.1 for local-only RPC
# --port 42069

# ============================================================================
# Reth Configuration
Expand All @@ -21,23 +25,26 @@ db_path = "/var/lib/reth/db"
# Checkpointing Configuration
# ============================================================================
[checkpoint]
# Number of blocks per epoch (checkpoint created at each epoch boundary)
# Example: 1000 means checkpoints at blocks 1000, 2000, 3000, etc.
# For testing, you can use a smaller value like 10 or 100
# Fixed checkpoint interval used only when [summit].enabled = false.
# When Summit integration is enabled, the checkpointer discovers completed
# checkpoint epochs and their exact historical height bounds through Summit RPC,
# so on-chain epoch-length changes are handled automatically.
epoch_blocks = 1000

# Number of blocks to wait after epoch boundary before creating checkpoint
# This gives reth time to flush all data to disk
# Example: If epoch_blocks=1000 and checkpoint_delay_blocks=3,
# the checkpoint will be created when we reach block 1003 (for epoch at block 1000)
# Recommended: 2-5 blocks depending on your setup
# Number of blocks to wait after the epoch boundary before creating a checkpoint.
# This gives Reth time to flush data. With Summit enabled, the exact boundary is
# obtained from getEpochBounds rather than calculated from epoch_blocks.
# Recommended: 2-5 blocks depending on your setup.
checkpoint_delay_blocks = 3

# Directory where checkpoints will be saved
# Each checkpoint creates a subdirectory like: epoch_5_block_1000/
# Make sure this location has enough disk space (each checkpoint can be large)
# Directory where checkpoints will be saved.
# Each checkpoint creates epoch_N/epoch_N.tar.gz with metadata.json alongside it.
# Make sure this location has enough disk space (each checkpoint can be large).
output_dir = "/var/lib/reth/checkpoints"

# Maximum number of snapshots to retain. Optional; when set, must be at least 1.
# max_snapshots = 50

# Use compact mode when copying the MDBX database
# This reduces the checkpoint size by compacting the database during copy
# Recommended: true (unless you need to preserve exact database layout)
Expand Down Expand Up @@ -65,14 +72,18 @@ enabled = true
# This is used to fetch checkpoint data for each epoch
rpc_url = "http://localhost:5052"

# Maximum duration for an individual Summit RPC request.
# This is separate from monitor.retry_interval_secs, which controls retry backoff.
rpc_timeout_secs = 100

# ============================================================================
# Monitoring Configuration
# ============================================================================
[monitor]
# How often to poll reth for the current block number (in seconds)
# Default: 12 seconds (approximately one Ethereum block time)
# Default: 30 seconds
# For faster networks or testing, you can reduce this
poll_interval_secs = 12
poll_interval_secs = 30

# How long to wait after an RPC failure before retrying (in seconds)
# This prevents spamming the RPC endpoint if reth is down
Expand Down Expand Up @@ -116,7 +127,7 @@ format = "pretty"
# compact = true
#
# [monitor]
# poll_interval_secs = 12
# poll_interval_secs = 30
#
# [logging]
# level = "info"
Expand Down
17 changes: 10 additions & 7 deletions config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ db_path = "/var/lib/reth/db"

# Checkpointing Configuration
[checkpoint]
# Number of blocks per epoch (checkpoint created at each epoch boundary)
# Fixed checkpoint interval used only when Summit integration is disabled.
# Summit-enabled mode discovers exact epoch bounds through Summit RPC.
epoch_blocks = 1000

# Number of blocks to wait after epoch boundary before creating checkpoint
# This gives reth time to flush all data to disk
# Number of blocks to wait after the exact epoch boundary before checkpointing.
checkpoint_delay_blocks = 3

# Directory where checkpoints will be saved
Expand All @@ -26,8 +26,8 @@ compact = true
# Path to mdbx_copy binary (can be absolute path or just "mdbx_copy" if in PATH)
mdbx_copy_path = "mdbx_copy"

# Maximum amount of snapshots we will hold in output_dir. Optional
#max_snapshots = 50
# Maximum number of snapshots to retain. Optional; when set, must be at least 1.
# max_snapshots = 50

# Path to reth binary (can be absolute path or just "reth" if in PATH)
reth_path = "reth"
Expand All @@ -40,11 +40,14 @@ enabled = false
# Summit RPC endpoint
rpc_url = "http://localhost:5052"

# Maximum duration for an individual Summit RPC request.
rpc_timeout_secs = 100

# Monitoring Configuration
[monitor]
# How often to check block number (in seconds)
# Default: 12 seconds (approximately one Ethereum block)
poll_interval_secs = 12
# Default: 30 seconds
poll_interval_secs = 30

# How long to wait after RPC failure before retrying (in seconds)
retry_interval_secs = 60
Expand Down
55 changes: 46 additions & 9 deletions src/checkpoint/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,13 +188,7 @@ impl CheckpointExecutor {
pub async fn unwind_database(&self, checkpoint_dir: &Path, target_block: u64) -> Result<()> {
tracing::info!("Unwinding database at {:?} to block {}", checkpoint_dir, target_block);

let mut cmd = Command::new(&self.reth_path);
cmd.arg("stage")
.arg("unwind")
.arg("--datadir")
.arg(checkpoint_dir)
.arg("to-block")
.arg(target_block.to_string());
let mut cmd = reth_unwind_command(&self.reth_path, checkpoint_dir, target_block);

// Capture stdout/stderr
cmd.stdout(Stdio::piped());
Expand Down Expand Up @@ -229,9 +223,9 @@ impl CheckpointExecutor {

tracing::info!("Compressing checkpoint to {:?}", archive_name);

// Build tar command to compress db, static_files, and summit_checkpoint
// Build tar command to compress the execution snapshot and metadata.
let mut cmd = Command::new("tar");
cmd.arg("-czf").arg(&archive_name).arg("db").arg("static_files");
cmd.arg("-czf").arg(&archive_name).arg("db").arg("static_files").arg("metadata.json");

// Only include summit_checkpoint if it exists
let summit_checkpoint_dir = checkpoint_dir.join("summit_checkpoint");
Expand Down Expand Up @@ -295,6 +289,22 @@ impl CheckpointExecutor {
}
}

fn reth_unwind_command(reth_path: &Path, checkpoint_dir: &Path, target_block: u64) -> Command {
let mut cmd = Command::new(reth_path);
// Offline maintenance commands do not need a live custodian. Explicitly use
// the built-in purpose keys because newer Seismic Reth versions default to
// fetching purpose keys from the custodian socket before every stage command.
cmd.arg("--seismic.purpose-keys-source")
.arg("built-in")
.arg("stage")
.arg("unwind")
.arg("--datadir")
.arg(checkpoint_dir)
.arg("to-block")
.arg(target_block.to_string());
cmd
}

/// Recursively copy a directory and its contents
fn copy_dir_recursive(source: &Path, destination: &Path) -> Result<()> {
if !destination.exists() {
Expand All @@ -315,3 +325,30 @@ fn copy_dir_recursive(source: &Path, destination: &Path) -> Result<()> {

Ok(())
}

#[cfg(test)]
mod tests {
use super::reth_unwind_command;
use std::{ffi::OsStr, path::Path};

#[test]
fn reth_unwind_uses_built_in_purpose_keys() {
let command = reth_unwind_command(Path::new("reth"), Path::new("/tmp/checkpoint"), 9998);
let args = command.as_std().get_args().collect::<Vec<&OsStr>>();

assert_eq!(
args,
[
"--seismic.purpose-keys-source",
"built-in",
"stage",
"unwind",
"--datadir",
"/tmp/checkpoint",
"to-block",
"9998",
]
.map(OsStr::new)
);
}
}
Loading
Loading