Skip to main content

filecoin_proofs/api/
mod.rs

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/// Unseals the sector at `sealed_path` and returns the bytes for a piece
74/// whose first (unpadded) byte begins at `offset` and ends at `offset` plus
75/// `num_bytes`, inclusive. Note that the entire sector is unsealed each time
76/// this function is called.
77///
78/// # Arguments
79///
80/// * `porep_config` - porep configuration containing the sector size.
81/// * `cache_path` - path to the directory in which the sector data's Merkle Tree is written.
82/// * `sealed_path` - path to the sealed sector file that we will unseal and read a byte range.
83/// * `output_path` - path to a file that we will write the requested byte range to.
84/// * `prover_id` - the prover-id that sealed the sector.
85/// * `sector_id` - the sector-id of the sealed sector.
86/// * `comm_d` - the commitment to the sector's data.
87/// * `ticket` - the ticket that was used to generate the sector's replica-id.
88/// * `offset` - the byte index in the unsealed sector of the first byte that we want to read.
89/// * `num_bytes` - the number of bytes that we want to read.
90#[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/// Unseals the sector read from `sealed_sector` and returns the bytes for a
128/// piece whose first (unpadded) byte begins at `offset` and ends at `offset`
129/// plus `num_bytes`, inclusive. Note that the entire sector is unsealed each
130/// time this function is called.
131///
132/// # Arguments
133///
134/// * `porep_config` - porep configuration containing the sector size.
135/// * `cache_path` - path to the directory in which the sector data's Merkle Tree is written.
136/// * `sealed_sector` - a byte source from which we read sealed sector data.
137/// * `unsealed_output` - a byte sink to which we write unsealed, un-bit-padded sector bytes.
138/// * `prover_id` - the prover-id that sealed the sector.
139/// * `sector_id` - the sector-id of the sealed sector.
140/// * `comm_d` - the commitment to the sector's data.
141/// * `ticket` - the ticket that was used to generate the sector's replica-id.
142/// * `offset` - the byte index in the unsealed sector of the first byte that we want to read.
143/// * `num_bytes` - the number of bytes that we want to read.
144#[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/// Unseals the sector read from `sealed_sector` and returns the bytes for a
196/// piece whose first (unpadded) byte begins at `offset` and ends at `offset`
197/// plus `num_bytes`, inclusive. Note that the entire sector is unsealed each
198/// time this function is called.
199///
200/// # Arguments
201///
202/// * `porep_config` - porep configuration containing the sector size.
203/// * `cache_path` - path to the directory in which the sector data's Merkle Tree is written.
204/// * `sealed_sector` - a byte source from which we read sealed sector data.
205/// * `unsealed_output` - a byte sink to which we write unsealed, un-bit-padded sector bytes.
206/// * `prover_id` - the prover-id that sealed the sector.
207/// * `sector_id` - the sector-id of the sealed sector.
208/// * `comm_d` - the commitment to the sector's data.
209/// * `ticket` - the ticket that was used to generate the sector's replica-id.
210/// * `offset` - the byte index in the unsealed sector of the first byte that we want to read.
211/// * `num_bytes` - the number of bytes that we want to read.
212#[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/// Unseals the sector read from `sealed_sector` and returns the bytes for a
265/// piece whose first (unpadded) byte begins at `offset` and ends at `offset`
266/// plus `num_bytes`, inclusive. Note that the entire sector is unsealed each
267/// time this function is called.
268///
269/// # Arguments
270///
271/// * `porep_config` - porep configuration containing the sector size.
272/// * `cache_path` - path to the directory in which the sector data's Merkle Tree is written.
273/// * `sealed_sector` - a byte source from which we read sealed sector data.
274/// * `unsealed_output` - a byte sink to which we write unsealed, un-bit-padded sector bytes.
275/// * `prover_id` - the prover-id that sealed the sector.
276/// * `sector_id` - the sector-id of the sealed sector.
277/// * `comm_d` - the commitment to the sector's data.
278/// * `ticket` - the ticket that was used to generate the sector's replica-id.
279/// * `offset` - the byte index in the unsealed sector of the first byte that we want to read.
280/// * `num_bytes` - the number of bytes that we want to read.
281#[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    // If the call to `extract_range` was successful, the `unsealed` vector must
316    // have a length which equals `num_bytes_padded`. The byte at its 0-index
317    // byte will be the byte at index `offset_padded` in the sealed sector.
318    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
327/// Generates a piece commitment for the provided byte source. Returns an error
328/// if the byte source produced more than `piece_size` bytes.
329///
330/// # Arguments
331///
332/// * `source` - a readable source of unprocessed piece bytes. The piece's commitment will be
333///   generated for the bytes read from the source plus any added padding.
334/// * `piece_size` - the number of unpadded user-bytes which can be read from source before EOF.
335pub 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        // send the source through the preprocessor
345        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
360/// Computes a NUL-byte prefix and/or suffix for `source` using the provided
361/// `piece_lengths` and `piece_size` (such that the `source`, after
362/// preprocessing, will occupy a subtree of a merkle tree built using the bytes
363/// from `target`), runs the resultant byte stream through the preprocessor,
364/// and writes the result to `target`. Returns a tuple containing the number of
365/// bytes written to `target` (`source` plus alignment) and the commitment.
366///
367/// WARNING: Depending on the ordering and size of the pieces in
368/// `piece_lengths`, this function could write a prefix of NUL bytes which
369/// wastes ($SIZESECTORSIZE/2)-$MINIMUM_PIECE_SIZE space. This function will be
370/// deprecated in favor of `write_and_preprocess`, and miners will be prevented
371/// from sealing sectors containing more than $TOOMUCH alignment bytes.
372///
373/// # Arguments
374///
375/// * `source` - a readable source of unprocessed piece bytes.
376/// * `target` - a writer where we will write the processed piece bytes.
377/// * `piece_size` - the number of unpadded user-bytes which can be read from source before EOF.
378/// * `piece_lengths` - the number of bytes for each previous piece in the sector.
379pub 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        // write left alignment
402        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        // write right alignment
417        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
451/// Writes bytes from `source` to `target`, adding bit-padding ("preprocessing")
452/// as needed. Returns a tuple containing the number of bytes written to
453/// `target` and the commitment.
454///
455/// WARNING: This function neither prepends nor appends alignment bytes to the
456/// `target`; it is the caller's responsibility to ensure properly sized
457/// and ordered writes to `target` such that `source`-bytes occupy whole
458/// subtrees of the final merkle tree built over `target`.
459///
460/// # Arguments
461///
462/// * `source` - a readable source of unprocessed piece bytes.
463/// * `target` - a writer where we will write the processed piece bytes.
464/// * `piece_size` - the number of unpadded user-bytes which can be read from source before EOF.
465pub 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
477// Verifies if a DiskStore specified by a config (or set of 'required_configs' is consistent).
478fn 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        // Configs may have split due to sector size, so we need to
482        // check deterministic paths from here.
483        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
550// Verifies if a LevelCacheStore specified by a config is consistent.
551fn 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        // Configs may have split due to sector size, so we need to
557        // check deterministic paths from here.
558        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
629// Checks for the existence of the tree d store, the replica, and all generated labels.
630pub 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    // Verify all stores/labels within the Labels object, but
648    // respecting the current cache_path.
649    let cache = cache_path.as_ref().to_path_buf();
650    seal_precommit_phase1_output
651        .labels
652        .verify_stores(verify_store, &cache)?;
653
654    // Update the previous phase store path to the current cache_path.
655    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
672// Checks for the existence of the replica data and t_aux, which in
673// turn allows us to verify the tree d, tree r, tree c, and the
674// labels.
675pub 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    // Verify that the replica exists and is not empty.
686    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    // Make sure p_aux exists and is valid.
702    let _ = util::get_p_aux::<Tree>(cache)?;
703
704    let t_aux = util::get_t_aux::<Tree>(cache, metadata.len())?;
705
706    // Verify all stores/labels within the Labels object.
707    let cache = cache_path.as_ref().to_path_buf();
708    t_aux.labels.verify_stores(verify_store, &cache)?;
709
710    // Verify each tree disk store.
711    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}