mina_tree/proofs/circuit_blobs.rs
1//! # Circuit Constraint Extraction
2//!
3//! This module handles circuit constraint extraction for the Mina Rust node.
4//! Circuit constraints are sourced from the
5//! [circuit-blobs](https://github.com/o1-labs/circuit-blobs) repository, which
6//! contains pre-compiled circuit data generated by the OCaml implementation.
7//!
8//! ## Overview
9//!
10//! The Mina Rust node automatically handles downloading and caching circuit
11//! files, making the process transparent to users. When you run the node or
12//! generate proofs, the system will automatically fetch the required circuit
13//! data if it's not already available locally.
14//!
15//! ## Circuit Data Files
16//!
17//! The circuit-blobs repository contains three types of files for each circuit:
18//!
19//! ### Gates Definition (`*_gates.json`)
20//!
21//! - JSON files containing the list of gates for each circuit
22//! - Define the circuit structure and operations
23//! - Used for generating proving and verification keys
24//!
25//! ### Internal Variables (`*_internal_vars.bin`)
26//!
27//! - Binary files containing internal variable mappings
28//! - Store relationships between field elements and variable indices
29//! - Essential for witness generation
30//!
31//! ### Row Reversal Data (`*_rows_rev.bin`)
32//!
33//! - Binary files containing constraint system data
34//! - Used for efficient constraint evaluation
35//! - Required for proof generation
36//!
37//! ## Extraction Process
38//!
39//! The Mina Rust node automatically handles circuit constraint extraction
40//! through this module's [`fetch_blocking`] function.
41//!
42//! ### Local Resolution
43//!
44//! The system searches for circuit files in these locations (in order):
45//!
46//! 1. **Environment Variable**: `MINA_CIRCUIT_BLOBS_BASE_DIR`
47//! 2. **Cargo Manifest Directory**: Current project directory
48//! 3. **Home Directory**: `~/.mina/circuit-blobs/`
49//! 4. **System Directory**: `/usr/local/lib/mina/circuit-blobs`
50//!
51//! ### Remote Fetching
52//!
53//! If files are not found locally, the system automatically downloads them from:
54//!
55//! ```text
56//! https://github.com/o1-labs/circuit-blobs/releases/download/<filename>
57//! ```
58//!
59//! Downloaded files are cached locally for future use.
60//!
61//! ## Web Environment
62//!
63//! For web deployments, circuit files are served statically:
64//!
65//! - **Frontend Path**: `/assets/webnode/circuit-blobs/<version>/`
66//! - **Version**: Specified by `CIRCUITS_VERSION` environment variable
67//!
68//! ## How It Works for Users
69//!
70//! ### First Run Experience
71//!
72//! When you first run the Mina Rust node or generate proofs:
73//!
74//! 1. **Automatic Detection**: The system checks for required circuit files
75//! 2. **Local Search**: Searches in these locations (in order):
76//! - `$MINA_CIRCUIT_BLOBS_BASE_DIR` (if set)
77//! - Current project directory
78//! - `~/.mina/circuit-blobs/`
79//! - `/usr/local/lib/mina/circuit-blobs`
80//! 3. **Automatic Download**: If files aren't found locally, downloads from GitHub
81//! 4. **Local Caching**: Saves downloaded files to `~/.mina/circuit-blobs/`
82//! 5. **Ready to Use**: Circuit data is loaded and ready for proof generation
83//!
84//! ### Subsequent Runs
85//!
86//! - Uses cached files from `~/.mina/circuit-blobs/`
87//! - No network requests needed
88//! - Fast startup times
89//!
90//! ## Development Guidelines
91//!
92//! ### Adding New Circuit Types
93//!
94//! 1. Generate circuit data using OCaml implementation
95//! 2. Add files to circuit-blobs repository
96//! 3. Update circuit configuration in `mina_core::network`
97//! 4. Add prover creation logic in [`crate::proofs::provers`]
98//!
99//! ### Local Development
100//!
101//! Set `MINA_CIRCUIT_BLOBS_BASE_DIR` environment variable to point to your
102//! local circuit-blobs repository clone:
103//!
104//! ```bash
105//! export MINA_CIRCUIT_BLOBS_BASE_DIR=/path/to/your/circuit-blobs
106//! ```
107//!
108//! ## Related Modules
109//!
110//! - [`crate::proofs::provers`]: Prover creation using circuit data
111//! - [`crate::proofs::constants`]: Circuit type definitions
112
113use std::path::Path;
114
115#[cfg(not(target_family = "wasm"))]
116pub fn home_base_dir() -> Option<std::path::PathBuf> {
117 let mut path = std::path::PathBuf::from(std::env::var("HOME").ok()?);
118 path.push(".mina/circuit-blobs");
119 Some(path)
120}
121
122fn git_release_url(filename: &impl AsRef<Path>) -> String {
123 const RELEASES_PATH: &str = "https://github.com/o1-labs/circuit-blobs/releases/download";
124 let filename_str = filename.as_ref().to_str().unwrap();
125
126 format!("{RELEASES_PATH}/{filename_str}")
127}
128
129#[cfg(not(target_family = "wasm"))]
130pub fn fetch_blocking(filename: &impl AsRef<Path>) -> std::io::Result<Vec<u8>> {
131 use std::path::PathBuf;
132
133 fn try_base_dir<P: Into<PathBuf>>(base_dir: P, filename: &impl AsRef<Path>) -> Option<PathBuf> {
134 let mut path = base_dir.into();
135 path.push(filename);
136 path.exists().then_some(path)
137 }
138
139 fn to_io_err(err: impl std::fmt::Display) -> std::io::Error {
140 std::io::Error::new(
141 std::io::ErrorKind::Other,
142 format!(
143 "failed to find circuit-blobs locally and to fetch the from github! error: {err}"
144 ),
145 )
146 }
147
148 let home_base_dir = home_base_dir();
149 let found = None
150 .or_else(|| try_base_dir(std::env::var("MINA_CIRCUIT_BLOBS_BASE_DIR").ok()?, filename))
151 .or_else(|| try_base_dir(env!("CARGO_MANIFEST_DIR").to_string(), filename))
152 .or_else(|| try_base_dir(home_base_dir.clone()?, filename))
153 .or_else(|| try_base_dir("/usr/local/lib/mina/circuit-blobs", filename));
154
155 if let Some(path) = found {
156 return std::fs::read(path);
157 }
158
159 mina_core::info!(
160 mina_core::log::system_time();
161 kind = "ledger proofs",
162 message = "circuit-blobs not found locally, so fetching it...",
163 filename = filename.as_ref().to_str().unwrap(),
164 );
165
166 let base_dir = home_base_dir.expect("$HOME env not set!");
167
168 let bytes = reqwest::blocking::get(git_release_url(filename))
169 .map_err(to_io_err)?
170 .bytes()
171 .map_err(to_io_err)?
172 .to_vec();
173
174 // cache it to home dir.
175 let cache_path = base_dir.join(filename);
176 mina_core::info!(
177 mina_core::log::system_time();
178 kind = "ledger proofs",
179 message = "caching circuit-blobs",
180 path = cache_path.to_str().unwrap(),
181 );
182 let _ = std::fs::create_dir_all(cache_path.parent().unwrap());
183 let _ = std::fs::write(cache_path, &bytes);
184
185 Ok(bytes)
186}
187
188#[cfg(target_family = "wasm")]
189pub async fn fetch(filename: &impl AsRef<Path>) -> std::io::Result<Vec<u8>> {
190 let prefix =
191 option_env!("CIRCUIT_BLOBS_HTTP_PREFIX").unwrap_or("/assets/webnode/circuit-blobs");
192 let url = format!("{prefix}/{}", filename.as_ref().to_str().unwrap());
193 mina_core::http::get_bytes(&url).await
194}
195
196#[cfg(target_family = "wasm")]
197pub fn fetch_blocking(filename: &impl AsRef<Path>) -> std::io::Result<Vec<u8>> {
198 let prefix =
199 option_env!("CIRCUIT_BLOBS_HTTP_PREFIX").unwrap_or("/assets/webnode/circuit-blobs");
200 let url = format!("{prefix}/{}", filename.as_ref().to_str().unwrap());
201 mina_core::http::get_bytes_blocking(&url)
202}