nexus_sdk/
traits.rs

1use crypto::digest::{Digest, OutputSizeUser};
2use crypto_common::generic_array::{ArrayLength, GenericArray};
3use nexus_common::constants::WORD_SIZE;
4use serde::{de::DeserializeOwned, Serialize};
5use std::path::Path;
6
7use nexus_core::nvm::internals::*;
8
9use crate::compile::*;
10use crate::error::*;
11
12/// A compute resource.
13pub trait Compute {}
14
15/// Use local compute to prove the zkVM.
16pub enum Local {}
17impl Compute for Local {}
18
19/// A view of an execution, the correctness of which is guaranteed by the proving and checked by the verification.
20pub trait CheckedView {
21    /// Rebuild from constitutent parts, for use by the verifier during verification.
22    fn new_from_expected(
23        memory_layout: &LinearMemoryLayout,
24        expected_public_input: &[u8],
25        expected_exit_code: &[u8],
26        expected_public_output: &[u8],
27        expected_elf: &nexus_core::nvm::ElfFile,
28        expected_ad: &[u8],
29    ) -> Self;
30}
31
32impl CheckedView for nexus_core::nvm::View {
33    fn new_from_expected(
34        memory_layout: &LinearMemoryLayout,
35        expected_public_input: &[u8],
36        expected_exit_code: &[u8],
37        expected_public_output: &[u8],
38        expected_elf: &nexus_core::nvm::ElfFile,
39        expected_ad: &[u8],
40    ) -> Self {
41        let emulator = LinearEmulator::default();
42
43        // Replace custom instructions `rin` and `wou` with `lw` and `sw`.
44        let instructions = expected_elf
45            .instructions
46            .iter()
47            .map(|instr| convert_instruction(&emulator.executor.instruction_executor, instr))
48            .collect();
49
50        let converted_elf = nexus_core::nvm::ElfFile {
51            instructions,
52            ..expected_elf.clone()
53        };
54
55        let program_memory = elf_into_program_info(&converted_elf, memory_layout);
56
57        let initial_memory = slice_into_io_entries::<MemoryInitializationEntry>(
58            memory_layout.public_input_address_location(),
59            &[
60                memory_layout.public_input_start().to_le_bytes(),
61                memory_layout.exit_code().to_le_bytes(), // the exit code is the first word of the output
62            ]
63            .concat(),
64        )
65        .iter()
66        .chain(map_into_io_entries::<MemoryInitializationEntry>(&expected_elf.rom_image).iter())
67        .chain(map_into_io_entries::<MemoryInitializationEntry>(&expected_elf.ram_image).iter())
68        .chain(
69            slice_into_io_entries::<MemoryInitializationEntry>(
70                memory_layout.public_input_start(),
71                &[
72                    &(expected_public_input.len() as u32).to_le_bytes(),
73                    expected_public_input,
74                ]
75                .concat(),
76            )
77            .iter(),
78        )
79        .copied()
80        .collect();
81
82        let exit_code = slice_into_io_entries::<PublicOutputEntry>(
83            memory_layout.exit_code(),
84            expected_exit_code,
85        );
86
87        let output_memory = slice_into_io_entries::<PublicOutputEntry>(
88            memory_layout.public_output_start(),
89            expected_public_output,
90        );
91
92        let static_memory_size =
93            (&expected_elf.rom_image.len_bytes() + &expected_elf.ram_image.len_bytes()) * WORD_SIZE;
94
95        Self::new(
96            &Some(*memory_layout),
97            &Vec::new(),
98            &program_memory,
99            &initial_memory,
100            memory_layout.tracked_ram_size(static_memory_size),
101            &exit_code,
102            &output_memory,
103            &expected_ad.to_vec(),
104        )
105    }
106}
107
108/// A view of an execution capturing the context needed for proof distribution and verification.
109pub trait Viewable {
110    /// Deserialize the public input used for the execution.
111    fn public_input<T: Serialize + DeserializeOwned + Sized>(&self) -> Result<T, IOError>;
112
113    /// Compute a digest over the public input used for the execution.
114    fn public_input_digest<T: Serialize + DeserializeOwned + Sized, H: Digest>(
115        &self,
116    ) -> Result<GenericArray<u8, H::OutputSize>, IOError>
117    where
118        <H as OutputSizeUser>::OutputSize: ArrayLength<u8>,
119    {
120        Ok(H::digest(
121            postcard::to_stdvec_cobs::<T>(&Self::public_input::<T>(self)?)
122                .map_err(IOError::from)?
123                .as_slice(),
124        ))
125    }
126
127    /// Deserialize the exit code resulting from the execution.
128    fn exit_code(&self) -> Result<u32, IOError>;
129
130    /// Compute a digest over the public output resulting from the execution.
131    fn exit_code_digest<H: Digest>(&self) -> Result<GenericArray<u8, H::OutputSize>, IOError>
132    where
133        <H as OutputSizeUser>::OutputSize: ArrayLength<u8>,
134    {
135        Ok(H::digest(Self::exit_code(self)?.to_le_bytes()))
136    }
137
138    /// Deserialize the public output resulting from the execution.
139    fn public_output<U: Serialize + DeserializeOwned + Sized>(&self) -> Result<U, IOError>;
140
141    /// Compute a digest over the public output resulting from the execution.
142    fn public_output_digest<U: Serialize + DeserializeOwned + Sized, H: Digest>(
143        &self,
144    ) -> Result<GenericArray<u8, H::OutputSize>, IOError>
145    where
146        <H as OutputSizeUser>::OutputSize: ArrayLength<u8>,
147    {
148        Ok(H::digest(
149            postcard::to_stdvec_cobs::<U>(&Self::public_output::<U>(self)?)
150                .map_err(IOError::from)?
151                .as_slice(),
152        ))
153    }
154
155    /// Deserialize the associated data bound into the execution.
156    fn associated_data(&self) -> Result<Vec<u8>, IOError>;
157
158    /// Compute a digest over the associated data bound into the execution.
159    fn associated_data_digest<H: Digest>(&self) -> Result<GenericArray<u8, H::OutputSize>, IOError>
160    where
161        <H as OutputSizeUser>::OutputSize: ArrayLength<u8>,
162    {
163        Ok(H::digest(Self::associated_data(self)?.as_slice()))
164    }
165
166    /// Recover any debug logs produced by the execution.
167    fn logs(&self) -> Result<Vec<String>, IOError>;
168}
169
170impl Viewable for nexus_core::nvm::View {
171    /// Deserialize the public input used for the execution.
172    fn public_input<T: Serialize + DeserializeOwned + Sized>(&self) -> Result<T, IOError> {
173        if let Some(mut bytes) = self.view_public_input() {
174            Ok(postcard::from_bytes_cobs::<T>(&mut bytes).map_err(IOError::from)?)
175        } else {
176            Err(IOError::NotYetAvailableError)
177        }
178    }
179
180    /// Deserialize the exit code resulting from the execution.
181    fn exit_code(&self) -> Result<u32, IOError> {
182        if let Some(bytes) = self.view_exit_code() {
183            Ok(postcard::from_bytes::<u32>(&bytes).map_err(IOError::from)?)
184        } else {
185            Err(IOError::NotYetAvailableError)
186        }
187    }
188
189    /// Deserialize the public output resulting from the execution.
190    fn public_output<U: Serialize + DeserializeOwned + Sized>(&self) -> Result<U, IOError> {
191        if let Some(mut bytes) = self.view_public_output() {
192            Ok(postcard::from_bytes_cobs::<U>(&mut bytes).map_err(IOError::from)?)
193        } else {
194            Err(IOError::NotYetAvailableError)
195        }
196    }
197
198    /// Deserialize the associated data bound into the execution.
199    fn associated_data(&self) -> Result<Vec<u8>, IOError> {
200        if let Some(bytes) = self.view_associated_data() {
201            Ok(bytes)
202        } else {
203            Err(IOError::NotYetAvailableError)
204        }
205    }
206
207    /// Recover any debug logs produced by the execution.
208    fn logs(&self) -> Result<Vec<String>, IOError> {
209        if let Some(bytes_vecs) = self.view_debug_logs() {
210            Ok(bytes_vecs
211                .iter()
212                .map(|raw_log: &Vec<u8>| String::from_utf8_lossy(raw_log).to_string())
213                .collect())
214        } else {
215            Err(IOError::NotYetAvailableError)
216        }
217    }
218}
219
220/// A proving instance that can be constructed through compiling a guest program.
221pub trait ByGuestCompilation: Prover {
222    /// Construct a new proving instance through dynamic compilation (see [`compile`](crate::compile)).
223    fn compile(compiler: &mut impl Compile) -> Result<Self, Self::Error>;
224}
225
226/// A prover for the zkVM.
227pub trait Prover: Sized {
228    type Proof: Verifiable;
229    type View: CheckedView;
230    type Error: From<nexus_core::nvm::VMError>;
231
232    /// Construct a new proving instance.
233    fn new(elf: &nexus_core::nvm::ElfFile) -> Result<Self, <Self as Prover>::Error>;
234
235    /// Construct a new proving instance from raw ELF bytes.
236    fn new_from_bytes(elf_bytes: &[u8]) -> Result<Self, <Self as Prover>::Error> {
237        Self::new(&nexus_core::nvm::ElfFile::from_bytes(elf_bytes)?)
238    }
239
240    /// Construct a new proving instance by reading an ELF file.
241    fn new_from_file<P: AsRef<Path> + ?Sized>(path: &P) -> Result<Self, <Self as Prover>::Error> {
242        Self::new(&nexus_core::nvm::ElfFile::from_path(&path)?)
243    }
244
245    /// Set the associated data bytes to be bound into the proof.
246    fn set_associated_data(&mut self, ad: &[u8]) -> Result<(), <Self as Prover>::Error>;
247
248    /// Run the zkVM and return a view of the execution output.
249    fn run(&self) -> Result<Self::View, <Self as Prover>::Error> {
250        Self::run_with_input::<(), ()>(self, &(), &())
251    }
252
253    /// Run the zkVM on private input of type `S` and public input of type `T` and return a view of the execution output.
254    fn run_with_input<S: Serialize + Sized, T: Serialize + DeserializeOwned + Sized>(
255        &self,
256        private_input: &S,
257        public_input: &T,
258    ) -> Result<Self::View, <Self as Prover>::Error>;
259
260    /// Run the zkVM and return a verifiable proof, along with a view of the execution output.
261    fn prove(self) -> Result<(Self::View, Self::Proof), <Self as Prover>::Error>
262    where
263        Self: Sized,
264    {
265        Self::prove_with_input::<(), ()>(self, &(), &())
266    }
267
268    /// Run the zkVM on private input of type `S` and public input of type `T` and return a verifiable proof, along with a view of the execution output.
269    fn prove_with_input<S: Serialize + Sized, T: Serialize + DeserializeOwned + Sized>(
270        self,
271        private_input: &S,
272        public_input: &T,
273    ) -> Result<(Self::View, Self::Proof), <Self as Prover>::Error>;
274}
275
276/// An object that can be configured with necessary parameters for proving and verification.
277///
278/// Currently only used by the legacy prover integrations.
279pub trait Setup<'a> {
280    /// Global parameters with trust assumptions.
281    type Reference;
282    /// Global parameters without trust assumptions.
283    type Parameters;
284    /// Program-specific parameters.
285    type Preprocessing;
286    type Error;
287
288    /// Configure reference string.
289    fn setup_reference<'b: 'a>(
290        &mut self,
291        reference: &'b Self::Reference,
292    ) -> Result<(), Self::Error>;
293
294    /// Configure parameters string.
295    fn setup_parameters<'b: 'a>(
296        &mut self,
297        parameters: &'b Self::Parameters,
298    ) -> Result<(), Self::Error>;
299
300    /// Detach prover or proof from setup to make it easier to pass around without needing to manage lifetimes.
301    fn detach(&mut self);
302
303    /// Access reference through borrow.
304    fn reference(&self) -> Result<&'a Self::Reference, Self::Error>;
305
306    // todo: add support for reference digest
307
308    /// Access parameters through borrow.
309    fn parameters(&self) -> Result<&'a Self::Parameters, Self::Error>;
310
311    // todo: add support for parameters digest
312
313    /// Return preprocessing.
314    fn preprocessing(&self) -> Result<Self::Preprocessing, Self::Error>;
315
316    // todo: add support for preprocessing digest
317}
318
319/// A global, trust-assumption-reliant parameter set used for proving and verifying, such as a common or structured reference string (CRS/SRS).
320///
321/// Currently only used by the legacy prover integrations.
322pub trait Reference {
323    type Error;
324
325    /// Generate reference.
326    fn generate() -> Result<Self, Self::Error>
327    where
328        Self: Sized;
329
330    /// Load reference from a file.
331    fn load(path: &Path) -> Result<Self, Self::Error>
332    where
333        Self: Sized;
334
335    /// Save reference to a file.
336    fn save(reference: &Self, path: &Path) -> Result<(), Self::Error>;
337}
338
339impl Reference for () {
340    type Error = ConfigurationError;
341
342    fn generate() -> Result<Self, Self::Error> {
343        Err(ConfigurationError::NotApplicableOperation)
344    }
345
346    fn load(_path: &Path) -> Result<Self, Self::Error> {
347        Err(ConfigurationError::NotApplicableOperation)
348    }
349
350    fn save(_reference: &Self, _path: &Path) -> Result<(), Self::Error> {
351        Err(ConfigurationError::NotApplicableOperation)
352    }
353}
354
355/// A global, no-trust-assumption parameter set used for proving and verifying.
356///
357/// Currently only used by the legacy prover integrations.
358pub trait Parameters {
359    type Ref: Reference;
360    type Error;
361
362    /// Generate parameters.
363    fn generate(reference: &Self::Ref) -> Result<Self, Self::Error>
364    where
365        Self: Sized;
366
367    /// Load parameters from a file.
368    fn load(path: &Path) -> Result<Self, Self::Error>
369    where
370        Self: Sized;
371
372    /// Save parameters to a file.
373    fn save(parameters: &Self, path: &Path) -> Result<(), Self::Error>;
374}
375
376impl Parameters for () {
377    type Ref = ();
378    type Error = ConfigurationError;
379
380    fn generate(_reference: &Self::Ref) -> Result<Self, Self::Error> {
381        Err(ConfigurationError::NotApplicableOperation)
382    }
383
384    fn load(_path: &Path) -> Result<Self, Self::Error> {
385        Err(ConfigurationError::NotApplicableOperation)
386    }
387
388    fn save(_parameters: &Self, _path: &Path) -> Result<(), Self::Error> {
389        Err(ConfigurationError::NotApplicableOperation)
390    }
391}
392
393/// A program-specific preprocessing parameter set.
394///
395/// Currently only used by the legacy prover integrations.
396pub trait Preprocessing {
397    type Ref: Reference;
398    type Params: Parameters;
399    type Error;
400
401    /// Load preprocessing from a file.
402    fn load(path: &Path) -> Result<Self, Self::Error>
403    where
404        Self: Sized;
405
406    /// Save preprocessing to a file.
407    fn save(preprocessing: &Self, path: &Path) -> Result<(), Self::Error>;
408}
409
410impl Preprocessing for () {
411    type Ref = ();
412    type Params = ();
413    type Error = ConfigurationError;
414
415    fn load(_path: &Path) -> Result<Self, Self::Error> {
416        Err(ConfigurationError::NotApplicableOperation)
417    }
418
419    fn save(_preprocessing: &Self, _path: &Path) -> Result<(), Self::Error> {
420        Err(ConfigurationError::NotApplicableOperation)
421    }
422}
423
424/// A verifiable proof of a zkVM execution.
425pub trait Verifiable: Serialize + DeserializeOwned {
426    type View: CheckedView;
427    type Error: From<nexus_core::nvm::VMError> + From<IOError>;
428
429    /// Get the memory layout configuration used for proving.
430    fn get_memory_layout(&self) -> &LinearMemoryLayout;
431
432    /// Verify the proof of an execution for a constructed [`CheckedView`](crate::traits::CheckedView).
433    fn verify(&self, expected_view: &Self::View) -> Result<(), <Self as Verifiable>::Error>;
434
435    /// Verify the proof of an execution.
436    fn verify_expected<
437        T: Serialize + DeserializeOwned + Sized,
438        U: Serialize + DeserializeOwned + Sized,
439    >(
440        &self,
441        expected_public_input: &T,
442        expected_exit_code: u32,
443        expected_public_output: &U,
444        expected_elf: &nexus_core::nvm::ElfFile,
445        expected_ad: &[u8],
446    ) -> Result<(), <Self as Verifiable>::Error> {
447        let mut input_encoded =
448            postcard::to_stdvec(&expected_public_input).map_err(IOError::from)?;
449        if !input_encoded.is_empty() {
450            let input = expected_public_input.to_owned();
451
452            input_encoded = postcard::to_stdvec_cobs(&input).map_err(IOError::from)?;
453            let input_padded_len = (input_encoded.len() + 3) & !3;
454
455            assert!(input_padded_len >= input_encoded.len());
456            input_encoded.resize(input_padded_len, 0x00); // cobs ignores 0x00 padding
457        }
458
459        let mut output_encoded =
460            postcard::to_stdvec(&expected_public_output).map_err(IOError::from)?;
461        if !output_encoded.is_empty() {
462            let output = expected_public_output.to_owned();
463
464            output_encoded = postcard::to_stdvec_cobs(&output).map_err(IOError::from)?;
465            let output_padded_len = (output_encoded.len() + 3) & !3;
466
467            assert!(output_padded_len >= output_encoded.len());
468            output_encoded.resize(output_padded_len, 0x00); // cobs ignores 0x00 padding
469        }
470
471        let view = Self::View::new_from_expected(
472            self.get_memory_layout(),
473            input_encoded.as_slice(),
474            &expected_exit_code.to_le_bytes(),
475            output_encoded.as_slice(),
476            expected_elf,
477            expected_ad,
478        );
479
480        self.verify(&view)
481    }
482
483    /// Verify the proof of an execution, with the elf provided as raw bytes.
484    fn verify_expected_from_program_bytes<
485        T: Serialize + DeserializeOwned + Sized,
486        U: Serialize + DeserializeOwned + Sized,
487    >(
488        &self,
489        expected_public_input: &T,
490        expected_exit_code: u32,
491        expected_public_output: &U,
492        expected_elf_bytes: &[u8],
493        expected_ad: &[u8],
494    ) -> Result<(), <Self as Verifiable>::Error> {
495        self.verify_expected(
496            expected_public_input,
497            expected_exit_code,
498            expected_public_output,
499            &nexus_core::nvm::ElfFile::from_bytes(expected_elf_bytes)?,
500            expected_ad,
501        )
502    }
503
504    /// Verify the proof of an execution, sourcing the program elf from a path.
505    fn verify_expected_from_program_path<
506        P: AsRef<Path> + ?Sized,
507        T: Serialize + DeserializeOwned + Sized,
508        U: Serialize + DeserializeOwned + Sized,
509    >(
510        &self,
511        expected_public_input: &T,
512        expected_exit_code: u32,
513        expected_public_output: &U,
514        expected_elf_path: &P,
515        expected_ad: &[u8],
516    ) -> Result<(), <Self as Verifiable>::Error> {
517        self.verify_expected(
518            expected_public_input,
519            expected_exit_code,
520            expected_public_output,
521            &nexus_core::nvm::ElfFile::from_path(expected_elf_path)?,
522            expected_ad,
523        )
524    }
525
526    /// Return a size estimate for the proof, in bytes.
527    fn size_estimate(&self) -> usize;
528}