winter_prover/
domain.rs

1// Copyright (c) Facebook, Inc. and its affiliates.
2//
3// This source code is licensed under the MIT license found in the
4// LICENSE file in the root directory of this source tree.
5
6use alloc::vec::Vec;
7
8use air::Air;
9use math::{fft, get_power_series, StarkField};
10
11// TYPES AND INTERFACES
12// ================================================================================================
13
14/// Info about domains related to specific instance of proof generation.
15pub struct StarkDomain<B: StarkField> {
16    /// Twiddles which can be used to evaluate polynomials in the trace domain. Length of this
17    /// vector is half the length of the trace domain size.
18    trace_twiddles: Vec<B>,
19
20    /// [g^i for i in (0..ce_domain_size)] where g is the constraint evaluation domain generator.
21    ce_domain: Vec<B>,
22
23    /// LDE domain size / constraint evaluation domain size
24    ce_to_lde_blowup: usize,
25
26    /// A mask which can be used to compute (x % ce_domain_size) via binary AND. This takes
27    /// advantage of the fact that ce_domain_size is a power of two. The mask is then simply
28    /// ce_domain_size - 1.
29    ce_domain_mod_mask: usize,
30
31    /// Offset of the low-degree extension domain.
32    domain_offset: B,
33}
34
35// STARK DOMAIN IMPLEMENTATION
36// ================================================================================================
37
38impl<B: StarkField> StarkDomain<B> {
39    /// Returns a new STARK domain initialized with the provided `context`.
40    pub fn new<A: Air<BaseField = B>>(air: &A) -> Self {
41        let trace_twiddles = fft::get_twiddles(air.trace_length());
42
43        // build constraint evaluation domain
44        let domain_gen = B::get_root_of_unity(air.ce_domain_size().ilog2());
45        let ce_domain = get_power_series(domain_gen, air.ce_domain_size());
46
47        StarkDomain {
48            trace_twiddles,
49            ce_domain,
50            ce_to_lde_blowup: air.lde_domain_size() / air.ce_domain_size(),
51            ce_domain_mod_mask: air.ce_domain_size() - 1,
52            domain_offset: air.domain_offset(),
53        }
54    }
55
56    /// Returns a new STARK domain initialized with the provided custom inputs.
57    pub fn from_twiddles(trace_twiddles: Vec<B>, blowup_factor: usize, domain_offset: B) -> Self {
58        // both `trace_twiddles` length and `blowup_factor` must be a power of two.
59        assert!(
60            trace_twiddles.len().is_power_of_two(),
61            "the length of trace twiddles must be a power of 2"
62        );
63        assert!(blowup_factor.is_power_of_two(), "blowup factor must be a power of 2");
64
65        let ce_domain_size = trace_twiddles.len() * blowup_factor * 2;
66        let domain_gen = B::get_root_of_unity(ce_domain_size.ilog2());
67        let ce_domain = get_power_series(domain_gen, ce_domain_size);
68
69        StarkDomain {
70            trace_twiddles,
71            ce_domain,
72            ce_to_lde_blowup: 1,
73            ce_domain_mod_mask: ce_domain_size - 1,
74            domain_offset,
75        }
76    }
77
78    // EXECUTION TRACE
79    // --------------------------------------------------------------------------------------------
80
81    /// Returns length of the execution trace for this computation.
82    pub fn trace_length(&self) -> usize {
83        &self.trace_twiddles.len() * 2
84    }
85
86    /// Returns twiddles which can be used to evaluate trace polynomials.
87    pub fn trace_twiddles(&self) -> &[B] {
88        &self.trace_twiddles
89    }
90
91    /// Returns blowup factor from trace to constraint evaluation domain.
92    pub fn trace_to_ce_blowup(&self) -> usize {
93        self.ce_domain_size() / self.trace_length()
94    }
95
96    /// Returns blowup factor from trace to LDE domain.
97    pub fn trace_to_lde_blowup(&self) -> usize {
98        self.lde_domain_size() / self.trace_length()
99    }
100
101    // CONSTRAINT EVALUATION DOMAIN
102    // --------------------------------------------------------------------------------------------
103
104    /// Returns the size of the constraint evaluation domain for this computation.
105    #[inline(always)]
106    pub fn ce_domain_size(&self) -> usize {
107        self.ce_domain.len()
108    }
109
110    /// Returns the generator of constraint evaluation domain.
111    pub fn ce_domain_generator(&self) -> B {
112        B::get_root_of_unity(self.ce_domain_size().ilog2())
113    }
114
115    /// Returns blowup factor from constraint evaluation to LDE domain.
116    pub fn ce_to_lde_blowup(&self) -> usize {
117        self.ce_to_lde_blowup
118    }
119
120    /// Returns s * g^step where g is the constraint evaluation domain generator and s is the
121    /// domain offset.
122    #[inline(always)]
123    pub fn get_ce_x_at(&self, step: usize) -> B {
124        self.ce_domain[step] * self.domain_offset
125    }
126
127    /// Returns (s * g^step)^power where g is the constraint evaluation domain generator and s is
128    /// the domain offset.
129    ///
130    /// The computation is performed without doing exponentiations. offset_exp is assumed to be
131    /// s^power which is pre-computed elsewhere.
132    #[inline(always)]
133    pub fn get_ce_x_power_at(&self, step: usize, power: u64, offset_exp: B) -> B {
134        debug_assert_eq!(offset_exp, self.offset().exp(power.into()));
135        // this computes (step * power) % ce_domain_size. even though both step and power could be
136        // 64-bit values, we are not concerned about overflow here because we are modding by a
137        // power of two. this is also the reason why we can do & ce_domain_mod_mask instead of
138        // performing the actual modulus operation.
139        let index = step.wrapping_mul(power as usize) & self.ce_domain_mod_mask;
140        self.ce_domain[index] * offset_exp
141    }
142
143    // LOW-DEGREE EXTENSION DOMAIN
144    // --------------------------------------------------------------------------------------------
145
146    /// Returns the size of the low-degree extension domain.
147    pub fn lde_domain_size(&self) -> usize {
148        self.ce_domain_size() * self.ce_to_lde_blowup()
149    }
150
151    /// Returns LDE domain offset.
152    pub fn offset(&self) -> B {
153        self.domain_offset
154    }
155}