1use std::fs::{File, OpenOptions};
2use std::io::{self, BufReader, BufWriter, Read, Write};
3use std::path::{Path, PathBuf};
4
5use anyhow::{ensure, Context, Result};
6use filecoin_hashers::Hasher;
7use fr32::{write_unpadded, Fr32Reader};
8use log::{info, trace};
9use memmap2::MmapOptions;
10use merkletree::store::{DiskStore, LevelCacheStore, StoreConfig};
11use storage_proofs_core::{
12 cache_key::CacheKey,
13 measurements::{measure_op, Operation},
14 merkle::get_base_tree_count,
15 pieces::generate_piece_commitment_bytes_from_source,
16 sector::SectorId,
17};
18use storage_proofs_porep::stacked::{self, generate_replica_id, PublicParams, StackedDrg};
19use typenum::Unsigned;
20
21use crate::{
22 commitment_reader::CommitmentReader,
23 constants::{
24 DefaultBinaryTree, DefaultOctTree, DefaultPieceDomain, DefaultPieceHasher,
25 MINIMUM_RESERVED_BYTES_FOR_PIECE_IN_FULLY_ALIGNED_SECTOR as MINIMUM_PIECE_SIZE,
26 },
27 parameters::public_params,
28 pieces::{get_piece_alignment, sum_piece_bytes_with_alignment},
29 types::{
30 Commitment, MerkleTreeTrait, PaddedBytesAmount, PieceInfo, PoRepConfig, ProverId,
31 SealPreCommitPhase1Output, Ticket, UnpaddedByteIndex, UnpaddedBytesAmount,
32 },
33};
34
35mod fake_seal;
36mod post_util;
37mod seal;
38mod update;
39mod util;
40mod window_post;
41mod winning_post;
42
43pub use fake_seal::*;
44pub use post_util::*;
45pub use seal::*;
46pub use update::*;
47pub use util::*;
48pub use window_post::*;
49pub use winning_post::*;
50
51pub use storage_proofs_update::constants::{partition_count, TreeRHasher};
52
53pub fn clear_cache(cache_dir: &Path) -> Result<()> {
54 info!("clear_cache:start");
55
56 let result = stacked::clear_cache_dir(cache_dir);
57
58 info!("clear_cache:finish");
59
60 result
61}
62
63pub fn clear_synthetic_proofs(cache_dir: &Path) -> Result<()> {
64 info!("clear_synthetic_proofs:start");
65
66 let result = stacked::clear_synthetic_proofs(cache_dir);
67
68 info!("clear_synthetic_proofs:finish");
69
70 result
71}
72
73#[allow(clippy::too_many_arguments)]
91pub fn get_unsealed_range<T: Into<PathBuf> + AsRef<Path>, Tree: 'static + MerkleTreeTrait>(
92 porep_config: &PoRepConfig,
93 cache_path: T,
94 sealed_path: T,
95 output_path: T,
96 prover_id: ProverId,
97 sector_id: SectorId,
98 comm_d: Commitment,
99 ticket: Ticket,
100 offset: UnpaddedByteIndex,
101 num_bytes: UnpaddedBytesAmount,
102) -> Result<UnpaddedBytesAmount> {
103 info!("get_unsealed_range:start");
104
105 let f_out = File::create(&output_path)
106 .with_context(|| format!("could not create output_path={:?}", output_path.as_ref()))?;
107
108 let buf_f_out = BufWriter::new(f_out);
109
110 let result = unseal_range_mapped::<_, _, Tree>(
111 porep_config,
112 cache_path,
113 sealed_path.into(),
114 buf_f_out,
115 prover_id,
116 sector_id,
117 comm_d,
118 ticket,
119 offset,
120 num_bytes,
121 );
122
123 info!("get_unsealed_range:finish");
124 result
125}
126
127#[allow(clippy::too_many_arguments)]
145pub fn unseal_range<P, R, W, Tree>(
146 porep_config: &PoRepConfig,
147 cache_path: P,
148 mut sealed_sector: R,
149 unsealed_output: W,
150 prover_id: ProverId,
151 sector_id: SectorId,
152 comm_d: Commitment,
153 ticket: Ticket,
154 offset: UnpaddedByteIndex,
155 num_bytes: UnpaddedBytesAmount,
156) -> Result<UnpaddedBytesAmount>
157where
158 P: Into<PathBuf> + AsRef<Path>,
159 R: Read,
160 W: Write,
161 Tree: 'static + MerkleTreeTrait,
162{
163 info!("unseal_range:start");
164 ensure!(comm_d != [0; 32], "Invalid all zero commitment (comm_d)");
165
166 let comm_d =
167 as_safe_commitment::<<DefaultPieceHasher as Hasher>::Domain, _>(&comm_d, "comm_d")?;
168
169 let replica_id = generate_replica_id::<Tree::Hasher, _>(
170 &prover_id,
171 sector_id.into(),
172 &ticket,
173 comm_d,
174 &porep_config.porep_id,
175 );
176
177 let mut data = Vec::new();
178 sealed_sector.read_to_end(&mut data)?;
179
180 let res = unseal_range_inner::<_, _, Tree>(
181 porep_config,
182 cache_path,
183 &mut data,
184 unsealed_output,
185 replica_id,
186 offset,
187 num_bytes,
188 )?;
189
190 info!("unseal_range:finish");
191
192 Ok(res)
193}
194
195#[allow(clippy::too_many_arguments)]
213pub fn unseal_range_mapped<P, W, Tree>(
214 porep_config: &PoRepConfig,
215 cache_path: P,
216 sealed_path: PathBuf,
217 unsealed_output: W,
218 prover_id: ProverId,
219 sector_id: SectorId,
220 comm_d: Commitment,
221 ticket: Ticket,
222 offset: UnpaddedByteIndex,
223 num_bytes: UnpaddedBytesAmount,
224) -> Result<UnpaddedBytesAmount>
225where
226 P: Into<PathBuf> + AsRef<Path>,
227 W: Write,
228 Tree: 'static + MerkleTreeTrait,
229{
230 info!("unseal_range_mapped:start");
231 ensure!(comm_d != [0; 32], "Invalid all zero commitment (comm_d)");
232
233 let comm_d =
234 as_safe_commitment::<<DefaultPieceHasher as Hasher>::Domain, _>(&comm_d, "comm_d")?;
235
236 let replica_id = generate_replica_id::<Tree::Hasher, _>(
237 &prover_id,
238 sector_id.into(),
239 &ticket,
240 comm_d,
241 &porep_config.porep_id,
242 );
243
244 let mapped_file = OpenOptions::new()
245 .read(true)
246 .write(true)
247 .open(sealed_path)?;
248 let mut data = unsafe { MmapOptions::new().map_copy(&mapped_file)? };
249
250 let result = unseal_range_inner::<_, _, Tree>(
251 porep_config,
252 cache_path,
253 &mut data,
254 unsealed_output,
255 replica_id,
256 offset,
257 num_bytes,
258 );
259 info!("unseal_range_mapped:finish");
260
261 result
262}
263
264#[allow(clippy::too_many_arguments)]
282fn unseal_range_inner<P, W, Tree>(
283 porep_config: &PoRepConfig,
284 cache_path: P,
285 data: &mut [u8],
286 mut unsealed_output: W,
287 replica_id: <Tree::Hasher as Hasher>::Domain,
288 offset: UnpaddedByteIndex,
289 num_bytes: UnpaddedBytesAmount,
290) -> Result<UnpaddedBytesAmount>
291where
292 P: Into<PathBuf> + AsRef<Path>,
293 W: Write,
294 Tree: 'static + MerkleTreeTrait,
295{
296 trace!("unseal_range_inner:start");
297
298 let config = StoreConfig::new(cache_path.as_ref(), CacheKey::CommDTree.to_string(), 0);
299 let pp: PublicParams<Tree> = public_params(porep_config)?;
300
301 let offset_padded: PaddedBytesAmount = UnpaddedBytesAmount::from(offset).into();
302 let num_bytes_padded: PaddedBytesAmount = num_bytes.into();
303
304 StackedDrg::<Tree, DefaultPieceHasher>::extract_and_invert_transform_layers(
305 &pp.graph,
306 pp.num_layers,
307 &replica_id,
308 data,
309 config,
310 )?;
311 let start: usize = offset_padded.into();
312 let end = start + usize::from(num_bytes_padded);
313 let unsealed = &data[start..end];
314
315 let written = write_unpadded(unsealed, &mut unsealed_output, 0, num_bytes.into())
319 .context("write_unpadded failed")?;
320
321 let amount = UnpaddedBytesAmount(written as u64);
322
323 trace!("unseal_range_inner:finish");
324 Ok(amount)
325}
326
327pub fn generate_piece_commitment<T: Read>(
336 source: T,
337 piece_size: UnpaddedBytesAmount,
338) -> Result<PieceInfo> {
339 trace!("generate_piece_commitment:start");
340
341 let result = measure_op(Operation::GeneratePieceCommitment, || {
342 ensure_piece_size(piece_size)?;
343
344 let source = BufReader::new(source);
346 let mut fr32_reader = Fr32Reader::new(source);
347
348 let commitment = generate_piece_commitment_bytes_from_source::<DefaultPieceHasher>(
349 &mut fr32_reader,
350 PaddedBytesAmount::from(piece_size).into(),
351 )?;
352
353 PieceInfo::new(commitment, piece_size)
354 });
355
356 trace!("generate_piece_commitment:finish");
357 result
358}
359
360pub fn add_piece<R, W>(
380 source: R,
381 target: W,
382 piece_size: UnpaddedBytesAmount,
383 piece_lengths: &[UnpaddedBytesAmount],
384) -> Result<(PieceInfo, UnpaddedBytesAmount)>
385where
386 R: Read,
387 W: Write,
388{
389 trace!("add_piece:start");
390
391 let result = measure_op(Operation::AddPiece, || {
392 ensure_piece_size(piece_size)?;
393
394 let source = BufReader::new(source);
395 let mut target = BufWriter::new(target);
396
397 let written_bytes = sum_piece_bytes_with_alignment(piece_lengths);
398 let piece_alignment = get_piece_alignment(written_bytes, piece_size);
399 let fr32_reader = Fr32Reader::new(source);
400
401 for _ in 0..usize::from(PaddedBytesAmount::from(piece_alignment.left_bytes)) {
403 target.write_all(&[0u8][..])?;
404 }
405
406 let mut commitment_reader = CommitmentReader::new(fr32_reader);
407 let n = io::copy(&mut commitment_reader, &mut target)
408 .context("failed to write and preprocess bytes")?;
409
410 ensure!(n != 0, "add_piece: read 0 bytes before EOF from source");
411 let n = PaddedBytesAmount(n);
412 let n: UnpaddedBytesAmount = n.into();
413
414 ensure!(n == piece_size, "add_piece: invalid bytes amount written");
415
416 for _ in 0..usize::from(PaddedBytesAmount::from(piece_alignment.right_bytes)) {
418 target.write_all(&[0u8][..])?;
419 }
420
421 let commitment = commitment_reader.finish()?;
422 let mut comm = [0u8; 32];
423 comm.copy_from_slice(commitment.as_ref());
424
425 let written = piece_alignment.left_bytes + piece_alignment.right_bytes + piece_size;
426
427 Ok((PieceInfo::new(comm, n)?, written))
428 });
429
430 trace!("add_piece:finish");
431 result
432}
433
434fn ensure_piece_size(piece_size: UnpaddedBytesAmount) -> Result<()> {
435 ensure!(
436 piece_size >= UnpaddedBytesAmount(MINIMUM_PIECE_SIZE),
437 "Piece must be at least {} bytes",
438 MINIMUM_PIECE_SIZE
439 );
440
441 let padded_piece_size: PaddedBytesAmount = piece_size.into();
442 ensure!(
443 u64::from(padded_piece_size).is_power_of_two(),
444 "Bit-padded piece size must be a power of 2 ({:?})",
445 padded_piece_size,
446 );
447
448 Ok(())
449}
450
451pub fn write_and_preprocess<R, W>(
466 source: R,
467 target: W,
468 piece_size: UnpaddedBytesAmount,
469) -> Result<(PieceInfo, UnpaddedBytesAmount)>
470where
471 R: Read,
472 W: Write,
473{
474 add_piece(source, target, piece_size, Default::default())
475}
476
477fn verify_store(config: &StoreConfig, arity: usize, required_configs: usize) -> Result<()> {
479 let store_path = StoreConfig::data_path(&config.path, &config.id);
480 if !Path::new(&store_path).exists() {
481 let orig_path = store_path
484 .clone()
485 .into_os_string()
486 .into_string()
487 .expect("failed to convert store_path to string");
488 let mut configs: Vec<StoreConfig> = Vec::with_capacity(required_configs);
489 for i in 0..required_configs {
490 let cur_path = orig_path
491 .clone()
492 .replace(".dat", format!("-{}.dat", i).as_str());
493
494 if Path::new(&cur_path).exists() {
495 let path_str = cur_path.as_str();
496 let tree_names = vec!["tree-d", "tree-c", "tree-r-last"];
497 for name in tree_names {
498 if path_str.contains(name) {
499 configs.push(StoreConfig::from_config(
500 config,
501 format!("{}-{}", name, i),
502 None,
503 ));
504 break;
505 }
506 }
507 }
508 }
509
510 ensure!(
511 configs.len() == required_configs,
512 "Missing store file (or associated split paths): {}",
513 store_path.display()
514 );
515
516 let store_len = config.size.expect("disk store size not configured");
517 for config in &configs {
518 let data_path = StoreConfig::data_path(&config.path, &config.id);
519 trace!(
520 "verify_store: {:?} has length {} bytes",
521 &data_path,
522 std::fs::metadata(&data_path)?.len()
523 );
524 ensure!(
525 DiskStore::<DefaultPieceDomain>::is_consistent(store_len, arity, config,)?,
526 "Store is inconsistent: {:?}",
527 &data_path
528 );
529 }
530 } else {
531 trace!(
532 "verify_store: {:?} has length {}",
533 &store_path,
534 std::fs::metadata(&store_path)?.len()
535 );
536 ensure!(
537 DiskStore::<DefaultPieceDomain>::is_consistent(
538 config.size.expect("disk store size not configured"),
539 arity,
540 config,
541 )?,
542 "Store is inconsistent: {:?}",
543 store_path
544 );
545 }
546
547 Ok(())
548}
549
550fn verify_level_cache_store<Tree: MerkleTreeTrait>(config: &StoreConfig) -> Result<()> {
552 let store_path = StoreConfig::data_path(&config.path, &config.id);
553 if !Path::new(&store_path).exists() {
554 let required_configs = get_base_tree_count::<Tree>();
555
556 let orig_path = store_path
559 .clone()
560 .into_os_string()
561 .into_string()
562 .expect("failed to convert store_path to string");
563 let mut configs: Vec<StoreConfig> = Vec::with_capacity(required_configs);
564 for i in 0..required_configs {
565 let cur_path = orig_path
566 .clone()
567 .replace(".dat", format!("-{}.dat", i).as_str());
568
569 if Path::new(&cur_path).exists() {
570 let path_str = cur_path.as_str();
571 let tree_names = vec!["tree-d", "tree-c", "tree-r-last"];
572 for name in tree_names {
573 if path_str.contains(name) {
574 configs.push(StoreConfig::from_config(
575 config,
576 format!("{}-{}", name, i),
577 None,
578 ));
579 break;
580 }
581 }
582 }
583 }
584
585 ensure!(
586 configs.len() == required_configs,
587 "Missing store file (or associated split paths): {}",
588 store_path.display()
589 );
590
591 let store_len = config.size.expect("disk store size not configured");
592 for config in &configs {
593 let data_path = StoreConfig::data_path(&config.path, &config.id);
594 trace!(
595 "verify_store: {:?} has length {}",
596 &data_path,
597 std::fs::metadata(&data_path)?.len()
598 );
599 ensure!(
600 LevelCacheStore::<DefaultPieceDomain, File>::is_consistent(
601 store_len,
602 Tree::Arity::to_usize(),
603 config,
604 )?,
605 "Store is inconsistent: {:?}",
606 &data_path
607 );
608 }
609 } else {
610 trace!(
611 "verify_store: {:?} has length {}",
612 &store_path,
613 std::fs::metadata(&store_path)?.len()
614 );
615 ensure!(
616 LevelCacheStore::<DefaultPieceDomain, File>::is_consistent(
617 config.size.expect("disk store size not configured"),
618 Tree::Arity::to_usize(),
619 config,
620 )?,
621 "Store is inconsistent: {:?}",
622 store_path
623 );
624 }
625
626 Ok(())
627}
628
629pub fn validate_cache_for_precommit_phase2<R, T, Tree: MerkleTreeTrait>(
631 cache_path: R,
632 replica_path: T,
633 seal_precommit_phase1_output: &SealPreCommitPhase1Output<Tree>,
634) -> Result<()>
635where
636 R: AsRef<Path>,
637 T: AsRef<Path>,
638{
639 info!("validate_cache_for_precommit_phase2:start");
640
641 ensure!(
642 replica_path.as_ref().exists(),
643 "Missing replica: {}",
644 replica_path.as_ref().to_path_buf().display()
645 );
646
647 let cache = cache_path.as_ref().to_path_buf();
650 seal_precommit_phase1_output
651 .labels
652 .verify_stores(verify_store, &cache)?;
653
654 let mut config = StoreConfig::from_config(
656 &seal_precommit_phase1_output.config,
657 &seal_precommit_phase1_output.config.id,
658 seal_precommit_phase1_output.config.size,
659 );
660 config.path = cache_path.as_ref().into();
661
662 let result = verify_store(
663 &config,
664 <DefaultBinaryTree as MerkleTreeTrait>::Arity::to_usize(),
665 get_base_tree_count::<Tree>(),
666 );
667
668 info!("validate_cache_for_precommit_phase2:finish");
669 result
670}
671
672pub fn validate_cache_for_commit<R, T, Tree: MerkleTreeTrait>(
676 cache_path: R,
677 replica_path: T,
678) -> Result<()>
679where
680 R: AsRef<Path>,
681 T: AsRef<Path>,
682{
683 info!("validate_cache_for_commit:start");
684
685 ensure!(
687 replica_path.as_ref().exists(),
688 "Missing replica: {}",
689 replica_path.as_ref().to_path_buf().display()
690 );
691
692 let metadata = File::open(&replica_path)?.metadata()?;
693 ensure!(
694 metadata.len() > 0,
695 "Replica {} exists, but is empty!",
696 replica_path.as_ref().to_path_buf().display()
697 );
698
699 let cache = &cache_path.as_ref();
700
701 let _ = util::get_p_aux::<Tree>(cache)?;
703
704 let t_aux = util::get_t_aux::<Tree>(cache, metadata.len())?;
705
706 let cache = cache_path.as_ref().to_path_buf();
708 t_aux.labels.verify_stores(verify_store, &cache)?;
709
710 verify_store(
712 &t_aux.tree_d_config,
713 <DefaultBinaryTree as MerkleTreeTrait>::Arity::to_usize(),
714 get_base_tree_count::<Tree>(),
715 )?;
716 verify_store(
717 &t_aux.tree_c_config,
718 <DefaultOctTree as MerkleTreeTrait>::Arity::to_usize(),
719 get_base_tree_count::<Tree>(),
720 )?;
721 verify_level_cache_store::<DefaultOctTree>(&t_aux.tree_r_last_config)?;
722
723 info!("validate_cache_for_commit:finish");
724
725 Ok(())
726}