-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathpaths.rs
More file actions
188 lines (171 loc) · 7.62 KB
/
Copy pathpaths.rs
File metadata and controls
188 lines (171 loc) · 7.62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
//! Explicit configuration for halo2 on-disk artifacts.
//!
//! `prove_voucher_for_event` needs three directories on disk:
//!
//! 1. **SRS** — `kzg_bn254_19.srs` (~64 MB), KZG parameter blob. Reproducible
//! from a fixed seed via `halo2_base::gen_srs` (see `ensure_srs`), so the
//! dev deploy tooling can generate it on first use. A halo2 proving host on
//! a real network would instead supply one from a genuine trusted-setup
//! ceremony. (The dodex `api` / `indexer` never use halo2, so none of this
//! applies to deploying the backend.)
//! 2. **Prover cache** — `pk_cache.bin` (~464 MB) + `vk_cache.bin` +
//! `break_points_cache.bin`. Read-write, persistent. Lost cache means one
//! ~5-minute keygen on the next call.
//! 3. **Fixture dir** — `dex_fixture_live_L{layer}_H{height}_T{nanos}.json`
//! witness dumps written per-proof. Read-write, ephemeral; safe to wipe
//! between runs.
//!
//! On a host machine env vars + `./params/...` defaults are fine, but
//! mobile sandboxes (iOS / Android Tauri builds) need the host to pick
//! these paths at runtime — that's what this module enables. Configure
//! `Halo2Paths` once at boot, validate it, then thread it through every
//! `prove_voucher_for_event` call.
use std::path::Path;
use std::path::PathBuf;
/// SRS file degree we proof against (`2^K = 524288` rows). The SRS file
/// on disk must be named `kzg_bn254_{K}.srs`.
pub const SRS_K: u32 = 19;
/// Default sub-dirs used when the host doesn't override them. They mirror
/// the historical env-var defaults so existing tests / CLI keep working.
const DEFAULT_SRS_DIR: &str = "params";
const DEFAULT_PROVER_CACHE_DIR: &str = "params/halo2_cache";
const DEFAULT_FIXTURE_DIR: &str = "target/halo2_fixtures";
const ENV_SRS_DIR: &str = "PARAMS_DIR";
const ENV_PROVER_CACHE_DIR: &str = "HALO2_PK_CACHE";
const ENV_FIXTURE_DIR: &str = "HALO2_FIXTURE_DIR";
/// Filesystem layout for halo2 prover artifacts. Cheap to clone; hold one
/// instance for the lifetime of the host process.
#[derive(Debug, Clone)]
pub struct Halo2Paths {
/// Directory containing `kzg_bn254_{SRS_K}.srs`. Read-only at runtime.
pub srs_dir: PathBuf,
/// Directory for `pk_cache.bin` / `vk_cache.bin` /
/// `break_points_cache.bin`. Read-write, persistent.
pub prover_cache_dir: PathBuf,
/// Directory for ephemeral witness fixture JSONs.
/// Read-write, safe to wipe.
pub fixture_dir: PathBuf,
}
impl Halo2Paths {
/// Construct from env vars with the existing project defaults
/// (`PARAMS_DIR=./params`, `HALO2_PK_CACHE=./params/halo2_cache`,
/// `HALO2_FIXTURE_DIR=./target/halo2_fixtures`). Suitable for tests
/// and CLI; mobile / packaged hosts should build the struct
/// explicitly from `app_data_dir` / `cache_dir` instead.
pub fn from_env() -> Self {
Self {
srs_dir: env_path(ENV_SRS_DIR, DEFAULT_SRS_DIR),
prover_cache_dir: env_path(ENV_PROVER_CACHE_DIR, DEFAULT_PROVER_CACHE_DIR),
fixture_dir: env_path(ENV_FIXTURE_DIR, DEFAULT_FIXTURE_DIR),
}
}
/// Verify the SRS file exists and the writable directories can be
/// created. Call once at boot — turns deep halo2-internal panics
/// (file not found inside `read_or_create_srs`) into an upfront
/// `Result` the host can surface to the user.
pub fn validate(&self) -> Result<(), Halo2PathsError> {
let srs_path = self.srs_path();
if !srs_path.is_file() {
return Err(Halo2PathsError::SrsNotFound { path: srs_path });
}
ensure_writable_dir(&self.prover_cache_dir).map_err(|source| {
Halo2PathsError::ProverCacheDirNotWritable {
path: self.prover_cache_dir.clone(),
source,
}
})?;
ensure_writable_dir(&self.fixture_dir).map_err(|source| {
Halo2PathsError::FixtureDirNotWritable { path: self.fixture_dir.clone(), source }
})?;
Ok(())
}
/// Absolute path of the SRS file (`{srs_dir}/kzg_bn254_{SRS_K}.srs`).
pub fn srs_path(&self) -> PathBuf {
self.srs_dir.join(format!("kzg_bn254_{SRS_K}.srs"))
}
/// Whether the SRS file is already on disk.
pub fn srs_exists(&self) -> bool {
self.srs_path().is_file()
}
/// Generate the SRS if it is absent. `halo2_base::gen_srs` derives the
/// KZG parameters from a fixed seed, so the output is byte-for-byte
/// reproducible and matches the verifier key the on-chain contract was
/// built against — there is nothing environment-specific to source.
/// Generating on first use spares a fresh checkout (the `.srs` is
/// gitignored, ~64 MB) from sourcing the file out of band. CPU-bound
/// and one-time; the result is cached on disk under `srs_dir`.
///
/// This is for the dev/test deploy tooling (`mint_pn_pool` /
/// `mint_ob_pool`), which targets networks with a giver — not the dodex
/// `api` / `indexer`, which never use halo2 at all. A halo2 proving host
/// on a real network (e.g. a wallet minting vouchers) would instead supply
/// an SRS from a genuine trusted-setup ceremony and rely on `validate()`
/// to reject a missing one.
pub fn ensure_srs(&self) {
if self.srs_exists() {
return;
}
self.install_env();
let _ = halo2_base::utils::fs::gen_srs(SRS_K);
}
/// Apply this configuration to the global env so the third-party
/// halo2 lib (which reads `PARAMS_DIR` directly inside `gen_srs`)
/// finds the SRS at the host-supplied location.
///
/// Safety: mutating the process env is not thread-safe. Call once at
/// boot from a single thread, before any halo2 work starts. The
/// `unsafe` is `std::env::set_var`'s in Rust 2024+; we don't add
/// extra invariants beyond "no concurrent env mutation".
pub fn install_env(&self) {
let dir_str = self.srs_dir.to_string_lossy().into_owned();
// SAFETY: documented contract — single-threaded boot-time call.
unsafe {
std::env::set_var(ENV_SRS_DIR, dir_str);
}
}
}
impl Default for Halo2Paths {
fn default() -> Self {
Self::from_env()
}
}
#[derive(Debug)]
pub enum Halo2PathsError {
SrsNotFound { path: PathBuf },
ProverCacheDirNotWritable { path: PathBuf, source: std::io::Error },
FixtureDirNotWritable { path: PathBuf, source: std::io::Error },
}
impl std::fmt::Display for Halo2PathsError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::SrsNotFound { path } => {
write!(f, "SRS file not found at {}", path.display())
}
Self::ProverCacheDirNotWritable { path, source } => {
write!(f, "prover cache dir {} not writable: {source}", path.display())
}
Self::FixtureDirNotWritable { path, source } => {
write!(f, "fixture dir {} not writable: {source}", path.display())
}
}
}
}
impl std::error::Error for Halo2PathsError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::SrsNotFound { .. } => None,
Self::ProverCacheDirNotWritable { source, .. }
| Self::FixtureDirNotWritable { source, .. } => Some(source),
}
}
}
fn env_path(var: &str, default: &str) -> PathBuf {
std::env::var_os(var).map(PathBuf::from).unwrap_or_else(|| PathBuf::from(default))
}
fn ensure_writable_dir(path: &Path) -> std::io::Result<()> {
std::fs::create_dir_all(path)?;
let probe = path.join(".halo2_paths_probe");
std::fs::write(&probe, b"")?;
std::fs::remove_file(&probe)?;
Ok(())
}