Skip to content
Draft
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
39 changes: 36 additions & 3 deletions ADVANCED_README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,32 @@ foc-devnet stop
- Preserves Portainer for persistent access
- Clears run ID

### Pausing and Resuming (Resource Saving)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm learning that this might not be all that good, Curio doesn't seem to like sleeping, its harmony task heartbeat seems to get out of whack and it needs a restart so I might have to remove these instructions, or at least provide curio restart instructions.


The devnet cluster can consume significant CPU even when idle. Instead of stopping and restarting, you can pause containers to freeze all processes while preserving state.

**Pause the cluster (instant, stops all CPU usage):**
```bash
docker ps --filter "name=foc-" -q | xargs docker pause
```

**Resume the cluster (instant):**
```bash
docker ps --filter "name=foc-" -q | xargs docker unpause
```

**Verify paused state:**
```bash
docker ps --filter "name=foc-" --format "{{.Names}}: {{.Status}}"
# Shows: foc-<run-id>-lotus: Up 12 minutes (Paused)
```

**Convenience aliases (add to ~/.bashrc or ~/.zshrc):**
```bash
alias foc-pause='docker ps --filter "name=foc-" -q | xargs docker pause && echo "Cluster paused"'
alias foc-unpause='docker ps --filter "name=foc-" -q | xargs docker unpause && echo "Cluster resumed"'
```

### `status`
Shows the current status of the foc-devnet system.

Expand Down Expand Up @@ -1090,9 +1116,6 @@ docker logs foc-<run-id>-curio-2
# Query provider IDs
cat ~/.foc-devnet/state/latest/pdp_sps/*.provider_id.json

# Access Yugabyte (shared by all SPs)
docker exec -it foc-<run-id>-yugabyte ysqlsh -h localhost -p 5433

# Query Lotus for miner info
docker exec foc-<run-id>-lotus lotus state miner-info f01000

Expand All @@ -1103,6 +1126,16 @@ docker exec foc-<run-id>-builder cast call \
<provider_id>
```

### Querying Yugabyte Database

Each Curio has its own Yugabyte (curio-N → yugabyte-N). Tables are in `curio` schema. Credentials: `yugabyte`/`yugabyte`/`yugabyte` (user/pass/db).

```bash
docker exec foc-<run-id>-yugabyte-1 bash -c "PGPASSWORD=yugabyte /yugabyte/bin/ysqlsh -h 127.0.0.1 -U yugabyte -d yugabyte -c \"<SQL>\""
```

Key tables: `curio.harmony_machines`, `curio.harmony_task`, `curio.harmony_task_history`, `curio.parked_pieces`.

---

## Troubleshooting
Expand Down
21 changes: 17 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,27 @@ A developer-friendly tool for spinning up complete Filecoin test networks with s

Get up and running in three simple steps:

### Step 0: Ensure non-root user
`foc-devnet` requires itself to be run by a non-root user. Please ensure that you are running as a non-root user which is part of `docker` group.
### Prerequisites

Run the following to see your User ID and groups you are a part of:
```
**Non-root user with Docker access**: `foc-devnet` must be run by a non-root user in the `docker` group.

```bash
echo $(id -u); groups | grep 'docker'
```

**Configure host.docker.internal**: Add this entry to `/etc/hosts` so SP URLs work from both host and containers:

```bash
echo '127.0.0.1 host.docker.internal' | sudo tee -a /etc/hosts
```

This is required for SP-to-SP fetch. `foc-devnet start` will check for this and fail with instructions if not configured.

For GitHub Actions, add this step before running foc-devnet:
```yaml
- run: echo '127.0.0.1 host.docker.internal' | sudo tee -a /etc/hosts
```

### Step 1: Initialize

```bash
Expand Down
2 changes: 2 additions & 0 deletions src/commands/start/curio/daemon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@ fn build_docker_create_args(
container_name.to_string(),
"--network".to_string(),
pdp_miner_network_name(run_id, sp_index),
// Enable host.docker.internal for SP-to-SP fetch (resolves to host gateway)
"--add-host=host.docker.internal:host-gateway".to_string(),
];

// Port mappings - get dynamically allocated ports from context
Expand Down
3 changes: 3 additions & 0 deletions src/commands/start/curio/db_setup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ pub fn build_foc_contract_env_vars(context: &SetupContext) -> Result<Vec<String>
.to_string(),
);

// Allow insecure sources (HTTP, localhost, private IPs) for SP-to-SP fetch in devnet
env_vars.push("CURIO_FETCH_ALLOW_INSECURE=1".to_string());

Ok(env_vars)
}

Expand Down
68 changes: 67 additions & 1 deletion src/commands/start/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,71 @@ use crate::paths::{foc_devnet_config, foc_devnet_run_dir};
use crate::run_id::{create_latest_symlink, save_current_run_id};
use crate::version_info::write_version_file;
pub use eth_acc_funding::constants::FEVM_ACCOUNTS_PREFUNDED;
use std::net::ToSocketAddrs;
use std::path::{Path, PathBuf};
use tracing::{info, warn};
use tracing::{error, info, warn};

/// Check that host.docker.internal resolves to 127.0.0.1.
///
/// This is required for SP-to-SP fetch to work. The hostname must resolve to localhost
/// so that URLs registered in the SP registry work from both the host and inside containers.
///
/// On macOS with Docker Desktop, this works automatically.
/// On Linux, users must add `127.0.0.1 host.docker.internal` to /etc/hosts.
fn check_host_docker_internal() -> Result<(), Box<dyn std::error::Error>> {
info!("Checking host.docker.internal resolution...");

// Try to resolve host.docker.internal:80 (port doesn't matter, just need DNS resolution)
match "host.docker.internal:80".to_socket_addrs() {
Ok(mut addrs) => {
// Check if any resolved address is 127.0.0.1
let is_localhost = addrs.any(|addr| addr.ip().is_loopback());

if is_localhost {
info!("✓ host.docker.internal resolves to localhost");
Ok(())
} else {
error!("════════════════════════════════════════════════════════════════════");
error!("ERROR: host.docker.internal does not resolve to localhost (127.0.0.1)");
error!("════════════════════════════════════════════════════════════════════");
error!("");
error!("SP-to-SP fetch requires host.docker.internal to resolve to 127.0.0.1");
error!("so that registered SP URLs work from both host and containers.");
error!("");
error!("To fix this, add the following line to /etc/hosts:");
error!("");
error!(" 127.0.0.1 host.docker.internal");
error!("");
error!("You can do this with:");
error!(" echo '127.0.0.1 host.docker.internal' | sudo tee -a /etc/hosts");
error!("");
error!("════════════════════════════════════════════════════════════════════");
Err("host.docker.internal must resolve to 127.0.0.1".into())
}
}
Err(_) => {
error!("════════════════════════════════════════════════════════════════════");
error!("ERROR: host.docker.internal does not resolve");
error!("════════════════════════════════════════════════════════════════════");
error!("");
error!("SP-to-SP fetch requires host.docker.internal to resolve to 127.0.0.1");
error!("so that registered SP URLs work from both host and containers.");
error!("");
error!("Add the following line to /etc/hosts:");
error!("");
error!(" 127.0.0.1 host.docker.internal");
error!("");
error!("You can do this with:");
error!(" echo '127.0.0.1 host.docker.internal' | sudo tee -a /etc/hosts");
error!("");
error!("For GitHub Actions, add this step before running foc-devnet:");
error!(" - run: echo '127.0.0.1 host.docker.internal' | sudo tee -a /etc/hosts");
error!("");
error!("════════════════════════════════════════════════════════════════════");
Err("host.docker.internal must be resolvable".into())
}
}
}

/// Stop any existing cluster before starting a new one.
fn stop_existing_cluster() -> Result<(), Box<dyn std::error::Error>> {
Expand Down Expand Up @@ -411,6 +474,9 @@ pub fn start_cluster(
run_id: String,
notest: bool,
) -> Result<(), Box<dyn std::error::Error>> {
// Check host.docker.internal resolution first (required for SP-to-SP fetch)
check_host_docker_internal()?;

stop_existing_cluster()?;

let (volumes_dir, run_dir, run_id) =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,8 @@ impl Step for PdpSpRegistrationStep {
let mut provider_ids = Vec::new();

for (sp_index, sp_address, sp_eth_address, pdp_port, should_approve) in sp_data {
let service_url = format!("http://localhost:{}", pdp_port);
// Use host.docker.internal so the URL works from both host and containers
let service_url = format!("http://host.docker.internal:{}", pdp_port);

match registration::register_single_provider(
&registration::ProviderRegistrationParams {
Expand Down
4 changes: 2 additions & 2 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,8 +228,8 @@ impl Default for Config {
commit: "773551bf1e9cf4cdc49aeb63a47a81f8dc5cb9e1".to_string(),
},
yugabyte_download_url: "https://software.yugabyte.com/releases/2.25.1.0/yugabyte-2.25.1.0-b381-linux-x86_64.tar.gz".to_string(),
approved_pdp_sp_count: 1,
active_pdp_sp_count: 1,
approved_pdp_sp_count: 2,
active_pdp_sp_count: 2,
}
}
}
Expand Down