../../.cargo/katex-header.html
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
//! A [`StarkConfig`] defines all the parameters to be used when proving a
//! [`Stark`][crate::stark::Stark].
//!
//! The default configuration is aimed for speed, yielding fast but large
//! proofs, with a targeted security level of 100 bits.

#[cfg(not(feature = "std"))]
use alloc::format;

use anyhow::{anyhow, Result};
use plonky2::field::extension::Extendable;
use plonky2::field::types::Field;
use plonky2::fri::reduction_strategies::FriReductionStrategy;
use plonky2::fri::{FriConfig, FriParams};
use plonky2::hash::hash_types::RichField;

/// A configuration containing the different parameters used by the STARK prover.
#[derive(Clone, Debug)]
pub struct StarkConfig {
    /// The targeted security level for the proofs generated with this configuration.
    pub security_bits: usize,

    /// The number of challenge points to generate, for IOPs that have soundness errors of (roughly)
    /// `degree / |F|`.
    pub num_challenges: usize,

    /// The configuration of the FRI sub-protocol.
    pub fri_config: FriConfig,
}

impl Default for StarkConfig {
    fn default() -> Self {
        Self::standard_fast_config()
    }
}

impl StarkConfig {
    /// Returns a custom STARK configuration.
    pub const fn new(security_bits: usize, num_challenges: usize, fri_config: FriConfig) -> Self {
        Self {
            security_bits,
            num_challenges,
            fri_config,
        }
    }

    /// A typical configuration with a rate of 2, resulting in fast but large proofs.
    /// Targets ~100 bit conjectured security.
    pub const fn standard_fast_config() -> Self {
        Self {
            security_bits: 100,
            num_challenges: 2,
            fri_config: FriConfig {
                rate_bits: 1,
                cap_height: 4,
                proof_of_work_bits: 16,
                reduction_strategy: FriReductionStrategy::ConstantArityBits(4, 5),
                num_query_rounds: 84,
            },
        }
    }

    /// Outputs the [`FriParams`] used during the FRI sub-protocol by this [`StarkConfig`].
    pub fn fri_params(&self, degree_bits: usize) -> FriParams {
        self.fri_config.fri_params(degree_bits, false)
    }

    /// Checks that this STARK configuration is consistent, i.e. that the different
    /// parameters meet the targeted security level.
    pub fn check_config<F: RichField + Extendable<D>, const D: usize>(&self) -> Result<()> {
        let StarkConfig {
            security_bits,
            fri_config:
                FriConfig {
                    rate_bits,
                    proof_of_work_bits,
                    num_query_rounds,
                    ..
                },
            ..
        } = &self;

        // Conjectured FRI security; see the ethSTARK paper.
        let fri_field_bits = F::Extension::order().bits() as usize;
        let fri_query_security_bits = num_query_rounds * rate_bits + *proof_of_work_bits as usize;
        let fri_security_bits = fri_field_bits.min(fri_query_security_bits);

        if fri_security_bits < *security_bits {
            Err(anyhow!(format!(
                "FRI params fall short of target security {}, reaching only {}",
                security_bits, fri_security_bits
            )))
        } else {
            Ok(())
        }
    }
}

#[cfg(test)]
mod tests {
    use plonky2::field::goldilocks_field::GoldilocksField;

    use super::*;

    #[test]
    fn test_valid_config() {
        type F = GoldilocksField;
        const D: usize = 2;

        let config = StarkConfig::standard_fast_config();
        assert!(config.check_config::<F, D>().is_ok());

        let high_rate_config = StarkConfig::new(
            100,
            2,
            FriConfig {
                rate_bits: 3,
                cap_height: 4,
                proof_of_work_bits: 16,
                reduction_strategy: FriReductionStrategy::ConstantArityBits(4, 5),
                num_query_rounds: 28,
            },
        );
        assert!(high_rate_config.check_config::<F, D>().is_ok());
    }

    #[test]
    fn test_invalid_config() {
        type F = GoldilocksField;
        const D: usize = 2;

        let too_few_queries_config = StarkConfig::new(
            100,
            2,
            FriConfig {
                rate_bits: 1,
                cap_height: 4,
                proof_of_work_bits: 16,
                reduction_strategy: FriReductionStrategy::ConstantArityBits(4, 5),
                num_query_rounds: 50,
            },
        );
        // The conjectured security yields `rate_bits` * `num_query_rounds` + `proof_of_work_bits` = 66
        // bits of security for FRI, which falls short of the 100 bits of security target.
        assert!(too_few_queries_config.check_config::<F, D>().is_err());
    }
}