1use crate::errors;
19use crate::utils::compute_split_vars;
20use core::fmt;
21use tracing::warn;
22
23#[derive(Clone, Copy, Debug, Eq, PartialEq)]
25pub enum Error {
26 SecurityTooLow {
28 estimated_bits: usize,
29 min_bits: usize,
30 },
31}
32
33impl fmt::Display for Error {
34 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35 match self {
36 Self::SecurityTooLow {
37 estimated_bits,
38 min_bits,
39 } => write!(
40 f,
41 "Security too low: estimated {estimated_bits} bits, but {min_bits} required",
42 ),
43 }
44 }
45}
46
47#[derive(Clone, Copy, Debug)]
49pub struct SecurityMetrics {
50 pub relative_distance: f64,
53
54 pub num_queries: usize,
56
57 pub soundness_error: f64,
60
61 pub ldt_bits: usize,
64
65 pub security_bits: usize,
68
69 pub expansion_degree: usize,
72}
73
74#[derive(Clone, Debug)]
75pub struct Config {
76 pub expansion_degree: usize,
79
80 pub num_queries: usize,
82
83 pub matrix_seed: [u8; 32],
86
87 pub sumcheck_blinding_factor: usize,
90
91 pub ldt_blinding_factor: usize,
95
96 pub min_security_bits: usize,
99}
100
101impl Default for Config {
102 fn default() -> Self {
103 Self {
104 expansion_degree: 16,
105 num_queries: 160,
106 matrix_seed: [42u8; 32],
107 min_security_bits: 99,
108 sumcheck_blinding_factor: 2,
109 ldt_blinding_factor: 200,
110 }
111 }
112}
113
114impl Config {
115 pub fn estimated_security_bits(&self, field_bits: usize) -> usize {
120 let delta = self.estimate_relative_distance();
121 let q = self.num_queries as f64;
122
123 let soundness_error = (1.0 - delta).powf(q);
124 let ldt_bits = (-soundness_error.log2()).floor() as usize;
125
126 ldt_bits.min(field_bits)
127 }
128
129 pub fn security_metrics(&self, field_bits: usize) -> SecurityMetrics {
131 let delta = self.estimate_relative_distance();
132 let q = self.num_queries as f64;
133 let soundness_error = (1.0 - delta).powf(q);
134 let ldt_bits = (-soundness_error.log2()).floor() as usize;
135
136 SecurityMetrics {
137 relative_distance: delta,
138 num_queries: self.num_queries,
139 soundness_error,
140 ldt_bits,
141 security_bits: ldt_bits.min(field_bits),
142 expansion_degree: self.expansion_degree,
143 }
144 }
145
146 pub fn check_security(&self, num_vars: usize, field_bits: usize) -> errors::Result<()> {
150 let split_vars = compute_split_vars(num_vars, self.num_queries);
151 let grid_cols = 1usize << split_vars;
152
153 if grid_cols > 0 && grid_cols < 128 && self.min_security_bits > 40 {
156 warn!("Grid width ({grid_cols}) too small for random expander guarantees");
157 }
158
159 if grid_cols > 0 && self.expansion_degree > grid_cols / 4 {
161 warn!(
162 "Expansion degree ({}) too large for grid width ({}), need < {}",
163 self.expansion_degree,
164 grid_cols,
165 grid_cols / 4
166 );
167 }
168
169 let est_bits = self.estimated_security_bits(field_bits);
170 if est_bits < self.min_security_bits {
171 return Err(Error::SecurityTooLow {
172 estimated_bits: est_bits,
173 min_bits: self.min_security_bits,
174 }
175 .into());
176 }
177
178 Ok(())
179 }
180
181 fn estimate_relative_distance(&self) -> f64 {
186 let d = self.expansion_degree as f64;
187 if d < 2.0 {
188 return 0.01;
189 }
190
191 let sqrt_term = 2.0 * (d - 1.0).sqrt();
192 let theoretical_delta = (d - sqrt_term) / d;
193
194 let correction_factor = if d >= 64.0 {
197 0.95
198 } else if d >= 32.0 {
199 0.90
200 } else if d >= 16.0 {
201 0.85
203 } else if d >= 8.0 {
204 0.75
205 } else {
206 0.60
207 };
208
209 (theoretical_delta * correction_factor).max(0.01)
210 }
211}