forest/utils/proofs_api/
parameters.rs

1// Copyright 2019-2025 ChainSafe Systems
2// SPDX-License-Identifier: Apache-2.0, MIT
3//! This module contains the logic for storing and verifying the proofs parameters.
4//!
5//! The parameters are fetched from the network and stored in the cache directory. The cache directory can be set
6//! using the [`PROOFS_PARAMETER_CACHE_ENV`] environment variable. If not set, the default directory is used.
7
8use std::{
9    fs::File as SyncFile,
10    io::{self, BufReader as SyncBufReader, copy as sync_copy},
11    path::{Path, PathBuf},
12};
13
14use ahash::HashMap;
15use anyhow::{Context, bail};
16use blake2b_simd::{Hash, State as Blake2b};
17use cid::Cid;
18use serde::{Deserialize, Serialize};
19use tracing::{debug, warn};
20
21use crate::utils::misc::env::is_env_truthy;
22
23const PROOF_DIGEST_LEN: usize = 16;
24
25/// Environment variable that allows skipping checksum verification of the parameter files.
26const FOREST_FORCE_TRUST_PARAMS_ENV: &str = "FOREST_FORCE_TRUST_PARAMS";
27
28/// Environment variable to set the directory where proofs parameters are stored. Defaults to
29/// [`PARAM_DIR`] in the data directory.
30pub(super) const PROOFS_PARAMETER_CACHE_ENV: &str = "FIL_PROOFS_PARAMETER_CACHE";
31
32/// Default directory name for storing proofs parameters.
33const PARAM_DIR: &str = "filecoin-proof-parameters";
34
35/// Default parameters, as outlined in Lotus `v1.26.2`.
36/// <https://github.com/filecoin-project/filecoin-ffi/blob/b715c9403faf919e95fdc702cd651e842f18d890/parameters.json>
37pub(super) const DEFAULT_PARAMETERS: &str = include_str!("./parameters.json");
38
39/// Map of parameter data, to be deserialized from the parameter file.
40pub(super) type ParameterMap = HashMap<String, ParameterData>;
41
42/// Data structure for retrieving the proof parameter data from provided JSON.
43#[derive(Debug, Deserialize, Serialize, Clone)]
44pub(super) struct ParameterData {
45    #[serde(with = "crate::lotus_json::stringify")]
46    pub cid: Cid,
47    #[serde(with = "hex::serde")]
48    pub digest: [u8; PROOF_DIGEST_LEN],
49    pub sector_size: u64,
50}
51
52/// Ensures the parameter file is downloaded and has the correct checksum.
53/// This behavior can be disabled by setting the [`FOREST_FORCE_TRUST_PARAMS_ENV`] environment variable to 1.
54pub(super) async fn check_parameter_file(path: &Path, info: &ParameterData) -> anyhow::Result<()> {
55    if is_env_truthy(FOREST_FORCE_TRUST_PARAMS_ENV) {
56        warn!("Assuming parameter files are okay. Do not use in production!");
57        return Ok(());
58    }
59
60    let hash = tokio::task::spawn_blocking({
61        let file = SyncFile::open(path)?;
62        move || -> Result<Hash, io::Error> {
63            let mut reader = SyncBufReader::new(file);
64            let mut hasher = Blake2b::new();
65            sync_copy(&mut reader, &mut hasher)?;
66            Ok(hasher.finalize())
67        }
68    })
69    .await??;
70
71    let hash_chunk = hash
72        .as_bytes()
73        .get(..PROOF_DIGEST_LEN)
74        .context("invalid digest length")?;
75    if info.digest == hash_chunk {
76        debug!("Parameter file {:?} is ok", path);
77        Ok(())
78    } else {
79        bail!(
80            "Checksum mismatch in param file {:?}. ({:x?} != {:x?})",
81            path,
82            hash_chunk,
83            info.digest,
84        )
85    }
86}
87
88// Proof parameter file directory. Defaults to
89// %DATA_DIR/filecoin-proof-parameters unless the FIL_PROOFS_PARAMETER_CACHE
90// environment variable is set.
91pub(super) fn param_dir(data_dir: &Path) -> PathBuf {
92    std::env::var(PathBuf::from(PROOFS_PARAMETER_CACHE_ENV))
93        .map(PathBuf::from)
94        .unwrap_or_else(|_| data_dir.join(PARAM_DIR))
95}
96
97/// Forest uses a set of external crates for verifying the proofs generated by
98/// the miners. These external crates require a specific set of parameter files
99/// to be located at in a specific folder. By default, it is
100/// `/var/tmp/filecoin-proof-parameters` but it can be overridden by the
101/// `FIL_PROOFS_PARAMETER_CACHE` environment variable. Forest will automatically
102/// download the parameter files from Cloudflare/IPFS and verify their validity. For
103/// consistency, Forest will prefer to download the files it's local data
104/// directory. To this end, the `FIL_PROOFS_PARAMETER_CACHE` environment
105/// variable is updated before the parameters are downloaded.
106///
107/// More information available [here](https://github.com/filecoin-project/rust-fil-proofs/blob/8f5bd86be36a55e33b9b293ba22ea13ca1f28163/README.md?plain=1#L219-L235).
108fn set_proofs_parameter_cache_dir_env(data_dir: &Path) {
109    unsafe { std::env::set_var(PROOFS_PARAMETER_CACHE_ENV, param_dir(data_dir)) };
110}
111
112/// Optionally set the proofs parameter cache directory environment variable if it is not already
113/// set. See [`set_proofs_parameter_cache_dir_env`] for more details.
114pub fn maybe_set_proofs_parameter_cache_dir_env(data_dir: &Path) {
115    if std::env::var(PROOFS_PARAMETER_CACHE_ENV).is_err() {
116        set_proofs_parameter_cache_dir_env(data_dir);
117    }
118}
119
120#[cfg(test)]
121mod tests {
122    use super::*;
123
124    #[tokio::test]
125    async fn test_proof_file_check() {
126        let tempfile = tempfile::Builder::new().tempfile().unwrap();
127        let path = tempfile.path();
128
129        let data = b"Cthulhu fhtagn!";
130        std::fs::write(path, data).unwrap();
131
132        let mut hasher = Blake2b::new();
133        hasher.update(data);
134        let digest = hasher
135            .finalize()
136            .as_bytes()
137            .get(..PROOF_DIGEST_LEN)
138            .unwrap()
139            .to_owned();
140
141        let param_data = ParameterData {
142            cid: Cid::default(),
143            digest: digest.try_into().unwrap(),
144            sector_size: 32,
145        };
146
147        check_parameter_file(path, &param_data).await.unwrap()
148    }
149
150    #[tokio::test]
151    async fn test_proof_file_check_no_file() {
152        let param_data = ParameterData {
153            cid: Cid::default(),
154            digest: [0; PROOF_DIGEST_LEN],
155            sector_size: 32,
156        };
157
158        let path = Path::new("cthulhuazathoh.dagon");
159        let ret = check_parameter_file(path, &param_data).await;
160        assert_eq!(
161            ret.unwrap_err().downcast_ref::<io::Error>().unwrap().kind(),
162            io::ErrorKind::NotFound
163        );
164    }
165}