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
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
//! Challenges are needed for the [cross-table arguments](CrossTableArg), _i.e._,
//! [Permutation Arguments](crate::table::cross_table_argument::PermArg),
//! [Evaluation Arguments](crate::table::cross_table_argument::EvalArg), and
//! [Lookup Arguments](crate::table::cross_table_argument::LookupArg),
//! as well as for the RAM Table's Contiguity Argument.
//!
//! There are three types of challenges:
//! - **Weights**. Weights are used to linearly combine multiple elements into one element. The
//! resulting single element can then be used in a cross-table argument.
//! - **Indeterminates**. All cross-table arguments work by checking the equality of polynomials (or
//! rational functions). Through the Schwartz-Zippel lemma, this equality check can be performed
//! by evaluating the polynomials (or rational functions) in a single point. The challenges that
//! are indeterminates are exactly this evaluation point. The polynomials (or rational functions)
//! are never stored explicitly. Instead, they are directly evaluated at the point indicated by a
//! challenge of “type” `Indeterminate`, giving rise to “running products”, “running
//! evaluations”, _et cetera_.
//! - **Terminals**. The public input (respectively output) of the program is not stored in any
//! table. Instead, the terminal of the Evaluation Argument is computed directly from the
//! public input (respectively output) and the indeterminate.

use std::fmt::Debug;
use std::hash::Hash;
use std::ops::Index;
use std::ops::Range;
use std::ops::RangeInclusive;

use arbitrary::Arbitrary;
use strum::Display;
use strum::EnumCount;
use strum::EnumIter;
use twenty_first::prelude::*;

use crate::table::challenges::ChallengeId::*;
use crate::table::cross_table_argument::CrossTableArg;
use crate::table::cross_table_argument::EvalArg;
use crate::Claim;

/// A `ChallengeId` is a unique, symbolic identifier for a challenge used in Triton VM. The
/// `ChallengeId` enum works in tandem with the struct [`Challenges`], which can be
/// instantiated to hold actual challenges that can be indexed by some `ChallengeId`.
///
/// Since almost all challenges relate to the Processor Table in some form, the words “Processor
/// Table” are usually omitted from the `ChallengeId`'s name.
#[repr(usize)]
#[derive(Debug, Display, Copy, Clone, Eq, PartialEq, Hash, EnumCount, EnumIter, Arbitrary)]
pub enum ChallengeId {
    /// The indeterminate for the [Evaluation Argument](EvalArg) compressing the program digest
    /// into a single extension field element, _i.e._, [`CompressedProgramDigest`].
    /// Relates to program attestation.
    CompressProgramDigestIndeterminate,

    /// The indeterminate for the [Evaluation Argument](EvalArg) with standard input.
    StandardInputIndeterminate,

    /// The indeterminate for the [Evaluation Argument](EvalArg) with standard output.
    StandardOutputIndeterminate,

    /// The indeterminate for the instruction
    /// [Lookup Argument](crate::table::cross_table_argument::LookupArg)
    /// between the [Processor Table](crate::table::processor_table) and the
    /// [Program Table](crate::table::program_table) guaranteeing that the instructions and their
    /// arguments are copied correctly.
    InstructionLookupIndeterminate,

    HashInputIndeterminate,
    HashDigestIndeterminate,
    SpongeIndeterminate,

    OpStackIndeterminate,
    RamIndeterminate,
    JumpStackIndeterminate,

    U32Indeterminate,

    /// The indeterminate for the Lookup Argument between the Processor Table and all memory-like
    /// tables, _i.e._, the OpStack Table, the Ram Table, and the JumpStack Table, guaranteeing
    /// that all clock jump differences are directed forward.
    ClockJumpDifferenceLookupIndeterminate,

    /// The indeterminate for the Contiguity Argument within the Ram Table.
    RamTableBezoutRelationIndeterminate,

    /// A weight for linearly combining multiple elements. Applies to
    /// - `Address` in the Program Table
    /// - `IP` in the Processor Table
    ProgramAddressWeight,

    /// A weight for linearly combining multiple elements. Applies to
    /// - `Instruction` in the Program Table
    /// - `CI` in the Processor Table
    ProgramInstructionWeight,

    /// A weight for linearly combining multiple elements. Applies to
    /// - `Instruction'` (_i.e._, in the next row) in the Program Table
    /// - `NIA` in the Processor Table
    ProgramNextInstructionWeight,

    OpStackClkWeight,
    OpStackIb1Weight,
    OpStackPointerWeight,
    OpStackFirstUnderflowElementWeight,

    RamClkWeight,
    RamPointerWeight,
    RamValueWeight,
    RamInstructionTypeWeight,

    JumpStackClkWeight,
    JumpStackCiWeight,
    JumpStackJspWeight,
    JumpStackJsoWeight,
    JumpStackJsdWeight,

    /// The indeterminate for compressing a [`RATE`][rate]-sized chunk of instructions into a
    /// single extension field element.
    /// Relates to program attestation.
    ///
    /// Used by the evaluation argument [`PrepareChunkEvalArg`][prep] and in the Hash Table.
    ///
    /// [rate]: twenty_first::math::tip5::RATE
    /// [prep]: crate::table::table_column::ProgramExtTableColumn::PrepareChunkRunningEvaluation
    ProgramAttestationPrepareChunkIndeterminate,

    /// The indeterminate for the bus over which the [`RATE`][rate]-sized chunks of instructions
    /// are sent. Relates to program attestation.
    /// Used by the evaluation arguments [`SendChunkEvalArg`][send] and
    /// [`ReceiveChunkEvalArg`][recv]. See also: [`ProgramAttestationPrepareChunkIndeterminate`].
    ///
    /// [rate]: twenty_first::math::tip5::RATE
    /// [send]: crate::table::table_column::ProgramExtTableColumn::SendChunkRunningEvaluation
    /// [recv]: crate::table::table_column::HashExtTableColumn::ReceiveChunkRunningEvaluation
    ProgramAttestationSendChunkIndeterminate,

    HashCIWeight,
    HashStateWeight0,
    HashStateWeight1,
    HashStateWeight2,
    HashStateWeight3,
    HashStateWeight4,
    HashStateWeight5,
    HashStateWeight6,
    HashStateWeight7,
    HashStateWeight8,
    HashStateWeight9,
    HashStateWeight10,
    HashStateWeight11,
    HashStateWeight12,
    HashStateWeight13,
    HashStateWeight14,
    HashStateWeight15,

    /// The indeterminate for the Lookup Argument between the Hash Table and the Cascade Table.
    HashCascadeLookupIndeterminate,

    /// A weight for linearly combining multiple elements. Applies to
    /// - `*LkIn` in the Hash Table, and
    /// - `2^16·LookInHi + LookInLo` in the Cascade Table.
    HashCascadeLookInWeight,

    /// A weight for linearly combining multiple elements. Applies to
    /// - `*LkOut` in the Hash Table, and
    /// - `2^16·LookOutHi + LookOutLo` in the Cascade Table.
    HashCascadeLookOutWeight,

    /// The indeterminate for the Lookup Argument between the Cascade Table and the Lookup Table.
    CascadeLookupIndeterminate,

    /// A weight for linearly combining multiple elements. Applies to
    /// - `LkIn*` in the Cascade Table, and
    /// - `LookIn` in the Lookup Table.
    LookupTableInputWeight,

    /// A weight for linearly combining multiple elements. Applies to
    /// - `LkOut*` in the Cascade Table, and
    /// - `LookOut` in the Lookup Table.
    LookupTableOutputWeight,

    /// The indeterminate for the public Evaluation Argument establishing correctness of the
    /// Lookup Table.
    LookupTablePublicIndeterminate,

    U32LhsWeight,
    U32RhsWeight,
    U32CiWeight,
    U32ResultWeight,

    /// The terminal for the [`EvaluationArgument`](EvalArg) with standard input.
    /// Makes use of challenge [`StandardInputIndeterminate`].
    StandardInputTerminal,

    /// The terminal for the [`EvaluationArgument`](EvalArg) with standard output.
    /// Makes use of challenge [`StandardOutputIndeterminate`].
    StandardOutputTerminal,

    /// The terminal for the [`EvaluationArgument`](EvalArg) establishing correctness of the
    /// [Lookup Table](crate::table::lookup_table::LookupTable).
    /// Makes use of challenge [`LookupTablePublicIndeterminate`].
    LookupTablePublicTerminal,

    /// The digest of the program to be executed, compressed into a single extension field element.
    /// The compression happens using an [`EvaluationArgument`](EvalArg) under challenge
    /// [`CompressProgramDigestIndeterminate`].
    /// Relates to program attestation.
    CompressedProgramDigest,
}

impl ChallengeId {
    pub const fn index(&self) -> usize {
        *self as usize
    }
}

impl From<ChallengeId> for usize {
    fn from(id: ChallengeId) -> Self {
        id.index()
    }
}

/// The `Challenges` struct holds the challenges used in Triton VM. The concrete challenges are
/// known only at runtime. The challenges are indexed using enum [`ChallengeId`]. The `Challenges`
/// struct is essentially a thin wrapper around an array of [`XFieldElement`]s, providing
/// convenience methods.
#[derive(Debug, Clone, Arbitrary)]
pub struct Challenges {
    pub challenges: [XFieldElement; Self::COUNT],
}

impl Challenges {
    /// The total number of challenges used in Triton VM.
    pub const COUNT: usize = ChallengeId::COUNT;

    /// The number of weights to sample using the Fiat-Shamir heuristic. This number is lower
    /// than the number of challenges because several challenges are not sampled, but computed
    /// from publicly known values and other, sampled challenges.
    ///
    /// Concretely:
    /// - The [`StandardInputTerminal`] is computed from Triton VM's public input and the sampled
    /// indeterminate [`StandardInputIndeterminate`].
    /// - The [`StandardOutputTerminal`] is computed from Triton VM's public output and the sampled
    /// indeterminate [`StandardOutputIndeterminate`].
    /// - The [`LookupTablePublicTerminal`] is computed from the publicly known and constant
    /// lookup table and the sampled indeterminate [`LookupTablePublicIndeterminate`].
    /// - The [`CompressedProgramDigest`] is computed from the program to be executed and the
    /// sampled indeterminate [`CompressProgramDigestIndeterminate`].
    // When modifying this, be sure to add to the compile-time assertions in the
    // `#[test] const fn compile_time_index_assertions() { … }`
    // at the end of this file.
    pub const SAMPLE_COUNT: usize = Self::COUNT - 4;

    #[deprecated(since = "0.39.0", note = "Use `Self::COUNT` instead")]
    pub const fn count() -> usize {
        Self::COUNT
    }

    #[deprecated(since = "0.39.0", note = "Use `Self::SAMPLE_COUNT` instead")]
    pub const fn num_challenges_to_sample() -> usize {
        Self::SAMPLE_COUNT
    }

    pub fn new(mut challenges: Vec<XFieldElement>, claim: &Claim) -> Self {
        assert_eq!(Self::SAMPLE_COUNT, challenges.len());

        let compressed_digest = EvalArg::compute_terminal(
            &claim.program_digest.values(),
            EvalArg::default_initial(),
            challenges[CompressProgramDigestIndeterminate.index()],
        );
        let input_terminal = EvalArg::compute_terminal(
            &claim.input,
            EvalArg::default_initial(),
            challenges[StandardInputIndeterminate.index()],
        );
        let output_terminal = EvalArg::compute_terminal(
            &claim.output,
            EvalArg::default_initial(),
            challenges[StandardOutputIndeterminate.index()],
        );
        let lookup_terminal = EvalArg::compute_terminal(
            &tip5::LOOKUP_TABLE.map(BFieldElement::from),
            EvalArg::default_initial(),
            challenges[LookupTablePublicIndeterminate.index()],
        );

        challenges.insert(StandardInputTerminal.index(), input_terminal);
        challenges.insert(StandardOutputTerminal.index(), output_terminal);
        challenges.insert(LookupTablePublicTerminal.index(), lookup_terminal);
        challenges.insert(CompressedProgramDigest.index(), compressed_digest);
        assert_eq!(Self::COUNT, challenges.len());
        let challenges = challenges.try_into().unwrap();

        Self { challenges }
    }
}

impl Index<usize> for Challenges {
    type Output = XFieldElement;

    fn index(&self, id: usize) -> &Self::Output {
        &self.challenges[id]
    }
}

impl Index<Range<usize>> for Challenges {
    type Output = [XFieldElement];

    fn index(&self, indices: Range<usize>) -> &Self::Output {
        &self.challenges[indices.start..indices.end]
    }
}

impl Index<RangeInclusive<usize>> for Challenges {
    type Output = [XFieldElement];

    fn index(&self, indices: RangeInclusive<usize>) -> &Self::Output {
        &self.challenges[*indices.start()..=*indices.end()]
    }
}

impl Index<ChallengeId> for Challenges {
    type Output = XFieldElement;

    fn index(&self, id: ChallengeId) -> &Self::Output {
        &self[id.index()]
    }
}

impl Index<Range<ChallengeId>> for Challenges {
    type Output = [XFieldElement];

    fn index(&self, indices: Range<ChallengeId>) -> &Self::Output {
        &self[indices.start.index()..indices.end.index()]
    }
}

impl Index<RangeInclusive<ChallengeId>> for Challenges {
    type Output = [XFieldElement];

    fn index(&self, indices: RangeInclusive<ChallengeId>) -> &Self::Output {
        &self[indices.start().index()..=indices.end().index()]
    }
}

#[cfg(test)]
pub(crate) mod tests {
    use super::*;

    // For testing purposes only.
    impl Default for Challenges {
        fn default() -> Self {
            Self::placeholder(&Claim::default())
        }
    }

    impl Challenges {
        /// Stand-in challenges for use in tests. For non-interactive STARKs, use the
        /// Fiat-Shamir heuristic to derive the actual challenges.
        pub fn placeholder(claim: &Claim) -> Self {
            let stand_in_challenges = (1..=Self::SAMPLE_COUNT)
                .map(|i| xfe!([42, i as u64, 24]))
                .collect();
            Self::new(stand_in_challenges, claim)
        }
    }

    #[test]
    const fn compile_time_index_assertions() {
        // Terminal challenges are computed from public information, such as public input or
        // public output, and other challenges. Because these other challenges are used to compute
        // the terminal challenges, the terminal challenges must be inserted into the challenges
        // vector after the used challenges.
        assert!(StandardInputIndeterminate.index() < StandardInputTerminal.index());
        assert!(StandardInputIndeterminate.index() < StandardOutputTerminal.index());
        assert!(StandardInputIndeterminate.index() < LookupTablePublicTerminal.index());
        assert!(StandardInputIndeterminate.index() < CompressedProgramDigest.index());

        assert!(StandardOutputIndeterminate.index() < StandardInputTerminal.index());
        assert!(StandardOutputIndeterminate.index() < StandardOutputTerminal.index());
        assert!(StandardOutputIndeterminate.index() < LookupTablePublicTerminal.index());
        assert!(StandardOutputIndeterminate.index() < CompressedProgramDigest.index());

        assert!(CompressProgramDigestIndeterminate.index() < StandardInputTerminal.index());
        assert!(CompressProgramDigestIndeterminate.index() < StandardOutputTerminal.index());
        assert!(CompressProgramDigestIndeterminate.index() < LookupTablePublicTerminal.index());
        assert!(CompressProgramDigestIndeterminate.index() < CompressedProgramDigest.index());

        assert!(LookupTablePublicIndeterminate.index() < StandardInputTerminal.index());
        assert!(LookupTablePublicIndeterminate.index() < StandardOutputTerminal.index());
        assert!(LookupTablePublicIndeterminate.index() < LookupTablePublicTerminal.index());
        assert!(LookupTablePublicIndeterminate.index() < CompressedProgramDigest.index());
    }

    // Ensure the compile-time assertions are actually executed by the compiler.
    const _: () = compile_time_index_assertions();

    #[test]
    fn various_challenge_indexing_operations_are_possible() {
        let challenges = Challenges::placeholder(&Claim::default());
        let _ = challenges[HashStateWeight0];
        let _ = challenges[HashStateWeight0..HashStateWeight8];
        let _ = challenges[HashStateWeight0..=HashStateWeight8];
        let _ = challenges[0];
        let _ = challenges[0..8];
        let _ = challenges[0..=8];
    }
}