triton_vm/
challenges.rs

1//! Challenges are needed for the [cross-table arguments](CrossTableArg),
2//! _i.e._, [Permutation Arguments](air::cross_table_argument::PermArg),
3//! [Evaluation Arguments](EvalArg), and
4//! [Lookup Arguments](air::cross_table_argument::LookupArg),
5//! as well as for the RAM Table's Contiguity Argument.
6//!
7//! There are three types of challenges:
8//! - **Weights**. Weights are used to linearly combine multiple elements into
9//!   one element. The resulting single element can then be used in a
10//!   cross-table argument.
11//! - **Indeterminates**. All cross-table arguments work by checking the
12//!   equality of polynomials (or rational functions). Through the
13//!   Schwartz-Zippel lemma, this equality check can be performed by evaluating
14//!   the polynomials (or rational functions) in a single point. The challenges
15//!   that are indeterminates are exactly this evaluation point. The polynomials
16//!   (or rational functions) are never stored explicitly. Instead, they are
17//!   directly evaluated at the point indicated by a challenge of “type”
18//!   `Indeterminate`, giving rise to “running products”, “running evaluations”,
19//!   _et cetera_.
20//! - **Terminals**. The public input (respectively output) of the program is
21//!   not stored in any table. Instead, the terminal of the Evaluation Argument
22//!   is computed directly from the public input (respectively output) and the
23//!   indeterminate.
24
25use arbitrary::Arbitrary;
26use std::ops::Index;
27use std::ops::Range;
28use std::ops::RangeInclusive;
29use strum::EnumCount;
30use twenty_first::prelude::*;
31use twenty_first::tip5;
32
33use air::challenge_id::ChallengeId;
34use air::cross_table_argument::CrossTableArg;
35use air::cross_table_argument::EvalArg;
36
37use crate::prelude::Claim;
38
39/// The `Challenges` struct holds the challenges used in Triton VM. The concrete
40/// challenges are known only at runtime. The challenges are indexed using enum
41/// [`ChallengeId`]. The `Challenges` struct is essentially a thin wrapper
42/// around an array of [`XFieldElement`]s, providing convenience methods.
43#[derive(Debug, Clone, Arbitrary)]
44pub struct Challenges {
45    pub challenges: [XFieldElement; Self::COUNT],
46}
47
48impl Challenges {
49    /// The total number of challenges used in Triton VM.
50    pub const COUNT: usize = ChallengeId::COUNT;
51
52    /// The number of weights to sample using the Fiat-Shamir heuristic. This
53    /// number is lower than the number of challenges because several
54    /// challenges are not sampled, but computed from publicly known values
55    /// and other, sampled challenges.
56    ///
57    /// Concretely:
58    /// - The [`StandardInputTerminal`][std_in_term] is computed from Triton
59    ///   VM's public input and the sampled indeterminate
60    ///   [`StandardInputIndeterminate`][std_in_ind].
61    /// - The [`StandardOutputTerminal`][std_out_term] is computed from Triton
62    ///   VM's public output and the sampled indeterminate
63    ///   [`StandardOutputIndeterminate`][std_out_ind].
64    /// - The [`LookupTablePublicTerminal`][lk_up_term] is computed from the
65    ///   publicly known and constant lookup table and the sampled indeterminate
66    ///   [`LookupTablePublicIndeterminate`][lk_up_ind].
67    /// - The [`CompressedProgramDigest`][program_digest] is computed from the
68    ///   program to be executed and the sampled indeterminate
69    ///   [`CompressProgramDigestIndeterminate`][program_digest_ind].
70    ///
71    /// [std_in_term]: ChallengeId::StandardInputTerminal
72    /// [std_in_ind]: ChallengeId::StandardInputIndeterminate
73    /// [std_out_term]: ChallengeId::StandardOutputTerminal
74    /// [std_out_ind]: ChallengeId::StandardOutputIndeterminate
75    /// [lk_up_term]: ChallengeId::LookupTablePublicTerminal
76    /// [lk_up_ind]: ChallengeId::LookupTablePublicIndeterminate
77    /// [program_digest]: ChallengeId::CompressedProgramDigest
78    /// [program_digest_ind]: ChallengeId::CompressProgramDigestIndeterminate
79    pub const SAMPLE_COUNT: usize = Self::COUNT - ChallengeId::NUM_DERIVED_CHALLENGES;
80
81    pub fn new(mut challenges: Vec<XFieldElement>, claim: &Claim) -> Self {
82        assert_eq!(Self::SAMPLE_COUNT, challenges.len());
83
84        let compressed_digest = EvalArg::compute_terminal(
85            &claim.program_digest.values(),
86            EvalArg::default_initial(),
87            challenges[ChallengeId::CompressProgramDigestIndeterminate.index()],
88        );
89        let input_terminal = EvalArg::compute_terminal(
90            &claim.input,
91            EvalArg::default_initial(),
92            challenges[ChallengeId::StandardInputIndeterminate.index()],
93        );
94        let output_terminal = EvalArg::compute_terminal(
95            &claim.output,
96            EvalArg::default_initial(),
97            challenges[ChallengeId::StandardOutputIndeterminate.index()],
98        );
99        let lookup_terminal = EvalArg::compute_terminal(
100            &tip5::LOOKUP_TABLE.map(BFieldElement::from),
101            EvalArg::default_initial(),
102            challenges[ChallengeId::LookupTablePublicIndeterminate.index()],
103        );
104
105        challenges.insert(ChallengeId::StandardInputTerminal.index(), input_terminal);
106        challenges.insert(ChallengeId::StandardOutputTerminal.index(), output_terminal);
107        challenges.insert(
108            ChallengeId::LookupTablePublicTerminal.index(),
109            lookup_terminal,
110        );
111        challenges.insert(
112            ChallengeId::CompressedProgramDigest.index(),
113            compressed_digest,
114        );
115        assert_eq!(Self::COUNT, challenges.len());
116        let challenges = challenges.try_into().unwrap();
117
118        Self { challenges }
119    }
120}
121
122impl Index<usize> for Challenges {
123    type Output = XFieldElement;
124
125    fn index(&self, id: usize) -> &Self::Output {
126        &self.challenges[id]
127    }
128}
129
130impl Index<Range<usize>> for Challenges {
131    type Output = [XFieldElement];
132
133    fn index(&self, indices: Range<usize>) -> &Self::Output {
134        &self.challenges[indices.start..indices.end]
135    }
136}
137
138impl Index<RangeInclusive<usize>> for Challenges {
139    type Output = [XFieldElement];
140
141    fn index(&self, indices: RangeInclusive<usize>) -> &Self::Output {
142        &self.challenges[*indices.start()..=*indices.end()]
143    }
144}
145
146impl Index<ChallengeId> for Challenges {
147    type Output = XFieldElement;
148
149    fn index(&self, id: ChallengeId) -> &Self::Output {
150        &self[id.index()]
151    }
152}
153
154impl Index<Range<ChallengeId>> for Challenges {
155    type Output = [XFieldElement];
156
157    fn index(&self, indices: Range<ChallengeId>) -> &Self::Output {
158        &self[indices.start.index()..indices.end.index()]
159    }
160}
161
162impl Index<RangeInclusive<ChallengeId>> for Challenges {
163    type Output = [XFieldElement];
164
165    fn index(&self, indices: RangeInclusive<ChallengeId>) -> &Self::Output {
166        &self[indices.start().index()..=indices.end().index()]
167    }
168}
169
170#[cfg(test)]
171#[cfg_attr(coverage_nightly, coverage(off))]
172mod tests {
173    use super::*;
174    use crate::prelude::Claim;
175    use twenty_first::xfe;
176
177    // For testing purposes only.
178    impl Default for Challenges {
179        fn default() -> Self {
180            Self::placeholder(&Claim::default())
181        }
182    }
183
184    impl Challenges {
185        /// Stand-in challenges for use in tests. For non-interactive STARKs,
186        /// use the Fiat-Shamir heuristic to derive the actual
187        /// challenges.
188        pub fn placeholder(claim: &Claim) -> Self {
189            let stand_in_challenges = (1..=Self::SAMPLE_COUNT)
190                .map(|i| xfe!([42, i as u64, 24]))
191                .collect();
192            Self::new(stand_in_challenges, claim)
193        }
194    }
195
196    #[test]
197    fn various_challenge_indexing_operations_are_possible() {
198        let challenges = Challenges::default();
199        let _ = challenges[ChallengeId::StackWeight0];
200        let _ = challenges[ChallengeId::StackWeight0..ChallengeId::StackWeight8];
201        let _ = challenges[ChallengeId::StackWeight0..=ChallengeId::StackWeight8];
202        let _ = challenges[0];
203        let _ = challenges[0..8];
204        let _ = challenges[0..=8];
205    }
206}