openvm_sdk/
fs.rs

1#[cfg(feature = "evm-prove")]
2use std::io::{BufReader, BufWriter, Write};
3use std::{
4    fs::{create_dir_all, read, write, File},
5    path::Path,
6};
7
8use eyre::{Report, Result};
9use openvm_stark_backend::codec::{Decode, Encode};
10use serde::{de::DeserializeOwned, Serialize};
11
12#[cfg(feature = "evm-prove")]
13use crate::{
14    keygen::Halo2ProvingKey,
15    types::{EvmHalo2Verifier, EvmVerifierByteCode},
16    OPENVM_VERSION,
17};
18
19pub const EVM_HALO2_VERIFIER_INTERFACE_NAME: &str = "IOpenVmHalo2Verifier.sol";
20pub const EVM_HALO2_VERIFIER_PARENT_NAME: &str = "Halo2Verifier.sol";
21pub const EVM_HALO2_VERIFIER_BASE_NAME: &str = "OpenVmHalo2Verifier.sol";
22pub const EVM_VERIFIER_ARTIFACT_FILENAME: &str = "verifier.bytecode.json";
23
24/// Reads the EVM halo2 verifier written by [`write_evm_halo2_verifier_to_folder`]. If
25/// `version_dir` is `Some`, it replaces the default `v[OPENVM_VERSION]` directory name.
26#[cfg(feature = "evm-prove")]
27pub fn read_evm_halo2_verifier_from_folder<P: AsRef<Path>>(
28    folder: P,
29    version_dir: Option<&str>,
30) -> Result<EvmHalo2Verifier> {
31    use std::fs::read_to_string;
32
33    let folder = folder
34        .as_ref()
35        .join("src")
36        .join(version_dir.map_or_else(|| format!("v{OPENVM_VERSION}"), str::to_string));
37    let halo2_verifier_code_path = folder.join(EVM_HALO2_VERIFIER_PARENT_NAME);
38    let openvm_verifier_code_path = folder.join(EVM_HALO2_VERIFIER_BASE_NAME);
39    let interface_path = folder
40        .join("interfaces")
41        .join(EVM_HALO2_VERIFIER_INTERFACE_NAME);
42    let halo2_verifier_code = read_to_string(&halo2_verifier_code_path)
43        .map_err(|e| read_error(&halo2_verifier_code_path, e.into()))?;
44    let openvm_verifier_code = read_to_string(&openvm_verifier_code_path)
45        .map_err(|e| read_error(&openvm_verifier_code_path, e.into()))?;
46    let interface =
47        read_to_string(&interface_path).map_err(|e| read_error(&interface_path, e.into()))?;
48
49    let artifact_path = folder.join(EVM_VERIFIER_ARTIFACT_FILENAME);
50    let artifact: EvmVerifierByteCode = File::open(&artifact_path)
51        .map_err(|e| read_error(&artifact_path, e.into()))
52        .and_then(|file| {
53            serde_json::from_reader(file).map_err(|e| read_error(&artifact_path, e.into()))
54        })?;
55
56    Ok(EvmHalo2Verifier {
57        halo2_verifier_code,
58        openvm_verifier_code,
59        openvm_verifier_interface: interface,
60        artifact,
61    })
62}
63
64/// Writes three Solidity contracts into the following folder structure:
65///
66/// ```text
67/// halo2/
68/// └── src/
69///     └── v[OPENVM_VERSION]/
70///         ├── interfaces/
71///         │   └── IOpenVmHalo2Verifier.sol
72///         ├── OpenVmHalo2Verifier.sol
73///         └── Halo2Verifier.sol
74/// ```
75///
76/// If `version_dir` is `Some`, it replaces the default `v[OPENVM_VERSION]` directory name.
77/// If the relevant directories do not exist, they will be created.
78#[cfg(feature = "evm-prove")]
79pub fn write_evm_halo2_verifier_to_folder<P: AsRef<Path>>(
80    verifier: EvmHalo2Verifier,
81    folder: P,
82    version_dir: Option<&str>,
83) -> Result<()> {
84    let folder = folder
85        .as_ref()
86        .join("src")
87        .join(version_dir.map_or_else(|| format!("v{OPENVM_VERSION}"), str::to_string));
88    if !folder.exists() {
89        create_dir_all(&folder)?; // Make sure directories exist
90    }
91
92    let halo2_verifier_code_path = folder.join(EVM_HALO2_VERIFIER_PARENT_NAME);
93    let openvm_verifier_code_path = folder.join(EVM_HALO2_VERIFIER_BASE_NAME);
94    let interface_path = folder
95        .join("interfaces")
96        .join(EVM_HALO2_VERIFIER_INTERFACE_NAME);
97
98    if let Some(parent) = interface_path.parent() {
99        create_dir_all(parent)?;
100    }
101
102    write(halo2_verifier_code_path, verifier.halo2_verifier_code)
103        .expect("Failed to write halo2 verifier code");
104    write(openvm_verifier_code_path, verifier.openvm_verifier_code)
105        .expect("Failed to write openvm halo2 verifier code");
106    write(interface_path, verifier.openvm_verifier_interface)
107        .expect("Failed to write openvm halo2 verifier interface");
108
109    let artifact_path = folder.join(EVM_VERIFIER_ARTIFACT_FILENAME);
110    serde_json::to_writer(File::create(artifact_path)?, &verifier.artifact)?;
111
112    Ok(())
113}
114
115pub fn read_object_from_file<T: DeserializeOwned, P: AsRef<Path>>(path: P) -> Result<T> {
116    read_from_file_bitcode(path)
117}
118
119pub fn write_object_to_file<T: Serialize, P: AsRef<Path>>(path: P, data: T) -> Result<()> {
120    write_to_file_bitcode(path, data)
121}
122
123/// Writes a [`Halo2ProvingKey`] to `path` in the streaming Halo2 pk format.
124#[cfg(feature = "evm-prove")]
125pub fn write_halo2_pk_to_file<P: AsRef<Path>>(path: P, halo2_pk: &Halo2ProvingKey) -> Result<()> {
126    if let Some(parent) = path.as_ref().parent() {
127        create_dir_all(parent).map_err(|e| write_error(&path, e.into()))?;
128    }
129    let file = File::create(&path).map_err(|e| write_error(&path, e.into()))?;
130    let mut writer = BufWriter::new(file);
131    halo2_pk
132        .encode(&mut writer)
133        .map_err(|e| write_error(&path, e.into()))?;
134    writer.flush().map_err(|e| write_error(&path, e.into()))?;
135    Ok(())
136}
137
138/// Reads a [`Halo2ProvingKey`] written by [`write_halo2_pk_to_file`].
139#[cfg(feature = "evm-prove")]
140pub fn read_halo2_pk_from_file<P: AsRef<Path>>(path: P) -> Result<Halo2ProvingKey> {
141    let file = File::open(&path).map_err(|e| read_error(&path, e.into()))?;
142    let mut reader = BufReader::new(file);
143    Halo2ProvingKey::decode(&mut reader).map_err(|e| read_error(&path, e.into()))
144}
145
146fn read_from_file_bitcode<T: DeserializeOwned, P: AsRef<Path>>(path: P) -> Result<T> {
147    let ret = read(&path)
148        .map_err(|e| read_error(&path, e.into()))
149        .and_then(|data| {
150            bitcode::deserialize(&data).map_err(|e: bitcode::Error| read_error(&path, e.into()))
151        })?;
152    Ok(ret)
153}
154
155fn write_to_file_bitcode<T: Serialize, P: AsRef<Path>>(path: P, data: T) -> Result<()> {
156    if let Some(parent) = path.as_ref().parent() {
157        create_dir_all(parent).map_err(|e| write_error(&path, e.into()))?;
158    }
159    bitcode::serialize(&data)
160        .map_err(|e| write_error(&path, e.into()))
161        .and_then(|bytes| write(&path, bytes).map_err(|e| write_error(&path, e.into())))?;
162    Ok(())
163}
164
165pub fn read_from_file_json<T: DeserializeOwned, P: AsRef<Path>>(path: P) -> Result<T> {
166    let ret: T = File::open(&path)
167        .and_then(|file| serde_json::from_reader(file).map_err(|e| e.into()))
168        .map_err(|e| read_error(&path, e.into()))?;
169    Ok(ret)
170}
171
172pub fn write_to_file_json<T: Serialize, P: AsRef<Path>>(path: P, data: T) -> Result<()> {
173    if let Some(parent) = path.as_ref().parent() {
174        create_dir_all(parent).map_err(|e| write_error(&path, e.into()))?;
175    }
176    File::create(&path)
177        .and_then(|file| serde_json::to_writer_pretty(file, &data).map_err(|e| e.into()))
178        .map_err(|e| write_error(&path, e.into()))?;
179    Ok(())
180}
181
182pub fn read_from_file_bytes<T: From<Vec<u8>>, P: AsRef<Path>>(path: P) -> Result<T> {
183    let bytes = read(&path).map_err(|e| read_error(&path, e.into()))?;
184    Ok(T::from(bytes))
185}
186
187pub fn write_to_file_bytes<T: Into<Vec<u8>>, P: AsRef<Path>>(path: P, data: T) -> Result<()> {
188    if let Some(parent) = path.as_ref().parent() {
189        create_dir_all(parent)?;
190    }
191    write(path, data.into())?;
192    Ok(())
193}
194
195pub fn decode_from_file<T: Decode, P: AsRef<Path>>(path: P) -> Result<T> {
196    let reader = &mut File::open(&path).map_err(|e| read_error(&path, e.into()))?;
197    let ret = T::decode(reader).map_err(|e| read_error(&path, e.into()))?;
198    Ok(ret)
199}
200
201pub fn encode_to_file<T: Encode, P: AsRef<Path>>(path: P, data: T) -> Result<()> {
202    if let Some(parent) = path.as_ref().parent() {
203        create_dir_all(parent)?;
204    }
205    let writer = &mut File::create(path)?;
206    data.encode(writer)?;
207    Ok(())
208}
209
210fn read_error<P: AsRef<Path>>(path: P, error: Report) -> Report {
211    eyre::eyre!(
212        "reading from {} failed with the following error:\n    {}",
213        path.as_ref().display(),
214        error,
215    )
216}
217
218fn write_error<P: AsRef<Path>>(path: P, error: Report) -> Report {
219    eyre::eyre!(
220        "writing to {} failed with the following error:\n    {}",
221        path.as_ref().display(),
222        error,
223    )
224}