cargo_openvm/commands/
setup.rs1use std::{
2 fs::{create_dir_all, write},
3 path::{Path, PathBuf},
4};
5
6use aws_config::{defaults, BehaviorVersion, Region};
7use aws_sdk_s3::Client;
8use clap::Parser;
9use eyre::{eyre, Context, Result};
10use openvm_sdk::{
11 config::AggregationSystemParams,
12 fs::{
13 write_object_to_file, EVM_HALO2_VERIFIER_BASE_NAME, EVM_HALO2_VERIFIER_INTERFACE_NAME,
14 EVM_HALO2_VERIFIER_PARENT_NAME, EVM_VERIFIER_ARTIFACT_FILENAME,
15 },
16 Sdk,
17};
18
19use crate::{
20 default::{
21 default_app_config, default_evm_halo2_verifier_path, default_internal_recursive_pk_path,
22 default_internal_recursive_vk_path, default_params_dir, default_root_pk_path,
23 },
24 util::evm_verifier_version_dir,
25};
26
27const MAX_HALO2_VERIFIER_K_FOR_DOWNLOAD: usize = 24;
30
31#[derive(Parser)]
32#[command(name = "setup", about = "Set up OpenVM recursive proving artifacts.")]
33pub struct SetupCmd {
34 #[arg(
35 long,
36 default_value = "false",
37 help = "Force verifier regeneration even if the verifier artifacts already exist"
38 )]
39 pub force: bool,
40
41 #[arg(
42 long,
43 default_value = "false",
44 help = "Also cache the root proving key and download EVM verifier artifacts. ATTENTION: this requires large amounts of computation and memory."
45 )]
46 pub evm: bool,
47
48 #[arg(
49 long,
50 default_value = "false",
51 help = "Download pre-built EVM verifier artifacts from S3 instead of generating locally"
52 )]
53 pub download: bool,
54}
55
56impl SetupCmd {
57 pub async fn run(&self) -> Result<()> {
58 let sdk = Sdk::new(default_app_config(), AggregationSystemParams::default())?;
59
60 let internal_recursive_pk = sdk.agg_pk().internal_recursive;
61 let internal_recursive_vk = internal_recursive_pk.get_vk();
62
63 let internal_recursive_pk_path = PathBuf::from(default_internal_recursive_pk_path());
64 println!(
65 "Writing internal-recursive proving key to {}",
66 internal_recursive_pk_path.display()
67 );
68 write_object_to_file(&internal_recursive_pk_path, internal_recursive_pk)?;
69
70 let internal_recursive_vk_path = PathBuf::from(default_internal_recursive_vk_path());
71 println!(
72 "Writing internal-recursive verifying key to {}",
73 internal_recursive_vk_path.display()
74 );
75 write_object_to_file(&internal_recursive_vk_path, internal_recursive_vk)?;
76
77 if !self.evm {
78 return Ok(());
79 }
80
81 let root_pk_path = PathBuf::from(default_root_pk_path());
82 println!("Writing root proving key to {}", root_pk_path.display());
83 write_object_to_file(&root_pk_path, sdk.root_pk())?;
84
85 Self::download_params(10, MAX_HALO2_VERIFIER_K_FOR_DOWNLOAD as u32).await?;
86
87 let halo2_pk_path = PathBuf::from(crate::default::default_halo2_pk_path());
88 if !self.force && halo2_pk_path.exists() {
89 println!(
90 "Halo2 proving key already exists in {}",
91 halo2_pk_path.display()
92 );
93 } else if self.download {
94 Self::download_halo2_pk(&halo2_pk_path).await?;
95 } else {
96 Self::generate_halo2_pk(&sdk, &halo2_pk_path)?;
97 }
98
99 let verifier_dir = PathBuf::from(default_evm_halo2_verifier_path());
100 let versioned_verifier_dir = verifier_dir.join("src").join(evm_verifier_version_dir());
101 if !self.force && Self::verifier_artifacts_exist(&versioned_verifier_dir) {
102 println!(
103 "EVM verifier artifacts already exist in {}",
104 verifier_dir.display()
105 );
106 } else if self.download {
107 Self::download_verifier(&versioned_verifier_dir).await?;
108 } else {
109 Self::generate_verifier(&sdk, &verifier_dir)?;
110 }
111
112 Ok(())
113 }
114
115 fn verifier_artifacts_exist(versioned_verifier_dir: &Path) -> bool {
116 versioned_verifier_dir
117 .join(EVM_HALO2_VERIFIER_PARENT_NAME)
118 .exists()
119 && versioned_verifier_dir
120 .join(EVM_HALO2_VERIFIER_BASE_NAME)
121 .exists()
122 && versioned_verifier_dir
123 .join(EVM_VERIFIER_ARTIFACT_FILENAME)
124 .exists()
125 && versioned_verifier_dir
126 .join("interfaces")
127 .join(EVM_HALO2_VERIFIER_INTERFACE_NAME)
128 .exists()
129 }
130
131 fn check_solc_installed() -> bool {
132 std::process::Command::new("solc")
133 .arg("--version")
134 .output()
135 .is_ok()
136 }
137
138 fn generate_verifier(sdk: &Sdk, verifier_dir: &Path) -> Result<()> {
139 if !Self::check_solc_installed() {
140 return Err(eyre!(
141 "solc is not installed, please install solc or rerun with --download"
142 ));
143 }
144
145 #[cfg(feature = "evm-verify")]
146 {
147 use openvm_sdk::fs::write_evm_halo2_verifier_to_folder;
148
149 println!("Generating verifier contract locally. Tip: use `--download` to download pre-built artifacts from S3 instead.");
150 let version_dir = evm_verifier_version_dir();
151 let verifier = sdk.generate_halo2_verifier_solidity_with_version_name(&version_dir)?;
152 println!("Writing verifier contract to {}", verifier_dir.display());
153 write_evm_halo2_verifier_to_folder(verifier, verifier_dir, Some(&version_dir))?;
154 Ok(())
155 }
156
157 #[cfg(not(feature = "evm-verify"))]
158 {
159 let _ = sdk;
160 let _ = verifier_dir;
161 Err(eyre!(
162 "this cargo-openvm build does not include local EVM verifier generation support; rerun with --download"
163 ))
164 }
165 }
166
167 fn generate_halo2_pk(sdk: &Sdk, halo2_pk_path: &Path) -> Result<()> {
168 #[cfg(feature = "evm-prove")]
169 {
170 use openvm_sdk::fs::write_halo2_pk_to_file;
171
172 println!("Generating Halo2 proving key locally. Tip: use `--download` to download a pre-built key from S3 instead.");
173 println!("Writing Halo2 proving key to {}", halo2_pk_path.display());
174 write_halo2_pk_to_file(halo2_pk_path, &sdk.halo2_pk())?;
175 Ok(())
176 }
177
178 #[cfg(not(feature = "evm-prove"))]
179 {
180 let _ = sdk;
181 let _ = halo2_pk_path;
182 Err(eyre!(
183 "this cargo-openvm build does not include local Halo2 proving key generation support; rerun with --download"
184 ))
185 }
186 }
187
188 async fn download_verifier(versioned_verifier_dir: &PathBuf) -> Result<()> {
189 create_dir_all(versioned_verifier_dir)?;
190 let interface_dir = versioned_verifier_dir.join("interfaces");
191 create_dir_all(&interface_dir)?;
192
193 let config = defaults(BehaviorVersion::latest())
194 .region(Region::new("us-east-1"))
195 .no_credentials()
196 .load()
197 .await;
198 let client = Client::new(&config);
199
200 const ARTIFACTS_BUCKET: &str = "openvm-public-artifacts-us-east-1";
201 const FULL_VERSION: &str = env!("CARGO_PKG_VERSION");
202
203 let halo2_s3_prefix = format!("v{FULL_VERSION}/halo2/src/{}", evm_verifier_version_dir());
204 let files = [
205 (
206 EVM_HALO2_VERIFIER_PARENT_NAME,
207 versioned_verifier_dir.join(EVM_HALO2_VERIFIER_PARENT_NAME),
208 ),
209 (
210 EVM_HALO2_VERIFIER_BASE_NAME,
211 versioned_verifier_dir.join(EVM_HALO2_VERIFIER_BASE_NAME),
212 ),
213 (
214 EVM_VERIFIER_ARTIFACT_FILENAME,
215 versioned_verifier_dir.join(EVM_VERIFIER_ARTIFACT_FILENAME),
216 ),
217 (
218 EVM_HALO2_VERIFIER_INTERFACE_NAME,
219 interface_dir.join(EVM_HALO2_VERIFIER_INTERFACE_NAME),
220 ),
221 ];
222
223 for (name, local_path) in &files {
224 let key = if *name == EVM_HALO2_VERIFIER_INTERFACE_NAME {
225 format!("{halo2_s3_prefix}/interfaces/{name}")
226 } else {
227 format!("{halo2_s3_prefix}/{name}")
228 };
229 println!("Downloading {name}");
230 let resp = client
231 .get_object()
232 .bucket(ARTIFACTS_BUCKET)
233 .key(&key)
234 .send()
235 .await
236 .map_err(|e| eyre!("Failed to download s3://{ARTIFACTS_BUCKET}/{key}: {e}"))?;
237 let data = resp.body.collect().await?;
238 write(local_path, data.into_bytes())?;
239 }
240
241 Ok(())
242 }
243
244 async fn download_halo2_pk(halo2_pk_path: &Path) -> Result<()> {
245 if let Some(parent) = halo2_pk_path.parent() {
246 create_dir_all(parent)?;
247 }
248
249 let config = defaults(BehaviorVersion::latest())
250 .region(Region::new("us-east-1"))
251 .no_credentials()
252 .load()
253 .await;
254 let client = Client::new(&config);
255
256 const ARTIFACTS_BUCKET: &str = "openvm-public-artifacts-us-east-1";
257 const FULL_VERSION: &str = env!("CARGO_PKG_VERSION");
258
259 let key = format!("v{FULL_VERSION}/halo2.pk");
260 println!(
261 "Downloading Halo2 proving key to {}",
262 halo2_pk_path.display()
263 );
264 let resp = client
265 .get_object()
266 .bucket(ARTIFACTS_BUCKET)
267 .key(&key)
268 .send()
269 .await
270 .map_err(|e| eyre!("Failed to download s3://{ARTIFACTS_BUCKET}/{key}: {e}"))?;
271 let data = resp.body.collect().await?;
272 write(halo2_pk_path, data.into_bytes())?;
273
274 Ok(())
275 }
276
277 async fn download_params(min_k: u32, max_k: u32) -> Result<()> {
278 let default_params_dir = default_params_dir();
279 create_dir_all(&default_params_dir)
280 .with_context(|| format!("failed to create params directory {default_params_dir}"))?;
281
282 let config = defaults(BehaviorVersion::latest())
283 .region(Region::new("us-east-1"))
284 .no_credentials()
285 .load()
286 .await;
287 let client = Client::new(&config);
288
289 for k in min_k..=max_k {
290 let file_name = format!("kzg_bn254_{k}.srs");
291 let local_file_path = PathBuf::from(&default_params_dir).join(&file_name);
292 if !local_file_path.exists() {
293 println!("Downloading {file_name}");
294 let key = format!("challenge_0085/{file_name}");
295 let resp = client
296 .get_object()
297 .bucket("axiom-crypto")
298 .key(&key)
299 .send()
300 .await
301 .with_context(|| format!("failed to download {file_name} from S3"))?;
302 let data = resp
303 .body
304 .collect()
305 .await
306 .with_context(|| format!("failed to download {file_name} body from S3"))?;
307 write(&local_file_path, data.into_bytes())
308 .with_context(|| format!("failed to write {}", local_file_path.display()))?;
309 }
310 }
311
312 Ok(())
313 }
314}