From 168f71b8044c4c8a37b08386f09d3165f698b5bf Mon Sep 17 00:00:00 2001 From: porcuquine <1746729+porcuquine@users.noreply.github.com> Date: Tue, 8 Sep 2020 09:27:56 -0700 Subject: [PATCH] Add FaultySectors error to Fallback PoSt. (#1274) * Add FaultySectors error to Fallback PoSt. * Test FaultySectors error with invalid PoSt. * Apply code review. Co-authored-by: porcuquine --- storage-proofs/core/src/error.rs | 3 + storage-proofs/post/src/fallback/vanilla.rs | 261 +++++++++++++++++++- 2 files changed, 252 insertions(+), 12 deletions(-) diff --git a/storage-proofs/core/src/error.rs b/storage-proofs/core/src/error.rs index 87f3316ac..e924fe639 100644 --- a/storage-proofs/core/src/error.rs +++ b/storage-proofs/core/src/error.rs @@ -1,5 +1,6 @@ use std::any::Any; +use crate::sector::SectorId; use bellperson::SynthesisError; pub use anyhow::Result; @@ -37,6 +38,8 @@ pub enum Error { Unclassified(String), #[error("Missing Private Input {0} for sector {1}")] MissingPrivateInput(&'static str, u64), + #[error("faulty sectors {:?}", _0)] + FaultySectors(Vec), } impl From> for Error { diff --git a/storage-proofs/post/src/fallback/vanilla.rs b/storage-proofs/post/src/fallback/vanilla.rs index aec2b67ad..a7f0c7995 100644 --- a/storage-proofs/post/src/fallback/vanilla.rs +++ b/storage-proofs/post/src/fallback/vanilla.rs @@ -1,3 +1,4 @@ +use std::collections::BTreeSet; use std::marker::PhantomData; use anyhow::ensure; @@ -10,7 +11,7 @@ use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; use storage_proofs_core::{ - error::Result, + error::{Error, Result}, hasher::{Domain, HashFunction, Hasher}, merkle::{MerkleProof, MerkleProofTrait, MerkleTreeTrait, MerkleTreeWrapper}, parameter_cache::ParameterSetMetadata, @@ -230,6 +231,11 @@ pub fn generate_leaf_challenge( Ok(challenged_range_index) } +enum ProofOrFault { + Proof(T), + Fault(SectorId), +} + impl<'a, Tree: 'a + MerkleTreeTrait> ProofScheme<'a> for FallbackPoSt<'a, Tree> { type PublicParams = PublicParams; type SetupParams = SetupParams; @@ -293,6 +299,9 @@ impl<'a, Tree: 'a + MerkleTreeTrait> ProofScheme<'a> for FallbackPoSt<'a, Tree> let mut partition_proofs = Vec::new(); + // Use `BTreeSet` so failure result will be canonically ordered (sorted). + let mut faulty_sectors = BTreeSet::new(); + for (j, (pub_sectors_chunk, priv_sectors_chunk)) in pub_inputs .sectors .chunks(num_sectors_per_chunk) @@ -319,7 +328,8 @@ impl<'a, Tree: 'a + MerkleTreeTrait> ProofScheme<'a> for FallbackPoSt<'a, Tree> Tree::Arity::to_usize(), ); - let inclusion_proofs = (0..pub_params.challenge_count) + let mut inclusion_proofs = Vec::new(); + for proof_or_fault in (0..pub_params.challenge_count) .into_par_iter() .map(|n| { let challenge_index = ((j * num_sectors_per_chunk + i) @@ -332,9 +342,34 @@ impl<'a, Tree: 'a + MerkleTreeTrait> ProofScheme<'a> for FallbackPoSt<'a, Tree> challenge_index, )?; - tree.gen_cached_proof(challenged_leaf_start as usize, Some(rows_to_discard)) + let proof = tree.gen_cached_proof( + challenged_leaf_start as usize, + Some(rows_to_discard), + ); + match proof { + Ok(proof) => { + if proof.validate(challenged_leaf_start as usize) + && proof.root() == priv_sector.comm_r_last + { + Ok(ProofOrFault::Proof(proof)) + } else { + Ok(ProofOrFault::Fault(sector_id)) + } + } + Err(_) => Ok(ProofOrFault::Fault(sector_id)), + } }) - .collect::>>()?; + .collect::>>()? + { + match proof_or_fault { + ProofOrFault::Proof(proof) => { + inclusion_proofs.push(proof); + } + ProofOrFault::Fault(sector_id) => { + faulty_sectors.insert(sector_id); + } + } + } proofs.push(SectorProof { inclusion_proofs, @@ -352,7 +387,11 @@ impl<'a, Tree: 'a + MerkleTreeTrait> ProofScheme<'a> for FallbackPoSt<'a, Tree> partition_proofs.push(Proof { sectors: proofs }); } - Ok(partition_proofs) + if faulty_sectors.is_empty() { + Ok(partition_proofs) + } else { + Err(Error::FaultySectors(faulty_sectors.into_iter().collect()).into()) + } } fn verify_all_partitions( @@ -390,6 +429,7 @@ impl<'a, Tree: 'a + MerkleTreeTrait> ProofScheme<'a> for FallbackPoSt<'a, Tree> proof.sectors.len(), num_sectors_per_chunk, ); + for (i, (pub_sector, sector_proof)) in pub_sectors_chunk .iter() .zip(proof.sectors.iter()) @@ -449,7 +489,6 @@ impl<'a, Tree: 'a + MerkleTreeTrait> ProofScheme<'a> for FallbackPoSt<'a, Tree> } } } - Ok(true) } @@ -512,13 +551,11 @@ mod tests { let mut pub_sectors = Vec::new(); let mut priv_sectors = Vec::new(); - let mut trees = Vec::new(); - for _i in 0..total_sector_count { - let (_data, tree) = - generate_tree::(rng, leaves, Some(temp_path.to_path_buf())); - trees.push(tree); - } + let trees = (0..total_sector_count) + .map(|_| generate_tree::(rng, leaves, Some(temp_path.to_path_buf())).1) + .collect::>(); + for (i, tree) in trees.iter().enumerate() { let comm_c = ::Domain::random(rng); let comm_r_last = tree.root(); @@ -562,102 +599,302 @@ mod tests { assert!(is_valid); } + fn test_invalid_fallback_post( + total_sector_count: usize, + sector_count: usize, + partitions: usize, + ) where + Tree::Store: 'static, + { + let rng = &mut XorShiftRng::from_seed(crate::TEST_SEED); + + let leaves = 64 * get_base_tree_count::(); + let sector_size = leaves * NODE_SIZE; + + let pub_params = PublicParams { + sector_size: sector_size as u64, + challenge_count: 10, + sector_count, + }; + + let randomness = ::Domain::random(rng); + let prover_id = ::Domain::random(rng); + + let temp_dir = tempfile::tempdir().unwrap(); + let temp_path = temp_dir.path(); + + let mut pub_sectors = Vec::new(); + let mut priv_sectors = Vec::new(); + + let mut trees = Vec::new(); + + let mut faulty_sectors = Vec::::new(); + + for _i in 0..total_sector_count { + let (_data, tree) = + generate_tree::(rng, leaves, Some(temp_path.to_path_buf())); + trees.push(tree); + } + + let faulty_denominator = 3; + + let (_data, wrong_tree) = + generate_tree::(rng, leaves, Some(temp_path.to_path_buf())); + + for (i, tree) in trees.iter().enumerate() { + let make_faulty = i % faulty_denominator == 0; + + let comm_c = ::Domain::random(rng); + let comm_r_last = tree.root(); + + priv_sectors.push(PrivateSector { + tree: if make_faulty { &wrong_tree } else { tree }, + comm_c, + comm_r_last, + }); + + let comm_r = ::Function::hash2(&comm_c, &comm_r_last); + + if make_faulty { + faulty_sectors.push((i as u64).into()); + } + + pub_sectors.push(PublicSector { + id: (i as u64).into(), + comm_r, + }); + } + + let pub_inputs = PublicInputs { + randomness, + prover_id, + sectors: &pub_sectors, + k: None, + }; + + let priv_inputs = PrivateInputs:: { + sectors: &priv_sectors[..], + }; + + let proof = FallbackPoSt::::prove_all_partitions( + &pub_params, + &pub_inputs, + &priv_inputs, + partitions, + ); + + match proof { + Ok(proof) => { + let is_valid = + FallbackPoSt::::verify_all_partitions(&pub_params, &pub_inputs, &proof) + .expect("verification failed"); + assert!(!is_valid, "PoSt returned a valid proof with invalid input"); + } + Err(e) => match e.downcast::() { + Err(_) => panic!("failed to downcast to Error"), + Ok(Error::FaultySectors(sector_ids)) => assert_eq!(faulty_sectors, sector_ids), + Ok(_) => panic!("PoSt failed to return FaultySectors error."), + }, + }; + } + #[test] fn fallback_post_pedersen_single_partition_matching_base_8() { test_fallback_post::>(5, 5, 1); } + #[test] + fn invalid_fallback_post_pedersen_single_partition_matching_base_8() { + test_invalid_fallback_post::>(5, 5, 1); + } + #[test] fn fallback_post_poseidon_single_partition_matching_base_8() { test_fallback_post::>(5, 5, 1); } + #[test] + fn invalid_fallback_post_poseidon_single_partition_matching_base_8() { + test_invalid_fallback_post::>(5, 5, 1); + } + #[test] fn fallback_post_poseidon_single_partition_smaller_base_8() { test_fallback_post::>(3, 5, 1); } + #[test] + fn invalid_fallback_post_poseidon_single_partition_smaller_base_8() { + test_invalid_fallback_post::>(3, 5, 1); + } + #[test] fn fallback_post_poseidon_two_partitions_matching_base_8() { test_fallback_post::>(4, 2, 2); } + #[test] + fn invalid_fallback_post_poseidon_two_partitions_matching_base_8() { + test_invalid_fallback_post::>(4, 2, 2); + } + #[test] fn fallback_post_poseidon_two_partitions_smaller_base_8() { test_fallback_post::>(5, 3, 2); } + #[test] + fn invalid_fallback_post_poseidon_two_partitions_smaller_base_8() { + test_invalid_fallback_post::>(5, 3, 2); + } + #[test] fn fallback_post_pedersen_single_partition_matching_sub_8_4() { test_fallback_post::>(5, 5, 1); } + #[test] + fn invalid_fallback_post_pedersen_single_partition_matching_sub_8_4() { + test_invalid_fallback_post::>(5, 5, 1); + } + #[test] fn fallback_post_poseidon_single_partition_matching_sub_8_4() { test_fallback_post::>(5, 5, 1); } + #[test] + fn invalid_fallback_post_poseidon_single_partition_matching_sub_8_4() { + test_invalid_fallback_post::>(5, 5, 1); + } + #[test] fn fallback_post_poseidon_single_partition_smaller_sub_8_4() { test_fallback_post::>(3, 5, 1); } + #[test] + fn invalid_fallback_post_poseidon_single_partition_smaller_sub_8_4() { + test_invalid_fallback_post::>(3, 5, 1); + } + #[test] fn fallback_post_poseidon_two_partitions_matching_sub_8_4() { test_fallback_post::>(4, 2, 2); } + #[test] + fn invalid_fallback_post_poseidon_two_partitions_matching_sub_8_4() { + test_invalid_fallback_post::>(4, 2, 2); + } + #[test] fn fallback_post_poseidon_two_partitions_matching_sub_8_8() { test_fallback_post::>(4, 2, 2); } + #[test] + fn invalid_fallback_post_poseidon_two_partitions_matching_sub_8_8() { + test_invalid_fallback_post::>(4, 2, 2); + } + #[test] fn fallback_post_poseidon_two_partitions_smaller_sub_8_4() { test_fallback_post::>(5, 3, 2); } + #[test] + fn invalid_fallback_post_poseidon_two_partitions_smaller_sub_8_4() { + test_invalid_fallback_post::>(5, 3, 2); + } + #[test] fn fallback_post_poseidon_two_partitions_smaller_sub_8_8() { test_fallback_post::>(5, 3, 2); } + #[test] + fn invalid_fallback_post_poseidon_two_partitions_smaller_sub_8_8() { + test_invalid_fallback_post::>(5, 3, 2); + } + #[test] fn fallback_post_pedersen_single_partition_matching_top_8_4_2() { test_fallback_post::>(5, 5, 1); } + + #[test] + fn invalid_fallback_post_pedersen_single_partition_matching_top_8_4_2() { + test_invalid_fallback_post::>(5, 5, 1); + } + #[test] fn fallback_post_pedersen_single_partition_matching_top_8_8_2() { test_fallback_post::>(5, 5, 1); } + #[test] + fn invalid_fallback_post_pedersen_single_partition_matching_top_8_8_2() { + test_invalid_fallback_post::>(5, 5, 1); + } + #[test] fn fallback_post_poseidon_single_partition_matching_top_8_4_2() { test_fallback_post::>(5, 5, 1); } + #[test] + fn invalid_fallback_post_poseidon_single_partition_matching_top_8_4_2() { + test_invalid_fallback_post::>(5, 5, 1); + } + #[test] fn fallback_post_poseidon_single_partition_matching_top_8_8_2() { test_fallback_post::>(5, 5, 1); } + #[test] + fn invalid_fallback_post_poseidon_single_partition_matching_top_8_8_2() { + test_invalid_fallback_post::>(5, 5, 1); + } + #[test] fn fallback_post_poseidon_single_partition_smaller_top_8_4_2() { test_fallback_post::>(3, 5, 1); } + #[test] + fn invalid_fallback_post_poseidon_single_partition_smaller_top_8_4_2() { + test_invalid_fallback_post::>(3, 5, 1); + } + #[test] fn fallback_post_poseidon_two_partitions_matching_top_8_4_2() { test_fallback_post::>(4, 2, 2); } + #[test] + fn invalid_fallback_post_poseidon_two_partitions_matching_top_8_4_2() { + test_invalid_fallback_post::>(4, 2, 2); + } + #[test] fn fallback_post_poseidon_two_partitions_smaller_top_8_4_2() { test_fallback_post::>(5, 3, 2); } + #[test] + fn invalid_fallback_post_poseidon_two_partitions_smaller_top_8_4_2() { + test_invalid_fallback_post::>(5, 3, 2); + } + #[test] fn fallback_post_poseidon_two_partitions_smaller_top_8_8_2() { test_fallback_post::>(5, 3, 2); } + + #[test] + fn invalid_fallback_post_poseidon_two_partitions_smaller_top_8_8_2() { + test_invalid_fallback_post::>(5, 3, 2); + } }