Skip to content

Add seal regression testing support #1765

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Oct 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,22 @@ Some results are displayed at the command line, or alternatively written as JSON

Note: On macOS you need `gtime` (`brew install gnu-time`), as the built in `time` command is not enough.

## Regression Testing

Within the `filecoin-proofs` crate there is a regression suite. The idea is to record some generated proofs at various proof release versions, so that future versions/revisions can always ensure that it can properly verify historical proofs as expected.

By default, there is a test that verifies all known regression records that exist within the source tree.

In order to generate a new set of regression records, the feature flag `persist-regression-proofs` must be used.

When the feature is used and all of the `filecoin-proofs` tests are run (including the ignored tests), the following files are written to disk:

```
filecoin-proofs/tests/seal_regression_records.json
```

Once the new files are generated with a given proof version, they should be renamed appropriately and added to the repository and then referenced for verification during routine testing in the `filecoin-proofs/tests/regression.rs` source (see the `const` values at the top and go from there).

## Logging

For better logging with backtraces on errors, developers should use `expects` rather than `expect` on `Result<T, E>` and `Option<T>`.
Expand Down
2 changes: 2 additions & 0 deletions filecoin-proofs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ serde = { workspace = true, features = ["rc", "derive"] }
serde_json.workspace = true
sha2.workspace = true
typenum.workspace = true
file-lock = { version = "2.1.10", optional = true }

[dev-dependencies]
# Sorted alphabetically
Expand Down Expand Up @@ -86,6 +87,7 @@ fixed-rows-to-discard = [
"storage-proofs-post/fixed-rows-to-discard",
"storage-proofs-update/fixed-rows-to-discard",
]
persist-regression-proofs = ["dep:file-lock"]

[[bench]]
name = "preprocessing"
Expand Down
17 changes: 3 additions & 14 deletions filecoin-proofs/src/types/porep_config.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::path::PathBuf;

use anyhow::{anyhow, Result};
use serde::{Deserialize, Serialize};
use storage_proofs_core::{
api_version::{ApiFeature, ApiVersion},
merkle::MerkleTreeTrait,
Expand All @@ -18,7 +19,7 @@ use crate::{
POREP_PARTITIONS,
};

#[derive(Clone, Debug)]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct PoRepConfig {
pub sector_size: SectorSize,
pub partitions: PoRepProofPartitions,
Expand Down Expand Up @@ -79,19 +80,7 @@ impl PoRepConfig {
api_version: ApiVersion,
api_features: Vec<ApiFeature>,
) -> Result<Self> {
let mut config = Self {
sector_size: SectorSize(sector_size),
partitions: PoRepProofPartitions(
*POREP_PARTITIONS
.read()
.expect("POREP_PARTITIONS poisoned")
.get(&sector_size)
.expect("unknown sector size"),
),
porep_id,
api_version,
api_features: vec![],
};
let mut config = PoRepConfig::new_groth16(sector_size, porep_id, api_version);
for feature in api_features {
config.enable_feature(feature)?;
}
Expand Down
4 changes: 3 additions & 1 deletion filecoin-proofs/src/types/porep_proof_partitions.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#[derive(Clone, Copy, Debug)]
use serde::{Deserialize, Serialize};

#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct PoRepProofPartitions(pub u8);

impl From<PoRepProofPartitions> for usize {
Expand Down
3 changes: 2 additions & 1 deletion filecoin-proofs/src/types/sector_size.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use fr32::to_unpadded_bytes;
use serde::{Deserialize, Serialize};

use crate::types::{PaddedBytesAmount, UnpaddedBytesAmount};

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct SectorSize(pub u64);

impl From<u64> for SectorSize {
Expand Down
101 changes: 83 additions & 18 deletions filecoin-proofs/tests/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,15 @@ use filecoin_proofs::constants::{

#[cfg(feature = "big-tests")]
use filecoin_proofs::{
SectorShape512MiB, SectorShape64GiB, SECTOR_SIZE_512_MIB, SECTOR_SIZE_64_GIB,
SectorShape512MiB, SectorShape64GiB, SectorShape8MiB, SECTOR_SIZE_512_MIB, SECTOR_SIZE_64_GIB,
SECTOR_SIZE_8_MIB,
};

#[cfg(feature = "persist-regression-proofs")]
mod regression;
#[cfg(feature = "persist-regression-proofs")]
use regression::persist_generated_proof_for_regression_testing;

// Use a fixed PoRep ID, so that the parents cache can be re-used between some tests.
// Note however, that parents caches cannot be shared when testing the differences
// between API v1 and v2 behaviour (since the parent caches will be different for the
Expand Down Expand Up @@ -403,30 +409,68 @@ fn test_seal_lifecycle_32kib_base_8() -> Result<()> {

#[cfg(feature = "big-tests")]
#[test]
fn test_seal_lifecycle_512mib_porep_id_v1_top_8_0_0_api_v1() -> Result<()> {
use filecoin_proofs::{SectorShape512MiB, SECTOR_SIZE_512_MIB};
let porep_id_v1: u64 = 2; // This is a RegisteredSealProof value
fn test_seal_lifecycle_8mib_base_8() -> Result<()> {
let test_inputs = vec![
(ARBITRARY_POREP_ID_V1_0_0, ApiVersion::V1_0_0, Vec::new()),
(ARBITRARY_POREP_ID_V1_1_0, ApiVersion::V1_1_0, Vec::new()),
(ARBITRARY_POREP_ID_V1_2_0, ApiVersion::V1_2_0, Vec::new()),
(
ARBITRARY_POREP_ID_V1_2_0,
ApiVersion::V1_2_0,
vec![ApiFeature::SyntheticPoRep],
),
(
ARBITRARY_POREP_ID_V1_2_0,
ApiVersion::V1_2_0,
vec![ApiFeature::NonInteractivePoRep],
),
];

let mut porep_id = [0u8; 32];
porep_id[..8].copy_from_slice(&porep_id_v1.to_le_bytes());
assert!(is_legacy_porep_id(porep_id));
for (porep_id, api_version, features) in test_inputs {
let porep_config = PoRepConfig::new_groth16_with_features(
SECTOR_SIZE_8_MIB,
porep_id,
api_version,
features,
)?;

let porep_config = PoRepConfig::new_groth16(SECTOR_SIZE_512_MIB, porep_id, ApiVersion::V1_0_0);
seal_lifecycle::<SectorShape512MiB>(&porep_config)
seal_lifecycle::<SectorShape8MiB>(&porep_config)?;
}

Ok(())
}

#[cfg(feature = "big-tests")]
#[test]
fn test_seal_lifecycle_512mib_porep_id_v1_top_8_0_0_api_v1_1() -> Result<()> {
use filecoin_proofs::{SectorShape512MiB, SECTOR_SIZE_512_MIB};
let porep_id_v1_1: u64 = 7; // This is a RegisteredSealProof value
fn test_seal_lifecycle_512mib_base_8() -> Result<()> {
let test_inputs = vec![
(ARBITRARY_POREP_ID_V1_0_0, ApiVersion::V1_0_0, Vec::new()),
(ARBITRARY_POREP_ID_V1_1_0, ApiVersion::V1_1_0, Vec::new()),
(ARBITRARY_POREP_ID_V1_2_0, ApiVersion::V1_2_0, Vec::new()),
(
ARBITRARY_POREP_ID_V1_2_0,
ApiVersion::V1_2_0,
vec![ApiFeature::SyntheticPoRep],
),
(
ARBITRARY_POREP_ID_V1_2_0,
ApiVersion::V1_2_0,
vec![ApiFeature::NonInteractivePoRep],
),
];

let mut porep_id = [0u8; 32];
porep_id[..8].copy_from_slice(&porep_id_v1_1.to_le_bytes());
assert!(!is_legacy_porep_id(porep_id));
for (porep_id, api_version, features) in test_inputs {
let porep_config = PoRepConfig::new_groth16_with_features(
SECTOR_SIZE_512_MIB,
porep_id,
api_version,
features,
)?;

seal_lifecycle::<SectorShape512MiB>(&porep_config)?;
}

let porep_config = PoRepConfig::new_groth16(SECTOR_SIZE_512_MIB, porep_id, ApiVersion::V1_1_0);
seal_lifecycle::<SectorShape512MiB>(&porep_config)
Ok(())
}

#[cfg(feature = "big-tests")]
Expand Down Expand Up @@ -962,6 +1006,15 @@ fn aggregate_seal_proofs<Tree: 'static + MerkleTreeTrait>(
);

for aggregate_version in aggregate_versions {
info!(
"Aggregating {} seal proofs with ApiVersion {}, Snarkpack{}, Features {:?}, and PoRep ID {:?}",
num_proofs_to_aggregate,
porep_config.api_version,
aggregate_version,
porep_config.api_features,
porep_config.porep_id
);

let mut commit_outputs = Vec::with_capacity(num_proofs_to_aggregate);
let mut commit_inputs = Vec::with_capacity(num_proofs_to_aggregate);
let mut seeds = Vec::with_capacity(num_proofs_to_aggregate);
Expand Down Expand Up @@ -2224,6 +2277,18 @@ fn proof_and_unseal<Tree: 'static + MerkleTreeTrait>(
aggregation_enabled,
)?;

// For regression suite only -- persist seal proof and everything required for the verify here
#[cfg(feature = "persist-regression-proofs")]
persist_generated_proof_for_regression_testing::<Tree>(
config,
prover_id,
sector_id,
ticket,
seed,
&pre_commit_output,
&commit_output,
)?;

unseal::<Tree>(
config,
cache_dir_path,
Expand Down Expand Up @@ -2283,7 +2348,7 @@ fn create_seal<R: Rng, Tree: 'static + MerkleTreeTrait>(
)?;
compare_trees::<Tree>(&tree_r_last_dir, &cache_dir, CacheKey::CommRLastTree)?;

// Check if creating only the tree_r generates the same output as the full pre commit phase 2
// Check if creating only the tree_c generates the same output as the full pre commit phase 2
// process.
let tree_c_dir = tempdir().expect("failed to create temp dir");
generate_tree_c::<_, _, Tree>(
Expand Down
Loading