use std::fs::File;
use std::io::{self, BufRead};
use std::path::Path;
use anyhow::Result;
use filecoin_proofs::{
verify_seal, SectorShape16KiB, SectorShape2KiB, SectorShape32KiB, SectorShape4KiB,
SECTOR_SIZE_16_KIB, SECTOR_SIZE_1_GIB, SECTOR_SIZE_2_KIB, SECTOR_SIZE_32_GIB,
SECTOR_SIZE_32_KIB, SECTOR_SIZE_4_KIB, SECTOR_SIZE_512_MIB, SECTOR_SIZE_64_GIB,
SECTOR_SIZE_8_MIB,
};
#[cfg(feature = "big-tests")]
use filecoin_proofs::{
SectorShape1GiB, SectorShape32GiB, SectorShape512MiB, SectorShape64GiB, SectorShape8MiB,
};
use log::{error, info};
use serde::{Deserialize, Serialize};
use filecoin_proofs::{Commitment, PoRepConfig, ProverId};
#[cfg(feature = "persist-regression-proofs")]
use filecoin_proofs::{
MerkleTreeTrait, SealCommitOutput, SealPreCommitOutput, PUBLISHED_SECTOR_SIZES,
};
use storage_proofs_core::sector::SectorId;
const V16_SEAL_REGRESSION_RECORDS: &str = "seal_regression_records-v16.json";
const V18_SEAL_REGRESSION_RECORDS: &str = "seal_regression_records-v18.json";
const SEAL_REGRESSION_RECORD_VERSIONS: [(&str, &str); 2] = [
(V16_SEAL_REGRESSION_RECORDS, "v16"),
(V18_SEAL_REGRESSION_RECORDS, "v18"),
];
#[derive(Debug, Clone, Serialize, Deserialize)]
pub(crate) struct SealRegressionRecord {
pub porep_config: PoRepConfig,
pub comm_r: Commitment,
pub comm_d: Commitment,
pub prover_id: ProverId,
pub sector_id: SectorId,
pub ticket: [u8; 32],
pub seed: [u8; 32],
pub proof: Vec<u8>,
}
#[cfg(feature = "persist-regression-proofs")]
#[allow(dead_code)]
#[allow(clippy::too_many_arguments)]
pub(crate) fn persist_generated_proof_for_regression_testing<Tree: 'static + MerkleTreeTrait>(
config: &PoRepConfig,
prover_id: ProverId,
sector_id: SectorId,
ticket: [u8; 32],
seed: [u8; 32],
pre_commit_output: &SealPreCommitOutput,
commit_output: &SealCommitOutput,
) -> Result<()> {
use std::io::Write;
use anyhow::ensure;
use file_lock::{FileLock, FileOptions};
const DEFAULT_SEAL_REGRESSION_RECORD: &str = "seal_regression_records.json";
let sector_size: u64 = config.sector_size.into();
if PUBLISHED_SECTOR_SIZES.contains(§or_size) {
let output_path = Path::new(&std::env::var("CARGO_MANIFEST_DIR")?)
.join("tests")
.join(DEFAULT_SEAL_REGRESSION_RECORD);
let comm_d = pre_commit_output.comm_d;
let comm_r = pre_commit_output.comm_r;
let verified = verify_seal::<Tree>(
config,
comm_r,
comm_d,
prover_id,
sector_id,
ticket,
seed,
&commit_output.proof,
)?;
ensure!(verified, "failed to verify valid seal");
let record = SealRegressionRecord {
porep_config: config.clone(),
comm_r,
comm_d,
prover_id,
sector_id,
ticket,
seed,
proof: commit_output.proof.clone(),
};
let json = serde_json::to_string(&record)?;
let options = FileOptions::new().write(true).create(true).append(true);
let mut filelock = FileLock::lock(output_path, true , options)?;
writeln!(filelock.file, "{}", json)?;
}
Ok(())
}
pub(crate) fn load_regression_records(records: &Path) -> Result<Vec<SealRegressionRecord>> {
let file = File::open(records)?;
let inputs = io::BufReader::new(file).lines();
let mut records = Vec::new();
for input in inputs {
let input = input.unwrap();
let record: SealRegressionRecord = serde_json::from_str(&input)?;
records.push(record);
}
Ok(records)
}
#[allow(non_snake_case)]
#[allow(unused_variables)]
pub(crate) fn regression_verify_seal_proof(record: &SealRegressionRecord) -> Result<bool> {
let r = record;
let sector_size: u64 = r.porep_config.sector_size.into();
let verified = match sector_size {
SECTOR_SIZE_2_KIB => verify_seal::<SectorShape2KiB>(
&r.porep_config,
r.comm_r,
r.comm_d,
r.prover_id,
r.sector_id,
r.ticket,
r.seed,
&r.proof,
),
SECTOR_SIZE_4_KIB => verify_seal::<SectorShape4KiB>(
&r.porep_config,
r.comm_r,
r.comm_d,
r.prover_id,
r.sector_id,
r.ticket,
r.seed,
&r.proof,
),
SECTOR_SIZE_16_KIB => verify_seal::<SectorShape16KiB>(
&r.porep_config,
r.comm_r,
r.comm_d,
r.prover_id,
r.sector_id,
r.ticket,
r.seed,
&r.proof,
),
SECTOR_SIZE_32_KIB => verify_seal::<SectorShape32KiB>(
&r.porep_config,
r.comm_r,
r.comm_d,
r.prover_id,
r.sector_id,
r.ticket,
r.seed,
&r.proof,
),
#[cfg(feature = "big-tests")]
SECTOR_SIZE_8_MIB => verify_seal::<SectorShape8MiB>(
&r.porep_config,
r.comm_r,
r.comm_d,
r.prover_id,
r.sector_id,
r.ticket,
r.seed,
&r.proof,
),
#[cfg(feature = "big-tests")]
SECTOR_SIZE_512_MIB => verify_seal::<SectorShape512MiB>(
&r.porep_config,
r.comm_r,
r.comm_d,
r.prover_id,
r.sector_id,
r.ticket,
r.seed,
&r.proof,
),
#[cfg(feature = "big-tests")]
SECTOR_SIZE_1_GIB => verify_seal::<SectorShape1GiB>(
&r.porep_config,
r.comm_r,
r.comm_d,
r.prover_id,
r.sector_id,
r.ticket,
r.seed,
&r.proof,
),
#[cfg(feature = "big-tests")]
SECTOR_SIZE_32_GIB => verify_seal::<SectorShape32GiB>(
&r.porep_config,
r.comm_r,
r.comm_d,
r.prover_id,
r.sector_id,
r.ticket,
r.seed,
&r.proof,
),
#[cfg(feature = "big-tests")]
SECTOR_SIZE_64_GIB => verify_seal::<SectorShape64GiB>(
&r.porep_config,
r.comm_r,
r.comm_d,
r.prover_id,
r.sector_id,
r.ticket,
r.seed,
&r.proof,
),
#[cfg(not(feature = "big-tests"))]
SECTOR_SIZE_8_MIB | SECTOR_SIZE_512_MIB | SECTOR_SIZE_1_GIB | SECTOR_SIZE_32_GIB
| SECTOR_SIZE_64_GIB => {
info!(
"`big-tests` feature is disabled, hence skipping verification for sector size [{}]",
sector_size
);
Ok(true)
}
_ => {
error!(
"Cannot verify proof: Unsupported sector size [{}]",
sector_size
);
Ok(false)
}
}?;
Ok(verified)
}
#[test]
#[cfg(not(target_os = "macos"))]
fn run_seal_regression_tests() -> Result<()> {
fil_logger::maybe_init();
let basedir = Path::new(&std::env::var("CARGO_MANIFEST_DIR")?).join("tests");
for (path, version) in SEAL_REGRESSION_RECORD_VERSIONS {
let path = basedir.join(path);
info!("Loading regression records from {:?}", path);
let records = load_regression_records(&path)?;
let total_records = records.len();
for (i, record) in records.iter().enumerate() {
let sector_size: u64 = record.porep_config.sector_size.into();
let status = match regression_verify_seal_proof(record)? {
true => "OK",
false => "FAILED",
};
info!(
"[Record {}/{}: {}] seal regression record verified for sector size {}: [{}]",
i, total_records, version, sector_size, status
);
}
info!("Done processing regression records from {:?}", path);
}
Ok(())
}