kontor_crypto/api/system.rs
1//! PorSystem: Unified API entry point for the Nova-based Proof-of-Retrievability system.
2//!
3//! This module provides a single, consolidated interface that encapsulates all
4//! proving and verification operations, managing dependencies like the FileLedger
5//! and parameter caching internally.
6
7use super::types::{Challenge, FileMetadata, PreparedFile, Proof};
8use crate::{ledger::FileLedger, KontorPoRError, Result};
9use std::collections::BTreeMap;
10use tracing::debug;
11
12/// The unified API entry point for the Nova-based Proof-of-Retrievability system.
13///
14/// PorSystem encapsulates the FileLedger and provides methods for file preparation,
15/// proof generation, and verification. It manages parameter caching and shape
16/// derivation internally.
17pub struct PorSystem<'a> {
18 /// Reference to the file ledger containing the aggregated Merkle tree
19 ledger: &'a FileLedger,
20}
21
22impl<'a> PorSystem<'a> {
23 /// Create a new PorSystem with the given FileLedger.
24 pub fn new(ledger: &'a FileLedger) -> Self {
25 Self { ledger }
26 }
27
28 /// Prepare a file for proving by applying erasure coding, chunking, and building a Merkle tree.
29 ///
30 /// This method uses the fixed chunk size from config::CHUNK_SIZE_BYTES and stores
31 /// the filename in the FileMetadata for operator UX.
32 ///
33 /// # Arguments
34 ///
35 /// * `data` - The raw file data to be processed
36 /// * `erasure_config` - Reed-Solomon erasure coding configuration
37 /// * `filename` - Filename for identification and UX
38 ///
39 /// # Returns
40 ///
41 /// Returns a tuple of (PreparedFile, FileMetadata) where:
42 /// - PreparedFile contains the private Merkle tree and file identifiers
43 /// - FileMetadata contains the public commitment and reconstruction information
44 pub fn prepare_file(
45 &self,
46 data: &[u8],
47 filename: &str,
48 nonce: &[u8],
49 ) -> Result<(PreparedFile, FileMetadata)> {
50 // Use the existing prepare_file function from mod.rs
51 super::prepare_file(data, filename, nonce)
52 }
53
54 /// Generate a single compact proof for any set of open Challenges.
55 ///
56 /// This method accepts a `Vec<PreparedFile>` and internally maps it to the
57 /// BTreeMap structure required by the underlying proving logic. Seeds
58 /// must be identical across all challenges in the batch.
59 ///
60 /// # Arguments
61 ///
62 /// * `files` - Vector of prepared files to prove
63 /// * `challenges` - Slice of challenges to answer
64 ///
65 /// # Returns
66 ///
67 /// Returns a Proof containing the compressed SNARK and the challenge IDs
68 pub fn prove(&self, files: Vec<&PreparedFile>, challenges: &[Challenge]) -> Result<Proof> {
69 // Convert Vec<&PreparedFile> to BTreeMap<String, &PreparedFile>
70 let mut files_map = BTreeMap::new();
71 for file in files {
72 if files_map.insert(file.file_id.clone(), file).is_some() {
73 return Err(KontorPoRError::InvalidInput(format!(
74 "Duplicate file_id provided: {}",
75 file.file_id
76 )));
77 }
78 }
79
80 // Validate that all files referenced by challenges are present
81 for challenge in challenges {
82 if !files_map.contains_key(&challenge.file_metadata.file_id) {
83 return Err(KontorPoRError::FileNotFound {
84 file_id: challenge.file_metadata.file_id.clone(),
85 });
86 }
87 }
88
89 debug!(
90 "PorSystem::prove - {} files, {} challenges",
91 files_map.len(),
92 challenges.len()
93 );
94
95 // Use the existing prove function from prove.rs
96 super::prove::prove(challenges, &files_map, self.ledger, None)
97 }
98
99 /// Verify a proof against the Challenges it claims to answer.
100 ///
101 /// This method validates that the proof's challenge_ids exactly match
102 /// the provided challenges and then performs SNARK verification.
103 ///
104 /// # Arguments
105 ///
106 /// * `proof` - The proof to verify
107 /// * `challenges` - The challenges that the proof claims to answer
108 ///
109 /// # Returns
110 ///
111 /// Returns Ok(true) if the proof is valid, Ok(false) if invalid,
112 /// or an error if verification fails unexpectedly.
113 pub fn verify(&self, proof: &Proof, challenges: &[Challenge]) -> Result<bool> {
114 // Validate that proof.challenge_ids matches the provided challenges
115 let expected_ids: Vec<_> = challenges.iter().map(|c| c.id()).collect();
116
117 if proof.challenge_ids.len() != expected_ids.len() {
118 return Err(KontorPoRError::InvalidInput(format!(
119 "Challenge count mismatch: proof covers {} challenges, provided {}",
120 proof.challenge_ids.len(),
121 expected_ids.len()
122 )));
123 }
124
125 // Check that all challenge IDs match (order matters for Nova)
126 for (i, (proof_id, expected_id)) in proof
127 .challenge_ids
128 .iter()
129 .zip(expected_ids.iter())
130 .enumerate()
131 {
132 if proof_id != expected_id {
133 return Err(KontorPoRError::InvalidInput(format!(
134 "Challenge ID mismatch at position {}: proof has {:?}, expected {:?}",
135 i, proof_id.0, expected_id.0
136 )));
137 }
138 }
139
140 debug!(
141 "PorSystem::verify - validated {} challenge IDs",
142 challenges.len()
143 );
144
145 // Use the existing verify function from verify.rs
146 super::verify::verify(challenges, proof, self.ledger)
147 }
148}