From 17fbdc3adf3e1c8d0bc9c576a1067898afbfdd35 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Fri, 21 Aug 2026 13:19:50 +0200 Subject: [PATCH 1/4] Update default dependencies and Synapse setup Signed-off-by: Jakub Sztandera --- scenarios/synapse.py | 6 +++++- src/config.rs | 40 ++++++++++++++++++++++++++-------------- 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/scenarios/synapse.py b/scenarios/synapse.py index d6b96e94..977ef8fd 100644 --- a/scenarios/synapse.py +++ b/scenarios/synapse.py @@ -78,10 +78,14 @@ def clone_and_build(tmp_dir: Path) -> Path | None: overrides = dependency.get("overrides", {}) if not apply_pnpm_workspace_overrides(sdk_dir, overrides): return None + # Persist the workaround because the build can launch a nested pnpm install. + workspace = sdk_dir / "pnpm-workspace.yaml" + policy = workspace.read_text().replace("trustPolicy: no-downgrade", "trustPolicy: off") + workspace.write_text(policy) # Install/build only what the e2e example needs, skipping the unrelated # playground, docs and react workspaces. if not run_cmd( - ["pnpm", "install", "--filter", "utils..."], + ["pnpm", "install", "--no-frozen-lockfile", "--filter", "utils..."], cwd=str(sdk_dir), label="pnpm install", ): diff --git a/src/config.rs b/src/config.rs index 45739854..4106f43c 100644 --- a/src/config.rs +++ b/src/config.rs @@ -278,8 +278,7 @@ impl Default for Config { /// node type and assumes pre-built executables are available in standard /// system locations (/usr/local/bin/). /// - /// The defaults should always use `GitCommit` or `GitTag` locations to ensure - /// reproducibility. + /// Defaults select the upstream revisions used by a new devnet. fn default() -> Self { Self { port_range_start: 5700, @@ -288,15 +287,18 @@ impl Default for Config { url: "https://github.com/filecoin-project/lotus.git".to_string(), tag: "v1.36.2".to_string(), }, - curio: Location::GitTag { + curio: Location::GitCommit { url: "https://github.com/filecoin-project/curio.git".to_string(), - tag: "v1.28.4".to_string(), + commit: "3f3d9633c09bf1e8343e722f3c16daa4de7cce98".to_string(), }, - filecoin_services: Location::GitTag { + filecoin_services: Location::GitCommit { url: "https://github.com/FilOzone/filecoin-services.git".to_string(), - tag: "v1.3.1".to_string(), + commit: "bb0c731de65aa1031abbd8f317bc56deeeb4514d".to_string(), }, - pdp: None, + pdp: Some(Location::GitCommit { + url: "https://github.com/FilOzone/pdp.git".to_string(), + commit: "cc3f5eaffee7df80471b671a4e35a42b000685b8".to_string(), + }), multicall3: Location::GitTag { url: "https://github.com/mds1/multicall3.git".to_string(), tag: "v3.1.0".to_string(), @@ -470,13 +472,23 @@ mod tests { } #[test] - fn default_config_omits_optional_pdp_and_parses_without_it() { - let serialized = toml::to_string(&Config::default()).unwrap(); - - assert!(!serialized.contains("[pdp")); - assert!(!serialized.contains("pdp =")); - let parsed: Config = toml::from_str(&serialized).unwrap(); - assert!(parsed.pdp.is_none()); + fn default_config_uses_requested_source_revisions() { + let config = Config::default(); + assert!(matches!( + config.curio, + Location::GitCommit { ref commit, .. } + if commit == "3f3d9633c09bf1e8343e722f3c16daa4de7cce98" + )); + assert!(matches!( + config.filecoin_services, + Location::GitCommit { ref commit, .. } + if commit == "bb0c731de65aa1031abbd8f317bc56deeeb4514d" + )); + assert!(matches!( + config.pdp, + Some(Location::GitCommit { ref commit, .. }) + if commit == "cc3f5eaffee7df80471b671a4e35a42b000685b8" + )); } #[test] From 947b47e56e18efe2ded8e2acafc3c8c97a438e89 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Fri, 21 Aug 2026 13:28:54 +0200 Subject: [PATCH 2/4] Reserve run IDs atomically Signed-off-by: Jakub Sztandera --- src/main.rs | 2 +- src/run_id/mod.rs | 68 ++++++++++++++++++++++++++--------------------- 2 files changed, 38 insertions(+), 32 deletions(-) diff --git a/src/main.rs b/src/main.rs index 203931e8..b7c8489b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,7 +14,7 @@ fn main() -> Result<(), Box> { let cli = Cli::parse(); // Generate a run ID for this execution and initialize logging - let run_id = generate_run_id(); + let run_id = generate_run_id()?; init_logging(&run_id)?; // Check for poison file and attempt recovery diff --git a/src/run_id/mod.rs b/src/run_id/mod.rs index 58020231..feb3affe 100644 --- a/src/run_id/mod.rs +++ b/src/run_id/mod.rs @@ -26,28 +26,35 @@ pub const NOUNS: &[&str] = &[ "Lulu", "Bear", "Fig", "Boo", ]; -/// Generate a unique run ID for this execution. +/// Generate and atomically reserve a unique run ID for this execution. /// -/// Returns a string like "20251215T2206_ZanyPip" where: -/// - 20251215 is the date (YYYYMMDD, condensed ISO8601 format) -/// - T is the date/time separator (ISO8601) -/// - 2206 is the time (HHMM, 24-hour format, no colons for Docker compatibility) -/// - ZanyPip is the random name (adjective + noun) -/// -/// Uses condensed ISO8601 format (no dashes or colons) for Docker network name compatibility. -pub fn generate_run_id() -> String { - let now = Local::now(); - let datetime = now.format("%Y%m%dT%H%M"); - - // Implement our own random name generator to control format - let random_name = { - let rng = &mut rand::rng(); - let adjective = ADJECTIVES.choose(rng).unwrap(); - let noun = NOUNS.choose(rng).unwrap(); - format!("{}{}", adjective, noun) - }; - - format!("{}_{}", datetime, random_name) +/// The ID uses condensed ISO8601 plus a readable random name. Its run +/// directory is created atomically, so concurrent processes cannot claim the +/// same ID. +pub fn generate_run_id() -> Result> { + reserve_run_id_in(&crate::paths::foc_devnet_runs()) +} + +/// Generate a readable candidate ID without reserving it. +fn generate_candidate() -> String { + let datetime = Local::now().format("%Y%m%dT%H%M"); + let rng = &mut rand::rng(); + let adjective = ADJECTIVES.choose(rng).unwrap(); + let noun = NOUNS.choose(rng).unwrap(); + format!("{}_{}{}", datetime, adjective, noun) +} + +/// Reserve an unused run ID under the supplied runs directory. +fn reserve_run_id_in(runs_dir: &std::path::Path) -> Result> { + std::fs::create_dir_all(runs_dir)?; + loop { + let run_id = generate_candidate(); + match std::fs::create_dir(runs_dir.join(&run_id)) { + Ok(()) => return Ok(run_id), + Err(error) if error.kind() == std::io::ErrorKind::AlreadyExists => continue, + Err(error) => return Err(error.into()), + } + } } /// Create a symlink to the latest run directory. @@ -110,7 +117,7 @@ mod tests { #[test] fn test_run_id_format() { - let run_id = generate_run_id(); + let run_id = generate_candidate(); // Should match pattern: YYYYMMDDTHHMM_RandomName (condensed ISO8601, no dashes/colons) let pattern = Regex::new(r"^\d{8}T\d{4}_.+$").unwrap(); @@ -122,14 +129,14 @@ mod tests { } #[test] - fn test_run_ids_are_different() { - // Generate multiple IDs in quick succession - // They should be different due to random names (time might be same) - let id1 = generate_run_id(); - let id2 = generate_run_id(); - - // At least the random name part should differ - assert_ne!(id1, id2, "Run IDs should be different"); + fn test_reserved_run_ids_are_different() { + let temp_dir = tempfile::TempDir::new().unwrap(); + let id1 = reserve_run_id_in(temp_dir.path()).unwrap(); + let id2 = reserve_run_id_in(temp_dir.path()).unwrap(); + + assert_ne!(id1, id2, "Reserved run IDs should be different"); + assert!(temp_dir.path().join(id1).is_dir()); + assert!(temp_dir.path().join(id2).is_dir()); } #[test] @@ -140,7 +147,6 @@ mod tests { use tempfile::TempDir; let temp_dir = TempDir::new().expect("Failed to create temp dir"); - let _run_id = generate_run_id(); // Mock the paths by using temp directory structure let runs_dir = temp_dir.path().join("run"); From 2c3af20c4f8a5c5d68a67a127b47b161873b6344 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Fri, 21 Aug 2026 13:37:06 +0200 Subject: [PATCH 3/4] Format Synapse scenario helper Signed-off-by: Jakub Sztandera --- scenarios/synapse.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scenarios/synapse.py b/scenarios/synapse.py index 977ef8fd..913c8cf5 100644 --- a/scenarios/synapse.py +++ b/scenarios/synapse.py @@ -80,7 +80,9 @@ def clone_and_build(tmp_dir: Path) -> Path | None: return None # Persist the workaround because the build can launch a nested pnpm install. workspace = sdk_dir / "pnpm-workspace.yaml" - policy = workspace.read_text().replace("trustPolicy: no-downgrade", "trustPolicy: off") + policy = workspace.read_text().replace( + "trustPolicy: no-downgrade", "trustPolicy: off" + ) workspace.write_text(policy) # Install/build only what the e2e example needs, skipping the unrelated # playground, docs and react workspaces. From 0b3eec63c2edf6ad9afc00ad48a9ca0a56433897 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Fri, 21 Aug 2026 14:19:55 +0200 Subject: [PATCH 4/4] Increase CI user USDFC deposit Signed-off-by: Jakub Sztandera --- src/commands/start/user_setup/constants.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/commands/start/user_setup/constants.rs b/src/commands/start/user_setup/constants.rs index 100ee8d7..3258afe2 100644 --- a/src/commands/start/user_setup/constants.rs +++ b/src/commands/start/user_setup/constants.rs @@ -12,11 +12,10 @@ pub const CONTAINER_FP_APPROVE_OPERATOR: &str = "user-fp-approve-operator"; /// Gas limit for cast send transactions on Filecoin FEVM. pub const CAST_GAS_LIMIT: &str = "100000000"; -/// 3 USDFC expressed in the token's 18-decimal base unit. +/// 10 USDFC expressed in the token's 18-decimal base unit. /// -/// Latest filecoin-pin defaults to a two-copy upload path that can require more -/// than 2 USDFC of locked funds before creating both data sets. -pub const USDFC_DEPOSIT_AMOUNT: &str = "3000000000000000000"; +/// Covers cumulative lockups when CI scenarios share the pre-funded user. +pub const USDFC_DEPOSIT_AMOUNT: &str = "10000000000000000000"; /// uint256 max value, used for unlimited operator approval allowances. pub const MAX_UINT256: &str =