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
3 changes: 1 addition & 2 deletions src/commands/start/lotus/lotus_step.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use super::super::step::{SetupContext, Step};
use super::container_management::{
check_existing_container, start_container, wait_for_container_init,
};
use super::prerequisites::{check_genesis_and_params, check_image_and_binary};
use super::prerequisites::check_genesis_and_params;
use super::setup::{build_docker_command, setup_directories};
use super::verification::{verify_api_connectivity, verify_ports, wait_for_api_file};
use std::error::Error;
Expand Down Expand Up @@ -39,7 +39,6 @@ impl Step for LotusStep {
/// Perform pre-execution checks
fn pre_execute(&self, context: &SetupContext) -> Result<(), Box<dyn Error>> {
let run_id = context.run_id();
check_image_and_binary()?;
check_genesis_and_params(run_id)?;

// Allocate ports for Lotus
Expand Down
27 changes: 1 addition & 26 deletions src/commands/start/lotus/prerequisites.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@
//! before starting the Lotus daemon container.

use super::super::genesis::constants::GENESIS_FILE;
use crate::constants::LOTUS_DOCKER_IMAGE;
use crate::paths::{
foc_devnet_bin, foc_devnet_genesis, foc_devnet_genesis_sectors, foc_devnet_proof_parameters,
};
use crate::paths::{foc_devnet_genesis, foc_devnet_genesis_sectors, foc_devnet_proof_parameters};
use std::error::Error;
use tracing::info;

Expand All @@ -26,28 +23,6 @@ pub fn verify_genesis_file(run_id: &str) -> Result<std::path::PathBuf, Box<dyn E
Ok(genesis_file)
}

/// Check that required Docker image and Lotus binary exist
pub fn check_image_and_binary() -> Result<(), Box<dyn Error>> {
// Verify Docker image exists
if !crate::docker::core::image_exists(LOTUS_DOCKER_IMAGE).unwrap_or(true) {
return Err(format!(
"Docker image '{}' not found. Please run 'foc-devnet init' to build the image.",
LOTUS_DOCKER_IMAGE
)
.into());
}
info!("✓ Docker image '{}' found", LOTUS_DOCKER_IMAGE);

// Verify lotus binary exists
let lotus_bin = foc_devnet_bin().join("lotus");
if !lotus_bin.exists() {
return Err("Lotus binary not found. Please run 'foc-devnet build lotus' first.".into());
}

info!("✓ Lotus binary found");
Ok(())
}

/// Check that genesis file, proof parameters, and sectors exist
pub fn check_genesis_and_params(run_id: &str) -> Result<(), Box<dyn Error>> {
// Verify genesis file exists
Expand Down
36 changes: 22 additions & 14 deletions src/commands/start/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ mod lotus_miner;
mod lotus_utils;
mod multicall3_deploy;
mod pdp_service_provider;
pub mod prerequisites_check;
pub mod step;
mod synapse_test_e2e;
mod usdfc_deploy;
Expand All @@ -23,6 +24,7 @@ use lotus::LotusStep;
use lotus_miner::LotusMinerStep;
use multicall3_deploy::MultiCall3DeployStep;
use pdp_service_provider::PdpSpRegistrationStep;
use prerequisites_check::PrerequisitesCheckStep;
pub use step::{execute_steps, execute_steps_parallel, SetupContext, Step};
use synapse_test_e2e::SynapseTestE2EStep;
use usdfc_deploy::USDFCDeployStep;
Expand Down Expand Up @@ -254,18 +256,22 @@ fn create_steps(
///
/// # Parallelization Strategy
///
/// - Epoch 1: Lotus + Yugabyte (independent services)
/// - Epoch 2: Lotus Miner (depends on Lotus)
/// - Epoch 3: ETH Account Funding (needs blockchain running)
/// - Epoch 4: MockUSDFC Deploy + MultiCall3 Deploy + FOC Deploy (can be parallelized)
/// - Epoch 5: MockUSDFC Funding + Curio daemons (can be parallelized, needs FOC Deploy)
/// - Epoch 6: PDP SP Registration (needs Curio daemons started)
/// - Epoch 1: Prerequisites check (binaries & Docker images - must run first)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the README_ADVANCED.md also get updated?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not necessary, this is an internal check/guardrail, that is optional. It is done for better UX.

/// - Epoch 2: Lotus (daemon start)
/// - Epoch 3: Lotus Miner (depends on Lotus)
/// - Epoch 4: ETH Account Funding (needs blockchain running)
/// - Epoch 5: MockUSDFC Deploy + MultiCall3 Deploy (can be parallelized)
/// - Epoch 6: FOC Deploy + MockUSDFC Funding + Yugabyte (can be parallelized, needs USDFC deployed)
/// - Epoch 7: Curio daemons (needs Yugabyte)
/// - Epoch 8: PDP SP Registration (needs Curio running, for port information)
/// - Epoch 9: Synapse E2E Test (final validation)
fn create_step_epochs(
volumes_dir: &Path,
run_dir: &Path,
config: &Config,
notest: bool,
) -> Vec<Vec<Box<dyn Step>>> {
let prerequisites_check_step = PrerequisitesCheckStep::new();
let lotus_step = LotusStep::new(volumes_dir.to_path_buf(), run_dir.to_path_buf());
let yugabyte_step = YugabyteStep::new(
volumes_dir.to_path_buf(),
Expand Down Expand Up @@ -296,28 +302,30 @@ fn create_step_epochs(
SynapseTestE2EStep::new(volumes_dir.to_path_buf(), run_dir.to_path_buf(), notest);

vec![
// Epoch 1: Start Lotus
// Epoch 1: Prerequisites check (binaries & Docker images - must run first)
vec![Box::new(prerequisites_check_step)],
// Epoch 2: Start Lotus
vec![Box::new(lotus_step)],
// Epoch 2: Start Lotus Miner (depends on Lotus)
// Epoch 3: Start Lotus Miner (depends on Lotus)
vec![Box::new(lotus_miner_step)],
// Epoch 3: ETH Account Funding (needs blockchain running)
// Epoch 4: ETH Account Funding (needs blockchain running)
vec![Box::new(eth_acc_funding_step)],
// Epoch 4: Deploy contracts (can be parallelized)
// Epoch 5: Deploy contracts (can be parallelized)
vec![
Box::new(usdfc_deploy_step),
Box::new(multicall3_deploy_step),
],
// Epoch 5: Fund accounts with USDFC, deploy foc (needs usdfc deployed), start yugabyte for curio later
// Epoch 6: Fund accounts with USDFC, deploy foc (needs usdfc deployed), start yugabyte for curio later
vec![
Box::new(foc_deploy_step),
Box::new(usdfc_funding_step),
Box::new(yugabyte_step),
],
// Epoch 6: Start Curio daemons
// Epoch 7: Start Curio daemons
vec![Box::new(curio_step)],
// Epoch 7: Register PDP SPs (needs Curio running, for port information)
// Epoch 8: Register PDP SPs (needs Curio running, for port information)
vec![Box::new(pdp_sp_reg_step)],
// Epoch 8: Run Synapse E2E Test
// Epoch 9: Run Synapse E2E Test
vec![Box::new(synapse_test_step)],
]
}
Expand Down
140 changes: 140 additions & 0 deletions src/commands/start/prerequisites_check.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
//! Binary and Docker image availability check for cluster startup.
//!
//! This module provides a unified check for all required binaries and Docker images
//! before starting the cluster. It provides clear error messages directing users to
//! build missing components.

use super::step::{SetupContext, Step};
use crate::constants::{REQUIRED_BINARIES, REQUIRED_DOCKER_IMAGES};
use crate::docker::core::image_exists;
use crate::paths::foc_devnet_bin;
use std::error::Error;
use tracing::info;

/// Check that all required binaries exist in the bin directory.
///
/// This check runs early in cluster startup before any containers are started,
/// ensuring that we fail fast with a helpful message if binaries are missing.
///
/// # Errors
///
/// Returns an error if any required binary is missing, listing all missing binaries
/// and providing instructions on how to build them.
fn check_all_binaries() -> Result<(), Box<dyn Error>> {
let bin_dir = foc_devnet_bin();

let mut missing_binaries = Vec::new();
let mut found_binaries = Vec::new();

for binary_name in REQUIRED_BINARIES {
let binary_path = bin_dir.join(binary_name);
if binary_path.exists() {
found_binaries.push(*binary_name);
} else {
missing_binaries.push(*binary_name);
}
}

// Log what we found
for binary in &found_binaries {
info!("✓ Binary '{}' found", binary);
}

// If any binaries are missing, return an error with instructions
if !missing_binaries.is_empty() {
let missing_list = missing_binaries.join("', '");
return Err(format!(
"Missing required binaries: '{}'\n\nPlease build them with 'foc-devnet build <name>' \
(e.g., 'foc-devnet build lotus' or 'foc-devnet build curio')",
missing_list
)
.into());
}

info!("✓ All required binaries are available");
Ok(())
}

/// Check that all required Docker images exist.
///
/// This check runs early in cluster startup before any containers are started,
/// ensuring that we fail fast with a helpful message if Docker images are missing.
///
/// # Errors
///
/// Returns an error if any required Docker image is missing, listing all missing images
/// and providing instructions on how to build them.
fn check_all_docker_images() -> Result<(), Box<dyn Error>> {
let mut missing_images = Vec::new();
let mut found_images = Vec::new();

for image_name in REQUIRED_DOCKER_IMAGES {
let exists = image_exists(image_name)
.map_err(|e| format!("Failed to check Docker image '{}': {}", image_name, e))?;

if exists {
found_images.push(*image_name);
} else {
missing_images.push(*image_name);
}
}

// Log what we found
for image in &found_images {
info!("✓ Docker image '{}' found", image);
}

// If any images are missing, return an error with instructions
if !missing_images.is_empty() {
let missing_list = missing_images.join("', '");
return Err(format!(
"Missing required Docker images: '{}'\n\nPlease run 'foc-devnet init' to build all Docker images.",
missing_list
)
.into());
}

info!("✓ All required Docker images are available");
Ok(())
}

/// Prerequisites check step for cluster startup.
///
/// This step verifies that all required binaries and Docker images are available
/// before starting any containers. It runs as the very first step in the startup sequence.
pub struct PrerequisitesCheckStep;

impl PrerequisitesCheckStep {
/// Create a new PrerequisitesCheckStep
pub fn new() -> Self {
Self
}
}

impl Default for PrerequisitesCheckStep {
fn default() -> Self {
Self::new()
}
}

impl Step for PrerequisitesCheckStep {
fn name(&self) -> &str {
"Prerequisites Check (Binaries & Docker Images)"
}

fn pre_execute(&self, _context: &SetupContext) -> Result<(), Box<dyn Error>> {
// No pre-checks needed
Ok(())
}

fn execute(&self, _context: &SetupContext) -> Result<(), Box<dyn Error>> {
check_all_binaries()?;
check_all_docker_images()?;
Ok(())
}

fn post_execute(&self, _context: &SetupContext) -> Result<(), Box<dyn Error>> {
// No post-checks needed
Ok(())
}
}
17 changes: 6 additions & 11 deletions src/commands/status/build_status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
//! - Display build timestamps
//! - Show relative time since build

use crate::paths::foc_devnet_bin;
use crate::{constants::REQUIRED_BINARIES, paths::foc_devnet_bin};
use chrono::{DateTime, Utc};
use std::process::Command;
use tracing::info;
Expand All @@ -19,6 +19,9 @@ use super::utils::format_time_ago;
/// This function displays the build status of all expected foc-devnet binaries,
/// including whether they exist, their file sizes, and when they were last built.
///
/// Note: The list of expected binaries is shared with the startup binary check
/// to ensure consistency.
///
/// # Examples
///
/// ```rust,no_run
Expand All @@ -33,16 +36,8 @@ use super::utils::format_time_ago;
pub fn print_build_status() -> Result<(), Box<dyn std::error::Error>> {
let bin_dir = foc_devnet_bin();

// Check for expected binaries
let expected_binaries = vec![
"lotus",
"lotus-miner",
"lotus-shed",
"lotus-seed",
"curio",
"pdptool",
"sptool",
];
// Get expected binaries from shared source of truth
let expected_binaries = REQUIRED_BINARIES;

for binary in expected_binaries {
let binary_path = bin_dir.join(binary);
Expand Down
20 changes: 20 additions & 0 deletions src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,26 @@ pub const YUGABYTE_DOCKER_IMAGE: &str = "foc-yugabyte";
pub const CURIO_DOCKER_IMAGE: &str = "foc-curio";
pub const PORTAINER_DOCKER_IMAGE: &str = "foc-portainer";

/// Required binaries for cluster startup
pub const REQUIRED_BINARIES: &[&str] = &[
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also check for Docker as part of #29 ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not necessary. Docker sock failure is pretty evident in later part of the program anyways.

"lotus",
"lotus-miner",
"lotus-shed",
"lotus-seed",
"curio",
"pdptool",
"sptool",
];

/// Required Docker images for cluster startup
pub const REQUIRED_DOCKER_IMAGES: &[&str] = &[
LOTUS_DOCKER_IMAGE,
LOTUS_MINER_DOCKER_IMAGE,
BUILDER_DOCKER_IMAGE,
YUGABYTE_DOCKER_IMAGE,
CURIO_DOCKER_IMAGE,
];

/// Docker container names (base - will be prefixed with foc-c-<RUN_ID>- in practice)
pub const LOTUS_CONTAINER: &str = "foc-c-lotus";
pub const LOTUS_MINER_CONTAINER: &str = "foc-c-lotus-miner";
Expand Down