nexus_sdk/legacy/hypernova/
seq.rs

1use crate::legacy::compile;
2use crate::legacy::traits::*;
3use crate::legacy::views::UncheckedView;
4use crate::traits::*;
5
6use crate::legacy::ark_serialize_utils::{ark_de, ark_se};
7use serde::{de::DeserializeOwned, Deserialize, Serialize};
8use std::path::Path;
9use thiserror::Error;
10
11use nexus_core_legacy::nvm::interactive::{eval, parse_elf, trace};
12use nexus_core_legacy::nvm::memory::MerkleTrie;
13use nexus_core_legacy::nvm::NexusVM;
14use nexus_core_legacy::prover::hypernova::pp::{gen_vm_pp, load_pp, save_pp};
15use nexus_core_legacy::prover::hypernova::prove_seq;
16use nexus_core_legacy::prover::hypernova::types::IVCProof;
17use nexus_core_legacy::prover::nova::srs::{
18    get_min_srs_size, load_srs,
19    test_srs::{gen_test_srs, save_srs},
20};
21
22use crate::error::{BuildError, ConfigurationError, IOError, PathError};
23use nexus_core_legacy::prover::hypernova::error::{NovaProofError, ProofError};
24
25// re-exports
26/// Public parameters used to prove and verify zkVM executions.
27pub use nexus_core_legacy::prover::hypernova::types::PP;
28
29use nexus_core_legacy::prover::hypernova::types::SRS as SRSi;
30/// Structured reference string (SRS) used to generate public parameters.
31pub struct SRS(SRSi); // for some reason the compiler doesn't like us using this directly for trait instantiation, so we use a newtype-style indirection.
32
33use std::marker::PhantomData;
34
35// hard-coded number of vm instructions to pack per recursion step
36const K: usize = 1;
37
38/// Errors that occur while proving using HyperNova.
39#[derive(Debug, Error)]
40pub enum Error {
41    /// An error occurred during parameter generation, execution, proving, or proof verification for the zkVM.
42    #[error(transparent)]
43    ProofError(#[from] ProofError),
44
45    /// An error occurred during srs generation or use for the zkVM.
46    #[error(transparent)]
47    SRSError(#[from] NovaProofError), // the srs handling comes from the nova crate
48
49    /// An error occurred building the guest program dynamically.
50    #[error(transparent)]
51    BuildError(#[from] BuildError),
52
53    /// An error occurred reading or writing to the filesystem.
54    #[error(transparent)]
55    HostIOError(#[from] std::io::Error),
56
57    /// An error occurred trying to parse a path for use with the filesystem.
58    #[error(transparent)]
59    PathError(#[from] PathError),
60
61    /// An error occurred reading or writing to the zkVM input/output tapes.
62    #[error(transparent)]
63    GuestIOError(#[from] IOError),
64
65    /// An error occured configuring the prover.
66    #[error(transparent)]
67    ConfigurationError(#[from] ConfigurationError),
68}
69
70/// Prover for the Nexus zkVM using HyperNova.
71pub struct HyperNova<'a, C: Compute = Local> {
72    vm: NexusVM<MerkleTrie>,
73    pp: Option<&'a PP>,
74    srs: Option<&'a SRS>,
75    _compute: PhantomData<C>,
76}
77
78/// A verifiable proof of a zkVM execution. Also contains a view capturing the output of the machine.
79///
80/// **Warning**: The proof contains an _unchecked_ view. Please review [`UncheckedView`].
81#[derive(Serialize, Deserialize)]
82pub struct Proof<'a> {
83    #[serde(serialize_with = "ark_se", deserialize_with = "ark_de")]
84    proof: IVCProof,
85    #[serde(skip)]
86    view: UncheckedView,
87    #[serde(skip)]
88    srs: Option<&'a SRS>,
89    #[serde(skip)]
90    pp: Option<&'a PP>,
91}
92
93macro_rules! setup {
94    ($id:ty) => {
95        impl<'a> Setup<'a> for $id {
96            type Reference = SRS;
97            type Parameters = PP;
98            type Preprocessing = ();
99            type Error = Error;
100
101            fn setup_reference<'b: 'a>(
102                &mut self,
103                reference: &'b Self::Reference,
104            ) -> Result<(), Self::Error> {
105                self.srs = Some(reference);
106                Ok(())
107            }
108
109            fn setup_parameters<'b: 'a>(
110                &mut self,
111                parameters: &'b Self::Parameters,
112            ) -> Result<(), Self::Error> {
113                self.pp = Some(parameters);
114                Ok(())
115            }
116
117            fn detach(&mut self) {
118                self.srs = None;
119                self.pp = None;
120            }
121
122            fn reference(&self) -> Result<&'a Self::Reference, Self::Error> {
123                if self.srs.is_none() {
124                    return Err(Error::from(ConfigurationError::NotYetConfigured));
125                } else {
126                    Ok(self.srs.unwrap())
127                }
128            }
129
130            fn parameters(&self) -> Result<&'a Self::Parameters, Self::Error> {
131                if self.pp.is_none() {
132                    return Err(Error::from(ConfigurationError::NotYetConfigured));
133                } else {
134                    Ok(self.pp.unwrap())
135                }
136            }
137
138            fn preprocessing(&self) -> Result<Self::Preprocessing, Self::Error> {
139                Ok(())
140            }
141        }
142    };
143}
144
145setup!(HyperNova<'a, Local>);
146setup!(Proof<'a>);
147
148impl<'a> LegacyProver<'a> for HyperNova<'a, Local> {
149    type Proof = Proof<'a>;
150    type View = UncheckedView;
151
152    fn new(elf_bytes: &[u8]) -> Result<Self, Self::Error> {
153        Ok(HyperNova::<Local> {
154            vm: parse_elf::<MerkleTrie>(elf_bytes).map_err(ProofError::from)?,
155            srs: None,
156            pp: None,
157            _compute: PhantomData,
158        })
159    }
160
161    fn compile(opts: &compile::CompileOpts) -> Result<Self, Self::Error> {
162        let mut iopts = opts.to_owned();
163
164        // if the user has not set the memory limit, default to 4mb
165        if iopts.memlimit.is_none() {
166            iopts.set_memlimit(4);
167        }
168
169        let elf_path = iopts.build(&compile::ForProver::Default)?;
170
171        Self::new_from_file(&elf_path)
172    }
173
174    fn run_with_input<S>(mut self, private_input: &S) -> Result<Self::View, Self::Error>
175    where
176        S: Serialize + Sized,
177    {
178        self.vm.syscalls.set_input(
179            postcard::to_stdvec(private_input)
180                .map_err(IOError::from)?
181                .as_slice(),
182        );
183
184        eval(&mut self.vm, false, false).map_err(ProofError::from)?;
185
186        Ok(Self::View {
187            out: self.vm.syscalls.get_output(),
188            logs: self
189                .vm
190                .syscalls
191                .get_log_buffer()
192                .into_iter()
193                .map(String::from_utf8)
194                .collect::<Result<Vec<_>, _>>()
195                .map_err(IOError::from)?,
196        })
197    }
198
199    fn prove_with_input<S>(mut self, private_input: &S) -> Result<Self::Proof, Self::Error>
200    where
201        S: Serialize + Sized,
202    {
203        if self.pp.is_none() {
204            return Err(Error::from(ConfigurationError::NotYetConfigured));
205        }
206
207        self.vm.syscalls.set_input(
208            postcard::to_stdvec(private_input)
209                .map_err(IOError::from)?
210                .as_slice(),
211        );
212
213        let tr = trace(&mut self.vm, K, false).map_err(ProofError::from)?;
214
215        Ok(Self::Proof {
216            proof: prove_seq(self.pp.as_ref().unwrap(), tr)?,
217            srs: self.srs,
218            pp: self.pp,
219            view: Self::View {
220                out: self.vm.syscalls.get_output(),
221                logs: self
222                    .vm
223                    .syscalls
224                    .get_log_buffer()
225                    .into_iter()
226                    .map(String::from_utf8)
227                    .collect::<Result<Vec<_>, _>>()
228                    .map_err(IOError::from)?,
229            },
230        })
231    }
232}
233
234impl Reference for SRS {
235    type Error = Error;
236
237    fn generate() -> Result<Self, Self::Error> {
238        eprintln!("Using test reference generation, not for production use!!!");
239        Ok(SRS(
240            gen_test_srs(get_min_srs_size(K)?).map_err(ProofError::from)?
241        ))
242    }
243
244    fn load(path: &Path) -> Result<Self, Self::Error> {
245        if let Some(path_str) = path.to_str() {
246            return Ok(SRS(load_srs(path_str).map_err(ProofError::from)?));
247        }
248
249        Err(Self::Error::PathError(
250            crate::error::PathError::EncodingError,
251        ))
252    }
253
254    fn save(reference: &Self, path: &Path) -> Result<(), Self::Error> {
255        if let Some(path_str) = path.to_str() {
256            let srs = &reference.0;
257            return Ok(save_srs(srs.clone(), path_str).map_err(ProofError::from)?);
258        }
259
260        Err(Self::Error::PathError(
261            crate::error::PathError::EncodingError,
262        ))
263    }
264}
265
266impl Parameters for PP {
267    type Ref = SRS;
268    type Error = Error;
269
270    fn generate(reference: &Self::Ref) -> Result<Self, Self::Error> {
271        eprintln!("Using test parameter generation, not for production use!!!");
272        let srs = &reference.0;
273        Ok(gen_vm_pp(K, srs, &())?)
274    }
275
276    fn load(path: &Path) -> Result<Self, Self::Error> {
277        if let Some(path_str) = path.to_str() {
278            return Ok(load_pp(path_str)?);
279        }
280
281        Err(Self::Error::PathError(
282            crate::error::PathError::EncodingError,
283        ))
284    }
285
286    fn save(pp: &Self, path: &Path) -> Result<(), Self::Error> {
287        if let Some(path_str) = path.to_str() {
288            return Ok(save_pp(pp, path_str)?);
289        }
290
291        Err(Self::Error::PathError(
292            crate::error::PathError::EncodingError,
293        ))
294    }
295}
296
297impl<'a> LegacyVerifiable<'a> for Proof<'a> {
298    type View = UncheckedView;
299
300    fn output<U: DeserializeOwned>(&self) -> Result<U, Self::Error> {
301        Ok(Self::View::output::<U>(&self.view)?)
302    }
303
304    fn logs(&self) -> &Vec<String> {
305        Self::View::logs(&self.view)
306    }
307
308    fn detach(&mut self) {
309        self.srs = None;
310        self.pp = None;
311    }
312
313    fn verify(&self) -> Result<(), Self::Error> {
314        if self.pp.is_none() {
315            return Err(Error::from(ConfigurationError::NotYetConfigured));
316        }
317
318        Ok(self
319            .proof
320            .verify(self.pp.as_ref().unwrap())
321            .map_err(ProofError::from)?)
322    }
323}