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//! - **Download Script**: `frontend/docker/startup.sh` handles automatic
67//! downloading
68//! - **Version**: Specified by `CIRCUITS_VERSION` environment variable
69//!
70//! ## How It Works for Users
71//!
72//! ### First Run Experience
73//!
74//! When you first run the Mina Rust node or generate proofs:
75//!
76//! 1. **Automatic Detection**: The system checks for required circuit files
77//! 2. **Local Search**: Searches in these locations (in order):
78//! - `$MINA_CIRCUIT_BLOBS_BASE_DIR` (if set)
79//! - Current project directory
80//! - `~/.mina/circuit-blobs/`
81//! - `/usr/local/lib/mina/circuit-blobs`
82//! 3. **Automatic Download**: If files aren't found locally, downloads from GitHub
83//! 4. **Local Caching**: Saves downloaded files to `~/.mina/circuit-blobs/`
84//! 5. **Ready to Use**: Circuit data is loaded and ready for proof generation
85//!
86//! ### Subsequent Runs
87//!
88//! - Uses cached files from `~/.mina/circuit-blobs/`
89//! - No network requests needed
90//! - Fast startup times
91//!
92//! ## Development Guidelines
93//!
94//! ### Adding New Circuit Types
95//!
96//! 1. Generate circuit data using OCaml implementation
97//! 2. Add files to circuit-blobs repository
98//! 3. Update circuit configuration in `mina_core::network`
99//! 4. Add prover creation logic in [`crate::proofs::provers`]
100//!
101//! ### Local Development
102//!
103//! Set `MINA_CIRCUIT_BLOBS_BASE_DIR` environment variable to point to your
104//! local circuit-blobs repository clone:
105//!
106//! ```bash
107//! export MINA_CIRCUIT_BLOBS_BASE_DIR=/path/to/your/circuit-blobs
108//! ```
109//!
110//! ## Related Modules
111//!
112//! - [`crate::proofs::provers`]: Prover creation using circuit data
113//! - [`crate::proofs::constants`]: Circuit type definitions
114
115use std::path::Path;
116
117#[cfg(not(target_family = "wasm"))]
118pub fn home_base_dir() -> Option<std::path::PathBuf> {
119 let mut path = std::path::PathBuf::from(std::env::var("HOME").ok()?);
120 path.push(".mina/circuit-blobs");
121 Some(path)
122}
123
124fn git_release_url(filename: &impl AsRef<Path>) -> String {
125 const RELEASES_PATH: &str = "https://github.com/o1-labs/circuit-blobs/releases/download";
126 let filename_str = filename.as_ref().to_str().unwrap();
127
128 format!("{RELEASES_PATH}/{filename_str}")
129}
130
131#[cfg(not(target_family = "wasm"))]
132pub fn fetch_blocking(filename: &impl AsRef<Path>) -> std::io::Result<Vec<u8>> {
133 use std::path::PathBuf;
134
135 fn try_base_dir<P: Into<PathBuf>>(base_dir: P, filename: &impl AsRef<Path>) -> Option<PathBuf> {
136 let mut path = base_dir.into();
137 path.push(filename);
138 path.exists().then_some(path)
139 }
140
141 fn to_io_err(err: impl std::fmt::Display) -> std::io::Error {
142 std::io::Error::new(
143 std::io::ErrorKind::Other,
144 format!(
145 "failed to find circuit-blobs locally and to fetch the from github! error: {err}"
146 ),
147 )
148 }
149
150 let home_base_dir = home_base_dir();
151 let found = None
152 .or_else(|| try_base_dir(std::env::var("MINA_CIRCUIT_BLOBS_BASE_DIR").ok()?, filename))
153 .or_else(|| try_base_dir(env!("CARGO_MANIFEST_DIR").to_string(), filename))
154 .or_else(|| try_base_dir(home_base_dir.clone()?, filename))
155 .or_else(|| try_base_dir("/usr/local/lib/mina/circuit-blobs", filename));
156
157 if let Some(path) = found {
158 return std::fs::read(path);
159 }
160
161 mina_core::info!(
162 mina_core::log::system_time();
163 kind = "ledger proofs",
164 message = "circuit-blobs not found locally, so fetching it...",
165 filename = filename.as_ref().to_str().unwrap(),
166 );
167
168 let base_dir = home_base_dir.expect("$HOME env not set!");
169
170 let bytes = reqwest::blocking::get(git_release_url(filename))
171 .map_err(to_io_err)?
172 .bytes()
173 .map_err(to_io_err)?
174 .to_vec();
175
176 // cache it to home dir.
177 let cache_path = base_dir.join(filename);
178 mina_core::info!(
179 mina_core::log::system_time();
180 kind = "ledger proofs",
181 message = "caching circuit-blobs",
182 path = cache_path.to_str().unwrap(),
183 );
184 let _ = std::fs::create_dir_all(cache_path.parent().unwrap());
185 let _ = std::fs::write(cache_path, &bytes);
186
187 Ok(bytes)
188}
189
190#[cfg(target_family = "wasm")]
191pub async fn fetch(filename: &impl AsRef<Path>) -> std::io::Result<Vec<u8>> {
192 let prefix =
193 option_env!("CIRCUIT_BLOBS_HTTP_PREFIX").unwrap_or("/assets/webnode/circuit-blobs");
194 let url = format!("{prefix}/{}", filename.as_ref().to_str().unwrap());
195 mina_core::http::get_bytes(&url).await
196 // http::get_bytes(&git_release_url(filename)).await
197}
198
199#[cfg(target_family = "wasm")]
200pub fn fetch_blocking(filename: &impl AsRef<Path>) -> std::io::Result<Vec<u8>> {
201 let prefix =
202 option_env!("CIRCUIT_BLOBS_HTTP_PREFIX").unwrap_or("/assets/webnode/circuit-blobs");
203 let url = format!("{prefix}/{}", filename.as_ref().to_str().unwrap());
204 mina_core::http::get_bytes_blocking(&url)
205}