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