1use std::collections::BTreeSet;
2use std::marker::PhantomData;
3
4use anyhow::ensure;
5use blstrs::Scalar as Fr;
6use byteorder::{ByteOrder, LittleEndian};
7use filecoin_hashers::{Domain, HashFunction, Hasher};
8use generic_array::typenum::Unsigned;
9use log::{error, trace};
10use rayon::prelude::{
11 IndexedParallelIterator, IntoParallelIterator, IntoParallelRefIterator, ParallelIterator,
12};
13use serde::{de::DeserializeOwned, Deserialize, Serialize};
14use sha2::{Digest, Sha256};
15use storage_proofs_core::{
16 api_version::ApiVersion,
17 error::{Error, Result},
18 merkle::{MerkleProof, MerkleProofTrait, MerkleTreeTrait, MerkleTreeWrapper},
19 parameter_cache::ParameterSetMetadata,
20 proof::ProofScheme,
21 sector::SectorId,
22 util::{default_rows_to_discard, NODE_SIZE},
23};
24
25use super::utils::get_challenge_index;
26
27#[derive(Debug, Clone)]
28pub struct SetupParams {
29 pub sector_size: u64,
31 pub challenge_count: usize,
33 pub sector_count: usize,
35 pub api_version: ApiVersion,
36}
37
38#[derive(Debug, Clone)]
39pub struct PublicParams {
40 pub sector_size: u64,
42 pub challenge_count: usize,
44 pub sector_count: usize,
46 pub api_version: ApiVersion,
47}
48
49#[derive(Debug, Default)]
50pub struct ChallengeRequirements {
51 pub minimum_challenge_count: usize,
53}
54
55impl ParameterSetMetadata for PublicParams {
56 fn identifier(&self) -> String {
57 format!(
58 "FallbackPoSt::PublicParams{{sector_size: {}, challenge_count: {}, sector_count: {}}}",
59 self.sector_size(),
60 self.challenge_count,
61 self.sector_count,
62 )
63 }
64
65 fn sector_size(&self) -> u64 {
66 self.sector_size
67 }
68}
69
70#[derive(Debug, Clone, Serialize, Deserialize)]
71pub struct PublicInputs<T: Domain> {
72 #[serde(bound = "")]
73 pub randomness: T,
74 #[serde(bound = "")]
75 pub prover_id: T,
76 #[serde(bound = "")]
77 pub sectors: Vec<PublicSector<T>>,
78 pub k: Option<usize>,
80}
81
82#[derive(Debug, Clone, Serialize, Deserialize)]
83pub struct PublicSector<T: Domain> {
84 pub id: SectorId,
85 #[serde(bound = "")]
86 pub comm_r: T,
87}
88
89#[derive(Debug)]
90pub struct PrivateSector<'a, Tree: MerkleTreeTrait> {
91 pub tree: &'a MerkleTreeWrapper<
92 Tree::Hasher,
93 Tree::Store,
94 Tree::Arity,
95 Tree::SubTreeArity,
96 Tree::TopTreeArity,
97 >,
98 pub comm_c: <Tree::Hasher as Hasher>::Domain,
99 pub comm_r_last: <Tree::Hasher as Hasher>::Domain,
100}
101
102#[derive(Debug)]
103pub struct PrivateInputs<'a, Tree: MerkleTreeTrait> {
104 pub sectors: &'a [PrivateSector<'a, Tree>],
105}
106
107#[derive(Debug, Clone, Serialize, Deserialize)]
108pub struct Proof<P: MerkleProofTrait> {
109 #[serde(bound(
110 serialize = "SectorProof<P>: Serialize",
111 deserialize = "SectorProof<P>: Deserialize<'de>"
112 ))]
113 pub sectors: Vec<SectorProof<P>>,
114}
115
116#[derive(Debug, Clone, Serialize, Deserialize)]
117pub struct SectorProof<Proof: MerkleProofTrait> {
118 #[serde(bound(
119 serialize = "MerkleProof<Proof::Hasher, Proof::Arity, Proof::SubTreeArity, Proof::TopTreeArity>: Serialize",
120 deserialize = "MerkleProof<Proof::Hasher, Proof::Arity, Proof::SubTreeArity, Proof::TopTreeArity>: DeserializeOwned"
121 ))]
122 pub inclusion_proofs:
123 Vec<MerkleProof<Proof::Hasher, Proof::Arity, Proof::SubTreeArity, Proof::TopTreeArity>>,
124 pub comm_c: <Proof::Hasher as Hasher>::Domain,
125 pub comm_r_last: <Proof::Hasher as Hasher>::Domain,
126}
127
128impl<P: MerkleProofTrait> SectorProof<P> {
129 pub fn leafs(&self) -> Vec<<P::Hasher as Hasher>::Domain> {
130 self.inclusion_proofs
131 .iter()
132 .map(MerkleProofTrait::leaf)
133 .collect()
134 }
135
136 pub fn comm_r_last(&self) -> <P::Hasher as Hasher>::Domain {
137 self.inclusion_proofs[0].root()
138 }
139
140 pub fn commitments(&self) -> Vec<<P::Hasher as Hasher>::Domain> {
141 self.inclusion_proofs
142 .iter()
143 .map(MerkleProofTrait::root)
144 .collect()
145 }
146
147 #[allow(clippy::type_complexity)]
148 pub fn paths(&self) -> Vec<Vec<(Vec<<P::Hasher as Hasher>::Domain>, usize)>> {
149 self.inclusion_proofs
150 .iter()
151 .map(MerkleProofTrait::path)
152 .collect()
153 }
154
155 pub fn as_options(&self) -> Vec<Vec<(Vec<Option<Fr>>, Option<usize>)>> {
156 self.inclusion_proofs
157 .iter()
158 .map(MerkleProofTrait::as_options)
159 .collect()
160 }
161
162 pub fn inclusion_proofs(
164 &self,
165 ) -> &Vec<MerkleProof<P::Hasher, P::Arity, P::SubTreeArity, P::TopTreeArity>> {
166 &self.inclusion_proofs
167 }
168}
169
170#[derive(Debug, Clone)]
171pub struct FallbackPoSt<'a, Tree>
172where
173 Tree: MerkleTreeTrait,
174{
175 _t: PhantomData<&'a Tree>,
176}
177
178pub fn generate_sector_challenges<T: Domain>(
179 randomness: T,
180 challenge_count: usize,
181 sector_set_len: u64,
182 prover_id: T,
183) -> Result<Vec<u64>> {
184 (0..challenge_count)
185 .map(|n| generate_sector_challenge(randomness, n, sector_set_len, prover_id))
186 .collect()
187}
188
189pub fn generate_sector_challenge<T: Domain>(
191 randomness: T,
192 n: usize,
193 sector_set_len: u64,
194 prover_id: T,
195) -> Result<u64> {
196 let mut hasher = Sha256::new();
197 hasher.update(AsRef::<[u8]>::as_ref(&prover_id));
198 hasher.update(AsRef::<[u8]>::as_ref(&randomness));
199 hasher.update(&n.to_le_bytes()[..]);
200
201 let hash = hasher.finalize();
202
203 let sector_challenge = LittleEndian::read_u64(&hash[..8]);
204 let sector_index = sector_challenge % sector_set_len;
205
206 Ok(sector_index)
207}
208
209pub fn generate_leaf_challenges<T: Domain>(
211 pub_params: &PublicParams,
212 randomness: T,
213 sector_id: u64,
214 challenge_count: usize,
215) -> Vec<u64> {
216 let mut challenges = Vec::with_capacity(challenge_count);
217
218 let mut hasher = Sha256::new();
219 hasher.update(AsRef::<[u8]>::as_ref(&randomness));
220 hasher.update(§or_id.to_le_bytes()[..]);
221
222 for challenge_index in 0..challenge_count {
223 let challenge =
224 generate_leaf_challenge_inner::<T>(hasher.clone(), pub_params, challenge_index as u64);
225 challenges.push(challenge)
226 }
227
228 challenges
229}
230
231pub fn generate_leaf_challenge<T: Domain>(
233 pub_params: &PublicParams,
234 randomness: T,
235 sector_id: u64,
236 leaf_challenge_index: u64,
237) -> u64 {
238 let mut hasher = Sha256::new();
239 hasher.update(AsRef::<[u8]>::as_ref(&randomness));
240 hasher.update(§or_id.to_le_bytes()[..]);
241
242 generate_leaf_challenge_inner::<T>(hasher, pub_params, leaf_challenge_index)
243}
244
245pub fn generate_leaf_challenge_inner<T: Domain>(
246 mut hasher: Sha256,
247 pub_params: &PublicParams,
248 leaf_challenge_index: u64,
249) -> u64 {
250 hasher.update(&leaf_challenge_index.to_le_bytes()[..]);
251 let hash = hasher.finalize();
252
253 let leaf_challenge = LittleEndian::read_u64(&hash[..8]);
254
255 leaf_challenge % (pub_params.sector_size / NODE_SIZE as u64)
256}
257
258pub fn vanilla_proof<Tree: MerkleTreeTrait>(
260 sector_id: SectorId,
261 priv_inputs: &PrivateInputs<'_, Tree>,
262 challenges: &[u64],
263) -> Result<Proof<Tree::Proof>> {
264 ensure!(
265 priv_inputs.sectors.len() == 1,
266 "vanilla_proof called with multiple sector proofs"
267 );
268
269 let priv_sector = &priv_inputs.sectors[0];
270 let comm_c = priv_sector.comm_c;
271 let comm_r_last = priv_sector.comm_r_last;
272 let tree = priv_sector.tree;
273
274 let tree_leafs = tree.leafs();
275 let rows_to_discard = default_rows_to_discard(tree_leafs, Tree::Arity::to_usize());
276
277 trace!(
278 "Generating proof for tree leafs {} and arity {} for sector {}",
279 tree_leafs,
280 Tree::Arity::to_usize(),
281 sector_id,
282 );
283
284 let inclusion_proofs = (0..challenges.len())
285 .into_par_iter()
286 .map(|challenged_leaf_index| {
287 let challenged_leaf = challenges[challenged_leaf_index];
288 let proof = tree.gen_cached_proof(challenged_leaf as usize, Some(rows_to_discard))?;
289
290 ensure!(
291 proof.validate(challenged_leaf as usize) && proof.root() == priv_sector.comm_r_last,
292 "Generated vanilla proof for sector {} is invalid",
293 sector_id
294 );
295
296 Ok(proof)
297 })
298 .collect::<Result<Vec<_>>>()?;
299
300 Ok(Proof {
301 sectors: vec![SectorProof {
302 inclusion_proofs,
303 comm_c,
304 comm_r_last,
305 }],
306 })
307}
308
309impl<'a, Tree: 'a + MerkleTreeTrait> ProofScheme<'a> for FallbackPoSt<'a, Tree> {
310 type PublicParams = PublicParams;
311 type SetupParams = SetupParams;
312 type PublicInputs = PublicInputs<<Tree::Hasher as Hasher>::Domain>;
313 type PrivateInputs = PrivateInputs<'a, Tree>;
314 type Proof = Proof<Tree::Proof>;
315 type Requirements = ChallengeRequirements;
316
317 fn setup(sp: &Self::SetupParams) -> Result<Self::PublicParams> {
318 Ok(PublicParams {
319 sector_size: sp.sector_size,
320 challenge_count: sp.challenge_count,
321 sector_count: sp.sector_count,
322 api_version: sp.api_version,
323 })
324 }
325
326 fn prove<'b>(
327 pub_params: &'b Self::PublicParams,
328 pub_inputs: &'b Self::PublicInputs,
329 priv_inputs: &'b Self::PrivateInputs,
330 ) -> Result<Self::Proof> {
331 let proofs = Self::prove_all_partitions(pub_params, pub_inputs, priv_inputs, 1)?;
332 let k = pub_inputs.k.unwrap_or(0);
333 assert!(
337 k < 1,
338 "It is a programmer error to call StackedDrg::prove with more than one partition."
339 );
340
341 Ok(proofs[k].to_owned())
342 }
343
344 fn prove_all_partitions<'b>(
345 pub_params: &'b Self::PublicParams,
346 pub_inputs: &'b Self::PublicInputs,
347 priv_inputs: &'b Self::PrivateInputs,
348 partition_count: usize,
349 ) -> Result<Vec<Self::Proof>> {
350 ensure!(
351 priv_inputs.sectors.len() == pub_inputs.sectors.len(),
352 "inconsistent number of private and public sectors {} != {}",
353 priv_inputs.sectors.len(),
354 pub_inputs.sectors.len(),
355 );
356
357 let num_sectors_per_chunk = pub_params.sector_count;
358 let num_sectors = pub_inputs.sectors.len();
359
360 ensure!(
361 num_sectors <= partition_count * num_sectors_per_chunk,
362 "cannot prove the provided number of sectors: {} > {} * {}",
363 num_sectors,
364 partition_count,
365 num_sectors_per_chunk,
366 );
367
368 let mut partition_proofs = Vec::new();
369
370 let mut faulty_sectors = BTreeSet::new();
372
373 for (j, (pub_sectors_chunk, priv_sectors_chunk)) in pub_inputs
374 .sectors
375 .chunks(num_sectors_per_chunk)
376 .zip(priv_inputs.sectors.chunks(num_sectors_per_chunk))
377 .enumerate()
378 {
379 let (mut proofs, mut faults) = pub_sectors_chunk
380 .par_iter()
381 .zip(priv_sectors_chunk.par_iter())
382 .enumerate()
383 .map(|(i, (pub_sector, priv_sector))| {
384 let sector_id = pub_sector.id;
385 let tree = priv_sector.tree;
386 let tree_leafs = tree.leafs();
387 let rows_to_discard =
388 default_rows_to_discard(tree_leafs, Tree::Arity::to_usize());
389
390 trace!(
391 "Generating proof for tree leafs {} and arity {} for sector {}",
392 tree_leafs,
393 Tree::Arity::to_usize(),
394 sector_id,
395 );
396
397 let mut challenge_hasher = Sha256::new();
399 challenge_hasher.update(AsRef::<[u8]>::as_ref(&pub_inputs.randomness));
400 challenge_hasher.update(&u64::from(sector_id).to_le_bytes()[..]);
401
402 let (inclusion_proofs, faults) = (0..pub_params.challenge_count)
403 .into_par_iter()
404 .fold(
405 || (Vec::new(), BTreeSet::new()),
406 |(mut inclusion_proofs, mut faults), n| {
407 let sector_index = j * num_sectors_per_chunk + i;
408 let challenge_index = get_challenge_index(
409 pub_params.api_version,
410 sector_index,
411 pub_params.challenge_count,
412 n,
413 );
414 let challenged_leaf = generate_leaf_challenge_inner::<
415 <Tree::Hasher as Hasher>::Domain,
416 >(
417 challenge_hasher.clone(),
418 pub_params,
419 challenge_index,
420 );
421 let proof = tree.gen_cached_proof(
422 challenged_leaf as usize,
423 Some(rows_to_discard),
424 );
425
426 match proof {
427 Ok(proof) => {
428 if proof.validate(challenged_leaf as usize)
429 && proof.root() == priv_sector.comm_r_last
430 && pub_sector.comm_r
431 == <Tree::Hasher as Hasher>::Function::hash2(
432 &priv_sector.comm_c,
433 &priv_sector.comm_r_last,
434 )
435 {
436 inclusion_proofs.push(proof);
437 } else {
438 error!("Found faulty sector: {:?}", sector_id);
439 faults.insert(sector_id);
440 }
441 }
442 Err(err) => {
443 error!("Found faulty sector: {:?}: {:?}", sector_id, err);
444 faults.insert(sector_id);
445 }
446 }
447 (inclusion_proofs, faults)
448 },
449 )
450 .reduce(
451 || (Vec::new(), BTreeSet::new()),
452 |(mut inclusion_proofs, mut faults), (p, f)| {
453 inclusion_proofs.extend(p);
454 faults.extend(f);
455 (inclusion_proofs, faults)
456 },
457 );
458
459 (
460 SectorProof {
461 inclusion_proofs,
462 comm_c: priv_sector.comm_c,
463 comm_r_last: priv_sector.comm_r_last,
464 },
465 faults,
466 )
467 })
468 .fold(
469 || (Vec::new(), BTreeSet::new()),
470 |(mut sector_proofs, mut sector_faults), (sector_proof, mut faults)| {
471 sector_faults.append(&mut faults);
472 sector_proofs.push(sector_proof);
473 (sector_proofs, sector_faults)
474 },
475 )
476 .reduce(
477 || (Vec::new(), BTreeSet::new()),
478 |(mut sector_proofs, mut sector_faults), (proofs, mut faults)| {
479 sector_proofs.extend(proofs);
480 sector_faults.append(&mut faults);
481 (sector_proofs, sector_faults)
482 },
483 );
484
485 while proofs.len() < num_sectors_per_chunk {
488 proofs.push(proofs[proofs.len() - 1].clone());
489 }
490
491 partition_proofs.push(Proof { sectors: proofs });
492 faulty_sectors.append(&mut faults);
493 }
494
495 if faulty_sectors.is_empty() {
496 Ok(partition_proofs)
497 } else {
498 trace!("Faulty sectors being reported {:?}", faulty_sectors);
499 Err(Error::FaultySectors(faulty_sectors.into_iter().collect()).into())
500 }
501 }
502
503 fn verify_all_partitions(
504 pub_params: &Self::PublicParams,
505 pub_inputs: &Self::PublicInputs,
506 partition_proofs: &[Self::Proof],
507 ) -> Result<bool> {
508 let num_sectors_per_chunk = pub_params.sector_count;
509 let num_sectors = pub_inputs.sectors.len();
510
511 ensure!(
512 num_sectors <= num_sectors_per_chunk * partition_proofs.len(),
513 "inconsistent number of sectors: {} > {} * {}",
514 num_sectors,
515 num_sectors_per_chunk,
516 partition_proofs.len(),
517 );
518
519 for (j, (proof, pub_sectors_chunk)) in partition_proofs
520 .iter()
521 .zip(pub_inputs.sectors.chunks(num_sectors_per_chunk))
522 .enumerate()
523 {
524 let is_valid = Self::verify(
525 pub_params,
526 &PublicInputs {
527 randomness: pub_inputs.randomness,
528 prover_id: pub_inputs.prover_id,
529 sectors: pub_sectors_chunk.to_vec(),
530 k: Some(j),
531 },
532 proof,
533 )?;
534
535 if !is_valid {
536 return Ok(false);
537 }
538 }
539 Ok(true)
540 }
541
542 fn with_partition(mut pub_in: Self::PublicInputs, k: Option<usize>) -> Self::PublicInputs {
543 pub_in.k = k;
544 pub_in
545 }
546
547 fn satisfies_requirements(
548 public_params: &Self::PublicParams,
549 requirements: &Self::Requirements,
550 partitions: usize,
551 ) -> bool {
552 let checked = partitions * public_params.sector_count;
553
554 assert_eq!(
555 partitions.checked_mul(public_params.sector_count),
556 Some(checked)
557 );
558 assert_eq!(
559 checked.checked_mul(public_params.challenge_count),
560 Some(checked * public_params.challenge_count)
561 );
562
563 checked * public_params.challenge_count >= requirements.minimum_challenge_count
564 }
565
566 fn verify(
567 pub_params: &Self::PublicParams,
568 pub_inputs: &Self::PublicInputs,
569 partition_proof: &Self::Proof,
570 ) -> Result<bool> {
571 ensure!(
572 pub_inputs.k.is_some(),
573 "must be called with a partition index"
574 );
575 let partition_index = pub_inputs.k.expect("prechecked");
576 let challenge_count = pub_params.challenge_count;
577 let num_sectors_per_chunk = pub_params.sector_count;
578
579 let j = partition_index;
580 let proof = partition_proof;
581 let pub_sectors_chunk = &pub_inputs.sectors;
582
583 ensure!(
584 pub_sectors_chunk.len() <= num_sectors_per_chunk,
585 "inconsistent number of public sectors: {} > {}",
586 pub_sectors_chunk.len(),
587 num_sectors_per_chunk,
588 );
589 ensure!(
590 proof.sectors.len() == num_sectors_per_chunk,
591 "invalid number of sectors in the partition proof {}: {} != {}",
592 j,
593 proof.sectors.len(),
594 num_sectors_per_chunk,
595 );
596
597 let is_valid = pub_sectors_chunk
598 .par_iter()
599 .zip(proof.sectors.par_iter())
600 .enumerate()
601 .map(|(i, (pub_sector, sector_proof))| {
602 let sector_id = pub_sector.id;
603 let comm_r = &pub_sector.comm_r;
604 let comm_c = sector_proof.comm_c;
605 let inclusion_proofs = §or_proof.inclusion_proofs;
606
607 trace!("Verifying inclusion proofs for sector {}", sector_id);
608
609 let comm_r_last = inclusion_proofs[0].root();
613
614 if AsRef::<[u8]>::as_ref(&<Tree::Hasher as Hasher>::Function::hash2(
615 &comm_c,
616 &comm_r_last,
617 )) != AsRef::<[u8]>::as_ref(comm_r)
618 {
619 error!("hash(comm_c || comm_r_last) != comm_r: {:?}", sector_id);
620 return Ok(false);
621 }
622
623 ensure!(
624 challenge_count == inclusion_proofs.len(),
625 "unexpected number of inclusion proofs: {} != {}",
626 challenge_count,
627 inclusion_proofs.len()
628 );
629
630 let mut challenge_hasher = Sha256::new();
632 challenge_hasher.update(AsRef::<[u8]>::as_ref(&pub_inputs.randomness));
633 challenge_hasher.update(&u64::from(sector_id).to_le_bytes()[..]);
634
635 let is_valid_list = inclusion_proofs
636 .par_iter()
637 .enumerate()
638 .map(|(n, inclusion_proof)| -> Result<bool> {
639 let sector_index = j * num_sectors_per_chunk + i;
640 let challenge_index = get_challenge_index(
641 pub_params.api_version,
642 sector_index,
643 pub_params.challenge_count,
644 n,
645 );
646 let challenged_leaf =
647 generate_leaf_challenge_inner::<<Tree::Hasher as Hasher>::Domain>(
648 challenge_hasher.clone(),
649 pub_params,
650 challenge_index,
651 );
652
653 if inclusion_proof.root() != comm_r_last {
655 error!("inclusion proof root != comm_r_last: {:?}", sector_id);
656 return Ok(false);
657 }
658
659 let expected_path_length = inclusion_proof
661 .expected_len(pub_params.sector_size as usize / NODE_SIZE);
662
663 if expected_path_length != inclusion_proof.path().len() {
664 error!("wrong path length: {:?}", sector_id);
665 return Ok(false);
666 }
667
668 if !inclusion_proof.validate(challenged_leaf as usize) {
669 error!("invalid inclusion proof: {:?}", sector_id);
670 return Ok(false);
671 }
672 Ok(true)
673 })
674 .collect::<Result<Vec<bool>>>()?;
675
676 Ok(is_valid_list.into_iter().all(|v| v))
677 })
678 .reduce(
679 || Ok(true),
680 |all_valid, is_valid| Ok(all_valid? && is_valid?),
681 )?;
682 if !is_valid {
683 return Ok(false);
684 }
685 Ok(true)
686 }
687}