Skip to main content

prover_elf_utils/
elf_info.rs

1use std::{env, fs, io::Write, path::Path};
2
3use bincode::Options;
4use sp1_prover::{HashableKey, SP1Prover, SP1VerifyingKey};
5
6pub fn bincode_options() -> impl bincode::Options {
7    bincode::DefaultOptions::new()
8        .with_big_endian()
9        .with_fixint_encoding()
10}
11
12/// Build time tool to emit information about a zkvm ELF.
13pub struct ElfInfo {
14    /// Lazily loaded SP1 prover client.
15    prover: Option<SP1Prover>,
16
17    /// Target file.
18    output: fs::File,
19}
20
21impl ElfInfo {
22    pub fn writing_to(file_name: impl AsRef<Path>) -> Self {
23        println!("cargo::rerun-if-changed=build.rs");
24
25        let prover = None;
26
27        let dir = env::var_os("OUT_DIR").expect("output directory");
28        let path = Path::new(&dir).join(file_name);
29        let output = fs::File::create(path).expect("elf info output file");
30
31        Self { prover, output }
32    }
33
34    /// Emit module corresponding to given ELF binary.
35    pub fn module<EB>(self, module_name: &str, elf_bytes: EB) -> Emitter<EB> {
36        Emitter::new(self, module_name, elf_bytes)
37    }
38
39    /// Like [Self::module] but the ELF is taken from a file.
40    pub fn module_from_file(
41        self,
42        module_name: &str,
43        elf_path: impl AsRef<Path>,
44    ) -> Emitter<Box<[u8]>> {
45        let path_string = elf_path.as_ref().to_string_lossy();
46        println!("cargo::rerun-if-changed={path_string}");
47
48        let elf_bytes = fs::read(elf_path).unwrap().into_boxed_slice();
49        self.module(module_name, elf_bytes)
50    }
51
52    fn prover(&mut self) -> &SP1Prover {
53        self.prover.get_or_insert_with(SP1Prover::new)
54    }
55}
56
57/// Takes care of emitting code for one proof binary.
58#[must_use = "Please finalize the sequence with a .finish() call"]
59pub struct Emitter<ElfBytes> {
60    context: ElfInfo,
61    elf: ElfBytes,
62    vkey: Option<SP1VerifyingKey>,
63}
64
65impl<ElfBytes> Emitter<ElfBytes> {
66    fn new(context: ElfInfo, name: &str, elf: ElfBytes) -> Self {
67        writeln!(&context.output, "pub mod {name} {{").unwrap();
68        let vkey = None;
69        Emitter { context, elf, vkey }
70    }
71
72    pub fn finish(self) -> ElfInfo {
73        self.output().write_all(b"}\n").unwrap();
74        self.context
75    }
76
77    fn output(&self) -> &fs::File {
78        &self.context.output
79    }
80}
81
82impl<ElfBytes: AsRef<[u8]>> Emitter<ElfBytes> {
83    /// Emit bincode-encoded vkey for given proof.
84    pub fn emit_vkey_bytes(mut self) -> Self {
85        let bytes = bincode_options().serialize(self.vkey()).unwrap();
86        writeln!(
87            self.output(),
88            "    pub const VKEY_BYTES: &[u8] = &{bytes:?};"
89        )
90        .unwrap();
91        self
92    }
93
94    /// Emit vkey hash for given proof.
95    pub fn emit_vkey_hash(mut self) -> Self {
96        let hash = self.vkey().hash_u32();
97        writeln!(
98            self.output(),
99            "    pub const VKEY_HASH: [u32; 8] = {hash:?};"
100        )
101        .unwrap();
102        self
103    }
104
105    /// Emit vkey hash commitment.
106    pub fn emit_vkey_commitment(mut self) -> Self {
107        let commitment = self.vkey().hash_bytes();
108        writeln!(
109            self.output(),
110            "    pub const VKEY_COMMITMENT: [u8; 32] = {commitment:?};"
111        )
112        .unwrap();
113        self
114    }
115
116    fn vkey(&mut self) -> &SP1VerifyingKey {
117        self.vkey.get_or_insert_with(|| {
118            let (_sp1_pkey, _stark_pkey, _program, vkey) =
119                self.context.prover().setup(self.elf.as_ref());
120            vkey
121        })
122    }
123}