openvm_sha2_air/
columns.rs

1//! WARNING: the order of fields in the structs is important, do not change it
2
3use core::ops::Add;
4
5use openvm_circuit_primitives::utils::not;
6use openvm_circuit_primitives_derive::ColsRef;
7use openvm_stark_backend::p3_field::PrimeCharacteristicRing;
8
9use crate::Sha2BlockHasherSubairConfig;
10
11/// In each SHA block:
12/// - First C::ROUND_ROWS rows use Sha2RoundCols
13/// - Final row uses Sha2DigestCols
14///
15/// For soundness, the AIR enforces that the trace ends in padding rows after the last digest row.
16/// Honest traces already satisfy this because the unpadded height is a multiple of 17 (SHA-256)
17/// or 21 (SHA-512), and thus not a power of 2.
18///
19/// Sha2RoundCols and Sha2DigestCols share the same first 3 fields:
20/// - flags
21/// - work_vars/hash (same type, different name)
22/// - schedule_helper
23///
24/// This design allows for:
25/// 1. Common constraints to work on either struct type by accessing these shared fields
26/// 2. Specific constraints to use the appropriate struct, with flags helping to do conditional
27///    constraints
28///
29/// Note that the `Sha2WorkVarsCols` field is used for different purposes in the two structs.
30#[repr(C)]
31#[derive(Clone, Copy, Debug, ColsRef)]
32#[config(Sha2BlockHasherSubairConfig)]
33pub struct Sha2RoundCols<
34    T,
35    const WORD_BITS: usize,
36    const WORD_U8S: usize,
37    const WORD_U16S: usize,
38    const ROUNDS_PER_ROW: usize,
39    const ROUNDS_PER_ROW_MINUS_ONE: usize,
40    const ROW_VAR_CNT: usize,
41> {
42    pub flags: Sha2FlagsCols<T, ROW_VAR_CNT>,
43    pub work_vars: Sha2WorkVarsCols<T, WORD_BITS, ROUNDS_PER_ROW, WORD_U16S>,
44    pub schedule_helper:
45        Sha2MessageHelperCols<T, WORD_U16S, ROUNDS_PER_ROW, ROUNDS_PER_ROW_MINUS_ONE>,
46    pub message_schedule: Sha2MessageScheduleCols<T, WORD_BITS, ROUNDS_PER_ROW, WORD_U8S>,
47}
48
49#[repr(C)]
50#[derive(Clone, Copy, Debug, ColsRef)]
51#[config(Sha2BlockHasherSubairConfig)]
52pub struct Sha2DigestCols<
53    T,
54    const WORD_BITS: usize,
55    const WORD_U8S: usize,
56    const WORD_U16S: usize,
57    const HASH_WORDS: usize,
58    const ROUNDS_PER_ROW: usize,
59    const ROUNDS_PER_ROW_MINUS_ONE: usize,
60    const ROW_VAR_CNT: usize,
61> {
62    pub flags: Sha2FlagsCols<T, ROW_VAR_CNT>,
63    /// Will serve as previous hash values for the next block
64    pub hash: Sha2WorkVarsCols<T, WORD_BITS, ROUNDS_PER_ROW, WORD_U16S>,
65    pub schedule_helper:
66        Sha2MessageHelperCols<T, WORD_U16S, ROUNDS_PER_ROW, ROUNDS_PER_ROW_MINUS_ONE>,
67    /// The actual final hash values of the given block
68    /// Note: the above `hash` will be equal to `final_hash` unless we are on the last block
69    pub final_hash: [[T; WORD_U8S]; HASH_WORDS],
70    /// The final hash of the previous block
71    /// Note: will be constrained using interactions with the chip itself
72    pub prev_hash: [[T; WORD_U16S]; HASH_WORDS],
73}
74
75#[repr(C)]
76#[derive(Clone, Copy, Debug, ColsRef)]
77#[config(Sha2BlockHasherSubairConfig)]
78pub struct Sha2MessageScheduleCols<
79    T,
80    const WORD_BITS: usize,
81    const ROUNDS_PER_ROW: usize,
82    const WORD_U8S: usize,
83> {
84    /// The message schedule words as bits
85    /// The first 16 words will be the message data
86    pub w: [[T; WORD_BITS]; ROUNDS_PER_ROW],
87    /// Will be message schedule carries for rows 4..C::ROUND_ROWS and a buffer for rows 0..4 to be
88    /// used freely by wrapper chips Note: carries are 2 bit numbers represented using 2 cells
89    /// as individual bits
90    /// Note: carry_or_buffer is left unconstrained on rounds 0..3
91    pub carry_or_buffer: [[T; WORD_U8S]; ROUNDS_PER_ROW],
92}
93
94#[repr(C)]
95#[derive(Clone, Copy, Debug, ColsRef)]
96#[config(Sha2BlockHasherSubairConfig)]
97pub struct Sha2WorkVarsCols<
98    T,
99    const WORD_BITS: usize,
100    const ROUNDS_PER_ROW: usize,
101    const WORD_U16S: usize,
102> {
103    /// `a` and `e` after each iteration as 32-bits
104    pub a: [[T; WORD_BITS]; ROUNDS_PER_ROW],
105    pub e: [[T; WORD_BITS]; ROUNDS_PER_ROW],
106    /// The carry's used for addition during each iteration when computing `a` and `e`
107    pub carry_a: [[T; WORD_U16S]; ROUNDS_PER_ROW],
108    pub carry_e: [[T; WORD_U16S]; ROUNDS_PER_ROW],
109}
110
111/// These are the columns that are used to help with the message schedule additions
112/// Note: these need to be correctly assigned for every row even on padding rows
113#[repr(C)]
114#[derive(Clone, Copy, Debug, ColsRef)]
115#[config(Sha2BlockHasherSubairConfig)]
116pub struct Sha2MessageHelperCols<
117    T,
118    const WORD_U16S: usize,
119    const ROUNDS_PER_ROW: usize,
120    const ROUNDS_PER_ROW_MINUS_ONE: usize,
121> {
122    /// The following are used to move data forward to constrain the message schedule additions
123    /// The value of `w` from 3 rounds ago
124    pub w_3: [[T; WORD_U16S]; ROUNDS_PER_ROW_MINUS_ONE],
125    /// Here intermediate(i) =  w_i + sig_0(w_{i+1})
126    /// Intermed_t represents the intermediate t rounds ago
127    /// This is needed to constrain the message schedule, since we can only constrain on two rows
128    /// at a time
129    pub intermed_4: [[T; WORD_U16S]; ROUNDS_PER_ROW],
130    pub intermed_8: [[T; WORD_U16S]; ROUNDS_PER_ROW],
131    pub intermed_12: [[T; WORD_U16S]; ROUNDS_PER_ROW],
132}
133
134#[repr(C)]
135#[derive(Clone, Copy, Debug, ColsRef)]
136#[config(Sha2BlockHasherSubairConfig)]
137pub struct Sha2FlagsCols<T, const ROW_VAR_CNT: usize> {
138    pub is_round_row: T,
139    /// A flag that indicates if the current row is among the first 4 rows of a block (the message
140    /// rows)
141    pub is_first_4_rows: T,
142    pub is_digest_row: T,
143    /// We will encode the row index [0..C::ROWS_PER_BLOCK] using ROW_VAR_CNT cells
144    pub row_idx: [T; ROW_VAR_CNT],
145    /// The global index of the current block, starts at 1 for the first block
146    /// and increments by 1 for each block. Padding rows use one more than the
147    /// last real block's index and remain constant across all padding rows.
148    pub global_block_idx: T,
149}
150
151impl<O, T> Sha2FlagsColsRef<'_, T>
152where
153    T: Add<Output = O> + Copy,
154{
155    // This refers to the padding rows that are added to the air to make the trace length a power of
156    // 2. Not to be confused with the padding added to messages as part of the SHA hash
157    // function.
158    pub fn is_not_padding_row(&self) -> O {
159        *self.is_round_row + *self.is_digest_row
160    }
161
162    // This refers to the padding rows that are added to the air to make the trace length a power of
163    // 2. Not to be confused with the padding added to messages as part of the SHA hash
164    // function.
165    pub fn is_padding_row(&self) -> O
166    where
167        O: PrimeCharacteristicRing,
168    {
169        not(self.is_not_padding_row())
170    }
171}