From e496b3fd0d64436b207fd276da9de1c665535fa5 Mon Sep 17 00:00:00 2001 From: hanabi1224 Date: Thu, 18 Dec 2025 22:00:18 +0800 Subject: [PATCH 01/17] fix(ci): try codecov on hosted runner --- .github/workflows/coverage.yml | 18 ++++++++++++++++-- Makefile | 2 +- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index b70846daef3..bff2cc9e2cc 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -28,19 +28,31 @@ env: CI: 1 CARGO_INCREMENTAL: 0 CACHE_TIMEOUT_MINUTES: 5 + AWS_ACCESS_KEY_ID: "${{ secrets.AWS_ACCESS_KEY_ID }}" + AWS_SECRET_ACCESS_KEY: "${{ secrets.AWS_SECRET_ACCESS_KEY }}" RUSTC_WRAPPER: sccache CC: sccache clang CXX: sccache clang++ # To minimize compile times: https://nnethercote.github.io/perf-book/build-configuration.html#minimizing-compile-times RUSTFLAGS: "-C linker=clang -C link-arg=-fuse-ld=lld" + FOREST_F3_SIDECAR_FFI_BUILD_OPT_OUT: 1 + FIL_PROOFS_PARAMETER_CACHE: /var/tmp/filecoin-proof-parameters jobs: codecov: name: Coverage - if: github.event.pull_request.draft == false && github.actor != 'dependabot[bot]' - runs-on: buildjet-4vcpu-ubuntu-2204 + # if: github.event.pull_request.draft == false && github.actor != 'dependabot[bot]' + runs-on: ubuntu-24.04-arm timeout-minutes: 45 steps: + - name: Configure SCCache variables + run: | + # External PRs do not have access to 'vars' or 'secrets'. + if [[ "${{secrets.AWS_ACCESS_KEY_ID}}" != "" ]]; then + echo "SCCACHE_ENDPOINT=${{ vars.SCCACHE_ENDPOINT}}" >> $GITHUB_ENV + echo "SCCACHE_BUCKET=${{ vars.SCCACHE_BUCKET}}" >> $GITHUB_ENV + echo "SCCACHE_REGION=${{ vars.SCCACHE_REGION}}" >> $GITHUB_ENV + fi - uses: actions/checkout@v6 - name: Setup sccache uses: mozilla-actions/sccache-action@v0.0.9 @@ -51,6 +63,8 @@ jobs: go-version-file: "go.work" - uses: taiki-e/install-action@cargo-llvm-cov - uses: taiki-e/install-action@nextest + - name: Fetch proof params + run: cargo run --bin forest-tool --no-default-features -- fetch-params --keys - name: Generate code coverage run: make codecov # Save lcov.info as an artifact for debugging purposes diff --git a/Makefile b/Makefile index 93c230baa92..a8e8722b335 100644 --- a/Makefile +++ b/Makefile @@ -115,7 +115,7 @@ test-release-docs: cargo test --profile quick --doc --features doctest-private codecov: - cargo llvm-cov --workspace --codecov --output-path lcov.info + cargo llvm-cov -p forest-filecoin --no-default-features --codecov --output-path lcov.info # Checks if all headers are present and adds if not license: From afa58f325e13f444b5808913cf0929e626b0af5b Mon Sep 17 00:00:00 2001 From: hanabi1224 Date: Fri, 19 Dec 2025 13:42:28 +0800 Subject: [PATCH 02/17] fix tests --- src/utils/proofs_api/parameters.rs | 11 +++++++++-- tests/common/mod.rs | 5 ++++- tests/config.rs | 1 + 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/utils/proofs_api/parameters.rs b/src/utils/proofs_api/parameters.rs index 8483ce8d716..8312c706ffb 100644 --- a/src/utils/proofs_api/parameters.rs +++ b/src/utils/proofs_api/parameters.rs @@ -90,8 +90,15 @@ pub(super) async fn check_parameter_file(path: &Path, info: &ParameterData) -> a // environment variable is set. pub(super) fn param_dir(data_dir: &Path) -> PathBuf { std::env::var(PathBuf::from(PROOFS_PARAMETER_CACHE_ENV)) - .map(PathBuf::from) - .unwrap_or_else(|_| data_dir.join(PARAM_DIR)) + .ok() + .and_then(|v| { + if v.is_empty() { + None + } else { + Some(PathBuf::from(v)) + } + }) + .unwrap_or_else(|| data_dir.join(PARAM_DIR)) } /// Forest uses a set of external crates for verifying the proofs generated by diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 7928c969d54..f33330017ef 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -41,7 +41,10 @@ impl CommonEnv for Command { // Always downloads proofs to same location to lower the overall test time // (by reducing multiple "fetching param file" steps). fn common_env(&mut self) -> &mut Self { - self.env("FIL_PROOFS_PARAMETER_CACHE", "/tmp/forest-test-fil-proofs") + match std::env::var("FIL_PROOFS_PARAMETER_CACHE").ok() { + Some(v) if !v.is_empty() => self, + _ => self.env("FIL_PROOFS_PARAMETER_CACHE", "/tmp/forest-test-fil-proofs"), + } } } diff --git a/tests/config.rs b/tests/config.rs index a6d325fa0d2..55f61e0812e 100644 --- a/tests/config.rs +++ b/tests/config.rs @@ -55,6 +55,7 @@ fn test_download_location_of_proof_parameter_files_default() { tool() .env("FOREST_CONFIG_PATH", config_file.path()) + .env("FIL_PROOFS_PARAMETER_CACHE", "") .arg("fetch-params") .arg("--keys") .arg("--dry-run") From 45f74a864790d6b2a8c243c817fd4d399dd2dce6 Mon Sep 17 00:00:00 2001 From: hanabi1224 Date: Fri, 19 Dec 2025 16:06:24 +0800 Subject: [PATCH 03/17] sccache for test-release --- .github/workflows/coverage.yml | 4 ++-- .github/workflows/unit-tests.yml | 11 +++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index bff2cc9e2cc..08eb5f889f8 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -41,8 +41,8 @@ env: jobs: codecov: name: Coverage - # if: github.event.pull_request.draft == false && github.actor != 'dependabot[bot]' - runs-on: ubuntu-24.04-arm + if: github.event.pull_request.draft == false && github.actor != 'dependabot[bot]' + runs-on: buildjet-4vcpu-ubuntu-2204 timeout-minutes: 45 steps: - name: Configure SCCache variables diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 84511234a1a..6cb1a6dc28b 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -28,9 +28,12 @@ env: CI: 1 CARGO_INCREMENTAL: 0 CACHE_TIMEOUT_MINUTES: 5 + AWS_ACCESS_KEY_ID: "${{ secrets.AWS_ACCESS_KEY_ID }}" + AWS_SECRET_ACCESS_KEY: "${{ secrets.AWS_SECRET_ACCESS_KEY }}" RUSTC_WRAPPER: "sccache" CC: "sccache clang" CXX: "sccache clang++" + FIL_PROOFS_PARAMETER_CACHE: /var/tmp/filecoin-proof-parameters jobs: tests-release: @@ -39,6 +42,14 @@ jobs: # This is done to limit the runner cost. if: github.event.pull_request.draft == false steps: + - name: Configure SCCache variables + run: | + # External PRs do not have access to 'vars' or 'secrets'. + if [[ "${{secrets.AWS_ACCESS_KEY_ID}}" != "" ]]; then + echo "SCCACHE_ENDPOINT=${{ vars.SCCACHE_ENDPOINT}}" >> $GITHUB_ENV + echo "SCCACHE_BUCKET=${{ vars.SCCACHE_BUCKET}}" >> $GITHUB_ENV + echo "SCCACHE_REGION=${{ vars.SCCACHE_REGION}}" >> $GITHUB_ENV + fi # find the nearest S3 space for storing cache files - name: Show IP run: curl ifconfig.me From 80b2f2edb0e89aa48ecea108902eb24fab9b5a35 Mon Sep 17 00:00:00 2001 From: hanabi1224 Date: Fri, 19 Dec 2025 16:20:08 +0800 Subject: [PATCH 04/17] fix AI comment --- src/utils/proofs_api/parameters.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/proofs_api/parameters.rs b/src/utils/proofs_api/parameters.rs index 8312c706ffb..931d42acd6a 100644 --- a/src/utils/proofs_api/parameters.rs +++ b/src/utils/proofs_api/parameters.rs @@ -89,7 +89,7 @@ pub(super) async fn check_parameter_file(path: &Path, info: &ParameterData) -> a // %DATA_DIR/filecoin-proof-parameters unless the FIL_PROOFS_PARAMETER_CACHE // environment variable is set. pub(super) fn param_dir(data_dir: &Path) -> PathBuf { - std::env::var(PathBuf::from(PROOFS_PARAMETER_CACHE_ENV)) + std::env::var(PROOFS_PARAMETER_CACHE_ENV) .ok() .and_then(|v| { if v.is_empty() { From 6ad6456509de66358d0554baed2e84bc5e414beb Mon Sep 17 00:00:00 2001 From: hanabi1224 Date: Fri, 19 Dec 2025 17:30:54 +0800 Subject: [PATCH 05/17] fetch proof params with docker --- .github/workflows/coverage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 08eb5f889f8..789ac25143a 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -64,7 +64,7 @@ jobs: - uses: taiki-e/install-action@cargo-llvm-cov - uses: taiki-e/install-action@nextest - name: Fetch proof params - run: cargo run --bin forest-tool --no-default-features -- fetch-params --keys + run: docker run --rm -v $FIL_PROOFS_PARAMETER_CACHE:$FIL_PROOFS_PARAMETER_CACHE -e FIL_PROOFS_PARAMETER_CACHE=$FIL_PROOFS_PARAMETER_CACHE --entrypoint forest-tool ghcr.io/chainsafe/forest fetch-params --keys - name: Generate code coverage run: make codecov # Save lcov.info as an artifact for debugging purposes From 4edf4807b9061aa2ea7f6415aad3a39d26b3f241 Mon Sep 17 00:00:00 2001 From: hanabi1224 Date: Fri, 19 Dec 2025 17:35:33 +0800 Subject: [PATCH 06/17] prove the fetch --- .github/workflows/coverage.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 789ac25143a..f23fe9e1001 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -64,7 +64,14 @@ jobs: - uses: taiki-e/install-action@cargo-llvm-cov - uses: taiki-e/install-action@nextest - name: Fetch proof params - run: docker run --rm -v $FIL_PROOFS_PARAMETER_CACHE:$FIL_PROOFS_PARAMETER_CACHE -e FIL_PROOFS_PARAMETER_CACHE=$FIL_PROOFS_PARAMETER_CACHE --entrypoint forest-tool ghcr.io/chainsafe/forest fetch-params --keys + run: | + docker run --rm \ + -v $FIL_PROOFS_PARAMETER_CACHE:$FIL_PROOFS_PARAMETER_CACHE \ + -e FIL_PROOFS_PARAMETER_CACHE=$FIL_PROOFS_PARAMETER_CACHE \ + --entrypoint forest-tool \ + ghcr.io/chainsafe/forest \ + fetch-params --keys + ls -ahl $FIL_PROOFS_PARAMETER_CACHE - name: Generate code coverage run: make codecov # Save lcov.info as an artifact for debugging purposes From 0eb281252e7407260ad4cda491d4c18d2a7d8ae4 Mon Sep 17 00:00:00 2001 From: hanabi1224 Date: Fri, 19 Dec 2025 17:48:28 +0800 Subject: [PATCH 07/17] set permission --- .github/workflows/coverage.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index f23fe9e1001..7a0700f6321 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -71,6 +71,7 @@ jobs: --entrypoint forest-tool \ ghcr.io/chainsafe/forest \ fetch-params --keys + sudo chmod -R 755 $FIL_PROOFS_PARAMETER_CACHE ls -ahl $FIL_PROOFS_PARAMETER_CACHE - name: Generate code coverage run: make codecov From 00f0d727408786e708d3500291e96cef90200cfa Mon Sep 17 00:00:00 2001 From: hanabi1224 Date: Fri, 19 Dec 2025 17:57:06 +0800 Subject: [PATCH 08/17] fetch proof params for test-release as well --- .github/workflows/unit-tests.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 6cb1a6dc28b..f7e6f5bfc72 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -72,6 +72,16 @@ jobs: go-version-file: "go.work" - name: install nextest uses: taiki-e/install-action@nextest + - name: Fetch proof params + run: | + docker run --rm \ + -v $FIL_PROOFS_PARAMETER_CACHE:$FIL_PROOFS_PARAMETER_CACHE \ + -e FIL_PROOFS_PARAMETER_CACHE=$FIL_PROOFS_PARAMETER_CACHE \ + --entrypoint forest-tool \ + ghcr.io/chainsafe/forest \ + fetch-params --keys + sudo chmod -R 755 $FIL_PROOFS_PARAMETER_CACHE + ls -ahl $FIL_PROOFS_PARAMETER_CACHE - run: | make test-release-docs make test-release From a74c99c11ac5b32fa9f805930b1097439ec31e30 Mon Sep 17 00:00:00 2001 From: hanabi1224 Date: Fri, 19 Dec 2025 19:34:38 +0800 Subject: [PATCH 09/17] less log --- .github/workflows/coverage.yml | 1 + .github/workflows/unit-tests.yml | 1 + src/chain_sync/chain_follower.rs | 9 +++++++-- src/utils/rand/mod.rs | 1 + tests/lint.rs | 9 +++++++-- 5 files changed, 17 insertions(+), 4 deletions(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 7a0700f6321..1d07a4e3d75 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -37,6 +37,7 @@ env: RUSTFLAGS: "-C linker=clang -C link-arg=-fuse-ld=lld" FOREST_F3_SIDECAR_FFI_BUILD_OPT_OUT: 1 FIL_PROOFS_PARAMETER_CACHE: /var/tmp/filecoin-proof-parameters + RUST_LOG: warn jobs: codecov: diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index f7e6f5bfc72..5d89cd70aba 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -34,6 +34,7 @@ env: CC: "sccache clang" CXX: "sccache clang++" FIL_PROOFS_PARAMETER_CACHE: /var/tmp/filecoin-proof-parameters + RUST_LOG: warn jobs: tests-release: diff --git a/src/chain_sync/chain_follower.rs b/src/chain_sync/chain_follower.rs index 447740c5aac..be1ba6562c4 100644 --- a/src/chain_sync/chain_follower.rs +++ b/src/chain_sync/chain_follower.rs @@ -884,13 +884,18 @@ mod tests { use num_bigint::BigInt; use num_traits::ToPrimitive; use std::sync::Arc; + use tracing::level_filters::LevelFilter; + use tracing_subscriber::EnvFilter; fn setup() -> (Arc>, Chain4U>) { // Initialize test logger let _ = tracing_subscriber::fmt() + .without_time() .with_env_filter( - tracing_subscriber::EnvFilter::from_default_env() - .add_directive(tracing::Level::DEBUG.into()), + EnvFilter::builder() + .with_default_directive(LevelFilter::DEBUG.into()) + .from_env() + .unwrap(), ) .try_init(); diff --git a/src/utils/rand/mod.rs b/src/utils/rand/mod.rs index 20a37aade34..3684ed82c31 100644 --- a/src/utils/rand/mod.rs +++ b/src/utils/rand/mod.rs @@ -35,6 +35,7 @@ fn forest_rng_internal(mode: ForestRngMode) -> impl Rng + CryptoRng { const ENV: &str = FIXED_RNG_SEED_ENV; if let Ok(v) = std::env::var(ENV) { if let Ok(seed) = v.parse() { + #[cfg(not(test))] tracing::warn!("[security] using test RNG with fixed seed {seed} set by {ENV}"); return Either::Left(rand_chacha::ChaChaRng::seed_from_u64(seed)); } else { diff --git a/tests/lint.rs b/tests/lint.rs index d4779dbdf66..9b38540720d 100644 --- a/tests/lint.rs +++ b/tests/lint.rs @@ -45,13 +45,18 @@ use lints::{Lint, Violation}; use proc_macro2::{LineColumn, Span}; use syn::visit::Visit; use tracing::{debug, info, level_filters::LevelFilter}; -use tracing_subscriber::util::SubscriberInitExt as _; +use tracing_subscriber::{EnvFilter, util::SubscriberInitExt as _}; #[test] fn lint() { let _guard = tracing_subscriber::fmt() .without_time() - .with_max_level(LevelFilter::DEBUG) + .with_env_filter( + EnvFilter::builder() + .with_default_directive(LevelFilter::DEBUG.into()) + .from_env() + .unwrap(), + ) .set_default(); LintRunner::new() .run::() From 284ec796f2d02317ba2035ef4f6ccf1674925e00 Mon Sep 17 00:00:00 2001 From: hanabi1224 Date: Fri, 19 Dec 2025 19:56:24 +0800 Subject: [PATCH 10/17] nextest --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index a8e8722b335..18a13b523f6 100644 --- a/Makefile +++ b/Makefile @@ -115,7 +115,7 @@ test-release-docs: cargo test --profile quick --doc --features doctest-private codecov: - cargo llvm-cov -p forest-filecoin --no-default-features --codecov --output-path lcov.info + cargo llvm-cov -p forest-filecoin --no-default-features --codecov --output-path lcov.info nextest # Checks if all headers are present and adds if not license: From 836b7a22ec6a49eb83326ce7da1df4bba4a66d4c Mon Sep 17 00:00:00 2001 From: hanabi1224 Date: Fri, 19 Dec 2025 20:05:46 +0800 Subject: [PATCH 11/17] no nextest --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 18a13b523f6..a8e8722b335 100644 --- a/Makefile +++ b/Makefile @@ -115,7 +115,7 @@ test-release-docs: cargo test --profile quick --doc --features doctest-private codecov: - cargo llvm-cov -p forest-filecoin --no-default-features --codecov --output-path lcov.info nextest + cargo llvm-cov -p forest-filecoin --no-default-features --codecov --output-path lcov.info # Checks if all headers are present and adds if not license: From 89ba6d981d4276b10ce245e35dc23859cbc6bf77 Mon Sep 17 00:00:00 2001 From: hanabi1224 Date: Fri, 19 Dec 2025 20:20:18 +0800 Subject: [PATCH 12/17] fetch proof params only once --- src/tool/subcommands/api_cmd/test_snapshot.rs | 4 ---- src/utils/proofs_api/paramfetch.rs | 18 ++++++++++++++---- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/tool/subcommands/api_cmd/test_snapshot.rs b/src/tool/subcommands/api_cmd/test_snapshot.rs index 55d50e79ac2..8c984dcb6d0 100644 --- a/src/tool/subcommands/api_cmd/test_snapshot.rs +++ b/src/tool/subcommands/api_cmd/test_snapshot.rs @@ -190,9 +190,7 @@ mod tests { use ahash::HashSet; use anyhow::Context as _; use directories::ProjectDirs; - use std::sync::LazyLock; use std::time::{Duration, Instant}; - use tokio::sync::Mutex; use url::Url; // To run a single test: cargo test --lib filecoin_multisig_statedecodeparams_1754230255631789 -- --nocapture @@ -201,8 +199,6 @@ mod tests { async fn rpc_regression_test_run(name: &str) { // Set proof parameter data dir and make sure the proofs are available { - static PROOF_PARAMS_LOCK: LazyLock> = LazyLock::new(|| Mutex::new(())); - let _guard = PROOF_PARAMS_LOCK.lock().await; crate::utils::proofs_api::maybe_set_proofs_parameter_cache_dir_env( &Config::default().client.data_dir, ); diff --git a/src/utils/proofs_api/paramfetch.rs b/src/utils/proofs_api/paramfetch.rs index a43240636c6..06efdad98f8 100644 --- a/src/utils/proofs_api/paramfetch.rs +++ b/src/utils/proofs_api/paramfetch.rs @@ -10,7 +10,7 @@ use std::{ io::{self, ErrorKind}, path::{Path, PathBuf}, - sync::Arc, + sync::{Arc, LazyLock}, }; use crate::{ @@ -23,7 +23,10 @@ use crate::{ use anyhow::{Context, bail}; use backon::{ExponentialBuilder, Retryable}; use futures::{AsyncWriteExt, TryStreamExt, stream::FuturesUnordered}; -use tokio::fs::{self}; +use tokio::{ + fs::{self}, + sync::Mutex, +}; use tracing::{debug, info, warn}; use super::parameters::{ @@ -61,8 +64,15 @@ pub async fn ensure_proof_params_downloaded() -> anyhow::Result<()> { if data_dir.is_empty() { anyhow::bail!("Proof parameter data dir is not set"); } - get_params_default(Path::new(&data_dir), SectorSizeOpt::Keys, false).await?; - Ok(()) + static RUN_ONCE: LazyLock> = LazyLock::new(|| Mutex::new(false)); + let mut run_once = RUN_ONCE.lock().await; + if *run_once { + Ok(()) + } else { + get_params_default(Path::new(&data_dir), SectorSizeOpt::Keys, false).await?; + *run_once = true; + Ok(()) + } } /// Get proofs parameters and all verification keys for a given sector size From f63bdc12114b81281cf955326b0a163c21d6be03 Mon Sep 17 00:00:00 2001 From: hanabi1224 Date: Fri, 19 Dec 2025 20:39:01 +0800 Subject: [PATCH 13/17] more retry in downloading rpc snapshots --- src/tool/subcommands/api_cmd/test_snapshot.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tool/subcommands/api_cmd/test_snapshot.rs b/src/tool/subcommands/api_cmd/test_snapshot.rs index 8c984dcb6d0..a32eff4ce1a 100644 --- a/src/tool/subcommands/api_cmd/test_snapshot.rs +++ b/src/tool/subcommands/api_cmd/test_snapshot.rs @@ -218,8 +218,8 @@ mod tests { } else { 120 })), - max_retries: Some(5), - ..Default::default() + max_retries: Some(10), + delay: Some(Duration::from_secs(1)), }, || async { download_file_with_cache(&url, &cache_dir, DownloadFileOption::NonResumable).await From b5de8d1620f07f7c11603dc756fbc91cfa04bf62 Mon Sep 17 00:00:00 2001 From: hanabi1224 Date: Fri, 19 Dec 2025 20:41:06 +0800 Subject: [PATCH 14/17] FOREST_TEST_SKIP_PROOF_PARAM_CHECK --- .github/workflows/unit-tests.yml | 1 + src/utils/proofs_api/paramfetch.rs | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 5d89cd70aba..9f0437e434f 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -89,6 +89,7 @@ jobs: env: # To minimize compile times: https://nnethercote.github.io/perf-book/build-configuration.html#minimizing-compile-times RUSTFLAGS: "-C linker=clang -C link-arg=-fuse-ld=lld" + FOREST_TEST_SKIP_PROOF_PARAM_CHECK: 1 - id: get-cache-hash run: | ls -lhR ~/.cache/forest/test/rpc-snapshots/rpc_test/* diff --git a/src/utils/proofs_api/paramfetch.rs b/src/utils/proofs_api/paramfetch.rs index 06efdad98f8..5c2a99b0edf 100644 --- a/src/utils/proofs_api/paramfetch.rs +++ b/src/utils/proofs_api/paramfetch.rs @@ -60,6 +60,11 @@ pub enum SectorSizeOpt { /// Ensures the parameter files are downloaded to cache dir pub async fn ensure_proof_params_downloaded() -> anyhow::Result<()> { + #[cfg(test)] + if is_env_truthy("FOREST_TEST_SKIP_PROOF_PARAM_CHECK") { + return Ok(()); + } + let data_dir = std::env::var(PROOFS_PARAMETER_CACHE_ENV).unwrap_or_default(); if data_dir.is_empty() { anyhow::bail!("Proof parameter data dir is not set"); From 7ea16b7cc3e67632ca73e28f1f8eb74ade270d83 Mon Sep 17 00:00:00 2001 From: hanabi1224 Date: Fri, 19 Dec 2025 21:14:02 +0800 Subject: [PATCH 15/17] pre-fetch rpc test snapshots --- .github/workflows/coverage.yml | 10 +-- .github/workflows/unit-tests.yml | 10 +-- src/bin/forest-dev.rs | 7 ++ src/dev/main.rs | 18 ++++ src/dev/mod.rs | 5 ++ src/dev/subcommands/mod.rs | 89 +++++++++++++++++++ src/lib.rs | 2 + src/tool/subcommands/api_cmd/test_snapshot.rs | 33 +------ src/utils/mod.rs | 46 +++++----- 9 files changed, 149 insertions(+), 71 deletions(-) create mode 100644 src/bin/forest-dev.rs create mode 100644 src/dev/main.rs create mode 100644 src/dev/mod.rs create mode 100644 src/dev/subcommands/mod.rs diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 1d07a4e3d75..c997e639765 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -64,15 +64,9 @@ jobs: go-version-file: "go.work" - uses: taiki-e/install-action@cargo-llvm-cov - uses: taiki-e/install-action@nextest - - name: Fetch proof params + - name: Fetch proof params and RPC test snapshots run: | - docker run --rm \ - -v $FIL_PROOFS_PARAMETER_CACHE:$FIL_PROOFS_PARAMETER_CACHE \ - -e FIL_PROOFS_PARAMETER_CACHE=$FIL_PROOFS_PARAMETER_CACHE \ - --entrypoint forest-tool \ - ghcr.io/chainsafe/forest \ - fetch-params --keys - sudo chmod -R 755 $FIL_PROOFS_PARAMETER_CACHE + cargo run --bin forest-dev --no-default-features --profile quick -- fetch-rpc-tests ls -ahl $FIL_PROOFS_PARAMETER_CACHE - name: Generate code coverage run: make codecov diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 9f0437e434f..06a08ffc31e 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -73,15 +73,9 @@ jobs: go-version-file: "go.work" - name: install nextest uses: taiki-e/install-action@nextest - - name: Fetch proof params + - name: Fetch proof params and RPC test snapshots run: | - docker run --rm \ - -v $FIL_PROOFS_PARAMETER_CACHE:$FIL_PROOFS_PARAMETER_CACHE \ - -e FIL_PROOFS_PARAMETER_CACHE=$FIL_PROOFS_PARAMETER_CACHE \ - --entrypoint forest-tool \ - ghcr.io/chainsafe/forest \ - fetch-params --keys - sudo chmod -R 755 $FIL_PROOFS_PARAMETER_CACHE + cargo run --bin forest-dev --no-default-features --profile quick -- fetch-rpc-tests ls -ahl $FIL_PROOFS_PARAMETER_CACHE - run: | make test-release-docs diff --git a/src/bin/forest-dev.rs b/src/bin/forest-dev.rs new file mode 100644 index 00000000000..c6d282c7b87 --- /dev/null +++ b/src/bin/forest-dev.rs @@ -0,0 +1,7 @@ +// Copyright 2019-2025 ChainSafe Systems +// SPDX-License-Identifier: Apache-2.0, MIT + +#[tokio::main(flavor = "multi_thread")] +async fn main() -> anyhow::Result<()> { + forest::forest_dev_main(std::env::args_os()).await +} diff --git a/src/dev/main.rs b/src/dev/main.rs new file mode 100644 index 00000000000..744a88325fa --- /dev/null +++ b/src/dev/main.rs @@ -0,0 +1,18 @@ +// Copyright 2019-2025 ChainSafe Systems +// SPDX-License-Identifier: Apache-2.0, MIT + +use super::subcommands::Cli; +use crate::cli_shared::logger::setup_minimal_logger; +use clap::Parser as _; +use std::ffi::OsString; + +pub async fn main(args: impl IntoIterator) -> anyhow::Result<()> +where + ArgT: Into + Clone, +{ + // Capture Cli inputs + let Cli { cmd } = Cli::parse_from(args); + setup_minimal_logger(); + let client = crate::rpc::Client::default_or_from_env(None)?; + cmd.run(client).await +} diff --git a/src/dev/mod.rs b/src/dev/mod.rs new file mode 100644 index 00000000000..0caa4b0d273 --- /dev/null +++ b/src/dev/mod.rs @@ -0,0 +1,5 @@ +// Copyright 2019-2025 ChainSafe Systems +// SPDX-License-Identifier: Apache-2.0, MIT + +pub mod main; +pub mod subcommands; diff --git a/src/dev/subcommands/mod.rs b/src/dev/subcommands/mod.rs new file mode 100644 index 00000000000..7ce59cad234 --- /dev/null +++ b/src/dev/subcommands/mod.rs @@ -0,0 +1,89 @@ +// Copyright 2019-2025 ChainSafe Systems +// SPDX-License-Identifier: Apache-2.0, MIT + +use crate::cli_shared::cli::HELP_MESSAGE; +use crate::rpc::Client; +use crate::utils::net::{DownloadFileOption, download_file_with_cache}; +use crate::utils::proofs_api::ensure_proof_params_downloaded; +use crate::utils::version::FOREST_VERSION_STRING; +use anyhow::Context as _; +use clap::Parser; +use directories::ProjectDirs; +use std::borrow::Cow; +use std::path::PathBuf; +use std::time::Duration; +use tokio::task::JoinSet; +use url::Url; + +/// Command-line options for the `forest-dev` binary +#[derive(Parser)] +#[command(name = env!("CARGO_PKG_NAME"), bin_name = "forest-dev", author = env!("CARGO_PKG_AUTHORS"), version = FOREST_VERSION_STRING.as_str(), about = env!("CARGO_PKG_DESCRIPTION") +)] +#[command(help_template(HELP_MESSAGE))] +pub struct Cli { + #[command(subcommand)] + pub cmd: Subcommand, +} + +/// forest-dev sub-commands +#[derive(clap::Subcommand)] +pub enum Subcommand { + /// Fetch RPC test snapshots to the local cache + FetchRpcTests, +} + +impl Subcommand { + pub async fn run(self, _client: Client) -> anyhow::Result<()> { + match self { + Self::FetchRpcTests => fetch_rpc_tests().await, + } + } +} + +async fn fetch_rpc_tests() -> anyhow::Result<()> { + crate::utils::proofs_api::maybe_set_proofs_parameter_cache_dir_env( + &crate::Config::default().client.data_dir, + ); + ensure_proof_params_downloaded().await?; + let tests = include_str!("../../tool/subcommands/api_cmd/test_snapshots.txt") + .lines() + .map(|i| { + // Remove comment + i.split("#").next().unwrap().trim().to_string() + }) + .filter(|l| !l.is_empty() && !l.starts_with('#')); + let mut joinset = JoinSet::new(); + for test in tests { + joinset.spawn(fetch_rpc_test_snapshot(test.into())); + } + for result in joinset.join_all().await { + if let Err(e) = result { + tracing::warn!("{e}"); + } + } + Ok(()) +} + +pub async fn fetch_rpc_test_snapshot<'a>(name: Cow<'a, str>) -> anyhow::Result { + let url: Url = + format!("https://forest-snapshots.fra1.cdn.digitaloceanspaces.com/rpc_test/{name}") + .parse() + .with_context(|| format!("Failed to parse URL for test: {name}"))?; + let project_dir = + ProjectDirs::from("com", "ChainSafe", "Forest").context("failed to get project dir")?; + let cache_dir = project_dir.cache_dir().join("test").join("rpc-snapshots"); + let path = crate::utils::retry( + crate::utils::RetryArgs { + timeout: Some(Duration::from_secs(60)), + max_retries: Some(5), + delay: Some(Duration::from_secs(1)), + }, + || async { + download_file_with_cache(&url, &cache_dir, DownloadFileOption::NonResumable).await + }, + ) + .await + .map_err(|e| anyhow::anyhow!("failed to fetch rpc test snapshot {name} :{e}"))? + .path; + Ok(path) +} diff --git a/src/lib.rs b/src/lib.rs index 17c035c8fdf..6df47bf946f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,6 +47,7 @@ mod cli; mod cli_shared; mod daemon; mod db; +mod dev; mod documentation; mod eth; mod f3; @@ -124,6 +125,7 @@ pub use auth::{JWT_IDENTIFIER, verify_token}; pub use cli::main::main as forest_main; pub use cli_shared::cli::{Client, Config}; pub use daemon::main::main as forestd_main; +pub use dev::main::main as forest_dev_main; pub use key_management::{ ENCRYPTED_KEYSTORE_NAME, FOREST_KEYSTORE_PHRASE_ENV, KEYSTORE_NAME, KeyStore, KeyStoreConfig, }; diff --git a/src/tool/subcommands/api_cmd/test_snapshot.rs b/src/tool/subcommands/api_cmd/test_snapshot.rs index a32eff4ce1a..4e1813230ad 100644 --- a/src/tool/subcommands/api_cmd/test_snapshot.rs +++ b/src/tool/subcommands/api_cmd/test_snapshot.rs @@ -185,13 +185,9 @@ async fn ctx( mod tests { use super::*; use crate::Config; - use crate::utils::net::{DownloadFileOption, download_file_with_cache}; use crate::utils::proofs_api::ensure_proof_params_downloaded; use ahash::HashSet; - use anyhow::Context as _; - use directories::ProjectDirs; - use std::time::{Duration, Instant}; - use url::Url; + use std::time::Instant; // To run a single test: cargo test --lib filecoin_multisig_statedecodeparams_1754230255631789 -- --nocapture include!(concat!(env!("OUT_DIR"), "/__rpc_regression_tests_gen.rs")); @@ -204,30 +200,9 @@ mod tests { ); ensure_proof_params_downloaded().await.unwrap(); } - let url: Url = - format!("https://forest-snapshots.fra1.cdn.digitaloceanspaces.com/rpc_test/{name}") - .parse() - .with_context(|| format!("Failed to parse URL for test: {name}")) - .unwrap(); - let project_dir = ProjectDirs::from("com", "ChainSafe", "Forest").unwrap(); - let cache_dir = project_dir.cache_dir().join("test").join("rpc-snapshots"); - let path = crate::utils::retry( - crate::utils::RetryArgs { - timeout: Some(Duration::from_secs(if crate::utils::is_ci() { - 20 - } else { - 120 - })), - max_retries: Some(10), - delay: Some(Duration::from_secs(1)), - }, - || async { - download_file_with_cache(&url, &cache_dir, DownloadFileOption::NonResumable).await - }, - ) - .await - .unwrap() - .path; + let path = crate::dev::subcommands::fetch_rpc_test_snapshot(name.into()) + .await + .unwrap(); // We need to set RNG seed so that tests are run with deterministic // output. The snapshots should be generated with a node running with the same seed, if diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 49de3de4428..09e1b228ea9 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -20,13 +20,9 @@ pub mod stream; pub mod version; use anyhow::{Context as _, bail}; -use futures::{ - Future, FutureExt, - future::{FusedFuture, pending}, - select, -}; +use futures::Future; use multiaddr::{Multiaddr, Protocol}; -use std::{pin::Pin, str::FromStr, time::Duration}; +use std::{str::FromStr, time::Duration}; use tokio::time::sleep; use tracing::error; use url::Url; @@ -125,29 +121,26 @@ where F: Future>, E: std::fmt::Debug, { - let mut timeout: Pin>> = match args.timeout { - Some(duration) => Box::pin(sleep(duration).fuse()), - None => Box::pin(pending()), - }; let max_retries = args.max_retries.unwrap_or(usize::MAX); - let mut task = Box::pin( - async { - for _ in 0..max_retries { - match make_fut().await { - Ok(ok) => return Ok(ok), - Err(err) => error!("retrying operation after {err:?}"), - } - if let Some(delay) = args.delay { - sleep(delay).await; - } + let task = async { + for _ in 0..max_retries { + match make_fut().await { + Ok(ok) => return Ok(ok), + Err(err) => error!("retrying operation after {err:?}"), + } + if let Some(delay) = args.delay { + sleep(delay).await; } - Err(RetryError::RetriesExceeded) } - .fuse(), - ); - select! { - _ = timeout => Err(RetryError::TimeoutExceeded), - res = task => res, + Err(RetryError::RetriesExceeded) + }; + + if let Some(timeout) = args.timeout { + tokio::time::timeout(timeout, task) + .await + .map_err(|_| RetryError::TimeoutExceeded)? + } else { + task.await } } @@ -187,6 +180,7 @@ mod tests { mod files; use RetryError::{RetriesExceeded, TimeoutExceeded}; + use futures::future::pending; use std::{future::ready, sync::atomic::AtomicUsize}; use super::*; From f136bddf1cdb4f0fc46f9beb574a53e9b037115f Mon Sep 17 00:00:00 2001 From: hanabi1224 Date: Fri, 19 Dec 2025 21:45:00 +0800 Subject: [PATCH 16/17] speedup nextest build --- Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index a8e8722b335..c99a13e8bc8 100644 --- a/Makefile +++ b/Makefile @@ -99,20 +99,20 @@ docker-run: docker build -t forest:latest -f ./Dockerfile . && docker run forest test: - cargo nextest run --workspace --no-fail-fast + cargo nextest run --workspace --no-default-features --no-fail-fast test-docs: # nextest doesn't run doctests https://github.com/nextest-rs/nextest/issues/16 # see also lib.rs::doctest_private - cargo test --doc --features doctest-private + cargo test --doc --no-default-features --features doctest-private test-release: - cargo nextest run --cargo-profile quick --workspace --no-fail-fast + cargo nextest run --cargo-profile quick --workspace --no-default-features --no-fail-fast test-release-docs: # nextest doesn't run doctests https://github.com/nextest-rs/nextest/issues/16 # see also lib.rs::doctest_private - cargo test --profile quick --doc --features doctest-private + cargo test --profile quick --doc --no-default-features --features doctest-private codecov: cargo llvm-cov -p forest-filecoin --no-default-features --codecov --output-path lcov.info From f3dae309a45b56aa017a338d81f4e2eb77e86b8d Mon Sep 17 00:00:00 2001 From: hanabi1224 Date: Fri, 19 Dec 2025 22:05:01 +0800 Subject: [PATCH 17/17] fix --- .config/nextest.toml | 8 ++++++++ src/dev/subcommands/mod.rs | 6 ++---- src/state_manager/utils.rs | 20 ++++++++++++++++---- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/.config/nextest.toml b/.config/nextest.toml index 11d1a92cc3e..a18eadce8bf 100644 --- a/.config/nextest.toml +++ b/.config/nextest.toml @@ -41,3 +41,11 @@ slow-timeout = { period = "120s", terminate-after = 3 } filter = 'test(rpc_snapshot_test_)' slow-timeout = { period = "120s", terminate-after = 3 } retries = { backoff = "exponential", count = 3, delay = "5s", jitter = true } + +# These tests download test snapshots from the network, which can take a while. +# There might be some network issues, so we allow some retries with backoff. +# Jitter is enabled to avoid [thundering herd issues](https://en.wikipedia.org/wiki/Thundering_herd_problem). +[[profile.default.overrides]] +filter = 'test(state_compute_)' +slow-timeout = { period = "120s", terminate-after = 3 } +retries = { backoff = "exponential", count = 3, delay = "5s", jitter = true } diff --git a/src/dev/subcommands/mod.rs b/src/dev/subcommands/mod.rs index 7ce59cad234..e1501438620 100644 --- a/src/dev/subcommands/mod.rs +++ b/src/dev/subcommands/mod.rs @@ -74,13 +74,11 @@ pub async fn fetch_rpc_test_snapshot<'a>(name: Cow<'a, str>) -> anyhow::Result