p3_binary_pcs/whir/
budget.rs1use crate::whir::error::BudgetError;
4use crate::whir::shape::ProofShape;
5
6#[derive(Clone, Copy, Debug, PartialEq, Eq)]
10pub struct BinaryWhirBudget {
11 pub max_stir_queries: usize,
13
14 pub max_proof_bytes: usize,
16
17 pub max_grinding_bits: usize,
19}
20
21impl BinaryWhirBudget {
22 pub const PRODUCTION: Self = Self {
30 max_stir_queries: 200,
31 max_proof_bytes: 256 * 1024,
32 max_grinding_bits: 24,
33 };
34
35 pub const fn check_shape(
41 &self,
42 shape: &ProofShape,
43 base_element_bytes: usize,
44 extension_element_bytes: usize,
45 digest_bytes: usize,
46 ) -> Result<(), BudgetError> {
47 if shape.stir_queries > self.max_stir_queries {
48 return Err(BudgetError::Queries {
49 actual: shape.stir_queries,
50 budget: self.max_stir_queries,
51 });
52 }
53 if shape.grinding_bits > self.max_grinding_bits {
54 return Err(BudgetError::Grinding {
55 actual: shape.grinding_bits,
56 budget: self.max_grinding_bits,
57 });
58 }
59 let bytes = shape.max_bytes(base_element_bytes, extension_element_bytes, digest_bytes);
60 if bytes > self.max_proof_bytes {
61 return Err(BudgetError::Bytes {
62 actual: bytes,
63 budget: self.max_proof_bytes,
64 });
65 }
66 Ok(())
67 }
68
69 pub const fn check_bytes(&self, bytes: usize) -> Result<(), BudgetError> {
75 if bytes > self.max_proof_bytes {
76 return Err(BudgetError::Bytes {
77 actual: bytes,
78 budget: self.max_proof_bytes,
79 });
80 }
81 Ok(())
82 }
83}
84
85#[cfg(test)]
86mod tests {
87 use super::{BinaryWhirBudget, BudgetError, ProofShape};
88
89 const SHAPE: ProofShape = ProofShape {
90 stir_queries: 40,
91 opened_base_elements: 100,
92 opened_extension_elements: 200,
93 merkle_digests: 300,
94 sent_base_elements: 10,
95 sent_extension_elements: 50,
96 grinding_bits: 16,
97 };
98
99 #[test]
100 fn a_shape_inside_the_ceiling_is_accepted() {
101 BinaryWhirBudget::PRODUCTION
102 .check_shape(&SHAPE, 4, 16, 32)
103 .unwrap();
104 }
105
106 #[test]
107 fn each_axis_refuses_on_its_own() {
108 let queries = BinaryWhirBudget {
109 max_stir_queries: 39,
110 ..BinaryWhirBudget::PRODUCTION
111 };
112 assert_eq!(
113 queries.check_shape(&SHAPE, 4, 16, 32),
114 Err(BudgetError::Queries {
115 actual: 40,
116 budget: 39
117 })
118 );
119
120 let grinding = BinaryWhirBudget {
121 max_grinding_bits: 15,
122 ..BinaryWhirBudget::PRODUCTION
123 };
124 assert_eq!(
125 grinding.check_shape(&SHAPE, 4, 16, 32),
126 Err(BudgetError::Grinding {
127 actual: 16,
128 budget: 15
129 })
130 );
131
132 let bytes = BinaryWhirBudget {
133 max_proof_bytes: 14_039,
134 ..BinaryWhirBudget::PRODUCTION
135 };
136 assert_eq!(
137 bytes.check_shape(&SHAPE, 4, 16, 32),
138 Err(BudgetError::Bytes {
139 actual: 14_040,
140 budget: 14_039
141 })
142 );
143 assert_eq!(
144 bytes.check_bytes(14_040),
145 Err(BudgetError::Bytes {
146 actual: 14_040,
147 budget: 14_039
148 })
149 );
150 assert_eq!(bytes.check_bytes(14_039), Ok(()));
151 }
152}