forked from maxgillett/winterfell
-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathoptions.rs
343 lines (307 loc) · 14.1 KB
/
options.rs
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
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
// Copyright (c) Facebook, Inc. and its affiliates.
//
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.
use fri::FriOptions;
use math::StarkField;
use utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable};
// TYPES AND INTERFACES
// ================================================================================================
/// Defines a set of available hash function for STARK protocol.
///
/// Choice of a hash function has a direct impact on proof generation time, proof size, and proof
/// soundness. In general, sounds of the proof is bounded by the collision resistance of the hash
/// function used by the protocol.
#[repr(u8)]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum HashFunction {
/// BLAKE3 hash function with 192 bit output.
///
/// When this function is used in the STARK protocol, proof security cannot exceed 96 bits.
Blake3_192 = 1,
/// BLAKE3 hash function with 256 bit output.
///
/// When this function is used in the STARK protocol, proof security cannot exceed 128 bits.
Blake3_256 = 2,
/// SHA3 hash function with 256 bit output.
///
/// When this function is used in the STARK protocol, proof security cannot exceed 128 bits.
Sha3_256 = 3,
/// BLAKE2s hash function with 256 bit output.
///
/// When this function is used in the STARK protocol, proof security cannot exceed 128 bits.
Blake2s_256 = 4,
/// PEDERSEN hash function with 256 bit output.
///
Pedersen_256 = 5,
}
/// Defines an extension field for the composition polynomial.
///
/// Choice of a field for a composition polynomial may impact proof soundness, and can also have
/// a non-negligible impact on proof generation time and proof size. Specifically, for small
/// fields, security offered by the base field itself may be inadequate or insufficient, and an
/// extension of the base field may need to be used.
///
/// For example, if the size of base field is ~64-bits, a quadratic extension must be use to
/// achieve ~100 bits of soundness, and a cubic extension must be used to achieve 128+ bits
/// of soundness.
///
/// However, increasing extension degree will increase proof generation time and proof size by
/// as much as 50%.
#[repr(u8)]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum FieldExtension {
/// Composition polynomial is constructed in the base field.
None = 1,
/// Composition polynomial is constructed in the quadratic extension of the base field.
Quadratic = 2,
/// Composition polynomial is constructed in the cubic extension of the base field.
Cubic = 3,
}
/// STARK protocol parameters.
///
/// These parameters have a direct impact on proof soundness, proof generation time, and proof
/// size. Specifically:
///
/// 1. Hash function - proof soundness is limited by the collision resistance of the hash function
/// used by the protocol. For example, if a hash function with 128-bit collision resistance is
/// used, soundness of a STARK proof cannot exceed 128 bits.
/// 2. Finite field - proof soundness depends on the size of finite field used by the protocol.
/// This means, that for small fields (e.g. smaller than ~128 bits), field extensions must be
/// used to achieve adequate security. And even for ~128 bit fields, to achieve security over
/// 100 bits, a field extension may be required.
/// 3. Number of queries - higher values increase proof soundness, but also increase proof size.
/// 4. Blowup factor - higher values increase proof soundness, but also increase proof generation
/// time and proof size. However, higher blowup factors require fewer queries for the same
/// security level. Thus, it is frequently possible to increase blowup factor and at the same
/// time decrease the number of queries in such a way that the proofs become smaller.
/// 5. Grinding factor - higher values increase proof soundness, but also may increase proof
/// generation time. More precisely, proof soundness is bounded by
/// `num_queries * log2(blowup_factor) + grinding_factor`.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct ProofOptions {
num_queries: u8,
blowup_factor: u8,
grinding_factor: u8,
hash_fn: HashFunction,
field_extension: FieldExtension,
fri_folding_factor: u8,
fri_max_remainder_size: u8, // stored as power of 2
}
// PROOF OPTIONS IMPLEMENTATION
// ================================================================================================
impl ProofOptions {
// CONSTANTS
// --------------------------------------------------------------------------------------------
/// Smallest allowed blowup factor which is currently set to 2.
///
/// The smallest allowed blowup factor for a given computation is derived from degrees of
/// constraints defined for that computation and may be greater than 2. But no computation may
/// have a blowup factor smaller than 2.
pub const MIN_BLOWUP_FACTOR: usize = 2;
// CONSTRUCTORS
// --------------------------------------------------------------------------------------------
/// Returns a new instance of [ProofOptions] struct constructed from the specified parameters.
///
/// # Panics
/// Panics if:
/// * `num_queries` is zero or greater than 128.
/// * `blowup_factor` is smaller than 4, greater than 256, or is not a power of two.
/// * `grinding_factor` is greater than 32.
/// * `fri_folding_factor` is not 2, 4, 8, or 16.
/// * `fri_max_remainder_size` is smaller than 32, greater than 1024, or is not a power of two.
#[rustfmt::skip]
pub fn new(
num_queries: usize,
blowup_factor: usize,
grinding_factor: u32,
hash_fn: HashFunction,
field_extension: FieldExtension,
fri_folding_factor: usize,
fri_max_remainder_size: usize,
) -> ProofOptions {
// TODO: return errors instead of panicking
assert!(num_queries > 0, "number of queries must be greater than 0");
assert!(num_queries <= 128, "number of queries cannot be greater than 128");
assert!(blowup_factor.is_power_of_two(), "blowup factor must be a power of 2");
assert!(blowup_factor >= Self::MIN_BLOWUP_FACTOR,
"blowup factor cannot be smaller than {}", Self::MIN_BLOWUP_FACTOR);
assert!(blowup_factor <= 128, "blowup factor cannot be greater than 128");
assert!(grinding_factor <= 32, "grinding factor cannot be greater than 32");
assert!(fri_folding_factor.is_power_of_two(), "FRI folding factor must be a power of 2");
assert!(fri_folding_factor >= 2, "FRI folding factor cannot be smaller than 2");
assert!(fri_folding_factor <= 16, "FRI folding factor cannot be greater than 16");
assert!(fri_max_remainder_size.is_power_of_two(), "FRI max remainder size must be a power of 2");
assert!(fri_max_remainder_size >= 32, "FRI max remainder size cannot be smaller than 32");
assert!(fri_max_remainder_size <= 1024, "FRI max remainder size cannot be greater than 1024");
ProofOptions {
num_queries: num_queries as u8,
blowup_factor: blowup_factor as u8,
grinding_factor: grinding_factor as u8,
hash_fn,
field_extension,
fri_folding_factor: fri_folding_factor as u8,
fri_max_remainder_size: fri_max_remainder_size.trailing_zeros() as u8,
}
}
// PUBLIC ACCESSORS
// --------------------------------------------------------------------------------------------
/// Returns number of queries for a STARK proof.
///
/// This directly impacts proof soundness as each additional query adds roughly
/// `log2(blowup_factor)` bits of security to a proof. However, each additional query also
/// increases proof size.
pub fn num_queries(&self) -> usize {
self.num_queries as usize
}
/// Returns trace blowup factor for a STARK proof.
///
/// This is the factor by which the execution trace is extended during low-degree extension. It
/// has a direct impact on proof soundness as each query adds roughly `log2(blowup_factor)`
/// bits of security to a proof. However, higher blowup factors also increases prover runtime,
/// and may increase proof size.
pub fn blowup_factor(&self) -> usize {
self.blowup_factor as usize
}
/// Returns query seed grinding factor for a STARK proof.
///
/// Grinding applies Proof-of-Work/ to the query position seed. An honest prover needs to
/// perform this work only once, while a dishonest prover will need to perform it every time
/// they try to change a commitment. Thus, higher grinding factor makes it more difficult to
/// forge a STARK proof. However, setting grinding factor too high (e.g. higher than 20) will
/// adversely affect prover time.
pub fn grinding_factor(&self) -> u32 {
self.grinding_factor as u32
}
/// Returns a hash functions to be used during STARK proof construction.
///
/// Security of a STARK proof is bounded by collision resistance of the hash function used
/// during proof construction. For example, if collision resistance of a hash function is
/// 128 bits, then soundness of a proof generated using this hash function cannot exceed
/// 128 bits.
pub fn hash_fn(&self) -> HashFunction {
self.hash_fn
}
/// Specifies whether composition polynomial should be constructed in an extension field
/// of STARK protocol.
///
/// Using a field extension increases maximum security level of a proof, but also has
/// non-negligible impact on prover performance.
pub fn field_extension(&self) -> FieldExtension {
self.field_extension
}
/// Returns the offset by which the low-degree extension domain is shifted in relation to the
/// trace domain.
///
/// Currently, this is hard-coded to the primitive element of the underlying base field.
pub fn domain_offset<B: StarkField>(&self) -> B {
B::GENERATOR
}
/// Returns options for FRI protocol instantiated with parameters from this proof options.
pub fn to_fri_options(&self) -> FriOptions {
let folding_factor = self.fri_folding_factor as usize;
let max_remainder_size = 2usize.pow(self.fri_max_remainder_size as u32);
FriOptions::new(self.blowup_factor(), folding_factor, max_remainder_size)
}
}
impl Serializable for ProofOptions {
/// Serializes `self` and writes the resulting bytes into the `target`.
fn write_into<W: ByteWriter>(&self, target: &mut W) {
target.write_u8(self.num_queries);
target.write_u8(self.blowup_factor);
target.write_u8(self.grinding_factor);
target.write(self.hash_fn);
target.write(self.field_extension);
target.write_u8(self.fri_folding_factor);
target.write_u8(self.fri_max_remainder_size);
}
}
impl Deserializable for ProofOptions {
/// Reads proof options from the specified `source` and returns the result.
///
/// # Errors
/// Returns an error of a valid proof options could not be read from the specified `source`.
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
Ok(ProofOptions::new(
source.read_u8()? as usize,
source.read_u8()? as usize,
source.read_u8()? as u32,
HashFunction::read_from(source)?,
FieldExtension::read_from(source)?,
source.read_u8()? as usize,
2usize.pow(source.read_u8()? as u32),
))
}
}
// FIELD EXTENSION IMPLEMENTATION
// ================================================================================================
impl FieldExtension {
/// Returns `true` if this field extension is set to `None`.
pub fn is_none(&self) -> bool {
matches!(self, Self::None)
}
/// Returns extension degree of this field extension.
pub fn degree(&self) -> u32 {
match self {
Self::None => 1,
Self::Quadratic => 2,
Self::Cubic => 3,
}
}
}
impl Serializable for FieldExtension {
/// Serializes `self` and writes the resulting bytes into the `target`.
fn write_into<W: ByteWriter>(&self, target: &mut W) {
target.write_u8(*self as u8);
}
}
impl Deserializable for FieldExtension {
/// Reads a field extension enum from the specified `source`.
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
match source.read_u8()? {
1 => Ok(FieldExtension::None),
2 => Ok(FieldExtension::Quadratic),
3 => Ok(FieldExtension::Cubic),
value => Err(DeserializationError::InvalidValue(format!(
"value {} cannot be deserialized as FieldExtension enum",
value
))),
}
}
}
// HASH FUNCTION IMPLEMENTATION
// ================================================================================================
impl HashFunction {
/// Returns collision resistance of this hash function in bits.
pub fn collision_resistance(&self) -> u32 {
match self {
Self::Blake3_192 => 96,
Self::Blake3_256 => 128,
Self::Sha3_256 => 128,
Self::Blake2s_256 => 128,
Self::Pedersen_256 => 128
}
}
}
impl Serializable for HashFunction {
/// Serializes `self` and writes the resulting bytes into the `target`.
fn write_into<W: ByteWriter>(&self, target: &mut W) {
target.write_u8(*self as u8);
}
}
impl Deserializable for HashFunction {
/// Reads a hash function enum from the specified `source`.
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
match source.read_u8()? {
1 => Ok(HashFunction::Blake3_192),
2 => Ok(HashFunction::Blake3_256),
3 => Ok(HashFunction::Sha3_256),
4 => Ok(HashFunction::Blake2s_256),
5 => Ok(HashFunction::Pedersen_256),
value => Err(DeserializationError::InvalidValue(format!(
"value {} cannot be deserialized as HashFunction enum",
value
))),
}
}
}