Skip to main content

storage_proofs_post/fallback/
circuit.rs

1use bellperson::{gadgets::num::AllocatedNum, Circuit, ConstraintSystem, SynthesisError};
2use blstrs::Scalar as Fr;
3use ff::Field;
4use filecoin_hashers::{HashFunction, Hasher};
5use rayon::prelude::{ParallelIterator, ParallelSlice};
6use storage_proofs_core::{
7    compound_proof::CircuitComponent,
8    error::Result,
9    gadgets::{
10        constraint,
11        por::{AuthPath, PoRCircuit},
12        variables::Root,
13    },
14    merkle::MerkleTreeTrait,
15    por,
16    settings::SETTINGS,
17    util::NODE_SIZE,
18};
19
20use crate::fallback::{PublicParams, PublicSector, SectorProof};
21
22/// This is the `FallbackPoSt` circuit.
23pub struct FallbackPoStCircuit<Tree: MerkleTreeTrait> {
24    pub prover_id: Option<Fr>,
25    pub sectors: Vec<Sector<Tree>>,
26}
27
28// We must manually implement Clone for all types generic over MerkleTreeTrait (instead of using
29// #[derive(Clone)]) because derive(Clone) will only expand for MerkleTreeTrait types that also
30// implement Clone. Not every MerkleTreeTrait type is Clone-able because not all merkel Store's are
31// Clone-able, therefore deriving Clone would impl Clone for less than all possible Tree types.
32impl<Tree: 'static + MerkleTreeTrait> Clone for FallbackPoStCircuit<Tree> {
33    fn clone(&self) -> Self {
34        FallbackPoStCircuit {
35            prover_id: self.prover_id,
36            sectors: self.sectors.clone(),
37        }
38    }
39}
40
41pub struct Sector<Tree: MerkleTreeTrait> {
42    pub comm_r: Option<Fr>,
43    pub comm_c: Option<Fr>,
44    pub comm_r_last: Option<Fr>,
45    pub leafs: Vec<Option<Fr>>,
46    pub paths: Vec<AuthPath<Tree::Hasher, Tree::Arity, Tree::SubTreeArity, Tree::TopTreeArity>>,
47    pub id: Option<Fr>,
48}
49
50// We must manually implement Clone for all types generic over MerkleTreeTrait (instead of using
51// #derive(Clone)).
52impl<Tree: MerkleTreeTrait> Clone for Sector<Tree> {
53    fn clone(&self) -> Self {
54        Sector {
55            comm_r: self.comm_r,
56            comm_c: self.comm_c,
57            comm_r_last: self.comm_r_last,
58            leafs: self.leafs.clone(),
59            paths: self.paths.clone(),
60            id: self.id,
61        }
62    }
63}
64
65impl<Tree: 'static + MerkleTreeTrait> Sector<Tree> {
66    pub fn circuit(
67        sector: &PublicSector<<Tree::Hasher as Hasher>::Domain>,
68        vanilla_proof: &SectorProof<Tree::Proof>,
69    ) -> Result<Self> {
70        let leafs = vanilla_proof
71            .leafs()
72            .iter()
73            .map(|l| Some((*l).into()))
74            .collect();
75
76        let paths = vanilla_proof
77            .as_options()
78            .into_iter()
79            .map(Into::into)
80            .collect();
81
82        Ok(Sector {
83            leafs,
84            id: Some(sector.id.into()),
85            comm_r: Some(sector.comm_r.into()),
86            comm_c: Some(vanilla_proof.comm_c.into()),
87            comm_r_last: Some(vanilla_proof.comm_r_last.into()),
88            paths,
89        })
90    }
91
92    pub fn blank_circuit(pub_params: &PublicParams) -> Self {
93        let challenges_count = pub_params.challenge_count;
94        let leaves = pub_params.sector_size as usize / NODE_SIZE;
95
96        let por_params = por::PublicParams {
97            leaves,
98            private: true,
99        };
100        let leafs = vec![None; challenges_count];
101        let paths = vec![AuthPath::blank(por_params.leaves); challenges_count];
102
103        Sector {
104            id: None,
105            comm_r: None,
106            comm_c: None,
107            comm_r_last: None,
108            leafs,
109            paths,
110        }
111    }
112}
113
114impl<Tree: 'static + MerkleTreeTrait> Circuit<Fr> for &Sector<Tree> {
115    fn synthesize<CS: ConstraintSystem<Fr>>(self, cs: &mut CS) -> Result<(), SynthesisError> {
116        let Sector {
117            comm_r,
118            comm_c,
119            comm_r_last,
120            leafs,
121            paths,
122            ..
123        } = self;
124
125        assert_eq!(paths.len(), leafs.len());
126
127        // 1. Verify comm_r
128        let comm_r_last_num = AllocatedNum::alloc(cs.namespace(|| "comm_r_last"), || {
129            comm_r_last
130                .map(Into::into)
131                .ok_or(SynthesisError::AssignmentMissing)
132        })?;
133
134        let comm_c_num = AllocatedNum::alloc(cs.namespace(|| "comm_c"), || {
135            comm_c
136                .map(Into::into)
137                .ok_or(SynthesisError::AssignmentMissing)
138        })?;
139
140        let comm_r_num = AllocatedNum::alloc(cs.namespace(|| "comm_r"), || {
141            comm_r
142                .map(Into::into)
143                .ok_or(SynthesisError::AssignmentMissing)
144        })?;
145
146        comm_r_num.inputize(cs.namespace(|| "comm_r_input"))?;
147
148        // 1. Verify H(Comm_C || comm_r_last) == comm_r
149        {
150            let hash_num = <Tree::Hasher as Hasher>::Function::hash2_circuit(
151                cs.namespace(|| "H_comm_c_comm_r_last"),
152                &comm_c_num,
153                &comm_r_last_num,
154            )?;
155
156            // Check actual equality
157            constraint::equal(
158                cs,
159                || "enforce_comm_c_comm_r_last_hash_comm_r",
160                &comm_r_num,
161                &hash_num,
162            );
163        }
164
165        // 2. Verify Inclusion Paths
166        for (i, (leaf, path)) in leafs.iter().zip(paths.iter()).enumerate() {
167            PoRCircuit::<Tree>::synthesize(
168                cs.namespace(|| format!("challenge_inclusion_{}", i)),
169                Root::Val(*leaf),
170                path.clone(),
171                Root::from_allocated::<CS>(comm_r_last_num.clone()),
172                true,
173            )?;
174        }
175
176        Ok(())
177    }
178}
179
180#[derive(Clone, Default)]
181pub struct ComponentPrivateInputs {}
182
183impl<Tree: MerkleTreeTrait> CircuitComponent for FallbackPoStCircuit<Tree> {
184    type ComponentPrivateInputs = ComponentPrivateInputs;
185}
186
187impl<Tree: 'static + MerkleTreeTrait> Circuit<Fr> for FallbackPoStCircuit<Tree> {
188    fn synthesize<CS: ConstraintSystem<Fr>>(self, cs: &mut CS) -> Result<(), SynthesisError> {
189        if CS::is_extensible() {
190            return self.synthesize_extendable(cs);
191        }
192
193        self.synthesize_default(cs)
194    }
195}
196
197impl<Tree: 'static + MerkleTreeTrait> FallbackPoStCircuit<Tree> {
198    fn synthesize_default<CS: ConstraintSystem<Fr>>(
199        self,
200        cs: &mut CS,
201    ) -> Result<(), SynthesisError> {
202        let cs = &mut cs.namespace(|| "outer namespace".to_string());
203
204        for (i, sector) in self.sectors.iter().enumerate() {
205            let cs = &mut cs.namespace(|| format!("sector_{}", i));
206            sector.synthesize(cs)?;
207        }
208        Ok(())
209    }
210
211    fn synthesize_extendable<CS: ConstraintSystem<Fr>>(
212        self,
213        cs: &mut CS,
214    ) -> Result<(), SynthesisError> {
215        let FallbackPoStCircuit { sectors, .. } = self;
216
217        let num_chunks = SETTINGS.window_post_synthesis_num_cpus as usize;
218
219        let chunk_size = (sectors.len() / num_chunks).max(1);
220        let css = sectors
221            .par_chunks(chunk_size)
222            .map(|sector_group| {
223                let mut cs = CS::new();
224                cs.alloc_input(|| "temp ONE", || Ok(Fr::ONE))?;
225
226                for (i, sector) in sector_group.iter().enumerate() {
227                    let mut cs = cs.namespace(|| format!("sector_{}", i));
228
229                    sector.synthesize(&mut cs)?;
230                }
231                Ok(cs)
232            })
233            .collect::<Result<Vec<_>, SynthesisError>>()?;
234
235        for sector_cs in css.into_iter() {
236            cs.extend(&sector_cs);
237        }
238
239        Ok(())
240    }
241}